native release #15
Workflow file for this run
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: native release | |
| on: | |
| workflow_dispatch: | |
| inputs: | |
| mode: | |
| description: Stage assets, verify an unpublished draft, or publish and update Homebrew. | |
| required: true | |
| type: choice | |
| options: | |
| - preflight | |
| - draft | |
| - publish | |
| default: preflight | |
| concurrency: | |
| group: native-release-${{ github.ref }} | |
| cancel-in-progress: false | |
| permissions: | |
| contents: read | |
| jobs: | |
| validate: | |
| runs-on: ubuntu-24.04 | |
| outputs: | |
| version: ${{ steps.release-source.outputs.version }} | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - uses: actions/setup-node@v6 | |
| with: | |
| node-version: 24.15.0 | |
| - name: Validate release source | |
| id: release-source | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| IMMUTABLE_RELEASES_ENABLED: ${{ vars.IMMUTABLE_RELEASES_ENABLED }} | |
| run: | | |
| set -euo pipefail | |
| if [[ "$GITHUB_REF" != refs/tags/v* ]]; then | |
| echo "::error::Select a v<version> tag under 'Run workflow from'; branches cannot produce releases. Selected reference: $GITHUB_REF" | |
| exit 1 | |
| fi | |
| version="${GITHUB_REF#refs/tags/v}" | |
| package_version="$(node -p "require('./packages/cli/package.json').version")" | |
| if [ "$version" != "$package_version" ]; then | |
| echo "::error::Selected tag v$version does not match package version $package_version." | |
| exit 1 | |
| fi | |
| tag_commit="$(git rev-list -n 1 "v$version")" | |
| if [ "$tag_commit" != "$GITHUB_SHA" ]; then | |
| echo "::error::Selected tag v$version does not resolve to the checked-out commit." | |
| exit 1 | |
| fi | |
| if [ "$IMMUTABLE_RELEASES_ENABLED" != 'true' ]; then | |
| echo "::error::Immutable GitHub Releases must be enabled before publishing native artifacts." | |
| exit 1 | |
| fi | |
| if gh release view "v$version" >/dev/null 2>&1; then | |
| echo "::error::v$version already has a GitHub Release" | |
| exit 1 | |
| fi | |
| echo "version=$version" >> "$GITHUB_OUTPUT" | |
| macos: | |
| needs: validate | |
| uses: ./.github/workflows/macos-release.yml | |
| with: | |
| production: true | |
| version: ${{ needs.validate.outputs.version }} | |
| secrets: inherit | |
| linux: | |
| needs: validate | |
| permissions: | |
| attestations: write | |
| contents: read | |
| id-token: write | |
| uses: ./.github/workflows/linux-release.yml | |
| with: | |
| production: true | |
| version: ${{ needs.validate.outputs.version }} | |
| secrets: inherit | |
| windows: | |
| needs: validate | |
| permissions: | |
| contents: read | |
| id-token: write | |
| uses: ./.github/workflows/windows-release.yml | |
| with: | |
| publish: false | |
| sign: true | |
| tag: v${{ needs.validate.outputs.version }} | |
| secrets: inherit | |
| stage: | |
| needs: | |
| - validate | |
| - macos | |
| - linux | |
| - windows | |
| runs-on: ubuntu-24.04 | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - uses: pnpm/action-setup@v6 | |
| - uses: actions/setup-node@v6 | |
| with: | |
| node-version: 24.15.0 | |
| cache: pnpm | |
| - name: Install | |
| run: pnpm install --frozen-lockfile | |
| - name: Download macOS release stage | |
| uses: actions/download-artifact@v7 | |
| with: | |
| name: inflow-native-macos-${{ needs.validate.outputs.version }} | |
| path: dist/native/macos | |
| - name: Download Linux release stage | |
| uses: actions/download-artifact@v7 | |
| with: | |
| name: inflow-native-linux-${{ needs.validate.outputs.version }} | |
| path: dist/native/linux | |
| - name: Download Windows release stage | |
| uses: actions/download-artifact@v7 | |
| with: | |
| name: inflow-windows-signed-release | |
| path: dist/native/windows | |
| - name: Assemble and verify release assets | |
| env: | |
| VERSION: ${{ needs.validate.outputs.version }} | |
| run: | | |
| set -euo pipefail | |
| node scripts/assemble-native-release-assets.mjs \ | |
| dist/native/assets \ | |
| dist/native/macos \ | |
| dist/native/linux \ | |
| dist/native/windows | |
| node scripts/verify-native-release-assets.mjs dist/native/assets "$VERSION" | |
| - name: Upload verified release stage | |
| uses: actions/upload-artifact@v6 | |
| with: | |
| name: inflow-native-release-${{ needs.validate.outputs.version }} | |
| path: dist/native/assets | |
| if-no-files-found: error | |
| retention-days: 7 | |
| publish: | |
| if: inputs.mode != 'preflight' | |
| needs: | |
| - validate | |
| - stage | |
| runs-on: ubuntu-24.04 | |
| permissions: | |
| contents: write | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - uses: pnpm/action-setup@v6 | |
| - uses: actions/setup-node@v6 | |
| with: | |
| node-version: 24.15.0 | |
| cache: pnpm | |
| - name: Install | |
| run: pnpm install --frozen-lockfile | |
| - name: Download verified release stage | |
| uses: actions/download-artifact@v7 | |
| with: | |
| name: inflow-native-release-${{ needs.validate.outputs.version }} | |
| path: dist/native/assets | |
| - name: Verify release assets | |
| env: | |
| VERSION: ${{ needs.validate.outputs.version }} | |
| run: node scripts/verify-native-release-assets.mjs dist/native/assets "$VERSION" | |
| - name: Create draft release with complete asset set | |
| id: release | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| VERSION: ${{ needs.validate.outputs.version }} | |
| run: | | |
| set -euo pipefail | |
| existing_release_ids="$(gh api "repos/$GITHUB_REPOSITORY/releases" \ | |
| --jq ".[] | select(.tag_name == \"v$VERSION\") | .id")" | |
| if [ -n "$existing_release_ids" ]; then | |
| echo "::error::v$VERSION already has a GitHub Release" | |
| exit 1 | |
| fi | |
| gh release create "v$VERSION" dist/native/assets/* \ | |
| --draft \ | |
| --generate-notes \ | |
| --title "InFlow $VERSION" \ | |
| --verify-tag | |
| release_id="$(gh api "repos/$GITHUB_REPOSITORY/releases" \ | |
| --jq ".[] | select(.draft and .tag_name == \"v$VERSION\") | .id")" | |
| test -n "$release_id" | |
| test "$(printf '%s\n' "$release_id" | wc -l | tr -d ' ')" = '1' | |
| echo "release_id=$release_id" >> "$GITHUB_OUTPUT" | |
| - name: Verify draft release assets | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| VERSION: ${{ needs.validate.outputs.version }} | |
| run: | | |
| set -euo pipefail | |
| gh api "repos/$GITHUB_REPOSITORY/releases/${{ steps.release.outputs.release_id }}" > dist/native/release.json | |
| node scripts/verify-native-release-assets.mjs dist/native/assets "$VERSION" dist/native/release.json | |
| - name: Publish release | |
| if: inputs.mode == 'publish' | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| RELEASE_ID: ${{ steps.release.outputs.release_id }} | |
| run: gh api --method PATCH "repos/$GITHUB_REPOSITORY/releases/$RELEASE_ID" -F draft=false -f make_latest=true | |
| - name: Verify published immutable release | |
| if: inputs.mode == 'publish' | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| RELEASE_ID: ${{ steps.release.outputs.release_id }} | |
| VERSION: ${{ needs.validate.outputs.version }} | |
| run: | | |
| set -euo pipefail | |
| gh api "repos/$GITHUB_REPOSITORY/releases/$RELEASE_ID" > dist/native/release.json | |
| test "$(jq -r '.draft' dist/native/release.json)" = 'false' | |
| test "$(jq -r '.immutable' dist/native/release.json)" = 'true' | |
| test "$(jq -r '.tag_name' dist/native/release.json)" = "v$VERSION" | |
| node scripts/verify-native-release-assets.mjs dist/native/assets "$VERSION" dist/native/release.json | |
| - name: Remove unpublished draft | |
| if: failure() || inputs.mode == 'draft' | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| RELEASE_ID: ${{ steps.release.outputs.release_id }} | |
| run: | | |
| if [ -n "$RELEASE_ID" ] && [ "$(gh api "repos/$GITHUB_REPOSITORY/releases/$RELEASE_ID" --jq '.draft' 2>/dev/null)" = 'true' ]; then | |
| gh api --method DELETE "repos/$GITHUB_REPOSITORY/releases/$RELEASE_ID" | |
| fi | |
| homebrew: | |
| if: inputs.mode == 'publish' | |
| needs: | |
| - validate | |
| - publish | |
| runs-on: ubuntu-24.04 | |
| permissions: | |
| contents: read | |
| steps: | |
| - name: Download macOS release stage | |
| uses: actions/download-artifact@v7 | |
| with: | |
| name: inflow-native-macos-${{ needs.validate.outputs.version }} | |
| path: dist/native/macos | |
| - name: Resolve Homebrew Cask | |
| id: cask | |
| run: | | |
| set -euo pipefail | |
| cask="$(find dist/native/macos -type f -name inflow.rb -print -quit)" | |
| test -n "$cask" | |
| echo "path=$cask" >> "$GITHUB_OUTPUT" | |
| - name: Create Homebrew tap token | |
| id: homebrew-tap-token | |
| uses: actions/create-github-app-token@v3 | |
| with: | |
| client-id: ${{ vars.HOMEBREW_TAP_APP_CLIENT_ID }} | |
| private-key: ${{ secrets.HOMEBREW_TAP_APP_PRIVATE_KEY }} | |
| owner: inflowpayai | |
| repositories: homebrew-tap | |
| permission-contents: write | |
| - name: Update Homebrew tap | |
| env: | |
| CASK: ${{ steps.cask.outputs.path }} | |
| HOMEBREW_TAP_TOKEN: ${{ steps.homebrew-tap-token.outputs.token }} | |
| VERSION: ${{ needs.validate.outputs.version }} | |
| run: | | |
| set -euo pipefail | |
| tap="$RUNNER_TEMP/homebrew-tap" | |
| git clone "https://x-access-token:${HOMEBREW_TAP_TOKEN}@github.com/inflowpayai/homebrew-tap.git" "$tap" | |
| cp "$CASK" "$tap/Casks/inflow.rb" | |
| git -C "$tap" config user.name github-actions | |
| git -C "$tap" config user.email github-actions@github.com | |
| git -C "$tap" add Casks/inflow.rb | |
| if git -C "$tap" diff --cached --quiet; then | |
| exit 0 | |
| fi | |
| git -C "$tap" commit -m "Update inflow to $VERSION" | |
| git -C "$tap" push origin HEAD:main |