mirror of
https://github.com/OpenHands/OpenHands.git
synced 2026-10-07 13:58:33 +08:00
chore: switch to tag-based release workflow with per-tier npm dist-tags (#996)
- create-release.yml now triggers on v* tag push (not PR merge).
The release branch is never merged to main; publishing is triggered
by pushing the tag directly to the rel-X.Y.Z branch.
- npm-publish.yml resolves the dist-tag from the version string:
alpha → alpha, beta → beta, rc → rc, stable → latest
Removes the temporary 'always publish as latest' workaround.
- release.md skill and AGENTS.md updated to document the new model.
Co-authored-by: openhands <openhands@all-hands.dev>
This commit is contained in:
committed by
GitHub
co-authored by
openhands
parent
14b1b1e8ad
commit
1d4fd1525c
+100
-148
@@ -1,6 +1,6 @@
|
||||
---
|
||||
name: release
|
||||
description: Guide the release process for @openhands/agent-canvas — version bump PR, E2E validation, merge with automatic tagging, and downstream npm/Docker publishing.
|
||||
description: Guide the release process for @openhands/agent-canvas — version bump on the release branch, QA, then tag to publish to npm and Docker.
|
||||
triggers:
|
||||
- release
|
||||
- new release
|
||||
@@ -11,191 +11,143 @@ triggers:
|
||||
|
||||
# Release Process for @openhands/agent-canvas
|
||||
|
||||
You are guiding a release of the `@openhands/agent-canvas` package. Follow these steps **in order**. Do NOT skip ahead — each step has a checkpoint where you must wait for the user.
|
||||
## Overview
|
||||
|
||||
## Step 1: Check Current Version and Ask the User
|
||||
Releases use a **long-lived release branch** model:
|
||||
|
||||
**IMPORTANT: You MUST complete this step and get explicit user confirmation before doing anything else.**
|
||||
1. A `rel-X.Y.Z` branch is created from `main` at the start of a release cycle.
|
||||
2. QA and fixes land on the branch (cherry-picked from main or landed directly).
|
||||
3. When ready, a tag (`vX.Y.Z-rc.1`, `vX.Y.Z`, etc.) is pushed to the branch.
|
||||
4. The tag push triggers all downstream workflows automatically.
|
||||
5. **The release branch is never merged back to main.**
|
||||
|
||||
First, read the current version from `package.json`:
|
||||
npm dist-tags by version tier:
|
||||
|
||||
| Version | Example | npm dist-tag | `npm install` resolves? |
|
||||
|---|---|---|---|
|
||||
| Alpha | `1.0.0-alpha.1` | `alpha` | `@alpha` only |
|
||||
| Beta | `1.0.0-beta.1` | `beta` | `@beta` only |
|
||||
| RC | `1.0.0-rc.1` | `rc` | `@rc` only |
|
||||
| Stable | `1.0.0` | `latest` | ✅ default |
|
||||
|
||||
---
|
||||
|
||||
## Step 1: Confirm the Release Branch Exists
|
||||
|
||||
The branch must be named `rel-X.Y.Z` (e.g. `rel-1.0.0`). Check:
|
||||
|
||||
```bash
|
||||
git branch -r | grep rel-
|
||||
```
|
||||
|
||||
If it doesn't exist yet, create it from main:
|
||||
|
||||
```bash
|
||||
git checkout main && git pull origin main
|
||||
git checkout -b rel-<X.Y.Z>
|
||||
git push -u origin rel-<X.Y.Z>
|
||||
```
|
||||
|
||||
**STOP HERE if the branch doesn't exist.** Ask the user to confirm the release series (e.g. `1.0.0`) before creating it.
|
||||
|
||||
---
|
||||
|
||||
## Step 2: Ensure `package.json` Version Is Set
|
||||
|
||||
The version in `package.json` must match the tag you're about to push.
|
||||
|
||||
Check the current version:
|
||||
|
||||
```bash
|
||||
node -p "require('./package.json').version"
|
||||
```
|
||||
|
||||
Then present the result to the user and suggest the next logical version. Use these rules to form your suggestion:
|
||||
- If the current version is a pre-release like `1.0.0-alpha.7`, suggest `1.0.0-alpha.8` (bump the last numeric segment).
|
||||
- If the current version is stable like `1.2.3`, suggest `1.2.4` (patch bump) but mention they can also do `1.3.0` (minor) or `2.0.0` (major).
|
||||
|
||||
**Version format**: This project uses semver with optional pre-release suffixes.
|
||||
- Pre-release examples: `1.0.0-alpha.8`, `1.0.0-beta.1`, `1.0.0-rc.1`
|
||||
- Stable examples: `1.0.0`, `1.1.0`, `2.0.0`
|
||||
|
||||
**STOP HERE.** Tell the user the current version, your suggested next version, and ask:
|
||||
|
||||
> The current version is `<current>`. I'd suggest bumping to `<suggested>`. What version would you like to release?
|
||||
|
||||
**Do not proceed to Step 2 until the user confirms a version.**
|
||||
|
||||
## Step 2: Create the Release PR
|
||||
|
||||
### 2a. Create the release branch
|
||||
|
||||
The branch **must** be named `rel-<version>` (e.g., `rel-1.0.0-alpha.8`). This naming convention is required — the `create-release.yml` workflow detects merged release PRs by matching the `rel-` branch prefix.
|
||||
If it needs updating (e.g. bumping from `1.0.0-alpha.8` to `1.0.0-rc.1`), update both `package.json` and `package-lock.json`:
|
||||
|
||||
```bash
|
||||
git checkout main
|
||||
git pull origin main
|
||||
git checkout -b rel-<version>
|
||||
git checkout rel-<X.Y.Z>
|
||||
git pull origin rel-<X.Y.Z>
|
||||
npm version <new-version> --no-git-tag-version
|
||||
git add package.json package-lock.json
|
||||
git commit -m "chore: bump version to <new-version>"
|
||||
git push
|
||||
```
|
||||
|
||||
### 2b. Bump the version
|
||||
|
||||
Update the version in **both** `package.json` and `package-lock.json`:
|
||||
**Also update version references in `README.md`** if the Docker image tag examples reference a specific version:
|
||||
|
||||
```bash
|
||||
npm version <version> --no-git-tag-version
|
||||
sed -i 's/ghcr.io\/openhands\/agent-canvas:[0-9]*\.[0-9]*\.[0-9]*[^ ]*/ghcr.io\/openhands\/agent-canvas:<new-version>/g' README.md
|
||||
git add README.md && git commit -m "docs: update README version to <new-version>" && git push
|
||||
```
|
||||
|
||||
This updates both files without creating a git tag (the tag is created automatically on merge).
|
||||
---
|
||||
|
||||
### 2c. Update version references in README.md
|
||||
## Step 3: Push the Tag
|
||||
|
||||
The `README.md` contains Docker image tags that reference a specific version (e.g., `ghcr.io/openhands/agent-canvas:<version>`). Update **all** version references in `README.md` to match the new release version:
|
||||
Confirm the branch is in the right state (CI green, QA done), then push the tag:
|
||||
|
||||
```bash
|
||||
# Find and replace the old version tag with the new one
|
||||
sed -i 's/ghcr.io\/openhands\/agent-canvas:[0-9]*\.[0-9]*\.[0-9]*[^ ]*/ghcr.io\/openhands\/agent-canvas:<version>/g' README.md
|
||||
git checkout rel-<X.Y.Z>
|
||||
git pull origin rel-<X.Y.Z>
|
||||
git tag v<version>
|
||||
git push origin v<version>
|
||||
```
|
||||
|
||||
Verify the change:
|
||||
Examples:
|
||||
- First release candidate: `git tag v1.0.0-rc.1 && git push origin v1.0.0-rc.1`
|
||||
- Subsequent RC: `git tag v1.0.0-rc.2 && git push origin v1.0.0-rc.2`
|
||||
- Full release: `git tag v1.0.0 && git push origin v1.0.0`
|
||||
|
||||
```bash
|
||||
git diff --stat
|
||||
# Should show: package.json, package-lock.json, and README.md changed
|
||||
```
|
||||
|
||||
### 2d. Commit and push
|
||||
|
||||
```bash
|
||||
git add package.json package-lock.json README.md
|
||||
git commit -m "chore: bump version to <version>"
|
||||
git push -u origin rel-<version>
|
||||
```
|
||||
|
||||
### 2e. Create the PR
|
||||
|
||||
Create the PR targeting `main` with the `e2e-tests` label:
|
||||
|
||||
```bash
|
||||
gh pr create \
|
||||
--title "chore: bump version to <version>" \
|
||||
--body "## Release v<version>
|
||||
|
||||
This PR bumps the version to **<version>** for release.
|
||||
|
||||
### Release Checklist
|
||||
- [x] Version bumped in package.json and package-lock.json
|
||||
- [x] Version references updated in README.md
|
||||
- [ ] CI passes (lint, test, build)
|
||||
- [ ] Visual snapshot tests pass
|
||||
- [ ] Mock-LLM E2E tests pass (triggered by \`e2e-tests\` label)
|
||||
- [ ] Review and approve
|
||||
|
||||
### What happens on merge
|
||||
When this PR is merged, the \`create-release.yml\` workflow will automatically:
|
||||
1. Create a GitHub release with tag \`v<version>\` and auto-generated notes
|
||||
2. The tag push triggers \`npm-publish.yml\` to publish to npm
|
||||
3. The tag push triggers \`docker.yml\` to build and push Docker images to GHCR" \
|
||||
--base main \
|
||||
--head "rel-<version>" \
|
||||
--label "e2e-tests"
|
||||
```
|
||||
|
||||
## Step 3: Wait for CI and E2E Tests
|
||||
|
||||
The following checks must pass before merging:
|
||||
|
||||
| Workflow | Trigger | What it checks |
|
||||
|---|---|---|
|
||||
| **CI** (`ci.yml`) | Every PR | Lint, unit tests, app build, library build |
|
||||
| **Snapshot Tests** (`snapshot-tests.yml`) | Every PR | Visual regression screenshots |
|
||||
| **Mock-LLM E2E Tests** (`mock-llm-e2e.yml`) | `e2e-tests` label | End-to-end tests with a mock LLM against a real agent-server |
|
||||
|
||||
Monitor the PR checks:
|
||||
|
||||
```bash
|
||||
gh pr checks <pr-number> --watch
|
||||
```
|
||||
|
||||
If the mock-LLM E2E tests fail, investigate the failure in the workflow artifacts. The `e2e-tests` label can be removed and re-added to re-trigger the workflow.
|
||||
|
||||
If snapshot tests show intentional changes (e.g., version string in the UI changed), add the `update-snapshots` label to acknowledge the changes.
|
||||
|
||||
## Step 4: Merge the PR
|
||||
|
||||
Once all checks pass and the PR is approved, merge it:
|
||||
|
||||
```bash
|
||||
gh pr merge <pr-number> --squash --delete-branch
|
||||
```
|
||||
|
||||
### Automatic tagging on merge
|
||||
|
||||
The `create-release.yml` workflow automatically runs when a PR from a `rel-*` branch is merged into `main`. It will:
|
||||
|
||||
1. **Extract the version** from the branch name (e.g., `rel-1.0.0-alpha.8` → `1.0.0-alpha.8`)
|
||||
2. **Create a GitHub release** with tag `v<version>` targeting the merge commit
|
||||
3. **Auto-generate release notes** from the commits since the previous release
|
||||
4. **Mark pre-release versions** (those containing a hyphen) as pre-releases
|
||||
|
||||
You do **not** need to manually create a tag or GitHub release.
|
||||
|
||||
### Downstream workflows triggered by the tag
|
||||
|
||||
The tag push (`v*`) automatically triggers:
|
||||
**The tag push is the release trigger.** Three workflows fire in parallel:
|
||||
|
||||
| Workflow | What it does |
|
||||
|---|---|
|
||||
| **npm-publish.yml** | Builds and publishes `@openhands/agent-canvas` to npm with provenance |
|
||||
| **docker.yml** | Builds and pushes multi-arch Docker images to `ghcr.io/openhands/agent-canvas` |
|
||||
| `create-release.yml` | Creates the GitHub Release object with auto-generated notes |
|
||||
| `npm-publish.yml` | Builds and publishes to npm with the correct dist-tag |
|
||||
| `docker.yml` | Builds and pushes multi-arch Docker images to GHCR |
|
||||
|
||||
### Verify the release
|
||||
---
|
||||
|
||||
After merging, verify the downstream workflows complete successfully:
|
||||
## Step 4: Verify the Release
|
||||
|
||||
```bash
|
||||
# Check the GitHub release was created
|
||||
# GitHub release
|
||||
gh release view v<version>
|
||||
|
||||
# Watch downstream workflow runs
|
||||
gh run list --workflow=npm-publish.yml --limit=1
|
||||
gh run list --workflow=docker.yml --limit=1
|
||||
# npm (allow ~2 min for publish to propagate)
|
||||
npm view @openhands/agent-canvas@<version>
|
||||
npm view @openhands/agent-canvas dist-tags # confirm correct dist-tag
|
||||
|
||||
# Docker
|
||||
docker pull ghcr.io/openhands/agent-canvas:<version>
|
||||
```
|
||||
|
||||
Confirm the package is available:
|
||||
- **npm**: `npm view @openhands/agent-canvas@<version>`
|
||||
- **Docker**: `docker pull ghcr.io/openhands/agent-canvas:<version>`
|
||||
Monitor workflow runs:
|
||||
|
||||
```bash
|
||||
gh run list --workflow=npm-publish.yml --limit=3
|
||||
gh run list --workflow=docker.yml --limit=3
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### E2E tests not triggering
|
||||
The `e2e-tests` label must be present on the PR. If you added it but tests didn't run, remove and re-add the label, or manually trigger the workflow from the Actions tab.
|
||||
|
||||
### Tag already exists
|
||||
If a tag `v<version>` already exists (e.g., from a previous failed attempt), the `create-release.yml` workflow will skip creation. Delete the existing release and tag first:
|
||||
### package.json version doesn't match the tag
|
||||
`npm-publish.yml` validates that `package.json` version equals the tag version and fails if they differ. Fix the version on the branch, push, then delete and re-push the tag:
|
||||
```bash
|
||||
gh release delete v<version> --yes
|
||||
git push origin :refs/tags/v<version>
|
||||
git push origin :refs/tags/v<version> # delete remote tag
|
||||
git tag -d v<version> # delete local tag
|
||||
# fix package.json, commit, push
|
||||
git tag v<version> && git push origin v<version>
|
||||
```
|
||||
|
||||
### npm publish failed
|
||||
The `npm-publish.yml` workflow validates that `package.json` version matches the tag version. If they don't match, the publish will fail. Ensure the version bump in Step 2b matches the branch name exactly.
|
||||
### GitHub release already exists
|
||||
`create-release.yml` skips silently if the release already exists. To recreate it:
|
||||
```bash
|
||||
gh release delete v<version> --yes
|
||||
```
|
||||
Then the workflow will re-create it on the next tag push (or run it manually from the Actions tab).
|
||||
|
||||
## Reference
|
||||
|
||||
This release process is modeled after the [OpenHands/software-agent-sdk release workflow](https://github.com/OpenHands/software-agent-sdk/blob/main/.github/workflows/README-RELEASE.md) for consistency across OpenHands projects. Key differences:
|
||||
- agent-canvas uses npm (not PyPI) for package publishing
|
||||
- agent-canvas uses `npm version` (not `make set-package-version`) for version bumping
|
||||
- agent-canvas release branches use `rel-<version>` naming (same as SDK)
|
||||
- Both repos use `create-release.yml` to auto-create GitHub releases on merge of `rel-*` PRs
|
||||
### npm publish failed mid-way
|
||||
Check the `npm-publish.yml` run logs. The dist-tag is resolved from the `package.json` version — if the version string contains `alpha`, `beta`, or `rc`, it uses the matching dist-tag; otherwise `latest`.
|
||||
|
||||
@@ -1,54 +1,46 @@
|
||||
---
|
||||
name: Create GitHub Release
|
||||
|
||||
# Automatically create a GitHub release when a release PR is merged into main.
|
||||
# This bridges the gap between merging the release PR and the downstream
|
||||
# workflows (npm-publish, docker) which trigger on tag push (v*).
|
||||
#
|
||||
# Modeled after OpenHands/software-agent-sdk's create-release.yml for consistency.
|
||||
# Automatically create a GitHub release object when a v* tag is pushed.
|
||||
# The tag is pushed manually to a rel-X.Y.Z release branch (never merged to main).
|
||||
# This workflow creates the GitHub Release; the same tag push also triggers
|
||||
# npm-publish.yml and docker.yml in parallel.
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: [closed]
|
||||
branches: [main]
|
||||
push:
|
||||
tags:
|
||||
- 'v*'
|
||||
|
||||
jobs:
|
||||
create-release:
|
||||
# Only run when a release PR is merged (not just closed)
|
||||
# and the branch follows the rel-X.Y.Z naming convention.
|
||||
if: >
|
||||
github.event.pull_request.merged == true &&
|
||||
startsWith(github.event.pull_request.head.ref, 'rel-')
|
||||
runs-on: ubuntu-24.04
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: read
|
||||
steps:
|
||||
- name: Extract version from branch name
|
||||
- name: Extract version from tag
|
||||
id: version
|
||||
env:
|
||||
PR_HEAD_REF: ${{ github.event.pull_request.head.ref }}
|
||||
run: |
|
||||
BRANCH="$PR_HEAD_REF"
|
||||
VERSION="${BRANCH#rel-}"
|
||||
TAG="${GITHUB_REF#refs/tags/}"
|
||||
VERSION="${TAG#v}"
|
||||
|
||||
# Accept semver with optional pre-release suffix (e.g. 1.0.0, 1.0.0-alpha.8)
|
||||
# Accept semver with optional pre-release suffix (e.g. 1.0.0, 1.0.0-rc.1)
|
||||
if ! [[ "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9.]+)?$ ]]; then
|
||||
echo "❌ Could not extract valid version from branch: $BRANCH"
|
||||
echo "❌ Could not extract valid version from tag: $TAG"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
|
||||
echo "tag=$TAG" >> "$GITHUB_OUTPUT"
|
||||
echo "📦 Version: $VERSION"
|
||||
|
||||
- name: Check release does not already exist
|
||||
id: check
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
VERSION: ${{ steps.version.outputs.version }}
|
||||
TAG: ${{ steps.version.outputs.tag }}
|
||||
run: |
|
||||
if gh release view "v${VERSION}" --repo "${{ github.repository }}" > /dev/null 2>&1; then
|
||||
echo "⚠️ Release v${VERSION} already exists, skipping"
|
||||
if gh release view "$TAG" --repo "${{ github.repository }}" > /dev/null 2>&1; then
|
||||
echo "⚠️ Release $TAG already exists, skipping"
|
||||
echo "exists=true" >> "$GITHUB_OUTPUT"
|
||||
else
|
||||
echo "exists=false" >> "$GITHUB_OUTPUT"
|
||||
@@ -69,12 +61,9 @@ jobs:
|
||||
- name: Create GitHub Release
|
||||
if: steps.check.outputs.exists == 'false'
|
||||
env:
|
||||
# Use a PAT (not GITHUB_TOKEN) so the resulting tag-push event
|
||||
# propagates to downstream workflows (npm-publish.yml, docker.yml).
|
||||
# Tags created via GITHUB_TOKEN are silently suppressed by GitHub
|
||||
# and will not trigger other workflow runs.
|
||||
GH_TOKEN: ${{ secrets.OPENHANDS_BOT_GITHUB_PAT_PUBLIC || secrets.GITHUB_TOKEN }}
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
VERSION: ${{ steps.version.outputs.version }}
|
||||
TAG: ${{ steps.version.outputs.tag }}
|
||||
PREV_TAG: ${{ steps.prev_tag.outputs.prev_tag }}
|
||||
run: |
|
||||
NOTES_START_FLAG=()
|
||||
@@ -82,34 +71,32 @@ jobs:
|
||||
NOTES_START_FLAG=(--notes-start-tag "$PREV_TAG")
|
||||
fi
|
||||
|
||||
# Detect pre-release versions (anything with a hyphen, e.g. 1.0.0-alpha.8)
|
||||
# Detect pre-release versions (anything with a hyphen, e.g. 1.0.0-rc.1)
|
||||
PRERELEASE_FLAG=()
|
||||
if [[ "$VERSION" == *-* ]]; then
|
||||
PRERELEASE_FLAG=(--prerelease)
|
||||
fi
|
||||
|
||||
gh release create "v${VERSION}" \
|
||||
gh release create "$TAG" \
|
||||
--repo "${{ github.repository }}" \
|
||||
--target "${{ github.event.pull_request.merge_commit_sha }}" \
|
||||
--title "v${VERSION}" \
|
||||
--title "$TAG" \
|
||||
--generate-notes \
|
||||
"${NOTES_START_FLAG[@]}" \
|
||||
"${PRERELEASE_FLAG[@]}"
|
||||
|
||||
echo "✅ Release v${VERSION} created!"
|
||||
echo "🔗 https://github.com/${{ github.repository }}/releases/tag/v${VERSION}"
|
||||
echo "✅ Release $TAG created!"
|
||||
echo "🔗 https://github.com/${{ github.repository }}/releases/tag/$TAG"
|
||||
|
||||
- name: Summary
|
||||
if: steps.check.outputs.exists == 'false'
|
||||
env:
|
||||
VERSION: ${{ steps.version.outputs.version }}
|
||||
TAG: ${{ steps.version.outputs.tag }}
|
||||
run: |
|
||||
echo "## ✅ Release v${VERSION} Created" >> "$GITHUB_STEP_SUMMARY"
|
||||
echo "## ✅ Release $TAG Created" >> "$GITHUB_STEP_SUMMARY"
|
||||
echo "" >> "$GITHUB_STEP_SUMMARY"
|
||||
echo "- **Tag**: v${VERSION}" >> "$GITHUB_STEP_SUMMARY"
|
||||
echo "- **Release**: https://github.com/${{ github.repository }}/releases/tag/v${VERSION}" >> "$GITHUB_STEP_SUMMARY"
|
||||
echo "- **Tag**: $TAG" >> "$GITHUB_STEP_SUMMARY"
|
||||
echo "- **Release**: https://github.com/${{ github.repository }}/releases/tag/$TAG" >> "$GITHUB_STEP_SUMMARY"
|
||||
echo "" >> "$GITHUB_STEP_SUMMARY"
|
||||
echo "### Triggered Downstream Workflows" >> "$GITHUB_STEP_SUMMARY"
|
||||
echo "The tag push \`v${VERSION}\` will automatically trigger:" >> "$GITHUB_STEP_SUMMARY"
|
||||
echo "Downstream workflows triggered by the same tag push:" >> "$GITHUB_STEP_SUMMARY"
|
||||
echo "- **npm-publish.yml** — publish \`@openhands/agent-canvas\` to npm" >> "$GITHUB_STEP_SUMMARY"
|
||||
echo "- **docker.yml** — build and push \`ghcr.io/openhands/agent-canvas\` Docker images" >> "$GITHUB_STEP_SUMMARY"
|
||||
|
||||
@@ -90,18 +90,29 @@ jobs:
|
||||
fi
|
||||
echo "✓ Version $PACKAGE_VERSION matches release tag"
|
||||
|
||||
# Temporary: publish all versions (including prerelease) as "latest"
|
||||
# until the first stable release (see cleanup issue #395).
|
||||
# Note: OIDC trusted-publishing tokens cover only the `npm publish`
|
||||
# call itself; a separate `npm dist-tag add` after publish fails
|
||||
# with E401, so we publish directly with --tag latest in one step.
|
||||
#
|
||||
# Named prerelease dist-tags (alpha/beta/rc) are intentionally NOT
|
||||
# published under this policy — only "latest" is used. This means
|
||||
# `npm install @openhands/agent-canvas@alpha` will not resolve to
|
||||
# any published version. When prerelease dist-tags need to be
|
||||
# re-introduced (issue #395 cleanup), update this step to publish
|
||||
# with a versioned dist-tag (e.g. --tag alpha) and only tag stable
|
||||
# releases as "latest".
|
||||
# Resolve the npm dist-tag from the version's pre-release identifier:
|
||||
# alpha → --tag alpha (e.g. 1.0.0-alpha.1)
|
||||
# beta → --tag beta (e.g. 1.0.0-beta.1)
|
||||
# rc → --tag rc (e.g. 1.0.0-rc.1)
|
||||
# stable → --tag latest (e.g. 1.0.0)
|
||||
# Note: OIDC trusted-publishing tokens cover only the `npm publish` call
|
||||
# itself; a separate `npm dist-tag add` would fail with E401, so the tag
|
||||
# is resolved and passed directly in one step.
|
||||
- name: Resolve npm dist-tag
|
||||
id: dist_tag
|
||||
run: |
|
||||
VERSION=$(node -p "require('./package.json').version")
|
||||
if [[ "$VERSION" == *-alpha* ]]; then
|
||||
DIST_TAG="alpha"
|
||||
elif [[ "$VERSION" == *-beta* ]]; then
|
||||
DIST_TAG="beta"
|
||||
elif [[ "$VERSION" == *-rc* ]]; then
|
||||
DIST_TAG="rc"
|
||||
else
|
||||
DIST_TAG="latest"
|
||||
fi
|
||||
echo "dist_tag=$DIST_TAG" >> "$GITHUB_OUTPUT"
|
||||
echo "📦 Version $VERSION → dist-tag: $DIST_TAG"
|
||||
|
||||
- name: Publish to npm with provenance
|
||||
run: npm publish --access public --provenance --tag latest
|
||||
run: npm publish --access public --provenance --tag ${{ steps.dist_tag.outputs.dist_tag }}
|
||||
|
||||
@@ -552,6 +552,6 @@ When adding code that needs a new string, decide up front which rule it falls un
|
||||
|
||||
- Spec files live under `specs/`. Spec IDs are stable — never renumber. Mark deprecated specs with ~~strikethrough~~. Tag implementation code and tests with `// @spec BM-002 — Short title` comments so specs are grep-able across the codebase (`grep -rn '@spec BM-' src/ __tests__/`). Place the comment on the line immediately above the relevant code block or test. When multiple tests cover the same spec, use `it.each` if the test structure is identical.
|
||||
|
||||
- Release automation: `.github/workflows/create-release.yml` automatically creates a GitHub release (with tag `v<version>`) when a PR from a `rel-*` branch is merged into `main`. This is modeled after the SDK's `create-release.yml`. The tag push then triggers `npm-publish.yml` and `docker.yml` downstream. The release skill (`.agents/skills/release.md`, keyword trigger: `release`) guides agents through the full process: version bump PR, `e2e-tests` label for mock-LLM E2E validation, merge with auto-tagging. Pre-release versions (containing a hyphen) are marked as pre-releases on the GitHub release.
|
||||
- Release automation: releases use a **long-lived release branch** model — a `rel-X.Y.Z` branch is created from `main`, QA/fixes land there, and publishing is triggered by pushing a `v*` tag directly to that branch (the branch is never merged back to main). Tag format determines the npm dist-tag: `alpha` → `alpha`, `beta` → `beta`, `rc` → `rc`, no suffix → `latest`. Three workflows fire in parallel on every `v*` tag push: `create-release.yml` (creates the GitHub Release object with auto-generated notes and marks pre-release for hyphenated versions), `npm-publish.yml` (builds and publishes to npm with the correct dist-tag), and `docker.yml` (builds multi-arch Docker images). The release skill (`.agents/skills/release.md`, keyword trigger: `release`) guides agents through the full process.
|
||||
|
||||
- Cloud conversation resume gating: when a cloud conversation is closed from the UI (`pauseCloudSandbox` is called), the conversation's `conversation_url` is NOT cleared -- it still points to the old sandbox host. `WebSocketProviderWrapper` must suppress the URL (pass `null` to `ConversationWebSocketProvider`) while `sandbox_status === "PAUSED"`, otherwise the WebSocket immediately tries the stale URL before the sandbox wakes. Symmetrically, `useActiveConversation`'s refetch interval must fast-poll (3 s) on both `!conversation_url` AND `sandbox_status === "PAUSED"` -- checking only the missing URL would leave the hook on the 30 s interval while the sandbox is resuming. The resume sequence: navigate -> sandbox PAUSED detected -> `resumeCloudSandbox` called (in `conversation.tsx`) -> fast-poll detects RUNNING -> `conversationUrl` unblocked -> WebSocket connects.
|
||||
|
||||
Reference in New Issue
Block a user