@minnovate/shadow-publisher 0.14.1 → 0.15.1

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.
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "git-publisher",
3
3
  "displayName": "Git Publisher",
4
- "version": "0.14.1",
4
+ "version": "0.15.1",
5
5
  "description": "Publish an app folder's Git history to the serverless Git publishing backend and keep it updated (\"publish my app\", \"update my app\", \"fetch the engineered version\").",
6
6
  "author": { "name": "git-publisher project" },
7
7
  "keywords": ["git", "publish", "deploy", "serverless"]
@@ -136,9 +136,9 @@ fast-forward or a clean merge — it never rewrites the user's work), and publis
136
136
  them when the person asks why it was slow. Write
137
137
  the message in the user's words ("Add a contact page"), not git's.
138
138
 
139
- A published app runs with no internet access: **it may not load anything from other sites** — no
140
- CDN scripts, web fonts or remote images. When you write or change an app, keep every file it needs
141
- in its folder. The checks refuse an app that does not (see "When the checks find something").
139
+ A published app runs with no internet access: **apps can’t load anything from other sites** — no
140
+ CDN scripts, web fonts or remote images — and can’t call other sites for data: connect the data as
141
+ a data source instead. When you write or change an app, keep every file it needs in its folder. The checks refuse an app that does not (see "When the checks find something").
142
142
  Keep it small too: each file at most 4 MB, the whole app at most 10 MB, at most 500 files —
143
143
  a larger app is refused before it is checked, and the refusal names the file or the limit it broke.
144
144
 
@@ -220,11 +220,17 @@ sentence, and `progress.next.action` is `"fix"`. Then:
220
220
  Their original app is still what is published; nothing broke.
221
221
  2. Fix every finding in the app folder, by its `kind`:
222
222
  - `external-resource`: the app loads `url` from another site (a CDN script, Google Fonts, an
223
- image). **Apps may not load anything from other sites.** Download the file into the app
223
+ image). **Apps can’t load anything from other sites.** Download the file into the app
224
224
  folder and point to it with a relative path, if it is at most 4 MB (a charting library
225
225
  such as Chart.js, about 200 KB, fits). Otherwise replace it with code of your own: a
226
226
  font falls back to a system font stack, a chart is drawn as inline SVG. Never keep the
227
227
  external address.
228
+ - `external-request`: the app CALLS `url` on another site for data (`fetch`, an API).
229
+ **Apps can't reach other sites.** Do not copy anything: remove the call, or bring the data
230
+ into the app as a data source (the `data_sources` tool, or `publisher data add`), and read
231
+ it from there.
232
+ - `server-start`: the app's server stopped before it answered `/`. `message` is the error it
233
+ printed and where. Fix that line, and run the server with `PORT` set to try it.
228
234
  - `console-error` / `application-error`: fix the code that logs or throws `message`.
229
235
  - `failed-request`: the app asks for `path` on its own site and gets `status`; add the file or
230
236
  stop requesting it.
