Skip to content

Commit c3bf861

Browse files
committed
Add self config mirror command
1 parent 8300927 commit c3bf861

4 files changed

Lines changed: 111 additions & 57 deletions

File tree

src/cli.cppm

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ void print_usage() {
9090
std::println("About mcpp itself:");
9191
std::println(" mcpp self doctor Diagnose mcpp environment health");
9292
std::println(" mcpp self env Print mcpp paths and toolchain");
93+
std::println(" mcpp self config [--mirror CN|GLOBAL] Show or modify mcpp's xlings config");
9394
std::println(" mcpp self version Show mcpp version");
9495
std::println(" mcpp self explain <CODE> Show extended description for an error code");
9596
std::println(" mcpp --help / --version Help / version");
@@ -3610,6 +3611,43 @@ int cmd_self_version(const mcpplibs::cmdline::ParsedArgs& /*parsed*/) {
36103611
return 0;
36113612
}
36123613

3614+
std::string upper_ascii(std::string s) {
3615+
for (char& ch : s) {
3616+
if (ch >= 'a' && ch <= 'z') ch = static_cast<char>(ch - 'a' + 'A');
3617+
}
3618+
return s;
3619+
}
3620+
3621+
int cmd_self_config(const mcpplibs::cmdline::ParsedArgs& parsed) {
3622+
auto cfg = mcpp::config::load_or_init(/*quiet=*/false, make_bootstrap_progress_callback());
3623+
if (!cfg) {
3624+
mcpp::ui::error(cfg.error().message);
3625+
return 4;
3626+
}
3627+
3628+
auto env = mcpp::config::make_xlings_env(*cfg);
3629+
auto mirror = parsed.option_or_empty("mirror").value();
3630+
if (mirror.empty()) {
3631+
auto rc = mcpp::xlings::config_show(env);
3632+
return rc == 0 ? 0 : 1;
3633+
}
3634+
3635+
mirror = upper_ascii(std::move(mirror));
3636+
if (mirror != "CN" && mirror != "GLOBAL") {
3637+
mcpp::ui::error(std::format(
3638+
"invalid mirror '{}'; expected CN or GLOBAL", mirror));
3639+
return 2;
3640+
}
3641+
3642+
auto rc = mcpp::xlings::config_set_mirror(env, mirror, /*quiet=*/true);
3643+
if (rc != 0) {
3644+
mcpp::ui::error(std::format("failed to set xlings mirror to {}", mirror));
3645+
return 1;
3646+
}
3647+
mcpp::ui::status("Configured", std::format("xlings mirror = {}", mirror));
3648+
return 0;
3649+
}
3650+
36133651
// Used both by `mcpp explain <CODE>` (top-level) and `mcpp self explain
36143652
// <CODE>` (legacy alias).
36153653
int cmd_explain_action(const mcpplibs::cmdline::ParsedArgs& parsed) {
@@ -3925,6 +3963,10 @@ int run(int argc, char** argv) {
39253963
.description("Diagnose mcpp environment health"))
39263964
.subcommand(cl::App("env")
39273965
.description("Print mcpp paths and configuration"))
3966+
.subcommand(cl::App("config")
3967+
.description("Show or modify mcpp's private xlings configuration")
3968+
.option(cl::Option("mirror").takes_value().value_name("CN|GLOBAL")
3969+
.help("Set xlings mirror for mcpp's private registry")))
39283970
.subcommand(cl::App("version")
39293971
.description("Show mcpp version"))
39303972
.subcommand(cl::App("explain")
@@ -3934,6 +3976,7 @@ int run(int argc, char** argv) {
39343976
return dispatch_sub("self", p, {
39353977
{"doctor", cmd_doctor},
39363978
{"env", cmd_env},
3979+
{"config", cmd_self_config},
39373980
{"version", cmd_self_version},
39383981
{"explain", cmd_explain_action},
39393982
});

src/config.cppm

Lines changed: 3 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,6 @@ struct GlobalConfig {
4646
// From config.toml [xlings]
4747
std::string xlingsBinaryMode; // "bundled" | "system" | absolute path
4848
std::filesystem::path xlingsHomeOverride; // empty = use registryDir
49-
std::string xlingsMirror = "CN"; // "CN" | "GLOBAL" | empty = xlings default
5049

5150
// From config.toml [index]
5251
std::string defaultIndex; // "mcpplibs"
@@ -175,30 +174,6 @@ void write_file(const std::filesystem::path& p, std::string_view content) {
175174
os << content;
176175
}
177176

178-
std::string normalize_xlings_mirror(std::string mirror) {
179-
for (char& ch : mirror) {
180-
if (ch >= 'a' && ch <= 'z') ch = static_cast<char>(ch - 'a' + 'A');
181-
}
182-
return mirror;
183-
}
184-
185-
std::optional<std::string> read_xlings_json_mirror(const std::filesystem::path& path) {
186-
std::ifstream is(path);
187-
if (!is) return std::nullopt;
188-
std::stringstream ss;
189-
ss << is.rdbuf();
190-
auto content = ss.str();
191-
auto key = content.find("\"mirror\"");
192-
if (key == std::string::npos) return std::nullopt;
193-
auto colon = content.find(':', key);
194-
if (colon == std::string::npos) return std::nullopt;
195-
auto first = content.find('"', colon + 1);
196-
if (first == std::string::npos) return std::nullopt;
197-
auto second = content.find('"', first + 1);
198-
if (second == std::string::npos) return std::nullopt;
199-
return content.substr(first + 1, second - first - 1);
200-
}
201-
202177
bool write_default_config_toml(const std::filesystem::path& path) {
203178
constexpr auto tmpl = R"(# mcpp global config — auto-generated; safe to edit.
204179
@@ -207,8 +182,6 @@ bool write_default_config_toml(const std::filesystem::path& path) {
207182
binary = "bundled"
208183
# home: empty = use $MCPP_HOME/registry; can override
209184
home = ""
210-
# mirror: "CN" | "GLOBAL" | "" (defer to xlings)
211-
mirror = "CN"
212185
213186
[index]
214187
default = "mcpplibs"
@@ -229,8 +202,7 @@ default_backend = "ninja"
229202
}
230203

231204
bool write_default_xlings_json(const std::filesystem::path& path,
232-
const std::vector<IndexRepo>& repos,
233-
std::string_view mirror)
205+
const std::vector<IndexRepo>& repos)
234206
{
235207
// Delegate to xlings module. Convert IndexRepo vec to pair span.
236208
std::vector<std::pair<std::string,std::string>> pairs;
@@ -240,7 +212,7 @@ bool write_default_xlings_json(const std::filesystem::path& path,
240212
// construct a temporary Env with home = path.parent_path().
241213
mcpp::xlings::Env env;
242214
env.home = path.parent_path();
243-
mcpp::xlings::seed_xlings_json(env, pairs, mirror);
215+
mcpp::xlings::seed_xlings_json(env, pairs);
244216
return std::filesystem::exists(path);
245217
}
246218

@@ -384,15 +356,6 @@ std::expected<GlobalConfig, ConfigError> load_or_init(
384356
cfg.xlingsBinaryMode = doc->get_string("xlings.binary").value_or("bundled");
385357
if (auto h = doc->get_string("xlings.home"); h && !h->empty())
386358
cfg.xlingsHomeOverride = *h;
387-
cfg.xlingsMirror = normalize_xlings_mirror(
388-
doc->get_string("xlings.mirror").value_or("CN"));
389-
if (!cfg.xlingsMirror.empty() &&
390-
cfg.xlingsMirror != "CN" &&
391-
cfg.xlingsMirror != "GLOBAL") {
392-
return std::unexpected(ConfigError{
393-
std::format("invalid xlings.mirror '{}': expected CN, GLOBAL, or empty",
394-
cfg.xlingsMirror)});
395-
}
396359
cfg.defaultIndex = doc->get_string("index.default").value_or("mcpplibs");
397360
cfg.searchTtlSeconds = doc->get_int("cache.search_ttl_seconds").value_or(3600);
398361
cfg.defaultJobs = doc->get_int("build.default_jobs").value_or(0);
@@ -424,7 +387,7 @@ std::expected<GlobalConfig, ConfigError> load_or_init(
424387
// 5. Seed registry/.xlings.json if missing
425388
auto xjson = cfg.xlingsHome() / ".xlings.json";
426389
if (!std::filesystem::exists(xjson)) {
427-
write_default_xlings_json(xjson, cfg.indexRepos, cfg.xlingsMirror);
390+
write_default_xlings_json(xjson, cfg.indexRepos);
428391
}
429392

430393
// 6. Acquire xlings binary if needed
@@ -445,15 +408,6 @@ std::expected<GlobalConfig, ConfigError> load_or_init(
445408
"configured xlings binary not found: {}", cfg.xlingsBinary.string())});
446409
}
447410

448-
if (!cfg.xlingsMirror.empty() &&
449-
read_xlings_json_mirror(xjson).value_or("") != cfg.xlingsMirror) {
450-
auto rc = mcpp::xlings::set_mirror(make_xlings_env(cfg), cfg.xlingsMirror, true);
451-
if (rc != 0 && !quiet) {
452-
std::println(stderr,
453-
"warning: failed to set xlings mirror to '{}'", cfg.xlingsMirror);
454-
}
455-
}
456-
457411
// 7. Sandbox bootstrap (mcpp self-contained xlings environment).
458412
// Order matters:
459413
// a. Mirror xlings binary into sandbox so shim creation works.
@@ -475,8 +429,6 @@ void print_env(const GlobalConfig& cfg) {
475429
std::println("MCPP_HOME = {}", cfg.mcppHome.string());
476430
std::println("xlings binary = {}", cfg.xlingsBinary.string());
477431
std::println("xlings home = {}", cfg.xlingsHome().string());
478-
std::println("xlings mirror = {}",
479-
cfg.xlingsMirror.empty() ? "(xlings default)" : cfg.xlingsMirror);
480432
std::println("config = {}", cfg.configFile.string());
481433
std::println("BMI cache = {}", cfg.bmiCacheDir.string());
482434
std::println("meta cache = {}", cfg.metaCacheDir.string());

src/xlings.cppm

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -193,7 +193,8 @@ void seed_xlings_json(const Env& env,
193193
std::string_view mirror = "CN");
194194

195195
// Persist the xlings mirror selection in .xlings.json via xlings itself.
196-
int set_mirror(const Env& env, std::string_view mirror, bool quiet = false);
196+
int config_show(const Env& env);
197+
int config_set_mirror(const Env& env, std::string_view mirror, bool quiet = false);
197198

198199
// Run xlings self init.
199200
void ensure_init(const Env& env, bool quiet);
@@ -707,13 +708,16 @@ void seed_xlings_json(const Env& env,
707708
write_file(path, json);
708709
}
709710

710-
int set_mirror(const Env& env, std::string_view mirror, bool quiet) {
711+
int config_show(const Env& env) {
712+
auto cmd = std::format("{} config", build_command_prefix(env));
713+
return std::system(cmd.c_str());
714+
}
715+
716+
int config_set_mirror(const Env& env, std::string_view mirror, bool quiet) {
711717
if (mirror.empty()) return 0;
712718
auto cmd = std::format(
713-
"cd {} && env -u XLINGS_PROJECT_DIR XLINGS_HOME={} {} config --mirror {} {}",
714-
shq(env.home.string()),
715-
shq(env.home.string()),
716-
shq(env.binary.string()),
719+
"{} config --mirror {} {}",
720+
build_command_prefix(env),
717721
shq(mirror),
718722
quiet ? ">/dev/null 2>&1" : "");
719723
return std::system(cmd.c_str());

tests/e2e/38_self_config_mirror.sh

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
#!/usr/bin/env bash
2+
# 38_self_config_mirror.sh — configure xlings mirror through mcpp self config.
3+
set -e
4+
5+
TMP=$(mktemp -d)
6+
trap "rm -rf $TMP" EXIT
7+
export MCPP_HOME="$TMP/mcpp-home"
8+
source "$(dirname "$0")/_inherit_toolchain.sh"
9+
10+
"$MCPP" self env > "$TMP/env.log" 2>&1 || {
11+
cat "$TMP/env.log"
12+
echo "FAIL: self env failed"
13+
exit 1
14+
}
15+
16+
grep -q '"mirror": "CN"' "$MCPP_HOME/registry/.xlings.json" || {
17+
cat "$MCPP_HOME/registry/.xlings.json"
18+
echo "FAIL: default mirror should be CN"
19+
exit 1
20+
}
21+
22+
"$MCPP" self config --mirror GLOBAL > "$TMP/global.log" 2>&1 || {
23+
cat "$TMP/global.log"
24+
echo "FAIL: self config --mirror GLOBAL failed"
25+
exit 1
26+
}
27+
grep -q '"mirror": "GLOBAL"' "$MCPP_HOME/registry/.xlings.json" || {
28+
cat "$MCPP_HOME/registry/.xlings.json"
29+
echo "FAIL: mirror should be GLOBAL"
30+
exit 1
31+
}
32+
33+
"$MCPP" self config --mirror cn > "$TMP/cn.log" 2>&1 || {
34+
cat "$TMP/cn.log"
35+
echo "FAIL: self config --mirror cn failed"
36+
exit 1
37+
}
38+
grep -q '"mirror": "CN"' "$MCPP_HOME/registry/.xlings.json" || {
39+
cat "$MCPP_HOME/registry/.xlings.json"
40+
echo "FAIL: mirror should be CN"
41+
exit 1
42+
}
43+
44+
if "$MCPP" self config --mirror BAD > "$TMP/bad.log" 2>&1; then
45+
cat "$TMP/bad.log"
46+
echo "FAIL: invalid mirror unexpectedly succeeded"
47+
exit 1
48+
fi
49+
grep -q "invalid mirror" "$TMP/bad.log" || {
50+
cat "$TMP/bad.log"
51+
echo "FAIL: invalid mirror diagnostic missing"
52+
exit 1
53+
}
54+
55+
echo "OK"

0 commit comments

Comments
 (0)