@multiplatform.one/cli 7.23.0 → 7.23.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/README.md CHANGED
@@ -46,10 +46,12 @@ A project is the multiplatform.one monorepo at one commit, pruned:
46
46
  `features/my-app` beside it. Every `--app` is another copy. The
47
47
  delivery targets (`gnome/`, `tauri/`, `vscode/`, `webext/`, the Frappe SPA)
48
48
  ride inside each product app; there is nothing to add for them.
49
- - `packages/themes` is the app's one theme config, and it carries a declared
50
- theme subset (`subset.ts`, `subset.tamagui.config.ts`). A narrow surface
51
- ships only the themes it can enter, because every theme is CSS in the served
52
- document; `packages/themes/README.md` has the measurement and the switch.
49
+ - `packages/themes` is the app's one theme config, and the app ships its
50
+ declared theme subset (`subset.ts`, `subset.tamagui.config.ts`): 340 of the
51
+ 1,580 house themes. A narrow surface ships only the themes it can enter,
52
+ because every theme is CSS in the served document;
53
+ `packages/themes/README.md` has the measurement and what to do when a
54
+ surface needs a hue the subset drops.
53
55
  - `docs/`, `deploy/`, `.changeset`, `typedoc.json` and `.gitlab-ci.yml` are
54
56
  monorepo concerns and go too. `AGENTS.md` is the consumer copy
55
57
  (`templates/AGENTS.md`), which names only paths and scripts the project has.
@@ -11,7 +11,7 @@ import { tmpdir } from "node:os";
11
11
  * ours = the project's HEAD (every commit the project made)
12
12
  * theirs = the scaffold it is moving to
13
13
  *
14
- * `git merge-tree --write-tree --merge-base=<base>` merges the scaffold's
14
+ * `git merge-tree --write-tree` (git 2.38+) merges the scaffold's
15
15
  * evolution with the project's history; the result lands in the index and
16
16
  * worktree, never in a commit, so the project commits when it is happy.
17
17
  * `.updateignore` entries (exact files or directory prefixes, not globs) and
@@ -296,6 +296,14 @@ async function mergeScaffolds(request) {
296
296
  const head = await gitOut(projectDir, ["rev-parse", "HEAD"]);
297
297
  const baseCommit = request.baseDir ? await commitTreeFromDir(projectDir, request.baseDir, request.baseLabel) : await emptyCommit(projectDir, request.baseLabel);
298
298
  const nextCommit = await commitTreeFromDir(projectDir, request.theirsDir, request.theirsLabel, baseCommit);
299
+ const oursCommit = await gitOut(projectDir, [
300
+ "commit-tree",
301
+ `${head}^{tree}`,
302
+ "-p",
303
+ baseCommit,
304
+ "-m",
305
+ "HEAD"
306
+ ]);
299
307
  let mergedTree;
300
308
  let conflicts = [];
301
309
  try {
@@ -303,8 +311,7 @@ async function mergeScaffolds(request) {
303
311
  "merge-tree",
304
312
  "--write-tree",
305
313
  "--name-only",
306
- `--merge-base=${baseCommit}`,
307
- head,
314
+ oursCommit,
308
315
  nextCommit
309
316
  ])).split("\n")[0].trim();
310
317
  } catch (err) {
package/lib/source.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "cliVersion": "7.23.0",
2
+ "cliVersion": "7.23.2",
3
3
  "url": "https://git.corp.bitspur.com/multiplatform.one/mpo.git",
4
- "sha": "bbb1b53984fb27eae564916820ed36fa54989197"
4
+ "sha": "650bb329c5583be3ad3f8c4bb4b50671cc3fe9a8"
5
5
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@multiplatform.one/cli",
3
- "version": "7.23.0",
3
+ "version": "7.23.2",
4
4
  "description": "multiplatform.one cli — mpo init clones and prunes the monorepo, mpo update merges it back",
5
5
  "keywords": [
6
6
  "create-multiplatform-app",
@@ -56,7 +56,7 @@
56
56
  "access": "public"
57
57
  },
58
58
  "dependencies": {
59
- "@multiplatform.one/utils": "7.23.0",
59
+ "@multiplatform.one/utils": "7.23.2",
60
60
  "commander": "^14.0.3",
61
61
  "dotenv": "^17.4.2",
62
62
  "inquirer": "^13.4.2",
@@ -1,5 +1,9 @@
1
- import { describe, expect, it } from "vitest";
2
- import { threeWayMergePackageJson } from "./merge";
1
+ import { chmodSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
2
+ import { tmpdir } from "node:os";
3
+ import { join } from "node:path";
4
+ import spawn from "nano-spawn";
5
+ import { afterEach, describe, expect, it } from "vitest";
6
+ import { gitOut, mergeScaffolds, threeWayMergePackageJson } from "./merge";
3
7
 
4
8
  const json = (value: unknown) => JSON.stringify(value, null, 2);
5
9
 
@@ -79,3 +83,54 @@ describe("threeWayMergePackageJson", () => {
79
83
  });
80
84
  });
81
85
  });
