From 16464d0347c5fb4f333c9579c0aa4cfe71b5dd14 Mon Sep 17 00:00:00 2001 From: showxu <10173746+showxu@users.noreply.github.com> Date: Wed, 21 Oct 2026 21:56:34 +0800 Subject: [PATCH] feat: adopt plugin API and release standards --- .github/workflows/release.yml | 31 +++++++ .../Architecture/VersioningAndRelease.md | 67 +++++++-------- Documentation/Reference/Release.md | 85 ++++++++++++------- Tests/test_build_package.py | 3 + computer-mcp-plugin.toml | 1 + 5 files changed, 122 insertions(+), 65 deletions(-) create mode 100644 .github/workflows/release.yml diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..549d87b --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,31 @@ +name: Release +on: + workflow_dispatch: + inputs: + ref: + description: Version tag, or commit for a dry run + type: string + required: true + publish: + description: Publish the verified release + type: boolean + default: false +permissions: {} +jobs: + release: + permissions: + contents: write + actions: read + uses: computer-mcp/.github/.github/workflows/plugin-release.yml@35766b350427ed900b456aa3c39098e97d9d3aa1 # master + with: + ref: ${{ inputs.ref }} + publish: ${{ inputs.publish }} + build: Scripts/build-package.sh + ci_artifact: computer-use-plugin-${commit} + notify-catalog: + needs: release + if: inputs.publish && needs.release.outputs.published == 'true' + permissions: {} + uses: ./.github/workflows/notify-catalog.yml + secrets: + CATALOG_APP_PRIVATE_KEY: ${{ secrets.CATALOG_APP_PRIVATE_KEY }} diff --git a/Documentation/Architecture/VersioningAndRelease.md b/Documentation/Architecture/VersioningAndRelease.md index f1b871e..ed509a0 100644 --- a/Documentation/Architecture/VersioningAndRelease.md +++ b/Documentation/Architecture/VersioningAndRelease.md @@ -1,27 +1,22 @@ # Versioning and Release -Release status: partly manual. CI builds and checks candidate archives on every -pull request and `master` push; tagging, acceptance and publication are manual -steps described in [Release](../Reference/Release.md). - This document owns the current version and release rules for -plugin-computer-use. +plugin-computer-use. Shared versioning, plugin compatibility, tags, release +contents and recovery rules belong to the organization's +[VERSIONING.md](https://github.com/computer-mcp/.github/blob/master/VERSIONING.md). +The [release guide](../Reference/Release.md) describes this repository's +acceptance and workflow procedure. ## Components and Version Authority | Shipped component | Version meaning | Authoritative source | Derived fields | Update and read-only check | | --- | --- | --- | --- | --- | -| `computer-use.zip` plugin package | SemVer 2.0.0 package version | `version` in `computer-mcp-plugin.toml` | builder receipt, tag `vX.Y.Z`, catalog entry, the App's bundled index | Edit the manifest; agreement with the tag is checked manually | - -Compatible fixes advance the patch number, compatible features the minor number, -and incompatible changes the major number. Compatibility is established by -review and tests, not by comparing version numbers. +| `computer-use.zip` plugin package | Package version | `version` in `computer-mcp-plugin.toml` | builder and release receipts, version tag, catalog entry, the App's bundled index | Edit the manifest; `release.yml` checks agreement with the tag | +| Manifest and host runtime contract | Plugin API version | `plugin_api` in `computer-mcp-plugin.toml` | release receipt and host compatibility evaluation | Package tests check the declaration; `release.yml` verifies it | The archive contains the README, contribution guide, manifest, license, notices, -`skills/` and `Documentation/`. Any change to those files ships only under a new -version; a published version is never rebuilt from different bytes. Changes to -tests, scripts or workflows alone do not change the archive and do not require a -release. +`skills/` and `Documentation/`. Changes to tests, scripts or workflows alone do +not change the archive. ## Dependencies and Verified Combinations @@ -30,9 +25,9 @@ client and ships no runtime code. The manifest locates that client by command name or application bundle, and the vendor owns its installation and caller authentication. -`minimum_host` in `[compatibility]` declares the oldest Computer MCP host that -reads every manifest field this package uses. Raise it only when the package -needs a newer host contract, after validating against that host. +The manifest owns this package's plugin API declaration and host compatibility +bounds. A host-version constraint is changed only after validating the package +against the required host. The builder needs only the Python 3.11 standard library. CI runs the tests and build on Python 3.11 and 3.13. @@ -41,26 +36,30 @@ build on Python 3.11 and 3.13. `Scripts/build_package.py` verifies the plugin ID, skips hidden files and Python caches, rejects links and special files, and produces the same archive bytes -from the same source. `Tests/test_build_package.py` pins the exact archive file -set. The builder receipt prints the ID, version and SHA-256. +from the same source. `Tests/test_build_package.py` checks the plugin API +declaration and pins the exact archive file set. The builder receipt prints the +ID, version and SHA-256. ## Candidate, Acceptance and Publication -The candidate is the CI artifact for a reviewed `master` commit. Acceptance -requires a byte-identical local rebuild, host package validation and -installation of that exact archive, and native catalog discovery with the -installed vendor client. Native actions remain subject to the vendor's caller -authentication and system permissions; acceptance records what was observed and -does not assume a working action path. +The candidate is the `computer-use-plugin-` artifact from a successful +`master` push run of `ci.yml` for a reviewed commit. Acceptance requires a +byte-identical local rebuild, host package validation and installation of that +exact archive, and native catalog discovery with the installed vendor client. +Native actions remain subject to the vendor's caller authentication and system +permissions; acceptance records what was observed and does not assume a working +action path. -A signed annotated `vX.Y.Z` tag binds the accepted commit, and the GitHub -Release publishes that exact archive with its builder receipt. Published tags -and archives are immutable; a defect is fixed in a new version. Publishing -triggers the catalog notification. +`release.yml` calls the shared release workflow with `Scripts/build-package.sh` +and this repository's exact CI artifact name. Manual acceptance results belong +in the Release notes. After verified publication, its dependent notifier calls +`notify-catalog.yml`. The Computer MCP App bundles a copy of this package, pinned by version and -SHA-256 in the App repository's `Resources/PluginArchives/index.json`. Updating -the bundled copy is an App change made after the release is public. +SHA-256 in the App repository's `Resources/PluginArchives/index.json`. Before +the first release, the host may bundle an archive reproducibly built from an +accepted commit before that plugin version is published. A host release +requires the plugin release with the same archive digest to be public first. ## Evidence Reuse and Invalidation @@ -74,9 +73,9 @@ affected checks again. | Operation | Existing command or explicit manual procedure | Required access | | --- | --- | --- | | Version update and check | Edit `computer-mcp-plugin.toml`; `python3 -m unittest discover -s Tests -p 'test_*.py'` | Local checkout | -| Candidate validation and build | `ci.yml`; locally `Scripts/build-package.sh ` | CI or local checkout | -| Status and interrupted-run recovery | Rerun the failed CI job or check; outputs go to new directories | Repository Actions | -| Acceptance and publication | Manual steps in [Release](../Reference/Release.md) | Signing key and release write access | +| Candidate validation and build | `ci.yml`; locally `Scripts/build-package.sh ` | CI or local checkout | +| Release verification and publication | Dispatch `release.yml`, dry run first; manual acceptance in [Release](../Reference/Release.md) | Repository Actions; publication also needs a signed tag and release write access | +| Status and interrupted-run recovery | Rerun failed workflow jobs; follow the shared recovery rules | Repository Actions | | Cleanup | Delete local output and evidence directories | Local checkout | CI keeps candidate artifacts for 14 days. The builder refuses to overwrite a diff --git a/Documentation/Reference/Release.md b/Documentation/Reference/Release.md index e0abb84..cf3e20e 100644 --- a/Documentation/Reference/Release.md +++ b/Documentation/Reference/Release.md @@ -1,33 +1,64 @@ # Release -This guide covers publishing an accepted package and notifying the official +This guide covers publishing an accepted package through +[`release.yml`](../../.github/workflows/release.yml) and notifying the official plugin catalog. [Versioning and Release](../Architecture/VersioningAndRelease.md) -owns the version, acceptance and immutability rules. +owns this repository's version and acceptance rules. The organization's +[VERSIONING.md](https://github.com/computer-mcp/.github/blob/master/VERSIONING.md) +and [release workflow guide](https://github.com/computer-mcp/.github/blob/master/Documentation/Reference/PluginRelease.md) +own the shared release contents, immutability and recovery contract. + +## Prerequisite + +Before the first release, the repository owner must enable GitHub release +immutability in repository settings. The release workflow checks that setting +before any Release write and refuses publication if it is disabled or cannot +be verified. Enabling it does not lock previously published releases; an +existing public release must itself be immutable to be accepted by the workflow. ## Publish 1. Confirm that `version` in `computer-mcp-plugin.toml` has not been released - and that the reviewed `master` commit has a successful CI run. -2. Download that run's `computer-use-plugin-` artifact. Rebuild the same - commit with `Scripts/build-package.sh ` and confirm the two - `computer-use.zip` files are byte-identical. -3. Validate and install that exact archive with the selected Computer MCP host, - and confirm native catalog discovery with the installed vendor client. GUI - checks use a disposable, explicitly authorized target. + and that the reviewed `master` commit has a successful `push` run of + `ci.yml`, including its `computer-use-plugin-` artifact. +2. Dispatch `release.yml` with `ref` set to that full commit SHA and `publish` + set to `false` (the default). The workflow rebuilds with + `Scripts/build-package.sh` and compares the archive bytes with the exact CI + artifact. Inspect the `plugin-release--` Actions artifact: + `assets/` contains `computer-use.zip`, `release-receipt.json` and `SHA256SUMS`. + An untagged dry run reports the signed annotated tag still required for + publication. +3. Rebuild the same commit with `Scripts/build-package.sh ` + and confirm it matches the dry-run archive. Validate and install that exact + archive with the selected Computer MCP host, and confirm native catalog + discovery with the installed vendor client. GUI checks use a disposable, + explicitly authorized target. 4. Create a signed annotated tag `vX.Y.Z` on the accepted commit and push it. -5. Create the GitHub Release for the tag with `computer-use.zip` and the CI - run's `package-receipt.json`, then publish it. + Create a draft GitHub Release for that tag, targeting the full commit SHA, + and record the manual acceptance results and their limits in its notes. +5. Dispatch `release.yml` with `ref` set to that version tag and `publish` set + to `true`. The workflow verifies the tag, manifest, CI source and archive + bytes, completes the draft's assets and publishes the verified release. + Confirm the public release is immutable and its archive digest matches the + accepted archive. -`package-receipt.json` is the builder's receipt: the plugin ID, version, archive -path and SHA-256. +The public assets are `computer-use.zip`, the shared `release-receipt.json` and +`SHA256SUMS`. CI's `package-receipt.json` remains builder evidence in the CI +artifact. ## Catalog notification -`notify-catalog.yml` runs when a release is published, edited, released, -unpublished or deleted, and on manual dispatch. It asks the website to reconcile -the complete catalog. The website verifies the actual GitHub releases rather -than the event payload, keeps verified history, and applies its own withdrawal -policy; an event never withdraws a release by itself. +`release.yml` calls `notify-catalog.yml` only when `publish` is `true` and the +shared release workflow reports `published: true`. Only the notifier job +receives the catalog App private key; the shared release workflow receives no +secret inputs. This explicit call handles publication through `GITHUB_TOKEN`, +which does not trigger release-event workflows. + +`notify-catalog.yml` also runs when a release is published, edited, released, +unpublished or deleted, and on manual dispatch. It asks the website to +reconcile the complete catalog. The website verifies the actual GitHub +releases rather than the event payload, keeps verified history, and applies +its own withdrawal policy; an event never withdraws a release by itself. The workflow authenticates as the receiver-scoped catalog GitHub App through the `CATALOG_APP_CLIENT_ID` variable and the `CATALOG_APP_PRIVATE_KEY` secret. @@ -40,21 +71,13 @@ The website's notification action is pinned to a reviewed full commit. Publish that website commit first, then update the pin here when adopting a change to the notification contract. -A release published with a repository `GITHUB_TOKEN` does not trigger -release-event workflows. Automation that publishes that way calls the workflow -as a dependent of the job that makes the release public: - -```yaml -notify-catalog: - needs: publish - uses: ./.github/workflows/notify-catalog.yml - secrets: - CATALOG_APP_PRIVATE_KEY: ${{ secrets.CATALOG_APP_PRIVATE_KEY }} -``` - ## Retry -For an operator-driven publication or a missed or failed notification, dispatch: +For an interrupted publication, use **Re-run failed jobs** and follow the shared +workflow guide's recovery rules. If verification already succeeded, the publish +job reuses its verified bundle and digest outputs. + +For a missed or failed notification, rerun only the notifier job or dispatch: ```sh gh workflow run notify-catalog.yml --repo computer-mcp/plugin-computer-use --ref master diff --git a/Tests/test_build_package.py b/Tests/test_build_package.py index 0501beb..9efda48 100644 --- a/Tests/test_build_package.py +++ b/Tests/test_build_package.py @@ -20,6 +20,9 @@ def test_reproducible_owned_contents(self): second, _ = BUILDER.package_bytes(ROOT) self.assertEqual(first, second) self.assertEqual(manifest["id"], "computer-use") + self.assertIs(type(manifest["plugin_api"]), int) + self.assertEqual(manifest["plugin_api"], 1) + self.assertEqual(manifest["compatibility"]["minimum_host"], "0.1.0") with zipfile.ZipFile(io.BytesIO(first)) as archive: self.assertEqual(set(archive.namelist()), { "README.md", "computer-mcp-plugin.toml", diff --git a/computer-mcp-plugin.toml b/computer-mcp-plugin.toml index 3d7f81f..386259e 100644 --- a/computer-mcp-plugin.toml +++ b/computer-mcp-plugin.toml @@ -1,6 +1,7 @@ id = 'computer-use' name = 'Computer Use' version = '0.1.0' +plugin_api = 1 description = 'Native Computer Use MCP registration and observe–act–verify guidance.' [[dependencies]]