@getmonoceros/workbench 1.37.2 → 1.37.7
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/dist/bin.js +152 -38
- package/dist/bin.js.map +1 -1
- package/package.json +1 -1
package/dist/bin.js
CHANGED
|
@@ -472,6 +472,13 @@ function interpolate(value, vars) {
|
|
|
472
472
|
});
|
|
473
473
|
return { value: out, missing };
|
|
474
474
|
}
|
|
475
|
+
function expandEnvRefs(env) {
|
|
476
|
+
const out = {};
|
|
477
|
+
for (const [key, value] of Object.entries(env)) {
|
|
478
|
+
out[key] = interpolate(value, env).value;
|
|
479
|
+
}
|
|
480
|
+
return out;
|
|
481
|
+
}
|
|
475
482
|
function interpolateServices(services, vars) {
|
|
476
483
|
const missing = [];
|
|
477
484
|
const resolved = services.map((svc) => {
|
|
@@ -560,6 +567,31 @@ async function ensureEnvVars(envPath, name, vars) {
|
|
|
560
567
|
}
|
|
561
568
|
return { created: !exists, added };
|
|
562
569
|
}
|
|
570
|
+
async function setEnvVarRef(envPath, name, key, value) {
|
|
571
|
+
const exists = existsSync2(envPath);
|
|
572
|
+
const content = exists ? readFileSync(envPath, "utf8") : buildEnvStub(name);
|
|
573
|
+
const lines = content.split(/\r?\n/);
|
|
574
|
+
let replaced = false;
|
|
575
|
+
for (let i = 0; i < lines.length; i++) {
|
|
576
|
+
const m = ENV_LINE_RE.exec(lines[i]);
|
|
577
|
+
if (m && m[1] === key) {
|
|
578
|
+
if (m[2].trim().length > 0) return false;
|
|
579
|
+
lines[i] = `${key}=${value}`;
|
|
580
|
+
replaced = true;
|
|
581
|
+
break;
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
let next = replaced ? lines.join("\n") : content;
|
|
585
|
+
if (!replaced) {
|
|
586
|
+
if (next.length > 0 && !next.endsWith("\n")) next += "\n";
|
|
587
|
+
next += `${key}=${value}
|
|
588
|
+
`;
|
|
589
|
+
}
|
|
590
|
+
if (next === content) return false;
|
|
591
|
+
await fsp.mkdir(path2.dirname(envPath), { recursive: true });
|
|
592
|
+
await fsp.writeFile(envPath, next);
|
|
593
|
+
return true;
|
|
594
|
+
}
|
|
563
595
|
function formatMissingVarsError(missing, envPathPretty) {
|
|
564
596
|
const lines = missing.map((m) => ` - \${${m.name}} (${m.location})`);
|
|
565
597
|
const uniqueNames = [...new Set(missing.map((m) => m.name))];
|
|
@@ -1334,6 +1366,13 @@ function resolveRepoTokens(config, catalog, envVars) {
|
|
|
1334
1366
|
const hostTokens = /* @__PURE__ */ new Map();
|
|
1335
1367
|
const used = [];
|
|
1336
1368
|
const missing = [];
|
|
1369
|
+
const ambiguous = [];
|
|
1370
|
+
const providersWithRepo = /* @__PURE__ */ new Set();
|
|
1371
|
+
const injectFeatureToken = (ref, token) => {
|
|
1372
|
+
if (!ref) return;
|
|
1373
|
+
const feature = features.find((f) => f.ref === ref);
|
|
1374
|
+
if (feature) feature.options = { ...feature.options, apiToken: token };
|
|
1375
|
+
};
|
|
1337
1376
|
for (const repo of config.repos ?? []) {
|
|
1338
1377
|
if (!repo.url.startsWith("https://")) continue;
|
|
1339
1378
|
let host;
|
|
@@ -1344,6 +1383,7 @@ function resolveRepoTokens(config, catalog, envVars) {
|
|
|
1344
1383
|
}
|
|
1345
1384
|
const provider = resolveProvider(host, repo.provider);
|
|
1346
1385
|
if (provider === "unknown") continue;
|
|
1386
|
+
providersWithRepo.add(provider);
|
|
1347
1387
|
const ref = catalog.get(provider)?.file.contributes.features?.[0]?.ref;
|
|
1348
1388
|
const tried = tokenVarCandidates(provider, repo.url);
|
|
1349
1389
|
const hit = tried.find((v) => (envVars[v] ?? "").trim().length > 0);
|
|
@@ -1353,17 +1393,47 @@ function resolveRepoTokens(config, catalog, envVars) {
|
|
|
1353
1393
|
}
|
|
1354
1394
|
const token = envVars[hit].trim();
|
|
1355
1395
|
hostTokens.set(host, token);
|
|
1356
|
-
used.push({ host, provider, varName: hit });
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1396
|
+
used.push({ host, provider, varName: hit, source: "repo" });
|
|
1397
|
+
injectFeatureToken(ref, token);
|
|
1398
|
+
}
|
|
1399
|
+
for (const provider of PROVIDER_VALUES) {
|
|
1400
|
+
if (providersWithRepo.has(provider)) continue;
|
|
1401
|
+
const ref = catalog.get(provider)?.file.contributes.features?.[0]?.ref;
|
|
1402
|
+
if (!ref || !config.features.some((f) => f.ref === ref)) continue;
|
|
1403
|
+
const p = provider.toUpperCase();
|
|
1404
|
+
const host = CANONICAL_HOST[provider];
|
|
1405
|
+
const direct = [`${p}_API_TOKEN`, `GIT_TOKEN__${p}`].find(
|
|
1406
|
+
(v) => (envVars[v] ?? "").trim().length > 0
|
|
1407
|
+
);
|
|
1408
|
+
if (direct) {
|
|
1409
|
+
const token = envVars[direct].trim();
|
|
1410
|
+
hostTokens.set(host, token);
|
|
1411
|
+
used.push({ host, provider, varName: direct, source: "feature" });
|
|
1412
|
+
injectFeatureToken(ref, token);
|
|
1413
|
+
continue;
|
|
1414
|
+
}
|
|
1415
|
+
const orgVars = Object.keys(envVars).filter((k) => k.startsWith(`GIT_TOKEN__${p}_`) && envVars[k].trim()).sort();
|
|
1416
|
+
if (orgVars.length === 1) {
|
|
1417
|
+
const token = envVars[orgVars[0]].trim();
|
|
1418
|
+
hostTokens.set(host, token);
|
|
1419
|
+
used.push({ host, provider, varName: orgVars[0], source: "feature" });
|
|
1420
|
+
injectFeatureToken(ref, token);
|
|
1421
|
+
} else if (orgVars.length > 1) {
|
|
1422
|
+
ambiguous.push({ provider, featureRef: ref, host, candidates: orgVars });
|
|
1423
|
+
} else {
|
|
1424
|
+
missing.push({
|
|
1425
|
+
host,
|
|
1426
|
+
provider,
|
|
1427
|
+
tried: [`${p}_API_TOKEN`, `GIT_TOKEN__${p}`]
|
|
1428
|
+
});
|
|
1360
1429
|
}
|
|
1361
1430
|
}
|
|
1362
1431
|
return {
|
|
1363
1432
|
features,
|
|
1364
1433
|
hostTokens,
|
|
1365
1434
|
used,
|
|
1366
|
-
missing
|
|
1435
|
+
missing,
|
|
1436
|
+
ambiguous
|
|
1367
1437
|
};
|
|
1368
1438
|
}
|
|
1369
1439
|
async function resolveContainerRepoTokens(name, home, catalog) {
|
|
@@ -1376,7 +1446,7 @@ async function resolveContainerRepoTokens(name, home, catalog) {
|
|
|
1376
1446
|
return resolveRepoTokens(config, catalog, envVars);
|
|
1377
1447
|
}
|
|
1378
1448
|
function formatTokenUse(use) {
|
|
1379
|
-
return
|
|
1449
|
+
return use.source === "feature" ? `Using ${use.varName} for the ${PROVIDER_LABEL[use.provider]} CLI` : `Using ${use.varName} for ${use.host}`;
|
|
1380
1450
|
}
|
|
1381
1451
|
function formatUnauthenticatedRepos(missing, containerName2) {
|
|
1382
1452
|
const lines = [
|
|
@@ -1407,6 +1477,7 @@ function formatUnauthenticatedRepos(missing, containerName2) {
|
|
|
1407
1477
|
lines.push("", ` Details: ${cyan2(REPO_DOCS_URL)}`);
|
|
1408
1478
|
return lines.join("\n");
|
|
1409
1479
|
}
|
|
1480
|
+
var CANONICAL_HOST;
|
|
1410
1481
|
var init_repo_token = __esm({
|
|
1411
1482
|
"src/apply/repo-token.ts"() {
|
|
1412
1483
|
"use strict";
|
|
@@ -1416,6 +1487,11 @@ var init_repo_token = __esm({
|
|
|
1416
1487
|
init_paths();
|
|
1417
1488
|
init_credentials();
|
|
1418
1489
|
init_format();
|
|
1490
|
+
CANONICAL_HOST = {
|
|
1491
|
+
github: "github.com",
|
|
1492
|
+
gitlab: "gitlab.com",
|
|
1493
|
+
bitbucket: "bitbucket.org"
|
|
1494
|
+
};
|
|
1419
1495
|
}
|
|
1420
1496
|
});
|
|
1421
1497
|
|
|
@@ -5439,7 +5515,6 @@ var init_yml = __esm({
|
|
|
5439
5515
|
// src/modify/index.ts
|
|
5440
5516
|
import { promises as fs11 } from "fs";
|
|
5441
5517
|
import { consola as consola2 } from "consola";
|
|
5442
|
-
import { createPatch } from "diff";
|
|
5443
5518
|
import path13 from "path";
|
|
5444
5519
|
function runAddLanguage(input) {
|
|
5445
5520
|
const spec = parseLanguageSpec(input.language);
|
|
@@ -5672,22 +5747,20 @@ async function tryCloneInRunningContainer(input, entry2) {
|
|
|
5672
5747
|
);
|
|
5673
5748
|
return;
|
|
5674
5749
|
}
|
|
5750
|
+
let noToken;
|
|
5675
5751
|
try {
|
|
5676
|
-
const { hostTokens } = await resolveContainerRepoTokens(
|
|
5752
|
+
const { hostTokens, missing } = await resolveContainerRepoTokens(
|
|
5677
5753
|
input.name,
|
|
5678
5754
|
home,
|
|
5679
5755
|
await loadComponentCatalog()
|
|
5680
5756
|
);
|
|
5681
|
-
|
|
5757
|
+
noToken = missing.find((m) => m.host === urlHost);
|
|
5758
|
+
if (!noToken) {
|
|
5682
5759
|
await collectGitCredentials(root, [{ host: urlHost, provider }], {
|
|
5683
5760
|
patByHost: hostTokens,
|
|
5684
5761
|
logger: { info: () => {
|
|
5685
5762
|
}, warn: (m) => logger.warn(m) }
|
|
5686
5763
|
});
|
|
5687
|
-
} else {
|
|
5688
|
-
logger.warn(
|
|
5689
|
-
`No access token set for ${urlHost} \u2014 cloning will fail if this repo is private. Set one (see ${REPO_DOCS_URL}) and re-run.`
|
|
5690
|
-
);
|
|
5691
5764
|
}
|
|
5692
5765
|
} catch (err) {
|
|
5693
5766
|
logger.warn(
|
|
@@ -5727,14 +5800,27 @@ async function tryCloneInRunningContainer(input, entry2) {
|
|
|
5727
5800
|
return;
|
|
5728
5801
|
}
|
|
5729
5802
|
if (exit.exitCode !== 0) {
|
|
5730
|
-
|
|
5731
|
-
|
|
5732
|
-
|
|
5803
|
+
if (noToken) {
|
|
5804
|
+
process.stderr.write(
|
|
5805
|
+
`
|
|
5806
|
+
${formatUnauthenticatedRepos([noToken], input.name)}
|
|
5807
|
+
`
|
|
5808
|
+
);
|
|
5809
|
+
} else {
|
|
5810
|
+
logger.warn(
|
|
5811
|
+
`In-container clone for ${entry2.url} exited ${exit.exitCode}. The yml is updated; \`monoceros apply ${input.name}\` retries.`
|
|
5812
|
+
);
|
|
5813
|
+
}
|
|
5733
5814
|
return;
|
|
5734
5815
|
}
|
|
5735
5816
|
logger.info(
|
|
5736
5817
|
`Cloned ${entry2.url} into /workspaces/${containerName2}/${targetRel} inside the running container.`
|
|
5737
5818
|
);
|
|
5819
|
+
if (noToken) {
|
|
5820
|
+
logger.warn(
|
|
5821
|
+
`No token set for ${urlHost}: the clone worked (public), but gh/glab aren't logged in and pushing needs a token. See ${REPO_DOCS_URL}.`
|
|
5822
|
+
);
|
|
5823
|
+
}
|
|
5738
5824
|
void path13;
|
|
5739
5825
|
}
|
|
5740
5826
|
function shquote(value) {
|
|
@@ -5970,16 +6056,6 @@ async function mutate(opts, apply) {
|
|
|
5970
6056
|
relocateLeakedSectionComments(parsed.doc);
|
|
5971
6057
|
const newText = stringifyConfig(parsed.doc);
|
|
5972
6058
|
parseConfig(newText, ymlPath);
|
|
5973
|
-
const out = opts.output ?? ((line) => process.stdout.write(line + "\n"));
|
|
5974
|
-
out(createPatch(ymlPath, oldText, newText, "before", "after"));
|
|
5975
|
-
if (!opts.yes) {
|
|
5976
|
-
const confirm = opts.confirm ?? defaultConfirm;
|
|
5977
|
-
const ok = await confirm("Apply these changes to the yml?");
|
|
5978
|
-
if (!ok) {
|
|
5979
|
-
logger.warn("Aborted by user. The yml was not modified.");
|
|
5980
|
-
return { status: "aborted" };
|
|
5981
|
-
}
|
|
5982
|
-
}
|
|
5983
6059
|
await fs11.writeFile(ymlPath, newText, "utf8");
|
|
5984
6060
|
logger.success(`Updated ${ymlPath}.`);
|
|
5985
6061
|
logger.info(
|
|
@@ -6049,7 +6125,6 @@ ${lines.join("\n")}`);
|
|
|
6049
6125
|
);
|
|
6050
6126
|
}
|
|
6051
6127
|
}
|
|
6052
|
-
var defaultConfirm;
|
|
6053
6128
|
var init_modify = __esm({
|
|
6054
6129
|
"src/modify/index.ts"() {
|
|
6055
6130
|
"use strict";
|
|
@@ -6074,13 +6149,6 @@ var init_modify = __esm({
|
|
|
6074
6149
|
init_transform();
|
|
6075
6150
|
init_briefing();
|
|
6076
6151
|
init_yml();
|
|
6077
|
-
defaultConfirm = async (message) => {
|
|
6078
|
-
const result = await consola2.prompt(message, {
|
|
6079
|
-
type: "confirm",
|
|
6080
|
-
initial: false
|
|
6081
|
-
});
|
|
6082
|
-
return result === true;
|
|
6083
|
-
};
|
|
6084
6152
|
}
|
|
6085
6153
|
});
|
|
6086
6154
|
|
|
@@ -8199,15 +8267,42 @@ ${sectionLine(label)}
|
|
|
8199
8267
|
warnOnDeprecatedFeatureRefs(parsed.config.features, globalConfig, logger);
|
|
8200
8268
|
const envPath = containerEnvPath(opts.name, home);
|
|
8201
8269
|
await ensureEnvGitignored(containerConfigsDir(home));
|
|
8202
|
-
const envVars = {
|
|
8270
|
+
const envVars = expandEnvRefs({
|
|
8203
8271
|
...readEnvFile(globalEnvPath(home)),
|
|
8204
8272
|
...readEnvFile(envPath),
|
|
8205
8273
|
...opts.env ?? {}
|
|
8206
|
-
};
|
|
8274
|
+
});
|
|
8207
8275
|
const catalog = await loadComponentCatalog();
|
|
8208
8276
|
const repoTokens = resolveRepoTokens(parsed.config, catalog, envVars);
|
|
8277
|
+
const featureTokenPrompt = opts.featureTokenPrompt ?? defaultFeatureTokenPrompt;
|
|
8278
|
+
for (const amb of repoTokens.ambiguous) {
|
|
8279
|
+
const chosen = await featureTokenPrompt(amb);
|
|
8280
|
+
if (!chosen) {
|
|
8281
|
+
const p = amb.provider.toUpperCase();
|
|
8282
|
+
repoTokens.missing.push({
|
|
8283
|
+
host: amb.host,
|
|
8284
|
+
provider: amb.provider,
|
|
8285
|
+
tried: [`${p}_API_TOKEN`, `GIT_TOKEN__${p}`]
|
|
8286
|
+
});
|
|
8287
|
+
continue;
|
|
8288
|
+
}
|
|
8289
|
+
const token = (envVars[chosen] ?? "").trim();
|
|
8290
|
+
repoTokens.hostTokens.set(amb.host, token);
|
|
8291
|
+
const feature = repoTokens.features.find((f) => f.ref === amb.featureRef);
|
|
8292
|
+
if (feature) feature.options = { ...feature.options, apiToken: token };
|
|
8293
|
+
const featureVar = `${amb.provider.toUpperCase()}_API_TOKEN`;
|
|
8294
|
+
const saved = await setEnvVarRef(
|
|
8295
|
+
envPath,
|
|
8296
|
+
opts.name,
|
|
8297
|
+
featureVar,
|
|
8298
|
+
`\${${chosen}}`
|
|
8299
|
+
);
|
|
8300
|
+
logger.info(
|
|
8301
|
+
`Using ${chosen} for the ${PROVIDER_LABEL[amb.provider]} CLI` + (saved ? ` (saved as ${featureVar} in ${prettyPath(envPath)})` : ` (${featureVar} already set in ${prettyPath(envPath)})`)
|
|
8302
|
+
);
|
|
8303
|
+
}
|
|
8209
8304
|
for (const use of repoTokens.used) {
|
|
8210
|
-
logger.info(
|
|
8305
|
+
logger.info(formatTokenUse(use));
|
|
8211
8306
|
}
|
|
8212
8307
|
const resolvedFeatures = interpolateFeatureOptions(
|
|
8213
8308
|
repoTokens.features,
|
|
@@ -8644,6 +8739,7 @@ async function persistPromptedIdentity(prompted, ymlPath, home, logger) {
|
|
|
8644
8739
|
}
|
|
8645
8740
|
}
|
|
8646
8741
|
}
|
|
8742
|
+
var SKIP_FEATURE_TOKEN, defaultFeatureTokenPrompt;
|
|
8647
8743
|
var init_apply = __esm({
|
|
8648
8744
|
"src/apply/index.ts"() {
|
|
8649
8745
|
"use strict";
|
|
@@ -8679,6 +8775,24 @@ var init_apply = __esm({
|
|
|
8679
8775
|
init_global();
|
|
8680
8776
|
init_yml();
|
|
8681
8777
|
init_repo_token();
|
|
8778
|
+
SKIP_FEATURE_TOKEN = "__skip__";
|
|
8779
|
+
defaultFeatureTokenPrompt = async ({
|
|
8780
|
+
provider,
|
|
8781
|
+
host,
|
|
8782
|
+
candidates
|
|
8783
|
+
}) => {
|
|
8784
|
+
const choice = await consola12.prompt(
|
|
8785
|
+
`The ${PROVIDER_LABEL[provider]} CLI feature has no repo to pick a token from. Which token should authenticate ${host}?`,
|
|
8786
|
+
{
|
|
8787
|
+
type: "select",
|
|
8788
|
+
options: [
|
|
8789
|
+
...candidates.map((v) => ({ label: v, value: v })),
|
|
8790
|
+
{ label: "Skip \u2014 leave unauthenticated", value: SKIP_FEATURE_TOKEN }
|
|
8791
|
+
]
|
|
8792
|
+
}
|
|
8793
|
+
);
|
|
8794
|
+
return typeof choice === "string" && choice !== SKIP_FEATURE_TOKEN ? choice : null;
|
|
8795
|
+
};
|
|
8682
8796
|
}
|
|
8683
8797
|
});
|
|
8684
8798
|
|
|
@@ -8853,7 +8967,7 @@ var CLI_VERSION;
|
|
|
8853
8967
|
var init_version = __esm({
|
|
8854
8968
|
"src/version.ts"() {
|
|
8855
8969
|
"use strict";
|
|
8856
|
-
CLI_VERSION = true ? "1.37.
|
|
8970
|
+
CLI_VERSION = true ? "1.37.7" : "dev";
|
|
8857
8971
|
}
|
|
8858
8972
|
});
|
|
8859
8973
|
|