From 3528de43e15818b0e1681e82d3c82b692b757e31 Mon Sep 17 00:00:00 2001 From: Daniel Murta Date: Tue, 29 Sep 2026 14:46:26 -0300 Subject: [PATCH 1/4] =?UTF-8?q?release:=20labelle-cli=20v4.0.0=20=E2=80=94?= =?UTF-8?q?=20the=20full=20agnostic=20core=20(RFC=20cli#471)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The CLI core names no platform, backend or store. The assembler answers backend and target facts through `describe` (protocol 7), and providers own iOS (labelle-ios) and SDL2 (labelle-sdl's sdl2 provider). See docs/migrating-to-4.0.md. A cli pin on an older major now points at that guide. --- build.zig.zon | 2 +- src/cli/compatibility.zig | 14 +++++++++++--- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/build.zig.zon b/build.zig.zon index 9943e01..9d1b376 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -1,7 +1,7 @@ .{ .fingerprint = 0xa3d95bf7b905a401, .name = .labelle_cli, - .version = "3.1.0", + .version = "4.0.0", .minimum_zig_version = "0.16.0", .dependencies = .{ // Issue #217: labelle-cli is a thin driver over the standalone diff --git a/src/cli/compatibility.zig b/src/cli/compatibility.zig index 0be531b..72bc8df 100644 --- a/src/cli/compatibility.zig +++ b/src/cli/compatibility.zig @@ -141,9 +141,10 @@ fn compatWarnings(cfg: project_config.ProjectConfig, comptime emit: bool) u8 { /// The migration guide for a `cli` pin on an older major line than this /// CLI, when crossing that line needs a project edit `upgrade all` cannot /// make: 2.0 moved the non-desktop targets into provider packages -/// (cli#405), and 3.0 removed the CLI's own browser toolchain, serve and -/// export in favour of its provider package (RFC cli#466). The guide of the -/// first line crossed comes first. Null for every other package or line. +/// (cli#405), 3.0 removed the CLI's own browser toolchain, serve and +/// export in favour of its provider package (RFC cli#466), and 4.0 left +/// backend identity to the assembler and the remaining platform code to +/// providers (RFC cli#471). The guide of the first line crossed comes first. Null for every other package or line. fn migrationGuide(d: DiamondPin) ?[]const u8 { if (!std.mem.eql(u8, d.name, "cli")) return null; const pinned = parseVersion(d.pinned).major; @@ -156,6 +157,10 @@ fn migrationGuide(d: DiamondPin) ?[]const u8 { return "upgrading alone is not enough: the 3.0 CLI removed its built-in browser toolchain, serve and\n" ++ " export, so the project may need migrating too; see https://github.com/labelle-toolkit/labelle-cli/blob/main/docs/migrating-to-3.0.md"; } + if (pinned < 4 and curated >= 4) { + return "upgrading alone is not enough: the 4.0 CLI needs assembler 0.118.0+ and moved the remaining platform\n" ++ + " code into provider packages, so the project may need migrating too; see https://github.com/labelle-toolkit/labelle-cli/blob/main/docs/migrating-to-4.0.md"; + } return null; } @@ -168,6 +173,9 @@ test "compat: a cli pin behind a breaking CLI line points at that line's migrati try std.testing.expect(std.mem.indexOf(u8, three, "docs/migrating-to-3.0.md") != null); try std.testing.expect(migrationGuide(.{ .name = "cli", .pinned = "2.0.0", .curated = "2.1.0" }) == null); try std.testing.expect(migrationGuide(.{ .name = "cli", .pinned = "3.0.0", .curated = "3.1.0" }) == null); + const four = migrationGuide(.{ .name = "cli", .pinned = "3.1.0", .curated = "4.0.0" }).?; + try std.testing.expect(std.mem.indexOf(u8, four, "docs/migrating-to-4.0.md") != null); + try std.testing.expect(migrationGuide(.{ .name = "cli", .pinned = "4.0.0", .curated = "4.0.1" }) == null); try std.testing.expect(migrationGuide(.{ .name = "cli", .pinned = "1.60.0", .curated = "1.75.0" }) == null); try std.testing.expect(migrationGuide(.{ .name = "engine", .pinned = "1.67.0", .curated = "2.0.0" }) == null); } From 129acf89ea4b83b99da3ea16051ebf77b3113b61 Mon Sep 17 00:00:00 2001 From: Daniel Murta Date: Tue, 29 Sep 2026 14:55:15 -0300 Subject: [PATCH 2/4] fix(compat): a pre-4 cli pin also gets the 4.0 guide A 1.x or 2.x pin returned the 2.0 or 3.0 guide first and never reached the 4.0 branch. Neither older guide links 4.0, and `upgrade all` then silences the warning. Chain the 4.0 steps after the first guide whenever the pin is below 4. --- src/cli/compatibility.zig | 29 +++++++++++++++++++++-------- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/src/cli/compatibility.zig b/src/cli/compatibility.zig index 72bc8df..98c873e 100644 --- a/src/cli/compatibility.zig +++ b/src/cli/compatibility.zig @@ -149,21 +149,28 @@ fn migrationGuide(d: DiamondPin) ?[]const u8 { if (!std.mem.eql(u8, d.name, "cli")) return null; const pinned = parseVersion(d.pinned).major; const curated = parseVersion(d.curated).major; + // Crossing 4.0 from an older line also needs the 4.0 steps, which the + // older guides predate: chain them after the first guide. + const then_four = pinned < 4 and curated >= 4; if (pinned < 2 and curated >= 2) { - return "upgrading alone is not enough: the 2.0 CLI builds every non-desktop target through\n" ++ - " provider packages, so the project needs migrating too; see https://github.com/labelle-toolkit/labelle-cli/blob/main/docs/migrating-to-2.0.md"; + return if (then_four) guide_two ++ guide_then_four else guide_two; } if (pinned < 3 and curated >= 3) { - return "upgrading alone is not enough: the 3.0 CLI removed its built-in browser toolchain, serve and\n" ++ - " export, so the project may need migrating too; see https://github.com/labelle-toolkit/labelle-cli/blob/main/docs/migrating-to-3.0.md"; - } - if (pinned < 4 and curated >= 4) { - return "upgrading alone is not enough: the 4.0 CLI needs assembler 0.118.0+ and moved the remaining platform\n" ++ - " code into provider packages, so the project may need migrating too; see https://github.com/labelle-toolkit/labelle-cli/blob/main/docs/migrating-to-4.0.md"; + return if (then_four) guide_three ++ guide_then_four else guide_three; } + if (then_four) return guide_four; return null; } +const guide_two = "upgrading alone is not enough: the 2.0 CLI builds every non-desktop target through\n" ++ + " provider packages, so the project needs migrating too; see https://github.com/labelle-toolkit/labelle-cli/blob/main/docs/migrating-to-2.0.md"; +const guide_three = "upgrading alone is not enough: the 3.0 CLI removed its built-in browser toolchain, serve and\n" ++ + " export, so the project may need migrating too; see https://github.com/labelle-toolkit/labelle-cli/blob/main/docs/migrating-to-3.0.md"; +const guide_four = "upgrading alone is not enough: the 4.0 CLI needs assembler 0.118.0+ and moved the remaining platform\n" ++ + " code into provider packages, so the project may need migrating too; see https://github.com/labelle-toolkit/labelle-cli/blob/main/docs/migrating-to-4.0.md"; +const guide_then_four = "\n then the 4.0 steps (assembler 0.118.0+, platform code in provider packages):\n" ++ + " https://github.com/labelle-toolkit/labelle-cli/blob/main/docs/migrating-to-4.0.md"; + test "compat: a cli pin behind a breaking CLI line points at that line's migration guide; nothing else does" { const guide = migrationGuide(.{ .name = "cli", .pinned = "1.67.0", .curated = "2.0.0" }).?; try std.testing.expect(std.mem.indexOf(u8, guide, "docs/migrating-to-2.0.md") != null); @@ -173,6 +180,12 @@ test "compat: a cli pin behind a breaking CLI line points at that line's migrati try std.testing.expect(std.mem.indexOf(u8, three, "docs/migrating-to-3.0.md") != null); try std.testing.expect(migrationGuide(.{ .name = "cli", .pinned = "2.0.0", .curated = "2.1.0" }) == null); try std.testing.expect(migrationGuide(.{ .name = "cli", .pinned = "3.0.0", .curated = "3.1.0" }) == null); + // Crossing 4.0 from 1.x or 2.x: the first guide, then the 4.0 steps. + for ([_][]const u8{ "1.67.0", "2.1.1" }) |old| { + const chained = migrationGuide(.{ .name = "cli", .pinned = old, .curated = "4.0.0" }).?; + try std.testing.expect(std.mem.indexOf(u8, chained, "docs/migrating-to-4.0.md") != null); + } + try std.testing.expect(std.mem.indexOf(u8, migrationGuide(.{ .name = "cli", .pinned = "2.1.1", .curated = "3.1.0" }).?, "migrating-to-4.0") == null); const four = migrationGuide(.{ .name = "cli", .pinned = "3.1.0", .curated = "4.0.0" }).?; try std.testing.expect(std.mem.indexOf(u8, four, "docs/migrating-to-4.0.md") != null); try std.testing.expect(migrationGuide(.{ .name = "cli", .pinned = "4.0.0", .curated = "4.0.1" }) == null); From 603db0a6b626d5daaab9e6819b56915d8a42c27d Mon Sep 17 00:00:00 2001 From: Daniel Murta Date: Tue, 29 Sep 2026 14:59:42 -0300 Subject: [PATCH 3/4] fix(compat): chain every crossed major's guide, 3.0 included A 1.x pin going to 4.0 got the 2.0 and 4.0 guides and skipped 3.0's browser-toolchain move. Chain every crossed line in order. --- src/cli/compatibility.zig | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/src/cli/compatibility.zig b/src/cli/compatibility.zig index 98c873e..88eac5b 100644 --- a/src/cli/compatibility.zig +++ b/src/cli/compatibility.zig @@ -149,11 +149,14 @@ fn migrationGuide(d: DiamondPin) ?[]const u8 { if (!std.mem.eql(u8, d.name, "cli")) return null; const pinned = parseVersion(d.pinned).major; const curated = parseVersion(d.curated).major; - // Crossing 4.0 from an older line also needs the 4.0 steps, which the - // older guides predate: chain them after the first guide. + // Every crossed major line has its own steps, and the older guides + // predate the newer ones: chain them all after the first guide. + const then_three = pinned < 3 and curated >= 3; const then_four = pinned < 4 and curated >= 4; if (pinned < 2 and curated >= 2) { - return if (then_four) guide_two ++ guide_then_four else guide_two; + if (then_four) return guide_two ++ guide_then_three ++ guide_then_four; + if (then_three) return guide_two ++ guide_then_three; + return guide_two; } if (pinned < 3 and curated >= 3) { return if (then_four) guide_three ++ guide_then_four else guide_three; @@ -168,6 +171,8 @@ const guide_three = "upgrading alone is not enough: the 3.0 CLI removed its buil " export, so the project may need migrating too; see https://github.com/labelle-toolkit/labelle-cli/blob/main/docs/migrating-to-3.0.md"; const guide_four = "upgrading alone is not enough: the 4.0 CLI needs assembler 0.118.0+ and moved the remaining platform\n" ++ " code into provider packages, so the project may need migrating too; see https://github.com/labelle-toolkit/labelle-cli/blob/main/docs/migrating-to-4.0.md"; +const guide_then_three = "\n then the 3.0 steps (the browser toolchain moved to the web provider):\n" ++ + " https://github.com/labelle-toolkit/labelle-cli/blob/main/docs/migrating-to-3.0.md"; const guide_then_four = "\n then the 4.0 steps (assembler 0.118.0+, platform code in provider packages):\n" ++ " https://github.com/labelle-toolkit/labelle-cli/blob/main/docs/migrating-to-4.0.md"; @@ -180,6 +185,12 @@ test "compat: a cli pin behind a breaking CLI line points at that line's migrati try std.testing.expect(std.mem.indexOf(u8, three, "docs/migrating-to-3.0.md") != null); try std.testing.expect(migrationGuide(.{ .name = "cli", .pinned = "2.0.0", .curated = "2.1.0" }) == null); try std.testing.expect(migrationGuide(.{ .name = "cli", .pinned = "3.0.0", .curated = "3.1.0" }) == null); + // Crossing 3.0 and 4.0 from 1.x: every crossed guide, in order. + const from_one = migrationGuide(.{ .name = "cli", .pinned = "1.67.0", .curated = "4.0.0" }).?; + const i2 = std.mem.indexOf(u8, from_one, "migrating-to-2.0").?; + const i3 = std.mem.indexOf(u8, from_one, "migrating-to-3.0").?; + const i4 = std.mem.indexOf(u8, from_one, "migrating-to-4.0").?; + try std.testing.expect(i2 < i3 and i3 < i4); // Crossing 4.0 from 1.x or 2.x: the first guide, then the 4.0 steps. for ([_][]const u8{ "1.67.0", "2.1.1" }) |old| { const chained = migrationGuide(.{ .name = "cli", .pinned = old, .curated = "4.0.0" }).?; From 89db52df895d21e31fdb908cd139cc18d033c76c Mon Sep 17 00:00:00 2001 From: Daniel Murta Date: Tue, 29 Sep 2026 15:02:14 -0300 Subject: [PATCH 4/4] fix(compat): rename test locals that shadow primitives; neutral guide wording The previous commit didn't compile (i2/i3/i4 shadow primitive types), and its 3.0 hint named a platform the agnostic guard forbids. --- src/cli/compatibility.zig | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/cli/compatibility.zig b/src/cli/compatibility.zig index 88eac5b..eee03fa 100644 --- a/src/cli/compatibility.zig +++ b/src/cli/compatibility.zig @@ -171,7 +171,7 @@ const guide_three = "upgrading alone is not enough: the 3.0 CLI removed its buil " export, so the project may need migrating too; see https://github.com/labelle-toolkit/labelle-cli/blob/main/docs/migrating-to-3.0.md"; const guide_four = "upgrading alone is not enough: the 4.0 CLI needs assembler 0.118.0+ and moved the remaining platform\n" ++ " code into provider packages, so the project may need migrating too; see https://github.com/labelle-toolkit/labelle-cli/blob/main/docs/migrating-to-4.0.md"; -const guide_then_three = "\n then the 3.0 steps (the browser toolchain moved to the web provider):\n" ++ +const guide_then_three = "\n then the 3.0 steps (the browser toolchain moved into its provider package):\n" ++ " https://github.com/labelle-toolkit/labelle-cli/blob/main/docs/migrating-to-3.0.md"; const guide_then_four = "\n then the 4.0 steps (assembler 0.118.0+, platform code in provider packages):\n" ++ " https://github.com/labelle-toolkit/labelle-cli/blob/main/docs/migrating-to-4.0.md"; @@ -187,10 +187,10 @@ test "compat: a cli pin behind a breaking CLI line points at that line's migrati try std.testing.expect(migrationGuide(.{ .name = "cli", .pinned = "3.0.0", .curated = "3.1.0" }) == null); // Crossing 3.0 and 4.0 from 1.x: every crossed guide, in order. const from_one = migrationGuide(.{ .name = "cli", .pinned = "1.67.0", .curated = "4.0.0" }).?; - const i2 = std.mem.indexOf(u8, from_one, "migrating-to-2.0").?; - const i3 = std.mem.indexOf(u8, from_one, "migrating-to-3.0").?; - const i4 = std.mem.indexOf(u8, from_one, "migrating-to-4.0").?; - try std.testing.expect(i2 < i3 and i3 < i4); + const at_two = std.mem.indexOf(u8, from_one, "migrating-to-2.0").?; + const at_three = std.mem.indexOf(u8, from_one, "migrating-to-3.0").?; + const at_four = std.mem.indexOf(u8, from_one, "migrating-to-4.0").?; + try std.testing.expect(at_two < at_three and at_three < at_four); // Crossing 4.0 from 1.x or 2.x: the first guide, then the 4.0 steps. for ([_][]const u8{ "1.67.0", "2.1.1" }) |old| { const chained = migrationGuide(.{ .name = "cli", .pinned = old, .curated = "4.0.0" }).?;