diff --git a/.claude/skills/prepare-release/SKILL.md b/.claude/skills/prepare-release/SKILL.md new file mode 100644 index 0000000..76814b3 --- /dev/null +++ b/.claude/skills/prepare-release/SKILL.md @@ -0,0 +1,42 @@ +--- +name: prepare-release +description: Use when cutting a new release of this plugin — bumping the version, updating the changelog, and refreshing the README compatibility tables before opening the release PR. Triggers on "prepare a release", "cut X.Y.Z", "bump the version", "release prep". +--- + +# Prepare a release + +Stage the release changes on a branch and stop for human approval before +committing/pushing. See `CLAUDE.md` for the version/tag facts. + +## Steps + +1. **Branch** off `master`: `chore/release-X.Y.Z` (or extend an existing + release-prep branch). + +2. **Bump the version** in `src/main/resources/VERSION` to `X.Y.Z` (repo-root + `VERSION` is a symlink to it). `release.yml` asserts this equals the git tag, + so it must match the tag you will push (no `v` prefix). + +3. **CHANGELOG.md** — rename the `## Unreleased` section to `## ver X.Y.Z`. List + user-facing changes and a `### Breaking Changes` subsection if any. Reference + PRs as `[#NNN](...)`. (Do not list dev-only tooling like Spotless bumps.) + +4. **README.md and README_JP.md (keep both in sync):** + - Version-compatibility table: set the right-hand column for newly + unsupported AGP rows to `\>=2.4.0,\`. + +2. **Start the mock server** (required for `ApiClientSpec` and the upload specs; + without it they hit `https://deploygate.com` and fail with `NetworkFailure` / + `Connection reset`): + ```bash + docker run -d --name dg-mock -p 3000:3000 ghcr.io/deploygate/deploygate-mock-server:main + # Apple Silicon: add --platform linux/amd64. If the port is taken, use another and adjust the URL. + export TEST_SERVER_URL=http://localhost:3000 + until curl -fsI "$TEST_SERVER_URL" >/dev/null 2>&1; do sleep 2; done + ``` + +3. **Pick the job to mirror:** + | CI job | Command | + |---|---| + | `unit-test` | `./gradlew publishToMavenLocal test` | + | `lint` | `./gradlew spotlessApply` | + | `acceptance-test` | `./gradlew testUnrollAcceptanceTest` | + | `acceptance-test-runtime-env` (one row) | `TEST_AGP_VERSION= TEST_GRADLE_VERSION= ./gradlew --no-daemon testPluginAcceptanceTest` | + + Add `--tests "*SomeSpec*"` to narrow a run. + +4. **Clean up:** `docker rm -f dg-mock`. + +## Gotchas + +- **`--no-daemon` is mandatory when setting `TEST_GRADLE_VERSION`** — a reused + daemon caches the env from its first start, so GradleRunner reads a stale value + and throws `'null' is not a valid Gradle version string`. +- ProjectBuilder unit specs fail with `Problems service is not initialized` on + Gradle 8.12/8.13 (gradle/gradle#31862) — the wrapper must avoid those. +- To exercise a different build-Gradle without touching the committed wrapper, + download that distribution and run `gradle -p ` from it. diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..50341bb --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,67 @@ +# CLAUDE.md — gradle-deploygate-plugin + +Project-specific knowledge for working in this repo. Version numbers (minimum +AGP/Gradle/JRE, wrapper version) live in `README.md` / `build.gradle` — treat +those as the source of truth rather than duplicating them here. + +## Local verification (reproducing CI) + +Gradle commands need to write `~/.gradle` and reach the network, so run them +**outside the command sandbox**. Use a JDK that satisfies the wrapper's Gradle +version (the build/dev baseline is JDK 17). + +- **Mock server (required for HTTP specs).** `ApiClientSpec` and the upload + acceptance specs talk to `TEST_SERVER_URL`; without it they hit + `https://deploygate.com` and fail with `NetworkFailure` / `Connection reset`. + Start it and export the URL: + ``` + docker run -d --name dg-mock -p 3000:3000 ghcr.io/deploygate/deploygate-mock-server:main + # Apple Silicon: add --platform linux/amd64 + export TEST_SERVER_URL=http://localhost:3000 + ``` +- **Unit tests:** `./gradlew test` (needs `TEST_SERVER_URL`). These are + `ProjectBuilder`-based and run on the wrapper's Gradle. +- **Plugin acceptance vs a specific AGP/Gradle:** + ``` + TEST_AGP_VERSION=8.0.0 TEST_GRADLE_VERSION=8.0 \ + ./gradlew --no-daemon testPluginAcceptanceTest --tests "*ConfigurationCacheSpec*" + ``` + **`--no-daemon` is mandatory** when setting `TEST_GRADLE_VERSION`: the Gradle + daemon caches env vars from its first start, so a reused daemon reads a stale + value and GradleRunner throws `'null' is not a valid Gradle version string`. +- **Unroll acceptance:** `./gradlew testUnrollAcceptanceTest`. +- **Format/lint:** `./gradlew spotlessApply`. + +## Build / version gotchas + +- **ProjectBuilder + Gradle 8.12/8.13** throws `Problems service is not + initialized` in the unit tests (gradle/gradle#31862); fixed in 8.14. Keep the + wrapper off 8.12/8.13. +- **Spotless 8.x** requires Gradle 8.1+ and Java 17. It is applied only on + Gradle 8.1+ (guard in `build.gradle`) so the lowest acceptance-matrix Gradle + row still validates the stated minimum. +- The **wrapper Gradle is the build toolchain**, intentionally separate from the + minimum Gradle that *users* of the plugin need. +- CI's `.github/actions/setup-java` installs Java 11 for `sdkmanager`, then the + `java-version` input becomes the active JDK; build jobs pass `java-version: 17`. + +## Release + +- The version is in `src/main/resources/VERSION` (repo-root `VERSION` is a + symlink). Bump it in a `chore: bump up version to X.Y.Z` commit. +- `.github/workflows/release.yml` asserts `VERSION` equals the pushed tag (no + `v` prefix) — tag exactly `X.Y.Z`. +- Keep **both** `README.md` and `README_JP.md` in sync (compatibility tables, + notes). Android Studio code names come from the official AGP compatibility + table. +- `CHANGELOG.md`: add a `## Unreleased` section only when there are entries; + rename it to `## ver X.Y.Z` at release time. + +## PR / review conventions + +- Reply to review comments — **including bots (devin, gemini, Copilot)** — in + the comment thread with an `@mention` of the author: + `gh api repos/{owner}/{repo}/pulls/{pr}/comments/{id}/replies -f body=...` + (not as a top-level PR comment). +- Bot findings are suggestions to verify, not orders. Push back with evidence + when they are wrong for this codebase.