FN-7209: require JDK 21 for Android CI builds
Align Android Gradle build environments with Capacitor's Java 21 source compatibility. - Update mobile, release, and test-release workflows to provision Temurin JDK 21. - Document Java 21 as the required Android Gradle build JDK. - Add CI workflow coverage that checks Android build jobs satisfy @capacitor/android sourceCompatibility. Files changed: .github/workflows/mobile.yml | 6 +- .github/workflows/release.yml | 6 +- .github/workflows/test-release.yml | 6 +- MOBILE.md | 4 +- packages/cli/src/__tests__/ci-workflow.test.ts | 99 +++++++++++++++++++++++++- 5 files changed, 112 insertions(+), 9 deletions(-) Fusion-Task-Id: FN-7209 Fusion-Task-Lineage: d9e50bee-d7f8-4e87-b33e-136ffffc93af Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
This commit is contained in:
6
.github/workflows/mobile.yml
vendored
6
.github/workflows/mobile.yml
vendored
@@ -111,11 +111,13 @@ jobs:
|
||||
- name: Setup Node and install dependencies
|
||||
uses: ./.github/actions/setup-node-pnpm
|
||||
|
||||
- name: Setup Java 17
|
||||
# FNXC:MobileAndroidBuild 2026-06-28-00:00:
|
||||
# Capacitor 7 @capacitor/android compiles its Android library with JavaVersion.VERSION_21; manual Android Gradle builds must provision JDK 21 because JDK 17 fails with `invalid source release: 21`.
|
||||
- name: Setup Java 21
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: temurin
|
||||
java-version: "17"
|
||||
java-version: "21"
|
||||
|
||||
- name: Cache Android Gradle caches
|
||||
uses: actions/cache@v4
|
||||
|
||||
6
.github/workflows/release.yml
vendored
6
.github/workflows/release.yml
vendored
@@ -389,11 +389,13 @@ jobs:
|
||||
- name: Setup Node and install dependencies
|
||||
uses: ./.github/actions/setup-node-pnpm
|
||||
|
||||
- name: Setup Java 17
|
||||
# FNXC:MobileAndroidBuild 2026-06-28-00:00:
|
||||
# Capacitor 7 @capacitor/android compiles its Android library with JavaVersion.VERSION_21; release APK/AAB Gradle builds must provision JDK 21 because JDK 17 fails with `invalid source release: 21`.
|
||||
- name: Setup Java 21
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: temurin
|
||||
java-version: "17"
|
||||
java-version: "21"
|
||||
|
||||
- name: Cache Android Gradle caches
|
||||
uses: actions/cache@v4
|
||||
|
||||
6
.github/workflows/test-release.yml
vendored
6
.github/workflows/test-release.yml
vendored
@@ -375,11 +375,13 @@ jobs:
|
||||
- name: Setup Node and install dependencies
|
||||
uses: ./.github/actions/setup-node-pnpm
|
||||
|
||||
- name: Setup Java 17
|
||||
# FNXC:MobileAndroidBuild 2026-06-28-00:00:
|
||||
# Capacitor 7 @capacitor/android compiles its Android library with JavaVersion.VERSION_21; rehearsal APK/AAB Gradle builds must provision JDK 21 because JDK 17 fails with `invalid source release: 21`.
|
||||
- name: Setup Java 21
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: temurin
|
||||
java-version: "17"
|
||||
java-version: "21"
|
||||
|
||||
- name: Cache Android Gradle caches
|
||||
uses: actions/cache@v4
|
||||
|
||||
@@ -8,7 +8,7 @@ Fusion mobile builds package the dashboard web client into Capacitor shells via
|
||||
- **pnpm** 10+
|
||||
- **Xcode** (iOS builds)
|
||||
- **Android Studio** (Android SDK + emulator tooling)
|
||||
- **Java JDK** 17+ (Android Gradle builds)
|
||||
- **Java JDK** 21+ (Android Gradle builds)
|
||||
|
||||
## Quick Start
|
||||
|
||||
@@ -160,7 +160,7 @@ You can also use ImageMagick if preferred.
|
||||
|
||||
### Android build fails
|
||||
|
||||
- Verify Java 17+ (`java -version`)
|
||||
- Verify Java 21+ (`java -version`)
|
||||
- Confirm Android SDK and Gradle tooling are installed via Android Studio
|
||||
|
||||
### PWA does not install
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { describe, it, expect, beforeAll } from "vitest";
|
||||
import { readFileSync, accessSync, constants } from "node:fs";
|
||||
import { readFileSync, accessSync, constants, existsSync, readdirSync } from "node:fs";
|
||||
import { join } from "node:path";
|
||||
import { parse } from "yaml";
|
||||
|
||||
@@ -27,6 +27,84 @@ function findCompositeSetupStep(steps: any[]) {
|
||||
return steps.find((step) => step.uses === "./.github/actions/setup-node-pnpm");
|
||||
}
|
||||
|
||||
function findSetupJavaStep(steps: any[]) {
|
||||
return steps.find((step) => typeof step.uses === "string" && step.uses.startsWith("actions/setup-java@"));
|
||||
}
|
||||
|
||||
function resolveCapacitorAndroidGradlePath(): string {
|
||||
const primaryPath = join(
|
||||
workspaceRoot,
|
||||
"packages",
|
||||
"mobile",
|
||||
"node_modules",
|
||||
"@capacitor",
|
||||
"android",
|
||||
"capacitor",
|
||||
"build.gradle",
|
||||
);
|
||||
if (existsSync(primaryPath)) {
|
||||
return primaryPath;
|
||||
}
|
||||
|
||||
const pnpmStoreCandidates = [
|
||||
join(workspaceRoot, "node_modules", ".pnpm"),
|
||||
join(workspaceRoot, "packages", "mobile", "node_modules", ".pnpm"),
|
||||
];
|
||||
|
||||
for (const pnpmStore of pnpmStoreCandidates) {
|
||||
if (!existsSync(pnpmStore)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (const entry of readdirSync(pnpmStore)) {
|
||||
if (!entry.startsWith("@capacitor+android@")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const candidate = join(
|
||||
pnpmStore,
|
||||
entry,
|
||||
"node_modules",
|
||||
"@capacitor",
|
||||
"android",
|
||||
"capacitor",
|
||||
"build.gradle",
|
||||
);
|
||||
if (existsSync(candidate)) {
|
||||
return candidate;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
throw new Error(
|
||||
"Unable to resolve @capacitor/android capacitor/build.gradle from packages/mobile/node_modules or the pnpm store",
|
||||
);
|
||||
}
|
||||
|
||||
function readCapacitorAndroidSourceCompatibilityMajor(): number {
|
||||
const gradleContent = readFileSync(resolveCapacitorAndroidGradlePath(), "utf-8");
|
||||
const match = gradleContent.match(/sourceCompatibility\s+JavaVersion\.VERSION_(\d+)/);
|
||||
if (!match) {
|
||||
throw new Error("Unable to parse @capacitor/android sourceCompatibility JavaVersion.VERSION_<major>");
|
||||
}
|
||||
return Number.parseInt(match[1], 10);
|
||||
}
|
||||
|
||||
function expectAndroidBuildJobJavaVersionAtLeast(workflow: any, workflowName: string, minimumJavaVersion: number) {
|
||||
const buildAndroidJob = workflow.jobs?.["build-android"];
|
||||
expect(buildAndroidJob, `${workflowName} must define a build-android job`).toBeDefined();
|
||||
|
||||
const setupJavaStep = findSetupJavaStep(buildAndroidJob?.steps ?? []);
|
||||
expect(setupJavaStep, `${workflowName} build-android must provision Java`).toBeDefined();
|
||||
expect(setupJavaStep.name).toBe("Setup Java 21");
|
||||
|
||||
const javaVersion = setupJavaStep.with?.["java-version"];
|
||||
expect(javaVersion, `${workflowName} build-android must pin JDK 21`).toBe("21");
|
||||
expect(Number.parseInt(javaVersion, 10), `${workflowName} JDK must satisfy @capacitor/android sourceCompatibility`).toBeGreaterThanOrEqual(
|
||||
minimumJavaVersion,
|
||||
);
|
||||
}
|
||||
|
||||
describe("Merge gate (.github/workflows/pr-checks.yml)", () => {
|
||||
let workflow: any;
|
||||
let content: string;
|
||||
@@ -342,6 +420,25 @@ describe("Version & Release workflow (.github/workflows/version.yml)", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("Android build JDK compatibility", () => {
|
||||
let capacitorAndroidSourceCompatibility: number;
|
||||
|
||||
beforeAll(() => {
|
||||
capacitorAndroidSourceCompatibility = readCapacitorAndroidSourceCompatibilityMajor();
|
||||
});
|
||||
|
||||
/*
|
||||
FNXC:MobileAndroidBuild 2026-06-28-00:00:
|
||||
Android CI jobs that run Capacitor Gradle builds must provision a JDK version greater than or equal to @capacitor/android's sourceCompatibility. FN-7209 proved JDK 17 silently drifted below Capacitor 7's JavaVersion.VERSION_21 requirement and failed release builds with `invalid source release: 21`.
|
||||
*/
|
||||
it("pins every Android-building workflow to a JDK compatible with Capacitor", () => {
|
||||
for (const workflowName of ["release.yml", "test-release.yml", "mobile.yml"]) {
|
||||
const workflow = loadWorkflow(workflowName).parsed;
|
||||
expectAndroidBuildJobJavaVersionAtLeast(workflow, workflowName, capacitorAndroidSourceCompatibility);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe("Binary release workflow (.github/workflows/release.yml)", () => {
|
||||
let workflow: any;
|
||||
let content: string;
|
||||
|
||||
Reference in New Issue
Block a user