@invarn/cibuild 2.9.0 → 2.9.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,183 @@
1
+ /**
2
+ * The SDK CMake a build file pins, installed before Gradle runs.
3
+ *
4
+ * IacobIonut01/ReFra pins `externalNativeBuild.cmake.version = "3.31.6"`, and
5
+ * its own native script (a Gradle task that runs before AGP configures CMake)
6
+ * requires `$SDK/cmake/3.31.6/bin/cmake`. AGP would download the pinned CMake,
7
+ * but only at its own configure step, which comes too late, and the build
8
+ * image bakes no SDK CMake. So the build failed with "cmake 3.31.6 not found".
9
+ *
10
+ * The shell under test is the one that ships: it is executed here, not
11
+ * re-implemented.
12
+ */
13
+ import { execFileSync } from "node:child_process";
14
+ import { chmodSync, copyFileSync, existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
15
+ import { tmpdir } from "node:os";
16
+ import { dirname, join } from "node:path";
17
+ import { fileURLToPath } from "node:url";
18
+ import { AndroidBuildForUITestingStepExecutor, AndroidLintStepExecutor, AndroidUnitTestStepExecutor, GradleBuildStepExecutor, } from "./android.js";
19
+ import { GRADLE_SDK_CMAKE_FUNCTIONS } from "./gradle-sdk-cmake.js";
20
+ import { testConfig } from "./test-config.js";
21
+ const FIXTURES = join(dirname(fileURLToPath(import.meta.url)), "../../../test/fixtures/gradle-sdk-cmake");
22
+ let dir;
23
+ let sdk;
24
+ beforeEach(() => {
25
+ dir = mkdtempSync(join(tmpdir(), "cibuild-sdk-cmake-"));
26
+ sdk = join(dir, "sdk");
27
+ mkdirSync(join(dir, "project", "app"), { recursive: true });
28
+ mkdirSync(sdk);
29
+ });
30
+ afterEach(() => {
31
+ rmSync(dir, { recursive: true, force: true });
32
+ });
33
+ /** A fake sdkmanager: records its arguments and installs what it is asked for. */
34
+ function fakeSdkmanager(exitCode = 0) {
35
+ const bin = join(sdk, "cmdline-tools", "latest", "bin");
36
+ mkdirSync(bin, { recursive: true });
37
+ const path = join(bin, "sdkmanager");
38
+ writeFileSync(path, `#!/bin/bash
39
+ echo "$*" >> "${dir}/sdkmanager-args"
40
+ [ ${exitCode} -eq 0 ] || { echo "Warning: Failed to find package"; exit ${exitCode}; }
41
+ for a in "$@"; do
42
+ case "$a" in
43
+ cmake\\;*) v="\${a#cmake;}"; mkdir -p "${sdk}/cmake/$v/bin"; printf '#!/bin/sh\\n' > "${sdk}/cmake/$v/bin/cmake"; chmod +x "${sdk}/cmake/$v/bin/cmake" ;;
44
+ esac
45
+ done
46
+ `);
47
+ chmodSync(path, 0o755);
48
+ return path;
49
+ }
50
+ /** Run the shipped install in the project directory, with the SDK in ANDROID_HOME. */
51
+ function install(env = { ANDROID_HOME: sdk }) {
52
+ const script = `${GRADLE_SDK_CMAKE_FUNCTIONS}
53
+ cibuild_install_pinned_cmake
54
+ echo "after: $?"`;
55
+ try {
56
+ const stdout = execFileSync("bash", ["-c", script], {
57
+ cwd: join(dir, "project"),
58
+ encoding: "utf-8",
59
+ env: { PATH: "/usr/bin:/bin", ...env },
60
+ });
61
+ return { status: 0, stdout };
62
+ }
63
+ catch (e) {
64
+ const err = e;
65
+ return { status: err.status, stdout: err.stdout };
66
+ }
67
+ }
68
+ function sdkmanagerArgs() {
69
+ const file = join(dir, "sdkmanager-args");
70
+ return existsSync(file) ? readFileSync(file, "utf-8").trim().split("\n") : [];
71
+ }
72
+ describe("cibuild_install_pinned_cmake", () => {
73
+ test("ReFra: the pinned cmake;3.31.6 is installed, and one line says why", () => {
74
+ copyFileSync(join(FIXTURES, "refra.app.build.gradle.kts"), join(dir, "project", "app", "build.gradle.kts"));
75
+ fakeSdkmanager();
76
+ const run = install();
77
+ expect(sdkmanagerArgs()).toEqual([`--sdk_root=${sdk} cmake;3.31.6`]);
78
+ expect(existsSync(join(sdk, "cmake", "3.31.6", "bin", "cmake"))).toBe(true);
79
+ expect(run.stdout).toContain("Android SDK: installing cmake;3.31.6, which ./app/build.gradle.kts:47 pins (externalNativeBuild cmake version) and this SDK does not have.");
80
+ expect(run.stdout).toContain("Android SDK: cmake;3.31.6 installed.");
81
+ expect(run.stdout).toContain("after: 0");
82
+ });
83
+ test("a CMake the SDK already has is left alone, and nothing is printed", () => {
84
+ copyFileSync(join(FIXTURES, "refra.app.build.gradle.kts"), join(dir, "project", "app", "build.gradle.kts"));
85
+ fakeSdkmanager();
86
+ mkdirSync(join(sdk, "cmake", "3.31.6", "bin"), { recursive: true });
87
+ writeFileSync(join(sdk, "cmake", "3.31.6", "bin", "cmake"), "#!/bin/sh\n");
88
+ chmodSync(join(sdk, "cmake", "3.31.6", "bin", "cmake"), 0o755);
89
+ const run = install();
90
+ expect(sdkmanagerArgs()).toEqual([]);
91
+ expect(run.stdout).not.toContain("Android SDK:");
92
+ });
93
+ test("Groovy's `version \"x\"`, and only inside a cmake block, and never in a comment", () => {
94
+ writeFileSync(join(dir, "project", "app", "build.gradle"), [
95
+ "version = '9.9.9'",
96
+ "android {",
97
+ " defaultConfig {",
98
+ " versionName \"1.2.3\"",
99
+ " externalNativeBuild { cmake { cppFlags '-std=c++17' } }",
100
+ " }",
101
+ " externalNativeBuild {",
102
+ " cmake {",
103
+ " path 'src/main/cpp/CMakeLists.txt'",
104
+ " // version '3.10.2'",
105
+ " version '3.22.1'",
106
+ " }",
107
+ " }",
108
+ " ndkVersion '27.0.12077973'",
109
+ "}",
110
+ "",
111
+ ].join("\n"));
112
+ fakeSdkmanager();
113
+ install();
114
+ expect(sdkmanagerArgs()).toEqual([`--sdk_root=${sdk} cmake;3.22.1`]);
115
+ });
116
+ test("a one-line cmake block, two modules pinning the same version, and build outputs skipped", () => {
117
+ writeFileSync(join(dir, "project", "app", "build.gradle.kts"), 'android { externalNativeBuild { cmake { version = "3.31.6" } } }\n');
118
+ mkdirSync(join(dir, "project", "lib"));
119
+ writeFileSync(join(dir, "project", "lib", "build.gradle.kts"), 'android {\n externalNativeBuild {\n cmake {\n version = "3.31.6"\n }\n }\n}\n');
120
+ mkdirSync(join(dir, "project", "app", "build", "tmp"), { recursive: true });
121
+ writeFileSync(join(dir, "project", "app", "build", "tmp", "build.gradle.kts"), 'cmake { version = "3.6.0" }\n');
122
+ fakeSdkmanager();
123
+ install();
124
+ expect(sdkmanagerArgs()).toEqual([`--sdk_root=${sdk} cmake;3.31.6`]);
125
+ });
126
+ test("a project that pins no CMake does nothing", () => {
127
+ writeFileSync(join(dir, "project", "app", "build.gradle.kts"), 'android { compileSdk = 36 }\n');
128
+ fakeSdkmanager();
129
+ const run = install();
130
+ expect(sdkmanagerArgs()).toEqual([]);
131
+ expect(run.stdout).toBe("after: 0\n");
132
+ });
133
+ test("the SDK from local.properties when ANDROID_HOME is unset", () => {
134
+ copyFileSync(join(FIXTURES, "refra.app.build.gradle.kts"), join(dir, "project", "app", "build.gradle.kts"));
135
+ writeFileSync(join(dir, "project", "local.properties"), `sdk.dir=${sdk}\n`);
136
+ fakeSdkmanager();
137
+ install({});
138
+ expect(sdkmanagerArgs()).toEqual([`--sdk_root=${sdk} cmake;3.31.6`]);
139
+ });
140
+ test("no SDK to install into: says so, and the step goes on", () => {
141
+ copyFileSync(join(FIXTURES, "refra.app.build.gradle.kts"), join(dir, "project", "app", "build.gradle.kts"));
142
+ const run = install({});
143
+ expect(run.stdout).toContain("Android SDK: ./app/build.gradle.kts:47 pins cmake;3.31.6, but no Android SDK was found (ANDROID_HOME, ANDROID_SDK_ROOT, local.properties sdk.dir), so it is not installed here.");
144
+ expect(run.stdout).toContain("after: 0");
145
+ });
146
+ test("an SDK without sdkmanager: says so, and the step goes on", () => {
147
+ copyFileSync(join(FIXTURES, "refra.app.build.gradle.kts"), join(dir, "project", "app", "build.gradle.kts"));
148
+ const run = install();
149
+ expect(run.stdout).toContain(`Android SDK: ./app/build.gradle.kts:47 pins cmake;3.31.6, but ${sdk} has no sdkmanager, so it is not installed here.`);
150
+ expect(run.stdout).toContain("after: 0");
151
+ });
152
+ test("a failed install says so, with sdkmanager's words, and the step goes on", () => {
153
+ copyFileSync(join(FIXTURES, "refra.app.build.gradle.kts"), join(dir, "project", "app", "build.gradle.kts"));
154
+ fakeSdkmanager(1);
155
+ const run = install();
156
+ expect(run.stdout).toContain("Android SDK: installing cmake;3.31.6 failed (sdkmanager exit 1); Gradle runs anyway:");
157
+ expect(run.stdout).toContain(" Warning: Failed to find package");
158
+ expect(run.stdout).toContain("after: 0");
159
+ });
160
+ });
161
+ describe("the Gradle steps", () => {
162
+ test("every Gradle step installs the pinned CMake before Gradle runs", async () => {
163
+ const steps = [
164
+ await new GradleBuildStepExecutor().execute({ gradle_task: "assembleDebug" }, {}, testConfig),
165
+ await new AndroidLintStepExecutor().execute({}, {}, testConfig),
166
+ await new AndroidUnitTestStepExecutor().execute({}, {}, testConfig),
167
+ await new AndroidBuildForUITestingStepExecutor().execute({}, {}, testConfig),
168
+ ];
169
+ for (const step of steps) {
170
+ const script = step.script;
171
+ const at = script.indexOf("\ncibuild_install_pinned_cmake\n");
172
+ expect(at).toBeGreaterThan(-1);
173
+ expect(at).toBeLessThan(script.indexOf("$GRADLE_CMD "));
174
+ execFileSync("bash", ["-n", "-c", script]);
175
+ }
176
+ });
177
+ test("no line the functions print can be taken for a step-progress marker", () => {
178
+ for (const line of GRADLE_SDK_CMAKE_FUNCTIONS.split("\n")) {
179
+ expect(line).not.toMatch(/INVARN_STEP|\[step_/);
180
+ }
181
+ });
182
+ });
183
+ //# sourceMappingURL=gradle-sdk-cmake.test.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@invarn/cibuild",
3
- "version": "2.9.0",
3
+ "version": "2.9.2",
4
4
  "description": "CI Build CLI — local pipeline orchestration and validation",
5
5
  "type": "module",
6
6
  "main": "dist/cli.cjs",