@hardfin/cli 0.1.0-dev.26 → 0.1.0-dev.28

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/README.md CHANGED
@@ -310,6 +310,7 @@ hardfin status --offline # no network calls at all
310
310
  | `credential` | Whether you are signed in, where the refresh token is kept, and when it was issued and last renewed |
311
311
  | `authorizationServer` | Whether discovery worked, and every endpoint it named |
312
312
  | `api` | Whether the API accepted the credential, when the access token expires, and its scopes |
313
+ | `telemetry` | Whether defects are reported, the setting that decided it, and where reports go |
313
314
 
314
315
  Nothing secret is printed. An API key and a refresh token are each reported as a
315
316
  `sha256:` fingerprint, which identifies a credential across two machines without disclosing
@@ -319,6 +320,35 @@ it.
319
320
  the server reported one falls back to Hardfin's 90-day rule, and `refreshExpiryIsEstimated`
320
321
  says which of the two you are looking at.
321
322
 
323
+ ## Error reports
324
+
325
+ When the CLI fails because of a defect in the CLI itself, it reports the error to the Hardfin
326
+ team through Sentry. It then tells you it did, with a reference to quote.
327
+
328
+ Only defects are reported. None of these are:
329
+
330
+ - an error the API answered, including a 500, which the server records itself
331
+ - a network failure, or a server that cannot be reached
332
+ - a mistake on the command line or in a settings file
333
+ - being signed out, or a sign in that was cancelled or refused
334
+ - the machine refusing a file, such as a full disk or a missing permission
335
+
336
+ A report carries the error, its stack trace, and what `hardfin status` would print at that
337
+ moment. The command is named, and its flags are listed by name without their values.
338
+
339
+ A report never carries tokens, API keys, argument or flag values, request or response bodies,
340
+ people's or organizations' names, your hostname, or your home directory. A person and an
341
+ organization appear only as their Hardfin keys.
342
+
343
+ | To | Set |
344
+ | ------------------------------------------------- | ------------------------------------------ |
345
+ | Stop reporting | `DO_NOT_TRACK=1`, or `HARDFIN_TELEMETRY=0` |
346
+ | Report from a checkout, which does not by default | `HARDFIN_TELEMETRY=1` |
347
+ | Send reports to another Sentry project | `HARDFIN_SENTRY_DSN` |
348
+
349
+ `hardfin status` shows which of these is in effect. `hardfin __crash` fails on purpose, to
350
+ check that a report arrives.
351
+
322
352
  ## Local configuration
323
353
 
324
354
  A local build reaches a local server without editing code. Four layers supply the same
