diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..aa4d0d9 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,18 @@ +name: Release + +on: + push: + branches: [main] + +permissions: + contents: write + pull-requests: write + +jobs: + release-please: + runs-on: ubuntu-latest + steps: + - uses: googleapis/release-please-action@v4 + with: + config-file: .release-please-config.json + manifest-file: .release-please-manifest.json diff --git a/.release-please-config.json b/.release-please-config.json new file mode 100644 index 0000000..94c1cdd --- /dev/null +++ b/.release-please-config.json @@ -0,0 +1,29 @@ +{ + "$schema": "https://raw.githubusercontent.com/googleapis/release-please/main/schemas/config.json", + "packages": { + ".": { + "release-type": "node", + "package-name": "deploygate", + "include-component-in-tag": true, + "include-v-in-tag": true, + "tag-separator": "--", + "extra-files": [ + { + "type": "json", + "path": "plugin/.claude-plugin/plugin.json", + "jsonpath": "$.version" + }, + { + "type": "json", + "path": "plugin/.codex-plugin/plugin.json", + "jsonpath": "$.version" + }, + { + "type": "json", + "path": ".claude-plugin/marketplace.json", + "jsonpath": "$.plugins[0].version" + } + ] + } + } +} diff --git a/.release-please-manifest.json b/.release-please-manifest.json new file mode 100644 index 0000000..96f1cd9 --- /dev/null +++ b/.release-please-manifest.json @@ -0,0 +1,3 @@ +{ + ".": "1.3.0" +} diff --git a/README.md b/README.md index 7a53712..f358512 100644 --- a/README.md +++ b/README.md @@ -150,6 +150,37 @@ npm run build # Compile TypeScript + bundle with esbuild npm test # Run tests ``` +## Releasing + +Releases are automated by [release-please](https://github.com/googleapis/release-please). + +### Branching and commits + +- All work lands on `main` via squash-merged pull requests. +- PR titles must follow [Conventional Commits](https://www.conventionalcommits.org/): + - `feat: …` → minor version bump + - `fix: …` → patch version bump + - `feat!: …` or a body containing `BREAKING CHANGE: …` → major version bump + - `chore:`, `docs:`, `refactor:`, `test:`, `build:`, `ci:` → no version bump (and, by default, do not appear in the changelog unless `changelog-sections` is configured in `.release-please-config.json`) +- Individual commits inside a PR can have any title; only the squash-merge title matters. + +### How a release happens + +1. Merge a `feat:` or `fix:` PR into `main`. +2. The `Release` workflow opens (or updates) a Release PR that bumps the version in `package.json`, both `plugin/.claude-plugin/plugin.json` and `plugin/.codex-plugin/plugin.json`, the `deploygate` entry in `.claude-plugin/marketplace.json`, and `.release-please-manifest.json`, and appends to `CHANGELOG.md`. +3. Merge the Release PR. +4. The `Release` workflow runs again, creates the git tag `deploygate--vX.Y.Z`, and publishes a GitHub Release. + +### Installing a specific version + +End users can pin to a tag with: + +``` +/plugin install DeployGate/deploygate-agent-plugin@deploygate--v1.4.0 +``` + +Without a pin, `claude plugin install` follows `main`. Users can fetch the latest published tag with `claude plugin update deploygate`. + ## Support & Project Status This plugin is open-source software provided **as-is on a best-effort diff --git a/src/__tests__/plugin.test.ts b/src/__tests__/plugin.test.ts index 9f5efd3..2d3df12 100644 --- a/src/__tests__/plugin.test.ts +++ b/src/__tests__/plugin.test.ts @@ -187,3 +187,22 @@ describe("plugin/skills/ (slash commands)", () => { expect(content).toContain("upload_app"); }); }); + +describe("version sync across release manifests", () => { + it("package.json, both plugin.json files, the marketplace entry, and the release-please manifest share the same version", () => { + const pkg = loadJson("package.json"); + const codexPlugin = loadJson("plugin/.codex-plugin/plugin.json"); + const claudePlugin = loadJson("plugin/.claude-plugin/plugin.json"); + const marketplace = loadJson(".claude-plugin/marketplace.json"); + const manifest = loadJson(".release-please-manifest.json"); + + const version = pkg.version as string; + expect(version).toMatch(/^\d+\.\d+\.\d+$/); + expect(codexPlugin.version).toBe(version); + expect(claudePlugin.version).toBe(version); + const plugins = marketplace.plugins as Array<{ name: string; version: string }>; + const deploygateEntry = plugins.find((p) => p.name === "deploygate"); + expect(deploygateEntry?.version).toBe(version); + expect(manifest["."]).toBe(version); + }); +});