Skip to content

fix(ci): read the unreleased Agent Server contract from source on release PRs - #5512

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

VascoSch92 merged 1 commit into
mainfrom
vasco/ts-client-unreleased-contract

Conversation

@VascoSch92

@VascoSch92 VascoSch92 commented Oct 5, 2026 •

Copy link
Copy Markdown
Member

HUMAN:

Follow-up to #5476: that PR moved the TS client's agent-server pin into the release PR, so npm no longer ships the client pinned to the previous version. Side effect: the release PR now pins a version that isn't published yet, so three TS checks (agent-server-api, endpoint-audit, smoke-test) fail trying to pull its image and openapi.json

The fix is to wait until that version is published, the contract checks read the schema built from source (as prepare-release already does) and the release-image smoke test is skipped.


AGENT:

Why

#5476 pins the TypeScript client to the version being released inside the rel-X.Y.Z PR. Three TypeScript checks then try to read that release before it exists: v1.53.0/openapi.json returns 404 and ghcr.io/openhands/agent-server:1.53.0-python is not in GHCR. On #5509 (Release v1.53.0), agent-server-api, endpoint-audit and smoke-test all fail with docker: Error response from daemon: manifest unknown. The push to main right after the release merge hits the same window before server.yml and release-binaries.yml publish.

