Skip to content

fix(ci): pin the TypeScript client's Agent Server in the release PR - #5476

Merged
VascoSch92 merged 1 commit into
mainfrom
vasco/ts-client-contract-in-release-pr
Oct 5, 2026
Merged

VascoSch92 merged 1 commit into
mainfrom
vasco/ts-client-contract-in-release-pr

Conversation

@VascoSch92

@VascoSch92 VascoSch92 commented Oct 3, 2026 •

Copy link
Copy Markdown
Member

HUMAN:

Fixing the CI for the release of the typescript client.


AGENT:

Why

typescript-client-npm-publish.yml packs clients/typescript exactly as it is at tag vX.Y.Z. The client's config.agentServerImage and src/generated/agent-server-schema.ts were only updated afterwards, by the bump-typescript-client PR from version-bump-prs.yml. So every npm release shipped an older contract, and because those PRs often stayed open the gap grew: @openhands/typescript-client@1.51.0 pins agent-server:1.47.0-python with 1.47.0 types (#5474).

The release PR doesn't need the published image to build the contract. export_agent_server_openapi.py builds it from source, as release-binaries.yml already does.

Summary

  • prepare-release.yml: after setting versions, export openapi.json from the release branch, run the existing prepare_typescript_client_agent_server_bump.py and generate:agent-server-api, and commit the result into the rel-X.Y.Z PR.
  • typescript-client-{npm,github-packages}-publish.yml: "Validate package version" also requires agentServerImage == ghcr.io/openhands/agent-server:X.Y.Z-python, so a mismatched client can't be published.
  • README-RELEASE.md updated. bump-typescript-client is left as is: it already exits early when the client is "already pinned to $VERSION", so it now only acts as a fallback.

Issue Number

Closes #5474

How to Test

The workflow is workflow_dispatch-only, so I ran its steps locally in a clean worktree of this branch. I pulled the run: scripts out of the edited prepare-release.yml with PyYAML and ran "Set package version" → "Set TypeScript client version" → "Pin TypeScript client to the release Agent Server" with INPUTS_VERSION=9.9.9:

Prepared TypeScript client Agent Server bump 1.51.0 -> 9.9.9; changed files: package.json
Generated .../clients/typescript/src/generated/agent-server-schema.ts from .../runner/openapi.json
  • The exported openapi.json has info.version == "9.9.9", so the bump script's version validation passes after make set-package-version.
  • git status matches what the release commit should contain: 4× pyproject.toml, uv.lock, and clients/typescript/{package.json,package-lock.json,src/generated/agent-server-schema.ts}. node_modules/dist are ignored.
  • Parity with the release artifact: main's schema was generated by chore(typescript): agent-server to v1.51.0 #5472 from the published v1.51.0 openapi.json. Comparing it with the source-built schema gives 0 changed lines apart from version strings.

Publish guard, using the extracted "Validate package version" script:

== pass case (9.9.9):
exit=0
== fail case (version ok, image stale):
Agent Server image ghcr.io/openhands/agent-server:1.47.0-python does not match requested version 9.9.9
exit=1

pre-commit run --files <changed workflows> passes.

Video/Screenshots

N/A (CI only).

Type

  • Bug fix
  • Feature
  • Refactor
  • Breaking change
  • Docs / chore

Notes


🐳 Agent Server images for this PR — GHCR package, pull/run commands, and all pushed tags (click to expand)

• GHCR package: https://github.com/OpenHands/agent-sdk/pkgs/container/agent-server

Variants & Base Images

Variant Architectures Base Image Docs / Tags
java amd64, arm64 eclipse-temurin:17-jdk Link
python-slim amd64, arm64 python-node-runtime Link
python-minimal amd64, arm64 python-node-runtime Link
python amd64, arm64 python-node-runtime Link
golang amd64, arm64 golang:1.21-bookworm Link

Pull (multi-arch manifest)

# Each variant is a multi-arch manifest supporting both amd64 and arm64
docker pull ghcr.io/openhands/agent-server:8130925-python

Run

docker run -it --rm \
  -p 8000:8000 \
  --name agent-server-8130925-python \
  ghcr.io/openhands/agent-server:8130925-python

All tags pushed for this build

