From 4bd3baccdecd0692eca81d46e512043f2b60d028 Mon Sep 17 00:00:00 2001 From: Engel Nyst Date: Thu, 9 Jul 2026 07:22:29 +0200 Subject: [PATCH] ci: adopt release-please via shared release-actions (#1496) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 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 * 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 * 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 * 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 * docs: document the release-please flow --------- Co-authored-by: smolpaws Co-authored-by: openhands Co-authored-by: hieptl --- .agents/skills/release.md | 172 ++++++++------------------- .github/release.yml | 14 +++ .github/workflows/create-release.yml | 117 ------------------ .github/workflows/pr.yml | 21 ++++ .github/workflows/release-ready.yml | 18 +++ .github/workflows/release.yml | 22 ++++ .release-please-manifest.json | 3 + AGENTS.md | 2 +- README.md | 2 +- README.windows.md | 4 +- release-please-config.json | 23 ++++ 11 files changed, 153 insertions(+), 245 deletions(-) create mode 100644 .github/release.yml delete mode 100644 .github/workflows/create-release.yml create mode 100644 .github/workflows/pr.yml create mode 100644 .github/workflows/release-ready.yml create mode 100644 .github/workflows/release.yml create mode 100644 .release-please-manifest.json create mode 100644 release-please-config.json diff --git a/.agents/skills/release.md b/.agents/skills/release.md index 76a750e038..a5f000a727 100644 --- a/.agents/skills/release.md +++ b/.agents/skills/release.md @@ -1,6 +1,6 @@ --- 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: - release - new release @@ -13,135 +13,71 @@ triggers: ## 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. -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.** +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. 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. 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. 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. 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`: - -| 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. +**Never commit to the release PR's branch by hand** — release-please owns it and force-pushes it on every push to `main`. --- -## Step 1: Confirm the Release Branch Exists - -The branch must be named `rel-X.Y.Z` (e.g. `rel-1.0.0`). Check: +## Step 1: Find the Release PR ```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 -git checkout main && git pull origin main -git checkout -b rel- -git push -u origin rel- +gh run list --workflow=release.yml --limit=3 ``` -**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: - -```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- -git pull origin rel- -npm version --no-git-tag-version -git add package.json package-lock.json -git commit -m "chore: bump version to " -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= -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). +- **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. --- -## 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 -git checkout rel- -git pull origin rel- -git tag v -git push origin v +gh pr ready ``` -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` - -**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 | +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). --- -## 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 # GitHub release @@ -149,38 +85,26 @@ gh release view v # npm (allow ~2 min for publish to propagate) npm view @openhands/agent-canvas@ -npm view @openhands/agent-canvas dist-tags # confirm correct dist-tag +npm view @openhands/agent-canvas dist-tags # stable releases get `latest` # Docker docker pull ghcr.io/openhands/agent-canvas: ``` -Monitor workflow runs: - -```bash -gh run list --workflow=npm-publish.yml --limit=3 -gh run list --workflow=docker.yml --limit=3 -``` +External install docs on docs.openhands.dev are maintained separately; update them there when closing #1073. --- ## 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 -`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 -git push origin :refs/tags/v # delete remote tag -git tag -d v # delete local tag -# fix package.json, commit, push -git tag v && git push origin v -``` - -### GitHub release already exists -`create-release.yml` skips silently if the release already exists. To recreate it: -```bash -gh release delete v --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`. +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. diff --git a/.github/release.yml b/.github/release.yml new file mode 100644 index 0000000000..9d267f3a43 --- /dev/null +++ b/.github/release.yml @@ -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: ["*"] diff --git a/.github/workflows/create-release.yml b/.github/workflows/create-release.yml deleted file mode 100644 index 26d4c683cf..0000000000 --- a/.github/workflows/create-release.yml +++ /dev/null @@ -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:-}" - - - 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" diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml new file mode 100644 index 0000000000..fa017cc955 --- /dev/null +++ b/.github/workflows/pr.yml @@ -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 diff --git a/.github/workflows/release-ready.yml b/.github/workflows/release-ready.yml new file mode 100644 index 0000000000..20cce0926b --- /dev/null +++ b/.github/workflows/release-ready.yml @@ -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' diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000000..aa4f2a2bd7 --- /dev/null +++ b/.github/workflows/release.yml @@ -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 diff --git a/.release-please-manifest.json b/.release-please-manifest.json new file mode 100644 index 0000000000..5fdd88304e --- /dev/null +++ b/.release-please-manifest.json @@ -0,0 +1,3 @@ +{ + ".": "1.1.0" +} diff --git a/AGENTS.md b/AGENTS.md index aacf30709b..8735e90915 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -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. -- 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. diff --git a/README.md b/README.md index 9c5e720938..15f36d3586 100644 --- a/README.md +++ b/README.md @@ -97,7 +97,7 @@ docker run -it --rm \ -p 8000:8000 \ -v "$HOME/.openhands:/home/openhands/.openhands" \ -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. diff --git a/README.windows.md b/README.windows.md index c59b8403f0..a9c1de9beb 100644 --- a/README.windows.md +++ b/README.windows.md @@ -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) ```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 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 ` -v "$($env:USERPROFILE)\.openhands:/home/openhands/.openhands" ` -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`. diff --git a/release-please-config.json b/release-please-config.json new file mode 100644 index 0000000000..852d58bdd8 --- /dev/null +++ b/release-please-config.json @@ -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" + ] + } + } +}