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:
Engel Nyst
2026-07-09 12:22:29 +07:00
committed by GitHub
co-authored by smolpaws openhands hieptl
parent 5538b699a7
commit 4bd3baccde
11 changed files with 153 additions and 245 deletions
+48 -124
View File
@@ -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`.
+14
View File
@@ -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: ["*"]
-117
View File
@@ -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"
+21
View File
@@ -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
+18
View File
@@ -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'
+22
View File
@@ -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
+3
View File
@@ -0,0 +1,3 @@
{
".": "1.1.0"
}
+1 -1
View File
@@ -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.
+1 -1
View File
@@ -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
View File
@@ -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`.
+23
View File
@@ -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"
]
}
}
}