ghcr.io/openhands/agent-server:8130925-golang-amd64
ghcr.io/openhands/agent-server:8130925782f9a2910573449034a46f128ce6b525-golang-amd64
ghcr.io/openhands/agent-server:vasco-ts-client-contract-in-release-pr-golang-amd64
ghcr.io/openhands/agent-server:8130925-golang_tag_1.21-bookworm-amd64
ghcr.io/openhands/agent-server:8130925-golang-arm64
ghcr.io/openhands/agent-server:8130925782f9a2910573449034a46f128ce6b525-golang-arm64
ghcr.io/openhands/agent-server:vasco-ts-client-contract-in-release-pr-golang-arm64
ghcr.io/openhands/agent-server:8130925-golang_tag_1.21-bookworm-arm64
ghcr.io/openhands/agent-server:8130925-java-amd64
ghcr.io/openhands/agent-server:8130925782f9a2910573449034a46f128ce6b525-java-amd64
ghcr.io/openhands/agent-server:vasco-ts-client-contract-in-release-pr-java-amd64
ghcr.io/openhands/agent-server:8130925-eclipse-temurin_tag_17-jdk-amd64
ghcr.io/openhands/agent-server:8130925-java-arm64
ghcr.io/openhands/agent-server:8130925782f9a2910573449034a46f128ce6b525-java-arm64
ghcr.io/openhands/agent-server:vasco-ts-client-contract-in-release-pr-java-arm64
ghcr.io/openhands/agent-server:8130925-eclipse-temurin_tag_17-jdk-arm64
ghcr.io/openhands/agent-server:8130925-python-amd64
ghcr.io/openhands/agent-server:8130925782f9a2910573449034a46f128ce6b525-python-amd64
ghcr.io/openhands/agent-server:vasco-ts-client-contract-in-release-pr-python-amd64
ghcr.io/openhands/agent-server:8130925-python-node-runtime-amd64
ghcr.io/openhands/agent-server:8130925-python-arm64
ghcr.io/openhands/agent-server:8130925782f9a2910573449034a46f128ce6b525-python-arm64
ghcr.io/openhands/agent-server:vasco-ts-client-contract-in-release-pr-python-arm64
ghcr.io/openhands/agent-server:8130925-python-node-runtime-arm64
ghcr.io/openhands/agent-server:8130925-python-minimal-amd64
ghcr.io/openhands/agent-server:8130925782f9a2910573449034a46f128ce6b525-python-minimal-amd64
ghcr.io/openhands/agent-server:vasco-ts-client-contract-in-release-pr-python-minimal-amd64
ghcr.io/openhands/agent-server:8130925-python-node-runtime-minimal-amd64
ghcr.io/openhands/agent-server:8130925-python-minimal-arm64
ghcr.io/openhands/agent-server:8130925782f9a2910573449034a46f128ce6b525-python-minimal-arm64
ghcr.io/openhands/agent-server:vasco-ts-client-contract-in-release-pr-python-minimal-arm64
ghcr.io/openhands/agent-server:8130925-python-node-runtime-minimal-arm64
ghcr.io/openhands/agent-server:8130925-python-slim-amd64
ghcr.io/openhands/agent-server:8130925782f9a2910573449034a46f128ce6b525-python-slim-amd64
ghcr.io/openhands/agent-server:vasco-ts-client-contract-in-release-pr-python-slim-amd64
ghcr.io/openhands/agent-server:8130925-python-node-runtime-slim-amd64
ghcr.io/openhands/agent-server:8130925-python-slim-arm64
ghcr.io/openhands/agent-server:8130925782f9a2910573449034a46f128ce6b525-python-slim-arm64
ghcr.io/openhands/agent-server:vasco-ts-client-contract-in-release-pr-python-slim-arm64
ghcr.io/openhands/agent-server:8130925-python-node-runtime-slim-arm64
ghcr.io/openhands/agent-server:8130925-golang
ghcr.io/openhands/agent-server:8130925782f9a2910573449034a46f128ce6b525-golang
ghcr.io/openhands/agent-server:vasco-ts-client-contract-in-release-pr-golang
ghcr.io/openhands/agent-server:8130925-golang_tag_1.21-bookworm
ghcr.io/openhands/agent-server:8130925-java
ghcr.io/openhands/agent-server:8130925782f9a2910573449034a46f128ce6b525-java
ghcr.io/openhands/agent-server:vasco-ts-client-contract-in-release-pr-java
ghcr.io/openhands/agent-server:8130925-eclipse-temurin_tag_17-jdk
ghcr.io/openhands/agent-server:8130925-python-minimal
ghcr.io/openhands/agent-server:8130925782f9a2910573449034a46f128ce6b525-python-minimal
ghcr.io/openhands/agent-server:vasco-ts-client-contract-in-release-pr-python-minimal
ghcr.io/openhands/agent-server:8130925-python-node-runtime-minimal
ghcr.io/openhands/agent-server:8130925-python-slim
ghcr.io/openhands/agent-server:8130925782f9a2910573449034a46f128ce6b525-python-slim
ghcr.io/openhands/agent-server:vasco-ts-client-contract-in-release-pr-python-slim
ghcr.io/openhands/agent-server:8130925-python-node-runtime-slim
ghcr.io/openhands/agent-server:8130925-python
ghcr.io/openhands/agent-server:8130925782f9a2910573449034a46f128ce6b525-python
ghcr.io/openhands/agent-server:vasco-ts-client-contract-in-release-pr-python
ghcr.io/openhands/agent-server:8130925-python-node-runtime