@@ -42,6 +42,17 @@ A value that is not an option is refused before anything is sent; `error.details
42
42
  the choices. Exit 4 means this sign-in may not answer (an owner or admin can); exit 3 means ask
43
43
  again. Then `publisher follow . --publication <id> --until-change --timeout 100 --json` to see it go live, telling them each step as `publish-app` step 4 says.
44
44
 
45
+ ## When it did not go live because of a fault on our side
46
+
47
+ When `publisher question . --json` shows a `blocked` preparation whose `message` says the fault
48
+ was on our side, not in the app, do not change the app. Try it again once:
49
+
50
+ ```bash
51
+ publisher retry . --json
52
+ ```
53
+
54
+ Then follow it again. If it fails a second time, tell the person, and give them the portal page.
55
+
45
56
  ## After it is live
46
57
 
47
58
  These run in the app's folder and answer `say` (tell the person) and `next` (what to do now):
@@ -5417,6 +5417,16 @@ function ending(stages) {
5417
5417
  return "stopped";
5418
5418
  }
5419
5419
  var POLL_INTERVAL_MS = 5e3;
5420
+ var MAX_REASON = 1e3;
5421
+ function settled(run2, stages, end) {
5422
+ const runId = isRecord(run2) && typeof run2["runId"] === "string" ? run2["runId"] : null;
5423
+ const failedAt = stages.find((s) => s.state === "failed")?.word ?? null;
5424
+ return { kind: "settled", ending: end, failedAt, stages, runId, reason: reasonOf(run2, end) };
5425
+ }
5426
+ function reasonOf(run2, end) {
5427
+ const summary = isRecord(run2) ? run2["summary"] : null;
5428
+ return end === "failed" && typeof summary === "string" && summary.trim() !== "" ? summary.trim().slice(0, MAX_REASON) : null;
5429
+ }
5420
5430
  var FOLLOW_TIMEOUT_MS = 15 * 6e4;
5421
5431
  async function followRun(input, opts) {
5422
5432
  const portal = new PortalSession(opts);
@@ -5469,8 +5479,7 @@ async function followRun(input, opts) {
5469
5479
  }
5470
5480
  const end = ending(read2.stages);
5471
5481
  if (end !== null) {
5472
- const runId = isRecord(run2.run) && typeof run2.run["runId"] === "string" ? run2.run["runId"] : null;
5473
- return { kind: "settled", ending: end, failedAt: read2.stages.find((s) => s.state === "failed")?.word ?? null, stages: read2.stages, runId };
5482
+ return settled(run2.run, read2.stages, end);
5474
5483
  }
5475
5484
  if (opts.untilChange === true && moved && answered) {
5476
5485
  return { kind: "moving", stages: read2.stages };
@@ -5540,6 +5549,9 @@ function readPreparation(input, opts) {
5540
5549
  function answerPreparation(input, opts) {
5541
5550
  return call(input, opts, { id: input.id, revision: input.revision, action: "answers", answers: input.answers });
5542
5551
  }
5552
+ function retryPreparation(input, opts) {
5553
+ return call(input, opts, { id: input.id, revision: input.revision, action: "retry" });
5554
+ }
5543
5555
  async function call(input, opts, body) {
5544
5556
  const url2 = `${input.portal.replace(/\/+$/, "")}/api/v1/terminal/repositories/${encodeURIComponent(input.repoId)}/preparation`;
5545
5557
  const answer2 = await new PortalSession(opts).ask(url2, body);
@@ -5599,7 +5611,16 @@ function readView(body) {
5599
5611
  if (isRecord(publication) && typeof publication["status"] === "string" && typeof publication["message"] === "string") {
5600
5612
  pub = { status: publication["status"], message: publication["message"], ...typeof publication["url"] === "string" ? { url: publication["url"] } : {} };
5601
5613
  }
5602
- return { id, revision, status: status2, message, questions: read2, publication: pub, findings: readFindings(body["findings"]) };
5614
+ return {
5615
+ id,
5616
+ revision,
5617
+ status: status2,
5618
+ message,
5619
+ questions: read2,
5620
+ publication: pub,
5621
+ findings: readFindings(body["findings"]),
5622
+ canRetry: body["canRetry"] === true
5623
+ };
5603
5624
  }
5604
5625
  function readFindings(value) {
5605
5626
  if (!Array.isArray(value)) {
@@ -6445,6 +6466,10 @@ var Publisher = class {
6445
6466
  async answerPreparation(input) {
6446
6467
  return answerPreparation(input, this.portalCall());
6447
6468
  }
6469
+ /** The repository's current preparation tried again, when it could not finish because of a fault on our side. */
6470
+ async retryPreparation(input) {
6471
+ return retryPreparation(input, this.portalCall());
6472
+ }
6448
6473
  /**
6449
6474
  * One of the creator's tools at the portal that serves this endpoint — starters, status, history,
6450
6475
  * going back, suggestions, sharing — with this publisher's credential and its one refresh. The
@@ -7080,7 +7105,7 @@ async function followPublication(p, endpoint, o, opts) {
7080
7105
  return {
7081
7106
  ...outcome2,
7082
7107
  portal,
7083
- 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.`,
7108
+ hint: `It didn\u2019t 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.`,
7084
7109
  findings,
7085
7110
  next: { action: "fix", command: `publisher sync ${dir} --until-change --json` }
7086
7111
  };
