Release #12
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
| --- | |
| name: Release | |
| on: | |
| push: | |
| tags: [v*.*.*] | |
| workflow_dispatch: | |
| inputs: | |
| crates-io: | |
| description: Publish iscc-lib to crates.io | |
| type: boolean | |
| default: false | |
| pypi: | |
| description: Publish iscc-lib to PyPI | |
| type: boolean | |
| default: false | |
| npm: | |
| description: Publish @iscc/lib and @iscc/wasm to npm | |
| type: boolean | |
| default: false | |
| maven: | |
| description: Publish iscc-lib to Maven Central | |
| type: boolean | |
| default: false | |
| ffi: | |
| description: Build and publish FFI tarballs to GitHub Releases | |
| type: boolean | |
| default: false | |
| rubygems: | |
| description: Publish iscc-lib to RubyGems | |
| type: boolean | |
| default: false | |
| permissions: | |
| contents: read | |
| concurrency: | |
| group: release | |
| cancel-in-progress: false # Never cancel a release in progress | |
| jobs: | |
| # Publish Rust core crate to crates.io via OIDC trusted publishing | |
| publish-crates-io: | |
| name: Publish to crates.io | |
| if: startsWith(github.ref, 'refs/tags/v') || inputs.crates-io | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| id-token: write | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: dtolnay/rust-toolchain@stable | |
| - uses: Swatinem/rust-cache@v2 | |
| - name: Get workspace version | |
| id: version | |
| run: | | |
| VERSION=$(grep -m1 '^version' Cargo.toml | sed 's/.*"\(.*\)"/\1/') | |
| echo "version=$VERSION" >> "$GITHUB_OUTPUT" | |
| - name: Check version on registry | |
| id: check | |
| run: | | |
| VERSION="${{ steps.version.outputs.version }}" | |
| if cargo info iscc-lib 2>/dev/null | grep -q "version: $VERSION"; then | |
| echo "Version $VERSION already published to crates.io, skipping" | |
| echo "skip=true" >> "$GITHUB_OUTPUT" | |
| else | |
| echo "skip=false" >> "$GITHUB_OUTPUT" | |
| fi | |
| - name: Run tests | |
| if: steps.check.outputs.skip != 'true' | |
| run: cargo test --workspace --exclude iscc-rb | |
| - name: Authenticate with crates.io | |
| if: steps.check.outputs.skip != 'true' | |
| id: crates-auth | |
| uses: rust-lang/crates-io-auth-action@v1 | |
| - name: Publish iscc-lib | |
| if: steps.check.outputs.skip != 'true' | |
| run: cargo publish -p iscc-lib | |
| env: | |
| CARGO_REGISTRY_TOKEN: ${{ steps.crates-auth.outputs.token }} | |
| # Build cross-platform Python wheels using maturin | |
| build-wheels: | |
| name: Build wheels (${{ matrix.target }}) | |
| if: startsWith(github.ref, 'refs/tags/v') || inputs.pypi | |
| runs-on: ${{ matrix.os }} | |
| strategy: | |
| matrix: | |
| include: | |
| - os: ubuntu-latest | |
| target: x86_64 | |
| interpreter: python3.10 | |
| - os: macos-14 | |
| target: universal2-apple-darwin | |
| interpreter: python3.10 | |
| - os: windows-latest | |
| target: x64 | |
| interpreter: python | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: actions/setup-python@v5 | |
| with: | |
| python-version: '3.10' | |
| - name: Build wheel | |
| uses: PyO3/maturin-action@v1 | |
| with: | |
| target: ${{ matrix.target }} | |
| args: --release --out dist --manifest-path crates/iscc-py/Cargo.toml -i | |
| ${{ matrix.interpreter }} | |
| manylinux: auto | |
| before-script-linux: | | |
| # Put Python 3.10 first in PATH so maturin uses it for abi3 wheel tagging. | |
| # Without this, the manylinux container defaults to Python 3.8 which is below | |
| # the abi3-py310 minimum and produces a version-specific cp38 wheel instead. | |
| export PATH="/opt/python/cp310-cp310/bin:$PATH" | |
| - name: Upload wheels | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: wheels-${{ matrix.os }}-${{ matrix.target }} | |
| path: dist | |
| # Build source distribution | |
| build-sdist: | |
| name: Build sdist | |
| if: startsWith(github.ref, 'refs/tags/v') || inputs.pypi | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Build sdist | |
| uses: PyO3/maturin-action@v1 | |
| with: | |
| command: sdist | |
| args: --out dist --manifest-path crates/iscc-py/Cargo.toml | |
| - name: Upload sdist | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: wheels-sdist | |
| path: dist | |
| # Smoke test Python wheel on Linux before publishing | |
| test-wheels: | |
| name: Test Python wheel | |
| needs: [build-wheels] | |
| if: startsWith(github.ref, 'refs/tags/v') || inputs.pypi | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/setup-python@v5 | |
| with: | |
| python-version: '3.10' | |
| - name: Download wheel | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: wheels-ubuntu-latest-x86_64 | |
| path: dist | |
| - name: Install and test | |
| run: | | |
| pip install dist/*.whl | |
| python -c "from iscc_lib import conformance_selftest; assert conformance_selftest()" | |
| # Publish all wheels and sdist to PyPI via OIDC trusted publishing | |
| publish-pypi: | |
| name: Publish to PyPI | |
| needs: [build-wheels, build-sdist, test-wheels] | |
| if: startsWith(github.ref, 'refs/tags/v') || inputs.pypi | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| id-token: write | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Get workspace version | |
| id: version | |
| run: | | |
| VERSION=$(grep -m1 '^version' Cargo.toml | sed 's/.*"\(.*\)"/\1/') | |
| echo "version=$VERSION" >> "$GITHUB_OUTPUT" | |
| - name: Check version on registry | |
| id: check | |
| run: | | |
| VERSION="${{ steps.version.outputs.version }}" | |
| if curl -sf "https://pypi.org/pypi/iscc-lib/$VERSION/json" > /dev/null 2>&1; then | |
| echo "Version $VERSION already published to PyPI, skipping" | |
| echo "skip=true" >> "$GITHUB_OUTPUT" | |
| else | |
| echo "skip=false" >> "$GITHUB_OUTPUT" | |
| fi | |
| - name: Download all artifacts | |
| if: steps.check.outputs.skip != 'true' | |
| uses: actions/download-artifact@v4 | |
| with: | |
| pattern: wheels-* | |
| path: dist | |
| merge-multiple: true | |
| - name: Publish to PyPI | |
| if: steps.check.outputs.skip != 'true' | |
| uses: pypa/gh-action-pypi-publish@release/v1 | |
| with: | |
| packages-dir: dist | |
| # Build napi-rs native addons for cross-platform npm publishing | |
| build-napi: | |
| name: Build napi (${{ matrix.target }}) | |
| if: startsWith(github.ref, 'refs/tags/v') || inputs.npm | |
| runs-on: ${{ matrix.os }} | |
| strategy: | |
| matrix: | |
| include: | |
| - os: ubuntu-latest | |
| target: x86_64-unknown-linux-gnu | |
| - os: ubuntu-latest | |
| target: aarch64-unknown-linux-gnu | |
| - os: macos-14 | |
| target: aarch64-apple-darwin | |
| - os: macos-14 | |
| target: x86_64-apple-darwin | |
| - os: windows-latest | |
| target: x86_64-pc-windows-msvc | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: actions/setup-node@v4 | |
| with: | |
| node-version: '22' | |
| - uses: dtolnay/rust-toolchain@stable | |
| with: | |
| targets: ${{ matrix.target }} | |
| - uses: Swatinem/rust-cache@v2 | |
| - name: Install npm dependencies | |
| run: npm install | |
| working-directory: crates/iscc-napi | |
| - name: Install cross-compiler | |
| if: matrix.target == 'aarch64-unknown-linux-gnu' | |
| run: | | |
| sudo apt-get update | |
| sudo apt-get install -y gcc-aarch64-linux-gnu | |
| - name: Build native addon | |
| run: npx napi build --platform --release --target ${{ matrix.target }} | |
| working-directory: crates/iscc-napi | |
| env: | |
| CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER: aarch64-linux-gnu-gcc | |
| - name: Upload artifact | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: napi-${{ matrix.target }} | |
| path: crates/iscc-napi/*.node | |
| # Smoke test NAPI native addon on Linux before publishing | |
| test-napi: | |
| name: Test NAPI addon | |
| needs: [build-napi] | |
| if: startsWith(github.ref, 'refs/tags/v') || inputs.npm | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/setup-node@v4 | |
| with: | |
| node-version: '22' | |
| - name: Download addon | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: napi-x86_64-unknown-linux-gnu | |
| path: addon | |
| - name: Test native addon | |
| run: node -e "const m = require('./addon/iscc-lib.linux-x64-gnu.node'); if | |
| (!m.conformance_selftest()) { throw new Error('conformance failed'); }" | |
| # Publish @iscc/lib to npm | |
| publish-npm-lib: | |
| name: Publish @iscc/lib to npm | |
| needs: [build-napi, test-napi] | |
| if: startsWith(github.ref, 'refs/tags/v') || inputs.npm | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| id-token: write | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: actions/setup-node@v4 | |
| with: | |
| node-version: '22' | |
| registry-url: https://registry.npmjs.org | |
| - name: Install npm dependencies | |
| run: npm install | |
| working-directory: crates/iscc-napi | |
| - name: Download all napi artifacts | |
| uses: actions/download-artifact@v4 | |
| with: | |
| pattern: napi-* | |
| path: crates/iscc-napi | |
| merge-multiple: true | |
| - name: Prepare npm packages | |
| run: npx napi prepublish -t npm | |
| working-directory: crates/iscc-napi | |
| - name: Get workspace version | |
| id: version | |
| run: | | |
| VERSION=$(grep -m1 '^version' Cargo.toml | sed 's/.*"\(.*\)"/\1/') | |
| echo "version=$VERSION" >> "$GITHUB_OUTPUT" | |
| - name: Check version on registry | |
| id: check | |
| run: | | |
| VERSION="${{ steps.version.outputs.version }}" | |
| if npm view "@iscc/lib@$VERSION" version 2>/dev/null; then | |
| echo "Version $VERSION already published to npm, skipping" | |
| echo "skip=true" >> "$GITHUB_OUTPUT" | |
| else | |
| echo "skip=false" >> "$GITHUB_OUTPUT" | |
| fi | |
| - name: Publish to npm | |
| if: steps.check.outputs.skip != 'true' | |
| run: npm publish --provenance --access public | |
| working-directory: crates/iscc-napi | |
| env: | |
| NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} | |
| # Build WASM package for npm publishing | |
| build-wasm: | |
| name: Build WASM package | |
| if: startsWith(github.ref, 'refs/tags/v') || inputs.npm | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: dtolnay/rust-toolchain@stable | |
| - uses: Swatinem/rust-cache@v2 | |
| - name: Install wasm-pack | |
| run: curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh | |
| - name: Build WASM package | |
| run: wasm-pack build --target web --release crates/iscc-wasm --features conformance | |
| - name: Set npm package name and version | |
| run: | | |
| node <<'SCRIPT' | |
| const fs = require('fs'); | |
| const pkg = JSON.parse(fs.readFileSync('crates/iscc-wasm/pkg/package.json', 'utf8')); | |
| pkg.name = '@iscc/wasm'; | |
| pkg.repository = { type: 'git', url: 'https://github.com/iscc/iscc-lib' }; | |
| const toml = fs.readFileSync('Cargo.toml', 'utf8'); | |
| const m = toml.match(/^version\s*=\s*"(.+?)"/m); | |
| if (m) pkg.version = m[1]; | |
| fs.writeFileSync('crates/iscc-wasm/pkg/package.json', JSON.stringify(pkg, null, 2) + '\n'); | |
| SCRIPT | |
| - name: Upload WASM package | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: wasm-pkg | |
| path: crates/iscc-wasm/pkg/ | |
| # Smoke test WASM package on Linux before publishing | |
| test-wasm: | |
| name: Test WASM package | |
| needs: [build-wasm] | |
| if: startsWith(github.ref, 'refs/tags/v') || inputs.npm | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/setup-node@v4 | |
| with: | |
| node-version: '22' | |
| - name: Download WASM package | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: wasm-pkg | |
| path: pkg | |
| - name: Test WASM module | |
| run: | | |
| cat > smoke.mjs << 'SCRIPT' | |
| import { readFileSync } from 'fs'; | |
| import init, { conformance_selftest } from './pkg/iscc_wasm.js'; | |
| const wasmBytes = readFileSync('./pkg/iscc_wasm_bg.wasm'); | |
| await init(wasmBytes); | |
| if (!conformance_selftest()) { throw new Error('conformance failed'); } | |
| SCRIPT | |
| node smoke.mjs | |
| # Publish @iscc/wasm to npm | |
| publish-npm-wasm: | |
| name: Publish @iscc/wasm to npm | |
| needs: [build-wasm, test-wasm] | |
| if: startsWith(github.ref, 'refs/tags/v') || inputs.npm | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| id-token: write | |
| steps: | |
| - uses: actions/setup-node@v4 | |
| with: | |
| node-version: '22' | |
| registry-url: https://registry.npmjs.org | |
| - name: Download WASM package | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: wasm-pkg | |
| path: pkg | |
| - name: Get package version | |
| id: version | |
| run: | | |
| VERSION=$(node -p "require('./pkg/package.json').version") | |
| echo "version=$VERSION" >> "$GITHUB_OUTPUT" | |
| - name: Check version on registry | |
| id: check | |
| run: | | |
| VERSION="${{ steps.version.outputs.version }}" | |
| if npm view "@iscc/wasm@$VERSION" version 2>/dev/null; then | |
| echo "Version $VERSION already published to npm, skipping" | |
| echo "skip=true" >> "$GITHUB_OUTPUT" | |
| else | |
| echo "skip=false" >> "$GITHUB_OUTPUT" | |
| fi | |
| - name: Publish to npm | |
| if: steps.check.outputs.skip != 'true' | |
| run: npm publish --provenance --access public | |
| working-directory: pkg | |
| env: | |
| NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} | |
| # Build JNI native libraries for cross-platform JAR packaging | |
| build-jni: | |
| name: Build JNI (${{ matrix.native-dir }}) | |
| if: startsWith(github.ref, 'refs/tags/v') || inputs.maven | |
| runs-on: ${{ matrix.os }} | |
| strategy: | |
| matrix: | |
| include: | |
| - os: ubuntu-latest | |
| target: x86_64-unknown-linux-gnu | |
| native-dir: linux-x86_64 | |
| lib-name: libiscc_jni.so | |
| - os: ubuntu-latest | |
| target: aarch64-unknown-linux-gnu | |
| native-dir: linux-aarch64 | |
| lib-name: libiscc_jni.so | |
| - os: macos-14 | |
| target: aarch64-apple-darwin | |
| native-dir: macos-aarch64 | |
| lib-name: libiscc_jni.dylib | |
| - os: macos-14 | |
| target: x86_64-apple-darwin | |
| native-dir: macos-x86_64 | |
| lib-name: libiscc_jni.dylib | |
| - os: windows-latest | |
| target: x86_64-pc-windows-msvc | |
| native-dir: windows-x86_64 | |
| lib-name: iscc_jni.dll | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: dtolnay/rust-toolchain@stable | |
| with: | |
| targets: ${{ matrix.target }} | |
| - uses: Swatinem/rust-cache@v2 | |
| - name: Install cross-compiler | |
| if: matrix.target == 'aarch64-unknown-linux-gnu' | |
| run: | | |
| sudo apt-get update | |
| sudo apt-get install -y gcc-aarch64-linux-gnu | |
| - name: Build JNI library | |
| run: cargo build -p iscc-jni --release --target ${{ matrix.target }} | |
| env: | |
| CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER: aarch64-linux-gnu-gcc | |
| - name: Upload artifact | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: jni-${{ matrix.native-dir }} | |
| path: target/${{ matrix.target }}/release/${{ matrix.lib-name }} | |
| # Assemble cross-platform JAR with bundled native libraries | |
| assemble-jar: | |
| name: Assemble JAR | |
| needs: [build-jni] | |
| if: startsWith(github.ref, 'refs/tags/v') || inputs.maven | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: actions/setup-java@v4 | |
| with: | |
| distribution: temurin | |
| java-version: '17' | |
| - name: Download JNI artifacts | |
| uses: actions/download-artifact@v4 | |
| with: | |
| pattern: jni-* | |
| path: jni-staging | |
| - name: Copy native libraries to resources | |
| run: | | |
| for dir in jni-staging/jni-*/; do | |
| native_dir=$(basename "$dir" | sed 's/^jni-//') | |
| dest="crates/iscc-jni/java/src/main/resources/META-INF/native/$native_dir" | |
| mkdir -p "$dest" | |
| cp "$dir"* "$dest/" | |
| done | |
| - name: Build JAR | |
| run: mvn package -DskipTests -f crates/iscc-jni/java/pom.xml | |
| - name: Upload JAR | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: iscc-lib-jar | |
| path: crates/iscc-jni/java/target/*.jar | |
| # Smoke test JNI native libraries on Linux before publishing | |
| test-jni: | |
| name: Test JNI libraries | |
| needs: [build-jni] | |
| if: startsWith(github.ref, 'refs/tags/v') || inputs.maven | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: actions/setup-java@v4 | |
| with: | |
| distribution: temurin | |
| java-version: '17' | |
| - name: Download JNI artifacts | |
| uses: actions/download-artifact@v4 | |
| with: | |
| pattern: jni-* | |
| path: jni-staging | |
| - name: Copy native libraries to resources | |
| run: | | |
| for dir in jni-staging/jni-*/; do | |
| native_dir=$(basename "$dir" | sed 's/^jni-//') | |
| dest="crates/iscc-jni/java/src/main/resources/META-INF/native/$native_dir" | |
| mkdir -p "$dest" | |
| cp "$dir"* "$dest/" | |
| done | |
| - name: Run tests | |
| run: mvn test -f crates/iscc-jni/java/pom.xml | |
| # Publish Java JAR to Maven Central via Sonatype Central Portal | |
| publish-maven: | |
| name: Publish to Maven Central | |
| needs: [build-jni, test-jni] | |
| if: startsWith(github.ref, 'refs/tags/v') || inputs.maven | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: actions/setup-java@v4 | |
| with: | |
| distribution: temurin | |
| java-version: '17' | |
| server-id: central | |
| server-username: MAVEN_USERNAME | |
| server-password: MAVEN_PASSWORD | |
| - name: Get workspace version | |
| id: version | |
| run: | | |
| VERSION=$(grep -m1 '^version' Cargo.toml | sed 's/.*"\(.*\)"/\1/') | |
| echo "version=$VERSION" >> "$GITHUB_OUTPUT" | |
| - name: Check version on Maven Central | |
| id: check | |
| run: | | |
| VERSION="${{ steps.version.outputs.version }}" | |
| if curl -sf "https://search.maven.org/solrsearch/select?q=g:io.iscc+AND+a:iscc-lib+AND+v:$VERSION&rows=1&wt=json" | grep -q '"numFound":1'; then | |
| echo "Version $VERSION already published to Maven Central, skipping" | |
| echo "skip=true" >> "$GITHUB_OUTPUT" | |
| else | |
| echo "skip=false" >> "$GITHUB_OUTPUT" | |
| fi | |
| - name: Download JNI artifacts | |
| if: steps.check.outputs.skip != 'true' | |
| uses: actions/download-artifact@v4 | |
| with: | |
| pattern: jni-* | |
| path: jni-staging | |
| - name: Copy native libraries to resources | |
| if: steps.check.outputs.skip != 'true' | |
| run: | | |
| for dir in jni-staging/jni-*/; do | |
| native_dir=$(basename "$dir" | sed 's/^jni-//') | |
| dest="crates/iscc-jni/java/src/main/resources/META-INF/native/$native_dir" | |
| mkdir -p "$dest" | |
| cp "$dir"* "$dest/" | |
| done | |
| - name: Import GPG key | |
| if: steps.check.outputs.skip != 'true' | |
| run: | | |
| echo "${{ secrets.MAVEN_GPG_PRIVATE_KEY }}" | gpg --batch --import || true | |
| gpg --list-secret-keys --keyid-format LONG | grep -q "sec" || { echo "GPG key import failed"; exit 1; } | |
| - name: Publish to Maven Central | |
| if: steps.check.outputs.skip != 'true' | |
| run: mvn deploy -Prelease -DskipTests -f crates/iscc-jni/java/pom.xml | |
| env: | |
| MAVEN_USERNAME: ${{ secrets.MAVEN_USERNAME }} | |
| MAVEN_PASSWORD: ${{ secrets.MAVEN_PASSWORD }} | |
| MAVEN_GPG_PASSPHRASE: ${{ secrets.MAVEN_GPG_PASSPHRASE }} | |
| # Build FFI shared/static libraries for C/C++ consumers | |
| build-ffi: | |
| name: Build FFI (${{ matrix.target }}) | |
| if: startsWith(github.ref, 'refs/tags/v') || inputs.ffi | |
| runs-on: ${{ matrix.os }} | |
| strategy: | |
| matrix: | |
| include: | |
| - os: ubuntu-latest | |
| target: x86_64-unknown-linux-gnu | |
| lib-shared: libiscc_ffi.so | |
| lib-static: libiscc_ffi.a | |
| - os: ubuntu-latest | |
| target: aarch64-unknown-linux-gnu | |
| lib-shared: libiscc_ffi.so | |
| lib-static: libiscc_ffi.a | |
| - os: macos-14 | |
| target: aarch64-apple-darwin | |
| lib-shared: libiscc_ffi.dylib | |
| lib-static: libiscc_ffi.a | |
| - os: macos-14 | |
| target: x86_64-apple-darwin | |
| lib-shared: libiscc_ffi.dylib | |
| lib-static: libiscc_ffi.a | |
| - os: windows-latest | |
| target: x86_64-pc-windows-msvc | |
| lib-shared: iscc_ffi.dll | |
| lib-static: iscc_ffi.lib | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: dtolnay/rust-toolchain@stable | |
| with: | |
| targets: ${{ matrix.target }} | |
| - uses: Swatinem/rust-cache@v2 | |
| - name: Install cross-compiler | |
| if: matrix.target == 'aarch64-unknown-linux-gnu' | |
| run: | | |
| sudo apt-get update | |
| sudo apt-get install -y gcc-aarch64-linux-gnu | |
| - name: Build FFI library | |
| run: cargo build -p iscc-ffi --release --target ${{ matrix.target }} | |
| env: | |
| CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER: aarch64-linux-gnu-gcc | |
| - name: Get workspace version | |
| id: version | |
| shell: bash | |
| run: | | |
| VERSION=$(grep -m1 '^version' Cargo.toml | sed 's/.*"\(.*\)"/\1/') | |
| echo "version=$VERSION" >> "$GITHUB_OUTPUT" | |
| - name: Stage artifacts (Unix) | |
| if: runner.os != 'Windows' | |
| run: | | |
| VERSION="${{ steps.version.outputs.version }}" | |
| TARGET="${{ matrix.target }}" | |
| DIR="iscc-ffi-v${VERSION}-${TARGET}" | |
| mkdir -p "$DIR" | |
| cp "target/${TARGET}/release/${{ matrix.lib-shared }}" "$DIR/" | |
| cp "target/${TARGET}/release/${{ matrix.lib-static }}" "$DIR/" | |
| cp crates/iscc-ffi/include/iscc.h "$DIR/" | |
| cp LICENSE "$DIR/" | |
| tar czf "${DIR}.tar.gz" "$DIR" | |
| - name: Stage artifacts (Windows) | |
| if: runner.os == 'Windows' | |
| shell: pwsh | |
| run: | | |
| $VERSION = "${{ steps.version.outputs.version }}" | |
| $TARGET = "${{ matrix.target }}" | |
| $DIR = "iscc-ffi-v${VERSION}-${TARGET}" | |
| New-Item -ItemType Directory -Path $DIR -Force | |
| Copy-Item "target/${TARGET}/release/iscc_ffi.dll" "$DIR/" | |
| Copy-Item "target/${TARGET}/release/iscc_ffi.dll.lib" "$DIR/" | |
| Copy-Item "target/${TARGET}/release/iscc_ffi.lib" "$DIR/" | |
| Copy-Item "crates/iscc-ffi/include/iscc.h" "$DIR/" | |
| Copy-Item "LICENSE" "$DIR/" | |
| Compress-Archive -Path "$DIR" -DestinationPath "${DIR}.zip" | |
| - name: Upload artifact | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: ffi-${{ matrix.target }} | |
| path: iscc-ffi-v*.* | |
| # Smoke test FFI shared library on Linux before publishing | |
| test-ffi: | |
| name: Test FFI library | |
| needs: [build-ffi] | |
| if: startsWith(github.ref, 'refs/tags/v') || inputs.ffi | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Download FFI artifact | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: ffi-x86_64-unknown-linux-gnu | |
| path: ffi-staging | |
| - name: Extract and test | |
| run: | | |
| tar xzf ffi-staging/*.tar.gz | |
| LIB_DIR=$(find . -maxdepth 1 -name 'iscc-ffi-v*' -type d) | |
| cc -o test_iscc crates/iscc-ffi/tests/test_iscc.c \ | |
| -I"$LIB_DIR" -L"$LIB_DIR" -liscc_ffi -lpthread -ldl -lm | |
| LD_LIBRARY_PATH="$LIB_DIR" ./test_iscc | |
| # Publish FFI tarballs to GitHub Releases | |
| publish-ffi: | |
| name: Publish FFI to GitHub Releases | |
| needs: [build-ffi, test-ffi] | |
| if: startsWith(github.ref, 'refs/tags/v') || inputs.ffi | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: write | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Get workspace version | |
| id: version | |
| run: | | |
| VERSION=$(grep -m1 '^version' Cargo.toml | sed 's/.*"\(.*\)"/\1/') | |
| echo "version=$VERSION" >> "$GITHUB_OUTPUT" | |
| - name: Download all FFI artifacts | |
| uses: actions/download-artifact@v4 | |
| with: | |
| pattern: ffi-* | |
| path: ffi-staging | |
| merge-multiple: true | |
| - name: List artifacts | |
| run: ls -lR ffi-staging/ | |
| - name: Upload release assets | |
| uses: softprops/action-gh-release@v2 | |
| with: | |
| tag_name: ${{ startsWith(github.ref, 'refs/tags/v') && github.ref_name || format('v{0}', steps.version.outputs.version) }} | |
| files: ffi-staging/iscc-ffi-v*.* | |
| # Build cross-platform precompiled Ruby gems via rb-sys-dock | |
| build-gem: | |
| name: Build gem (${{ matrix.platform }}) | |
| if: startsWith(github.ref, 'refs/tags/v') || inputs.rubygems | |
| runs-on: ubuntu-latest | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| platform: | |
| - x86_64-linux | |
| - aarch64-linux | |
| - x86_64-darwin | |
| - arm64-darwin | |
| - x64-mingw-ucrt | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: ruby/setup-ruby@v1 | |
| with: | |
| ruby-version: '3.3' | |
| bundler-cache: true | |
| working-directory: crates/iscc-rb | |
| - name: Cross-compile gem | |
| uses: oxidize-rb/actions/cross-gem@v1 | |
| with: | |
| platform: ${{ matrix.platform }} | |
| ruby-versions: 3.1, 3.2, 3.3 | |
| working-directory: crates/iscc-rb | |
| # Pin to 0.9.123 (Ruby 3.4 build tool). The 0.9.124 image has Ruby 4.0 | |
| # which breaks RbSys::ExtensionTask. Must match rb_sys in Gemfile.lock. | |
| tag: 0.9.123 | |
| - name: Upload gem | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: gem-${{ matrix.platform }} | |
| path: crates/iscc-rb/pkg/*-${{ matrix.platform }}.gem | |
| # Smoke test Ruby gem on Linux before publishing | |
| test-gem: | |
| name: Test Ruby gem | |
| needs: [build-gem] | |
| if: startsWith(github.ref, 'refs/tags/v') || inputs.rubygems | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: ruby/setup-ruby@v1 | |
| with: | |
| ruby-version: '3.3' | |
| - name: Download gem | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: gem-x86_64-linux | |
| path: gems | |
| - name: Install and test | |
| run: | | |
| gem install gems/*.gem | |
| ruby -e "require 'iscc_lib'; raise 'Conformance failed' unless IsccLib.conformance_selftest" | |
| # Publish precompiled Ruby gems and source gem to RubyGems.org | |
| publish-rubygems: | |
| name: Publish to RubyGems | |
| needs: [build-gem, test-gem] | |
| if: startsWith(github.ref, 'refs/tags/v') || inputs.rubygems | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: ruby/setup-ruby@v1 | |
| with: | |
| ruby-version: '3.3' | |
| - name: Get workspace version | |
| id: version | |
| run: | | |
| VERSION=$(grep -m1 '^version' Cargo.toml | sed 's/.*"\(.*\)"/\1/') | |
| echo "version=$VERSION" >> "$GITHUB_OUTPUT" | |
| - name: Check version on RubyGems | |
| id: check | |
| run: | | |
| VERSION="${{ steps.version.outputs.version }}" | |
| if curl -sf "https://rubygems.org/api/v1/versions/iscc-lib.json" | grep -q "\"number\":\"$VERSION\""; then | |
| echo "Version $VERSION already published to RubyGems, skipping" | |
| echo "skip=true" >> "$GITHUB_OUTPUT" | |
| else | |
| echo "skip=false" >> "$GITHUB_OUTPUT" | |
| fi | |
| - name: Download gem artifacts | |
| if: steps.check.outputs.skip != 'true' | |
| uses: actions/download-artifact@v4 | |
| with: | |
| pattern: gem-* | |
| path: gems | |
| merge-multiple: true | |
| - name: Build source gem | |
| if: steps.check.outputs.skip != 'true' | |
| run: gem build iscc-lib.gemspec | |
| working-directory: crates/iscc-rb | |
| - name: Publish gems | |
| if: steps.check.outputs.skip != 'true' | |
| run: | | |
| # Publish precompiled platform gems | |
| for gem in gems/*.gem; do | |
| echo "Publishing $gem" | |
| gem push "$gem" | |
| done | |
| # Publish source gem (fallback for platforms without precompiled gems) | |
| for gem in crates/iscc-rb/*.gem; do | |
| echo "Publishing $gem" | |
| gem push "$gem" | |
| done | |
| env: | |
| GEM_HOST_API_KEY: ${{ secrets.GEM_HOST_API_KEY }} |