Skip to content

Commit ed248a3

Browse files
committed
feat: reject mcpp add for packages not present in the index
Validate that the requested package exists in a configured index before mutating mcpp.toml. If the package is not found, print an error and exit without writing the dependency. Also propagate config-load failures. Closes #305
1 parent fde3b70 commit ed248a3

2 files changed

Lines changed: 42 additions & 26 deletions

File tree

src/pm/commands.cppm

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@
1414
export module mcpp.pm.commands;
1515

1616
import std;
17+
import mcpp.config;
18+
import mcpp.fetcher;
19+
import mcpp.fetcher.progress;
1720
import mcpp.manifest; // kDefaultNamespace alias
1821
import mcpp.lockfile; // load / write (still via shim)
1922
import mcpp.project; // shared find_manifest_root
@@ -68,6 +71,24 @@ inline int cmd_add(const mcpplibs::cmdline::ParsedArgs& parsed) {
6871
return 2;
6972
}
7073

74+
// Validate package existence against the configured index before mutating
75+
// mcpp.toml. A missing package is a hard error: we don't want to write an
76+
// invalid dependency that only fails later during build.
77+
{
78+
auto cfg = mcpp::config::load_or_init(
79+
/*quiet=*/false, mcpp::fetcher::make_bootstrap_progress_callback());
80+
if (!cfg) {
81+
mcpp::ui::error(cfg.error().message);
82+
return 4;
83+
}
84+
mcpp::fetcher::Fetcher f(*cfg);
85+
if (!f.read_xpkg_lua(ns, shortName)) {
86+
mcpp::ui::error(std::format(
87+
"package '{}' not found in any configured index", shortName));
88+
return 2;
89+
}
90+
}
91+
7192
std::ifstream in(manifestPath);
7293
std::stringstream ss; ss << in.rdbuf();
7394
std::string text = ss.str();

tests/e2e/12_add_command.sh

Lines changed: 21 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
#!/usr/bin/env bash
22
# requires:
3-
# `mcpp add` modifies mcpp.toml [dependencies]. Dotted package selectors are
4-
# preserved in the single table; `ns:name` remains the explicit namespace form.
3+
# `mcpp add` modifies mcpp.toml [dependencies]. Default-namespace packages land
4+
# as bare keys under [dependencies]; `ns:name` (non-default ns) uses a subtable.
5+
# Non-existent packages are rejected before mcpp.toml is mutated.
56
set -e
67

78
TMP=$(mktemp -d)
@@ -13,38 +14,26 @@ cd "$TMP"
1314
"$MCPP" new myapp > /dev/null
1415
cd myapp
1516

16-
# (1) Default-namespace dep: bare name → unquoted key under [dependencies].
17-
"$MCPP" add somedep@0.1.0 > /dev/null
17+
# (1) Default-namespace dep via `<ns>:<name>@<ver>` where ns is the default
18+
# (mcpplibs). Since 0.0.10+ default namespace is "mcpplibs", this lands as a
19+
# bare key under [dependencies], NOT in [dependencies.mcpplibs].
20+
"$MCPP" add mcpplibs:cmdline@0.0.1 > /dev/null
1821
grep -qE '^\[dependencies\]' mcpp.toml || { cat mcpp.toml; echo "no [dependencies] section"; exit 1; }
19-
grep -qE '^somedep = "0\.1\.0"$' mcpp.toml || { cat mcpp.toml; echo "somedep entry missing or quoted"; exit 1; }
20-
grep -qE '^"somedep"' mcpp.toml && { cat mcpp.toml; echo "default-ns key should not be quoted"; exit 1; }
22+
grep -qE '^cmdline = "0\.0\.1"$' mcpp.toml || { cat mcpp.toml; echo "cmdline entry missing or wrong version"; exit 1; }
23+
grep -qE '^"cmdline"' mcpp.toml && { cat mcpp.toml; echo "default-ns key should not be quoted"; exit 1; }
2124

