@invarn/cibuild 2.8.0 → 2.8.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.
- package/dist/cli.cjs +11 -11
- package/dist/src/commands/a-properties-stand-in-declares-the-keys-the-build-reads.test.d.ts +2 -0
- package/dist/src/commands/a-properties-stand-in-declares-the-keys-the-build-reads.test.d.ts.map +1 -0
- package/dist/src/commands/a-properties-stand-in-declares-the-keys-the-build-reads.test.js +354 -0
- package/dist/src/commands/android-scanner.d.ts +22 -0
- package/dist/src/commands/android-scanner.d.ts.map +1 -1
- package/dist/src/commands/android-scanner.js +424 -0
- package/dist/src/commands/index.d.ts +1 -0
- package/dist/src/commands/index.d.ts.map +1 -1
- package/dist/src/commands/index.js +3 -0
- package/dist/src/commands/ios-scanner.d.ts.map +1 -1
- package/dist/src/commands/ios-scanner.js +15 -24
- package/dist/src/commands/ios-scheme-ranking.test.js +5 -0
- package/dist/src/shared/detect-project.d.ts +32 -0
- package/dist/src/shared/detect-project.d.ts.map +1 -1
- package/dist/src/shared/detect-project.js +62 -10
- package/dist/src/shared/xcode-container.test.d.ts +2 -0
- package/dist/src/shared/xcode-container.test.d.ts.map +1 -0
- package/dist/src/shared/xcode-container.test.js +123 -0
- package/package.json +1 -1
|
@@ -33,6 +33,62 @@ function readOrNull(path) {
|
|
|
33
33
|
return null;
|
|
34
34
|
}
|
|
35
35
|
}
|
|
36
|
+
/**
|
|
37
|
+
* True when a path that is *named* like an Xcode container actually is one.
|
|
38
|
+
*
|
|
39
|
+
* **The** predicate — every question of the form "is this `.xcodeproj` a
|
|
40
|
+
* project" goes through here, and nothing else in cibuild joins a directory
|
|
41
|
+
* name to "there is a project there".
|
|
42
|
+
*
|
|
43
|
+
* A `.xcodeproj` is a directory whose whole content is `project.pbxproj`;
|
|
44
|
+
* without it `xcodebuild` refuses to open the project at all — "missing its
|
|
45
|
+
* project.pbxproj file". A `.xcworkspace` is `contents.xcworkspacedata` in
|
|
46
|
+
* the same way. Anything else ending in those suffixes is a directory with a
|
|
47
|
+
* suggestive name, and offering one is offering something Xcode would reject.
|
|
48
|
+
*
|
|
49
|
+
* `Uwi0/Oakane` is the row that found it. Its `iosApp/` ships two: a tracked
|
|
50
|
+
* `iosApp.xcodeproj` holding nothing but `project.xcworkspace/`, and the real
|
|
51
|
+
* `oakane.xcodeproj` beside it with a 20 217-byte `project.pbxproj`. The husk
|
|
52
|
+
* carries the name the KMM wizard gives, so the conventional name outranked
|
|
53
|
+
* the project that exists — and it supplied BOTH `IOS_PROJECT_PATH` and, via
|
|
54
|
+
* the scheme scan's project-name fallback, `IOS_SCHEME`. The build reached
|
|
55
|
+
* step 9 and died on "Unable to read project 'iosApp.xcodeproj'".
|
|
56
|
+
*
|
|
57
|
+
* This is the iOS twin of the Gradle module scan once treating "a directory
|
|
58
|
+
* holding a file called build.gradle" as a module: wrong in the same
|
|
59
|
+
* direction, and fixed the same way — one reader, used by everyone.
|
|
60
|
+
*
|
|
61
|
+
* The `.pbxproj` is deliberately NOT parsed. Its existence and non-emptiness
|
|
62
|
+
* is the whole question; a shared `.xcscheme` remains the oracle for what a
|
|
63
|
+
* project builds.
|
|
64
|
+
*/
|
|
65
|
+
export function isXcodeContainer(path) {
|
|
66
|
+
const manifest = path.endsWith(".xcworkspace")
|
|
67
|
+
? "contents.xcworkspacedata"
|
|
68
|
+
: path.endsWith(".xcodeproj")
|
|
69
|
+
? "project.pbxproj"
|
|
70
|
+
: null;
|
|
71
|
+
if (manifest === null)
|
|
72
|
+
return false;
|
|
73
|
+
try {
|
|
74
|
+
return statSync(resolve(path, manifest)).size > 0;
|
|
75
|
+
}
|
|
76
|
+
catch {
|
|
77
|
+
return false;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
/** The entries of `dir` that are real Xcode containers, in `readdir` order. */
|
|
81
|
+
export function xcodeContainersIn(dir) {
|
|
82
|
+
let entries;
|
|
83
|
+
try {
|
|
84
|
+
entries = readdirSync(dir);
|
|
85
|
+
}
|
|
86
|
+
catch {
|
|
87
|
+
return [];
|
|
88
|
+
}
|
|
89
|
+
return entries.filter((e) => (e.endsWith(".xcodeproj") || e.endsWith(".xcworkspace")) &&
|
|
90
|
+
isXcodeContainer(resolve(dir, e)));
|
|
91
|
+
}
|
|
36
92
|
/** True when this directory holds something an Apple build reads. */
|
|
37
93
|
function holdsAppleConsumer(dir) {
|
|
38
94
|
let entries;
|
|
@@ -42,7 +98,7 @@ function holdsAppleConsumer(dir) {
|
|
|
42
98
|
catch {
|
|
43
99
|
return false;
|
|
44
100
|
}
|
|
45
|
-
if (
|
|
101
|
+
if (xcodeContainersIn(dir).length > 0) {
|
|
46
102
|
return true;
|
|
47
103
|
}
|
|
48
104
|
if (entries.includes("Podfile"))
|
|
@@ -144,15 +200,11 @@ export function detectMobileProjectRoot(dir) {
|
|
|
144
200
|
const iosFileIndicators = ["Podfile"];
|
|
145
201
|
const hasIosFile = iosFileIndicators.some((f) => existsSync(resolve(dir, f)));
|
|
146
202
|
if (!hasIosFile) {
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
}
|
|
153
|
-
catch {
|
|
154
|
-
// Unreadable directory — fall through.
|
|
155
|
-
}
|
|
203
|
+
// A husk — a directory named `*.xcodeproj` with no `project.pbxproj` —
|
|
204
|
+
// does not make this an iOS project root. Saying `ios` on the strength of
|
|
205
|
+
// one writes a pipeline aimed at something xcodebuild cannot open.
|
|
206
|
+
if (xcodeContainersIn(dir).length > 0)
|
|
207
|
+
return "ios";
|
|
156
208
|
}
|
|
157
209
|
else {
|
|
158
210
|
return "ios";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"xcode-container.test.d.ts","sourceRoot":"","sources":["../../../src/shared/xcode-container.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A `.xcodeproj` with no `project.pbxproj` is not a project.
|
|
3
|
+
*
|
|
4
|
+
* `Uwi0/Oakane` is the row. Its `iosApp/` ships two Xcode projects: a tracked
|
|
5
|
+
* `iosApp.xcodeproj` holding nothing but `project.xcworkspace/`, and the real
|
|
6
|
+
* `oakane.xcodeproj` beside it. The scan chose the husk — `iosApp` is the name
|
|
7
|
+
* the KMM wizard gives, so the conventional name outranked the project that
|
|
8
|
+
* exists — and the build died at step 9 on
|
|
9
|
+
*
|
|
10
|
+
* xcodebuild: error: Unable to read project 'iosApp.xcodeproj' …
|
|
11
|
+
* Reason: … missing its project.pbxproj file.
|
|
12
|
+
*
|
|
13
|
+
* The husk supplied `IOS_SCHEME` too: with no shared schemes anywhere in that
|
|
14
|
+
* repository, `detectSchemes` falls back to each project's own directory name,
|
|
15
|
+
* so the husk contributed the scheme `iosApp` as well as the project path.
|
|
16
|
+
*
|
|
17
|
+
* This is the iOS twin of the Gradle module scan once treating "a directory
|
|
18
|
+
* holding a file called build.gradle" as a module, and it is fixed the same
|
|
19
|
+
* way: one reader, used by everyone.
|
|
20
|
+
*/
|
|
21
|
+
import { mkdtempSync, rmSync, mkdirSync, writeFileSync } from "node:fs";
|
|
22
|
+
import { join } from "node:path";
|
|
23
|
+
import { tmpdir } from "node:os";
|
|
24
|
+
import { isXcodeContainer, xcodeContainersIn, detectMobileProjectRoot } from "./detect-project.js";
|
|
25
|
+
import { scanIosProject } from "../commands/ios-scanner.js";
|
|
26
|
+
let root;
|
|
27
|
+
beforeEach(() => {
|
|
28
|
+
root = mkdtempSync(join(tmpdir(), "cibuild-husk-"));
|
|
29
|
+
});
|
|
30
|
+
afterEach(() => {
|
|
31
|
+
rmSync(root, { recursive: true, force: true });
|
|
32
|
+
});
|
|
33
|
+
/** A project Xcode could open. */
|
|
34
|
+
function project(name) {
|
|
35
|
+
mkdirSync(join(root, `${name}.xcodeproj`), { recursive: true });
|
|
36
|
+
writeFileSync(join(root, `${name}.xcodeproj`, "project.pbxproj"), "// !$*UTF8*$!\n");
|
|
37
|
+
}
|
|
38
|
+
/** Oakane's shape: the name, the inner workspace, and no project at all. */
|
|
39
|
+
function husk(name) {
|
|
40
|
+
mkdirSync(join(root, `${name}.xcodeproj`, "project.xcworkspace"), { recursive: true });
|
|
41
|
+
writeFileSync(join(root, `${name}.xcodeproj`, "project.xcworkspace", "contents.xcworkspacedata"), '<?xml version="1.0" encoding="UTF-8"?>\n<Workspace version = "1.0"></Workspace>\n');
|
|
42
|
+
}
|
|
43
|
+
function workspace(name) {
|
|
44
|
+
mkdirSync(join(root, `${name}.xcworkspace`), { recursive: true });
|
|
45
|
+
writeFileSync(join(root, `${name}.xcworkspace`, "contents.xcworkspacedata"), '<?xml version="1.0" encoding="UTF-8"?>\n<Workspace version = "1.0"></Workspace>\n');
|
|
46
|
+
}
|
|
47
|
+
describe("isXcodeContainer", () => {
|
|
48
|
+
test("a project with a project.pbxproj is one", () => {
|
|
49
|
+
project("Real");
|
|
50
|
+
expect(isXcodeContainer(join(root, "Real.xcodeproj"))).toBe(true);
|
|
51
|
+
});
|
|
52
|
+
test("a project with no project.pbxproj is not", () => {
|
|
53
|
+
husk("iosApp");
|
|
54
|
+
expect(isXcodeContainer(join(root, "iosApp.xcodeproj"))).toBe(false);
|
|
55
|
+
});
|
|
56
|
+
test("an empty project.pbxproj is not a project either", () => {
|
|
57
|
+
mkdirSync(join(root, "Empty.xcodeproj"), { recursive: true });
|
|
58
|
+
writeFileSync(join(root, "Empty.xcodeproj", "project.pbxproj"), "");
|
|
59
|
+
expect(isXcodeContainer(join(root, "Empty.xcodeproj"))).toBe(false);
|
|
60
|
+
});
|
|
61
|
+
test("a workspace needs its contents.xcworkspacedata", () => {
|
|
62
|
+
workspace("Real");
|
|
63
|
+
mkdirSync(join(root, "Husk.xcworkspace"), { recursive: true });
|
|
64
|
+
expect(isXcodeContainer(join(root, "Real.xcworkspace"))).toBe(true);
|
|
65
|
+
expect(isXcodeContainer(join(root, "Husk.xcworkspace"))).toBe(false);
|
|
66
|
+
});
|
|
67
|
+
test("a path that is not named like a container is never one", () => {
|
|
68
|
+
mkdirSync(join(root, "Sources"), { recursive: true });
|
|
69
|
+
expect(isXcodeContainer(join(root, "Sources"))).toBe(false);
|
|
70
|
+
expect(isXcodeContainer(join(root, "does-not-exist"))).toBe(false);
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
describe("xcodeContainersIn", () => {
|
|
74
|
+
test("keeps the real projects and drops the husks", () => {
|
|
75
|
+
husk("iosApp");
|
|
76
|
+
project("oakane");
|
|
77
|
+
expect(xcodeContainersIn(root)).toEqual(["oakane.xcodeproj"]);
|
|
78
|
+
});
|
|
79
|
+
test("an unreadable directory is empty, not a throw", () => {
|
|
80
|
+
expect(xcodeContainersIn(join(root, "nowhere"))).toEqual([]);
|
|
81
|
+
});
|
|
82
|
+
});
|
|
83
|
+
describe("detectMobileProjectRoot", () => {
|
|
84
|
+
test("a husk alone is not an iOS project root", () => {
|
|
85
|
+
husk("iosApp");
|
|
86
|
+
expect(detectMobileProjectRoot(root)).toBeNull();
|
|
87
|
+
});
|
|
88
|
+
test("a real project beside a husk still is", () => {
|
|
89
|
+
husk("iosApp");
|
|
90
|
+
project("oakane");
|
|
91
|
+
expect(detectMobileProjectRoot(root)).toBe("ios");
|
|
92
|
+
});
|
|
93
|
+
});
|
|
94
|
+
describe("scanIosProject", () => {
|
|
95
|
+
test("Oakane's shape resolves the project that exists, and its scheme", async () => {
|
|
96
|
+
husk("iosApp");
|
|
97
|
+
project("oakane");
|
|
98
|
+
const scan = await scanIosProject(root);
|
|
99
|
+
expect(scan.projectPath).toBe("oakane.xcodeproj");
|
|
100
|
+
// No shared schemes anywhere, so each project falls back to its own name —
|
|
101
|
+
// and only one of them is a project.
|
|
102
|
+
expect(scan.detectedSchemes).toEqual(["oakane"]);
|
|
103
|
+
});
|
|
104
|
+
test("a repository whose only project is a husk resolves nothing", async () => {
|
|
105
|
+
husk("iosApp");
|
|
106
|
+
const scan = await scanIosProject(root);
|
|
107
|
+
expect(scan.projectPath).toBe("");
|
|
108
|
+
expect(scan.detectedSchemes).toEqual([]);
|
|
109
|
+
});
|
|
110
|
+
test("a single valid project is unchanged", async () => {
|
|
111
|
+
project("Contributions");
|
|
112
|
+
const scan = await scanIosProject(root);
|
|
113
|
+
expect(scan.projectPath).toBe("Contributions.xcodeproj");
|
|
114
|
+
expect(scan.detectedSchemes).toEqual(["Contributions"]);
|
|
115
|
+
});
|
|
116
|
+
test("a workspace still outranks a project beside it", async () => {
|
|
117
|
+
project("QiniuSDK");
|
|
118
|
+
workspace("QiniuSDK");
|
|
119
|
+
const scan = await scanIosProject(root);
|
|
120
|
+
expect(scan.projectPath).toBe("QiniuSDK.xcworkspace");
|
|
121
|
+
});
|
|
122
|
+
});
|
|
123
|
+
//# sourceMappingURL=xcode-container.test.js.map
|