@@ -424,6 +454,12 @@ The workflow picks the preview version itself. It reads `version` from `package.
424
454
  if that version is already on npm it increments the patch, so a preview always sorts after
425
455
  the last release.
426
456
 
457
+ Before publishing, the workflow stamps each file in `dist` and its source map with a matching
458
+ debug ID, and uploads the maps to Sentry under the release `@hardfin/cli@<version>`. An error
459
+ report's stack trace then reads as the TypeScript source. The maps are not in the package, and
460
+ publishing skips `prepublishOnly`, so the stamped files are the ones published. The upload
461
+ needs the `SENTRY_AUTH_TOKEN` secret.
462
+
427
463
  ### Cut a release
428
464
 
429
465
  1. Open a pull request against `dev` that sets `version` in `package.json` to the version
package/dist/cli.js CHANGED
@@ -1,4 +1,6 @@
1
1
  #!/usr/bin/env node
2
+
3
+ !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="497b0264-fbd1-51d6-bd23-fb6c7eed397b")}catch(e){}}();
2
4
  import module from "node:module";
3
5
  //#region src/cli.ts
4
6
  module.enableCompileCache?.();
@@ -6,11 +8,31 @@ process.stdout.on("error", (error) => {
6
8
  if (error.code === "EPIPE") process.exit(0);
7
9
  throw error;
8
10
  });
9
- const answer = await toQuickAnswer(process.argv.slice(2));
10
- if (answer === void 0) {
11
- const { toCli } = await import("./program-C_9qi3K1.js");
12
- await toCli().parseAsync(process.argv);
13
- } else process.stdout.write(`${answer}\n`);
11
+ let isCrashing = false;
12
+ process.on("uncaughtException", (error) => void toCrash(error));
13
+ process.on("unhandledRejection", (error) => void toCrash(error));
14
+ try {
15
+ const answer = await toQuickAnswer(process.argv.slice(2));
16
+ if (answer === void 0) {
17
+ const { toCli, wasReportAttempted } = await import("./program-C3SGdPbf.js");
18
+ await toCli().parseAsync(process.argv);
19
+ if (wasReportAttempted()) process.exit();
20
+ } else process.stdout.write(`${answer}\n`);
21
+ } catch (error) {
22
+ await toCrash(error);
23
+ }
24
+ /** toCrash ends the process for an error nothing caught, once it has been reported. */
25
+ async function toCrash(error) {
26
+ if (isCrashing) return;
27
+ isCrashing = true;
28
+ process.stderr.write(`error: ${error instanceof Error ? error.message : String(error)}\n`);
29
+ try {
30
+ const { toReported } = await import("./program-C3SGdPbf.js");
31
+ await toReported(error, false, void 0, "fatal");
32
+ } finally {
33
+ process.exit(1);
34
+ }
35
+ }
14
36
  /**
15
37
  * toQuickAnswer answers a completion script without loading zod or the commands, which is
16
38
  * most of what a start costs, because a Tab starts the CLI every time.
@@ -21,3 +43,6 @@ async function toQuickAnswer(args) {
21
43
  }
22
44
  //#endregion
23
45
  export {};
46
+
47
+ //# sourceMappingURL=cli.js.map
48
+ //# debugId=497b0264-fbd1-51d6-bd23-fb6c7eed397b
@@ -1,4 +1,6 @@
1
1
  #!/usr/bin/env node
2
+
3
+ !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="a0e4ab3e-9a55-518b-a49b-37189993952e")}catch(e){}}();
2
4
  import { n as toCompletion, t as toAnswer } from "./completion-tree-rR_Rmeij.js";
3
5
  import { readFileSync } from "node:fs";
4
6
  //#region src/command/completion-quick.ts
@@ -18,3 +20,6 @@ function toQuickAnswer(words, location = new URL("./completion-tree.json", impor
18
20
  }
19
21
  //#endregion
20
22
  export { toQuickAnswer };
23
+
24
+ //# sourceMappingURL=completion-quick-BJHDp8OL.js.map
25
+ //# debugId=a0e4ab3e-9a55-518b-a49b-37189993952e
@@ -1,6 +1,8 @@
1
1
  #!/usr/bin/env node
2
2
  //#region src/command/completion-tree.ts
3
3
  /** Directive is the last line of an answer, which tells the script what to do beyond the candidates. */
4
+
5
+ !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="b5b3cdcd-37e1-50f6-880a-13e965b5e7ed")}catch(e){}}();
4
6
  const Directive = {
5
7
  NONE: ":0",
6
8
  FILES: ":1"
@@ -158,3 +160,6 @@ function toLine(description) {
158
160
  }
159
161
  //#endregion
160
162
  export { toCompletion as n, toAnswer as t };
163
+
164
+ //# sourceMappingURL=completion-tree-rR_Rmeij.js.map
165
+ //# debugId=b5b3cdcd-37e1-50f6-880a-13e965b5e7ed
@@ -2565,6 +2565,15 @@
2565
2565
  }
2566
2566
  ],
2567
2567
  "subcommands": []
2568
+ },
2569
+ {
2570
+ "name": "__crash",
2571
+ "summary": "Fail on purpose, to check that defects are reported",
2572
+ "hidden": true,
2573
+ "runs": true,
2574
+ "flags": [],
2575
+ "arguments": [],
2576
+ "subcommands": []
2568
2577
  }
2569
2578
  ]
2570
2579
  }