Release #8
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: | |
| workflow_dispatch: | |
| inputs: | |
| bump: | |
| description: Version bump to release | |
| type: choice | |
| required: true | |
| options: | |
| - patch | |
| - minor | |
| - major | |
| concurrency: | |
| group: release-${{ github.ref_name }} | |
| cancel-in-progress: false | |
| permissions: | |
| contents: write | |
| jobs: | |
| publish: | |
| name: Publish crate | |
| if: ${{ github.ref == 'refs/heads/main' }} | |
| runs-on: ubuntu-latest | |
| environment: Production | |
| outputs: | |
| crate_name: ${{ steps.version.outputs.crate_name }} | |
| next_version: ${{ steps.version.outputs.next_version }} | |
| tag: ${{ steps.version.outputs.tag }} | |
| tinybus_version: ${{ steps.version.outputs.tinybus_version }} | |
| steps: | |
| - uses: actions/checkout@v7 | |
| with: | |
| fetch-depth: 0 | |
| submodules: true | |
| - uses: dtolnay/rust-toolchain@stable | |
| with: | |
| components: rustfmt, clippy | |
| - uses: taiki-e/install-action@cargo-llvm-cov | |
| - uses: Swatinem/rust-cache@v2 | |
| - name: Check formatting | |
| run: cargo fmt --all -- --check | |
| - name: Clippy | |
| run: cargo clippy --all-targets --all-features -- -D warnings | |
| - name: Test | |
| run: cargo test --all-features | |
| - name: Require 90% line coverage in every source file | |
| run: .github/scripts/check-file-coverage.sh 90 target/coverage.json | |
| - name: Build documentation | |
| env: | |
| RUSTDOCFLAGS: -D warnings | |
| run: cargo doc --no-deps --all-features | |
| - name: Compute next version | |
| id: version | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| metadata="$(cargo metadata --format-version 1 --no-deps)" | |
| crate_name="tinydocs" | |
| current_version="$(jq -r '.packages[] | select(.name == "tinydocs") | .version' <<< "$metadata")" | |
| tinybus_version="$( | |
| cargo metadata \ | |
| --manifest-path vendor/tinybus/Cargo.toml \ | |
| --format-version 1 \ | |
| --no-deps \ | |
| | jq -r '.packages[] | select(.name == "tinybus") | .version' | |
| )" | |
| if [[ -z "$current_version" || "$current_version" == "null" ]]; then | |
| echo "Could not resolve the current crate version" >&2 | |
| exit 1 | |
| fi | |
| if [[ -z "$tinybus_version" || "$tinybus_version" == "null" ]]; then | |
| echo "Could not resolve the TinyBus version" >&2 | |
| exit 1 | |
| fi | |
| IFS=. read -r major minor patch <<< "$current_version" | |
| case "${{ inputs.bump }}" in | |
| major) | |
| major=$((major + 1)) | |
| minor=0 | |
| patch=0 | |
| ;; | |
| minor) | |
| minor=$((minor + 1)) | |
| patch=0 | |
| ;; | |
| patch) | |
| patch=$((patch + 1)) | |
| ;; | |
| *) | |
| echo "Unsupported bump: ${{ inputs.bump }}" >&2 | |
| exit 1 | |
| ;; | |
| esac | |
| next_version="${major}.${minor}.${patch}" | |
| tag="v${next_version}" | |
| git fetch --tags origin | |
| if git rev-parse --verify --quiet "refs/tags/${tag}"; then | |
| echo "Tag ${tag} already exists" >&2 | |
| exit 1 | |
| fi | |
| { | |
| echo "crate_name=${crate_name}" | |
| echo "current_version=${current_version}" | |
| echo "next_version=${next_version}" | |
| echo "tag=${tag}" | |
| echo "tinybus_version=${tinybus_version}" | |
| } >> "$GITHUB_OUTPUT" | |
| - name: Update crate version | |
| env: | |
| CRATE_NAME: ${{ steps.version.outputs.crate_name }} | |
| NEXT_VERSION: ${{ steps.version.outputs.next_version }} | |
| run: | | |
| set -euo pipefail | |
| perl -0pi -e 's/(\[package\][\s\S]*?\nversion = ")[^"]+(")/$1$ENV{NEXT_VERSION}$2/' Cargo.toml | |
| perl -0pi -e 's/(\[package\][\s\S]*?\nversion = ")[^"]+(")/$1$ENV{NEXT_VERSION}$2/' crates/tinydocs-module/Cargo.toml | |
| cargo update -p "$CRATE_NAME" --precise "$NEXT_VERSION" | |
| - name: Commit version bump and tag | |
| env: | |
| RELEASE_TAG: ${{ steps.version.outputs.tag }} | |
| run: | | |
| set -euo pipefail | |
| git config user.name "github-actions[bot]" | |
| git config user.email "41898282+github-actions[bot]@users.noreply.github.com" | |
| git add Cargo.toml Cargo.lock crates/tinydocs-module/Cargo.toml | |
| git commit -m "Release ${RELEASE_TAG}" | |
| git tag -a "${RELEASE_TAG}" -m "Release ${RELEASE_TAG}" | |
| - name: Package crate | |
| run: cargo package --locked --package tinydocs | |
| - name: Package TinyBus source and module SDK | |
| run: | | |
| set -euo pipefail | |
| tinybus_revision="$(git -C vendor/tinybus rev-parse --short=12 HEAD)" | |
| git -C vendor/tinybus archive \ | |
| --format=tar.gz \ | |
| --prefix="tinybus-${tinybus_revision}/" \ | |
| --output="$PWD/target/package/tinybus-source-${tinybus_revision}.tar.gz" \ | |
| HEAD | |
| - name: Upload source packages | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: source-packages | |
| path: | | |
| target/package/${{ steps.version.outputs.crate_name }}-${{ steps.version.outputs.next_version }}.crate | |
| target/package/tinybus-source-*.tar.gz | |
| if-no-files-found: error | |
| - name: Push release commit and tag | |
| env: | |
| RELEASE_TAG: ${{ steps.version.outputs.tag }} | |
| run: | | |
| set -euo pipefail | |
| git push origin "HEAD:${GITHUB_REF_NAME}" | |
| git push origin "${RELEASE_TAG}" | |
| native-bundles: | |
| name: Module bundle (${{ matrix.id }}) | |
| needs: publish | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| - id: ubuntu-22.04-x86_64 | |
| os: ubuntu-22.04 | |
| target: x86_64-unknown-linux-gnu | |
| - id: ubuntu-22.04-arm64 | |
| os: ubuntu-22.04-arm | |
| target: aarch64-unknown-linux-gnu | |
| - id: ubuntu-24.04-x86_64 | |
| os: ubuntu-24.04 | |
| target: x86_64-unknown-linux-gnu | |
| - id: ubuntu-24.04-arm64 | |
| os: ubuntu-24.04-arm | |
| target: aarch64-unknown-linux-gnu | |
| - id: macos-15-x86_64 | |
| os: macos-15-intel | |
| target: x86_64-apple-darwin | |
| - id: macos-15-arm64 | |
| os: macos-15 | |
| target: aarch64-apple-darwin | |
| - id: macos-26-x86_64 | |
| os: macos-26-intel | |
| target: x86_64-apple-darwin | |
| - id: macos-26-arm64 | |
| os: macos-26 | |
| target: aarch64-apple-darwin | |
| - id: windows-2022-x86_64 | |
| os: windows-2022 | |
| target: x86_64-pc-windows-msvc | |
| - id: windows-2025-x86_64 | |
| os: windows-2025 | |
| target: x86_64-pc-windows-msvc | |
| - id: windows-11-arm64 | |
| os: windows-11-arm | |
| target: aarch64-pc-windows-msvc | |
| runs-on: ${{ matrix.os }} | |
| steps: | |
| - uses: actions/checkout@v7 | |
| with: | |
| ref: ${{ needs.publish.outputs.tag }} | |
| persist-credentials: false | |
| submodules: true | |
| - uses: dtolnay/rust-toolchain@stable | |
| - uses: Swatinem/rust-cache@v2 | |
| - name: Verify native Rust target | |
| shell: bash | |
| env: | |
| EXPECTED_TARGET: ${{ matrix.target }} | |
| run: | | |
| set -euo pipefail | |
| actual_target="$(rustc -vV | sed -n 's/^host: //p')" | |
| [[ "$actual_target" == "$EXPECTED_TARGET" ]] | |
| - name: Build installable module | |
| run: cargo build --locked --release --package tinydocs-module | |
| - name: Assemble Unix module package | |
| if: ${{ runner.os != 'Windows' }} | |
| id: unix_package | |
| shell: bash | |
| env: | |
| BUNDLE_ID: ${{ matrix.id }} | |
| VERSION: ${{ needs.publish.outputs.next_version }} | |
| run: | | |
| set -euo pipefail | |
| library_name="tinydocs_module" | |
| case "$RUNNER_OS" in | |
| Linux) module="target/release/lib${library_name}.so" ;; | |
| macOS) module="target/release/lib${library_name}.dylib" ;; | |
| *) echo "unsupported Unix runner: ${RUNNER_OS}" >&2; exit 1 ;; | |
| esac | |
| package_name="tinydocs-module-${VERSION}-${BUNDLE_ID}" | |
| package_root="dist/${package_name}" | |
| mkdir -p "$package_root" | |
| install -m 755 "$module" "$package_root/" | |
| install -m 644 LICENSE README.md docs/specs/tinybus-module.md "$package_root/" | |
| module_name="$(basename "$module")" | |
| module_hash="$(sha256sum "$package_root/$module_name" | awk '{print $1}')" | |
| printf '"%s" = "%s"\n' "$module_name" "$module_hash" \ | |
| > "$package_root/modules.toml" | |
| tar -C "$package_root" -czf "dist/${package_name}.tar.gz" . | |
| echo "archive=dist/${package_name}.tar.gz" >> "$GITHUB_OUTPUT" | |
| - name: Assemble Windows module package | |
| if: ${{ runner.os == 'Windows' }} | |
| id: windows_package | |
| shell: pwsh | |
| env: | |
| BUNDLE_ID: ${{ matrix.id }} | |
| VERSION: ${{ needs.publish.outputs.next_version }} | |
| run: | | |
| $ErrorActionPreference = 'Stop' | |
| $libraryName = 'tinydocs_module' | |
| $module = "target/release/$libraryName.dll" | |
| $packageName = "tinydocs-module-$env:VERSION-$env:BUNDLE_ID" | |
| $packageRoot = "dist/$packageName" | |
| New-Item -ItemType Directory -Force $packageRoot | Out-Null | |
| Copy-Item -LiteralPath $module, 'LICENSE', 'README.md' -Destination $packageRoot | |
| $hash = (Get-FileHash -LiteralPath $module -Algorithm SHA256).Hash.ToLowerInvariant() | |
| $moduleName = Split-Path -Leaf $module | |
| "`"$moduleName`" = `"$hash`"`n" | | |
| Set-Content -Path "$packageRoot/modules.toml" -Encoding utf8NoBOM | |
| Compress-Archive -Path "$packageRoot/*" -DestinationPath "dist/$packageName.zip" | |
| "archive=dist/$packageName.zip" >> $env:GITHUB_OUTPUT | |
| - name: Upload Unix module package | |
| if: ${{ runner.os != 'Windows' }} | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: tinydocs-module-${{ matrix.id }} | |
| path: ${{ steps.unix_package.outputs.archive }} | |
| if-no-files-found: error | |
| - name: Upload Windows module package | |
| if: ${{ runner.os == 'Windows' }} | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: tinydocs-module-${{ matrix.id }} | |
| path: ${{ steps.windows_package.outputs.archive }} | |
| if-no-files-found: error | |
| github-release: | |
| name: Create GitHub release | |
| needs: | |
| - publish | |
| - native-bundles | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Download workflow artifacts | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| run: | | |
| set -euo pipefail | |
| mkdir -p release-assets | |
| mapfile -t artifact_ids < <( | |
| gh api --paginate \ | |
| "/repos/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}/artifacts" \ | |
| --jq '.artifacts[] | select(.expired == false) | .id' | |
| ) | |
| if [[ ${#artifact_ids[@]} -eq 0 ]]; then | |
| echo "no unexpired workflow artifacts found" >&2 | |
| exit 1 | |
| fi | |
| for artifact_id in "${artifact_ids[@]}"; do | |
| archive="$(mktemp)" | |
| gh api \ | |
| "/repos/${GITHUB_REPOSITORY}/actions/artifacts/${artifact_id}/zip" \ | |
| > "$archive" | |
| unzip -q "$archive" -d release-assets | |
| rm -f "$archive" | |
| done | |
| - uses: actions/checkout@v7 | |
| with: | |
| ref: ${{ needs.publish.outputs.tag }} | |
| persist-credentials: false | |
| submodules: true | |
| - uses: dtolnay/rust-toolchain@stable | |
| - name: Create release checksum manifest with TinyBus | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| shopt -s nullglob | |
| mapfile -t assets < <( | |
| find release-assets -type f \ | |
| \( -name 'tinydocs-module-*.tar.gz' -o -name 'tinydocs-module-*.zip' \) \ | |
| | sort | |
| ) | |
| if [[ ${#assets[@]} -ne 11 ]]; then | |
| echo "expected 11 module archives, found ${#assets[@]}" >&2 | |
| exit 1 | |
| fi | |
| checksum_args=() | |
| for asset in "${assets[@]}"; do checksum_args+=(--path "$asset"); done | |
| cargo run --manifest-path vendor/tinybus/Cargo.toml --locked \ | |
| --package tinybus --all-features --bin tinybus -- \ | |
| modules checksum "${checksum_args[@]}" --output release-assets/checksum.toml | |
| - name: Create release and upload assets | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| RELEASE_TAG: ${{ needs.publish.outputs.tag }} | |
| REPOSITORY: ${{ github.repository }} | |
| run: | | |
| set -euo pipefail | |
| mapfile -t release_files < <(find release-assets -type f | sort) | |
| gh release create "$RELEASE_TAG" "${release_files[@]}" \ | |
| --repo "$REPOSITORY" \ | |
| --verify-tag \ | |
| --title "$RELEASE_TAG" \ | |
| --generate-notes | |
| - name: Verify the published module through TinyBus | |
| shell: bash | |
| env: | |
| RELEASE_TAG: ${{ needs.publish.outputs.tag }} | |
| REPOSITORY: ${{ github.repository }} | |
| VERSION: ${{ needs.publish.outputs.next_version }} | |
| run: | | |
| set -euo pipefail | |
| archive="tinydocs-module-${VERSION}-ubuntu-24.04-x86_64.tar.gz" | |
| release_url="https://github.com/${REPOSITORY}/releases/tag/${RELEASE_TAG}" | |
| sha256="$(sed -n "s/^\"${archive}\" = \"\([0-9a-f]\{64\}\)\"$/\1/p" release-assets/checksum.toml)" | |
| test -n "$sha256" | |
| cargo run --manifest-path vendor/tinybus/Cargo.toml --locked \ | |
| --package tinybus --all-features --example github_module_host -- \ | |
| "$release_url" "$archive" "$sha256" |