From eb3200a97c7bf74704b5615f9c958fe662ea2ef3 Mon Sep 17 00:00:00 2001 From: sunrisepeak Date: Mon, 29 Jun 2026 23:35:01 +0800 Subject: [PATCH 1/5] feat(profile): default build profile = dev (mainstream); --release opt-in; v0.0.76 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Resolves #179. mcpp's old default profile was "release" (inherited from its xmake/xlings lineage), but the dominant convention is the opposite: a bare build is debug/dev and release is opt-in (Cargo/Meson/CMake/Zig/Bazel/MSBuild). Since mcpp's surface is Cargo-flavored, flip the global default to "dev" (-O0 -g); release is opt-in via --release / --profile release. - prepare.cppm: global default profile release -> dev. Precedence: --profile/--release/--dev flag > [build].default-profile > "dev". - cli.cppm/cmd_build.cppm: add --release / --dev shorthands (--release is now the mainstream opt-in flag). - manifest.cppm: [build].default-profile (alias: profile) — a project's own default; its role inverts under the flip to "opt into release" for projects that ship/run optimized. This is the migration mechanism. - mcpp.toml: default-profile = "release" — keeps mcpp's self-host CI build and release.yml at -O2 with ZERO workflow changes (no --release threaded through release pipelines; mcpp's own CI doesn't slow to -O0). - cross-build-test.yml: bump xlings cache key v2->v3 (the failing job was a poisoned ~/.xlings cache from the bootstrap-pin-timing, not code). Tests: 87_build_default_profile.sh (global dev; --release/--dev; project default-profile=release; --dev override), 68_profile_passthrough.sh (updated for the new default). Regression: unit 27/0. Doc: design doc L0.5 (convention table + decision + migration). mcpp self-build verified 'Finished release [optimized]'. --- ...anifest-environment-and-platform-design.md | 37 ++++++++++++++ .github/workflows/cross-build-test.yml | 4 +- mcpp.toml | 7 ++- src/build/prepare.cppm | 15 ++++-- src/cli.cppm | 2 + src/cli/cmd_build.cppm | 5 ++ src/manifest.cppm | 10 ++++ src/toolchain/fingerprint.cppm | 2 +- tests/e2e/68_profile_passthrough.sh | 12 ++--- tests/e2e/87_build_default_profile.sh | 49 +++++++++++++++++++ 10 files changed, 130 insertions(+), 13 deletions(-) create mode 100755 tests/e2e/87_build_default_profile.sh diff --git a/.agents/docs/2026-06-29-manifest-environment-and-platform-design.md b/.agents/docs/2026-06-29-manifest-environment-and-platform-design.md index 5ecfd9f9..bf511581 100644 --- a/.agents/docs/2026-06-29-manifest-environment-and-platform-design.md +++ b/.agents/docs/2026-06-29-manifest-environment-and-platform-design.md @@ -92,6 +92,43 @@ expressible, all backed by existing xlings behavior. --- +## L0.5 — Default build profile: follow the mainstream (dev), `--release` opt-in + +(Issue #179.) mcpp's old default was `release`; the prevailing convention across +modern build tools is the **opposite** — a bare build is **debug/dev**, release is +opt-in: + +| Tool | bare-command default | release via | +|---|---|---| +| Cargo | **dev/debug** (-O0 + debuginfo) | `cargo build --release` | +| Meson | **debug** | `--buildtype=release` | +| CMake | empty `CMAKE_BUILD_TYPE` (≈debug) | `-DCMAKE_BUILD_TYPE=Release` | +| Zig | **Debug** | `-Doptimize=ReleaseFast/Safe/Small` | +| Bazel | **fastbuild** (no opt) | `-c opt` | +| MSBuild/VS | **Debug** | Release configuration | +| **xmake** | **release** | `xmake f -m debug` | + +Debug-default dominates (Cargo/Meson/CMake/Zig/Bazel/MSBuild); only the **xmake** +lineage defaults to release — exactly where mcpp's old default came from (mcpp builds +on xlings/xmake). But mcpp's *surface* is Cargo-flavored, so users expect Cargo +semantics. **Decision: flip the global default to `dev` (-O0 -g); release is opt-in +via `--release` / `--profile release`.** + +- **Precedence**: `--profile NAME` / `--release` / `--dev` flag > `[build].default-profile` + (project default) > global `dev`. (`prepare.cppm`; `--dev`/`--release` are shorthands + in `cli.cppm`/`cmd_build.cppm`.) +- **`[build].default-profile`** (alias: `profile`) — a project's own default; its role + *inverts* under the flip: it now means **"opt into release"** for projects that ship/ + run optimized by default. **mcpp's own `mcpp.toml` sets `default-profile = "release"`** + so the self-host CI build and `release.yml` stay `-O2` with **zero workflow changes** — + the knob *is* the migration mechanism (no `--release` threaded through release pipelines, + and mcpp's own CI doesn't slow to `-O0`). +- **Distribution footgun**: a project that defaults to dev passes `--release` to produce + a distributable (a pack-time release guard is a possible follow-up). +- Tests: `tests/e2e/87_build_default_profile.sh`, `68_profile_passthrough.sh` (updated). + +--- + ## L0 — Dependency categories: keep three axes, wire `build` Keep Cargo/Conan's **normal / dev / build** trichotomy (mcpp already declares all diff --git a/.github/workflows/cross-build-test.yml b/.github/workflows/cross-build-test.yml index ccc4c323..190b30db 100644 --- a/.github/workflows/cross-build-test.yml +++ b/.github/workflows/cross-build-test.yml @@ -74,9 +74,9 @@ jobs: uses: actions/cache@v4 with: path: ~/.xlings - key: xlings-${{ runner.os }}-v2-${{ hashFiles('.xlings.json') }} + key: xlings-${{ runner.os }}-v3-${{ hashFiles('.xlings.json') }} restore-keys: | - xlings-${{ runner.os }}-v2- + xlings-${{ runner.os }}-v3- - name: Install qemu-user-static run: | diff --git a/mcpp.toml b/mcpp.toml index f1a48d06..2eb31431 100644 --- a/mcpp.toml +++ b/mcpp.toml @@ -1,12 +1,17 @@ [package] name = "mcpp" -version = "0.0.75" +version = "0.0.76" description = "Modern C++ build & package management tool" license = "Apache-2.0" authors = ["mcpp-community"] repo = "https://github.com/mcpp-community/mcpp" [build] +# The global default profile is now "dev" (-O0 -g, mainstream convention); +# mcpp itself is a shipped tool, so opt its plain `mcpp build` (and release.yml's +# self-host build) back into the optimized profile. Without this the released +# binary would be -O0. A `--profile`/`--dev`/`--release` flag still overrides. +default-profile = "release" # nlohmann/json.hpp lives in src/libs/json/; expose it to the global # module fragment `#include ` in src/libs/json.cppm. include_dirs = ["src/libs/json"] diff --git a/src/build/prepare.cppm b/src/build/prepare.cppm index afa216d3..94a4f213 100644 --- a/src/build/prepare.cppm +++ b/src/build/prepare.cppm @@ -554,10 +554,19 @@ prepare_build(bool print_fingerprint, // 1. project mcpp.toml [toolchain]. or .default // 2. global ~/.mcpp/config.toml [toolchain].default // 3. hard error (no system fallback) - // Resolve the build profile: --profile (default "release") → built-in - // defaults, overlaid by any [profile.] from the manifest → buildConfig. + // Resolve the build profile, overlaid by any [profile.] from the + // manifest → buildConfig. { - std::string pname = overrides.profile.empty() ? "release" : overrides.profile; + // Precedence: --profile / --release / --dev flag (overrides.profile) > + // [build].default-profile (project default) > "dev" (global default). + // The global default is "dev" (-O0 -g) to follow the dominant convention + // (Cargo/Meson/CMake/Zig/Bazel/MSBuild all default to debug); release is + // opt-in via --release / --profile release. A project that wants its + // plain `mcpp build` optimized sets [build].default-profile = "release" + // (mcpp's own mcpp.toml does this, so the released binary stays -O2). + std::string pname = !overrides.profile.empty() ? overrides.profile + : !m->buildConfig.defaultProfile.empty() ? m->buildConfig.defaultProfile + : "dev"; mcpp::manifest::Profile pr; if (pname == "dev" || pname == "debug") { pr.optLevel = "0"; pr.debug = true; } else if (pname == "dist") { pr.optLevel = "3"; pr.strip = true; } diff --git a/src/cli.cppm b/src/cli.cppm index 6f4c0f7e..f9e5d9e3 100644 --- a/src/cli.cppm +++ b/src/cli.cppm @@ -226,6 +226,8 @@ int run(int argc, char** argv) { .help("Build only the named workspace member")) .option(cl::Option("profile").takes_value().value_name("NAME") .help("Build profile: release (default) | dev | dist | <[profile.*] name>")) + .option(cl::Option("release").help("Shorthand for --profile release")) + .option(cl::Option("dev").help("Shorthand for --profile dev (-O0 -g)")) .option(cl::Option("features").takes_value().value_name("LIST") .help("Activate root-package features (comma-separated)")) .option(cl::Option("cap").takes_value().value_name("LIST") diff --git a/src/cli/cmd_build.cppm b/src/cli/cmd_build.cppm index e5ff22de..0291e569 100644 --- a/src/cli/cmd_build.cppm +++ b/src/cli/cmd_build.cppm @@ -26,7 +26,12 @@ export int cmd_build(const mcpplibs::cmdline::ParsedArgs& parsed) { mcpp::build::BuildOverrides ov; if (auto t = parsed.value("target")) ov.target_triple = *t; if (auto p = parsed.value("package")) ov.package_filter = *p; + // Profile selection precedence: --profile NAME > --release / --dev > the + // project default ([build].default-profile) > "release", resolved in + // prepare_build. --release/--dev are shorthands only. if (auto pr = parsed.value("profile")) ov.profile = *pr; + else if (parsed.is_flag_set("release")) ov.profile = "release"; + else if (parsed.is_flag_set("dev")) ov.profile = "dev"; if (auto fs = parsed.value("features")) ov.features = *fs; if (auto cp = parsed.value("cap")) ov.capabilities = *cp; ov.strict = parsed.is_flag_set("strict"); diff --git a/src/manifest.cppm b/src/manifest.cppm index 032a5800..2f8d5010 100644 --- a/src/manifest.cppm +++ b/src/manifest.cppm @@ -148,6 +148,14 @@ struct BuildConfig { bool debug = false; // -g bool lto = false; // -flto bool strip = false; // link -s + // `[build].default-profile` (alias: `profile`) — the project's DEFAULT + // profile when no --profile/--dev/--release is passed. The global convention + // default stays "release"; this lets a project opt its plain `mcpp build` + // into e.g. "dev" without typing --profile. Precedence: --profile/--dev/ + // --release flag > [build].default-profile > "release". NOTE (distribution + // footgun): a project that defaults to dev should pass `--profile release` + // when producing a distributable (a pack-time release guard is a follow-up). + std::string defaultProfile; }; // `[runtime]` — requirements needed when launching built binaries. @@ -1118,6 +1126,8 @@ std::expected parse_string(std::string_view content, if (auto v = doc->get_string_array("build.cxxflags")) m.buildConfig.cxxflags = *v; if (auto v = doc->get_string_array("build.ldflags")) m.buildConfig.ldflags = *v; if (auto v = doc->get_string("build.c_standard")) m.buildConfig.cStandard = *v; + if (auto v = doc->get_string("build.default-profile")) m.buildConfig.defaultProfile = *v; + else if (auto v = doc->get_string("build.profile")) m.buildConfig.defaultProfile = *v; // accepted alias if (auto v = doc->get_string("build.macos_deployment_target")) m.buildConfig.macosDeploymentTarget = *v; for (auto const& flag : m.buildConfig.cxxflags) { diff --git a/src/toolchain/fingerprint.cppm b/src/toolchain/fingerprint.cppm index 94b77e6d..ee8c1f73 100644 --- a/src/toolchain/fingerprint.cppm +++ b/src/toolchain/fingerprint.cppm @@ -18,7 +18,7 @@ import mcpp.toolchain.detect; export namespace mcpp::toolchain { -inline constexpr std::string_view MCPP_VERSION = "0.0.75"; +inline constexpr std::string_view MCPP_VERSION = "0.0.76"; struct FingerprintInputs { Toolchain toolchain; diff --git a/tests/e2e/68_profile_passthrough.sh b/tests/e2e/68_profile_passthrough.sh index 6654b1b6..23f87588 100755 --- a/tests/e2e/68_profile_passthrough.sh +++ b/tests/e2e/68_profile_passthrough.sh @@ -17,15 +17,15 @@ strip = true cxxflags = ["-fno-plt"] EOF -# Built-in release default: -O2, no -g. -"$MCPP" build --verbose > rel.log 2>&1 || { cat rel.log; echo "release build failed"; exit 1; } +# Global default is now "dev" (-O0 -g, mainstream convention); release is opt-in. +"$MCPP" build --release --verbose > rel.log 2>&1 || { cat rel.log; echo "release build failed"; exit 1; } grep -q -- "-O2" rel.log || { echo "release missing -O2"; exit 1; } -# dev: -O0 -g. +# Bare `mcpp build` (no flag) → the dev default: -O0 -g. rm -rf target -"$MCPP" build --profile dev --verbose > dev.log 2>&1 || { cat dev.log; echo "dev build failed"; exit 1; } -grep -q -- "-O0" dev.log || { echo "dev missing -O0"; exit 1; } -grep -q -- "-g" dev.log || { echo "dev missing -g"; exit 1; } +"$MCPP" build --verbose > dev.log 2>&1 || { cat dev.log; echo "dev build failed"; exit 1; } +grep -q -- "-O0" dev.log || { echo "default (dev) missing -O0"; exit 1; } +grep -q -- "-g" dev.log || { echo "default (dev) missing -g"; exit 1; } # dist from [profile.dist]: -O3 -flto + passthrough cxxflag, stripped binary. rm -rf target diff --git a/tests/e2e/87_build_default_profile.sh b/tests/e2e/87_build_default_profile.sh new file mode 100755 index 00000000..d12441c4 --- /dev/null +++ b/tests/e2e/87_build_default_profile.sh @@ -0,0 +1,49 @@ +#!/usr/bin/env bash +# 87_build_default_profile.sh — profile selection follows the mainstream convention: +# the GLOBAL default is "dev" (-O0 -g, like Cargo/Meson/CMake/Zig/Bazel/MSBuild); +# "release" is opt-in via --release / --profile release. A project can set its own +# default with `[build] default-profile = ""` (e.g. opt back into release). +# Precedence: --profile/--release/--dev flag > [build].default-profile > "dev". +# See .agents/docs/2026-06-29-manifest-environment-and-platform-design.md. +set -e + +TMP=$(mktemp -d) +trap "rm -rf $TMP" EXIT +cd "$TMP" + +# --- (1) A plain project: no [build].default-profile → GLOBAL default = dev. --- +mkdir -p plain/src +cat > plain/mcpp.toml <<'EOF' +[package] +name = "plain" +version = "0.1.0" +EOF +echo 'int main() { return 0; }' > plain/src/main.cpp +( cd plain + "$MCPP" build > b.log 2>&1 || { cat b.log; echo "FAIL: build errored"; exit 1; } + grep -q '\-O0' compile_commands.json || { echo "FAIL: global default is not dev (-O0)"; cat compile_commands.json; exit 1; } + grep -q '\-g\b' compile_commands.json || { echo "FAIL: global default dev lacks -g"; exit 1; } + # --release opts into the optimized profile. + "$MCPP" build --release > b2.log 2>&1 || { cat b2.log; echo "FAIL: --release errored"; exit 1; } + grep -q '\-O2' compile_commands.json || { echo "FAIL: --release did not yield -O2"; cat compile_commands.json; exit 1; } +) + +# --- (2) A project that opts into release via [build].default-profile. --- +mkdir -p opt/src +cat > opt/mcpp.toml <<'EOF' +[package] +name = "opt" +version = "0.1.0" +[build] +default-profile = "release" +EOF +echo 'int main() { return 0; }' > opt/src/main.cpp +( cd opt + "$MCPP" build > b.log 2>&1 || { cat b.log; echo "FAIL: build errored"; exit 1; } + grep -q '\-O2' compile_commands.json || { echo "FAIL: [build].default-profile=release did not yield -O2"; cat compile_commands.json; exit 1; } + # --dev overrides the project's release default back to -O0. + "$MCPP" build --dev > b2.log 2>&1 || { cat b2.log; echo "FAIL: --dev errored"; exit 1; } + grep -q '\-O0' compile_commands.json || { echo "FAIL: --dev did not override project default to -O0"; cat compile_commands.json; exit 1; } +) + +echo "OK" From 5088e3f25c6f319900e0c5ac153cb8c27f38eda7 Mon Sep 17 00:00:00 2001 From: sunrisepeak Date: Tue, 30 Jun 2026 00:55:44 +0800 Subject: [PATCH 2/5] ci+docs: revert cross-build cache key to v2 (warm); document dev default in user docs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - cross-build-test.yml: revert xlings cache key v3->v2. The v3 bump forced a COLD ~/.xlings, whose first-time toolchain install 127'd; v2 reuses the warm cache the passing e2e/ci-linux jobs already use (has 0.0.75 + the toolchain). The original 'version 0.0.75 not found' poison is moot now 0.0.75 is fully propagated. The self-host bootstrap is otherwise unrelated to this PR's code (e2e passed; step 6 bootstrap succeeded). - docs/05-mcpp-toml.md (+ zh): document the new convention — bare `mcpp build` uses dev (-O0 -g); release is opt-in via --release/--profile release; --dev shorthand; [build].default-profile per-project default (and the distribution note). --- .github/workflows/cross-build-test.yml | 4 ++-- docs/05-mcpp-toml.md | 12 ++++++++++-- docs/zh/05-mcpp-toml.md | 10 ++++++++-- 3 files changed, 20 insertions(+), 6 deletions(-) diff --git a/.github/workflows/cross-build-test.yml b/.github/workflows/cross-build-test.yml index 190b30db..ccc4c323 100644 --- a/.github/workflows/cross-build-test.yml +++ b/.github/workflows/cross-build-test.yml @@ -74,9 +74,9 @@ jobs: uses: actions/cache@v4 with: path: ~/.xlings - key: xlings-${{ runner.os }}-v3-${{ hashFiles('.xlings.json') }} + key: xlings-${{ runner.os }}-v2-${{ hashFiles('.xlings.json') }} restore-keys: | - xlings-${{ runner.os }}-v3- + xlings-${{ runner.os }}-v2- - name: Install qemu-user-static run: | diff --git a/docs/05-mcpp-toml.md b/docs/05-mcpp-toml.md index 396f740e..9ec02573 100644 --- a/docs/05-mcpp-toml.md +++ b/docs/05-mcpp-toml.md @@ -411,8 +411,16 @@ cxxflags = ["-fno-plt"] ldflags = [] ``` -- Selection: `mcpp build --profile ` (and `mcpp test --profile `, which builds the - code-under-test plus the test binaries under that profile), defaulting to `release`. +- Selection & default: a bare `mcpp build` uses the **`dev`** profile (`-O0 -g`) — the + mainstream convention (cf. Cargo/Meson/CMake/Zig/Bazel). **Release is opt-in:** + `mcpp build --release` (shorthand) or `--profile release`. `--dev` is the explicit + shorthand for dev. Same applies to `mcpp test --profile ` (builds the + code-under-test plus the test binaries under that profile). +- **Per-project default** — `[build].default-profile = ""` (alias: `profile`) sets + the project's own default when no flag is passed. The typical use is a tool/library + that should build optimized by default: `[build] default-profile = "release"`. Precedence: + `--profile`/`--release`/`--dev` flag **>** `[build].default-profile` **>** global `dev`. + (A project that defaults to dev should pass `--release` when producing a distributable.) - Built-in profiles: `release` (-O2) / `dev`, `debug` (-O0 -g) / `dist` (-O3 + strip; **LTO is not enabled by default**). `[profile.]` can override a built-in definition wholesale. diff --git a/docs/zh/05-mcpp-toml.md b/docs/zh/05-mcpp-toml.md index bf759e37..c1b03cc2 100644 --- a/docs/zh/05-mcpp-toml.md +++ b/docs/zh/05-mcpp-toml.md @@ -389,8 +389,14 @@ cxxflags = ["-fno-plt"] ldflags = [] ``` -- 选择:`mcpp build --profile `(以及 `mcpp test --profile `,会让被测代码与测试二进制 - 都在该 profile 下编译),默认 `release`。 +- 选择与默认:裸 `mcpp build` 走 **`dev`** 档(`-O0 -g`)——主流惯例(参照 + Cargo/Meson/CMake/Zig/Bazel)。**release 为 opt-in:** `mcpp build --release`(短写)或 + `--profile release`;`--dev` 是 dev 的显式短写。`mcpp test --profile ` 同理 + (被测代码与测试二进制都在该 profile 下编译)。 +- **项目级默认** —— `[build].default-profile = ""`(别名 `profile`)设置该项目在不带 + flag 时的默认。典型用途是"以发布优化为常态"的工具/库:`[build] default-profile = "release"`。 + 优先级:`--profile`/`--release`/`--dev` flag **>** `[build].default-profile` **>** 全局 `dev`。 + (默认 dev 的项目在产出可分发物时应显式 `--release`。) - 内置档案:`release`(-O2)/ `dev`、`debug`(-O0 -g)/ `dist`(-O3 + strip; **不默认开 lto**)。`[profile.<内置名>]` 可整体覆盖内置定义。 From 0312c3cfe2824f9c22c7da782f40cb01f26cd89b Mon Sep 17 00:00:00 2001 From: sunrisepeak Date: Tue, 30 Jun 2026 01:05:58 +0800 Subject: [PATCH 3/5] ci(cross-build): xlings 0.4.30 -> 0.4.61 + force index re-sync (fix bootstrap pin resolution) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The cross-build aarch64 job failed at the self-host bootstrap with 'xlings: version not found for mcpp' — the classic first-PR-after-a- bootstrap-pin-bump failure: a warm cached ~/.xlings carried a 'fresh-looking' TTL refresh marker, so 0.4.30's `xlings update` no-op'd and the stale index never saw the just-bumped pin (0.0.75). (Not this PR's code: e2e passed, the build+tests pass locally.) Fix: (1) bump XLINGS_VERSION to 0.4.61 (current; has the index-refresh fixes); (2) delete the .xlings-index-cache.json TTL markers before `xlings update` so it actually re-pulls the latest index while the toolchain payloads stay cached (avoids both the stale-index 'not found' and the cold-cache toolchain 127). --- .github/workflows/cross-build-test.yml | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/.github/workflows/cross-build-test.yml b/.github/workflows/cross-build-test.yml index ccc4c323..351c8dce 100644 --- a/.github/workflows/cross-build-test.yml +++ b/.github/workflows/cross-build-test.yml @@ -87,7 +87,11 @@ jobs: - name: Bootstrap mcpp via xlings env: XLINGS_NON_INTERACTIVE: '1' - XLINGS_VERSION: '0.4.30' + # 0.4.61 (current) carries the index-refresh fixes; 0.4.30 left a + # "fresh-looking" TTL marker on a cached ~/.xlings so `xlings update` + # no-op'd and the stale index never saw a just-bumped bootstrap pin + # (mcpp@ "not found" on the first PR after a pin bump). + XLINGS_VERSION: '0.4.61' run: | tarball="xlings-${XLINGS_VERSION}-linux-x86_64.tar.gz" curl -fsSL -o "/tmp/${tarball}" \ @@ -96,8 +100,10 @@ jobs: "/tmp/xlings-${XLINGS_VERSION}-linux-x86_64/subos/default/bin/xlings" self install export PATH="$HOME/.xlings/subos/default/bin:$PATH" xlings --version - # Refresh the index so a cached ~/.xlings still sees newly published - # cross toolchains (xim:aarch64-linux-musl-gcc, static ninja, ...). + # Force a real index re-sync even on a warm cache: drop the TTL refresh + # markers so `xlings update` actually pulls the latest index (sees the + # current bootstrap pin) while the toolchain payloads stay cached. + find "$HOME/.xlings" -name '.xlings-index-cache.json' -delete 2>/dev/null || true xlings config --mirror GLOBAL 2>/dev/null || true xlings update -y 2>/dev/null || xlings update 2>/dev/null || true xlings install mcpp -y From d2c7c28d686f1653d940df8cb519e6cd99e4d132 Mon Sep 17 00:00:00 2001 From: sunrisepeak Date: Tue, 30 Jun 2026 01:13:33 +0800 Subject: [PATCH 4/5] ci(cross-build): keep xlings 0.4.30, fix via index marker-clear only MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 0.4.61 resolved the pin (index re-sync worked) but 404'd downloading xim:mcpp@0.0.75 — it changed the XLINGS_RES download resolution and no longer matches the xlings-res/mcpp asset layout (0.4.30's resolution works; e2e@0.4.30 downloads 0.0.75 fine). The actual bug was the STALE INDEX (a fresh-looking TTL marker made 0.4.30's `xlings update` no-op). Revert to 0.4.30 and keep the .xlings-index-cache.json marker-clear, so update re-pulls the index (gets the bootstrap pin) while download uses 0.4.30's working path. --- .github/workflows/cross-build-test.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/cross-build-test.yml b/.github/workflows/cross-build-test.yml index 351c8dce..1d10d9ad 100644 --- a/.github/workflows/cross-build-test.yml +++ b/.github/workflows/cross-build-test.yml @@ -87,11 +87,11 @@ jobs: - name: Bootstrap mcpp via xlings env: XLINGS_NON_INTERACTIVE: '1' - # 0.4.61 (current) carries the index-refresh fixes; 0.4.30 left a - # "fresh-looking" TTL marker on a cached ~/.xlings so `xlings update` - # no-op'd and the stale index never saw a just-bumped bootstrap pin - # (mcpp@ "not found" on the first PR after a pin bump). - XLINGS_VERSION: '0.4.61' + # Stay on 0.4.30: its XLINGS_RES download resolution matches the + # xlings-res/mcpp release asset layout (newer 0.4.61 changed it and + # 404s on the mcpp artifact). The real bug was a stale INDEX, not the + # version — fixed by the marker-clear below. + XLINGS_VERSION: '0.4.30' run: | tarball="xlings-${XLINGS_VERSION}-linux-x86_64.tar.gz" curl -fsSL -o "/tmp/${tarball}" \ From 1d9745e809224b6e035d158a736d67c68f2f9b08 Mon Sep 17 00:00:00 2001 From: sunrisepeak Date: Tue, 30 Jun 2026 01:42:51 +0800 Subject: [PATCH 5/5] ci(cross-build): xlings 0.4.61 (root cause was broken xlings-res assets, now fixed) The real root cause of the cross-build bootstrap failures was NOT the xlings version or the profile code: the xlings-res/mcpp 0.0.75 GitHub release assets were uploaded in a BROKEN state during a flaky network (asset records present but blobs missing -> HTTP 404 on download). cross-build uses --mirror GLOBAL (GitHub), so its 'xlings install mcpp@0.0.75' 404'd; the downstream symptom on 0.4.30 was 'version 0.0.75 not found'. GitCode assets were fine all along. Re-uploaded all 4 GitHub xlings-res/mcpp 0.0.75 assets clean (verified GET 200 + correct sizes). With downloads working, use 0.4.61 (current) + keep the .xlings-index-cache.json marker-clear (forces index re-sync past a stale TTL marker on warm caches). --- .github/workflows/cross-build-test.yml | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/.github/workflows/cross-build-test.yml b/.github/workflows/cross-build-test.yml index 1d10d9ad..017f3ca8 100644 --- a/.github/workflows/cross-build-test.yml +++ b/.github/workflows/cross-build-test.yml @@ -87,11 +87,12 @@ jobs: - name: Bootstrap mcpp via xlings env: XLINGS_NON_INTERACTIVE: '1' - # Stay on 0.4.30: its XLINGS_RES download resolution matches the - # xlings-res/mcpp release asset layout (newer 0.4.61 changed it and - # 404s on the mcpp artifact). The real bug was a stale INDEX, not the - # version — fixed by the marker-clear below. - XLINGS_VERSION: '0.4.30' + # 0.4.61 (current). The earlier 0.4.61 "download 404 for mcpp@" was + # NOT a version bug — the xlings-res/mcpp GitHub release assets were + # uploaded in a broken state (records present, blobs missing → 404 on + # GET); re-uploaded clean. The stale-INDEX half is handled by the + # marker-clear below. + XLINGS_VERSION: '0.4.61' run: | tarball="xlings-${XLINGS_VERSION}-linux-x86_64.tar.gz" curl -fsSL -o "/tmp/${tarball}" \