Merge pull request #94 from sampleXbro/develop #99
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: | |
| branches: [master] | |
| workflow_dispatch: | |
| inputs: | |
| tag: | |
| description: "Release tag to rebuild assets for (e.g., v0.13.0)" | |
| required: true | |
| type: string | |
| concurrency: | |
| group: release | |
| cancel-in-progress: false | |
| jobs: | |
| publish: | |
| if: github.event_name == 'push' | |
| runs-on: ubuntu-latest | |
| env: | |
| FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true | |
| permissions: | |
| contents: write | |
| pull-requests: write | |
| id-token: write | |
| outputs: | |
| published: ${{ steps.changesets.outputs.published }} | |
| version: ${{ steps.version.outputs.version }} | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| - uses: pnpm/action-setup@v4 | |
| with: | |
| version: 10 | |
| - uses: actions/setup-node@v4 | |
| with: | |
| node-version: 24 | |
| cache: pnpm | |
| registry-url: "https://registry.npmjs.org" | |
| - name: Install dependencies | |
| run: pnpm install --frozen-lockfile | |
| - name: Create release PR or publish | |
| id: changesets | |
| uses: changesets/action@v1 | |
| with: | |
| publish: pnpm release | |
| title: "chore: version packages" | |
| commit: "chore: version packages" | |
| createGithubReleases: false | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} | |
| - name: Push version tag | |
| if: steps.changesets.outputs.published == 'true' | |
| run: | | |
| VERSION=$(node -e "console.log(require('./package.json').version)") | |
| TAG="v$VERSION" | |
| # changesets/action runs `changeset publish`, which already creates this | |
| # tag locally for single-package repos. Skip the create if it exists, and | |
| # push idempotently — `git push origin <tag>` is a no-op when origin | |
| # already matches, and succeeds when origin is missing the tag (the case | |
| # that motivates this step in the first place). | |
| git rev-parse --verify --quiet "$TAG" >/dev/null || git tag "$TAG" | |
| git push origin "$TAG" | |
| - name: Export version | |
| if: steps.changesets.outputs.published == 'true' | |
| id: version | |
| run: | | |
| VERSION=$(node -e "console.log(require('./package.json').version)") | |
| echo "version=$VERSION" >> "$GITHUB_OUTPUT" | |
| release-assets: | |
| needs: publish | |
| # GitHub Actions skips dependent jobs when any `needs:` job is skipped or fails | |
| # unless the dependent job uses an explicit status check. `!cancelled()` overrides | |
| # that cascade so workflow_dispatch can rebuild assets even though `publish` is | |
| # skipped (it only runs on push). The `result == 'success'` guard keeps a failed | |
| # publish from accidentally triggering an asset build on push. | |
| if: | | |
| !cancelled() && ( | |
| (needs.publish.result == 'success' && needs.publish.outputs.published == 'true') || | |
| github.event_name == 'workflow_dispatch' | |
| ) | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: write | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| - uses: pnpm/action-setup@v4 | |
| with: | |
| version: 10 | |
| - uses: actions/setup-node@v4 | |
| with: | |
| node-version: 24 | |
| cache: pnpm | |
| - uses: oven-sh/setup-bun@v2 | |
| with: | |
| # Pinned: `latest` would silently change the compiled binary on a Bun release. | |
| # Bump intentionally — this is the tool that produces the distributed binaries. | |
| bun-version: "1.2" | |
| - name: Install dependencies | |
| run: pnpm install --frozen-lockfile | |
| - name: Determine version | |
| id: version | |
| # `inputs.tag` is collaborator-controlled (workflow_dispatch). Pass it via env: so the | |
| # value is a normal shell variable inside `run:`, not raw text spliced into the script | |
| # by Actions templating (CWE-78). Then validate the shape before writing to outputs. | |
| env: | |
| INPUT_TAG: ${{ inputs.tag }} | |
| PUBLISHED_VERSION: ${{ needs.publish.outputs.version }} | |
| run: | | |
| if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then | |
| VERSION="${INPUT_TAG#v}" | |
| else | |
| VERSION="$PUBLISHED_VERSION" | |
| fi | |
| case "$VERSION" in | |
| [!0-9]*|*[!a-zA-Z0-9.+-]*|"") | |
| echo "Invalid VERSION (expected semver-shaped, e.g. 0.13.0): '$VERSION'" >&2 | |
| exit 1 | |
| ;; | |
| esac | |
| echo "version=$VERSION" >> "$GITHUB_OUTPUT" | |
| echo "tag=v$VERSION" >> "$GITHUB_OUTPUT" | |
| - name: Build | |
| run: pnpm build | |
| - name: Pack npm tarball | |
| run: pnpm pack | |
| - name: Build Bun binaries | |
| run: | | |
| VERSION="${{ steps.version.outputs.version }}" | |
| mkdir -p dist-bin | |
| bun build --compile --minify --define "globalThis.__AGENTSMESH_VERSION__='$VERSION'" --target=bun-linux-x64 --outfile=dist-bin/agentsmesh-linux-x64 dist/cli.js | |
| bun build --compile --minify --define "globalThis.__AGENTSMESH_VERSION__='$VERSION'" --target=bun-linux-arm64 --outfile=dist-bin/agentsmesh-linux-arm64 dist/cli.js | |
| bun build --compile --minify --define "globalThis.__AGENTSMESH_VERSION__='$VERSION'" --target=bun-darwin-arm64 --outfile=dist-bin/agentsmesh-darwin-arm64 dist/cli.js | |
| bun build --compile --minify --define "globalThis.__AGENTSMESH_VERSION__='$VERSION'" --target=bun-darwin-x64 --outfile=dist-bin/agentsmesh-darwin-x64 dist/cli.js | |
| bun build --compile --minify --define "globalThis.__AGENTSMESH_VERSION__='$VERSION'" --target=bun-windows-x64 --outfile=dist-bin/agentsmesh-windows-x64.exe dist/cli.js | |
| - name: Generate SHA256SUMS | |
| run: | | |
| cd dist-bin && sha256sum agentsmesh-* > SHA256SUMS | |
| sha256sum ../agentsmesh-*.tgz | sed 's| \.\./||' >> SHA256SUMS | |
| cat SHA256SUMS | |
| - name: Verify CHANGELOG top entry header | |
| # Guard against release notes silently degrading to generic text: the | |
| # first `## ` heading in CHANGELOG.md must be a valid `## <semver>` entry | |
| # (what changesets emits). Fail loudly here rather than ship "Release vX". | |
| run: | | |
| TOP=$(grep -m1 '^## ' CHANGELOG.md || true) | |
| if ! printf '%s\n' "$TOP" | grep -Eq '^## [0-9]+\.[0-9]+\.[0-9]+'; then | |
| echo "CHANGELOG top entry is not a valid '## <version>' header: '$TOP'" >&2 | |
| exit 1 | |
| fi | |
| echo "CHANGELOG top entry OK: $TOP" | |
| - name: Extract release notes from CHANGELOG | |
| run: | | |
| VERSION="${{ steps.version.outputs.version }}" | |
| NOTES=$(awk "/^## $VERSION/{flag=1; next} /^## [0-9]/{flag=0} flag" CHANGELOG.md) | |
| if [ -z "$NOTES" ]; then | |
| NOTES="Release v$VERSION" | |
| fi | |
| echo "$NOTES" > release-notes.md | |
| - name: Create draft GitHub release | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| TAG="${{ steps.version.outputs.tag }}" | |
| gh release delete "$TAG" --yes 2>/dev/null || true | |
| gh release create "$TAG" \ | |
| --draft \ | |
| --title "v${{ steps.version.outputs.version }}" \ | |
| --notes-file release-notes.md \ | |
| agentsmesh-*.tgz \ | |
| dist-bin/agentsmesh-linux-x64 \ | |
| dist-bin/agentsmesh-linux-arm64 \ | |
| dist-bin/agentsmesh-darwin-arm64 \ | |
| dist-bin/agentsmesh-darwin-x64 \ | |
| dist-bin/agentsmesh-windows-x64.exe \ | |
| dist-bin/SHA256SUMS \ | |
| scripts/install.sh | |
| - name: Publish release | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| gh release edit "${{ steps.version.outputs.tag }}" --draft=false --latest | |
| update-tap: | |
| needs: [publish, release-assets] | |
| # Same cascade-skip override as release-assets, plus a hard gate on release-assets | |
| # success so we don't push a formula whose binaries failed to build. | |
| if: | | |
| !cancelled() && | |
| needs['release-assets'].result == 'success' && ( | |
| (needs.publish.result == 'success' && needs.publish.outputs.published == 'true') || | |
| github.event_name == 'workflow_dispatch' | |
| ) | |
| runs-on: ubuntu-latest | |
| # Explicit minimum: this job does not need write access to the source repo. | |
| # Pushing to the tap is authenticated via HOMEBREW_TAP_TOKEN, not GITHUB_TOKEN. | |
| permissions: | |
| contents: read | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Determine version | |
| id: version | |
| env: | |
| INPUT_TAG: ${{ inputs.tag }} | |
| PUBLISHED_VERSION: ${{ needs.publish.outputs.version }} | |
| run: | | |
| if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then | |
| VERSION="${INPUT_TAG#v}" | |
| else | |
| VERSION="$PUBLISHED_VERSION" | |
| fi | |
| case "$VERSION" in | |
| [!0-9]*|*[!a-zA-Z0-9.+-]*|"") | |
| echo "Invalid VERSION (expected semver-shaped, e.g. 0.13.0): '$VERSION'" >&2 | |
| exit 1 | |
| ;; | |
| esac | |
| echo "version=$VERSION" >> "$GITHUB_OUTPUT" | |
| - name: Compute npm tarball SHA256 | |
| id: sha | |
| env: | |
| VERSION: ${{ steps.version.outputs.version }} | |
| run: | | |
| URL="https://registry.npmjs.org/agentsmesh/-/agentsmesh-${VERSION}.tgz" | |
| for i in 1 2 3 4 5; do | |
| SHA=$(curl -fsSL "$URL" | sha256sum | awk '{print $1}') && break | |
| echo "Waiting for npm registry to serve v$VERSION (attempt $i)..." | |
| sleep 15 | |
| done | |
| echo "sha256=$SHA" >> "$GITHUB_OUTPUT" | |
| - name: Render formula | |
| env: | |
| VERSION: ${{ steps.version.outputs.version }} | |
| SHA256: ${{ steps.sha.outputs.sha256 }} | |
| run: | | |
| sed "s/{{VERSION}}/$VERSION/g; s/{{SHA256}}/$SHA256/g" homebrew/agentsmesh.rb.tmpl > agentsmesh.rb | |
| cat agentsmesh.rb | |
| # Use actions/checkout with a token: the credential ends up in the git credential | |
| # helper config, not embedded in the remote URL. `git push` from inside the checkout | |
| # then re-uses the helper. This avoids leaking HOMEBREW_TAP_TOKEN to .git/config | |
| # (which is read by every later git command in the same directory). | |
| - name: Checkout tap repo | |
| uses: actions/checkout@v4 | |
| with: | |
| repository: sampleXbro/homebrew-agentsmesh | |
| token: ${{ secrets.HOMEBREW_TAP_TOKEN }} | |
| path: tap | |
| - name: Update formula on tap | |
| env: | |
| VERSION: ${{ steps.version.outputs.version }} | |
| run: | | |
| mkdir -p tap/Formula | |
| cp agentsmesh.rb tap/Formula/agentsmesh.rb | |
| cd tap | |
| git config user.name "github-actions[bot]" | |
| git config user.email "github-actions[bot]@users.noreply.github.com" | |
| git add Formula/agentsmesh.rb | |
| git diff --cached --quiet && echo "No changes to commit" && exit 0 | |
| git commit -m "agentsmesh $VERSION" | |
| git push |