Skip to content

Merge pull request #94 from sampleXbro/develop #99

Merge pull request #94 from sampleXbro/develop

Merge pull request #94 from sampleXbro/develop #99

Workflow file for this run

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