Summary

  • New .github/scripts/resolve_typescript_client_agent_server.py (stdlib only) outputs pinned_is_source (client pin == openhands-agent-server version) and use_source_contract (that, plus the release's openapi.json is a 404; other HTTP errors fail the job instead of silently falling back).
  • agent-server-api and endpoint-audit: when use_source_contract is true, export the contract with export_agent_server_openapi.py (as prepare-release.yml does) and pass it via AGENT_SERVER_OPENAPI_PATH. endpoint-audit.mjs now forwards that variable to the loader, as the generator already does.
  • smoke-test: skip pull/health/workspace steps only when the pinned image is missing from GHCR and the pin is the branch's own version; integration-test already exercises the branch-built server. A missing image for any other pin still fails.

Requiring the pin to equal the source version keeps every other PR on the released contract: after a release, main stays at that version until the next one, and the artifact check then routes back to the published openapi.json.

Issue Number

Fixes #5511

How to Test

Ran locally on a scratch worktree of origin/rel-1.53.0 (the #5509 head) with this diff applied:

$ python3 .github/scripts/resolve_typescript_client_agent_server.py
pinned_version=1.53.0
pinned_is_source=true
use_source_contract=true
$ uv sync --frozen --package openhands-agent-server
$ OPENHANDS_SUPPRESS_BANNER=1 uv run python .github/scripts/export_agent_server_openapi.py --output /tmp/oa.json
$ cd clients/typescript && npm ci
$ AGENT_SERVER_OPENAPI_PATH=/tmp/oa.json npm run check:agent-server-api
Checked-in Agent Server contract is current.
$ AGENT_SERVER_OPENAPI_PATH=/tmp/oa.json npm run audit:endpoints
✅ endpoint audit passed

On current main the resolver keeps the old path:

pinned_version=1.51.0
pinned_is_source=false
use_source_contract=false

Smoke-test image detection:

$ docker manifest inspect ghcr.io/openhands/agent-server:1.52.0-python  # exit 0 -> published
$ docker manifest inspect ghcr.io/openhands/agent-server:1.53.0-python  # exit 1 -> missing

Also: pytest tests/cross/test_resolve_typescript_client_agent_server.py (5 passed), pre-commit run on the changed files, prettier --check scripts/endpoint-audit.mjs, npm run test:endpoint-audit-tooling.

Video/Screenshots

N/A (CI-only change; logs above).

Type

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

Notes

The Release Security Scan and the MiniMax run-integration-tests failures on #5509 are unrelated: a changed-after-approval finding on #5479 (the bot's own .pr/ cleanup commit) and a Chromium snap install flake.

🤖 Generated with Claude Code


🐳 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:6d8dac6-python

Run

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

All tags pushed for this build

ghcr.io/openhands/agent-server:6d8dac6-golang-amd64
ghcr.io/openhands/agent-server:6d8dac619e5d59e2da88e730e9e2149cce590485-golang-amd64
ghcr.io/openhands/agent-server:vasco-ts-client-unreleased-contract-golang-amd64
ghcr.io/openhands/agent-server:6d8dac6-golang_tag_1.21-bookworm-amd64
ghcr.io/openhands/agent-server:6d8dac6-golang-arm64
ghcr.io/openhands/agent-server:6d8dac619e5d59e2da88e730e9e2149cce590485-golang-arm64
ghcr.io/openhands/agent-server:vasco-ts-client-unreleased-contract-golang-arm64
ghcr.io/openhands/agent-server:6d8dac6-golang_tag_1.21-bookworm-arm64
ghcr.io/openhands/agent-server:6d8dac6-java-amd64
ghcr.io/openhands/agent-server:6d8dac619e5d59e2da88e730e9e2149cce590485-java-amd64
ghcr.io/openhands/agent-server:vasco-ts-client-unreleased-contract-java-amd64
ghcr.io/openhands/agent-server:6d8dac6-eclipse-temurin_tag_17-jdk-amd64
ghcr.io/openhands/agent-server:6d8dac6-java-arm64
ghcr.io/openhands/agent-server:6d8dac619e5d59e2da88e730e9e2149cce590485-java-arm64
ghcr.io/openhands/agent-server:vasco-ts-client-unreleased-contract-java-arm64
ghcr.io/openhands/agent-server:6d8dac6-eclipse-temurin_tag_17-jdk-arm64
ghcr.io/openhands/agent-server:6d8dac6-python-amd64
ghcr.io/openhands/agent-server:6d8dac619e5d59e2da88e730e9e2149cce590485-python-amd64
ghcr.io/openhands/agent-server:vasco-ts-client-unreleased-contract-python-amd64
ghcr.io/openhands/agent-server:6d8dac6-python-node-runtime-amd64
ghcr.io/openhands/agent-server:6d8dac6-python-arm64
ghcr.io/openhands/agent-server:6d8dac619e5d59e2da88e730e9e2149cce590485-python-arm64
ghcr.io/openhands/agent-server:vasco-ts-client-unreleased-contract-python-arm64
ghcr.io/openhands/agent-server:6d8dac6-python-node-runtime-arm64
ghcr.io/openhands/agent-server:6d8dac6-python-minimal-amd64
ghcr.io/openhands/agent-server:6d8dac619e5d59e2da88e730e9e2149cce590485-python-minimal-amd64
ghcr.io/openhands/agent-server:vasco-ts-client-unreleased-contract-python-minimal-amd64
ghcr.io/openhands/agent-server:6d8dac6-python-node-runtime-minimal-amd64
ghcr.io/openhands/agent-server:6d8dac6-python-minimal-arm64
ghcr.io/openhands/agent-server:6d8dac619e5d59e2da88e730e9e2149cce590485-python-minimal-arm64
ghcr.io/openhands/agent-server:vasco-ts-client-unreleased-contract-python-minimal-arm64
ghcr.io/openhands/agent-server:6d8dac6-python-node-runtime-minimal-arm64
ghcr.io/openhands/agent-server:6d8dac6-python-slim-amd64
ghcr.io/openhands/agent-server:6d8dac619e5d59e2da88e730e9e2149cce590485-python-slim-amd64
ghcr.io/openhands/agent-server:vasco-ts-client-unreleased-contract-python-slim-amd64
ghcr.io/openhands/agent-server:6d8dac6-python-node-runtime-slim-amd64
ghcr.io/openhands/agent-server:6d8dac6-python-slim-arm64
ghcr.io/openhands/agent-server:6d8dac619e5d59e2da88e730e9e2149cce590485-python-slim-arm64
ghcr.io/openhands/agent-server:vasco-ts-client-unreleased-contract-python-slim-arm64
ghcr.io/openhands/agent-server:6d8dac6-python-node-runtime-slim-arm64
ghcr.io/openhands/agent-server:6d8dac6-golang
ghcr.io/openhands/agent-server:6d8dac619e5d59e2da88e730e9e2149cce590485-golang
ghcr.io/openhands/agent-server:vasco-ts-client-unreleased-contract-golang
ghcr.io/openhands/agent-server:6d8dac6-golang_tag_1.21-bookworm
ghcr.io/openhands/agent-server:6d8dac6-java
ghcr.io/openhands/agent-server:6d8dac619e5d59e2da88e730e9e2149cce590485-java
ghcr.io/openhands/agent-server:vasco-ts-client-unreleased-contract-java
ghcr.io/openhands/agent-server:6d8dac6-eclipse-temurin_tag_17-jdk
ghcr.io/openhands/agent-server:6d8dac6-python-minimal
ghcr.io/openhands/agent-server:6d8dac619e5d59e2da88e730e9e2149cce590485-python-minimal
ghcr.io/openhands/agent-server:vasco-ts-client-unreleased-contract-python-minimal
ghcr.io/openhands/agent-server:6d8dac6-python-node-runtime-minimal
ghcr.io/openhands/agent-server:6d8dac6-python-slim
ghcr.io/openhands/agent-server:6d8dac619e5d59e2da88e730e9e2149cce590485-python-slim
ghcr.io/openhands/agent-server:vasco-ts-client-unreleased-contract-python-slim
ghcr.io/openhands/agent-server:6d8dac6-python-node-runtime-slim
ghcr.io/openhands/agent-server:6d8dac6-python
ghcr.io/openhands/agent-server:6d8dac619e5d59e2da88e730e9e2149cce590485-python
ghcr.io/openhands/agent-server:vasco-ts-client-unreleased-contract-python
ghcr.io/openhands/agent-server:6d8dac6-python-node-runtime

About Multi-Architecture Support

  • Each variant tag (e.g., 6d8dac6-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., 6d8dac6-python-amd64) are also available if needed

…ease PRs

The release PR pins the TypeScript client to the version it is about to
publish, so that release's openapi.json and image only exist after merge.
When the pin equals the branch's agent-server version and is unpublished,
export the contract from source for the contract check and endpoint audit,
and skip the release-image smoke test.

Fixes #5511
@github-actions

github-actions Bot commented Oct 5, 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 5, 2026 12:14

@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).

Reviewed head 6d8dac619e5d59e2da88e730e9e2149cce590485 (matches the requested SHA; no prior review exists on this head). Scope is correct for this repository — the change is confined to .github/ workflows/scripts and clients/typescript/scripts/, and it does not change any package version.

Verified on the current head:

  • The new resolver is stdlib-only, and tests/cross/test_resolve_typescript_client_agent_server.py passes (5 passed). On main it reports pinned_is_source=false, use_source_contract=false, so non-release PRs keep the published-release path and make no network call.
  • The release-PR path reproduces on a scratch copy: with pin == source == 1.53.0 the resolver reports use_source_contract=true; uv sync --frozen --package openhands-agent-server still succeeds after the uv version bump (the release PR rewrites uv.lock, so --frozen is safe); the source export yields an OpenAPI document whose info.version equals the pinned version.
  • 404 handling matches the real endpoints (v1.51.0/v1.52.0 → 200, v1.53.0 → 404), and non-404 HTTP errors propagate and fail the job instead of silently falling back — the safe direction.
  • endpoint-audit.mjs forwards AGENT_SERVER_OPENAPI_PATH to the loader exactly as the generator already does, so both contract checks read the same explicit schema and the release PR's checked-in generated contract compares cleanly.
  • Current-head CI is green, including agent-server-api, endpoint-audit, smoke-test, integration-test, cross-tests, and pre-commit.

No material correctness, security, compatibility, or acceptance-criterion defect. The smoke-test skip is gated on both a missing image and pinned_is_source, so a missing image for any other pin still fails and the skip cannot persist once the image is published.

✅ APPROVED

@VascoSch92
VascoSch92 merged commit 091d451 into main Oct 5, 2026
61 of 62 checks passed
@VascoSch92
VascoSch92 deleted the vasco/ts-client-unreleased-contract branch October 5, 2026 12:43
VascoSch92 added a commit that referenced this pull request Oct 5, 2026
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.

TypeScript client checks fail on every release PR: pinned Agent Server is not published yet

3 participants