diff --git a/.github/workflows/cli-binaries.yaml b/.github/workflows/cli-binaries.yaml new file mode 100644 index 00000000..c96cdad3 --- /dev/null +++ b/.github/workflows/cli-binaries.yaml @@ -0,0 +1,46 @@ +name: cli-binaries +on: + workflow_call: + inputs: + tag: + description: CLI release tag to build binaries for, e.g. @cartesi/cli@2.0.0-alpha.36 + type: string + required: true +permissions: + contents: write +jobs: + upload: + name: Upload CLI binaries + runs-on: ubuntu-latest + steps: + # Build from the release tag, not the pushed commit, so the binaries + # match the version published on npm. + - name: Checkout + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + ref: refs/tags/${{ inputs.tag }} + + - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 + - uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0 + + # The CLI build depends on @cartesi/devnet, which needs forge/anvil + - name: Install Foundry + uses: foundry-rs/foundry-toolchain@908c540300062bd5a7e473851cdb4282204cee09 # v1.9.1 + with: + version: v1.4.3 + + - name: Install Dependencies + run: bun install --frozen-lockfile + + - name: Build + run: bun run build --filter @cartesi/cli + + - name: Upload binaries to the GitHub release + run: | + for f in cartesi-*; do tar -czf "$f.tar.gz" "$f"; done + # --clobber replaces assets left by a partial upload + gh release upload "$TAG" cartesi-*.tar.gz --clobber + working-directory: ./apps/cli/bin + env: + GH_TOKEN: ${{ github.token }} + TAG: ${{ inputs.tag }} diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 31a292ff..8d20f11c 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -16,9 +16,6 @@ jobs: release: name: Release runs-on: ubuntu-latest - outputs: - published: ${{ steps.changeset.outputs.published }} - publishedPackages: ${{ steps.changeset.outputs.publishedPackages }} steps: - name: Checkout Repo uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 @@ -70,20 +67,72 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - name: Release CLI binaries - if: ${{ steps.changeset.outputs.published == 'true' && contains(fromJSON(steps.changeset.outputs.publishedPackages).*.name, '@cartesi/cli') }} + artifacts: + name: Check release artifacts + needs: release + # Also run when `release` failed: publish can fail after tags were pushed, + # and the tags plus registry state are what decide here. + if: ${{ !cancelled() }} + runs-on: ubuntu-latest + outputs: + sdk_tag: ${{ steps.sdk.outputs.tag }} + cli_tag: ${{ steps.cli.outputs.tag }} + steps: + - name: Checkout Repo + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + + - name: Find missing SDK images + id: sdk + run: | + VERSION=$(jq -r .version packages/sdk/package.json) + TAG="@cartesi/sdk@${VERSION}" + # Only a tagged version has been released + if ! git ls-remote --exit-code --tags origin "refs/tags/${TAG}" > /dev/null; then + echo "${TAG} is not tagged, nothing to publish" + exit 0 + fi + for image in sdk rollups-runtime rollups-database; do + for registry in docker.io ghcr.io; do + if ! docker buildx imagetools inspect "${registry}/cartesi/${image}:${VERSION}" > /dev/null 2>&1; then + echo "Missing ${registry}/cartesi/${image}:${VERSION}" + echo "tag=${TAG}" >> "$GITHUB_OUTPUT" + exit 0 + fi + done + done + echo "All images for ${TAG} are published" + + - name: Find missing CLI binaries + id: cli run: | - for f in cartesi-*; do tar -czf "$f.tar.gz" "$f"; done - VERSION=$(jq -r '.[] | select(.name=="@cartesi/cli") | .version' <<< '${{ steps.changeset.outputs.publishedPackages }}') + VERSION=$(jq -r .version apps/cli/package.json) TAG="@cartesi/cli@${VERSION}" - gh release upload "$TAG" cartesi-*.tar.gz - working-directory: ./apps/cli/bin + # Count only the CLI tarballs, so another uploaded file can't hide a missing binary + if ! ASSETS=$(gh release view "${TAG}" --json assets --jq '[.assets[] | select(.name | startswith("cartesi-") and endswith(".tar.gz"))] | length' 2>/dev/null); then + echo "::warning::No GitHub release for ${TAG}, skipping CLI binaries" + exit 0 + fi + # One tarball per compile target in apps/cli/build.ts + if [ "${ASSETS}" -lt 4 ]; then + echo "${TAG} has ${ASSETS}/4 binaries" + echo "tag=${TAG}" >> "$GITHUB_OUTPUT" + fi env: GH_TOKEN: ${{ github.token }} build_sdk: name: Build SDK - needs: release - if: ${{ needs.release.outputs.published == 'true' && contains(fromJSON(needs.release.outputs.publishedPackages).*.name, '@cartesi/sdk') }} + needs: artifacts + if: ${{ needs.artifacts.outputs.sdk_tag != '' }} uses: ./.github/workflows/sdk.yaml + with: + ref: refs/tags/${{ needs.artifacts.outputs.sdk_tag }} secrets: inherit + + cli_binaries: + name: CLI binaries + needs: artifacts + if: ${{ needs.artifacts.outputs.cli_tag != '' }} + uses: ./.github/workflows/cli-binaries.yaml + with: + tag: ${{ needs.artifacts.outputs.cli_tag }} diff --git a/.github/workflows/sdk.yaml b/.github/workflows/sdk.yaml index 2747d576..60aa8ed3 100644 --- a/.github/workflows/sdk.yaml +++ b/.github/workflows/sdk.yaml @@ -1,6 +1,14 @@ name: sdk on: workflow_call: + inputs: + # Lets release.yaml build images from a version tag, so a recovery + # run publishes exactly what was released. + ref: + description: "Git ref to build the images from, e.g. refs/tags/@cartesi/sdk@0.12.0-alpha.42 (default: triggering commit)" + type: string + required: false + default: "" secrets: DOCKERHUB_USERNAME: required: true @@ -30,6 +38,8 @@ jobs: steps: - name: Checkout uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + ref: ${{ inputs.ref }} - name: Get package tag/version id: package-version @@ -61,6 +71,8 @@ jobs: steps: - name: Checkout uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + ref: ${{ inputs.ref }} - name: Download all docker-metadata artifacts uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1