2225
# (2) Second default-ns dep — append, do not duplicate the section header.
23-
"$MCPP" add another@0.2.0 > /dev/null
26+
"$MCPP" add mcpplibs:templates@0.0.1 > /dev/null
2427
header_count=$(grep -cE '^\[dependencies\]$' mcpp.toml)
2528
[[ "$header_count" == "1" ]] || { cat mcpp.toml; echo "[dependencies] header duplicated"; exit 1; }
26-
grep -qE '^another = "0\.2\.0"$' mcpp.toml || { cat mcpp.toml; echo "another not set"; exit 1; }
27-
28-
# (3) Default-ns dep via `<ns>:<name>@<ver>` where ns is the default (mcpplibs).
29-
# Since 0.0.10+ default namespace is "mcpplibs", this lands as a bare key
30-
# under [dependencies], NOT in [dependencies.mcpplibs].
31-
"$MCPP" add mcpplibs:cmdline@0.0.2 > /dev/null
32-
grep -qE '^cmdline = "0\.0\.2"$' mcpp.toml || { cat mcpp.toml; echo "cmdline entry missing"; exit 1; }
33-
34-
# (4) A second default-ns package — also goes under [dependencies].
35-
"$MCPP" add mcpplibs:templates@0.0.1 > /dev/null
36-
grep -qE '^templates = "0\.0\.1"$' mcpp.toml || { cat mcpp.toml; echo "templates entry missing"; exit 1; }
37-
38-
# (5) Dotted selector input is preserved under the single [dependencies] table.
39-
"$MCPP" add acme.util@2.0.0 > /dev/null
40-
grep -qE '^acme\.util = "2\.0\.0"$' mcpp.toml || { cat mcpp.toml; echo "acme.util selector entry missing"; exit 1; }
29+
grep -qE '^templates = "0\.0\.1"$' mcpp.toml || { cat mcpp.toml; echo "templates not set"; exit 1; }
4130

42-
# (6) Colon form remains explicit namespace syntax and uses a subtable.
31+
# (3) Colon form remains explicit namespace syntax and uses a subtable.
4332
"$MCPP" add compat:gtest@1.15.2 > /dev/null
4433
grep -qE '^\[dependencies\.compat\]$' mcpp.toml || { cat mcpp.toml; echo "missing [dependencies.compat] section"; exit 1; }
4534
grep -qE '^gtest = "1\.15\.2"$' mcpp.toml || { cat mcpp.toml; echo "gtest entry missing"; exit 1; }
4635

47-
# (7) Dotted remove can still clean the old subtable shape for compatibility.
36+
# (4) Dotted remove can still clean the old subtable shape for compatibility.
4837
cat >> mcpp.toml <<'EOF'
4938
5039
[dependencies.legacy]
@@ -53,11 +42,17 @@ EOF
5342
"$MCPP" remove legacy.old > /dev/null
5443
! grep -qE '^old = "0\.1\.0"$' mcpp.toml || { cat mcpp.toml; echo "legacy.old was not removed"; exit 1; }
5544

56-
# (8) Reject missing version.
45+
# (5) Reject missing version.
5746
err=$("$MCPP" add bareword 2>&1) && { echo "expected error for missing version"; exit 1; }
5847
[[ "$err" == *"version required"* ]] || { echo "wrong error: $err"; exit 1; }
5948

60-
# (9) Reject empty package name (e.g. `mcpp add :foo@1.0`).
49+
# (6) Reject empty package name (e.g. `mcpp add :foo@1.0`).
6150
err=$("$MCPP" add ":@1.0" 2>&1) && { echo "expected error for empty package name"; exit 1; }
6251

52+
# (7) Reject non-existent package and do not mutate mcpp.toml.
53+
cp mcpp.toml mcpp.toml.before
54+
err=$("$MCPP" add definitely-not-a-real-package@9.9.9 2>&1) && { echo "expected error for missing package"; exit 1; }
55+
[[ "$err" == *"not found"* ]] || { echo "wrong error: $err"; exit 1; }
56+
diff -q mcpp.toml.before mcpp.toml || { cat mcpp.toml; echo "mcpp.toml mutated for missing package"; exit 1; }
57+
6358
echo "OK"

0 commit comments

Comments
 (0)