@@ -7153,7 +7178,7 @@ function progressHint(r, portal, answerable = false) {
7153
7178
  case "not-deployed":
7154
7179
  return "Published and checked; this organisation does not deploy automatically.";
7155
7180
  case "failed":
7156
- return inPortal(portal, `It stopped at "${r.failedAt ?? "a step"}".`, "See why");
7181
+ return inPortal(portal, `It stopped at "${r.failedAt ?? "a step"}".${r.reason === null ? "" : ` ${r.reason}`}`, r.reason === null ? "See why" : "See it");
7157
7182
  case "stopped":
7158
7183
  return "A newer publication replaced this one before it went live.";
7159
7184
  }
@@ -7865,6 +7890,20 @@ async function answerAt(p, endpoint, repoId, specs, opts = {}) {
7865
7890
  }
7866
7891
  return shown(at.portal, await p.answerPreparation({ portal: at.portal, repoId, id: read2.preparation.id, revision: read2.preparation.revision, answers }));
7867
7892
  }
7893
+ async function retryAt(p, endpoint, repoId, opts = {}) {
7894
+ const read2 = await questionAt(p, endpoint, repoId, opts);
7895
+ if (read2.kind !== "view") {
7896
+ return read2;
7897
+ }
7898
+ if (read2.preparation.status !== "blocked") {
7899
+ throw new PublisherError("LOCAL_STATE", `nothing to try again (the preparation is ${read2.preparation.status})`);
7900
+ }
7901
+ if (!read2.preparation.canRetry) {
7902
+ throw new PublisherError("LOCAL_STATE", "this version cannot be tried again: only one that stopped because of a fault on our side can, three times at most");
7903
+ }
7904
+ const tried = shown(read2.portal, await p.retryPreparation({ portal: read2.portal, repoId, id: read2.preparation.id, revision: read2.preparation.revision }));
7905
+ return tried.kind === "view" ? { ...tried, hint: "It is being tried again. Run: publisher follow, or watch it in the portal." } : tried;
7906
+ }
7868
7907
  function parseSpecs(specs) {
7869
7908
  if (specs.length === 0) {
7870
7909
  throw new PublisherError("INVALID_REQUEST", "answer takes one or more question=value");
@@ -7951,6 +7990,16 @@ var question = {
7951
7990
  return 0;
7952
7991
  }
7953
7992
  };
7993
+ var retry = {
7994
+ git: false,
7995
+ async run(inv) {
7996
+ const dir = inv.dirArg(1);
7997
+ const { p, endpoint } = await inv.client(dir);
7998
+ const repoId = await inv.repoId(dir);
7999
+ inv.out({ repoId, ...await retryAt(p, endpoint, repoId) });
8000
+ return 0;
8001
+ }
8002
+ };
7954
8003
  var answer = {
7955
8004
  git: false,
7956
8005
  async run(inv) {
@@ -8441,6 +8490,8 @@ Usage:
8441
8490
  with each option's value (answering needs owner or admin)
8442
8491
  publisher answer <question>=<value>... [--repo <id>] answer it from this folder; a value that is not one of
8443
8492
  the question's options is refused before anything is sent
8493
+ publisher retry [dir] [--repo <id>] try the app's newest version again, when it did not go
8494
+ live because of a fault on our side (three tries at most)
8444
8495
  publisher starters [what you want to stop doing] working apps to start from, the ones that fit first
8445
8496
  publisher start <starter> [--name <n>] [--replaces <what>] a new app made from a starter, live in about a minute
8446
8497
  publisher history this folder's app: the versions that went live, newest first
@@ -8497,6 +8548,7 @@ var COMMANDS = /* @__PURE__ */ new Map([
8497
8548
  ["follow", follow],
8498
8549
  ["question", question],
8499
8550
  ["answer", answer],
8551
+ ["retry", retry],
8500
8552
  ["skills", skills],
8501
8553
  ["merge-check", mergeCheck2],
8502
8554
  ["info", info],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@minnovate/shadow-publisher",
3
- "version": "0.14.1",
3
+ "version": "0.15.1",
4
4
  "description": "publisher: publish an app's Git history to the serverless Git backend",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",