86
+
87
+ describe("mergeScaffolds on git 2.38 and 2.39, whose merge-tree has no --merge-base", () => {
88
+ const scratch: string[] = [];
89
+ const path = process.env.PATH;
90
+
91
+ afterEach(() => {
92
+ process.env.PATH = path;
93
+ for (const dir of scratch.splice(0)) rmSync(dir, { recursive: true, force: true });
94
+ });
95
+
96
+ function tempDir(files: Record<string, string> = {}): string {
97
+ const dir = mkdtempSync(join(tmpdir(), "mpo-merge-spec-"));
98
+ scratch.push(dir);
99
+ for (const [name, content] of Object.entries(files)) writeFileSync(join(dir, name), content);
100
+ return dir;
101
+ }
102
+
103
+ it("merges the scaffold's change into the project through history alone", async () => {
104
+ const shim = tempDir({
105
+ git: `#!/bin/sh\nfor arg in "$@"; do\n case $arg in --merge-base*) exit 129 ;; esac\ndone\nPATH='${path}' exec git "$@"\n`,
106
+ });
107
+ chmodSync(join(shim, "git"), 0o755);
108
+
109
+ const project = tempDir({ "a.txt": "one\n", "b.txt": "b\n", "mine.txt": "mine\n" });
110
+ await spawn("git", ["init", "--quiet"], { cwd: project });
111
+ await spawn("git", ["config", "user.email", "spec@example.com"], { cwd: project });
112
+ await spawn("git", ["config", "user.name", "Spec"], { cwd: project });
113
+ await spawn("git", ["add", "-A"], { cwd: project });
114
+ await spawn("git", ["commit", "--quiet", "-m", "project"], { cwd: project });
115
+
116
+ process.env.PATH = `${shim}:${path}`;
117
+ await expect(
118
+ spawn("git", ["merge-tree", "--write-tree", "--merge-base=HEAD", "HEAD", "HEAD"], {
119
+ cwd: project,
120
+ }),
121
+ ).rejects.toMatchObject({ exitCode: 129 });
122
+
123
+ const outcome = await mergeScaffolds({
124
+ projectDir: project,
125
+ baseDir: tempDir({ "a.txt": "one\n", "b.txt": "b\n" }),
126
+ theirsDir: tempDir({ "a.txt": "two\n", "b.txt": "b\n" }),
127
+ baseLabel: "scaffold 1",
128
+ theirsLabel: "scaffold 2",
129
+ });
130
+
131
+ expect(outcome.conflicts).toEqual([]);
132
+ expect(outcome.changed).toBe(true);
133
+ expect(await gitOut(project, ["show", `${outcome.mergedTree}:a.txt`])).toBe("two");
134
+ expect(await gitOut(project, ["show", `${outcome.mergedTree}:mine.txt`])).toBe("mine");
135
+ });
136
+ });
@@ -5,7 +5,7 @@
5
5
  * ours = the project's HEAD (every commit the project made)
6
6
  * theirs = the scaffold it is moving to
7
7
  *
8
- * `git merge-tree --write-tree --merge-base=<base>` merges the scaffold's
8
+ * `git merge-tree --write-tree` (git 2.38+) merges the scaffold's
9
9
  * evolution with the project's history; the result lands in the index and
10
10
  * worktree, never in a commit, so the project commits when it is happy.
11
11
  * `.updateignore` entries (exact files or directory prefixes, not globs) and
@@ -324,6 +324,17 @@ export async function mergeScaffolds(request: MergeRequest): Promise<MergeOutcom
324
324
  request.theirsLabel,
325
325
  baseCommit,
326
326
  );
327
+ // merge-tree reads --merge-base only from git 2.40, and Debian bookworm
328
+ // ships 2.39. HEAD's tree on a commit parented by the base hands it the
329
+ // same base through history instead.
330
+ const oursCommit = await gitOut(projectDir, [
331
+ "commit-tree",
332
+ `${head}^{tree}`,
333
+ "-p",
334
+ baseCommit,
335
+ "-m",
336
+ "HEAD",
337
+ ]);
327
338
 
