name: Agent marketplace release on: workflow_call: inputs: target_branch: description: Branch that receives release-please changes. required: false type: string default: main skill_root: description: Canonical skill directory relative to the repository root. required: false type: string default: skills claude_marketplace_file: description: Claude marketplace manifest relative to the repository root. required: false type: string default: .claude-plugin/marketplace.json claude_plugin_file: description: Claude plugin manifest relative to the repository root. required: false type: string default: .claude-plugin/plugin.json codex_plugin_root: description: Codex plugin root relative to the repository root. required: false type: string default: plugins/marketplace cursor_plugin_root: description: Cursor plugin root relative to the repository root. required: false type: string default: plugins/marketplace release_config_file: description: Release Please configuration file. required: false type: string default: release-please-config.json release_manifest_file: description: Release Please manifest file. required: false type: string default: .release-please-manifest.json package_paths: description: Space-separated paths to package into the release archive. required: false type: string default: skills .claude-plugin .cursor-plugin .agents plugins README.md CHANGELOG.md archive_name: description: Prefix used for the release archive. required: false type: string default: agent-marketplace claude_package_name: description: Public Gitea generic package name for the Claude release artifact. required: false type: string default: agent-marketplace-claude codex_package_name: description: Public Gitea generic package name for the Codex release artifact. required: false type: string default: agent-marketplace-codex claude_plugin_name: description: Claude plugin name used in the installation summary. required: false type: string default: marketplace codex_plugin_name: description: Codex plugin name used in the installation summary. required: false type: string default: marketplace claude_marketplace_name: description: Claude marketplace name used in the installation summary. required: false type: string default: marketplace codex_marketplace_name: description: Codex marketplace name shown in the installation summary. required: false type: string default: marketplace cursor_plugin_name: description: Cursor plugin name shown in the installation summary. required: false type: string default: marketplace publish_gitea: description: Upload public Claude and Codex installer packages to Gitea. required: false type: boolean default: false gitea_url: description: Base URL of the Gitea instance. required: false type: string default: "" gitea_user: description: Gitea user or organization that owns the generic package. required: false type: string default: "" secrets: GITHUB_PAT: description: Optional GitHub token for release-please pull requests. required: false GITEA_TOKEN: description: Gitea token with permission to publish generic packages. required: false permissions: contents: write pull-requests: write concurrency: group: release-please cancel-in-progress: false jobs: release-please: runs-on: ubuntu-latest outputs: release_created: ${{ steps.release.outputs.release_created }} tag_name: ${{ steps.release.outputs.tag_name }} version: ${{ steps.release.outputs.version }} steps: - name: Create or update release PR id: release uses: googleapis/release-please-action@v4 with: token: ${{ secrets.GITHUB_PAT || github.token }} target-branch: ${{ inputs.target_branch }} config-file: ${{ inputs.release_config_file }} manifest-file: ${{ inputs.release_manifest_file }} publish: needs: release-please if: needs.release-please.outputs.release_created == 'true' runs-on: ubuntu-latest permissions: contents: write steps: - name: Check out released version uses: actions/checkout@v4 with: ref: ${{ needs.release-please.outputs.tag_name }} - name: Validate released marketplace uses: walter-base/common-workflow/actions/validate-agent-marketplace@master with: skill_root: ${{ inputs.skill_root }} claude_marketplace_file: ${{ inputs.claude_marketplace_file }} claude_plugin_file: ${{ inputs.claude_plugin_file }} codex_plugin_root: ${{ inputs.codex_plugin_root }} cursor_plugin_root: ${{ inputs.cursor_plugin_root }} - name: Build release archive env: VERSION: ${{ needs.release-please.outputs.version }} PACKAGE_PATHS: ${{ inputs.package_paths }} ARCHIVE_NAME: ${{ inputs.archive_name }} shell: bash run: | dir="${ARCHIVE_NAME}-${VERSION}" mkdir -p "dist/${dir}" for path in ${PACKAGE_PATHS}; do cp -R "$path" "dist/${dir}/" done tar -czf "dist/${ARCHIVE_NAME}-claude-${VERSION}.tar.gz" -C dist "${dir}" tar -czf "dist/${ARCHIVE_NAME}-codex-${VERSION}.tar.gz" -C dist "${dir}" - name: Build Gitea installers if: inputs.publish_gitea env: ARCHIVE_NAME: ${{ inputs.archive_name }} CLAUDE_PACKAGE_NAME: ${{ inputs.claude_package_name }} CODEX_PACKAGE_NAME: ${{ inputs.codex_package_name }} GITEA_URL: ${{ inputs.gitea_url }} GITEA_USER: ${{ inputs.gitea_user }} VERSION: ${{ needs.release-please.outputs.version }} CLAUDE_PLUGIN_NAME: ${{ inputs.claude_plugin_name }} CLAUDE_MARKETPLACE_NAME: ${{ inputs.claude_marketplace_name }} CODEX_PLUGIN_NAME: ${{ inputs.codex_plugin_name }} CODEX_MARKETPLACE_NAME: ${{ inputs.codex_marketplace_name }} shell: bash run: | : "${GITEA_URL:?Set gitea_url when publish_gitea is enabled}" : "${GITEA_USER:?Set gitea_user when publish_gitea is enabled}" cat > "dist/install-${ARCHIVE_NAME}-claude-${VERSION}.sh" </dev/null 2>&1 || true claude plugin marketplace remove "\$marketplace_name" >/dev/null 2>&1 || true claude plugin marketplace add "\$install_dir" claude plugin install "${CLAUDE_PLUGIN_NAME}@\$marketplace_name" echo "Installed ${CLAUDE_PLUGIN_NAME} for Claude Code from Gitea marketplace \$marketplace_name." EOF chmod +x "dist/install-${ARCHIVE_NAME}-claude-${VERSION}.sh" cat > "dist/install-${ARCHIVE_NAME}-codex-${VERSION}.sh" </dev/null 2>&1 || true codex plugin marketplace remove "\$marketplace_name" >/dev/null 2>&1 || true codex plugin marketplace add "\$install_dir" codex plugin add "${CODEX_PLUGIN_NAME}@\$marketplace_name" echo "Installed ${CODEX_PLUGIN_NAME} for Codex from Gitea marketplace \$marketplace_name." EOF chmod +x "dist/install-${ARCHIVE_NAME}-codex-${VERSION}.sh" - name: Upload archive to Gitea if: inputs.publish_gitea id: gitea env: ARCHIVE_NAME: ${{ inputs.archive_name }} CLAUDE_PACKAGE_NAME: ${{ inputs.claude_package_name }} CODEX_PACKAGE_NAME: ${{ inputs.codex_package_name }} GITEA_URL: ${{ inputs.gitea_url }} GITEA_USER: ${{ inputs.gitea_user }} GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }} VERSION: ${{ needs.release-please.outputs.version }} run: | : "${GITEA_URL:?Set gitea_url when publish_gitea is enabled}" : "${GITEA_USER:?Set gitea_user when publish_gitea is enabled}" : "${GITEA_TOKEN:?Pass GITEA_TOKEN when publish_gitea is enabled}" claude_archive="${ARCHIVE_NAME}-claude-${VERSION}.tar.gz" codex_archive="${ARCHIVE_NAME}-codex-${VERSION}.tar.gz" claude_installer="install-${ARCHIVE_NAME}-claude-${VERSION}.sh" codex_installer="install-${ARCHIVE_NAME}-codex-${VERSION}.sh" claude_base="${GITEA_URL%/}/api/packages/${GITEA_USER}/generic/${CLAUDE_PACKAGE_NAME}/${VERSION}" codex_base="${GITEA_URL%/}/api/packages/${GITEA_USER}/generic/${CODEX_PACKAGE_NAME}/${VERSION}" for upload in \ "dist/${claude_archive}|${claude_base}/${claude_archive}" \ "dist/${claude_installer}|${claude_base}/${claude_installer}" \ "dist/${codex_archive}|${codex_base}/${codex_archive}" \ "dist/${codex_installer}|${codex_base}/${codex_installer}"; do file="${upload%%|*}" url="${upload#*|}" curl --fail-with-body --retry 3 \ --user "${GITEA_USER}:${GITEA_TOKEN}" \ --upload-file "$file" \ "$url" done echo "claude_installer_url=${claude_base}/${claude_installer}" >> "$GITHUB_OUTPUT" echo "codex_installer_url=${codex_base}/${codex_installer}" >> "$GITHUB_OUTPUT" - name: Upload archive to GitHub Release if: ${{ !inputs.publish_gitea }} uses: softprops/action-gh-release@v2 with: tag_name: ${{ needs.release-please.outputs.tag_name }} files: dist/* - name: Write GitHub installation summary env: VERSION: ${{ needs.release-please.outputs.version }} TAG: ${{ needs.release-please.outputs.tag_name }} REPOSITORY: ${{ github.repository }} SERVER_URL: ${{ github.server_url }} SKILL_ROOT: ${{ inputs.skill_root }} CLAUDE_PLUGIN_NAME: ${{ inputs.claude_plugin_name }} CLAUDE_MARKETPLACE_NAME: ${{ inputs.claude_marketplace_name }} CODEX_PLUGIN_NAME: ${{ inputs.codex_plugin_name }} CODEX_MARKETPLACE_NAME: ${{ inputs.codex_marketplace_name }} CURSOR_PLUGIN_NAME: ${{ inputs.cursor_plugin_name }} GITEA_CLAUDE_INSTALLER_URL: ${{ steps.gitea.outputs.claude_installer_url }} GITEA_CODEX_INSTALLER_URL: ${{ steps.gitea.outputs.codex_installer_url }} run: | { printf '# Agent plugin %s\n\n' "$VERSION" printf 'Release tag: `%s`\n\n' "$TAG" if [[ -n "$GITEA_CLAUDE_INSTALLER_URL" && -n "$GITEA_CODEX_INSTALLER_URL" ]]; then printf '## Claude Code\n\n' printf '%s\n' '```bash' printf 'curl --fail --location --output install-claude.sh "%s"\n' "$GITEA_CLAUDE_INSTALLER_URL" printf 'chmod +x install-claude.sh\n' printf './install-claude.sh\n' printf '%s\n\n' '```' printf '## Codex\n\n' printf '%s\n' '```bash' printf 'curl --fail --location --output install-codex.sh "%s"\n' "$GITEA_CODEX_INSTALLER_URL" printf 'chmod +x install-codex.sh\n' printf './install-codex.sh\n' printf '%s\n\n' '```' else printf '## Claude Code\n\n' printf '%s\n' '```bash' printf 'claude plugin marketplace add %s@%s\n' "$REPOSITORY" "$TAG" printf 'claude plugin install %s@%s\n' "$CLAUDE_PLUGIN_NAME" "$CLAUDE_MARKETPLACE_NAME" printf '%s\n\n' '```' printf '## Codex\n\n' printf '%s\n' '```bash' printf 'codex plugin marketplace add %s --ref %s\n' "$REPOSITORY" "$TAG" printf 'codex plugin add %s@%s\n' "$CODEX_PLUGIN_NAME" "$CODEX_MARKETPLACE_NAME" printf '%s\n\n' '```' fi printf '## OpenCode and Cursor\n\n' printf '%s\n' '```bash' printf 'npx skills add %s/%s/tree/%s/%s --agent opencode --skill '\''*'\'' --yes --global\n' "$SERVER_URL" "$REPOSITORY" "$TAG" "$SKILL_ROOT" printf 'npx skills add %s/%s/tree/%s/%s --agent cursor --skill '\''*'\'' --yes --global\n' "$SERVER_URL" "$REPOSITORY" "$TAG" "$SKILL_ROOT" printf '%s\n' '```' if [[ -n "$GITEA_CLAUDE_INSTALLER_URL" && -n "$GITEA_CODEX_INSTALLER_URL" ]]; then printf '\nInstallers are public Gitea artifacts; no Gitea token is required.\n' fi } >> "$GITHUB_STEP_SUMMARY"