diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index c6cf5c9c..8ae35ae0 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -1,5 +1,6 @@ # Forma CI — plugins, sample app, and adjacent tooling. -# F-004: pin JDK 17 on every job; install Android SDK for the sample app. +# F-018: pin JDK 21 on every job (Gradle 8.7+); install Android SDK for the sample app. +# App bytecode / jvmTarget stays at project settings (not raised with the daemon JDK). name: CI on: @@ -17,11 +18,11 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - name: Set up JDK 17 + - name: Set up JDK 21 uses: actions/setup-java@v4 with: distribution: temurin - java-version: "17" + java-version: "21" - name: Set up Gradle uses: gradle/actions/setup-gradle@v4 - name: Build plugins @@ -33,11 +34,11 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - name: Set up JDK 17 + - name: Set up JDK 21 uses: actions/setup-java@v4 with: distribution: temurin - java-version: "17" + java-version: "21" - name: Set up Gradle uses: gradle/actions/setup-gradle@v4 - name: Build includer @@ -49,11 +50,11 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - name: Set up JDK 17 + - name: Set up JDK 21 uses: actions/setup-java@v4 with: distribution: temurin - java-version: "17" + java-version: "21" - name: Set up Gradle uses: gradle/actions/setup-gradle@v4 - name: Build depgen @@ -65,11 +66,11 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - name: Set up JDK 17 + - name: Set up JDK 21 uses: actions/setup-java@v4 with: distribution: temurin - java-version: "17" + java-version: "21" # Sample app: compileSdk 34 (Compose AAR metadata), targetSdk 33. # setup-android accepts licenses, installs cmdline-tools + listed packages, # and exports ANDROID_HOME / ANDROID_SDK_ROOT for AGP. diff --git a/AGENTS.md b/AGENTS.md index bcabc265..c110b1cd 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -15,7 +15,7 @@ Ship Forma as a **working Android product**, then **forma-core** extraction, the 5. Do **not** create new cron jobs from a cron run. 6. Do **not** force-push `master` or `v2` on upstream. Prefer branch + PR to `formatools/forma` (or stepango fork if permissions require). 7. Ground reports in tool output (builds, git, gh). Never invent green builds. -8. If JDK/Android SDK missing, work F-001 first (install Temurin 17+ via brew/sdkman; document exact commands in PROGRESS). +8. If JDK/Android SDK missing, bootstrap first (`brew install openjdk@21` + `source scripts/env-mac.sh`; document exact commands in PROGRESS). 9. **Base of operations is `v2`** (not `master`). Feature branches and PRs target `v2`. Promote `v2` → `master` only when the user asks or CI is ready for public default. ## Git workflow diff --git a/README.md b/README.md index 73326814..0da57742 100644 --- a/README.md +++ b/README.md @@ -115,7 +115,7 @@ androidUtil( ## Development environment -Worker / contributor host setup (JDK 17+, Android SDK platform **34** for +Worker / contributor host setup (JDK **21** build JVM, Android SDK platform **34** for `compileSdk`, 33 still useful for target): see [`docs/ENV.md`](docs/ENV.md) and `source scripts/env-mac.sh`. diff --git a/TICKETS.md b/TICKETS.md index ee467db0..d9b1513b 100644 --- a/TICKETS.md +++ b/TICKETS.md @@ -12,7 +12,7 @@ Update this file when picking or finishing work. Cron workers must pick the **hi | F-002 | done | Audit build graph: plugins, sample app, CI workflows | Map modules → forma-core candidates; capture in `docs/ARCHITECTURE.md` | | F-003 | done | Get plugins + sample `application/` building on modern toolchain | Plugins compile AGP 8.1.2 matches sample; Gradle 8.3 (plugins) / 8.4 (app); host builds green | | F-004 | done | CI green on GitHub Actions for plugins + application | Temurin 17 all jobs + Android SDK 33 for app; PR #153 GHA green | -| F-018 | in_progress | JDK 21 + Gradle/AGP staged modernization | **Phase 1 done (this PR):** all wrappers → **Gradle 8.7**; KSP **1.9.22-1.0.16**; Compose compiler **1.5.10**; kotlin-stdlib forces for failOnVersionConflict. Next: host/CI JDK 21; AGP 8.5.2+. Plan: `~/.hermes/plans/2026-07-13_024508-forma-gradle-agp-upgrade.md` | +| F-018 | in_progress | JDK 21 + Gradle/AGP staged modernization | **Phase 1 + JDK 21 done:** wrappers **8.7** (#180); host/CI **JDK 21**; KSP **1.9.22-1.0.16**; Compose compiler **1.5.10**. AGP still **8.1.2** — next: AGP 8.5.2 → 8.7 → 8.13. AGP 9 = optional F-019. Plan: `~/.hermes/plans/2026-07-13_024508-forma-gradle-agp-upgrade.md` | ## P1 — Android working product diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md index d1db51b0..058bdffc 100644 --- a/docs/ARCHITECTURE.md +++ b/docs/ARCHITECTURE.md @@ -271,17 +271,17 @@ Display name **CI** (`on: push`, `pull_request`, `workflow_dispatch`). | Job | Directory | Java | Notes | |-----|-----------|------|-------| -| `build_application` | `application/` | Temurin **17** | `android-actions/setup-android@v3` (SDK 33 + build-tools 33/34); `./gradlew build --stacktrace --console=plain` | -| `build_plugins` | `plugins/` | Temurin **17** | `./gradlew build --stacktrace --console=plain` | -| `build_includer` | `includer/` | Temurin **17** | same | -| `build_depgen` | `depgen/` | Temurin **17** | same | +| `build_application` | `application/` | Temurin **21** | `android-actions/setup-android@v3` (SDK 33 + build-tools 33/34); `./gradlew build --stacktrace --console=plain` | +| `build_plugins` | `plugins/` | Temurin **21** | `./gradlew build --stacktrace --console=plain` | +| `build_includer` | `includer/` | Temurin **21** | same | +| `build_depgen` | `depgen/` | Temurin **21** | same | Concurrency group `ci-${{ github.workflow }}-${{ github.ref }}` cancels in-progress runs on the same ref. -F-004 fixes applied (2026-07-11): +CI pins (evolved F-004 → F-018): -1. **All jobs** pin Temurin 17 via `actions/setup-java@v4`. -2. **Application** installs Android SDK packages matching sample `compileSdk` 33. +1. **All jobs** pin Temurin **21** via `actions/setup-java@v4` (was 17; requires Gradle ≥8.5 — wrappers are **8.7**). +2. **Application** installs Android SDK packages matching sample `compileSdk` 34 / target 33. 3. README badge → `formatools/forma` + `actions/workflows/main.yml/badge.svg`. 4. Gradle setup via `gradle/actions/setup-gradle@v4` (successor of `gradle-build-action`). 5. Dropped unconditional `--scan` (no build-scan account coupling in CI). @@ -295,10 +295,11 @@ keys) or full Portal publish via secrets; deeper Gradle remote cache. | Component | Declared / observed | |-----------|---------------------| -| JDK | 17 (all CI jobs Temurin; host OpenJDK 17 via Homebrew) | -| Gradle | **8.7** (all wrappers; F-018 Phase 1) | -| AGP | **8.1.2** sample runtime + plugins compile (aligned F-003) | -| Kotlin | embeddedKotlin from Gradle distribution | +| JDK (daemon / CI) | **21** (CI Temurin 21; host OpenJDK 21 via Homebrew `openjdk@21`) | +| App bytecode / `jvmTarget` | unchanged (sample default `JavaVersion.VERSION_1_8`) — not raised with daemon JDK | +| Gradle | **8.7** (all wrappers; F-018 Phase 1 / PR #180) | +| AGP | **8.1.2** sample runtime + plugins compile (aligned F-003; AGP ladder = later F-018) | +| Kotlin | embeddedKotlin from Gradle 8.7 (**1.9.22**); Compose compiler default **1.5.10** | | Android SDK | sample compileSdk **34** / target 33; host platforms 34+33 + build-tools 33/34 | | Forma version | 0.1.3 | diff --git a/docs/ENV.md b/docs/ENV.md index 8b7ee912..e02bb325 100644 --- a/docs/ENV.md +++ b/docs/ENV.md @@ -1,13 +1,16 @@ # Worker host environment (macOS) -Forma workers need **JDK 17+** and an **Android SDK** with platform **34** -(sample `compileSdk`; Compose AAR metadata) and optionally **33** (`targetSdk`). +Forma workers need **JDK 21** (build/daemon JVM; F-018) and an **Android SDK** +with platform **34** (sample `compileSdk`; Compose AAR metadata) and optionally +**33** (`targetSdk`). Android app language level / `jvmTarget` stays at the +project’s configured `JavaVersion` (sample default still 1.8) — raising bytecode +is a separate decision from the daemon JDK. ## Quick start (this Mac) ```bash # Already installed on the Forma worker host (no sudo): -# brew install openjdk@17 +# brew install openjdk@21 # brew install --cask android-commandlinetools # sdk packages: platforms;android-34, platforms;android-33, platform-tools, # build-tools 33.0.2 + 34.0.0 @@ -18,8 +21,9 @@ source scripts/env-mac.sh printf 'sdk.dir=%s\n' "$ANDROID_HOME" > application/local.properties # Verify -java -version -cd plugins && ./gradlew build +java -version # expect 21.x +cd plugins && ./gradlew --version # Gradle 8.7 + JVM 21 +cd ../plugins && ./gradlew build cd ../application && ./gradlew build cd ../includer && ./gradlew build cd ../depgen && ./gradlew build @@ -29,19 +33,27 @@ cd ../depgen && ./gradlew build | Tool | Location | |------|----------| -| JDK 17 | `/usr/local/opt/openjdk@17/libexec/openjdk.jdk/Contents/Home` | +| JDK 21 (Intel Homebrew) | `/usr/local/opt/openjdk@21/libexec/openjdk.jdk/Contents/Home` | +| JDK 21 (Apple Silicon) | `/opt/homebrew/opt/openjdk@21/libexec/openjdk.jdk/Contents/Home` | | Android SDK root | `/usr/local/share/android-commandlinetools` | | cmdline-tools | `$ANDROID_HOME/cmdline-tools/latest` | -`openjdk@17` is Homebrew **keg-only** (not on system `PATH` until you export `JAVA_HOME`). Prefer it over `brew install --cask temurin@17`, which requires **sudo** for the `.pkg` installer and fails in non-interactive cron. +`scripts/env-mac.sh` probes both Homebrew prefixes. `openjdk@21` is Homebrew +**keg-only** (not on system `PATH` until you export `JAVA_HOME`). Prefer it over +`brew install --cask temurin@21`, which requires **sudo** for the `.pkg` +installer and fails in non-interactive cron. ## One-time install (fresh machine) ```bash -brew install openjdk@17 +brew install openjdk@21 brew install --cask android-commandlinetools -export JAVA_HOME="/usr/local/opt/openjdk@17/libexec/openjdk.jdk/Contents/Home" +# Intel: +export JAVA_HOME="/usr/local/opt/openjdk@21/libexec/openjdk.jdk/Contents/Home" +# Apple Silicon: +# export JAVA_HOME="/opt/homebrew/opt/openjdk@21/libexec/openjdk.jdk/Contents/Home" + export ANDROID_HOME="/usr/local/share/android-commandlinetools" export ANDROID_SDK_ROOT="$ANDROID_HOME" export PATH="$JAVA_HOME/bin:$ANDROID_HOME/cmdline-tools/latest/bin:$ANDROID_HOME/platform-tools:$PATH" @@ -60,21 +72,24 @@ printf 'sdk.dir=%s\n' "$ANDROID_HOME" > application/local.properties Optional system-wide JVM registration (needs sudo, not required for Gradle): ```bash -sudo ln -sfn /usr/local/opt/openjdk@17/libexec/openjdk.jdk \ - /Library/Java/JavaVirtualMachines/openjdk-17.jdk +sudo ln -sfn /usr/local/opt/openjdk@21/libexec/openjdk.jdk \ + /Library/Java/JavaVirtualMachines/openjdk-21.jdk ``` -## Verified on 2026-07-10 (F-001) / re-verified 2026-07-11 (F-003, F-013) +## Verified on 2026-07-19 (F-018 Phase 1 + JDK 21) -- `java` / `javac` 17.0.19 (Homebrew OpenJDK) -- `plugins/`: `./gradlew build` → **BUILD SUCCESSFUL** (AGP compile dep **8.1.2**) -- `includer/`: `./gradlew build` → **BUILD SUCCESSFUL** -- `depgen/`: `./gradlew build` → **BUILD SUCCESSFUL** -- `application/`: `./gradlew build` → **BUILD SUCCESSFUL** (compileSdk **34**, - targetSdk 33, Compose compiler **1.5.10** / Kotlin **1.9.22**, F-018) -- Gradle wrappers: **8.7** (all roots; was 8.3/8.4 split) +- `java` / `javac` **21.x** (Homebrew OpenJDK 21) via `scripts/env-mac.sh` +- Gradle wrappers: **8.7** (all roots; was 8.3/8.4 split — PR #180) +- Compose compiler default **1.5.10** (Kotlin **1.9.22** / Gradle 8.7 embedded) +- AGP still **8.1.2** (Phase 2+ climb is later F-018 work) +- CI: Temurin **21** all jobs (`.github/workflows/main.yml`) - Toolchain note: plugins compile AGP matches sample forced AGP (no 7.4.2 skew) +See `docs/PROGRESS.md` for the latest host build tails. + ## Shell profile -`~/.zshrc` on the worker exports `JAVA_HOME` / `ANDROID_HOME` so interactive shells pick them up. Cron / Hermes sessions should still `source scripts/env-mac.sh` (or set the same vars) because non-interactive jobs may not load `.zshrc`. +`~/.zshrc` on the worker should export `JAVA_HOME` / `ANDROID_HOME` so interactive +shells pick them up (prefer openjdk@21). Cron / Hermes sessions should still +`source scripts/env-mac.sh` (or set the same vars) because non-interactive jobs +may not load `.zshrc`. diff --git a/docs/GETTING-STARTED.md b/docs/GETTING-STARTED.md index 17799d19..ce83baf2 100644 --- a/docs/GETTING-STARTED.md +++ b/docs/GETTING-STARTED.md @@ -39,10 +39,10 @@ structure and boundaries, not business logic. | Tool | Version / notes | |------|-----------------| -| JDK | **17+** | +| JDK | **21** build/daemon (F-018); app language level unchanged | | Android SDK | Platform **34** (`compileSdk`; required for modern Compose AARs), **33** useful for `targetSdk` | | Build tools | 33.0.2 / 34.0.0 as used by the sample | -| Gradle | Use the wrapper in the project (`./gradlew`); sample uses **8.4**, plugins build **8.3** | +| Gradle | Use the wrapper in the project (`./gradlew`); **8.7** all roots | Worker / macOS install steps: [ENV.md](ENV.md) (`source scripts/env-mac.sh`). diff --git a/docs/PROGRESS.md b/docs/PROGRESS.md index 40e27fdf..0def9354 100644 --- a/docs/PROGRESS.md +++ b/docs/PROGRESS.md @@ -2,20 +2,41 @@ Newest entries first. +## 2026-07-19 — F-018 Phase 1b: host + CI → JDK 21 + +- **Ticket:** F-018 remains `in_progress` (wrappers #180 + JDK 21 done; AGP ladder remains) +- **Branch / PR:** `forma/F-018-jdk21` → new PR base `v2` (Phase 1 already on tip via #180) +- **Host:** `brew install openjdk@21` → OpenJDK **21.0.11** at `/usr/local/opt/openjdk@21/...` +- **Code / config:** + - `scripts/env-mac.sh` default `JAVA_HOME` → openjdk@21 (probes `/usr/local` + `/opt/homebrew`) + - `.github/workflows/main.yml` all jobs Temurin **17 → 21** + - Docs: `ENV.md`, `ARCHITECTURE.md` CI/toolchain snapshot, `SAMPLE-APP.md`, `GETTING-STARTED.md`, `README.md`, `AGENTS.md` + - App bytecode / `jvmTarget` **not** raised (sample still `JavaVersion.VERSION_1_8`) +- **Verify (real host, `source scripts/env-mac.sh`):** + - `java -version` → **21.0.11** (Homebrew) + - `plugins/ ./gradlew --version` → **Gradle 8.7** + **JVM 21.0.11** + Kotlin 1.9.22 + - `plugins/ ./gradlew build` → **BUILD SUCCESSFUL** (89 tasks) + - `application/ ./gradlew build` → **BUILD SUCCESSFUL** (2203 tasks, 4m16s) + - `includer/ ./gradlew build` → **BUILD SUCCESSFUL** + - `depgen/ ./gradlew build` → **BUILD SUCCESSFUL** +- **Not in this slice:** AGP still **8.1.2** (Phase 2 = 8.5.2+); no F-019 +- **Next:** AGP lockstep 8.5.2 (separate PR preferred) + ## 2026-07-19 — F-018 Phase 1: unify Gradle wrappers → 8.7 - **Ticket:** F-018 → `in_progress` (Phase 1 shipped; JDK 21 + AGP climb remain) +- **PR:** #180 merged to `v2` (`7464d3e`) - **Why delayed:** Jul 12 session wrote the plan + asked priority buttons instead of implementing; F-018 was not on `TICKETS.md` until 2026-07-19, so 4h workers continued forma-core/Bazel/flat-structure. User call-out: do the work, don't re-plan. - **Code:** - All `gradle-wrapper.properties` (plugins, application, includer, depgen, jvm-application, bazel-adapter, examples/*, build-*, root) → **Gradle 8.7** - `application/settings.gradle.kts`: force full kotlin-stdlib family to `$embeddedKotlinVersion` (1.9.22) under `failOnVersionConflict`; KSP **1.9.22-1.0.16** (1.0.18 caused kapt↔ksp task cycle) - Default Compose compiler **1.5.3 → 1.5.10** (Kotlin 1.9.22 map); example 06-compose aligned -- **Verify (real, OpenJDK 17 + env-mac.sh):** +- **Verify (real, OpenJDK 17 at Phase 1 commit; re-verified on 21 in Phase 1b):** - `plugins/ ./gradlew --version` → **8.7** / Kotlin 1.9.22 - `plugins/ ./gradlew build` → **BUILD SUCCESSFUL** - `application/ ./gradlew build` → **BUILD SUCCESSFUL** (2203 tasks) - `includer`, `depgen`, `bazel-adapter`, `jvm-application` builds → **BUILD SUCCESSFUL** -- **Next:** F-018 Phase 1b host/CI JDK 21; Phase 2 AGP 8.5.2 lockstep +- **Next:** F-018 Phase 1b host/CI JDK 21 (this run); Phase 2 AGP 8.5.2 lockstep ## 2026-07-19 — F-018 scheduled (JDK 21 + toolchain ladder) diff --git a/docs/SAMPLE-APP.md b/docs/SAMPLE-APP.md index 0433ba03..262350ab 100644 --- a/docs/SAMPLE-APP.md +++ b/docs/SAMPLE-APP.md @@ -108,7 +108,8 @@ cd application && ./gradlew build ``` Toolchain: AGP **8.1.2**, compileSdk **34**, targetSdk **33**, Gradle **8.7**, -JDK **17**. See [ENV.md](ENV.md), [COMPOSE.md](COMPOSE.md), [ARCHITECTURE.md](ARCHITECTURE.md). +build JDK **21** (app language level unchanged). See [ENV.md](ENV.md), +[COMPOSE.md](COMPOSE.md), [ARCHITECTURE.md](ARCHITECTURE.md). ## What “gold standard” means here diff --git a/scripts/env-mac.sh b/scripts/env-mac.sh index 0d8c5dc3..441c37a5 100755 --- a/scripts/env-mac.sh +++ b/scripts/env-mac.sh @@ -3,9 +3,10 @@ # source scripts/env-mac.sh # # Install (one-time, no sudo required for these paths): -# brew install openjdk@17 +# brew install openjdk@21 # brew install --cask android-commandlinetools -# export JAVA_HOME="/usr/local/opt/openjdk@17/libexec/openjdk.jdk/Contents/Home" +# export JAVA_HOME="/usr/local/opt/openjdk@21/libexec/openjdk.jdk/Contents/Home" +# # Apple Silicon Homebrew prefix is /opt/homebrew instead of /usr/local # export ANDROID_HOME="/usr/local/share/android-commandlinetools" # yes | "$ANDROID_HOME/cmdline-tools/latest/bin/sdkmanager" --licenses # "$ANDROID_HOME/cmdline-tools/latest/bin/sdkmanager" --install \ @@ -13,16 +14,36 @@ # "build-tools;33.0.2" "build-tools;34.0.0" # printf 'sdk.dir=%s\n' "$ANDROID_HOME" > application/local.properties # -# Note: brew install --cask temurin@17 needs sudo for the system pkg installer; -# openjdk@17 (formula) is keg-only and does not need sudo. +# Note: brew install --cask temurin@21 needs sudo for the system pkg installer; +# openjdk@21 (formula) is keg-only and does not need sudo. +# +# Build JVM is JDK 21 (F-018). Android/app bytecode stays at the project's +# configured JavaVersion (sample still 1.8 / jvmTarget from settings) — do not +# confuse the daemon JDK with language level. + +_forma_java_home_candidates=( + /usr/local/opt/openjdk@21/libexec/openjdk.jdk/Contents/Home + /opt/homebrew/opt/openjdk@21/libexec/openjdk.jdk/Contents/Home +) + +if [[ -z "${JAVA_HOME:-}" ]]; then + for _candidate in "${_forma_java_home_candidates[@]}"; do + if [[ -x "$_candidate/bin/java" ]]; then + export JAVA_HOME="$_candidate" + break + fi + done +fi +unset _candidate _forma_java_home_candidates -export JAVA_HOME="${JAVA_HOME:-/usr/local/opt/openjdk@17/libexec/openjdk.jdk/Contents/Home}" +export JAVA_HOME="${JAVA_HOME:-/usr/local/opt/openjdk@21/libexec/openjdk.jdk/Contents/Home}" export ANDROID_HOME="${ANDROID_HOME:-/usr/local/share/android-commandlinetools}" export ANDROID_SDK_ROOT="${ANDROID_SDK_ROOT:-$ANDROID_HOME}" export PATH="$JAVA_HOME/bin:$ANDROID_HOME/cmdline-tools/latest/bin:$ANDROID_HOME/platform-tools:$PATH" if [[ ! -x "$JAVA_HOME/bin/java" ]]; then echo "env-mac.sh: java not found at $JAVA_HOME/bin/java" >&2 + echo "env-mac.sh: install with: brew install openjdk@21" >&2 return 1 2>/dev/null || exit 1 fi