singularity-forge/.github/workflows/prod-release.yml
2026-05-08 03:01:20 +02:00

177 lines
6.6 KiB
YAML

name: Prod Release
# Manual prod release. Click "Run workflow" in the Actions tab to cut @latest
# from main. Gated by the `prod` GitHub Environment approval before any
# publishing or commit-push side effects run.
on:
workflow_dispatch: {}
concurrency:
group: prod-release
cancel-in-progress: false
permissions:
contents: write
packages: write
pull-requests: write
jobs:
prod-release:
name: Production Release
runs-on: ubuntu-latest
environment: prod
steps:
- uses: actions/checkout@v6
with:
ref: main
fetch-depth: 0
token: ${{ secrets.RELEASE_PAT }}
- uses: actions/setup-node@v6
with:
node-version: '26.1'
registry-url: https://registry.npmjs.org
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Cache Next.js build
uses: actions/cache@v4
with:
path: web/.next/cache
key: nextjs-${{ runner.os }}-${{ hashFiles('web/package-lock.json') }}-${{ hashFiles('web/app/**', 'web/components/**', 'web/lib/**', 'web/hooks/**') }}
restore-keys: |
nextjs-${{ runner.os }}-${{ hashFiles('web/package-lock.json') }}-
nextjs-${{ runner.os }}-
- name: Run live LLM tests (optional)
continue-on-error: true
run: npm run test:live || echo "::warning::Live LLM tests failed — non-blocking, but worth investigating"
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
SF_LIVE_TESTS: "1"
- name: Generate changelog and determine version
id: release
run: |
OUTPUT=$(node scripts/generate-changelog.mjs)
echo "$OUTPUT" | jq .
echo "version=$(echo "$OUTPUT" | jq -r '.newVersion')" >> "$GITHUB_OUTPUT"
echo "$OUTPUT" | jq -r '.changelogEntry' > /tmp/changelog-entry.md
echo "$OUTPUT" | jq -r '.releaseNotes' > /tmp/release-notes.md
- name: Bump version and sync packages
env:
RELEASE_VERSION: ${{ steps.release.outputs.version }}
run: node scripts/bump-version.mjs "$RELEASE_VERSION"
- name: Validate package files after version bump
run: |
node -e "require('./package.json')" && \
node -e "require('./packages/pi-coding-agent/package.json')" && \
node -e "require('./pkg/package.json')" && \
echo "All package.json files are valid"
- name: Update CHANGELOG.md
run: node scripts/update-changelog.mjs /tmp/changelog-entry.md
- name: Commit and tag release
env:
RELEASE_VERSION: ${{ steps.release.outputs.version }}
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git add package.json package-lock.json web/package-lock.json CHANGELOG.md rust-engine/npm/*/package.json pkg/package.json packages/*/package.json
git commit -m "release: v${RELEASE_VERSION}"
git pull --rebase origin main
git tag "v${RELEASE_VERSION}"
- name: Build release
run: npm run build
- name: Publish release to npm @latest
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
RELEASE_VERSION: ${{ steps.release.outputs.version }}
run: |
OUTPUT=$(npm publish 2>&1) && echo "$OUTPUT" || {
if echo "$OUTPUT" | grep -q "cannot publish over the previously published"; then
echo "Version already published — promoting to latest"
npm dist-tag add "singularity-forge@${RELEASE_VERSION}" latest
else
echo "$OUTPUT"
exit 1
fi
}
- name: Push release commit and tag
env:
RELEASE_VERSION: ${{ steps.release.outputs.version }}
run: |
git push origin main
git push origin "v${RELEASE_VERSION}"
- name: Create GitHub Release
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
RELEASE_VERSION: ${{ steps.release.outputs.version }}
run: |
gh release create "v${RELEASE_VERSION}" \
--title "v${RELEASE_VERSION}" \
--notes-file /tmp/release-notes.md \
--latest
- name: Post to Discord
if: ${{ env.DISCORD_WEBHOOK != '' }}
env:
DISCORD_WEBHOOK: ${{ secrets.DISCORD_CHANGELOG_WEBHOOK }}
RELEASE_VERSION: ${{ steps.release.outputs.version }}
run: |
NOTES=$(cat /tmp/release-notes.md)
curl -s -X POST "$DISCORD_WEBHOOK" \
-H "Content-Type: application/json" \
-d "$(jq -n --arg c "**SF v${RELEASE_VERSION} Released**\n\n${NOTES}\n\n\`npm i singularity-forge@${RELEASE_VERSION}\`" '{content:$c}')"
# Docker publish disabled — no ghcr.io package configured yet
# - name: Log in to GHCR
# uses: docker/login-action@v4
# with:
# registry: ghcr.io
# username: ${{ github.actor }}
# password: ${{ secrets.GITHUB_TOKEN }}
#
# - name: Build and push release Docker image
# env:
# RELEASE_VERSION: ${{ steps.release.outputs.version }}
# run: |
# docker build --target runtime \
# -t ghcr.io/singularity-ng/singularity-forge:latest \
# -t "ghcr.io/singularity-ng/singularity-forge:${RELEASE_VERSION}" \
# .
# docker push "ghcr.io/singularity-ng/singularity-forge:${RELEASE_VERSION}"
# docker push ghcr.io/singularity-ng/singularity-forge:latest
- name: Open back-merge PR main→next if behind
env:
GH_TOKEN: ${{ secrets.RELEASE_PAT }}
RELEASE_VERSION: ${{ steps.release.outputs.version }}
run: |
if ! git ls-remote --exit-code --heads origin next >/dev/null 2>&1; then
echo "next branch does not exist yet; skipping back-merge"
exit 0
fi
git fetch origin next main
BEHIND=$(git rev-list --count origin/next..origin/main)
if [ "$BEHIND" -gt 0 ]; then
BRANCH="backmerge/main-to-next-v${RELEASE_VERSION}"
git checkout -B "$BRANCH" origin/main
git push origin "$BRANCH" --force-with-lease
gh pr create --base next --head "$BRANCH" \
--title "chore: back-merge main to next (v${RELEASE_VERSION})" \
--body "Sync release commit and version bump from main into next." || true
else
echo "next is up to date with main; no back-merge needed"
fi