328
339
  let mergedTree: string;
329
340
  let conflicts: string[] = [];
@@ -332,8 +343,7 @@ export async function mergeScaffolds(request: MergeRequest): Promise<MergeOutcom
332
343
  "merge-tree",
333
344
  "--write-tree",
334
345
  "--name-only",
335
- `--merge-base=${baseCommit}`,
336
- head,
346
+ oursCommit,
337
347
  nextCommit,
338
348
  ]);
339
349
  mergedTree = out.split("\n")[0]!.trim();
@@ -5,7 +5,7 @@
5
5
  * ours = the project's HEAD (every commit the project made)
6
6
  * theirs = the scaffold it is moving to
7
7
  *
8
- * `git merge-tree --write-tree --merge-base=<base>` merges the scaffold's
8
+ * `git merge-tree --write-tree` (git 2.38+) merges the scaffold's
9
9
  * evolution with the project's history; the result lands in the index and
10
10
  * worktree, never in a commit, so the project commits when it is happy.
11
11
  * `.updateignore` entries (exact files or directory prefixes, not globs) and
@@ -1 +1 @@
1
- {"version":3,"file":"merge.d.ts","sourceRoot":"","sources":["../../src/commands/merge.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAOH,wBAAsB,GAAG,CAAC,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,wCAKzF;AAED,wBAAsB,MAAM,CAAC,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,mBAG5F;AAED,wBAAsB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAIpE;AAED,+EAA+E;AAC/E,wBAAsB,gBAAgB,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAUzF;AAuED;;;;;;GAMG;AACH,wBAAgB,QAAQ,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,EAAE,CAkBrD;AA2ED;0EAC0E;AAC1E,wBAAgB,wBAAwB,CACtC,IAAI,EAAE,MAAM,GAAG,SAAS,EACxB,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,GACb,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAerC;AAwDD,MAAM,WAAW,YAAY;IAC3B,UAAU,EAAE,MAAM,CAAC;IACnB,yEAAyE;IACzE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,YAAY;IAC3B,UAAU,EAAE,MAAM,CAAC;IACnB,yDAAyD;IACzD,OAAO,EAAE,OAAO,CAAC;IACjB,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAED,8FAA8F;AAC9F,wBAAsB,cAAc,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,YAAY,CAAC,CA2DjF;AAED;2DAC2D;AAC3D,wBAAsB,QAAQ,CAAC,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAE9E;AAED,gFAAgF;AAChF,wBAAsB,WAAW,CAC/B,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,YAAY,EACrB,aAAa,EAAE,MAAM,EACrB,OAAO,GAAE;IAAE,WAAW,CAAC,EAAE,OAAO,CAAA;CAAO,GACtC,OAAO,CAAC,IAAI,CAAC,CAuBf"}
1
+ {"version":3,"file":"merge.d.ts","sourceRoot":"","sources":["../../src/commands/merge.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAOH,wBAAsB,GAAG,CAAC,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,wCAKzF;AAED,wBAAsB,MAAM,CAAC,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,mBAG5F;AAED,wBAAsB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAIpE;AAED,+EAA+E;AAC/E,wBAAsB,gBAAgB,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAUzF;AAuED;;;;;;GAMG;AACH,wBAAgB,QAAQ,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,EAAE,CAkBrD;AA2ED;0EAC0E;AAC1E,wBAAgB,wBAAwB,CACtC,IAAI,EAAE,MAAM,GAAG,SAAS,EACxB,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,GACb,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAerC;AAwDD,MAAM,WAAW,YAAY;IAC3B,UAAU,EAAE,MAAM,CAAC;IACnB,yEAAyE;IACzE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,YAAY;IAC3B,UAAU,EAAE,MAAM,CAAC;IACnB,yDAAyD;IACzD,OAAO,EAAE,OAAO,CAAC;IACjB,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAED,8FAA8F;AAC9F,wBAAsB,cAAc,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,YAAY,CAAC,CAqEjF;AAED;2DAC2D;AAC3D,wBAAsB,QAAQ,CAAC,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAE9E;AAED,gFAAgF;AAChF,wBAAsB,WAAW,CAC/B,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,YAAY,EACrB,aAAa,EAAE,MAAM,EACrB,OAAO,GAAE;IAAE,WAAW,CAAC,EAAE,OAAO,CAAA;CAAO,GACtC,OAAO,CAAC,IAAI,CAAC,CAuBf"}