From ba4991b7e7f89dc0586edba31dfa71634dbc651b Mon Sep 17 00:00:00 2001 From: Hal Eisen Date: Mon, 7 Sep 2026 15:37:07 -0700 Subject: [PATCH] chore: refuse hand-built libs/ jars in pull requests libs/ holds build outputs. The "Update libs from CodeOnTheGo" workflow builds them on JDK 17, the JDK every runner here uses, and pushes to main with an admin PAT that bypasses the main ruleset. A laptop on JDK 21 writes class file version 65 into gradle-plugin.jar instead; a JDK 17 runner reads only up to 61. PR #87 shipped such a jar, so "Publish addons" failed with UnsupportedClassVersionError on a tree that built fine on the machine that produced it. Every human change reaches main through a pull request, so a PR-level check closes the laptop path without touching the CI path. Claude-Session: https://claude.ai/code/session_01XWXk1HTqbD46eYmnfAMPNv --- .github/workflows/check-toolchain.yml | 26 ++++++++++++++++++++++++++ CLAUDE.md | 4 +++- 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/.github/workflows/check-toolchain.yml b/.github/workflows/check-toolchain.yml index 9dfd001e..538cda73 100644 --- a/.github/workflows/check-toolchain.yml +++ b/.github/workflows/check-toolchain.yml @@ -20,6 +20,8 @@ on: permissions: contents: read + # the libs/ guard below reads the pull request's changed file list + pull-requests: read jobs: check-toolchain: @@ -29,6 +31,30 @@ jobs: - name: Checkout uses: actions/checkout@v4 + # libs/ holds build outputs, not source. Only the "Update libs from + # CodeOnTheGo" workflow may change them. It builds the jars on JDK 17, + # which is the JDK every runner here uses, so what it commits always + # loads. A laptop on JDK 21 emits class file version 65 instead, and a + # JDK 17 runner reads only up to 61 -- that is what broke "Publish + # addons" after #87 shipped hand-built jars. That workflow pushes + # straight to main with an admin PAT and bypasses the main ruleset, so + # this check never sees it. + - name: Refuse hand-built libs/ jars + if: github.event_name == 'pull_request' + env: + GH_TOKEN: ${{ github.token }} + PR: ${{ github.event.pull_request.number }} + run: | + set -euo pipefail + changed="$(gh api --paginate \ + "repos/${GITHUB_REPOSITORY}/pulls/${PR}/files" \ + -q '.[].filename' | grep '^libs/' || true)" + if [ -n "$changed" ]; then + printf '%s\n' "$changed" + echo "::error::A pull request must not change libs/. Run the 'Update libs from CodeOnTheGo' workflow instead -- it builds the jars on the JDK the runners use." + exit 1 + fi + - name: Check toolchain versions run: ./scripts/check-toolchain.sh diff --git a/CLAUDE.md b/CLAUDE.md index f11790d4..90db18d9 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -26,6 +26,8 @@ Build every plugin from scratch (after rebuilding libs): The script clones CoGo into `.cache/CodeOnTheGo/` on first run, rebuilds both jars, copies them into `libs/`, then runs `assemblePlugin` for every example. It auto-detects examples by scanning for `build.gradle.kts` files that apply `com.itsaky.androidide.plugins.build`. +**Never commit the jars this script writes.** Run it locally only to test a CoGo change before it lands; throw the `libs/` diff away afterwards (`git restore libs/`). `libs/` on `main` may only be changed by the **Update libs from CodeOnTheGo** workflow, which builds on JDK 17 — the JDK every runner and every other workflow here uses. A laptop on JDK 21 writes class file version 65 into `gradle-plugin.jar`; a JDK 17 runner reads only up to 61, so `assemblePlugin` dies with `UnsupportedClassVersionError` in CI while it still works on the laptop that produced it. PR #87 shipped such a jar and broke **Publish addons**. `Check toolchain` now fails any pull request that touches `libs/`. + `local.properties` must contain `sdk.dir=...`. The committed `local.properties` at the repo root is harmless leftover; each plugin needs its own. ## Git workflow @@ -44,7 +46,7 @@ The script clones CoGo into `.cache/CodeOnTheGo/` on first run, rebuilds both ja There is also **one shared Gradle wrapper at the repo root** (`gradlew` + `gradle/wrapper/`). New plugins should use it — build them with `cd plugins/ && ../../gradlew assemblePlugin` rather than bundling a per-plugin `gradlew`/`gradle/wrapper/` copy. (`flutter-template` follows this; most older plugins still carry their own local wrapper and can be migrated opportunistically.) -An addon under `plugins/` references the shared jars as `../../libs/*.jar`. **Always use the repo-root `libs/` jars and the repo-root Gradle wrapper — never bundle per-plugin copies.** A plugin that ships its own `libs/plugin-api.jar` / `libs/gradle-plugin.jar` (e.g. copied from another plugin) can drift out of sync with the rest of the repo; point `build.gradle.kts` (`compileOnly`) and `settings.gradle.kts` (buildscript `classpath`) at `../../libs/*.jar` and delete any local `libs/`. The root `plugin-api.jar` already carries the full API surface (including `IdeTemplateService`/`CgtTemplateBuilder`), so newer sub-APIs do not justify a local copy. **A plugin folder is not standalone in isolation** — copy the root `libs/` along if you move one elsewhere. When CoGo's API changes, refresh via the script above or the **Update libs from CodeOnTheGo** GitHub Action (which commits the refreshed jars and cuts a release). Publishing addons is a separate workflow, **Publish addons**, which uploads to Cloudflare R2. +An addon under `plugins/` references the shared jars as `../../libs/*.jar`. **Always use the repo-root `libs/` jars and the repo-root Gradle wrapper — never bundle per-plugin copies.** A plugin that ships its own `libs/plugin-api.jar` / `libs/gradle-plugin.jar` (e.g. copied from another plugin) can drift out of sync with the rest of the repo; point `build.gradle.kts` (`compileOnly`) and `settings.gradle.kts` (buildscript `classpath`) at `../../libs/*.jar` and delete any local `libs/`. The root `plugin-api.jar` already carries the full API surface (including `IdeTemplateService`/`CgtTemplateBuilder`), so newer sub-APIs do not justify a local copy. **A plugin folder is not standalone in isolation** — copy the root `libs/` along if you move one elsewhere. When CoGo's API changes, refresh with the **Update libs from CodeOnTheGo** GitHub Action (which commits the refreshed jars and cuts a release) — never by committing what `scripts/update-libs.sh` writes on your own machine, for the reason given above. Publishing addons is a separate workflow, **Publish addons**, which uploads to Cloudflare R2. ### Credentials: use the host's `KeystoreSecretStore`, never your own crypto