diff --git a/.github/scripts/cargo-release-tags.sh b/.github/scripts/cargo-release-tags.sh new file mode 100755 index 0000000000..355040b60b --- /dev/null +++ b/.github/scripts/cargo-release-tags.sh @@ -0,0 +1,36 @@ +#!/usr/bin/env bash + +# This script emits all of the local cargo packages and their current versions +# in topological order, so that they can be published to crates.io in the +# correct order. The output is in the format "package-name/vVERSION", one per +# line. When tagging cargo packages, the tag is expected to match the format +# exactly. + +set -euo pipefail + +METADATA=$(cargo metadata --locked --format-version=1 --all-features --no-deps) + +QUERY=' +# store the input for multiple passes +. as $input | + +# first pass: collect the name and version for each local package +$input.packages[] | [{ key: .name, value: .version }] | +from_entries as $versions | + +# second pass: for each package, emit a single object for each (package, +# dependency) pair where the dependency is also a local package +$input.packages[] | { + name, + version, + dependency: (.dependencies[] | { + name, + version: $versions[.name] + } | select(.version != null)) +} | + +# join the dependency and package with a tab, then sort topologically +"\(.dependency.name)/v\(.dependency.version)\t\(.name)/v\(.version)" +' + +jq -r "${QUERY}" <<< "$METADATA" | tsort diff --git a/.github/workflows/attach-static-libs.yaml b/.github/workflows/attach-static-libs.yaml index d216daadcc..18073323e0 100644 --- a/.github/workflows/attach-static-libs.yaml +++ b/.github/workflows/attach-static-libs.yaml @@ -8,7 +8,9 @@ on: required: true push: tags: - - "*" + # Only tagging a release for the `firewood-ffi` crate can trigger a new + # release for the Go wrapper module. + - firewood-ffi/v* branches: - "main" pull_request: @@ -164,17 +166,10 @@ jobs: git commit -m "firewood ci ${{ github.sha }}: attach firewood static libs" git push -u origin ${{ steps.determine_branch.outputs.target_branch }} --force - if [[ "${{ github.ref_type }}" == "tag" ]]; then - # If the tag is a semantic version, prefix it with "ffi/" to ensure go get correctly - # fetches the submodule. Otherwise, use the tag name as is. - # Note: we explicitly ignore semantic versions with suffixes ie. v1.1.1-beta because - # go get treats them as non-semantic version tags. - # Ref: https://github.com/ava-labs/firewood/pull/991 - if [[ "${{ github.ref_name }}" =~ ^v?[0-9]+\.[0-9]+\.[0-9]+$ ]]; then - tag_name="ffi/${GITHUB_REF#refs/tags/}" - else - tag_name="${GITHUB_REF#refs/tags/}" - fi + ref_name="${GITHUB_REF#refs/tags/}" + if [[ "${{ github.ref_type }}" == "tag" && "$ref_name" == firewood-ffi/v* ]]; then + # If the tag is for `firewood-ffi/*`, rename to `ffi/*` before tagging the other repo + tag_name="${ref_name/firewood-ffi\//ffi/}" git tag -a "$tag_name" -m "firewood ci ${{ github.sha }}: attach firewood static libs" git push origin "refs/tags/$tag_name" fi diff --git a/.github/workflows/publish.yaml b/.github/workflows/publish.yaml index ad8264d1dc..71a5e3a41d 100644 --- a/.github/workflows/publish.yaml +++ b/.github/workflows/publish.yaml @@ -6,53 +6,45 @@ on: types: [published] jobs: - publish-firewood-crate: - name: firewood-lib + publish-single-firewood-crate: + name: publish-crate runs-on: ubuntu-latest - if: startsWith(github.event.release.tag_name, 'v') + # e.g., firewood/v0.2.0 or fireweood-storage/v0.2.0 + if: startsWith(github.event.release.tag_name, 'firewood') && contains(github.event.release.tag_name, '/v') steps: - uses: actions/checkout@v1 - - uses: dtolnay/rust-toolchain@stable - ## NOTE: keep these packages sorted in reverse topological order! - ## cargo tree --workspace -e all | grep firewood - - name: publish firewood-macros crate - continue-on-error: false - run: | - cargo login ${{ secrets.CARGO_TOKEN }} - cargo publish -p firewood-macros - # TODO(demosdemon): detect when version is bumped and only publish then - # - name: publish firewood-triehash crate - # continue-on-error: false - # run: | - # cargo login ${{ secrets.CARGO_TOKEN }} - # cargo publish -p firewood-triehash - - name: publish firewood-storage crate - continue-on-error: false - run: | - cargo login ${{ secrets.CARGO_TOKEN }} - cargo publish -p firewood-storage - - name: publish firewood crate - continue-on-error: false - run: | - cargo login ${{ secrets.CARGO_TOKEN }} - cargo publish -p firewood - - name: publish firewood-replay crate - continue-on-error: false - run: | - cargo login ${{ secrets.CARGO_TOKEN }} - cargo publish -p firewood-replay - - name: publish firewood-ffi crate - continue-on-error: false - run: | - cargo login ${{ secrets.CARGO_TOKEN }} - cargo publish -p firewood-ffi - - name: publish firewood-fwdctl crate - continue-on-error: false + - name: parse and validate tag + id: parse_tag run: | - cargo login ${{ secrets.CARGO_TOKEN }} - cargo publish -p firewood-fwdctl - - name: publish firewood-benchmark crate - continue-on-error: false - run: | - cargo login ${{ secrets.CARGO_TOKEN }} - cargo publish -p firewood-benchmark + set -exo pipefail + + TAG_REF="${GITHUB_REF#refs/tags/}" + + mapfile -t valid_tags < <(.github/scripts/cargo-release-tags.sh) + + for tag in "${valid_tags[@]}"; do + if [[ "$TAG_REF" == "$tag" ]]; then + echo "Tag $TAG_REF is valid, proceeding with publish" + echo "FOUND_TAG=$tag" >> "$GITHUB_OUTPUT" + echo "PACKAGE=${tag%%/*}" >> "$GITHUB_OUTPUT" + exit 0 + fi + done + + echo "FOUND_TAG=none" >> "$GITHUB_OUTPUT" + - uses: dtolnay/rust-toolchain@stable + if: steps.parse_tag.outputs.FOUND_TAG != 'none' + - name: dry-run publish crate + if: steps.parse_tag.outputs.FOUND_TAG != 'none' + env: + CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_TOKEN }} + # Will build the crate and verify its dependencies, but won't actually publish it. This is + # a minor safety check to make sure the crate is publishable before we attempt to publish it + # for real. + run: cargo publish ${{ steps.parse_tag.outputs.PACKAGE }} --locked --dry-run + - name: publish crate + if: steps.parse_tag.outputs.FOUND_TAG != 'none' + env: + CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_TOKEN }} + # --no-verify since we just did a dry-run + run: cargo publish ${{ steps.parse_tag.outputs.PACKAGE }} --locked --no-verify