@minnovate/shadow-publisher 0.11.9 → 0.11.10
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/publisher.cjs
CHANGED
|
@@ -27,9 +27,6 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
27
27
|
mod
|
|
28
28
|
));
|
|
29
29
|
|
|
30
|
-
// src/main.ts
|
|
31
|
-
var import_node_path9 = require("node:path");
|
|
32
|
-
|
|
33
30
|
// ../protocol/src/versions.ts
|
|
34
31
|
var PROTOCOL_VERSION = 1;
|
|
35
32
|
var PROTOCOL_HEADER = "x-publisher-protocol";
|
|
@@ -656,7 +653,7 @@ function $constructor(name, initializer2, proto, params) {
|
|
|
656
653
|
Internals.prototype = zodProto;
|
|
657
654
|
const protoMembers = proto;
|
|
658
655
|
const initialized = protoMembers && /* @__PURE__ */ new WeakSet();
|
|
659
|
-
function
|
|
656
|
+
function init2(inst, def) {
|
|
660
657
|
if (!inst._zod) {
|
|
661
658
|
_zodDesc.value = new Internals(def);
|
|
662
659
|
try {
|
|
@@ -696,7 +693,7 @@ function $constructor(name, initializer2, proto, params) {
|
|
|
696
693
|
Object.defineProperty(Definition, "name", { value: name });
|
|
697
694
|
function _(def) {
|
|
698
695
|
const inst = params?.Parent ? newError(Definition) : this;
|
|
699
|
-
|
|
696
|
+
init2(inst, def);
|
|
700
697
|
const deferred = inst._zod.deferred;
|
|
701
698
|
if (deferred) {
|
|
702
699
|
for (const fn of deferred) {
|
|
@@ -709,7 +706,7 @@ function $constructor(name, initializer2, proto, params) {
|
|
|
709
706
|
pp(inst);
|
|
710
707
|
return inst;
|
|
711
708
|
}
|
|
712
|
-
Object.defineProperty(_, "init", { value:
|
|
709
|
+
Object.defineProperty(_, "init", { value: init2 });
|
|
713
710
|
Object.defineProperty(_, Symbol.hasInstance, {
|
|
714
711
|
value: (inst) => {
|
|
715
712
|
if (params?.Parent && inst instanceof params.Parent)
|
|
@@ -3235,8 +3232,8 @@ function closeOrIgnored(child) {
|
|
|
3235
3232
|
if (lc.ignored) {
|
|
3236
3233
|
return Promise.resolve(false);
|
|
3237
3234
|
}
|
|
3238
|
-
return new Promise((
|
|
3239
|
-
lc.waiters.add(() =>
|
|
3235
|
+
return new Promise((resolve4) => {
|
|
3236
|
+
lc.waiters.add(() => resolve4(childClosed(child)));
|
|
3240
3237
|
});
|
|
3241
3238
|
}
|
|
3242
3239
|
async function endedOrIgnored(child) {
|
|
@@ -3281,7 +3278,7 @@ function waitWithin(start, ms, stop) {
|
|
|
3281
3278
|
return Promise.resolve({ status: "stopped" });
|
|
3282
3279
|
}
|
|
3283
3280
|
const ac = new AbortController();
|
|
3284
|
-
return new Promise((
|
|
3281
|
+
return new Promise((resolve4) => {
|
|
3285
3282
|
let timer = null;
|
|
3286
3283
|
let done = false;
|
|
3287
3284
|
const onStop = () => settle({ status: "stopped" });
|
|
@@ -3297,7 +3294,7 @@ function waitWithin(start, ms, stop) {
|
|
|
3297
3294
|
if (r.status !== "value" && r.status !== "error") {
|
|
3298
3295
|
ac.abort();
|
|
3299
3296
|
}
|
|
3300
|
-
|
|
3297
|
+
resolve4(r);
|
|
3301
3298
|
};
|
|
3302
3299
|
let p;
|
|
3303
3300
|
try {
|
|
@@ -3336,24 +3333,24 @@ function spawnTracked(args, opts) {
|
|
|
3336
3333
|
}
|
|
3337
3334
|
function feedStdin(child, input, onSourceError) {
|
|
3338
3335
|
let unpipe = () => void 0;
|
|
3339
|
-
const done = new Promise((
|
|
3340
|
-
child.stdin.on("error", () =>
|
|
3336
|
+
const done = new Promise((resolve4) => {
|
|
3337
|
+
child.stdin.on("error", () => resolve4());
|
|
3341
3338
|
if (input === void 0) {
|
|
3342
3339
|
child.stdin.end();
|
|
3343
|
-
|
|
3340
|
+
resolve4();
|
|
3344
3341
|
} else if (typeof input === "string" || Buffer.isBuffer(input)) {
|
|
3345
|
-
child.stdin.end(input, () =>
|
|
3342
|
+
child.stdin.end(input, () => resolve4());
|
|
3346
3343
|
} else {
|
|
3347
3344
|
input.on("error", () => {
|
|
3348
3345
|
onSourceError();
|
|
3349
|
-
|
|
3346
|
+
resolve4();
|
|
3350
3347
|
});
|
|
3351
3348
|
input.pipe(child.stdin);
|
|
3352
3349
|
unpipe = () => {
|
|
3353
3350
|
input.unpipe(child.stdin);
|
|
3354
|
-
|
|
3351
|
+
resolve4();
|
|
3355
3352
|
};
|
|
3356
|
-
child.stdin.on("finish", () =>
|
|
3353
|
+
child.stdin.on("finish", () => resolve4());
|
|
3357
3354
|
child.on("close", unpipe);
|
|
3358
3355
|
}
|
|
3359
3356
|
});
|
|
@@ -3466,11 +3463,11 @@ function startWatchdog(child, opts, kill, isClosed) {
|
|
|
3466
3463
|
const pgid = child.pid;
|
|
3467
3464
|
let stopped = false;
|
|
3468
3465
|
const stopper = new AbortController();
|
|
3469
|
-
const sleep2 = (ms) => new Promise((
|
|
3466
|
+
const sleep2 = (ms) => new Promise((resolve4) => {
|
|
3470
3467
|
const wake = () => {
|
|
3471
3468
|
clearTimeout(t);
|
|
3472
3469
|
stopper.signal.removeEventListener("abort", wake);
|
|
3473
|
-
|
|
3470
|
+
resolve4();
|
|
3474
3471
|
};
|
|
3475
3472
|
const t = setTimeout(wake, ms);
|
|
3476
3473
|
t.unref();
|
|
@@ -3548,8 +3545,8 @@ async function scanProcessGroup(pgid, signal) {
|
|
|
3548
3545
|
if (Number(rest[2]) !== pgid) {
|
|
3549
3546
|
continue;
|
|
3550
3547
|
}
|
|
3551
|
-
const
|
|
3552
|
-
members2.push({ pid: Number(name), rss: Number(/^VmRSS:\s+(\d+)/m.exec(
|
|
3548
|
+
const status2 = await (0, import_promises.readFile)(`/proc/${name}/status`, "utf8");
|
|
3549
|
+
members2.push({ pid: Number(name), rss: Number(/^VmRSS:\s+(\d+)/m.exec(status2)?.[1] ?? 0) * 1024 });
|
|
3553
3550
|
} catch (e) {
|
|
3554
3551
|
const code = e.code;
|
|
3555
3552
|
if (code === "ENOENT" || code === "ESRCH") {
|
|
@@ -3727,7 +3724,7 @@ async function reapChild(child, opts = {}) {
|
|
|
3727
3724
|
if (groupCleared || !childClosed(child) || Date.now() >= deadline) {
|
|
3728
3725
|
break;
|
|
3729
3726
|
}
|
|
3730
|
-
await new Promise((
|
|
3727
|
+
await new Promise((resolve4) => setTimeout(resolve4, Math.min(25, deadline - Date.now())));
|
|
3731
3728
|
}
|
|
3732
3729
|
if (groupCleared === false) {
|
|
3733
3730
|
timedOut = true;
|
|
@@ -3735,12 +3732,12 @@ async function reapChild(child, opts = {}) {
|
|
|
3735
3732
|
}
|
|
3736
3733
|
return { closed: childClosed(child), groupCleared, timedOut, survivors };
|
|
3737
3734
|
}
|
|
3738
|
-
function reapSucceeded(
|
|
3739
|
-
return
|
|
3735
|
+
function reapSucceeded(outcome2) {
|
|
3736
|
+
return outcome2.closed && outcome2.groupCleared === true;
|
|
3740
3737
|
}
|
|
3741
|
-
function cleanupFailure(what,
|
|
3742
|
-
const why = !
|
|
3743
|
-
const cleanup = { closed:
|
|
3738
|
+
function cleanupFailure(what, outcome2, cause) {
|
|
3739
|
+
const why = !outcome2.closed ? "the process did not close within the cleanup bound" : outcome2.groupCleared === null ? "its process group could not be observed" : `its process group still has ${outcome2.survivors.length} member(s)`;
|
|
3740
|
+
const cleanup = { closed: outcome2.closed, groupCleared: outcome2.groupCleared, timedOut: outcome2.timedOut, survivors: outcome2.survivors };
|
|
3744
3741
|
if (typeof cause === "string") {
|
|
3745
3742
|
cleanup["preempted"] = cause;
|
|
3746
3743
|
}
|
|
@@ -3782,12 +3779,12 @@ var import_promises2 = require("node:fs/promises");
|
|
|
3782
3779
|
async function sha256File(path) {
|
|
3783
3780
|
const h = (0, import_node_crypto2.createHash)("sha256");
|
|
3784
3781
|
let size = 0;
|
|
3785
|
-
await new Promise((
|
|
3782
|
+
await new Promise((resolve4, reject) => {
|
|
3786
3783
|
(0, import_node_fs2.createReadStream)(path).on("data", (c) => {
|
|
3787
3784
|
const b = typeof c === "string" ? Buffer.from(c) : c;
|
|
3788
3785
|
size += b.length;
|
|
3789
3786
|
h.update(b);
|
|
3790
|
-
}).on("error", reject).on("end", () =>
|
|
3787
|
+
}).on("error", reject).on("end", () => resolve4());
|
|
3791
3788
|
});
|
|
3792
3789
|
const st = await (0, import_promises2.stat)(path);
|
|
3793
3790
|
if (st.size !== size) {
|
|
@@ -3913,7 +3910,7 @@ async function packObjectsToFile(repoDir, extraArgs, stdinLines, opts) {
|
|
|
3913
3910
|
opts.signal?.addEventListener("abort", onAbort, { once: true });
|
|
3914
3911
|
let failure2 = null;
|
|
3915
3912
|
try {
|
|
3916
|
-
const finished = new Promise((
|
|
3913
|
+
const finished = new Promise((resolve4, reject) => out.on("finish", () => resolve4()).on("error", reject));
|
|
3917
3914
|
finished.catch(() => void 0);
|
|
3918
3915
|
child.stdout.pipe(out);
|
|
3919
3916
|
child.stdin.on("error", () => void 0);
|
|
@@ -3962,8 +3959,8 @@ async function packObjectsToFile(repoDir, extraArgs, stdinLines, opts) {
|
|
|
3962
3959
|
}
|
|
3963
3960
|
function delay(ms) {
|
|
3964
3961
|
let t;
|
|
3965
|
-
const p = new Promise((
|
|
3966
|
-
t = setTimeout(
|
|
3962
|
+
const p = new Promise((resolve4) => {
|
|
3963
|
+
t = setTimeout(resolve4, ms);
|
|
3967
3964
|
t.unref();
|
|
3968
3965
|
});
|
|
3969
3966
|
p.cancel = () => clearTimeout(t);
|
|
@@ -4357,19 +4354,19 @@ async function advanceWorkspaceBase(directory, next, now = /* @__PURE__ */ new D
|
|
|
4357
4354
|
await setConfig(m.directory, `${NS}.baseadvancedat`, updated.baseAdvancedAt);
|
|
4358
4355
|
return updated;
|
|
4359
4356
|
}
|
|
4360
|
-
async function noteAcceptedPublication(directory,
|
|
4361
|
-
if (
|
|
4357
|
+
async function noteAcceptedPublication(directory, outcome2, now = /* @__PURE__ */ new Date()) {
|
|
4358
|
+
if (outcome2.state !== "accepted" || outcome2.resultGeneration === null) {
|
|
4362
4359
|
return;
|
|
4363
4360
|
}
|
|
4364
4361
|
const m = await getWorkspaceMapping(directory).catch(() => null);
|
|
4365
|
-
if (!m || m.repoId !==
|
|
4362
|
+
if (!m || m.repoId !== outcome2.repoId) {
|
|
4366
4363
|
return;
|
|
4367
4364
|
}
|
|
4368
|
-
const oid =
|
|
4365
|
+
const oid = outcome2.refs?.[`refs/heads/${m.branch}`];
|
|
4369
4366
|
if (!oid) {
|
|
4370
4367
|
return;
|
|
4371
4368
|
}
|
|
4372
|
-
await advanceWorkspaceBase(directory, { oid, generation:
|
|
4369
|
+
await advanceWorkspaceBase(directory, { oid, generation: outcome2.resultGeneration }, now);
|
|
4373
4370
|
}
|
|
4374
4371
|
async function adoptWorkspace(client, directory, call2 = {}, now = /* @__PURE__ */ new Date()) {
|
|
4375
4372
|
const dir = (0, import_node_path3.resolve)(directory);
|
|
@@ -4926,13 +4923,13 @@ function credentialStatus(input) {
|
|
|
4926
4923
|
const credentials = resolution.kind === "ambiguous" ? "ambiguous" : resolution.kind === "resolved" ? env["GIT_PUBLISHER_TOKEN"] && !endpointStated(resolution.source) ? "unbound" : env["GIT_PUBLISHER_TOKEN"] || file.endpoints[resolution.endpoint] ? "ok" : "missing" : profiles.length === 0 ? "none" : "missing";
|
|
4927
4924
|
return { profiles, defaultProfile: file.defaultProfile ?? null, resolution, credentials };
|
|
4928
4925
|
}
|
|
4929
|
-
function nextStep(
|
|
4930
|
-
const profiles =
|
|
4931
|
-
const r =
|
|
4932
|
-
if (
|
|
4926
|
+
function nextStep(status2) {
|
|
4927
|
+
const profiles = status2.profiles.map((p) => p.name);
|
|
4928
|
+
const r = status2.resolution;
|
|
4929
|
+
if (status2.credentials === "ok") {
|
|
4933
4930
|
return { action: "ready", ask: null, command: null, profiles };
|
|
4934
4931
|
}
|
|
4935
|
-
if (
|
|
4932
|
+
if (status2.credentials === "unbound") {
|
|
4936
4933
|
return { action: "bind-endpoint", ask: null, command: null, profiles };
|
|
4937
4934
|
}
|
|
4938
4935
|
if (r.kind === "ambiguous" || r.kind === "unknown-profile") {
|
|
@@ -5079,33 +5076,33 @@ async function createApplication(input, opts) {
|
|
|
5079
5076
|
let refreshed = false;
|
|
5080
5077
|
let lastOutage = "";
|
|
5081
5078
|
for (let attempt = 0; attempt < ATTEMPTS; ) {
|
|
5082
|
-
const
|
|
5083
|
-
if (
|
|
5079
|
+
const answer2 = await post(fetchImpl, url2, await opts.token(), body);
|
|
5080
|
+
if (answer2.status === 401 && !refreshed && opts.refresh !== void 0) {
|
|
5084
5081
|
refreshed = true;
|
|
5085
5082
|
if (await opts.refresh()) {
|
|
5086
5083
|
continue;
|
|
5087
5084
|
}
|
|
5088
5085
|
}
|
|
5089
|
-
if (
|
|
5090
|
-
return read(
|
|
5086
|
+
if (answer2.status === 200 || answer2.status === 201) {
|
|
5087
|
+
return read(answer2.body, answer2.status === 201);
|
|
5091
5088
|
}
|
|
5092
|
-
const code = portalRefusalCode(
|
|
5093
|
-
if (
|
|
5089
|
+
const code = portalRefusalCode(answer2.body) ?? "";
|
|
5090
|
+
if (answer2.status === 401) {
|
|
5094
5091
|
throw new PublisherError("UNAUTHORIZED", 'the portal did not accept this credential; run "publisher login" again');
|
|
5095
5092
|
}
|
|
5096
|
-
if (
|
|
5093
|
+
if (answer2.status === 403) {
|
|
5097
5094
|
throw new PublisherError("FORBIDDEN", `the portal refused to make an application here (${code || "forbidden"})`);
|
|
5098
5095
|
}
|
|
5099
|
-
if (
|
|
5096
|
+
if (answer2.status === 409 && REFUSALS[code] !== void 0) {
|
|
5100
5097
|
throw new PublisherError(REFUSALS[code], `the portal refused the repository (${code})`, { details: { portal: code } });
|
|
5101
5098
|
}
|
|
5102
|
-
if (
|
|
5103
|
-
throw new PublisherError("INVALID_REQUEST", `the portal refused the request (${code ||
|
|
5099
|
+
if (answer2.status === 400 || answer2.status === 409) {
|
|
5100
|
+
throw new PublisherError("INVALID_REQUEST", `the portal refused the request (${code || answer2.status})`, { details: { portal: code } });
|
|
5104
5101
|
}
|
|
5105
|
-
lastOutage =
|
|
5102
|
+
lastOutage = answer2.status === 0 ? "the portal could not be reached" : `the portal answered ${answer2.status}`;
|
|
5106
5103
|
attempt += 1;
|
|
5107
5104
|
if (attempt < ATTEMPTS) {
|
|
5108
|
-
await new Promise((
|
|
5105
|
+
await new Promise((resolve4) => setTimeout(resolve4, (opts.retryDelayMs ?? 1e3) * attempt));
|
|
5109
5106
|
}
|
|
5110
5107
|
}
|
|
5111
5108
|
throw new PublisherError("UNAVAILABLE", `could not make the application: ${lastOutage}; run init again`, { retryable: true });
|
|
@@ -5241,21 +5238,21 @@ var Transport = class {
|
|
|
5241
5238
|
}
|
|
5242
5239
|
}
|
|
5243
5240
|
};
|
|
5244
|
-
function fallbackCode(
|
|
5245
|
-
if (
|
|
5241
|
+
function fallbackCode(status2) {
|
|
5242
|
+
if (status2 === 401) {
|
|
5246
5243
|
return "UNAUTHORIZED";
|
|
5247
5244
|
}
|
|
5248
|
-
if (
|
|
5245
|
+
if (status2 === 403) {
|
|
5249
5246
|
return "FORBIDDEN";
|
|
5250
5247
|
}
|
|
5251
|
-
if (
|
|
5248
|
+
if (status2 === 429) {
|
|
5252
5249
|
return "UNAVAILABLE";
|
|
5253
5250
|
}
|
|
5254
|
-
return
|
|
5251
|
+
return status2 >= 500 ? "UNAVAILABLE" : "INVALID_REQUEST";
|
|
5255
5252
|
}
|
|
5256
5253
|
function sleep(ms, signal) {
|
|
5257
|
-
return new Promise((
|
|
5258
|
-
const t = setTimeout(
|
|
5254
|
+
return new Promise((resolve4, reject) => {
|
|
5255
|
+
const t = setTimeout(resolve4, ms);
|
|
5259
5256
|
signal?.addEventListener(
|
|
5260
5257
|
"abort",
|
|
5261
5258
|
() => {
|
|
@@ -5331,24 +5328,24 @@ async function followRun(input, opts) {
|
|
|
5331
5328
|
let refreshed = false;
|
|
5332
5329
|
let answered = false;
|
|
5333
5330
|
for (; ; ) {
|
|
5334
|
-
const
|
|
5335
|
-
if (
|
|
5331
|
+
const answer2 = await ask(fetchImpl, url2, await opts.token());
|
|
5332
|
+
if (answer2.status === 401 && !refreshed && opts.refresh !== void 0) {
|
|
5336
5333
|
refreshed = true;
|
|
5337
5334
|
if (await opts.refresh()) {
|
|
5338
5335
|
continue;
|
|
5339
5336
|
}
|
|
5340
5337
|
}
|
|
5341
|
-
if (
|
|
5338
|
+
if (answer2.status === 401) {
|
|
5342
5339
|
return { kind: "unauthorized" };
|
|
5343
5340
|
}
|
|
5344
|
-
if (
|
|
5341
|
+
if (answer2.status === 404) {
|
|
5345
5342
|
return { kind: "unavailable", reason: "no-route" };
|
|
5346
5343
|
}
|
|
5347
|
-
if (
|
|
5344
|
+
if (answer2.status === 403) {
|
|
5348
5345
|
return { kind: "forbidden" };
|
|
5349
5346
|
}
|
|
5350
|
-
if (
|
|
5351
|
-
const run = soleRun(
|
|
5347
|
+
if (answer2.status === 200) {
|
|
5348
|
+
const run = soleRun(answer2.body);
|
|
5352
5349
|
if ("detail" in run) {
|
|
5353
5350
|
return { kind: "malformed", detail: run.detail };
|
|
5354
5351
|
}
|
|
@@ -5385,7 +5382,7 @@ async function followRun(input, opts) {
|
|
|
5385
5382
|
answered = true;
|
|
5386
5383
|
lastError = null;
|
|
5387
5384
|
} else {
|
|
5388
|
-
lastError =
|
|
5385
|
+
lastError = answer2.status === 0 ? "the portal could not be reached" : `the portal answered ${answer2.status}`;
|
|
5389
5386
|
}
|
|
5390
5387
|
if (now() + interval2 > deadline) {
|
|
5391
5388
|
return { kind: "timed-out", stages: last, lastError };
|
|
@@ -5471,44 +5468,44 @@ async function call(input, opts, body) {
|
|
|
5471
5468
|
const url2 = `${input.portal.replace(/\/+$/, "")}/api/v1/terminal/repositories/${encodeURIComponent(input.repoId)}/preparation`;
|
|
5472
5469
|
let refreshed = false;
|
|
5473
5470
|
for (; ; ) {
|
|
5474
|
-
const
|
|
5475
|
-
if (
|
|
5471
|
+
const answer2 = await ask2(fetchImpl, url2, await opts.token(), body);
|
|
5472
|
+
if (answer2.status === 401 && !refreshed && opts.refresh !== void 0) {
|
|
5476
5473
|
refreshed = true;
|
|
5477
5474
|
if (await opts.refresh()) {
|
|
5478
5475
|
continue;
|
|
5479
5476
|
}
|
|
5480
5477
|
}
|
|
5481
|
-
if (
|
|
5478
|
+
if (answer2.status === 401) {
|
|
5482
5479
|
return { kind: "unauthorized" };
|
|
5483
5480
|
}
|
|
5484
|
-
if (
|
|
5481
|
+
if (answer2.status === 403) {
|
|
5485
5482
|
return { kind: "forbidden" };
|
|
5486
5483
|
}
|
|
5487
|
-
if (
|
|
5484
|
+
if (answer2.status === 409) {
|
|
5488
5485
|
return { kind: "moved-on" };
|
|
5489
5486
|
}
|
|
5490
|
-
if (
|
|
5491
|
-
return portalRefusalCode(
|
|
5487
|
+
if (answer2.status === 404) {
|
|
5488
|
+
return portalRefusalCode(answer2.body) === "no-such-application" ? { kind: "no-application" } : { kind: "unavailable", detail: "the portal does not serve this address" };
|
|
5492
5489
|
}
|
|
5493
|
-
if (
|
|
5494
|
-
const view = readView(
|
|
5490
|
+
if (answer2.status === 200) {
|
|
5491
|
+
const view = readView(answer2.body);
|
|
5495
5492
|
return "detail" in view ? { kind: "malformed", detail: view.detail } : { kind: "view", view };
|
|
5496
5493
|
}
|
|
5497
|
-
return { kind: "unavailable", detail:
|
|
5494
|
+
return { kind: "unavailable", detail: answer2.status === 0 ? "the portal could not be reached" : `the portal answered ${answer2.status}` };
|
|
5498
5495
|
}
|
|
5499
5496
|
}
|
|
5500
5497
|
function readView(body) {
|
|
5501
5498
|
if (!isRecord4(body)) {
|
|
5502
5499
|
return { detail: "an answer that is not a preparation" };
|
|
5503
5500
|
}
|
|
5504
|
-
const { id, revision, status, message, questions, publication } = body;
|
|
5501
|
+
const { id, revision, status: status2, message, questions, publication } = body;
|
|
5505
5502
|
if (typeof id !== "string" || id === "") {
|
|
5506
5503
|
return { detail: "a preparation with no id" };
|
|
5507
5504
|
}
|
|
5508
5505
|
if (typeof revision !== "number" || !Number.isSafeInteger(revision) || revision < 1) {
|
|
5509
5506
|
return { detail: "a preparation with no revision" };
|
|
5510
5507
|
}
|
|
5511
|
-
if (!STATUSES.includes(
|
|
5508
|
+
if (!STATUSES.includes(status2)) {
|
|
5512
5509
|
return { detail: "a preparation with an unknown status" };
|
|
5513
5510
|
}
|
|
5514
5511
|
if (typeof message !== "string") {
|
|
@@ -5535,7 +5532,7 @@ function readView(body) {
|
|
|
5535
5532
|
if (isRecord4(publication) && typeof publication["status"] === "string" && typeof publication["message"] === "string") {
|
|
5536
5533
|
pub = { status: publication["status"], message: publication["message"], ...typeof publication["url"] === "string" ? { url: publication["url"] } : {} };
|
|
5537
5534
|
}
|
|
5538
|
-
return { id, revision, status, message, questions: read2, publication: pub, findings: readFindings(body["findings"]) };
|
|
5535
|
+
return { id, revision, status: status2, message, questions: read2, publication: pub, findings: readFindings(body["findings"]) };
|
|
5539
5536
|
}
|
|
5540
5537
|
function readFindings(value) {
|
|
5541
5538
|
if (!Array.isArray(value)) {
|
|
@@ -5895,14 +5892,14 @@ async function publish(ctx, repoId, remote, opts, call2) {
|
|
|
5895
5892
|
progress({ phase: "resolve", message: `resuming publication ${pending2.publicationId}` });
|
|
5896
5893
|
return runPublication(ctx, repoId, remote, repoDir, store2, pending2, opts, call2, true);
|
|
5897
5894
|
}
|
|
5898
|
-
const
|
|
5899
|
-
if (
|
|
5895
|
+
const outcome2 = await resolvePending(ctx, repoDir, remote, pending2, call2, store2);
|
|
5896
|
+
if (outcome2.state === "finalizing" || outcome2.state === "unknown") {
|
|
5900
5897
|
throw new PublisherError("OUTCOME_UNKNOWN", `publication ${pending2.publicationId} is still being processed; run status before publishing again`, {
|
|
5901
5898
|
retryable: true,
|
|
5902
5899
|
details: { publicationId: pending2.publicationId }
|
|
5903
5900
|
});
|
|
5904
5901
|
}
|
|
5905
|
-
if (
|
|
5902
|
+
if (outcome2.state === "accepted") {
|
|
5906
5903
|
const fresh = await ctx.getRepository(repoId, call2);
|
|
5907
5904
|
let remaining = [];
|
|
5908
5905
|
try {
|
|
@@ -5913,7 +5910,7 @@ async function publish(ctx, repoId, remote, opts, call2) {
|
|
|
5913
5910
|
}
|
|
5914
5911
|
}
|
|
5915
5912
|
if (remaining.length === 0) {
|
|
5916
|
-
return toOutcome(
|
|
5913
|
+
return toOutcome(outcome2, true, pending2.refUpdates);
|
|
5917
5914
|
}
|
|
5918
5915
|
return publish(ctx, repoId, remote, opts, call2);
|
|
5919
5916
|
}
|
|
@@ -5929,9 +5926,9 @@ async function publish(ctx, repoId, remote, opts, call2) {
|
|
|
5929
5926
|
if (needsPack) {
|
|
5930
5927
|
progress({ phase: "pack", message: "packing objects" });
|
|
5931
5928
|
const exclude = [];
|
|
5932
|
-
for (const
|
|
5933
|
-
if (
|
|
5934
|
-
exclude.push(
|
|
5929
|
+
for (const info2 of await batchCheck(repoDir, remoteOids)) {
|
|
5930
|
+
if (info2.type !== "missing") {
|
|
5931
|
+
exclude.push(info2.oid);
|
|
5935
5932
|
}
|
|
5936
5933
|
}
|
|
5937
5934
|
const outPath = store2.packPath(publicationId);
|
|
@@ -6101,12 +6098,12 @@ function toOutcome(s, resumed, updates) {
|
|
|
6101
6098
|
};
|
|
6102
6099
|
}
|
|
6103
6100
|
var REF_MOVED = /* @__PURE__ */ new Set(["NON_FAST_FORWARD", "EXPECTED_OLD_MISMATCH", "STALE_GENERATION"]);
|
|
6104
|
-
function publishNext(rejection,
|
|
6101
|
+
function publishNext(rejection, published2) {
|
|
6105
6102
|
if (rejection === null || !REF_MOVED.has(rejection.code)) {
|
|
6106
6103
|
return null;
|
|
6107
6104
|
}
|
|
6108
6105
|
const named = rejection.details?.["ref"];
|
|
6109
|
-
const ref = typeof named === "string" ? named :
|
|
6106
|
+
const ref = typeof named === "string" ? named : published2;
|
|
6110
6107
|
const name = ref?.replace(/^refs\/heads\//, "") ?? "main";
|
|
6111
6108
|
return {
|
|
6112
6109
|
action: "sync-and-publish",
|
|
@@ -6457,9 +6454,9 @@ var Publisher = class {
|
|
|
6457
6454
|
/** Publish existing local commits. Never modifies the worktree; safe to retry and resume. */
|
|
6458
6455
|
async publish(options, call2 = {}) {
|
|
6459
6456
|
const { repoId, remote } = await this.resolveRepo(options.repoDir, options.repoId);
|
|
6460
|
-
const
|
|
6461
|
-
await noteAcceptedPublication(options.repoDir,
|
|
6462
|
-
return
|
|
6457
|
+
const outcome2 = await publish(this.publishContext(), repoId, remote, options, call2);
|
|
6458
|
+
await noteAcceptedPublication(options.repoDir, outcome2);
|
|
6459
|
+
return outcome2;
|
|
6463
6460
|
}
|
|
6464
6461
|
// ---- workspaces (docs/archive/workspace-parity.md) ------------------------------------------------
|
|
6465
6462
|
/** Create a NEW editable workspace (clone + mapping) for a published repository. */
|
|
@@ -6517,8 +6514,8 @@ var Publisher = class {
|
|
|
6517
6514
|
if (tip === null || tip === (reconciled.before.remote?.oid ?? null)) {
|
|
6518
6515
|
return { ...base, state: "up_to_date", published: null, marked: false };
|
|
6519
6516
|
}
|
|
6520
|
-
const
|
|
6521
|
-
return { ...base, state:
|
|
6517
|
+
const published2 = await this.publish({ repoDir: dir }, call2);
|
|
6518
|
+
return { ...base, state: published2.state === "accepted" ? "published" : published2.state === "rejected" ? "rejected" : "unknown", published: published2, marked: false };
|
|
6522
6519
|
}
|
|
6523
6520
|
/** Resolve interrupted publications recorded locally without creating duplicates. */
|
|
6524
6521
|
async resumePublications(repoDir, call2 = {}) {
|
|
@@ -6768,10 +6765,84 @@ async function removePendingLogin() {
|
|
|
6768
6765
|
await (0, import_promises10.rm)(pendingLoginPath(), { force: true });
|
|
6769
6766
|
}
|
|
6770
6767
|
|
|
6771
|
-
// src/
|
|
6772
|
-
var
|
|
6773
|
-
|
|
6774
|
-
|
|
6768
|
+
// src/args.ts
|
|
6769
|
+
var import_node_util2 = require("node:util");
|
|
6770
|
+
var OPTIONS = {
|
|
6771
|
+
endpoint: { type: "string" },
|
|
6772
|
+
profile: { type: "string" },
|
|
6773
|
+
token: { type: "string" },
|
|
6774
|
+
portal: { type: "string" },
|
|
6775
|
+
name: { type: "string" },
|
|
6776
|
+
repo: { type: "string" },
|
|
6777
|
+
message: { type: "string" },
|
|
6778
|
+
publication: { type: "string" },
|
|
6779
|
+
timeout: { type: "string" },
|
|
6780
|
+
branch: { type: "string" },
|
|
6781
|
+
ours: { type: "string" },
|
|
6782
|
+
theirs: { type: "string" },
|
|
6783
|
+
to: { type: "string" },
|
|
6784
|
+
cursor: { type: "string" },
|
|
6785
|
+
ref: { type: "string", multiple: true },
|
|
6786
|
+
json: { type: "boolean" },
|
|
6787
|
+
quiet: { type: "boolean" },
|
|
6788
|
+
force: { type: "boolean" },
|
|
6789
|
+
apply: { type: "boolean" },
|
|
6790
|
+
"mark-conflicts": { type: "boolean" },
|
|
6791
|
+
help: { type: "boolean" },
|
|
6792
|
+
version: { type: "boolean" },
|
|
6793
|
+
"set-default": { type: "boolean" },
|
|
6794
|
+
archived: { type: "boolean" },
|
|
6795
|
+
// A flag of its own, not a negation of --wait: there is no --wait.
|
|
6796
|
+
"no-wait": { type: "boolean" },
|
|
6797
|
+
"until-change": { type: "boolean" },
|
|
6798
|
+
// `login --continue`: finish the sign-in a `login --no-wait` started.
|
|
6799
|
+
continue: { type: "boolean" }
|
|
6800
|
+
};
|
|
6801
|
+
function str2(v) {
|
|
6802
|
+
return typeof v === "string" ? v : void 0;
|
|
6803
|
+
}
|
|
6804
|
+
function parseArgs(argv) {
|
|
6805
|
+
try {
|
|
6806
|
+
const { positionals, values } = (0, import_node_util2.parseArgs)({ args: argv, options: OPTIONS, allowPositionals: true, strict: true });
|
|
6807
|
+
return { cmd: positionals, flags: { ...values } };
|
|
6808
|
+
} catch (e) {
|
|
6809
|
+
throw new PublisherError("INVALID_REQUEST", e instanceof Error ? e.message : String(e));
|
|
6810
|
+
}
|
|
6811
|
+
}
|
|
6812
|
+
|
|
6813
|
+
// src/discover.ts
|
|
6814
|
+
var DEFAULT_PORTAL_URL = "https://shadow.minnovate.ai";
|
|
6815
|
+
function portalBase(env, flag) {
|
|
6816
|
+
return (flag ?? env["GIT_PUBLISHER_PORTAL"] ?? DEFAULT_PORTAL_URL).replace(/\/+$/, "");
|
|
6817
|
+
}
|
|
6818
|
+
var DISCOVER_TIMEOUT_MS = 1e4;
|
|
6819
|
+
async function discoverEndpoint(portal, fetchImpl = fetch) {
|
|
6820
|
+
const url2 = `${portal}/api/v1/config`;
|
|
6821
|
+
const bound = new AbortController();
|
|
6822
|
+
const timer = setTimeout(() => bound.abort(new DOMException("the request timed out", "TimeoutError")), DISCOVER_TIMEOUT_MS);
|
|
6823
|
+
let body;
|
|
6824
|
+
try {
|
|
6825
|
+
const response = await fetchImpl(url2, { signal: bound.signal });
|
|
6826
|
+
if (!response.ok) {
|
|
6827
|
+
throw new PublisherError("UNAVAILABLE", `could not ask ${portal} where to publish (it answered ${response.status})`, { retryable: true });
|
|
6828
|
+
}
|
|
6829
|
+
body = await response.json();
|
|
6830
|
+
} catch (e) {
|
|
6831
|
+
if (PublisherError.isPublisherError(e)) {
|
|
6832
|
+
throw e;
|
|
6833
|
+
}
|
|
6834
|
+
throw new PublisherError("NETWORK", `could not reach ${portal}: ${e instanceof Error ? e.message : String(e)}`, { retryable: true });
|
|
6835
|
+
} finally {
|
|
6836
|
+
clearTimeout(timer);
|
|
6837
|
+
}
|
|
6838
|
+
const endpoint = body?.publishingEndpoint;
|
|
6839
|
+
if (endpoint === null || endpoint === void 0) {
|
|
6840
|
+
throw new PublisherError("LOCAL_STATE", `${portal} is not set up for publishing; ask whoever runs it, or pass --endpoint`);
|
|
6841
|
+
}
|
|
6842
|
+
if (typeof endpoint !== "string" || endpoint.trim() === "") {
|
|
6843
|
+
throw new PublisherError("UNAVAILABLE", `${portal} answered with something this version does not understand`, { retryable: false });
|
|
6844
|
+
}
|
|
6845
|
+
return endpoint;
|
|
6775
6846
|
}
|
|
6776
6847
|
|
|
6777
6848
|
// src/progress.ts
|
|
@@ -6891,38 +6962,38 @@ async function followPublication(p, endpoint, o, opts) {
|
|
|
6891
6962
|
}
|
|
6892
6963
|
const [ref, commit] = refs[0];
|
|
6893
6964
|
opts.log("Following it to live: received -> checked -> going live -> live. Ctrl-C stops watching; the publication is saved and carries on without you.");
|
|
6894
|
-
const
|
|
6965
|
+
const outcome2 = await p.followRun(
|
|
6895
6966
|
{ portal, repoId: o.repoId, publicationId: o.publicationId, ref, commit },
|
|
6896
6967
|
{ onStage: opts.onStage, ...opts.timeoutMs === void 0 ? {} : { timeoutMs: opts.timeoutMs }, ...opts.untilChange === true ? { untilChange: true } : {} }
|
|
6897
6968
|
).finally(() => opts.stopStages?.());
|
|
6898
6969
|
const answerable = confirmed.terminal.includes(PREPARATION);
|
|
6899
6970
|
const dir = shellWord(opts.dir);
|
|
6900
|
-
const next =
|
|
6901
|
-
const findings = await failedChecks(p, portal, o.repoId,
|
|
6971
|
+
const next = outcome2.kind === "timed-out" || outcome2.kind === "moving" ? { action: "follow", command: `publisher follow ${dir} --publication ${o.publicationId}${opts.untilChange === true ? " --until-change" : ""} --json` } : outcome2.kind === "waiting" && answerable ? { action: "question", command: `publisher question ${dir} --json` } : void 0;
|
|
6972
|
+
const findings = await failedChecks(p, portal, o.repoId, outcome2, answerable);
|
|
6902
6973
|
if (findings !== null) {
|
|
6903
6974
|
return {
|
|
6904
|
-
...
|
|
6975
|
+
...outcome2,
|
|
6905
6976
|
portal,
|
|
6906
6977
|
hint: `It did not pass its checks (${findings.length === 1 ? "one finding" : `${findings.length} findings`}). Fix each finding in the app folder, then run next.command to publish again.`,
|
|
6907
6978
|
findings,
|
|
6908
6979
|
next: { action: "fix", command: `publisher sync ${dir} --until-change --json` }
|
|
6909
6980
|
};
|
|
6910
6981
|
}
|
|
6911
|
-
return { ...
|
|
6982
|
+
return { ...outcome2, portal, hint: progressHint(outcome2, portal, answerable), ...next === void 0 ? {} : { next } };
|
|
6912
6983
|
}
|
|
6913
|
-
async function failedChecks(p, portal, repoId,
|
|
6914
|
-
if (!served ||
|
|
6984
|
+
async function failedChecks(p, portal, repoId, outcome2, served) {
|
|
6985
|
+
if (!served || outcome2.kind !== "settled" || outcome2.ending !== "failed" || outcome2.runId === null) {
|
|
6915
6986
|
return null;
|
|
6916
6987
|
}
|
|
6917
|
-
if (
|
|
6988
|
+
if (outcome2.stages.find((s) => s.state === "failed")?.stage !== "running") {
|
|
6918
6989
|
return null;
|
|
6919
6990
|
}
|
|
6920
6991
|
try {
|
|
6921
|
-
const
|
|
6922
|
-
if (
|
|
6992
|
+
const answer2 = await p.readPreparation({ portal, repoId });
|
|
6993
|
+
if (answer2.kind !== "view" || answer2.view.id !== outcome2.runId || answer2.view.status !== "blocked") {
|
|
6923
6994
|
return null;
|
|
6924
6995
|
}
|
|
6925
|
-
return
|
|
6996
|
+
return answer2.view.findings.length === 0 ? null : answer2.view.findings;
|
|
6926
6997
|
} catch {
|
|
6927
6998
|
return null;
|
|
6928
6999
|
}
|
|
@@ -6990,122 +7061,6 @@ function progressHint(r, portal, answerable = false) {
|
|
|
6990
7061
|
}
|
|
6991
7062
|
}
|
|
6992
7063
|
|
|
6993
|
-
// src/list.ts
|
|
6994
|
-
async function listApps(p, opts = {}) {
|
|
6995
|
-
const page = await p.listRepositories({ state: opts.archived ? "archived" : "active", limit: 200, ...opts.cursor ? { cursor: opts.cursor } : {} });
|
|
6996
|
-
const apps = page.repositories.map((r) => ({
|
|
6997
|
-
repoId: r.repoId,
|
|
6998
|
-
name: r.name,
|
|
6999
|
-
createdAt: r.createdAt,
|
|
7000
|
-
publishedAt: r.lastTransaction?.committedAt ?? null,
|
|
7001
|
-
archived: r.archived,
|
|
7002
|
-
checkout: `publisher checkout ${shellWord(folderFor(r.name))} --repo ${r.repoId} --json`
|
|
7003
|
-
})).sort((a, b) => (b.publishedAt ?? b.createdAt).localeCompare(a.publishedAt ?? a.createdAt));
|
|
7004
|
-
const hint = apps.length === 0 ? "You have not published an app from this sign-in yet." : "To work on one in a new folder, run its checkout command.";
|
|
7005
|
-
return { apps, nextCursor: page.nextCursor, hint };
|
|
7006
|
-
}
|
|
7007
|
-
function folderFor(name) {
|
|
7008
|
-
const slug2 = name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
7009
|
-
return slug2 === "" ? "app" : slug2;
|
|
7010
|
-
}
|
|
7011
|
-
|
|
7012
|
-
// src/init.ts
|
|
7013
|
-
async function repositoryForInit(p, endpoint, name, opts = {}) {
|
|
7014
|
-
const portal = portalForEndpoint(endpoint);
|
|
7015
|
-
if (portal === null) {
|
|
7016
|
-
return { repoId: (await p.createRepository({ name, grants: [] })).repoId, appId: null };
|
|
7017
|
-
}
|
|
7018
|
-
const confirmed = await confirmPortal(portal, endpoint, opts.fetch);
|
|
7019
|
-
if (confirmed === null) {
|
|
7020
|
-
throw new PublisherError("UNAVAILABLE", `could not confirm that ${portal} publishes to ${endpoint}; run init again`, { retryable: true });
|
|
7021
|
-
}
|
|
7022
|
-
if (!confirmed.terminal.includes(APPLICATION_CREATION)) {
|
|
7023
|
-
throw new PublisherError("UNAVAILABLE", `${portal} cannot make an application from a terminal yet, and a repository needs one; ask whoever runs it to update the portal`);
|
|
7024
|
-
}
|
|
7025
|
-
const made = await p.createApplication({ portal, name });
|
|
7026
|
-
return { repoId: made.repository.repoId, appId: made.appId };
|
|
7027
|
-
}
|
|
7028
|
-
|
|
7029
|
-
// src/args.ts
|
|
7030
|
-
var import_node_util2 = require("node:util");
|
|
7031
|
-
var OPTIONS = {
|
|
7032
|
-
endpoint: { type: "string" },
|
|
7033
|
-
profile: { type: "string" },
|
|
7034
|
-
token: { type: "string" },
|
|
7035
|
-
portal: { type: "string" },
|
|
7036
|
-
name: { type: "string" },
|
|
7037
|
-
repo: { type: "string" },
|
|
7038
|
-
message: { type: "string" },
|
|
7039
|
-
publication: { type: "string" },
|
|
7040
|
-
timeout: { type: "string" },
|
|
7041
|
-
branch: { type: "string" },
|
|
7042
|
-
ours: { type: "string" },
|
|
7043
|
-
theirs: { type: "string" },
|
|
7044
|
-
to: { type: "string" },
|
|
7045
|
-
cursor: { type: "string" },
|
|
7046
|
-
ref: { type: "string", multiple: true },
|
|
7047
|
-
json: { type: "boolean" },
|
|
7048
|
-
quiet: { type: "boolean" },
|
|
7049
|
-
force: { type: "boolean" },
|
|
7050
|
-
apply: { type: "boolean" },
|
|
7051
|
-
"mark-conflicts": { type: "boolean" },
|
|
7052
|
-
help: { type: "boolean" },
|
|
7053
|
-
version: { type: "boolean" },
|
|
7054
|
-
"set-default": { type: "boolean" },
|
|
7055
|
-
archived: { type: "boolean" },
|
|
7056
|
-
// A flag of its own, not a negation of --wait: there is no --wait.
|
|
7057
|
-
"no-wait": { type: "boolean" },
|
|
7058
|
-
"until-change": { type: "boolean" },
|
|
7059
|
-
// `login --continue`: finish the sign-in a `login --no-wait` started.
|
|
7060
|
-
continue: { type: "boolean" }
|
|
7061
|
-
};
|
|
7062
|
-
function str2(v) {
|
|
7063
|
-
return typeof v === "string" ? v : void 0;
|
|
7064
|
-
}
|
|
7065
|
-
function parseArgs(argv) {
|
|
7066
|
-
try {
|
|
7067
|
-
const { positionals, values } = (0, import_node_util2.parseArgs)({ args: argv, options: OPTIONS, allowPositionals: true, strict: true });
|
|
7068
|
-
return { cmd: positionals, flags: { ...values } };
|
|
7069
|
-
} catch (e) {
|
|
7070
|
-
throw new PublisherError("INVALID_REQUEST", e instanceof Error ? e.message : String(e));
|
|
7071
|
-
}
|
|
7072
|
-
}
|
|
7073
|
-
|
|
7074
|
-
// src/discover.ts
|
|
7075
|
-
var DEFAULT_PORTAL_URL = "https://shadow.minnovate.ai";
|
|
7076
|
-
function portalBase(env, flag) {
|
|
7077
|
-
return (flag ?? env["GIT_PUBLISHER_PORTAL"] ?? DEFAULT_PORTAL_URL).replace(/\/+$/, "");
|
|
7078
|
-
}
|
|
7079
|
-
var DISCOVER_TIMEOUT_MS = 1e4;
|
|
7080
|
-
async function discoverEndpoint(portal, fetchImpl = fetch) {
|
|
7081
|
-
const url2 = `${portal}/api/v1/config`;
|
|
7082
|
-
const bound = new AbortController();
|
|
7083
|
-
const timer = setTimeout(() => bound.abort(new DOMException("the request timed out", "TimeoutError")), DISCOVER_TIMEOUT_MS);
|
|
7084
|
-
let body;
|
|
7085
|
-
try {
|
|
7086
|
-
const response = await fetchImpl(url2, { signal: bound.signal });
|
|
7087
|
-
if (!response.ok) {
|
|
7088
|
-
throw new PublisherError("UNAVAILABLE", `could not ask ${portal} where to publish (it answered ${response.status})`, { retryable: true });
|
|
7089
|
-
}
|
|
7090
|
-
body = await response.json();
|
|
7091
|
-
} catch (e) {
|
|
7092
|
-
if (PublisherError.isPublisherError(e)) {
|
|
7093
|
-
throw e;
|
|
7094
|
-
}
|
|
7095
|
-
throw new PublisherError("NETWORK", `could not reach ${portal}: ${e instanceof Error ? e.message : String(e)}`, { retryable: true });
|
|
7096
|
-
} finally {
|
|
7097
|
-
clearTimeout(timer);
|
|
7098
|
-
}
|
|
7099
|
-
const endpoint = body?.publishingEndpoint;
|
|
7100
|
-
if (endpoint === null || endpoint === void 0) {
|
|
7101
|
-
throw new PublisherError("LOCAL_STATE", `${portal} is not set up for publishing; ask whoever runs it, or pass --endpoint`);
|
|
7102
|
-
}
|
|
7103
|
-
if (typeof endpoint !== "string" || endpoint.trim() === "") {
|
|
7104
|
-
throw new PublisherError("UNAVAILABLE", `${portal} answered with something this version does not understand`, { retryable: false });
|
|
7105
|
-
}
|
|
7106
|
-
return endpoint;
|
|
7107
|
-
}
|
|
7108
|
-
|
|
7109
7064
|
// src/prompt.ts
|
|
7110
7065
|
var import_promises11 = require("node:readline/promises");
|
|
7111
7066
|
function isInteractive(env) {
|
|
@@ -7114,8 +7069,8 @@ function isInteractive(env) {
|
|
|
7114
7069
|
function suggestProfileName(endpoint, taken) {
|
|
7115
7070
|
return hasReadableName(endpoint) ? profileNameFor(endpoint, taken) : null;
|
|
7116
7071
|
}
|
|
7117
|
-
function resolveChoice(
|
|
7118
|
-
const a =
|
|
7072
|
+
function resolveChoice(answer2, names) {
|
|
7073
|
+
const a = answer2.trim();
|
|
7119
7074
|
if (!a) {
|
|
7120
7075
|
return null;
|
|
7121
7076
|
}
|
|
@@ -7125,10 +7080,10 @@ function resolveChoice(answer, names) {
|
|
|
7125
7080
|
}
|
|
7126
7081
|
return names.find((n) => n.toLowerCase() === a.toLowerCase()) ?? null;
|
|
7127
7082
|
}
|
|
7128
|
-
async function ask3(
|
|
7083
|
+
async function ask3(question2) {
|
|
7129
7084
|
const rl = (0, import_promises11.createInterface)({ input: process.stdin, output: process.stderr });
|
|
7130
7085
|
try {
|
|
7131
|
-
return await rl.question(
|
|
7086
|
+
return await rl.question(question2);
|
|
7132
7087
|
} catch {
|
|
7133
7088
|
return null;
|
|
7134
7089
|
} finally {
|
|
@@ -7143,12 +7098,12 @@ ${purpose}
|
|
|
7143
7098
|
names.forEach((n, i) => process.stderr.write(` ${i + 1}) ${n}
|
|
7144
7099
|
`));
|
|
7145
7100
|
for (let attempt = 0; attempt < 3; attempt++) {
|
|
7146
|
-
const
|
|
7101
|
+
const answer2 = await ask3(`
|
|
7147
7102
|
Which one? [1-${names.length}, or a name] `);
|
|
7148
|
-
if (
|
|
7103
|
+
if (answer2 === null) {
|
|
7149
7104
|
return null;
|
|
7150
7105
|
}
|
|
7151
|
-
const chosen = resolveChoice(
|
|
7106
|
+
const chosen = resolveChoice(answer2, names);
|
|
7152
7107
|
if (chosen) {
|
|
7153
7108
|
return chosen;
|
|
7154
7109
|
}
|
|
@@ -7163,13 +7118,13 @@ Name this connection, so you never have to type ${endpoint} again.
|
|
|
7163
7118
|
(for example: prod, dev, staging)
|
|
7164
7119
|
`);
|
|
7165
7120
|
for (let attempt = 0; attempt < 3; attempt++) {
|
|
7166
|
-
const
|
|
7121
|
+
const answer2 = await ask3(suggested ? `
|
|
7167
7122
|
Profile name [${suggested}]: ` : `
|
|
7168
7123
|
Profile name: `);
|
|
7169
|
-
if (
|
|
7124
|
+
if (answer2 === null) {
|
|
7170
7125
|
return null;
|
|
7171
7126
|
}
|
|
7172
|
-
const name =
|
|
7127
|
+
const name = answer2.trim() || suggested;
|
|
7173
7128
|
if (name && /^[A-Za-z0-9][A-Za-z0-9._-]*$/.test(name)) {
|
|
7174
7129
|
return name;
|
|
7175
7130
|
}
|
|
@@ -7270,17 +7225,17 @@ async function login(ctx) {
|
|
|
7270
7225
|
throw new PublisherError("INVALID_REQUEST", "login requires --endpoint (or --profile naming a stored one)");
|
|
7271
7226
|
}
|
|
7272
7227
|
clock.lap("discovery");
|
|
7273
|
-
const
|
|
7228
|
+
const profile2 = str2(ctx.flags["profile"]);
|
|
7274
7229
|
const setDefault = ctx.flags["set-default"] === true;
|
|
7275
7230
|
const given = str2(ctx.flags["token"]) ?? process.env["GIT_PUBLISHER_TOKEN"];
|
|
7276
7231
|
if (given) {
|
|
7277
|
-
return finish(ctx, { endpoint, token: given, signIn: null, profile, setDefault }, clock);
|
|
7232
|
+
return finish(ctx, { endpoint, token: given, signIn: null, profile: profile2, setDefault }, clock);
|
|
7278
7233
|
}
|
|
7279
7234
|
if (ctx.flags["no-wait"] === true) {
|
|
7280
7235
|
const started = await startDeviceLogin({ endpoint });
|
|
7281
7236
|
clock.lap("start");
|
|
7282
7237
|
prompt(started);
|
|
7283
|
-
const pendingFile = await savePendingLogin({ endpoint, started, ...
|
|
7238
|
+
const pendingFile = await savePendingLogin({ endpoint, started, ...profile2 ? { profile: profile2 } : {}, ...setDefault ? { setDefault } : {} });
|
|
7284
7239
|
ctx.out(pendingResult(endpoint, started, "Have the code approved, then run next.command to finish signing in.", clock, { pendingFile }));
|
|
7285
7240
|
return 0;
|
|
7286
7241
|
}
|
|
@@ -7292,7 +7247,7 @@ async function login(ctx) {
|
|
|
7292
7247
|
}
|
|
7293
7248
|
});
|
|
7294
7249
|
clock.lap("approval");
|
|
7295
|
-
return finish(ctx, { endpoint, token: signIn.token, signIn, profile, setDefault }, clock);
|
|
7250
|
+
return finish(ctx, { endpoint, token: signIn.token, signIn, profile: profile2, setDefault }, clock);
|
|
7296
7251
|
}
|
|
7297
7252
|
async function continueLogin(ctx) {
|
|
7298
7253
|
const clock = stopwatch();
|
|
@@ -7344,20 +7299,20 @@ async function finish(ctx, got, clock) {
|
|
|
7344
7299
|
const { endpoint, token, signIn } = got;
|
|
7345
7300
|
const caps = await verifiedCapabilities(createPublisher({ endpoint, token }));
|
|
7346
7301
|
clock.lap("verify");
|
|
7347
|
-
let
|
|
7348
|
-
if (!
|
|
7302
|
+
let profile2 = got.profile;
|
|
7303
|
+
if (!profile2 && isInteractive({ json: ctx.json, isTTY: process.stdin.isTTY })) {
|
|
7349
7304
|
const suggested = suggestProfileName(endpoint, new Set(Object.keys(ctx.input.file.profiles)));
|
|
7350
|
-
|
|
7305
|
+
profile2 = await askProfileName(endpoint, suggested) ?? void 0;
|
|
7351
7306
|
}
|
|
7352
7307
|
const path = await saveCredential(endpoint, token, void 0, {
|
|
7353
|
-
...
|
|
7308
|
+
...profile2 ? { profile: profile2 } : {},
|
|
7354
7309
|
setDefault: got.setDefault,
|
|
7355
7310
|
// The refresh half, when the service issued one: the sign-in outlives the credential.
|
|
7356
7311
|
...signIn ? { signIn } : {}
|
|
7357
7312
|
});
|
|
7358
7313
|
const file = await readCredentials();
|
|
7359
7314
|
clock.lap("save");
|
|
7360
|
-
const named = profileForEndpoint(file, endpoint,
|
|
7315
|
+
const named = profileForEndpoint(file, endpoint, profile2);
|
|
7361
7316
|
const timings = clock.read();
|
|
7362
7317
|
ctx.log(timingsLine(timings));
|
|
7363
7318
|
ctx.out({
|
|
@@ -7374,6 +7329,67 @@ async function finish(ctx, got, clock) {
|
|
|
7374
7329
|
return 0;
|
|
7375
7330
|
}
|
|
7376
7331
|
|
|
7332
|
+
// src/commands/account.ts
|
|
7333
|
+
var login2 = {
|
|
7334
|
+
git: false,
|
|
7335
|
+
async run(inv) {
|
|
7336
|
+
return login({ flags: inv.flags, json: inv.json, out: inv.out, log: inv.log, input: await inv.resolutionInput() });
|
|
7337
|
+
}
|
|
7338
|
+
};
|
|
7339
|
+
var logout = {
|
|
7340
|
+
git: false,
|
|
7341
|
+
async run(inv) {
|
|
7342
|
+
const profile2 = inv.str("profile");
|
|
7343
|
+
const endpoint = inv.str("endpoint") ?? process.env["GIT_PUBLISHER_ENDPOINT"];
|
|
7344
|
+
if (!profile2 && !endpoint) {
|
|
7345
|
+
throw new PublisherError("INVALID_REQUEST", "logout requires --endpoint or --profile");
|
|
7346
|
+
}
|
|
7347
|
+
const { revoked } = profile2 ? await removeProfile(profile2, { revoke: true }) : await removeCredential(endpoint, { revoke: true });
|
|
7348
|
+
inv.out({ ok: true, ...profile2 ? { profile: profile2 } : { endpoint }, revoked });
|
|
7349
|
+
return 0;
|
|
7350
|
+
}
|
|
7351
|
+
};
|
|
7352
|
+
var profile = {
|
|
7353
|
+
git: false,
|
|
7354
|
+
async run(inv) {
|
|
7355
|
+
const file = await readCredentials();
|
|
7356
|
+
switch (inv.cmd[1]) {
|
|
7357
|
+
case void 0:
|
|
7358
|
+
case "list": {
|
|
7359
|
+
const s = credentialStatus({ file, env: process.env });
|
|
7360
|
+
inv.out({ profiles: s.profiles, defaultProfile: s.defaultProfile, credentialsFile: credentialsPath() });
|
|
7361
|
+
return 0;
|
|
7362
|
+
}
|
|
7363
|
+
case "use": {
|
|
7364
|
+
const name = inv.cmd[2];
|
|
7365
|
+
if (!name) {
|
|
7366
|
+
throw new PublisherError("INVALID_REQUEST", "profile use requires a name");
|
|
7367
|
+
}
|
|
7368
|
+
await setDefaultProfile(name);
|
|
7369
|
+
inv.out({ ok: true, defaultProfile: name, endpoint: file.profiles[name].endpoint });
|
|
7370
|
+
return 0;
|
|
7371
|
+
}
|
|
7372
|
+
case "remove": {
|
|
7373
|
+
const name = inv.cmd[2];
|
|
7374
|
+
if (!name) {
|
|
7375
|
+
throw new PublisherError("INVALID_REQUEST", "profile remove requires a name");
|
|
7376
|
+
}
|
|
7377
|
+
await removeProfile(name);
|
|
7378
|
+
inv.out({ ok: true, removed: name });
|
|
7379
|
+
return 0;
|
|
7380
|
+
}
|
|
7381
|
+
default:
|
|
7382
|
+
throw new PublisherError("INVALID_REQUEST", `unknown profile command: ${inv.cmd[1]}; expected list, use or remove`);
|
|
7383
|
+
}
|
|
7384
|
+
}
|
|
7385
|
+
};
|
|
7386
|
+
|
|
7387
|
+
// src/git-minimum.ts
|
|
7388
|
+
var CLIENT_GIT_MINIMUM = "2.43.0";
|
|
7389
|
+
function gitSupported(version2) {
|
|
7390
|
+
return compareVersions(version2, CLIENT_GIT_MINIMUM) >= 0;
|
|
7391
|
+
}
|
|
7392
|
+
|
|
7377
7393
|
// src/skills.ts
|
|
7378
7394
|
var import_node_child_process3 = require("node:child_process");
|
|
7379
7395
|
var import_node_fs7 = require("node:fs");
|
|
@@ -7391,14 +7407,14 @@ function packageVersion() {
|
|
|
7391
7407
|
}
|
|
7392
7408
|
}
|
|
7393
7409
|
function claude(args) {
|
|
7394
|
-
return new Promise((
|
|
7410
|
+
return new Promise((resolve4) => {
|
|
7395
7411
|
let stdout = "";
|
|
7396
7412
|
let stderr = "";
|
|
7397
7413
|
const child = (0, import_node_child_process3.spawn)("claude", args, { stdio: ["ignore", "pipe", "pipe"] });
|
|
7398
7414
|
child.stdout.on("data", (d) => stdout += d.toString("utf8"));
|
|
7399
7415
|
child.stderr.on("data", (d) => stderr += d.toString("utf8"));
|
|
7400
|
-
child.on("error", (e) =>
|
|
7401
|
-
child.on("close", (code) =>
|
|
7416
|
+
child.on("error", (e) => resolve4({ code: null, stdout, stderr: e.message, missing: e.code === "ENOENT" }));
|
|
7417
|
+
child.on("close", (code) => resolve4({ code, stdout, stderr, missing: false }));
|
|
7402
7418
|
});
|
|
7403
7419
|
}
|
|
7404
7420
|
async function installSkills({ log }) {
|
|
@@ -7431,21 +7447,290 @@ async function installSkills({ log }) {
|
|
|
7431
7447
|
};
|
|
7432
7448
|
}
|
|
7433
7449
|
|
|
7434
|
-
// src/
|
|
7435
|
-
|
|
7436
|
-
|
|
7437
|
-
|
|
7438
|
-
|
|
7450
|
+
// src/tempdir.ts
|
|
7451
|
+
var import_node_fs8 = require("node:fs");
|
|
7452
|
+
var import_promises12 = require("node:fs/promises");
|
|
7453
|
+
var import_node_os4 = require("node:os");
|
|
7454
|
+
var FALLBACK_TEMP_DIRS = ["/tmp", "/var/tmp"];
|
|
7455
|
+
async function unusable(path) {
|
|
7456
|
+
try {
|
|
7457
|
+
if (!(await (0, import_promises12.stat)(path)).isDirectory()) {
|
|
7458
|
+
return "is not a directory";
|
|
7459
|
+
}
|
|
7460
|
+
} catch {
|
|
7461
|
+
return "does not exist";
|
|
7439
7462
|
}
|
|
7440
|
-
|
|
7441
|
-
|
|
7442
|
-
|
|
7443
|
-
|
|
7444
|
-
|
|
7445
|
-
if ("reason" in at) {
|
|
7446
|
-
return at;
|
|
7463
|
+
try {
|
|
7464
|
+
await (0, import_promises12.access)(path, import_node_fs8.constants.W_OK | import_node_fs8.constants.X_OK);
|
|
7465
|
+
return null;
|
|
7466
|
+
} catch {
|
|
7467
|
+
return "cannot be written";
|
|
7447
7468
|
}
|
|
7448
|
-
|
|
7469
|
+
}
|
|
7470
|
+
async function ensureTempDir(env = process.env, fallbacks = FALLBACK_TEMP_DIRS) {
|
|
7471
|
+
const named = (0, import_node_os4.tmpdir)();
|
|
7472
|
+
const reason = await unusable(named);
|
|
7473
|
+
if (reason === null) {
|
|
7474
|
+
return { path: named, replaced: null };
|
|
7475
|
+
}
|
|
7476
|
+
for (const candidate of fallbacks) {
|
|
7477
|
+
if (candidate !== named && await unusable(candidate) === null) {
|
|
7478
|
+
env["TMPDIR"] = candidate;
|
|
7479
|
+
return { path: candidate, replaced: { path: named, reason } };
|
|
7480
|
+
}
|
|
7481
|
+
}
|
|
7482
|
+
throw new PublisherError(
|
|
7483
|
+
"LOCAL_STATE",
|
|
7484
|
+
`the temporary directory ${named} ${reason}, and neither ${fallbacks.join(" nor ")} can be used: set TMPDIR to a directory that can be written`,
|
|
7485
|
+
{
|
|
7486
|
+
details: { tempDir: named, reason, tried: [...fallbacks] }
|
|
7487
|
+
}
|
|
7488
|
+
);
|
|
7489
|
+
}
|
|
7490
|
+
function replacedLine(t) {
|
|
7491
|
+
return t.replaced === null ? null : `note: the temporary directory ${t.replaced.path} ${t.replaced.reason}; using ${t.path} instead`;
|
|
7492
|
+
}
|
|
7493
|
+
|
|
7494
|
+
// src/commands/environment.ts
|
|
7495
|
+
var doctor = {
|
|
7496
|
+
git: false,
|
|
7497
|
+
async run(inv) {
|
|
7498
|
+
const temp = await ensureTempDir().catch((e) => e instanceof PublisherError ? { error: e.message } : null);
|
|
7499
|
+
const git = await gitVersion().catch(() => null);
|
|
7500
|
+
const dir = inv.dirArg(1);
|
|
7501
|
+
const s = credentialStatus(await inv.resolutionInput(dir));
|
|
7502
|
+
const association = await isGitRepo(dir) ? await readAssociation(dir) : null;
|
|
7503
|
+
const probe = association ? await probeEndpoint(association.endpoint) : null;
|
|
7504
|
+
inv.out({
|
|
7505
|
+
node: process.version,
|
|
7506
|
+
git,
|
|
7507
|
+
gitOk: git !== null && gitSupported(git),
|
|
7508
|
+
endpoint: s.resolution.kind === "resolved" ? s.resolution.endpoint : null,
|
|
7509
|
+
endpointSource: s.resolution.kind === "resolved" ? s.resolution.source : null,
|
|
7510
|
+
profile: s.resolution.kind === "resolved" ? s.resolution.profile ?? null : null,
|
|
7511
|
+
credentials: s.credentials,
|
|
7512
|
+
// What to do next and how to put it to the person, so the caller never has to invent it.
|
|
7513
|
+
next: nextStep(s),
|
|
7514
|
+
// The discriminated resolution, so a caller can tell "no credential for a settled endpoint"
|
|
7515
|
+
// from "the profile you named does not exist" — both of which report credentials "missing".
|
|
7516
|
+
resolution: s.resolution,
|
|
7517
|
+
profiles: s.profiles,
|
|
7518
|
+
defaultProfile: s.defaultProfile,
|
|
7519
|
+
credentialsFile: credentialsPath(),
|
|
7520
|
+
tempDir: temp,
|
|
7521
|
+
association: association === null ? null : { endpoint: association.endpoint, repoId: association.repoId },
|
|
7522
|
+
// Printed as it comes: `moved` carries where to, `unreachable` carries why, and the two
|
|
7523
|
+
// need different next steps so they are not collapsed into "not ok".
|
|
7524
|
+
associationStatus: probe
|
|
7525
|
+
});
|
|
7526
|
+
return git ? 0 : 1;
|
|
7527
|
+
}
|
|
7528
|
+
};
|
|
7529
|
+
var migrate = {
|
|
7530
|
+
git: false,
|
|
7531
|
+
async run(inv) {
|
|
7532
|
+
const dir = inv.dirArg(1);
|
|
7533
|
+
const result = await migrateAssociation({ repoDir: dir, ...inv.optStr("to") });
|
|
7534
|
+
const command = `publisher login --endpoint ${result.to} --json`;
|
|
7535
|
+
inv.log(`re-pointed ${dir}: ${result.from} -> ${result.to}; sign in there with: ${command}`);
|
|
7536
|
+
inv.out({ ...result, next: { action: "reauthenticate", ask: null, command } });
|
|
7537
|
+
return 0;
|
|
7538
|
+
}
|
|
7539
|
+
};
|
|
7540
|
+
var skills = {
|
|
7541
|
+
git: false,
|
|
7542
|
+
async run(inv) {
|
|
7543
|
+
if (inv.cmd[1] !== "install") {
|
|
7544
|
+
throw new PublisherError("INVALID_REQUEST", `unknown skills command: ${inv.cmd[1] ?? "(none)"}; expected install`);
|
|
7545
|
+
}
|
|
7546
|
+
const r = await installSkills({ log: inv.log });
|
|
7547
|
+
inv.out(r);
|
|
7548
|
+
return 0;
|
|
7549
|
+
}
|
|
7550
|
+
};
|
|
7551
|
+
|
|
7552
|
+
// src/init.ts
|
|
7553
|
+
async function repositoryForInit(p, endpoint, name, opts = {}) {
|
|
7554
|
+
const portal = portalForEndpoint(endpoint);
|
|
7555
|
+
if (portal === null) {
|
|
7556
|
+
return { repoId: (await p.createRepository({ name, grants: [] })).repoId, appId: null };
|
|
7557
|
+
}
|
|
7558
|
+
const confirmed = await confirmPortal(portal, endpoint, opts.fetch);
|
|
7559
|
+
if (confirmed === null) {
|
|
7560
|
+
throw new PublisherError("UNAVAILABLE", `could not confirm that ${portal} publishes to ${endpoint}; run init again`, { retryable: true });
|
|
7561
|
+
}
|
|
7562
|
+
if (!confirmed.terminal.includes(APPLICATION_CREATION)) {
|
|
7563
|
+
throw new PublisherError("UNAVAILABLE", `${portal} cannot make an application from a terminal yet, and a repository needs one; ask whoever runs it to update the portal`);
|
|
7564
|
+
}
|
|
7565
|
+
const made = await p.createApplication({ portal, name });
|
|
7566
|
+
return { repoId: made.repository.repoId, appId: made.appId };
|
|
7567
|
+
}
|
|
7568
|
+
|
|
7569
|
+
// src/commands/link.ts
|
|
7570
|
+
var init = {
|
|
7571
|
+
git: true,
|
|
7572
|
+
async run(inv) {
|
|
7573
|
+
const dir = inv.dirArg(1);
|
|
7574
|
+
const { p, endpoint } = await inv.client();
|
|
7575
|
+
const repo = inv.str("repo");
|
|
7576
|
+
const linked = await linkFolder(inv, p, endpoint, dir, repo ? { repoId: repo } : {});
|
|
7577
|
+
inv.out({ ok: true, ...linked, endpoint, dir });
|
|
7578
|
+
return 0;
|
|
7579
|
+
}
|
|
7580
|
+
};
|
|
7581
|
+
async function linkFolder(inv, p, endpoint, dir, attach) {
|
|
7582
|
+
let onboarding = null;
|
|
7583
|
+
if (!await isGitRepo(dir) || !await headCommit(dir)) {
|
|
7584
|
+
onboarding = await onboardFolder({ dir, ...inv.optStr("message") });
|
|
7585
|
+
inv.log(`prepared ${onboarding.committed.length} files (${onboarding.excluded.length} excluded)`);
|
|
7586
|
+
}
|
|
7587
|
+
let repoId = attach.repoId ?? null;
|
|
7588
|
+
let appId = null;
|
|
7589
|
+
if (repoId === null) {
|
|
7590
|
+
const made = await repositoryForInit(p, endpoint, inv.str("name") ?? dir.split("/").pop() ?? "");
|
|
7591
|
+
repoId = made.repoId;
|
|
7592
|
+
appId = made.appId;
|
|
7593
|
+
} else {
|
|
7594
|
+
await p.getRepository(repoId);
|
|
7595
|
+
}
|
|
7596
|
+
await p.associate(dir, repoId);
|
|
7597
|
+
const workspace = await p.adoptWorkspace(dir).catch(() => null);
|
|
7598
|
+
return {
|
|
7599
|
+
repoId,
|
|
7600
|
+
appId,
|
|
7601
|
+
created: attach.repoId === void 0,
|
|
7602
|
+
onboarding: onboarding ? { commit: onboarding.commit, committed: onboarding.committed.length, excluded: onboarding.excluded } : null,
|
|
7603
|
+
workspace: workspace === null ? null : { branch: workspace.branch, baseOid: workspace.baseOid, baseGeneration: workspace.baseGeneration }
|
|
7604
|
+
};
|
|
7605
|
+
}
|
|
7606
|
+
|
|
7607
|
+
// src/commands/publication.ts
|
|
7608
|
+
var publish2 = {
|
|
7609
|
+
git: true,
|
|
7610
|
+
async run(inv) {
|
|
7611
|
+
const dir = inv.dirArg(1);
|
|
7612
|
+
const { p } = await inv.client(dir);
|
|
7613
|
+
const refs = Array.isArray(inv.flags["ref"]) ? inv.flags["ref"] : [];
|
|
7614
|
+
const force = inv.flags["force"] === true;
|
|
7615
|
+
const updates = refs.length ? refs.map((r) => ({ ref: r.startsWith("refs/") ? r : `refs/heads/${r}`, force })) : void 0;
|
|
7616
|
+
if (!updates && force) {
|
|
7617
|
+
const b = await currentBranch(dir);
|
|
7618
|
+
if (!b) {
|
|
7619
|
+
throw new PublisherError("LOCAL_STATE", "detached HEAD");
|
|
7620
|
+
}
|
|
7621
|
+
return published(inv, await p.publish({ repoDir: dir, updates: [{ ref: b, force: true }], wait: inv.flags["no-wait"] !== true }, { onProgress: inv.progress }));
|
|
7622
|
+
}
|
|
7623
|
+
return published(inv, await p.publish({ repoDir: dir, ...updates ? { updates } : {}, wait: inv.flags["no-wait"] !== true }, { onProgress: inv.progress }));
|
|
7624
|
+
}
|
|
7625
|
+
};
|
|
7626
|
+
function published(inv, o) {
|
|
7627
|
+
inv.out({
|
|
7628
|
+
state: o.state,
|
|
7629
|
+
publicationId: o.publicationId,
|
|
7630
|
+
repoId: o.repoId,
|
|
7631
|
+
generation: o.resultGeneration,
|
|
7632
|
+
refs: o.refs,
|
|
7633
|
+
rejection: o.rejection,
|
|
7634
|
+
resumed: o.resumed,
|
|
7635
|
+
...o.next === null ? {} : { next: o.next }
|
|
7636
|
+
});
|
|
7637
|
+
return exitFor(o.state);
|
|
7638
|
+
}
|
|
7639
|
+
function exitFor(state) {
|
|
7640
|
+
return state === "accepted" ? 0 : state === "rejected" ? 2 : 3;
|
|
7641
|
+
}
|
|
7642
|
+
var status = {
|
|
7643
|
+
git: true,
|
|
7644
|
+
async run(inv) {
|
|
7645
|
+
const dir = inv.dirArg(1);
|
|
7646
|
+
const { p } = await inv.client(dir);
|
|
7647
|
+
const pubId = inv.str("publication");
|
|
7648
|
+
if (pubId) {
|
|
7649
|
+
const repoId = await inv.repoId(dir);
|
|
7650
|
+
const s = await p.getPublicationStatus(repoId, pubId);
|
|
7651
|
+
inv.out(s);
|
|
7652
|
+
return exitFor(s.state);
|
|
7653
|
+
}
|
|
7654
|
+
const pending = await p.pendingPublications(dir);
|
|
7655
|
+
if (pending.length === 0) {
|
|
7656
|
+
inv.out({ pending: [], resolved: [] });
|
|
7657
|
+
return 0;
|
|
7658
|
+
}
|
|
7659
|
+
const resolved = await p.resumePublications(dir, { onProgress: inv.progress });
|
|
7660
|
+
inv.out({
|
|
7661
|
+
pending: pending.map((x) => x.publicationId),
|
|
7662
|
+
resolved: resolved.map((o) => ({
|
|
7663
|
+
publicationId: o.publicationId,
|
|
7664
|
+
state: o.state,
|
|
7665
|
+
generation: o.resultGeneration,
|
|
7666
|
+
rejection: o.rejection,
|
|
7667
|
+
...o.next === null ? {} : { next: o.next }
|
|
7668
|
+
}))
|
|
7669
|
+
});
|
|
7670
|
+
return resolved.every((o) => o.state === "accepted") ? 0 : resolved.some((o) => o.state === "rejected") ? 2 : 3;
|
|
7671
|
+
}
|
|
7672
|
+
};
|
|
7673
|
+
var follow = {
|
|
7674
|
+
git: false,
|
|
7675
|
+
async run(inv) {
|
|
7676
|
+
const dir = inv.dirArg(1);
|
|
7677
|
+
const publicationId = inv.str("publication");
|
|
7678
|
+
if (!publicationId) {
|
|
7679
|
+
throw new PublisherError("INVALID_REQUEST", "follow requires --publication <id>");
|
|
7680
|
+
}
|
|
7681
|
+
const timeoutMs = followTimeoutMs(inv.str("timeout"));
|
|
7682
|
+
const { p, endpoint } = await inv.client(dir);
|
|
7683
|
+
const repoId = await inv.repoId(dir);
|
|
7684
|
+
const publication = await p.getPublicationStatus(repoId, publicationId);
|
|
7685
|
+
if (publication.state !== "accepted") {
|
|
7686
|
+
throw new PublisherError("LOCAL_STATE", `publication ${publicationId} is ${publication.state}, not accepted; run publisher status`);
|
|
7687
|
+
}
|
|
7688
|
+
const clock = stopwatch();
|
|
7689
|
+
const followed = await followPublication(p, endpoint, publication, { ...inv.following, timeoutMs, dir });
|
|
7690
|
+
const timings = clock.read("stages" in followed ? runTimings(followed.stages, null) : {});
|
|
7691
|
+
inv.log(timingsLine(timings));
|
|
7692
|
+
inv.out({ publicationId, progress: followed, timings });
|
|
7693
|
+
return 0;
|
|
7694
|
+
}
|
|
7695
|
+
};
|
|
7696
|
+
|
|
7697
|
+
// src/commands/repository.ts
|
|
7698
|
+
var import_node_path9 = require("node:path");
|
|
7699
|
+
|
|
7700
|
+
// src/list.ts
|
|
7701
|
+
async function listApps(p, opts = {}) {
|
|
7702
|
+
const page = await p.listRepositories({ state: opts.archived ? "archived" : "active", limit: 200, ...opts.cursor ? { cursor: opts.cursor } : {} });
|
|
7703
|
+
const apps = page.repositories.map((r) => ({
|
|
7704
|
+
repoId: r.repoId,
|
|
7705
|
+
name: r.name,
|
|
7706
|
+
createdAt: r.createdAt,
|
|
7707
|
+
publishedAt: r.lastTransaction?.committedAt ?? null,
|
|
7708
|
+
archived: r.archived,
|
|
7709
|
+
checkout: `publisher checkout ${shellWord(folderFor(r.name))} --repo ${r.repoId} --json`
|
|
7710
|
+
})).sort((a, b) => (b.publishedAt ?? b.createdAt).localeCompare(a.publishedAt ?? a.createdAt));
|
|
7711
|
+
const hint = apps.length === 0 ? "You have not published an app from this sign-in yet." : "To work on one in a new folder, run its checkout command.";
|
|
7712
|
+
return { apps, nextCursor: page.nextCursor, hint };
|
|
7713
|
+
}
|
|
7714
|
+
function folderFor(name) {
|
|
7715
|
+
const slug2 = name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
7716
|
+
return slug2 === "" ? "app" : slug2;
|
|
7717
|
+
}
|
|
7718
|
+
|
|
7719
|
+
// src/question.ts
|
|
7720
|
+
async function questionAt(p, endpoint, repoId, opts = {}) {
|
|
7721
|
+
const at = await portalAt(endpoint, opts);
|
|
7722
|
+
if ("reason" in at) {
|
|
7723
|
+
return at;
|
|
7724
|
+
}
|
|
7725
|
+
return shown(at.portal, await p.readPreparation({ portal: at.portal, repoId }));
|
|
7726
|
+
}
|
|
7727
|
+
async function answerAt(p, endpoint, repoId, specs, opts = {}) {
|
|
7728
|
+
const answers = parseSpecs(specs);
|
|
7729
|
+
const at = await portalAt(endpoint, opts);
|
|
7730
|
+
if ("reason" in at) {
|
|
7731
|
+
return at;
|
|
7732
|
+
}
|
|
7733
|
+
const read2 = shown(at.portal, await p.readPreparation({ portal: at.portal, repoId }));
|
|
7449
7734
|
if (read2.kind !== "view") {
|
|
7450
7735
|
return read2;
|
|
7451
7736
|
}
|
|
@@ -7453,13 +7738,13 @@ async function answerAt(p, endpoint, repoId, specs, opts = {}) {
|
|
|
7453
7738
|
throw new PublisherError("LOCAL_STATE", `nothing is waiting for an answer (the preparation is ${read2.preparation.status})`);
|
|
7454
7739
|
}
|
|
7455
7740
|
for (const [id, value] of Object.entries(answers)) {
|
|
7456
|
-
const
|
|
7457
|
-
if (
|
|
7741
|
+
const question2 = read2.questions.find((q) => q.id === id);
|
|
7742
|
+
if (question2 === void 0) {
|
|
7458
7743
|
throw new PublisherError("INVALID_REQUEST", `no pending question "${id}"; pending: ${read2.questions.map((q) => q.id).join(", ")}`, {
|
|
7459
7744
|
details: { questions: read2.questions }
|
|
7460
7745
|
});
|
|
7461
7746
|
}
|
|
7462
|
-
const values =
|
|
7747
|
+
const values = question2.options.map((o) => o.value);
|
|
7463
7748
|
if (!values.includes(value)) {
|
|
7464
7749
|
throw new PublisherError("INVALID_REQUEST", `"${value}" is not one of ${values.join(", ")} for "${id}"`, { details: { questions: read2.questions } });
|
|
7465
7750
|
}
|
|
@@ -7519,50 +7804,166 @@ function hintFor(view) {
|
|
|
7519
7804
|
return `It is waiting for your answer. Run: publisher answer ${how}`;
|
|
7520
7805
|
}
|
|
7521
7806
|
|
|
7522
|
-
// src/
|
|
7523
|
-
var
|
|
7524
|
-
|
|
7525
|
-
|
|
7526
|
-
|
|
7527
|
-
|
|
7528
|
-
try {
|
|
7529
|
-
if (!(await (0, import_promises12.stat)(path)).isDirectory()) {
|
|
7530
|
-
return "is not a directory";
|
|
7531
|
-
}
|
|
7532
|
-
} catch {
|
|
7533
|
-
return "does not exist";
|
|
7807
|
+
// src/commands/repository.ts
|
|
7808
|
+
var list = {
|
|
7809
|
+
git: false,
|
|
7810
|
+
async run(inv) {
|
|
7811
|
+
inv.out(await listApps((await inv.client()).p, { archived: inv.flags["archived"] === true, cursor: inv.str("cursor") }));
|
|
7812
|
+
return 0;
|
|
7534
7813
|
}
|
|
7535
|
-
|
|
7536
|
-
|
|
7537
|
-
|
|
7538
|
-
|
|
7539
|
-
|
|
7814
|
+
};
|
|
7815
|
+
var info = {
|
|
7816
|
+
git: false,
|
|
7817
|
+
async run(inv) {
|
|
7818
|
+
const dir = inv.dirArg(1);
|
|
7819
|
+
const { p } = await inv.client(dir);
|
|
7820
|
+
const repoId = await inv.repoId(dir);
|
|
7821
|
+
inv.out(await p.getRepository(repoId));
|
|
7822
|
+
return 0;
|
|
7540
7823
|
}
|
|
7541
|
-
}
|
|
7542
|
-
|
|
7543
|
-
|
|
7544
|
-
|
|
7545
|
-
|
|
7546
|
-
|
|
7824
|
+
};
|
|
7825
|
+
var question = {
|
|
7826
|
+
git: false,
|
|
7827
|
+
async run(inv) {
|
|
7828
|
+
const dir = inv.dirArg(1);
|
|
7829
|
+
const { p, endpoint } = await inv.client(dir);
|
|
7830
|
+
const repoId = await inv.repoId(dir);
|
|
7831
|
+
inv.out({ repoId, ...await questionAt(p, endpoint, repoId) });
|
|
7832
|
+
return 0;
|
|
7547
7833
|
}
|
|
7548
|
-
|
|
7549
|
-
|
|
7550
|
-
|
|
7551
|
-
|
|
7552
|
-
|
|
7834
|
+
};
|
|
7835
|
+
var answer = {
|
|
7836
|
+
git: false,
|
|
7837
|
+
async run(inv) {
|
|
7838
|
+
const dir = (0, import_node_path9.resolve)(".");
|
|
7839
|
+
const { p, endpoint } = await inv.client(dir);
|
|
7840
|
+
const repoId = await inv.repoId(dir);
|
|
7841
|
+
inv.out({ repoId, ...await answerAt(p, endpoint, repoId, inv.cmd.slice(1)) });
|
|
7842
|
+
return 0;
|
|
7553
7843
|
}
|
|
7554
|
-
|
|
7555
|
-
|
|
7556
|
-
|
|
7557
|
-
|
|
7558
|
-
|
|
7559
|
-
|
|
7560
|
-
|
|
7844
|
+
};
|
|
7845
|
+
|
|
7846
|
+
// src/commands/sync.ts
|
|
7847
|
+
var sync = {
|
|
7848
|
+
git: true,
|
|
7849
|
+
async run(inv) {
|
|
7850
|
+
const dir = inv.dirArg(1);
|
|
7851
|
+
const timeoutMs = followTimeoutMs(inv.str("timeout"));
|
|
7852
|
+
const clock = stopwatch();
|
|
7853
|
+
const { p, endpoint, source } = await inv.client(dir);
|
|
7854
|
+
clock.lap("discovery");
|
|
7855
|
+
await resolveToken(endpoint, inv.str("token"), { endpointSource: source });
|
|
7856
|
+
clock.lap("authentication");
|
|
7857
|
+
const linked = await isGitRepo(dir) && await readAssociation(dir) !== null ? null : await linkFolder(inv, p, endpoint, dir, {});
|
|
7858
|
+
clock.lap("link", linked !== null);
|
|
7859
|
+
const sending = splitAtBegin(clock, inv.progress);
|
|
7860
|
+
const r = await p.syncChanges({ directory: dir, markConflicts: inv.flags["mark-conflicts"] === true, ...inv.optStr("message") }, { onProgress: sending.onProgress });
|
|
7861
|
+
const acceptedAt = sending.done();
|
|
7862
|
+
const o = r.published;
|
|
7863
|
+
const followed = o !== null && o.state === "accepted" && inv.flags["no-wait"] !== true ? await followPublication(p, endpoint, o, { ...inv.following, timeoutMs, dir }) : null;
|
|
7864
|
+
const syncTimings = clock.read(followed !== null && "stages" in followed ? runTimings(followed.stages, acceptedAt) : {});
|
|
7865
|
+
inv.log(timingsLine(syncTimings));
|
|
7866
|
+
inv.out({
|
|
7867
|
+
ok: r.state === "published" || r.state === "up_to_date",
|
|
7868
|
+
state: r.state,
|
|
7869
|
+
linked: linked === null ? null : { repoId: linked.repoId, appId: linked.appId, created: linked.created },
|
|
7870
|
+
...outcome(r),
|
|
7871
|
+
progress: followed,
|
|
7872
|
+
timings: syncTimings
|
|
7873
|
+
});
|
|
7874
|
+
return r.state === "published" || r.state === "up_to_date" ? 0 : r.state === "unknown" ? 3 : 2;
|
|
7875
|
+
}
|
|
7876
|
+
};
|
|
7877
|
+
function outcome(r) {
|
|
7878
|
+
const o = r.published;
|
|
7879
|
+
return {
|
|
7880
|
+
recorded: r.recorded,
|
|
7881
|
+
merged: r.reconciled.applied,
|
|
7882
|
+
conflicts: r.reconciled.before.conflicts,
|
|
7883
|
+
marked: r.marked,
|
|
7884
|
+
publication: o === null ? null : {
|
|
7885
|
+
state: o.state,
|
|
7886
|
+
publicationId: o.publicationId,
|
|
7887
|
+
generation: o.resultGeneration,
|
|
7888
|
+
refs: o.refs,
|
|
7889
|
+
rejection: o.rejection,
|
|
7890
|
+
...o.next === null ? {} : { next: o.next }
|
|
7891
|
+
},
|
|
7892
|
+
hint: syncHint(r.state, r.reconciled.before.conflicts, r.marked)
|
|
7893
|
+
};
|
|
7561
7894
|
}
|
|
7562
|
-
function
|
|
7563
|
-
|
|
7895
|
+
function syncHint(state, conflicts, marked) {
|
|
7896
|
+
switch (state) {
|
|
7897
|
+
case "published":
|
|
7898
|
+
return "Your changes are saved and published.";
|
|
7899
|
+
case "up_to_date":
|
|
7900
|
+
return "Nothing new to publish: the service already has this version.";
|
|
7901
|
+
case "conflict":
|
|
7902
|
+
return marked ? `Both versions are now in ${conflicts.join(", ")}, between <<<<<<< and >>>>>>> lines. Edit each to what should be kept, remove those lines, then run publisher sync.` : `Your changes and the published version both changed ${conflicts.join(", ")}. Nothing was published and nothing was lost; decide which version to keep.`;
|
|
7903
|
+
case "rejected":
|
|
7904
|
+
return "The service refused the publication; see publication.rejection.";
|
|
7905
|
+
default:
|
|
7906
|
+
return "The publication was sent but its outcome is not known yet; run publisher status.";
|
|
7907
|
+
}
|
|
7564
7908
|
}
|
|
7565
7909
|
|
|
7910
|
+
// src/commands/workspace.ts
|
|
7911
|
+
var fetch2 = {
|
|
7912
|
+
git: true,
|
|
7913
|
+
async run(inv) {
|
|
7914
|
+
const dir = inv.dirArg(1);
|
|
7915
|
+
const { p } = await inv.client(dir);
|
|
7916
|
+
const r = await p.fetch({ repoDir: dir }, { onProgress: inv.progress });
|
|
7917
|
+
inv.out({ ok: true, repoId: r.repoId, generation: r.generation, remote: r.remote, refs: r.refs, tracking: r.tracking, bytes: r.bytes });
|
|
7918
|
+
return 0;
|
|
7919
|
+
}
|
|
7920
|
+
};
|
|
7921
|
+
var checkout = {
|
|
7922
|
+
git: true,
|
|
7923
|
+
async run(inv) {
|
|
7924
|
+
const dir = inv.dirArg(1);
|
|
7925
|
+
const repoId = inv.str("repo");
|
|
7926
|
+
if (!repoId) {
|
|
7927
|
+
throw new PublisherError("INVALID_REQUEST", "checkout requires --repo <id>");
|
|
7928
|
+
}
|
|
7929
|
+
const { p } = await inv.client();
|
|
7930
|
+
const branch = inv.str("branch");
|
|
7931
|
+
const m = await p.checkout({ directory: dir, repoId, ...branch ? { branch } : {} }, { onProgress: inv.progress });
|
|
7932
|
+
inv.out({ ok: true, workspace: m });
|
|
7933
|
+
return 0;
|
|
7934
|
+
}
|
|
7935
|
+
};
|
|
7936
|
+
var workspaceStatus = {
|
|
7937
|
+
git: true,
|
|
7938
|
+
async run(inv) {
|
|
7939
|
+
const dir = inv.dirArg(1);
|
|
7940
|
+
const { p } = await inv.client(dir);
|
|
7941
|
+
const s = await p.workspaceStatus(dir, { onProgress: inv.progress });
|
|
7942
|
+
inv.out(s);
|
|
7943
|
+
return s.state === "conflict" ? 2 : 0;
|
|
7944
|
+
}
|
|
7945
|
+
};
|
|
7946
|
+
var mergeCheck2 = {
|
|
7947
|
+
git: true,
|
|
7948
|
+
async run(inv) {
|
|
7949
|
+
const dir = inv.dirArg(1);
|
|
7950
|
+
const ours = inv.str("ours");
|
|
7951
|
+
const theirs = inv.str("theirs");
|
|
7952
|
+
if (!ours || !theirs) {
|
|
7953
|
+
throw new PublisherError("INVALID_REQUEST", "merge-check requires --ours and --theirs");
|
|
7954
|
+
}
|
|
7955
|
+
const r = await mergeRemote({
|
|
7956
|
+
repoDir: dir,
|
|
7957
|
+
ours: ours.startsWith("refs/") ? ours : `refs/heads/${ours}`,
|
|
7958
|
+
theirs,
|
|
7959
|
+
apply: inv.flags["apply"] === true,
|
|
7960
|
+
...inv.optStr("message")
|
|
7961
|
+
});
|
|
7962
|
+
inv.out(r);
|
|
7963
|
+
return r.ok ? 0 : 2;
|
|
7964
|
+
}
|
|
7965
|
+
};
|
|
7966
|
+
|
|
7566
7967
|
// src/exit-code.ts
|
|
7567
7968
|
var EXIT_CODE = {
|
|
7568
7969
|
UNAUTHORIZED: 4,
|
|
@@ -7607,7 +8008,124 @@ function exitCodeFor(code) {
|
|
|
7607
8008
|
return EXIT_CODE[code];
|
|
7608
8009
|
}
|
|
7609
8010
|
|
|
7610
|
-
// src/
|
|
8011
|
+
// src/invocation.ts
|
|
8012
|
+
var import_node_path10 = require("node:path");
|
|
8013
|
+
var Invocation = class {
|
|
8014
|
+
cmd;
|
|
8015
|
+
flags;
|
|
8016
|
+
json;
|
|
8017
|
+
quiet;
|
|
8018
|
+
/** How a run is followed: each stage as it is seen (`stageEmitter`), and with --until-change back at each step. */
|
|
8019
|
+
following;
|
|
8020
|
+
constructor({ cmd, flags }) {
|
|
8021
|
+
this.cmd = cmd;
|
|
8022
|
+
this.flags = flags;
|
|
8023
|
+
this.json = flags["json"] === true;
|
|
8024
|
+
this.quiet = flags["quiet"] === true || this.json;
|
|
8025
|
+
this.following = { ...stageEmitter(this.json, this.quiet), log: this.log, untilChange: flags["until-change"] === true };
|
|
8026
|
+
}
|
|
8027
|
+
out = (v) => {
|
|
8028
|
+
process.stdout.write((this.json ? JSON.stringify(v) : formatHuman(v)) + "\n");
|
|
8029
|
+
};
|
|
8030
|
+
log = (s) => {
|
|
8031
|
+
if (!this.quiet) {
|
|
8032
|
+
process.stderr.write(s + "\n");
|
|
8033
|
+
}
|
|
8034
|
+
};
|
|
8035
|
+
/** The repository a command acts on: `--repo`, or else the one this folder is associated with. */
|
|
8036
|
+
async repoId(dir) {
|
|
8037
|
+
const repoId = this.str("repo") ?? (await readAssociation(dir))?.repoId;
|
|
8038
|
+
if (!repoId) {
|
|
8039
|
+
throw new PublisherError("LOCAL_STATE", "no repository association; pass --repo");
|
|
8040
|
+
}
|
|
8041
|
+
return repoId;
|
|
8042
|
+
}
|
|
8043
|
+
progress = (e) => {
|
|
8044
|
+
if (this.quiet) {
|
|
8045
|
+
return;
|
|
8046
|
+
}
|
|
8047
|
+
const pct = e.total ? ` ${Math.round((e.bytes ?? 0) / e.total * 100)}%` : "";
|
|
8048
|
+
process.stderr.write(`\r[${e.phase}] ${e.message}${pct} `);
|
|
8049
|
+
if (e.phase === "done") {
|
|
8050
|
+
process.stderr.write("\n");
|
|
8051
|
+
}
|
|
8052
|
+
};
|
|
8053
|
+
/** The folder named at position `i` of the command path, or the current one. */
|
|
8054
|
+
dirArg(i) {
|
|
8055
|
+
return (0, import_node_path10.resolve)(this.cmd[i] ?? ".");
|
|
8056
|
+
}
|
|
8057
|
+
/** A flag's value when it is a string, else undefined. */
|
|
8058
|
+
str(key) {
|
|
8059
|
+
return str2(this.flags[key]);
|
|
8060
|
+
}
|
|
8061
|
+
/** `{ [key]: value }` when the flag is a non-empty string, else nothing to spread. */
|
|
8062
|
+
optStr(key) {
|
|
8063
|
+
const v = this.str(key);
|
|
8064
|
+
return v ? { [key]: v } : {};
|
|
8065
|
+
}
|
|
8066
|
+
async resolutionInput(dir) {
|
|
8067
|
+
let association;
|
|
8068
|
+
if (dir && await isGitRepo(dir)) {
|
|
8069
|
+
association = (await readAssociation(dir))?.endpoint;
|
|
8070
|
+
}
|
|
8071
|
+
return {
|
|
8072
|
+
...this.optStr("endpoint"),
|
|
8073
|
+
...this.optStr("profile"),
|
|
8074
|
+
env: process.env,
|
|
8075
|
+
...association ? { association } : {},
|
|
8076
|
+
file: await readCredentials()
|
|
8077
|
+
};
|
|
8078
|
+
}
|
|
8079
|
+
/**
|
|
8080
|
+
* Resolve, and on the one ambiguous case ASK rather than refuse — but only at a real terminal
|
|
8081
|
+
* whose output a person is reading. Everywhere else (an agent's --json, CI) `demand` still fails
|
|
8082
|
+
* with the names in `error.details.profiles`, because a prompt there is a hang.
|
|
8083
|
+
*/
|
|
8084
|
+
async endpointFor(dir) {
|
|
8085
|
+
const input = await this.resolutionInput(dir);
|
|
8086
|
+
const r = resolveEndpoint(input);
|
|
8087
|
+
if (r.kind === "ambiguous" && isInteractive({ json: this.json, isTTY: process.stdin.isTTY })) {
|
|
8088
|
+
const chosen = await chooseProfile(r.profiles, "You have more than one connection, and none is the default.");
|
|
8089
|
+
if (chosen) {
|
|
8090
|
+
this.log(`Using ${chosen}. Make it permanent with: publisher profile use ${chosen}`);
|
|
8091
|
+
return demand(resolveEndpoint({ ...input, profile: chosen }), input);
|
|
8092
|
+
}
|
|
8093
|
+
}
|
|
8094
|
+
return demand(r, input);
|
|
8095
|
+
}
|
|
8096
|
+
async client(dir) {
|
|
8097
|
+
const { endpoint, source } = await this.endpointFor(dir);
|
|
8098
|
+
const token = this.str("token");
|
|
8099
|
+
return {
|
|
8100
|
+
endpoint,
|
|
8101
|
+
source,
|
|
8102
|
+
p: createPublisher({ endpoint, endpointSource: source, ...token ? { token } : {}, clientName: "git-publisher-cli", clientVersion: CLIENT_VERSION })
|
|
8103
|
+
};
|
|
8104
|
+
}
|
|
8105
|
+
};
|
|
8106
|
+
async function demand(r, input) {
|
|
8107
|
+
if (r.kind === "resolved") {
|
|
8108
|
+
return r;
|
|
8109
|
+
}
|
|
8110
|
+
const next = nextStep(credentialStatus(input));
|
|
8111
|
+
const details = { profiles: r.kind === "none" ? [] : r.profiles, next };
|
|
8112
|
+
const run = next.command === null ? "" : ` Run: ${next.command}`;
|
|
8113
|
+
switch (r.kind) {
|
|
8114
|
+
case "unknown-profile":
|
|
8115
|
+
throw new PublisherError("LOCAL_STATE", `no such profile: ${r.name}.${run}`, { details });
|
|
8116
|
+
case "ambiguous":
|
|
8117
|
+
throw new PublisherError("LOCAL_STATE", `several connections and none is the default: pass --profile <name>, or choose one once.${run}`, { details });
|
|
8118
|
+
case "none": {
|
|
8119
|
+
const where = await whereCredentialsWere();
|
|
8120
|
+
throw new PublisherError("LOCAL_STATE", `not connected to a publishing service yet: ${where.sentence}${run}`, { details: { ...details, credentialsFile: where.details } });
|
|
8121
|
+
}
|
|
8122
|
+
}
|
|
8123
|
+
}
|
|
8124
|
+
function formatHuman(v) {
|
|
8125
|
+
return JSON.stringify(v, null, 2);
|
|
8126
|
+
}
|
|
8127
|
+
|
|
8128
|
+
// src/usage.ts
|
|
7611
8129
|
var USAGE = `publisher \u2014 publish an app's Git history to the serverless Git backend
|
|
7612
8130
|
|
|
7613
8131
|
Usage:
|
|
@@ -7674,467 +8192,55 @@ Tokens come from --token, GIT_PUBLISHER_TOKEN, or the credentials file. A token
|
|
|
7674
8192
|
GIT_PUBLISHER_TOKEN is used only with --endpoint, --profile, GIT_PUBLISHER_ENDPOINT or
|
|
7675
8193
|
GIT_PUBLISHER_PROFILE, never with an endpoint the folder or a default profile supplied.
|
|
7676
8194
|
`;
|
|
7677
|
-
|
|
7678
|
-
|
|
7679
|
-
|
|
7680
|
-
|
|
8195
|
+
|
|
8196
|
+
// src/main.ts
|
|
8197
|
+
var COMMANDS = /* @__PURE__ */ new Map([
|
|
8198
|
+
["doctor", doctor],
|
|
8199
|
+
["migrate", migrate],
|
|
8200
|
+
["login", login2],
|
|
8201
|
+
["logout", logout],
|
|
8202
|
+
["profile", profile],
|
|
8203
|
+
["init", init],
|
|
8204
|
+
["publish", publish2],
|
|
8205
|
+
["status", status],
|
|
8206
|
+
["fetch", fetch2],
|
|
8207
|
+
["list", list],
|
|
8208
|
+
["checkout", checkout],
|
|
8209
|
+
["workspace-status", workspaceStatus],
|
|
8210
|
+
["sync", sync],
|
|
8211
|
+
["follow", follow],
|
|
8212
|
+
["question", question],
|
|
8213
|
+
["answer", answer],
|
|
8214
|
+
["skills", skills],
|
|
8215
|
+
["merge-check", mergeCheck2],
|
|
8216
|
+
["info", info]
|
|
8217
|
+
]);
|
|
7681
8218
|
async function main(argv) {
|
|
7682
|
-
const
|
|
7683
|
-
const
|
|
7684
|
-
const
|
|
7685
|
-
const
|
|
7686
|
-
|
|
7687
|
-
if (!quiet) {
|
|
7688
|
-
process.stderr.write(s + "\n");
|
|
7689
|
-
}
|
|
7690
|
-
};
|
|
7691
|
-
const progress = (e) => {
|
|
7692
|
-
if (quiet) {
|
|
7693
|
-
return;
|
|
7694
|
-
}
|
|
7695
|
-
const pct = e.total ? ` ${Math.round((e.bytes ?? 0) / e.total * 100)}%` : "";
|
|
7696
|
-
process.stderr.write(`\r[${e.phase}] ${e.message}${pct} `);
|
|
7697
|
-
if (e.phase === "done") {
|
|
7698
|
-
process.stderr.write("\n");
|
|
7699
|
-
}
|
|
7700
|
-
};
|
|
7701
|
-
const dirArg = (i) => (0, import_node_path9.resolve)(cmd[i] ?? ".");
|
|
7702
|
-
const following = { ...stageEmitter(json, quiet), log, untilChange: flags["until-change"] === true };
|
|
7703
|
-
const resolutionInput = async (dir) => {
|
|
7704
|
-
let association;
|
|
7705
|
-
if (dir && await isGitRepo(dir)) {
|
|
7706
|
-
association = (await readAssociation(dir))?.endpoint;
|
|
7707
|
-
}
|
|
7708
|
-
return {
|
|
7709
|
-
...optStr(flags, "endpoint"),
|
|
7710
|
-
...optStr(flags, "profile"),
|
|
7711
|
-
env: process.env,
|
|
7712
|
-
...association ? { association } : {},
|
|
7713
|
-
file: await readCredentials()
|
|
7714
|
-
};
|
|
7715
|
-
};
|
|
7716
|
-
const endpointFor = async (dir) => {
|
|
7717
|
-
const input = await resolutionInput(dir);
|
|
7718
|
-
const r = resolveEndpoint(input);
|
|
7719
|
-
if (r.kind === "ambiguous" && isInteractive({ json, isTTY: process.stdin.isTTY })) {
|
|
7720
|
-
const chosen = await chooseProfile(r.profiles, "You have more than one connection, and none is the default.");
|
|
7721
|
-
if (chosen) {
|
|
7722
|
-
log(`Using ${chosen}. Make it permanent with: publisher profile use ${chosen}`);
|
|
7723
|
-
return demand(resolveEndpoint({ ...input, profile: chosen }), input);
|
|
7724
|
-
}
|
|
7725
|
-
}
|
|
7726
|
-
return demand(r, input);
|
|
7727
|
-
};
|
|
7728
|
-
const client = async (dir) => {
|
|
7729
|
-
const { endpoint, source } = await endpointFor(dir);
|
|
7730
|
-
const token = str2(flags["token"]);
|
|
7731
|
-
return {
|
|
7732
|
-
endpoint,
|
|
7733
|
-
source,
|
|
7734
|
-
p: createPublisher({ endpoint, endpointSource: source, ...token ? { token } : {}, clientName: "git-publisher-cli", clientVersion: CLIENT_VERSION })
|
|
7735
|
-
};
|
|
7736
|
-
};
|
|
7737
|
-
const linkFolder = async (p, endpoint, dir, attach) => {
|
|
7738
|
-
let onboarding = null;
|
|
7739
|
-
if (!await isGitRepo(dir) || !await headCommit(dir)) {
|
|
7740
|
-
onboarding = await onboardFolder({ dir, ...optStr(flags, "message") });
|
|
7741
|
-
log(`prepared ${onboarding.committed.length} files (${onboarding.excluded.length} excluded)`);
|
|
7742
|
-
}
|
|
7743
|
-
let repoId = attach.repoId ?? null;
|
|
7744
|
-
let appId = null;
|
|
7745
|
-
if (repoId === null) {
|
|
7746
|
-
const made = await repositoryForInit(p, endpoint, str2(flags["name"]) ?? dir.split("/").pop() ?? "");
|
|
7747
|
-
repoId = made.repoId;
|
|
7748
|
-
appId = made.appId;
|
|
7749
|
-
} else {
|
|
7750
|
-
await p.getRepository(repoId);
|
|
7751
|
-
}
|
|
7752
|
-
await p.associate(dir, repoId);
|
|
7753
|
-
const workspace = await p.adoptWorkspace(dir).catch(() => null);
|
|
7754
|
-
return {
|
|
7755
|
-
repoId,
|
|
7756
|
-
appId,
|
|
7757
|
-
created: attach.repoId === void 0,
|
|
7758
|
-
onboarding: onboarding ? { commit: onboarding.commit, committed: onboarding.committed.length, excluded: onboarding.excluded } : null,
|
|
7759
|
-
workspace: workspace === null ? null : { branch: workspace.branch, baseOid: workspace.baseOid, baseGeneration: workspace.baseGeneration }
|
|
7760
|
-
};
|
|
7761
|
-
};
|
|
7762
|
-
if (flags["version"] === true) {
|
|
8219
|
+
const inv = new Invocation(parseArgs(argv));
|
|
8220
|
+
const name = inv.cmd[0];
|
|
8221
|
+
const help = inv.flags["help"] === true;
|
|
8222
|
+
const command = name === void 0 ? void 0 : COMMANDS.get(name);
|
|
8223
|
+
if (inv.flags["version"] === true) {
|
|
7763
8224
|
process.stdout.write(`${packageVersion()}
|
|
7764
8225
|
`);
|
|
7765
8226
|
return 0;
|
|
7766
8227
|
}
|
|
7767
|
-
if (
|
|
8228
|
+
if (!help && command?.git === true) {
|
|
7768
8229
|
const line = replacedLine(await ensureTempDir());
|
|
7769
8230
|
if (line !== null) {
|
|
7770
8231
|
process.stderr.write(line + "\n");
|
|
7771
8232
|
}
|
|
7772
8233
|
}
|
|
7773
|
-
|
|
7774
|
-
|
|
7775
|
-
|
|
7776
|
-
process.stdout.write(USAGE);
|
|
7777
|
-
return 0;
|
|
7778
|
-
case "doctor": {
|
|
7779
|
-
const temp = await ensureTempDir().catch((e) => e instanceof PublisherError ? { error: e.message } : null);
|
|
7780
|
-
const git = await gitVersion().catch(() => null);
|
|
7781
|
-
const dir = dirArg(1);
|
|
7782
|
-
const s = credentialStatus(await resolutionInput(dir));
|
|
7783
|
-
const association = await isGitRepo(dir) ? await readAssociation(dir) : null;
|
|
7784
|
-
const probe = association ? await probeEndpoint(association.endpoint) : null;
|
|
7785
|
-
out({
|
|
7786
|
-
node: process.version,
|
|
7787
|
-
git,
|
|
7788
|
-
gitOk: git !== null && gitSupported(git),
|
|
7789
|
-
endpoint: s.resolution.kind === "resolved" ? s.resolution.endpoint : null,
|
|
7790
|
-
endpointSource: s.resolution.kind === "resolved" ? s.resolution.source : null,
|
|
7791
|
-
profile: s.resolution.kind === "resolved" ? s.resolution.profile ?? null : null,
|
|
7792
|
-
credentials: s.credentials,
|
|
7793
|
-
// What to do next and how to put it to the person, so the caller never has to invent it.
|
|
7794
|
-
next: nextStep(s),
|
|
7795
|
-
// The discriminated resolution, so a caller can tell "no credential for a settled endpoint"
|
|
7796
|
-
// from "the profile you named does not exist" — both of which report credentials "missing".
|
|
7797
|
-
resolution: s.resolution,
|
|
7798
|
-
profiles: s.profiles,
|
|
7799
|
-
defaultProfile: s.defaultProfile,
|
|
7800
|
-
credentialsFile: credentialsPath(),
|
|
7801
|
-
tempDir: temp,
|
|
7802
|
-
association: association === null ? null : { endpoint: association.endpoint, repoId: association.repoId },
|
|
7803
|
-
// Printed as it comes: `moved` carries where to, `unreachable` carries why, and the two
|
|
7804
|
-
// need different next steps so they are not collapsed into "not ok".
|
|
7805
|
-
associationStatus: probe
|
|
7806
|
-
});
|
|
7807
|
-
return git ? 0 : 1;
|
|
7808
|
-
}
|
|
7809
|
-
/**
|
|
7810
|
-
* Re-point a folder whose service moved. The credential is not carried to the new address,
|
|
7811
|
-
* which the old one chose, so the person signs in there.
|
|
7812
|
-
*/
|
|
7813
|
-
case "migrate": {
|
|
7814
|
-
const dir = dirArg(1);
|
|
7815
|
-
const result = await migrateAssociation({ repoDir: dir, ...optStr(flags, "to") });
|
|
7816
|
-
const command = `publisher login --endpoint ${result.to} --json`;
|
|
7817
|
-
log(`re-pointed ${dir}: ${result.from} -> ${result.to}; sign in there with: ${command}`);
|
|
7818
|
-
out({ ...result, next: { action: "reauthenticate", ask: null, command } });
|
|
7819
|
-
return 0;
|
|
7820
|
-
}
|
|
7821
|
-
case "login":
|
|
7822
|
-
return login({ flags, json, out, log, input: await resolutionInput() });
|
|
7823
|
-
case "logout": {
|
|
7824
|
-
const profile = str2(flags["profile"]);
|
|
7825
|
-
const endpoint = str2(flags["endpoint"]) ?? process.env["GIT_PUBLISHER_ENDPOINT"];
|
|
7826
|
-
if (!profile && !endpoint) {
|
|
7827
|
-
throw new PublisherError("INVALID_REQUEST", "logout requires --endpoint or --profile");
|
|
7828
|
-
}
|
|
7829
|
-
const { revoked } = profile ? await removeProfile(profile, { revoke: true }) : await removeCredential(endpoint, { revoke: true });
|
|
7830
|
-
out({ ok: true, ...profile ? { profile } : { endpoint }, revoked });
|
|
7831
|
-
return 0;
|
|
7832
|
-
}
|
|
7833
|
-
case "profile": {
|
|
7834
|
-
const file = await readCredentials();
|
|
7835
|
-
const list = () => {
|
|
7836
|
-
const s = credentialStatus({ file, env: process.env });
|
|
7837
|
-
return { profiles: s.profiles, defaultProfile: s.defaultProfile, credentialsFile: credentialsPath() };
|
|
7838
|
-
};
|
|
7839
|
-
switch (cmd[1]) {
|
|
7840
|
-
case void 0:
|
|
7841
|
-
case "list":
|
|
7842
|
-
out(list());
|
|
7843
|
-
return 0;
|
|
7844
|
-
case "use": {
|
|
7845
|
-
const name = cmd[2];
|
|
7846
|
-
if (!name) {
|
|
7847
|
-
throw new PublisherError("INVALID_REQUEST", "profile use requires a name");
|
|
7848
|
-
}
|
|
7849
|
-
await setDefaultProfile(name);
|
|
7850
|
-
out({ ok: true, defaultProfile: name, endpoint: file.profiles[name].endpoint });
|
|
7851
|
-
return 0;
|
|
7852
|
-
}
|
|
7853
|
-
case "remove": {
|
|
7854
|
-
const name = cmd[2];
|
|
7855
|
-
if (!name) {
|
|
7856
|
-
throw new PublisherError("INVALID_REQUEST", "profile remove requires a name");
|
|
7857
|
-
}
|
|
7858
|
-
await removeProfile(name);
|
|
7859
|
-
out({ ok: true, removed: name });
|
|
7860
|
-
return 0;
|
|
7861
|
-
}
|
|
7862
|
-
default:
|
|
7863
|
-
throw new PublisherError("INVALID_REQUEST", `unknown profile command: ${cmd[1]}; expected list, use or remove`);
|
|
7864
|
-
}
|
|
7865
|
-
}
|
|
7866
|
-
case "init": {
|
|
7867
|
-
const dir = dirArg(1);
|
|
7868
|
-
const { p, endpoint } = await client();
|
|
7869
|
-
const repo = str2(flags["repo"]);
|
|
7870
|
-
const linked = await linkFolder(p, endpoint, dir, repo ? { repoId: repo } : {});
|
|
7871
|
-
out({ ok: true, ...linked, endpoint, dir });
|
|
7872
|
-
return 0;
|
|
7873
|
-
}
|
|
7874
|
-
case "publish": {
|
|
7875
|
-
let finishPublish2 = function(o) {
|
|
7876
|
-
out({
|
|
7877
|
-
state: o.state,
|
|
7878
|
-
publicationId: o.publicationId,
|
|
7879
|
-
repoId: o.repoId,
|
|
7880
|
-
generation: o.resultGeneration,
|
|
7881
|
-
refs: o.refs,
|
|
7882
|
-
rejection: o.rejection,
|
|
7883
|
-
resumed: o.resumed,
|
|
7884
|
-
...o.next === null ? {} : { next: o.next }
|
|
7885
|
-
});
|
|
7886
|
-
return o.state === "accepted" ? 0 : o.state === "rejected" ? 2 : 3;
|
|
7887
|
-
};
|
|
7888
|
-
var finishPublish = finishPublish2;
|
|
7889
|
-
const dir = dirArg(1);
|
|
7890
|
-
const { p } = await client(dir);
|
|
7891
|
-
const refs = Array.isArray(flags["ref"]) ? flags["ref"] : [];
|
|
7892
|
-
const force = flags["force"] === true;
|
|
7893
|
-
const updates = refs.length ? refs.map((r) => ({ ref: r.startsWith("refs/") ? r : `refs/heads/${r}`, force })) : void 0;
|
|
7894
|
-
if (!updates && force) {
|
|
7895
|
-
const b = await currentBranch(dir);
|
|
7896
|
-
if (!b) {
|
|
7897
|
-
throw new PublisherError("LOCAL_STATE", "detached HEAD");
|
|
7898
|
-
}
|
|
7899
|
-
return finishPublish2(await p.publish({ repoDir: dir, updates: [{ ref: b, force: true }], wait: flags["no-wait"] !== true }, { onProgress: progress }));
|
|
7900
|
-
}
|
|
7901
|
-
return finishPublish2(await p.publish({ repoDir: dir, ...updates ? { updates } : {}, wait: flags["no-wait"] !== true }, { onProgress: progress }));
|
|
7902
|
-
}
|
|
7903
|
-
case "status": {
|
|
7904
|
-
const dir = dirArg(1);
|
|
7905
|
-
const { p } = await client(dir);
|
|
7906
|
-
const pubId = str2(flags["publication"]);
|
|
7907
|
-
if (pubId) {
|
|
7908
|
-
const a = await readAssociation(dir);
|
|
7909
|
-
const repoId = str2(flags["repo"]) ?? a?.repoId;
|
|
7910
|
-
if (!repoId) {
|
|
7911
|
-
throw new PublisherError("LOCAL_STATE", "no repository association; pass --repo");
|
|
7912
|
-
}
|
|
7913
|
-
const s = await p.getPublicationStatus(repoId, pubId);
|
|
7914
|
-
out(s);
|
|
7915
|
-
return s.state === "accepted" ? 0 : s.state === "rejected" ? 2 : 3;
|
|
7916
|
-
}
|
|
7917
|
-
const pending = await p.pendingPublications(dir);
|
|
7918
|
-
if (pending.length === 0) {
|
|
7919
|
-
out({ pending: [], resolved: [] });
|
|
7920
|
-
return 0;
|
|
7921
|
-
}
|
|
7922
|
-
const resolved = await p.resumePublications(dir, { onProgress: progress });
|
|
7923
|
-
out({
|
|
7924
|
-
pending: pending.map((x) => x.publicationId),
|
|
7925
|
-
resolved: resolved.map((o) => ({
|
|
7926
|
-
publicationId: o.publicationId,
|
|
7927
|
-
state: o.state,
|
|
7928
|
-
generation: o.resultGeneration,
|
|
7929
|
-
rejection: o.rejection,
|
|
7930
|
-
...o.next === null ? {} : { next: o.next }
|
|
7931
|
-
}))
|
|
7932
|
-
});
|
|
7933
|
-
return resolved.every((o) => o.state === "accepted") ? 0 : resolved.some((o) => o.state === "rejected") ? 2 : 3;
|
|
7934
|
-
}
|
|
7935
|
-
case "fetch": {
|
|
7936
|
-
const dir = dirArg(1);
|
|
7937
|
-
const { p } = await client(dir);
|
|
7938
|
-
const r = await p.fetch({ repoDir: dir }, { onProgress: progress });
|
|
7939
|
-
out({ ok: true, repoId: r.repoId, generation: r.generation, remote: r.remote, refs: r.refs, tracking: r.tracking, bytes: r.bytes });
|
|
7940
|
-
return 0;
|
|
7941
|
-
}
|
|
7942
|
-
case "list":
|
|
7943
|
-
out(await listApps((await client()).p, { archived: flags["archived"] === true, cursor: str2(flags["cursor"]) }));
|
|
7944
|
-
return 0;
|
|
7945
|
-
case "checkout": {
|
|
7946
|
-
const dir = dirArg(1);
|
|
7947
|
-
const repoId = str2(flags["repo"]);
|
|
7948
|
-
if (!repoId) {
|
|
7949
|
-
throw new PublisherError("INVALID_REQUEST", "checkout requires --repo <id>");
|
|
7950
|
-
}
|
|
7951
|
-
const { p } = await client();
|
|
7952
|
-
const branch = str2(flags["branch"]);
|
|
7953
|
-
const m = await p.checkout({ directory: dir, repoId, ...branch ? { branch } : {} }, { onProgress: progress });
|
|
7954
|
-
out({ ok: true, workspace: m });
|
|
7955
|
-
return 0;
|
|
7956
|
-
}
|
|
7957
|
-
case "workspace-status": {
|
|
7958
|
-
const dir = dirArg(1);
|
|
7959
|
-
const { p } = await client(dir);
|
|
7960
|
-
const s = await p.workspaceStatus(dir, { onProgress: progress });
|
|
7961
|
-
out(s);
|
|
7962
|
-
return s.state === "conflict" ? 2 : 0;
|
|
7963
|
-
}
|
|
7964
|
-
/**
|
|
7965
|
-
* The creator's one verb: record the folder's changes, bring in what the service has, publish.
|
|
7966
|
-
* `--apply` is still accepted, so a `sync --apply` written down anywhere does the whole round
|
|
7967
|
-
* trip rather than failing.
|
|
7968
|
-
*/
|
|
7969
|
-
case "sync": {
|
|
7970
|
-
const dir = dirArg(1);
|
|
7971
|
-
const timeoutMs = followTimeoutMs(str2(flags["timeout"]));
|
|
7972
|
-
const clock = stopwatch();
|
|
7973
|
-
const { p, endpoint, source } = await client(dir);
|
|
7974
|
-
clock.lap("discovery");
|
|
7975
|
-
await resolveToken(endpoint, str2(flags["token"]), { endpointSource: source });
|
|
7976
|
-
clock.lap("authentication");
|
|
7977
|
-
const linked = await isGitRepo(dir) && await readAssociation(dir) !== null ? null : await linkFolder(p, endpoint, dir, {});
|
|
7978
|
-
clock.lap("link", linked !== null);
|
|
7979
|
-
const sending = splitAtBegin(clock, progress);
|
|
7980
|
-
const r = await p.syncChanges({ directory: dir, markConflicts: flags["mark-conflicts"] === true, ...optStr(flags, "message") }, { onProgress: sending.onProgress });
|
|
7981
|
-
const acceptedAt = sending.done();
|
|
7982
|
-
const o = r.published;
|
|
7983
|
-
const followed = o !== null && o.state === "accepted" && flags["no-wait"] !== true ? await followPublication(p, endpoint, o, { ...following, timeoutMs, dir }) : null;
|
|
7984
|
-
const syncTimings = clock.read(followed !== null && "stages" in followed ? runTimings(followed.stages, acceptedAt) : {});
|
|
7985
|
-
log(timingsLine(syncTimings));
|
|
7986
|
-
out({
|
|
7987
|
-
ok: r.state === "published" || r.state === "up_to_date",
|
|
7988
|
-
state: r.state,
|
|
7989
|
-
linked: linked === null ? null : { repoId: linked.repoId, appId: linked.appId, created: linked.created },
|
|
7990
|
-
recorded: r.recorded,
|
|
7991
|
-
merged: r.reconciled.applied,
|
|
7992
|
-
conflicts: r.reconciled.before.conflicts,
|
|
7993
|
-
marked: r.marked,
|
|
7994
|
-
publication: o === null ? null : {
|
|
7995
|
-
state: o.state,
|
|
7996
|
-
publicationId: o.publicationId,
|
|
7997
|
-
generation: o.resultGeneration,
|
|
7998
|
-
refs: o.refs,
|
|
7999
|
-
rejection: o.rejection,
|
|
8000
|
-
...o.next === null ? {} : { next: o.next }
|
|
8001
|
-
},
|
|
8002
|
-
hint: syncHint(r.state, r.reconciled.before.conflicts, r.marked),
|
|
8003
|
-
progress: followed,
|
|
8004
|
-
timings: syncTimings
|
|
8005
|
-
});
|
|
8006
|
-
return r.state === "published" || r.state === "up_to_date" ? 0 : r.state === "unknown" ? 3 : 2;
|
|
8007
|
-
}
|
|
8008
|
-
/**
|
|
8009
|
-
* Pick up following a run where a bounded `sync --timeout` (or an interrupted one) left it. The
|
|
8010
|
-
* ref and commit come from git-publisher's own record of the publication, so the command needs
|
|
8011
|
-
* only its id. Each call reports the stages as they now stand, from the first.
|
|
8012
|
-
*/
|
|
8013
|
-
case "follow": {
|
|
8014
|
-
const dir = dirArg(1);
|
|
8015
|
-
const publicationId = str2(flags["publication"]);
|
|
8016
|
-
if (!publicationId) {
|
|
8017
|
-
throw new PublisherError("INVALID_REQUEST", "follow requires --publication <id>");
|
|
8018
|
-
}
|
|
8019
|
-
const timeoutMs = followTimeoutMs(str2(flags["timeout"]));
|
|
8020
|
-
const { p, endpoint } = await client(dir);
|
|
8021
|
-
const repoId = str2(flags["repo"]) ?? (await readAssociation(dir))?.repoId;
|
|
8022
|
-
if (!repoId) {
|
|
8023
|
-
throw new PublisherError("LOCAL_STATE", "no repository association; pass --repo");
|
|
8024
|
-
}
|
|
8025
|
-
const publication = await p.getPublicationStatus(repoId, publicationId);
|
|
8026
|
-
if (publication.state !== "accepted") {
|
|
8027
|
-
throw new PublisherError("LOCAL_STATE", `publication ${publicationId} is ${publication.state}, not accepted; run publisher status`);
|
|
8028
|
-
}
|
|
8029
|
-
const clock = stopwatch();
|
|
8030
|
-
const followed = await followPublication(p, endpoint, publication, { ...following, timeoutMs, dir });
|
|
8031
|
-
const timings = clock.read("stages" in followed ? runTimings(followed.stages, null) : {});
|
|
8032
|
-
log(timingsLine(timings));
|
|
8033
|
-
out({ publicationId, progress: followed, timings });
|
|
8034
|
-
return 0;
|
|
8035
|
-
}
|
|
8036
|
-
/**
|
|
8037
|
-
* The business question the app's current preparation waits on, from this folder's repository.
|
|
8038
|
-
* Exit 0 whether or not one is pending: `questions` says which. An outage is exit 3, never "none".
|
|
8039
|
-
*/
|
|
8040
|
-
case "question": {
|
|
8041
|
-
const dir = dirArg(1);
|
|
8042
|
-
const { p, endpoint } = await client(dir);
|
|
8043
|
-
const repoId = str2(flags["repo"]) ?? (await readAssociation(dir))?.repoId;
|
|
8044
|
-
if (!repoId) {
|
|
8045
|
-
throw new PublisherError("LOCAL_STATE", "no repository association; pass --repo");
|
|
8046
|
-
}
|
|
8047
|
-
out({ repoId, ...await questionAt(p, endpoint, repoId) });
|
|
8048
|
-
return 0;
|
|
8049
|
-
}
|
|
8050
|
-
/** Answer it, as `question=value` pairs, re-reading the preparation first. */
|
|
8051
|
-
case "answer": {
|
|
8052
|
-
const dir = (0, import_node_path9.resolve)(".");
|
|
8053
|
-
const { p, endpoint } = await client(dir);
|
|
8054
|
-
const repoId = str2(flags["repo"]) ?? (await readAssociation(dir))?.repoId;
|
|
8055
|
-
if (!repoId) {
|
|
8056
|
-
throw new PublisherError("LOCAL_STATE", "no repository association; pass --repo");
|
|
8057
|
-
}
|
|
8058
|
-
out({ repoId, ...await answerAt(p, endpoint, repoId, cmd.slice(1)) });
|
|
8059
|
-
return 0;
|
|
8060
|
-
}
|
|
8061
|
-
case "skills": {
|
|
8062
|
-
if (cmd[1] !== "install") {
|
|
8063
|
-
throw new PublisherError("INVALID_REQUEST", `unknown skills command: ${cmd[1] ?? "(none)"}; expected install`);
|
|
8064
|
-
}
|
|
8065
|
-
const r = await installSkills({ log });
|
|
8066
|
-
out(r);
|
|
8067
|
-
return 0;
|
|
8068
|
-
}
|
|
8069
|
-
case "merge-check": {
|
|
8070
|
-
const dir = dirArg(1);
|
|
8071
|
-
const ours = str2(flags["ours"]);
|
|
8072
|
-
const theirs = str2(flags["theirs"]);
|
|
8073
|
-
if (!ours || !theirs) {
|
|
8074
|
-
throw new PublisherError("INVALID_REQUEST", "merge-check requires --ours and --theirs");
|
|
8075
|
-
}
|
|
8076
|
-
const r = await mergeRemote({
|
|
8077
|
-
repoDir: dir,
|
|
8078
|
-
ours: ours.startsWith("refs/") ? ours : `refs/heads/${ours}`,
|
|
8079
|
-
theirs,
|
|
8080
|
-
apply: flags["apply"] === true,
|
|
8081
|
-
...optStr(flags, "message")
|
|
8082
|
-
});
|
|
8083
|
-
out(r);
|
|
8084
|
-
return r.ok ? 0 : 2;
|
|
8085
|
-
}
|
|
8086
|
-
case "info": {
|
|
8087
|
-
const dir = dirArg(1);
|
|
8088
|
-
const { p } = await client(dir);
|
|
8089
|
-
const a = await readAssociation(dir);
|
|
8090
|
-
const repoId = str2(flags["repo"]) ?? a?.repoId;
|
|
8091
|
-
if (!repoId) {
|
|
8092
|
-
throw new PublisherError("LOCAL_STATE", "no repository association; pass --repo");
|
|
8093
|
-
}
|
|
8094
|
-
out(await p.getRepository(repoId));
|
|
8095
|
-
return 0;
|
|
8096
|
-
}
|
|
8097
|
-
default:
|
|
8098
|
-
process.stderr.write(`unknown command: ${cmd[0]}
|
|
8099
|
-
${USAGE}`);
|
|
8100
|
-
return 1;
|
|
8101
|
-
}
|
|
8102
|
-
}
|
|
8103
|
-
var GIT_COMMANDS = /* @__PURE__ */ new Set(["init", "publish", "status", "fetch", "checkout", "workspace-status", "sync", "merge-check"]);
|
|
8104
|
-
async function demand(r, input) {
|
|
8105
|
-
if (r.kind === "resolved") {
|
|
8106
|
-
return r;
|
|
8107
|
-
}
|
|
8108
|
-
const next = nextStep(credentialStatus(input));
|
|
8109
|
-
const details = { profiles: r.kind === "none" ? [] : r.profiles, next };
|
|
8110
|
-
const run = next.command === null ? "" : ` Run: ${next.command}`;
|
|
8111
|
-
switch (r.kind) {
|
|
8112
|
-
case "unknown-profile":
|
|
8113
|
-
throw new PublisherError("LOCAL_STATE", `no such profile: ${r.name}.${run}`, { details });
|
|
8114
|
-
case "ambiguous":
|
|
8115
|
-
throw new PublisherError("LOCAL_STATE", `several connections and none is the default: pass --profile <name>, or choose one once.${run}`, { details });
|
|
8116
|
-
case "none": {
|
|
8117
|
-
const where = await whereCredentialsWere();
|
|
8118
|
-
throw new PublisherError("LOCAL_STATE", `not connected to a publishing service yet: ${where.sentence}${run}`, { details: { ...details, credentialsFile: where.details } });
|
|
8119
|
-
}
|
|
8234
|
+
if (help || name === void 0 || name === "help") {
|
|
8235
|
+
process.stdout.write(USAGE);
|
|
8236
|
+
return 0;
|
|
8120
8237
|
}
|
|
8121
|
-
|
|
8122
|
-
|
|
8123
|
-
|
|
8124
|
-
|
|
8125
|
-
return "Your changes are saved and published.";
|
|
8126
|
-
case "up_to_date":
|
|
8127
|
-
return "Nothing new to publish: the service already has this version.";
|
|
8128
|
-
case "conflict":
|
|
8129
|
-
return marked ? `Both versions are now in ${conflicts.join(", ")}, between <<<<<<< and >>>>>>> lines. Edit each to what should be kept, remove those lines, then run publisher sync.` : `Your changes and the published version both changed ${conflicts.join(", ")}. Nothing was published and nothing was lost; decide which version to keep.`;
|
|
8130
|
-
case "rejected":
|
|
8131
|
-
return "The service refused the publication; see publication.rejection.";
|
|
8132
|
-
default:
|
|
8133
|
-
return "The publication was sent but its outcome is not known yet; run publisher status.";
|
|
8238
|
+
if (command === void 0) {
|
|
8239
|
+
process.stderr.write(`unknown command: ${name}
|
|
8240
|
+
${USAGE}`);
|
|
8241
|
+
return 1;
|
|
8134
8242
|
}
|
|
8135
|
-
|
|
8136
|
-
function formatHuman(v) {
|
|
8137
|
-
return JSON.stringify(v, null, 2);
|
|
8243
|
+
return command.run(inv);
|
|
8138
8244
|
}
|
|
8139
8245
|
for (const sig of ["SIGINT", "SIGTERM"]) {
|
|
8140
8246
|
process.once(sig, () => {
|