Repository navigation
Build against DuckDB v1.5.6 (#515) #1729
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # | |
| # This workflow calls the main distribution pipeline from DuckDB to build, test and (optionally) release the extension | |
| # | |
| name: Main Extension Distribution Pipeline | |
| on: | |
| pull_request: | |
| # `main` for release-bound PRs, plus `feat/**` and `fix/**` so stacked | |
| # PRs (each targeting the previous branch rather than `main`) trigger | |
| # CI automatically — instead of needing a manual `workflow_dispatch` | |
| # call from a reviewer or from a maintainer's local machine. | |
| branches: | |
| - main | |
| - 'feat/**' | |
| - 'fix/**' | |
| paths-ignore: | |
| - '**/README.md' | |
| - 'doc/**' | |
| push: | |
| branches: | |
| - main | |
| paths-ignore: | |
| - '**/README.md' | |
| - 'doc/**' | |
| workflow_dispatch: | |
| concurrency: | |
| group: ${{ github.workflow }}-${{ github.ref }}-${{ github.head_ref || '' }}-${{ github.base_ref || '' }}-${{ github.ref != 'refs/heads/main' || github.sha }} | |
| cancel-in-progress: true | |
| jobs: | |
| # THE ONE PLACE A SUPPORTED DUCKDB VERSION IS WRITTEN. | |
| # The version used to appear nine times in this file, and the comment on the | |
| # artifact download asked the reader to "keep the version here in sync with the | |
| # two jobs above" — a rule nothing enforces. Every consumer below reads this | |
| # job's output, so adding or dropping a supported line is one edit to the list | |
| # in the `pick` step, and a matrix over it builds each version named there. | |
| # | |
| # The two `uses:` refs stay literal because a reusable-workflow reference admits | |
| # no expression ("You cannot use contexts or expressions in this keyword"). They | |
| # name the CI-TOOLS workflow to run, which is a different fact from which DuckDB | |
| # versions are supported: one such workflow serves several of them through the | |
| # `duckdb_version` and `ci_tools_version` inputs. | |
| versions: | |
| name: Supported DuckDB versions | |
| runs-on: ubuntu-24.04 | |
| outputs: | |
| supported: ${{ steps.pick.outputs.supported }} | |
| steps: | |
| - id: pick | |
| shell: bash | |
| run: echo 'supported=["v1.5.6"]' >> "$GITHUB_OUTPUT" | |
| duckdb-latest-build: | |
| needs: versions | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| duckdb: ${{ fromJSON(needs.versions.outputs.supported) }} | |
| # The upstream distribution workflow with one addition: every job keeps a vcpkg binary | |
| # cache of its own, so the dependency set the manifest names — GDAL, GEOS, | |
| # PROJ, OpenSSL, libmeos and the twenty ports beneath them — is restored | |
| # rather than compiled. The public cache the workflow reads holds an archive | |
| # only for the dependency sets DuckDB itself builds, and this extension | |
| # carries a port of its own, so twenty-seven of its twenty-eight packages | |
| # are compiled from source on every job: 12.7 minutes of a 27.7-minute | |
| # Linux job, and the larger share of a macOS one. The addition is offered | |
| # upstream; the fork is where it runs until it is taken. On the MinGW job the fork | |
| # also takes the R the runner image carries: the build compiles with the Rtools | |
| # toolchain and never runs R, so R devel is not downloaded. | |
| uses: estebanzimanyi/extension-ci-tools/.github/workflows/_extension_distribution.yml@v1.5.6-node24 | |
| with: | |
| duckdb_version: ${{ matrix.duckdb }} | |
| extension_name: mobilityduck | |
| ci_tools_version: ${{ matrix.duckdb }} | |
| vcpkg_commit: c27eeddba73f608f10605d80bc0144c1166f8fb7 | |
| # A compiler cache only pays off if it is still there next time, and a | |
| # repository gets 10 GB for all of them. The distribution workflow allows | |
| # each job 5 GB, so a matrix of seven architectures overruns the budget and | |
| # GitHub evicts least-recently-used to make room; the entries that lose that | |
| # race are the small ones, which is why the Linux leg reads | |
| # `cache hit rate 0.00 %` with all 724 objects recompiled, 46 minutes of a | |
| # 51-minute job. | |
| # | |
| # `test_env_variables` is appended to the Docker environment file after the | |
| # workflow writes its own defaults, and a later assignment wins there, so | |
| # naming the ceiling here overrides the 5 GB for every architecture built in | |
| # Docker. The ceiling bounds the cache directory, which the Docker legs keep | |
| # uncompressed (CCACHE_NOCOMPRESS=1), while the entry saved from it is a | |
| # zstd archive about six times smaller. One build writes about 300 MB of | |
| # objects, and a job starts from the directory the last one saved, which | |
| # after a DuckDB version change holds the other version's objects. 1.5G | |
| # holds such a restored directory and a full rebuild without evicting the | |
| # objects the next job reads, and saves an entry of at most about 250 MB. | |
| # The macOS and Windows legs take their ceiling from the ccache action | |
| # instead and are unaffected. | |
| test_config: '{"test_env_variables": {"CCACHE_MAXSIZE": "1.5G"}}' | |
| # Windows builds through MinGW (windows_amd64_mingw), the toolchain MEOS | |
| # is built with on Windows. windows_amd64 is excluded: it compiles with | |
| # MSVC, which MEOS has no build for. | |
| # | |
| # On pull_request events the reduced matrix leaves the native targets | |
| # that run the suite. macOS is where a non-portable binary shows up first | |
| # — the loader there rejects a missing @rpath dependency outright — so | |
| # osx_arm64 is one of those a pull request keeps, and the | |
| # verify-standalone job below inspects its artifact on the event that | |
| # gates review. | |
| exclude_archs: 'windows_amd64;linux_amd64_musl' | |
| # Of the four native targets a pull request builds, two run the suite — | |
| # linux_amd64 and osx_arm64 — and two only build: linux_arm64 and | |
| # osx_amd64 report `Test extension … skipped`, the latter because it is | |
| # cross-compiled on an arm64 host. Reduced mode keeps exactly the pair | |
| # that tests, so a pull request pays for two builds rather than four and | |
| # loses no assertion, only the compile of the two archs it drops. Every | |
| # other event keeps the full matrix, so a push to main and a release | |
| # still build every target. | |
| reduced_ci_mode: ${{ github.event_name == 'pull_request' && 'enabled' || 'disabled' }} | |
| # A hand-written binding declares its result type twice — in the registration | |
| # DuckDB reads and in the executor template that writes the result vector — | |
| # and nothing makes the two agree. The audit is source-only, so it answers in | |
| # seconds and gates the pull request rather than waiting on a build. | |
| source-audits: | |
| name: Source audits | |
| runs-on: ubuntu-24.04 | |
| steps: | |
| - uses: actions/checkout@v7 | |
| - name: Declared return types are the ones the executors write | |
| run: python3 scripts/return-type-audit.py . | |
| # The published artifact is a single file a user downloads and LOADs, so the | |
| # guarantee it has to carry is that it names no shared library it does not | |
| # ship, and that a stock DuckDB CLI can LOAD it and evaluate a MEOS-backed | |
| # expression. It runs before deploy so a non-portable binary is never released. | |
| verify-standalone: | |
| name: Standalone load (${{ matrix.duckdb }}, ${{ matrix.duckdb_arch }}) | |
| needs: [versions, duckdb-latest-build] | |
| # An entry consumes the artifact of the build job of its own architecture, so the set of | |
| # entries is the set the build matrix produces: a pull request builds linux_amd64 and | |
| # osx_arm64, and asking for either of the other two would fail on a download of an artifact | |
| # nothing produced. The two lists are written out rather than filtered by a job-level `if`, | |
| # which cannot read the `matrix` context. | |
| # | |
| # Reading an artifact's declared dependencies works from any host — `otool`/`readelf` parse | |
| # headers and do not execute code — so that assertion covers every entry. Running the loader | |
| # needs a host of the artifact's own architecture, so `native` marks the entries where the | |
| # LOAD step can run. | |
| # | |
| # GitHub has no Intel macOS runner: a job asking for `macos-13` is never assigned one and is | |
| # cancelled at the 24h queue limit having run no step, which reads as a red check that no code | |
| # change can turn green. The build job does not hit this because it produces osx_amd64 by | |
| # cross-compiling on an arm64 host. So inspect that artifact on arm64 and leave its LOAD to | |
| # the deployed binary's osx_arm64 sibling, which exercises the same linkage. | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| duckdb: ${{ fromJSON(needs.versions.outputs.supported) }} | |
| include: ${{ fromJSON(github.event_name == 'pull_request' | |
| && '[{"duckdb_arch":"linux_amd64","runner":"ubuntu-24.04","native":true},{"duckdb_arch":"osx_arm64","runner":"macos-14","native":true}]' | |
| || '[{"duckdb_arch":"linux_amd64","runner":"ubuntu-24.04","native":true},{"duckdb_arch":"linux_arm64","runner":"ubuntu-24.04-arm","native":true},{"duckdb_arch":"osx_amd64","runner":"macos-14","native":false},{"duckdb_arch":"osx_arm64","runner":"macos-14","native":true}]') }} | |
| runs-on: ${{ matrix.runner }} | |
| steps: | |
| # Artifact name is built from duckdb_version + extension_name, both read | |
| # from the matrix rather than written again. | |
| - uses: actions/download-artifact@v8 | |
| with: | |
| name: mobilityduck-${{ matrix.duckdb }}-extension-${{ matrix.duckdb_arch }} | |
| path: artifact | |
| - name: Assert the artifact names no non-system shared library | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| EXT=artifact/mobilityduck.duckdb_extension | |
| test -f "$EXT" | |
| if [ "${{ runner.os }}" = "macOS" ]; then | |
| # Drop line 1 (otool echoes the file path) and keep the load-command | |
| # paths. The extension's own LC_ID_DYLIB appears here too and is not | |
| # a dependency, hence the explicit allowance for it. | |
| otool -L "$EXT" | tail -n +2 | awk 'NF {print $1}' > deps.txt | |
| ALLOW='^(/usr/lib/|/System/Library/Frameworks/|@rpath/mobilityduck\.duckdb_extension$)' | |
| else | |
| readelf -d "$EXT" | sed -n 's/.*NEEDED.*\[\(.*\)\]/\1/p' > deps.txt | |
| # glibc's own set plus the C++ runtime. Anything vcpkg built (MEOS, | |
| # GEOS, PROJ, GDAL, GSL, json-c, h3, libxml2) must be absorbed into | |
| # the binary, so none of them may appear here. | |
| ALLOW='^(libc\.so|libm\.so|libdl\.so|libpthread\.so|librt\.so|libresolv\.so|libstdc\+\+\.so|libgcc_s\.so|libz\.so|ld-linux)' | |
| fi | |
| echo "--- shared libraries named by the artifact:" | |
| cat deps.txt | |
| if grep -Ev "$ALLOW" deps.txt > offenders.txt; then | |
| echo "::error::The published artifact depends on libraries it does not ship. A downloader cannot dlopen it." | |
| cat offenders.txt | |
| exit 1 | |
| fi | |
| echo "OK: only system libraries are referenced." | |
| - name: LOAD the artifact into a stock DuckDB CLI | |
| if: ${{ matrix.native }} | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| case "${{ matrix.duckdb_arch }}" in | |
| linux_amd64) CLI=duckdb_cli-linux-amd64.zip ;; | |
| linux_arm64) CLI=duckdb_cli-linux-arm64.zip ;; | |
| osx_*) CLI=duckdb_cli-osx-universal.zip ;; | |
| esac | |
| curl -fsSL -o duckdb.zip "https://github.com/duckdb/duckdb/releases/download/${{ matrix.duckdb }}/${CLI}" | |
| unzip -q -o duckdb.zip | |
| chmod +x duckdb | |
| # tbool() round-trips through MEOS and depends on the Europe/Brussels | |
| # session timezone the extension installs at load time, so a correct | |
| # answer proves MEOS is both linked in and initialised — not merely | |
| # that dlopen succeeded. Mirrors test/sql/tbool.test. | |
| ./duckdb -unsigned -noheader -list -c \ | |
| "LOAD 'artifact/mobilityduck.duckdb_extension'; SELECT tbool(true, timestamptz '2012-01-01 08:00:00');" \ | |
| > out.txt | |
| echo "--- output:"; cat out.txt | |
| grep -qx 't@2012-01-01 08:00:00+01' out.txt | |
| duckdb-latest-deploy: | |
| needs: [versions, duckdb-latest-build, verify-standalone] | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| duckdb: ${{ fromJSON(needs.versions.outputs.supported) }} | |
| uses: estebanzimanyi/extension-ci-tools/.github/workflows/_extension_deploy.yml@v1.5.6-node24 | |
| secrets: inherit | |
| with: | |
| duckdb_version: ${{ matrix.duckdb }} | |
| ci_tools_version: ${{ matrix.duckdb }} | |
| extension_name: mobilityduck | |
| deploy_latest: ${{ startsWith(github.ref, 'refs/heads/v') || github.ref == 'refs/heads/main' }} | |
| # Gathers exactly the artifacts the build job produces, per event: the full | |
| # matrix on push/dispatch, and on pull_request the linux_amd64 the reduced | |
| # matrix leaves, since asking for an arch that matrix does not build fails | |
| # on "Artifact not found for name: mobilityduck-…-linux_arm64" and cancels | |
| # its siblings with it. The build workflow produces no Wasm artifact, so | |
| # the Wasm targets of this workflow's own matrix are excluded on every | |
| # event, as are windows_amd64 and musl (see the build job above). | |
| exclude_archs: ${{ github.event_name == 'pull_request' && 'windows_amd64;windows_amd64_mingw;linux_amd64_musl;linux_arm64;osx_amd64;osx_arm64;wasm_mvp;wasm_eh;wasm_threads' || 'windows_amd64;linux_amd64_musl;wasm_mvp;wasm_eh;wasm_threads' }} |