name: Build & Release
on:
workflow_dispatch:
push:
tags:
- "v*"
permissions:
contents: write
env:
RUST_MIN_STACK: 16777216
concurrency:
group: dev-build
cancel-in-progress: true
jobs:
lint-workflows:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- name: actionlint
run: |
bash <(curl -fsSL https://raw.githubusercontent.com/rhysd/actionlint/main/scripts/download-actionlint.bash)
./actionlint -color
test:
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v6
- uses: dtolnay/rust-toolchain@stable
with:
components: clippy, rustfmt
- if: matrix.os == 'ubuntu-latest'
run: cargo fmt --check --all
- if: matrix.os == 'ubuntu-latest'
run: cargo clippy --all-targets --locked -- -D warnings
- run: cargo test --workspace --locked
build-cli:
needs: [test, lint-workflows]
strategy:
fail-fast: false
matrix:
include:
- os: ubuntu-latest
target: x86_64-unknown-linux-gnu
ext: tar.gz
- os: ubuntu-latest
target: aarch64-unknown-linux-gnu
ext: tar.gz
cross: true
- os: ubuntu-latest
target: x86_64-unknown-linux-musl
ext: tar.gz
cross: true
- os: ubuntu-latest
target: aarch64-unknown-linux-musl
ext: tar.gz
cross: true
- os: macos-latest
target: aarch64-apple-darwin
ext: tar.gz
- os: macos-latest
target: x86_64-apple-darwin
ext: tar.gz
- os: windows-latest
target: x86_64-pc-windows-msvc
ext: zip
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v6
- uses: dtolnay/rust-toolchain@stable
with:
targets: ${{ matrix.target }}
- name: Install cross
if: matrix.cross
run: cargo install cross --locked
- name: Build (native)
if: ${{ !matrix.cross }}
run: cargo build --release --bin graphdblite --target ${{ matrix.target }}
- name: Build (cross)
if: matrix.cross
run: cross build --release --bin graphdblite --target ${{ matrix.target }}
- name: Package (unix)
if: runner.os != 'Windows'
run: |
tar czf graphdblite-${{ matrix.target }}.${{ matrix.ext }} \
-C target/${{ matrix.target }}/release graphdblite
- name: Package (windows)
if: runner.os == 'Windows'
shell: pwsh
run: |
Compress-Archive -Path target\${{ matrix.target }}\release\graphdblite.exe `
-DestinationPath graphdblite-${{ matrix.target }}.${{ matrix.ext }}
- name: Checksum
shell: bash
run: |
sha256sum graphdblite-${{ matrix.target }}.${{ matrix.ext }} \
> graphdblite-${{ matrix.target }}.${{ matrix.ext }}.sha256
- uses: actions/upload-artifact@v7
with:
name: cli-${{ matrix.target }}
path: |
graphdblite-${{ matrix.target }}.${{ matrix.ext }}
graphdblite-${{ matrix.target }}.${{ matrix.ext }}.sha256
build-ffi:
needs: [test, lint-workflows]
strategy:
fail-fast: false
matrix:
include:
- os: ubuntu-latest
target: x86_64-unknown-linux-gnu
- os: ubuntu-latest
target: aarch64-unknown-linux-gnu
cross: true
- os: ubuntu-latest
target: x86_64-unknown-linux-musl
cross: true
- os: ubuntu-latest
target: aarch64-unknown-linux-musl
cross: true
- os: macos-latest
target: aarch64-apple-darwin
- os: macos-latest
target: x86_64-apple-darwin
- os: windows-latest
target: x86_64-pc-windows-msvc
- os: windows-latest
target: x86_64-pc-windows-gnu
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v6
- uses: dtolnay/rust-toolchain@stable
with:
targets: ${{ matrix.target }}
- name: Install cross
if: matrix.cross
run: cargo install cross --locked
- name: Pre-generate FFI header (host)
shell: bash
run: cargo check -p graphdblite-ffi
- name: Build (native)
if: ${{ !matrix.cross }}
run: cargo build --release -p graphdblite-ffi --target ${{ matrix.target }}
- name: Build (cross)
if: matrix.cross
run: cross build --release -p graphdblite-ffi --target ${{ matrix.target }}
- name: Package (unix)
if: runner.os != 'Windows'
shell: bash
run: |
mkdir -p staging
cp bindings/ffi/graphdblite.h staging/
cp target/${{ matrix.target }}/release/libgraphdblite_ffi.a staging/ 2>/dev/null || true
cp target/${{ matrix.target }}/release/libgraphdblite_ffi.so staging/ 2>/dev/null || true
cp target/${{ matrix.target }}/release/libgraphdblite_ffi.dylib staging/ 2>/dev/null || true
tar czf graphdblite-ffi-${{ matrix.target }}.tar.gz -C staging .
- name: Package (windows)
if: runner.os == 'Windows'
shell: pwsh
run: |
New-Item -ItemType Directory -Path staging
Copy-Item bindings\ffi\graphdblite.h staging\
# MSVC outputs (x86_64-pc-windows-msvc): .dll, .dll.lib, .lib
Copy-Item target\${{ matrix.target }}\release\graphdblite_ffi.dll staging\ -ErrorAction SilentlyContinue
Copy-Item target\${{ matrix.target }}\release\graphdblite_ffi.dll.lib staging\ -ErrorAction SilentlyContinue
Copy-Item target\${{ matrix.target }}\release\graphdblite_ffi.lib staging\ -ErrorAction SilentlyContinue
# MinGW outputs (x86_64-pc-windows-gnu): libgraphdblite_ffi.a, .dll, .dll.a — for Go cgo
Copy-Item target\${{ matrix.target }}\release\libgraphdblite_ffi.a staging\ -ErrorAction SilentlyContinue
Copy-Item target\${{ matrix.target }}\release\libgraphdblite_ffi.dll.a staging\ -ErrorAction SilentlyContinue
Compress-Archive -Path staging\* -DestinationPath graphdblite-ffi-${{ matrix.target }}.zip
- name: Checksum
shell: bash
run: |
for f in graphdblite-ffi-${{ matrix.target }}.*; do
case "$f" in *.sha256) continue ;; esac
sha256sum "$f" > "$f.sha256"
done
- uses: actions/upload-artifact@v7
with:
name: ffi-${{ matrix.target }}
path: |
graphdblite-ffi-${{ matrix.target }}.*
build-node:
needs: [test, lint-workflows]
strategy:
fail-fast: false
matrix:
include:
- os: ubuntu-latest
target: x86_64-unknown-linux-gnu
- os: ubuntu-latest
target: aarch64-unknown-linux-gnu
use_zig: true
- os: ubuntu-latest
target: x86_64-unknown-linux-musl
use_zig: true
- os: ubuntu-latest
target: aarch64-unknown-linux-musl
use_zig: true
- os: macos-latest
target: aarch64-apple-darwin
- os: macos-latest
target: x86_64-apple-darwin
- os: windows-latest
target: x86_64-pc-windows-msvc
runs-on: ${{ matrix.os }}
defaults:
run:
working-directory: bindings/node
steps:
- uses: actions/checkout@v6
- uses: dtolnay/rust-toolchain@stable
with:
targets: ${{ matrix.target }}
- uses: actions/setup-node@v6
with:
node-version: "20"
- run: npm install
- name: Build (native)
if: ${{ !matrix.use_zig }}
run: npx napi build --platform --release --target ${{ matrix.target }}
- name: Install zig
if: matrix.use_zig
working-directory: ${{ runner.temp }}
run: |
ZIG_VERSION=0.14.1
# Note: official filename is `zig-x86_64-linux-X.Y.Z.tar.xz`
# (target-host order, not host-target).
curl -fsSL "https://ziglang.org/download/${ZIG_VERSION}/zig-x86_64-linux-${ZIG_VERSION}.tar.xz" \
-o zig.tar.xz
tar -xJf zig.tar.xz
echo "${{ runner.temp }}/zig-x86_64-linux-${ZIG_VERSION}" >> "$GITHUB_PATH"
- name: Build (zig cross)
if: matrix.use_zig
run: |
zig version
npx napi build --platform --release --target ${{ matrix.target }} --zig
- uses: actions/upload-artifact@v7
with:
name: node-${{ matrix.target }}
path: bindings/node/*.node
publish-npm:
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v')
needs: build-node
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/setup-node@v6
with:
node-version: "22"
registry-url: "https://registry.npmjs.org"
- name: Install Node deps
working-directory: bindings/node
run: npm install
- uses: actions/download-artifact@v8
with:
path: bindings/node/artifacts
pattern: node-*
merge-multiple: true
- name: List downloaded artifacts
working-directory: bindings/node
run: ls -R artifacts
- name: Stage per-platform npm packages
working-directory: bindings/node
run: |
# napi-rs v2 CLI: command is `create-npm-dir` (singular).
# Passing `-t .` makes it iterate the triples from package.json
# rather than expecting a single target.
npx napi create-npm-dir -t .
npx napi artifacts --dir artifacts
echo "--- npm tree ---"
ls -R npm
- name: Publish per-platform packages
working-directory: bindings/node
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
run: |
for d in npm/*/; do
echo "Publishing $d"
(cd "$d" && npm publish --access public)
done
- name: Publish main wrapper
working-directory: bindings/node
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
run: npm publish --access public
build-wheels:
needs: [test, lint-workflows]
strategy:
matrix:
include:
- os: ubuntu-latest
target: x86_64
manylinux: "2_28"
- os: ubuntu-latest
target: x86_64
manylinux: musllinux_1_2
- os: ubuntu-latest
target: aarch64
manylinux: "2_28"
- os: ubuntu-latest
target: aarch64
manylinux: musllinux_1_2
- os: macos-latest
target: x86_64
manylinux: auto
- os: macos-latest
target: aarch64
manylinux: auto
- os: windows-latest
target: x64
manylinux: auto
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v6
- uses: astral-sh/setup-uv@v8.1.0
- name: Build wheels
uses: PyO3/maturin-action@v1
with:
target: ${{ matrix.target }}
args: --release --out dist --manifest-path bindings/python/Cargo.toml
manylinux: ${{ matrix.manylinux }}
- uses: actions/upload-artifact@v7
with:
name: wheels-${{ matrix.os }}-${{ matrix.target }}-${{ matrix.manylinux }}
path: dist/*.whl
test-go:
needs: build-ffi
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: dtolnay/rust-toolchain@stable
- uses: actions/setup-go@v6
with:
go-version: "1.26"
cache: false - name: Build FFI and run Go tests
run: make -C bindings/go test
test-ffi-conformance:
needs: [test, lint-workflows]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: dtolnay/rust-toolchain@stable
- name: Build FFI and run C conformance suite
run: make -C bindings/ffi conformance
test-python-conformance:
needs: [test, lint-workflows]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: dtolnay/rust-toolchain@stable
- uses: astral-sh/setup-uv@v8.1.0
- name: Build and install Python binding
working-directory: bindings/python
run: |
uv venv --python 3.13
uv pip install maturin pytest
uv run maturin develop --release
- name: Run conformance suite
working-directory: bindings/python
run: uv run pytest tests/conformance/ -v
test-node-conformance:
needs: [test, lint-workflows]
runs-on: ubuntu-latest
defaults:
run:
working-directory: bindings/node
steps:
- uses: actions/checkout@v6
- uses: dtolnay/rust-toolchain@stable
- uses: actions/setup-node@v6
with:
node-version: "20"
- run: npm install
- name: Build native addon
run: npx napi build --platform --release
- name: Run conformance suite
run: npm test
collect-artifacts:
needs: [build-cli, build-ffi, build-node, build-wheels, test-go, test-ffi-conformance, test-python-conformance, test-node-conformance]
runs-on: ubuntu-latest
steps:
- uses: actions/download-artifact@v8
with:
pattern: cli-*
merge-multiple: true
path: cli/
- uses: actions/download-artifact@v8
with:
pattern: ffi-*
merge-multiple: true
path: ffi/
- uses: actions/download-artifact@v8
with:
pattern: node-*
merge-multiple: true
path: node/
- uses: actions/download-artifact@v8
with:
pattern: wheels-*
merge-multiple: true
path: wheels/
- name: Combine checksums
run: cat cli/*.sha256 ffi/*.sha256 > sha256sums.txt
- uses: actions/upload-artifact@v7
with:
name: release-bundle
path: |
cli/
ffi/
node/
wheels/
sha256sums.txt
publish-dev-latest:
if: github.event_name == 'workflow_dispatch'
needs: collect-artifacts
runs-on: ubuntu-latest
env:
GH_TOKEN: ${{ github.token }}
steps:
- uses: actions/checkout@v6
- uses: actions/download-artifact@v8
with:
name: release-bundle
path: bundle/
- name: Replace rolling dev-latest release
run: |
gh release delete dev-latest --yes --cleanup-tag || true
gh release create dev-latest \
--title "Dev Build (latest main)" \
--notes "Rolling development build from main branch.
Commit: ${{ github.sha }}
Date: $(date -u +%Y-%m-%d)
## Artifacts
- **CLI binaries**: Linux (x86_64, arm64, musl x86_64, musl arm64), macOS (x86_64, arm64), Windows (x86_64)
- **Python wheels**: full PyPI matrix (glibc + musl Linux, macOS, Windows)
- **C FFI libraries**: same 7 targets as CLI, plus Windows MinGW for Go cgo
- **Node.js addons**: same 7 targets as CLI" \
--prerelease \
--target ${{ github.sha }} \
bundle/cli/graphdblite-*.tar.gz bundle/cli/graphdblite-*.zip bundle/sha256sums.txt \
bundle/ffi/graphdblite-ffi-*.tar.gz bundle/ffi/graphdblite-ffi-*.zip \
bundle/node/*.node \
bundle/wheels/*.whl
publish-tag-release:
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v')
needs: collect-artifacts
runs-on: ubuntu-latest
env:
GH_TOKEN: ${{ github.token }}
steps:
- uses: actions/checkout@v6
- uses: actions/download-artifact@v8
with:
name: release-bundle
path: bundle/
- name: Create release for ${{ github.ref_name }}
run: |
gh release create "${{ github.ref_name }}" \
--title "${{ github.ref_name }}" \
--notes "Release ${{ github.ref_name }}.
Commit: ${{ github.sha }}
## Artifacts
- **CLI binaries**: Linux (x86_64, arm64, musl x86_64, musl arm64), macOS (x86_64, arm64), Windows (x86_64)
- **Python wheels**: full PyPI matrix (glibc + musl Linux, macOS, Windows)
- **C FFI libraries**: same 7 targets as CLI, plus Windows MinGW for Go cgo
- **Node.js addons**: same 7 targets as CLI
See [CHANGELOG.md](https://github.com/${{ github.repository }}/blob/${{ github.ref_name }}/CHANGELOG.md) for details." \
bundle/cli/graphdblite-*.tar.gz bundle/cli/graphdblite-*.zip bundle/sha256sums.txt \
bundle/ffi/graphdblite-ffi-*.tar.gz bundle/ffi/graphdblite-ffi-*.zip \
bundle/node/*.node \
bundle/wheels/*.whl