About Multi-Architecture Support

  • Each variant tag (e.g., 8130925-python) is a multi-arch manifest supporting both amd64 and arm64
  • Docker automatically pulls the correct architecture for your platform
  • Individual architecture tags (e.g., 8130925-python-amd64) are also available if needed

The npm publish packs the release tag as-is, but the client's
agentServerImage and generated types were only bumped by a PR opened
after the release, so every published client shipped a stale contract
(1.51.0 pinned agent-server 1.47.0).

Prepare Release now exports the OpenAPI contract from the release
branch and regenerates the client with the existing bump script, and
both publishers refuse a package whose agentServerImage doesn't match
the release. The post-release bump job becomes a no-op.

Closes #5474
@VascoSch92
VascoSch92 requested a review from neubig October 3, 2026 08:24
@github-actions

github-actions Bot commented Oct 3, 2026

Copy link
Copy Markdown
Contributor

REST API breakage checks (OpenAPI) — ✅ PASSED

Result: ✅ PASSED

Action log

@VascoSch92
VascoSch92 marked this pull request as ready for review October 3, 2026 08:25
@neubig
neubig requested a review from all-hands-bot October 3, 2026 23:22

@all-hands-bot all-hands-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This review was posted by an AI agent (OpenHands).

Scope check: this change belongs in this repository (release automation plus clients/typescript, both owned here) and has the direction it needs, so I reviewed the implementation. The core idea is right: build the contract from the release-branch source, as release-binaries.yml already does, and refuse to publish a client whose agentServerImage does not match the release.

One material problem, described inline: pinning the release PR to ghcr.io/openhands/agent-server:X.Y.Z-python before that image and its openapi.json release asset exist makes the release PR's own TypeScript client CI fail, and those checks cannot be re-run green while the PR is open. The PR description's local test exercised only the prepare-release.yml steps, not the release PR's client checks, so this path is untested.

The publish-time guard in both publish workflows is correct and does not depend on the image existing (it only reads package.json), and leaving bump-typescript-client in place as a no-op fallback is fine. The blocking issue is the release-PR CI regression.

🔄 CHANGES REQUESTED

--openapi "$OPENAPI_PATH"
npm ci --prefix "$CLIENT_ROOT"
AGENT_SERVER_OPENAPI_PATH="$OPENAPI_PATH" \
npm --prefix "$CLIENT_ROOT" run generate:agent-server-api

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This step pins the release branch to ghcr.io/openhands/agent-server:X.Y.Z-python, but that image and the matching release asset openapi.json are only produced by create-release.yml after this PR merges (it dispatches server.yml and release-binaries.yml post-merge). The TypeScript client resolves its contract from exactly that pin: clients/typescript/scripts/agent-server-openapi.mjs first fetches https://github.com/OpenHands/software-agent-sdk/releases/download/vX.Y.Z/openapi.json, then falls back to docker run <pinned image>. Neither exists while the release PR is open, so:

  • .github/workflows/typescript-client-ci.yml (npm run check:agent-server-api) fails: check-agent-server-api.mjs regenerates with no AGENT_SERVER_OPENAPI_PATH, so it resolves through the pin and the generator throws.
  • The endpoint-audit job fails for the same reason (endpoint-audit.config.json uses source: pinned-agent-server-release).
  • The smoke-test job fails at docker pull $AGENT_SERVER_IMAGE.

Evidence on the currently open release PR #5483 (rel-1.51.1, pin still 1.51.0): agent-server-api, endpoint-audit, and smoke-test all pass, and the agent-server-api log reads Generated .../src/generated/agent-server-schema.ts from https://github.com/OpenHands/software-agent-sdk/releases/download/v1.51.0/openapi.json. For the new pin, .../releases/download/v1.51.1/openapi.json returns 404 and docker manifest inspect ghcr.io/openhands/agent-server:1.51.1-python reports manifest unknown. Because the asset and image never exist during the PR's lifetime, these checks cannot be re-run green, which blocks the release this workflow is meant to prepare.

Consider feeding the branch-built contract to those checks instead of the pin (for example, set AGENT_SERVER_OPENAPI_PATH / AGENT_SERVER_SCHEMA_SOURCE for the client contract jobs on rel-* branches), or otherwise ensure the contract jobs tolerate a not-yet-published pin while still verifying the checked-in schema.

@VascoSch92
VascoSch92 merged commit 56bd60c into main Oct 5, 2026
62 of 63 checks passed
@VascoSch92
VascoSch92 deleted the vasco/ts-client-contract-in-release-pr branch October 5, 2026 10:29
@github-actions

github-actions Bot commented Oct 5, 2026

Copy link
Copy Markdown
Contributor

🧹 PR Artifact Cleanup Queued

The .pr/ directory reached main after merge. A cleanup PR has been opened or updated: #5482

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: npm typescript-client is published before its agent-server pin and types are bumped (1.51.0 ships 1.47.0 contract)

3 participants