mirror of
https://github.com/walter-base/common-workflow.git
synced 2026-09-08 19:26:46 -04:00
feat: target the flat layout and the central marketplace
Three defects, all of which surfaced flattening the first repository. The two reusable workflows described the same layout differently: CI left codex_plugin_root and cursor_plugin_root empty while release defaulted both to a scaffold path. A repository that stopped shipping a nested plugin therefore passed CI and failed release, on a directory it had never had. Both now default to the repository itself, with cursor validation off unless asked for. claude_marketplace_file defaulted to a manifest that a plugin repository no longer carries, because the marketplace is generated centrally from the package registry rather than committed to each repo. The installation notes documented the flow this replaces — a per-release installer URL, or a marketplace add against the repo at a tag. With a central marketplace the instructions no longer mention the version at all: the manifest names the package and npm resolves the newest. Adds a dispatch step so a newly published plugin reaches the marketplace promptly. Gitea's registry_package event cannot serve here — it only fires for packages linked to the repository, and these are published from GitHub — so the generator is called over Gitea's API instead. The step never fails the release: the package is already published, and the generator's schedule catches up. Co-Authored-By: Claude Opus 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01MgsFmepr9FxbFkax5Y2h6z
This commit is contained in:
@@ -9,23 +9,33 @@ on:
|
|||||||
type: string
|
type: string
|
||||||
default: skills
|
default: skills
|
||||||
claude_marketplace_file:
|
claude_marketplace_file:
|
||||||
description: Claude marketplace manifest relative to the repository root.
|
description: >-
|
||||||
|
Claude marketplace manifest relative to the repository root. Empty by
|
||||||
|
default: a plugin repository no longer carries one, because the
|
||||||
|
central marketplace is generated from the package registry.
|
||||||
required: false
|
required: false
|
||||||
type: string
|
type: string
|
||||||
default: .claude-plugin/marketplace.json
|
default: ""
|
||||||
claude_plugin_file:
|
claude_plugin_file:
|
||||||
description: Claude plugin manifest relative to the repository root.
|
description: Claude plugin manifest relative to the repository root.
|
||||||
required: false
|
required: false
|
||||||
type: string
|
type: string
|
||||||
default: .claude-plugin/plugin.json
|
default: .claude-plugin/plugin.json
|
||||||
codex_plugin_root:
|
codex_plugin_root:
|
||||||
description: Codex plugin root relative to the repository root.
|
description: >-
|
||||||
|
Codex plugin root relative to the repository root. Defaults to the
|
||||||
|
repository itself, matching the release workflow — these two described
|
||||||
|
the same layout differently, so CI passed while release failed.
|
||||||
required: false
|
required: false
|
||||||
type: string
|
type: string
|
||||||
|
default: "."
|
||||||
cursor_plugin_root:
|
cursor_plugin_root:
|
||||||
description: Cursor plugin root relative to the repository root.
|
description: >-
|
||||||
|
Cursor plugin root relative to the repository root. Empty disables
|
||||||
|
cursor validation.
|
||||||
required: false
|
required: false
|
||||||
type: string
|
type: string
|
||||||
|
default: ""
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
|
|||||||
@@ -14,25 +14,53 @@ on:
|
|||||||
type: string
|
type: string
|
||||||
default: skills
|
default: skills
|
||||||
claude_marketplace_file:
|
claude_marketplace_file:
|
||||||
description: Claude marketplace manifest relative to the repository root.
|
description: >-
|
||||||
|
Claude marketplace manifest relative to the repository root. Empty by
|
||||||
|
default: a plugin repository no longer carries one, because the
|
||||||
|
central marketplace is generated from the package registry.
|
||||||
required: false
|
required: false
|
||||||
type: string
|
type: string
|
||||||
default: .claude-plugin/marketplace.json
|
default: ""
|
||||||
claude_plugin_file:
|
claude_plugin_file:
|
||||||
description: Claude plugin manifest relative to the repository root.
|
description: Claude plugin manifest relative to the repository root.
|
||||||
required: false
|
required: false
|
||||||
type: string
|
type: string
|
||||||
default: .claude-plugin/plugin.json
|
default: .claude-plugin/plugin.json
|
||||||
codex_plugin_root:
|
codex_plugin_root:
|
||||||
description: Codex plugin root relative to the repository root.
|
description: >-
|
||||||
|
Codex plugin root relative to the repository root. Defaults to the
|
||||||
|
repository itself: codex's npm plugin source has no path field, so the
|
||||||
|
package root must be the plugin root.
|
||||||
required: false
|
required: false
|
||||||
type: string
|
type: string
|
||||||
default: plugins/marketplace
|
default: "."
|
||||||
cursor_plugin_root:
|
cursor_plugin_root:
|
||||||
description: Cursor plugin root relative to the repository root.
|
description: >-
|
||||||
|
Cursor plugin root relative to the repository root. Empty disables
|
||||||
|
cursor validation. It defaulted to a scaffold path, which meant every
|
||||||
|
caller that stopped shipping a cursor bundle had to remember to blank
|
||||||
|
it or CI would demand a directory that never existed.
|
||||||
required: false
|
required: false
|
||||||
type: string
|
type: string
|
||||||
default: plugins/marketplace
|
default: ""
|
||||||
|
marketplace_url:
|
||||||
|
description: >-
|
||||||
|
URL of the central marketplace manifest, printed in the release notes
|
||||||
|
as the way to install. Empty falls back to the per-release
|
||||||
|
instructions.
|
||||||
|
required: false
|
||||||
|
type: string
|
||||||
|
default: ""
|
||||||
|
marketplace_dispatch_url:
|
||||||
|
description: >-
|
||||||
|
Gitea workflow-dispatch endpoint for the manifest generator. Called
|
||||||
|
after publishing so the marketplace reflects a new plugin promptly
|
||||||
|
rather than at the next scheduled run. Gitea's own registry_package
|
||||||
|
event cannot be used: it only fires for packages linked to the
|
||||||
|
repository, and these are published from here.
|
||||||
|
required: false
|
||||||
|
type: string
|
||||||
|
default: ""
|
||||||
release_config_file:
|
release_config_file:
|
||||||
description: Release Please configuration file.
|
description: Release Please configuration file.
|
||||||
required: false
|
required: false
|
||||||
@@ -128,6 +156,11 @@ on:
|
|||||||
GITHUB_PAT:
|
GITHUB_PAT:
|
||||||
description: Optional GitHub token for release-please pull requests.
|
description: Optional GitHub token for release-please pull requests.
|
||||||
required: false
|
required: false
|
||||||
|
MARKETPLACE_DISPATCH_TOKEN:
|
||||||
|
description: >-
|
||||||
|
Gitea token permitted to dispatch the manifest generator workflow.
|
||||||
|
Only needed when marketplace_dispatch_url is set.
|
||||||
|
required: false
|
||||||
GITEA_TOKEN:
|
GITEA_TOKEN:
|
||||||
description: Gitea token with permission to publish generic packages.
|
description: Gitea token with permission to publish generic packages.
|
||||||
required: false
|
required: false
|
||||||
@@ -479,6 +512,38 @@ jobs:
|
|||||||
tag_name: ${{ needs.release-please.outputs.tag_name }}
|
tag_name: ${{ needs.release-please.outputs.tag_name }}
|
||||||
files: dist/*
|
files: dist/*
|
||||||
|
|
||||||
|
- name: Refresh the central marketplace
|
||||||
|
# The manifest is derived from the package registry, so it only needs
|
||||||
|
# regenerating when the set of plugins changes — a new version of an
|
||||||
|
# existing plugin leaves it byte-identical, because entries carry no
|
||||||
|
# version and npm resolves `latest`. This call exists so a *new* plugin
|
||||||
|
# appears immediately rather than at the next scheduled run.
|
||||||
|
if: inputs.marketplace_dispatch_url != ''
|
||||||
|
env:
|
||||||
|
DISPATCH_URL: ${{ inputs.marketplace_dispatch_url }}
|
||||||
|
DISPATCH_TOKEN: ${{ secrets.MARKETPLACE_DISPATCH_TOKEN }}
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
if [[ -z "${DISPATCH_TOKEN:-}" ]]; then
|
||||||
|
echo "marketplace_dispatch_url is set but MARKETPLACE_DISPATCH_TOKEN is not; skipping." >&2
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
# Never fail the release over this: the package is already published,
|
||||||
|
# and the generator's schedule will pick it up regardless. Failing here
|
||||||
|
# would turn a delayed catalogue into a red release.
|
||||||
|
status="$(curl --silent --show-error --output /dev/stderr --write-out '%{http_code}' \
|
||||||
|
--max-time 30 -X POST \
|
||||||
|
-H "Authorization: token ${DISPATCH_TOKEN}" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d '{"ref":"main"}' \
|
||||||
|
"$DISPATCH_URL" || true)"
|
||||||
|
if [[ "$status" == "204" || "$status" == "201" || "$status" == "200" ]]; then
|
||||||
|
echo "Requested marketplace regeneration (HTTP $status)."
|
||||||
|
else
|
||||||
|
echo "Could not reach the manifest generator (HTTP $status); the scheduled run will catch up." >&2
|
||||||
|
fi
|
||||||
|
|
||||||
- name: Write GitHub installation summary
|
- name: Write GitHub installation summary
|
||||||
env:
|
env:
|
||||||
VERSION: ${{ needs.release-please.outputs.version }}
|
VERSION: ${{ needs.release-please.outputs.version }}
|
||||||
@@ -495,6 +560,7 @@ jobs:
|
|||||||
GITEA_INSTALLER_URL: ${{ steps.gitea.outputs.installer_url }}
|
GITEA_INSTALLER_URL: ${{ steps.gitea.outputs.installer_url }}
|
||||||
NPM_PACKAGE: ${{ steps.npm.outputs.npm_package }}
|
NPM_PACKAGE: ${{ steps.npm.outputs.npm_package }}
|
||||||
NPM_REGISTRY_URL: ${{ steps.npm.outputs.npm_registry }}
|
NPM_REGISTRY_URL: ${{ steps.npm.outputs.npm_registry }}
|
||||||
|
MARKETPLACE_URL: ${{ inputs.marketplace_url }}
|
||||||
run: |
|
run: |
|
||||||
{
|
{
|
||||||
printf '# Agent plugin %s\n\n' "$VERSION"
|
printf '# Agent plugin %s\n\n' "$VERSION"
|
||||||
@@ -511,7 +577,21 @@ jobs:
|
|||||||
printf '%s\n\n' '```'
|
printf '%s\n\n' '```'
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ -n "$GITEA_INSTALLER_URL" ]]; then
|
if [[ -n "$MARKETPLACE_URL" ]]; then
|
||||||
|
# One marketplace lists every plugin, so these instructions do
|
||||||
|
# not change per release and never name this version: the
|
||||||
|
# manifest points at the package and npm resolves the newest.
|
||||||
|
printf '## Claude Code\n\n'
|
||||||
|
printf '%s\n' '```bash'
|
||||||
|
printf 'claude plugin marketplace add %s\n' "$MARKETPLACE_URL"
|
||||||
|
printf 'claude plugin install %s@%s\n' "$CLAUDE_PLUGIN_NAME" "$CLAUDE_MARKETPLACE_NAME"
|
||||||
|
printf '%s\n\n' '```'
|
||||||
|
|
||||||
|
printf 'Later releases arrive with:\n\n'
|
||||||
|
printf '%s\n' '```bash'
|
||||||
|
printf 'claude plugin marketplace update %s\n' "$CLAUDE_MARKETPLACE_NAME"
|
||||||
|
printf '%s\n\n' '```'
|
||||||
|
elif [[ -n "$GITEA_INSTALLER_URL" ]]; then
|
||||||
printf '## Claude Code\n\n'
|
printf '## Claude Code\n\n'
|
||||||
printf '%s\n' '```bash'
|
printf '%s\n' '```bash'
|
||||||
printf 'curl --fail --location --retry 3 "%s" | bash -s -- claude\n' "$GITEA_INSTALLER_URL"
|
printf 'curl --fail --location --retry 3 "%s" | bash -s -- claude\n' "$GITEA_INSTALLER_URL"
|
||||||
|
|||||||
Reference in New Issue
Block a user