mirror of
https://github.com/OpenHands/OpenHands.git
synced 2026-10-06 14:33:11 +08:00
ci: adopt release-please via shared release-actions (#1496)
* ci: adopt release-please via shared release-actions
Standardize agent-canvas releases on the org-wide release-please
automation (OpenHands/release-actions), matching typescript-client and
OpenHands/OpenHands.
Adds the three caller workflows:
- release.yml release-please on push to main / release/**
- pr.yml Conventional-Commit title lint + type: labels
- release-ready.yml draft release PR -> "Ready for review" gate
(Slack alert to #proj-agent-canvas + optional tests)
Adds the release-please state files: release-please-config.json
(node, draft PRs, extra-files), .release-please-manifest.json,
.github/release.yml (changelog categories), version.txt. Manifest is
seeded at 1.0.0 (the current GA release, v1.0.0 shipped 2026-06-15).
extra-files keeps the non-package.json version pins in lockstep:
config/defaults.json ($.versions.agentCanvas) and the README docker
example. The node release-type already bumps package.json and
package-lock.json.
Corrects stale on-main version refs (package.json rc.6,
config/defaults.json + README rc.11) to the real released 1.0.0, so all
pins share a single source of truth.
Removes create-release.yml: release-please now owns tag + GitHub Release
creation. npm-publish.yml and docker.yml are unchanged - they trigger on
the v* tags release-please still produces (pushed with the release App
token so tag-triggered workflows fire).
Co-authored-by: smolpaws <engel@enyst.org>
* fix: sync README.windows.md docker tag + track it in release-please
The docs-version-sync test also checks README.windows.md, which still
pinned 1.0.0-rc.11. Update both its docker image refs to the released
1.0.0 and add it to release-please extra-files (with the
x-release-please-version annotation) so future bumps keep it in lockstep
alongside README.md and config/defaults.json.
Co-authored-by: smolpaws <engel@enyst.org>
* ci: pin v-prefix tagging and first-release start point
Two robustness fixes after confirming agent-canvas's release conventions:
- include-v-in-tag: true (explicit). agent-canvas tags are vX.Y.Z
(v1.0.0, v1.0.0-rc.12), and npm-publish.yml + docker.yml trigger on
push tags 'v*'. release-please's node default already adds the v, but
pin it explicitly so a default change can't silently drop the prefix
and break tag-triggered publishing. (Matches typescript-client, which
also ships v-prefixed tags; differs from OpenHands/OpenHands, which
sets include-v-in-tag:false for its no-v scheme.)
- last-release-sha pinned to the v1.0.0 release commit
(7b9c17e). v1.0.0 is a real, published release (2026-06-15; npm
latest). This tells release-please the first managed release starts
from there, so its first run scans only post-1.0.0 commits instead of
walking the whole history.
Co-authored-by: smolpaws <engel@enyst.org>
* ci: drop orphaned version.txt
Per AI review: version.txt would never be updated and nothing consumes
it. The README's "four state files" guidance assumes the `simple`
release-type (which release-actions itself uses, where version.txt is
the canonical version source). agent-canvas uses `release-type: node`,
where package.json is the source of truth — so version.txt is redundant
and not auto-bumped. The other node adopters (typescript-client,
OpenHands/OpenHands) ship no version.txt either. Remove it rather than
keep a file that silently goes stale.
Co-authored-by: smolpaws <engel@enyst.org>
* docs: document the release-please flow
---------
Co-authored-by: smolpaws <engel@enyst.org>
Co-authored-by: openhands <openhands@all-hands.dev>
Co-authored-by: hieptl <hieptl.developer@gmail.com>
This commit is contained in:
committed by
GitHub
co-authored by
smolpaws
openhands
hieptl
parent
5538b699a7
commit
4bd3baccde
+48
-124
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
name: release
|
name: release
|
||||||
description: Guide the release process for @openhands/agent-canvas — version bump on the release branch, QA, then tag to publish to npm and Docker.
|
description: Guide the release process for @openhands/agent-canvas — review the release-please draft PR, mark it ready, merge it; the tag push publishes to npm and Docker.
|
||||||
triggers:
|
triggers:
|
||||||
- release
|
- release
|
||||||
- new release
|
- new release
|
||||||
@@ -13,135 +13,71 @@ triggers:
|
|||||||
|
|
||||||
## Overview
|
## Overview
|
||||||
|
|
||||||
Releases use a **long-lived release branch** model:
|
Releases are **trunk-based and automated by release-please**, via the shared reusable
|
||||||
|
workflows in [`OpenHands/release-actions`](https://github.com/OpenHands/release-actions):
|
||||||
|
|
||||||
1. A `rel-X.Y.Z` branch is created from `main` at the start of a release cycle.
|
1. PRs merge to `main` with **Conventional Commit titles** (`feat`, `fix`, `perf`, `docs`, `chore`, `build`, `ci`, `refactor`, `style`, `test`, `revert`). `.github/workflows/pr.yml` lints the title and applies the matching `type:` label; squash merge uses the PR title as the commit message.
|
||||||
2. QA and fixes land on the branch (cherry-picked from main or landed directly).
|
2. On every push to `main`, `.github/workflows/release.yml` runs release-please, which maintains a **draft release PR** titled `chore(main): release X.Y.Z` accumulating everything merged since the last release.
|
||||||
3. When ready, a tag (`vX.Y.Z-rc.1`, `vX.Y.Z`, etc.) is pushed to the branch.
|
3. The release PR pre-stages **every version bump**: `package.json`, `package-lock.json`, `config/defaults.json` (`versions.agentCanvas`), and the Docker image pins in `README.md` / `README.windows.md` (lines annotated with `x-release-please-version`).
|
||||||
4. The tag push triggers all downstream workflows automatically.
|
4. Marking the release PR **Ready for review** is the explicit cut-a-release signal: `.github/workflows/release-ready.yml` notifies `#proj-agent-canvas` on Slack and labels the PR `release: ready`.
|
||||||
5. **The release branch is never merged back to main.**
|
5. Merging the release PR makes release-please push the `vX.Y.Z` tag (using the org release App token) and create the GitHub Release. The same tag push triggers `npm-publish.yml` (npm) and `docker.yml` (multi-arch GHCR images).
|
||||||
|
|
||||||
npm dist-tags by version tier:
|
The next version is derived from the conventional-commit types merged since the last release: `fix` → patch, `feat` → minor, any `!` suffix or `BREAKING CHANGE` footer → major. Other types (`docs`, `chore`, `refactor`, …) appear in the release notes but do not by themselves produce a release PR. Release notes are grouped by the `type:` labels via `.github/release.yml`; there is no `CHANGELOG.md` file — GitHub Releases are the changelog.
|
||||||
|
|
||||||
The workflow checks npm at publish time to see whether any full stable release (no pre-release suffix) has ever been published:
|
Configuration lives in `release-please-config.json` (version surfaces, draft PR) and `.release-please-manifest.json` (current released version). Maintenance releases for an older line use `release/**` branches (`release.yml` triggers there too).
|
||||||
|
|
||||||
**Before the first stable release** — all versions use `--tag latest`:
|
**Never commit to the release PR's branch by hand** — release-please owns it and force-pushes it on every push to `main`.
|
||||||
|
|
||||||
| Version | Example | npm dist-tag | `npm install` resolves? |
|
|
||||||
|---|---|---|---|
|
|
||||||
| Alpha | `1.0.0-alpha.1` | `latest` | ✅ default |
|
|
||||||
| Beta | `1.0.0-beta.1` | `latest` | ✅ default |
|
|
||||||
| RC | `1.0.0-rc.1` | `latest` | ✅ default |
|
|
||||||
| Stable | `1.0.0` | `latest` | ✅ default |
|
|
||||||
|
|
||||||
**After the first stable release** — pre-release versions revert to their own dist-tags:
|
|
||||||
|
|
||||||
| 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 |
|
|
||||||
|
|
||||||
This transition is automatic — no workflow changes are needed when the first stable version ships.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Step 1: Confirm the Release Branch Exists
|
## Step 1: Find the Release PR
|
||||||
|
|
||||||
The branch must be named `rel-X.Y.Z` (e.g. `rel-1.0.0`). Check:
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
git branch -r | grep rel-
|
gh pr list --state open --label "autorelease: pending"
|
||||||
```
|
```
|
||||||
|
|
||||||
If it doesn't exist yet, create it from main:
|
If no release PR is open, nothing releasable (`feat`/`fix`/breaking) has merged since the last release — or `release.yml` is failing on `main`:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
git checkout main && git pull origin main
|
gh run list --workflow=release.yml --limit=3
|
||||||
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
|
## Step 2: Review It
|
||||||
|
|
||||||
The version in `package.json` must match the tag you're about to push.
|
Open the release PR and confirm:
|
||||||
|
|
||||||
Check the current version:
|
- **The version** in the title matches expectations. It is computed from the merged commit types — if it looks wrong, check the conventional types of the PR titles merged since the last release. To force a specific version, merge a commit to `main` whose message contains a `Release-As: X.Y.Z` footer.
|
||||||
|
- **The staged bumps** cover all version surfaces (`package.json`, `package-lock.json`, `config/defaults.json`, `README.md`, `README.windows.md`) and the notes list the expected changes.
|
||||||
```bash
|
|
||||||
node -p "require('./package.json').version"
|
|
||||||
```
|
|
||||||
|
|
||||||
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 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
|
|
||||||
```
|
|
||||||
|
|
||||||
**Also update the documented Docker install version** on the release branch before tagging (the `create-release.yml` workflow fails if `versions.agentCanvas` does not match the tag):
|
|
||||||
|
|
||||||
```bash
|
|
||||||
VERSION=<new-version>
|
|
||||||
export VERSION
|
|
||||||
node <<'NODE'
|
|
||||||
const fs = require("fs");
|
|
||||||
const version = process.env.VERSION;
|
|
||||||
const configPath = "config/defaults.json";
|
|
||||||
const config = JSON.parse(fs.readFileSync(configPath, "utf8"));
|
|
||||||
config.versions.agentCanvas = version;
|
|
||||||
fs.writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n");
|
|
||||||
const image = `${config.images.agentCanvas}:${version}`;
|
|
||||||
const imageRefPattern = /ghcr\.io\/openhands\/agent-canvas:[^\s`"]+/g;
|
|
||||||
for (const file of ["README.md", "README.windows.md"]) {
|
|
||||||
fs.writeFileSync(file, fs.readFileSync(file, "utf8").replace(imageRefPattern, image));
|
|
||||||
}
|
|
||||||
NODE
|
|
||||||
git add config/defaults.json README.md README.windows.md
|
|
||||||
git commit -m "docs: update Docker install version to $VERSION"
|
|
||||||
git push
|
|
||||||
```
|
|
||||||
|
|
||||||
External install docs on docs.openhands.dev are maintained separately; update them there when closing #1073. Pre-release Docker images are tagged by exact version only (`latest` is published for stable releases).
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Step 3: Push the Tag
|
## Step 3: Cut the Release
|
||||||
|
|
||||||
Confirm the branch is in the right state (CI green, QA done), then push the tag:
|
**STOP HERE and confirm with the user before proceeding.** Marking the PR ready and merging it publishes to npm and GHCR.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
git checkout rel-<X.Y.Z>
|
gh pr ready <release-pr-number>
|
||||||
git pull origin rel-<X.Y.Z>
|
|
||||||
git tag v<version>
|
|
||||||
git push origin v<version>
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Examples:
|
This fires the release-ready gate: a Slack notification lands in `#proj-agent-canvas` and the PR is labeled `release: ready`. Then merge the release PR (squash, like any other PR).
|
||||||
- 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`
|
|
||||||
|
|
||||||
**The tag push is the release trigger.** Three workflows fire in parallel:
|
|
||||||
|
|
||||||
| Workflow | What it does |
|
|
||||||
|---|---|
|
|
||||||
| `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 |
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Step 4: Verify the Release
|
## Step 4: Watch the Pipeline
|
||||||
|
|
||||||
|
Merging the release PR triggers `release.yml` on `main`, which pushes the `vX.Y.Z` tag and creates the GitHub Release; the tag push then fires the publish workflows:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
gh run list --workflow=release.yml --limit=3
|
||||||
|
gh run list --workflow=npm-publish.yml --limit=3
|
||||||
|
gh run list --workflow=docker.yml --limit=3
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Step 5: Verify the Release
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# GitHub release
|
# GitHub release
|
||||||
@@ -149,38 +85,26 @@ gh release view v<version>
|
|||||||
|
|
||||||
# npm (allow ~2 min for publish to propagate)
|
# npm (allow ~2 min for publish to propagate)
|
||||||
npm view @openhands/agent-canvas@<version>
|
npm view @openhands/agent-canvas@<version>
|
||||||
npm view @openhands/agent-canvas dist-tags # confirm correct dist-tag
|
npm view @openhands/agent-canvas dist-tags # stable releases get `latest`
|
||||||
|
|
||||||
# Docker
|
# Docker
|
||||||
docker pull ghcr.io/openhands/agent-canvas:<version>
|
docker pull ghcr.io/openhands/agent-canvas:<version>
|
||||||
```
|
```
|
||||||
|
|
||||||
Monitor workflow runs:
|
External install docs on docs.openhands.dev are maintained separately; update them there when closing #1073.
|
||||||
|
|
||||||
```bash
|
|
||||||
gh run list --workflow=npm-publish.yml --limit=3
|
|
||||||
gh run list --workflow=docker.yml --limit=3
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Troubleshooting
|
## Troubleshooting
|
||||||
|
|
||||||
|
### No release PR appears after merging to main
|
||||||
|
Only `feat`, `fix`, and breaking changes produce a release PR. Also check `release.yml` runs on `main` — the workflow fails by design if the org secrets `RELEASE_APP_ID` / `RELEASE_APP_PRIVATE_KEY` are unavailable (a `GITHUB_TOKEN` fallback would create a tag that never triggers the publish workflows).
|
||||||
|
|
||||||
|
### The proposed version is wrong
|
||||||
|
The version comes from the conventional-commit history since the last release. Fix forward: merge a commit to `main` with a `Release-As: X.Y.Z` footer to pin the next version.
|
||||||
|
|
||||||
|
### No Slack message when the PR was marked ready
|
||||||
|
`SLACK_BOT_TOKEN` is optional by design — the gate still applies the `release: ready` label and the release proceeds normally.
|
||||||
|
|
||||||
### package.json version doesn't match the tag
|
### 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:
|
This cannot happen in the normal flow: release-please bumps `package.json` in the release PR and tags the resulting merge commit, and `npm-publish.yml` validates they match. If it ever fails, someone pushed a tag by hand — delete the tag and let release-please own tagging.
|
||||||
```bash
|
|
||||||
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>
|
|
||||||
```
|
|
||||||
|
|
||||||
### 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).
|
|
||||||
|
|
||||||
### npm publish failed mid-way
|
|
||||||
Check the `npm-publish.yml` run logs. The dist-tag is resolved dynamically: if no stable release (no `-` in the version) has ever been published to npm, all versions use `latest`; once a stable version exists, pre-release versions use their own tag (`alpha` / `beta` / `rc`) and only stable versions use `latest`.
|
|
||||||
|
|||||||
@@ -0,0 +1,14 @@
|
|||||||
|
changelog:
|
||||||
|
categories:
|
||||||
|
- title: Features
|
||||||
|
labels: ["type: feat"]
|
||||||
|
- title: Bug Fixes
|
||||||
|
labels: ["type: fix"]
|
||||||
|
- title: Performance
|
||||||
|
labels: ["type: perf"]
|
||||||
|
- title: Documentation
|
||||||
|
labels: ["type: docs"]
|
||||||
|
- title: Maintenance
|
||||||
|
labels: ["type: chore", "type: build", "type: ci", "type: refactor", "type: style", "type: test", "type: revert"]
|
||||||
|
- title: Other Changes
|
||||||
|
labels: ["*"]
|
||||||
@@ -1,117 +0,0 @@
|
|||||||
---
|
|
||||||
name: Create GitHub Release
|
|
||||||
|
|
||||||
# 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:
|
|
||||||
push:
|
|
||||||
tags:
|
|
||||||
- 'v*'
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
create-release:
|
|
||||||
runs-on: ubuntu-24.04
|
|
||||||
permissions:
|
|
||||||
contents: write
|
|
||||||
steps:
|
|
||||||
- name: Extract version from tag
|
|
||||||
id: version
|
|
||||||
run: |
|
|
||||||
TAG="${GITHUB_REF#refs/tags/}"
|
|
||||||
VERSION="${TAG#v}"
|
|
||||||
|
|
||||||
# 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 tag: $TAG"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
|
|
||||||
echo "tag=$TAG" >> "$GITHUB_OUTPUT"
|
|
||||||
echo "📦 Version: $VERSION"
|
|
||||||
|
|
||||||
- name: Checkout tag
|
|
||||||
uses: actions/checkout@v6
|
|
||||||
|
|
||||||
- name: Verify documented Docker install version matches release
|
|
||||||
id: docker_docs
|
|
||||||
run: |
|
|
||||||
VERSION="${{ steps.version.outputs.version }}"
|
|
||||||
PINNED=$(node -p "require('./config/defaults.json').versions.agentCanvas")
|
|
||||||
if [ "$PINNED" != "$VERSION" ]; then
|
|
||||||
echo "❌ versions.agentCanvas is '$PINNED' but release tag is v$VERSION"
|
|
||||||
echo "Update config/defaults.json and README Docker examples before tagging."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
echo "✅ versions.agentCanvas matches release ($VERSION)"
|
|
||||||
|
|
||||||
- name: Check release does not already exist
|
|
||||||
id: check
|
|
||||||
env:
|
|
||||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
TAG: ${{ steps.version.outputs.tag }}
|
|
||||||
run: |
|
|
||||||
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"
|
|
||||||
fi
|
|
||||||
|
|
||||||
- name: Find previous release tag
|
|
||||||
if: steps.check.outputs.exists == 'false'
|
|
||||||
id: prev_tag
|
|
||||||
env:
|
|
||||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
run: |
|
|
||||||
PREV_TAG=$(gh release list --repo "${{ github.repository }}" \
|
|
||||||
--exclude-drafts --limit 1 \
|
|
||||||
--json tagName --jq '.[0].tagName')
|
|
||||||
echo "prev_tag=${PREV_TAG}" >> "$GITHUB_OUTPUT"
|
|
||||||
echo "📌 Previous release tag: ${PREV_TAG:-<none>}"
|
|
||||||
|
|
||||||
- name: Create GitHub Release
|
|
||||||
if: steps.check.outputs.exists == 'false'
|
|
||||||
env:
|
|
||||||
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=()
|
|
||||||
if [ -n "$PREV_TAG" ]; then
|
|
||||||
NOTES_START_FLAG=(--notes-start-tag "$PREV_TAG")
|
|
||||||
fi
|
|
||||||
|
|
||||||
# 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 "$TAG" \
|
|
||||||
--repo "${{ github.repository }}" \
|
|
||||||
--title "$TAG" \
|
|
||||||
--generate-notes \
|
|
||||||
"${NOTES_START_FLAG[@]}" \
|
|
||||||
"${PRERELEASE_FLAG[@]}"
|
|
||||||
|
|
||||||
echo "✅ Release $TAG created!"
|
|
||||||
echo "🔗 https://github.com/${{ github.repository }}/releases/tag/$TAG"
|
|
||||||
|
|
||||||
- name: Summary
|
|
||||||
if: steps.check.outputs.exists == 'false'
|
|
||||||
env:
|
|
||||||
TAG: ${{ steps.version.outputs.tag }}
|
|
||||||
run: |
|
|
||||||
echo "## ✅ Release $TAG Created" >> "$GITHUB_STEP_SUMMARY"
|
|
||||||
echo "" >> "$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 "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"
|
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
name: pr
|
||||||
|
|
||||||
|
# Lints PR titles (Conventional Commits) and applies `type:` labels that drive
|
||||||
|
# release notes + the version release-please derives (OpenHands/release-actions).
|
||||||
|
#
|
||||||
|
# pull_request_target (not pull_request) so it also runs on fork PRs, where the
|
||||||
|
# label step needs a writable token. Safe ONLY because the reusable workflow
|
||||||
|
# never checks out PR code (reads the title from the payload) and this caller
|
||||||
|
# does NOT pass `secrets: inherit` (so the App token stays out of scope). Don't
|
||||||
|
# add a checkout step or `secrets: inherit`.
|
||||||
|
on:
|
||||||
|
pull_request_target:
|
||||||
|
# synchronize: re-run on each push so the check stays green on release-please's
|
||||||
|
# force-pushed release PR.
|
||||||
|
types: [opened, edited, reopened, synchronize]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
pr-title:
|
||||||
|
permissions:
|
||||||
|
pull-requests: write
|
||||||
|
uses: OpenHands/release-actions/.github/workflows/pr-title.yml@main
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
name: release ready
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request_target:
|
||||||
|
types: [ready_for_review]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
release-ready:
|
||||||
|
permissions:
|
||||||
|
actions: write
|
||||||
|
contents: read
|
||||||
|
issues: write
|
||||||
|
pull-requests: write
|
||||||
|
secrets: inherit
|
||||||
|
uses: OpenHands/release-actions/.github/workflows/release-ready.yml@main
|
||||||
|
with:
|
||||||
|
product-name: Agent Canvas
|
||||||
|
slack-channel: '#proj-agent-canvas'
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
name: release
|
||||||
|
|
||||||
|
# release-please caller (OpenHands/release-actions). On push to main, maintains a
|
||||||
|
# "release PR" that, on merge, bumps package.json + tags vX.Y.Z + creates the
|
||||||
|
# Release; that release drives the publish workflows via `release: published`.
|
||||||
|
# Config: release-please-config.json, .release-please-manifest.json, .github/release.yml.
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
- 'release/**' # maintenance branches; the release is scoped to this branch
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
release-please:
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
pull-requests: write
|
||||||
|
# Required, not an optimization: release-please needs the org App token
|
||||||
|
# (RELEASE_APP_ID / RELEASE_APP_PRIVATE_KEY) so the release it creates fires
|
||||||
|
# `release: published` — a GITHUB_TOKEN release would be suppressed.
|
||||||
|
secrets: inherit
|
||||||
|
uses: OpenHands/release-actions/.github/workflows/release-please.yml@main
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
".": "1.1.0"
|
||||||
|
}
|
||||||
@@ -647,6 +647,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.
|
- 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: 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). The dist-tag is resolved dynamically at publish time by querying npm: if no stable version (no pre-release suffix) has ever been published, all releases use `--tag latest`; once a stable version exists, pre-release versions get their own dist-tag (`alpha` / `beta` / `rc`) and only stable versions keep `latest`. The transition is automatic — no workflow change is needed when the first stable release ships. 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.
|
- Release automation: releases are **trunk-based via release-please**, using the shared reusable workflows from [`OpenHands/release-actions`](https://github.com/OpenHands/release-actions) through three thin callers: `.github/workflows/release.yml` (runs release-please on every push to `main` / `release/**`; maintains a draft `chore(main): release X.Y.Z` PR that accumulates merged changes and pre-stages every version bump), `.github/workflows/pr.yml` (lints PR titles for Conventional Commits under `pull_request_target` — never checks out PR code and must NOT get `secrets: inherit` — and applies the `type:` labels that drive release-notes grouping via `.github/release.yml`), and `.github/workflows/release-ready.yml` (marking the draft release PR **Ready for review** is the cut-a-release signal: Slack notification to `#proj-agent-canvas` + `release: ready` label). Config lives in `release-please-config.json` — node release-type (bumps `package.json` + `package-lock.json`) plus `extra-files` keeping `config/defaults.json` `versions.agentCanvas` and the README / README.windows Docker image pins (lines annotated `x-release-please-version`) in lockstep — and `.release-please-manifest.json` (current released version). There is no `CHANGELOG.md`; GitHub Releases are the changelog. Merging the release PR pushes the `vX.Y.Z` tag with the org release App token (`RELEASE_APP_ID` / `RELEASE_APP_PRIVATE_KEY` — required so the tag push fires downstream workflows; a `GITHUB_TOKEN` push would be suppressed) and creates the GitHub Release; the same tag push triggers `npm-publish.yml` (dist-tag resolved dynamically at publish time — stable versions get `latest`) and `docker.yml` (multi-arch images + semver tags). Version derivation: `fix` → patch, `feat` → minor, `!`/`BREAKING CHANGE` → major; other types don't produce a release PR. 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.
|
- 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.
|
||||||
|
|||||||
@@ -97,7 +97,7 @@ docker run -it --rm \
|
|||||||
-p 8000:8000 \
|
-p 8000:8000 \
|
||||||
-v "$HOME/.openhands:/home/openhands/.openhands" \
|
-v "$HOME/.openhands:/home/openhands/.openhands" \
|
||||||
-v "${PROJECTS_PATH}:/projects" \
|
-v "${PROJECTS_PATH}:/projects" \
|
||||||
ghcr.io/openhands/agent-canvas:1.1.0
|
ghcr.io/openhands/agent-canvas:1.1.0 # x-release-please-version
|
||||||
```
|
```
|
||||||
|
|
||||||
**Windows (PowerShell / Windows Terminal):** See [README.windows.md](./README.windows.md) for the equivalent commands.
|
**Windows (PowerShell / Windows Terminal):** See [README.windows.md](./README.windows.md) for the equivalent commands.
|
||||||
|
|||||||
+2
-2
@@ -12,7 +12,7 @@ For the main install options and overall context, see [README.md](./README.md).
|
|||||||
- A host directory for `PROJECTS_PATH` containing the project folders you want the agent to access (create it before starting the container)
|
- A host directory for `PROJECTS_PATH` containing the project folders you want the agent to access (create it before starting the container)
|
||||||
|
|
||||||
```powershell
|
```powershell
|
||||||
docker pull ghcr.io/openhands/agent-canvas:1.1.0
|
docker pull ghcr.io/openhands/agent-canvas:1.1.0 # x-release-please-version
|
||||||
|
|
||||||
$env:PROJECTS_PATH = Join-Path $HOME "projects" # directory containing your project folders
|
$env:PROJECTS_PATH = Join-Path $HOME "projects" # directory containing your project folders
|
||||||
New-Item -ItemType Directory -Force -Path $env:PROJECTS_PATH, (Join-Path $env:USERPROFILE ".openhands") | Out-Null
|
New-Item -ItemType Directory -Force -Path $env:PROJECTS_PATH, (Join-Path $env:USERPROFILE ".openhands") | Out-Null
|
||||||
@@ -21,7 +21,7 @@ docker run -it --rm `
|
|||||||
-p 8000:8000 `
|
-p 8000:8000 `
|
||||||
-v "$($env:USERPROFILE)\.openhands:/home/openhands/.openhands" `
|
-v "$($env:USERPROFILE)\.openhands:/home/openhands/.openhands" `
|
||||||
-v "$($env:PROJECTS_PATH):/projects" `
|
-v "$($env:PROJECTS_PATH):/projects" `
|
||||||
ghcr.io/openhands/agent-canvas:1.1.0
|
ghcr.io/openhands/agent-canvas:1.1.0 # x-release-please-version
|
||||||
```
|
```
|
||||||
|
|
||||||
The agent will be able to access any project under `PROJECTS_PATH`.
|
The agent will be able to access any project under `PROJECTS_PATH`.
|
||||||
|
|||||||
@@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://raw.githubusercontent.com/googleapis/release-please/main/schemas/config.json",
|
||||||
|
"include-component-in-tag": false,
|
||||||
|
"include-v-in-tag": true,
|
||||||
|
"draft-pull-request": true,
|
||||||
|
"last-release-sha": "7fff6665c74b57789f29f938fe13c7e11d23ef34",
|
||||||
|
"packages": {
|
||||||
|
".": {
|
||||||
|
"release-type": "node",
|
||||||
|
"changelog-type": "github",
|
||||||
|
"skip-changelog": true,
|
||||||
|
"extra-files": [
|
||||||
|
{
|
||||||
|
"type": "json",
|
||||||
|
"path": "config/defaults.json",
|
||||||
|
"jsonpath": "$.versions.agentCanvas"
|
||||||
|
},
|
||||||
|
"README.md",
|
||||||
|
"README.windows.md"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user