@smoothbricks/cli 0.11.16 → 0.11.18

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.
Files changed (79) hide show
  1. package/README.md +65 -4
  2. package/dist/cli.d.ts.map +1 -1
  3. package/dist/cli.js +47 -0
  4. package/dist/github-ci/index.d.ts +1 -1
  5. package/dist/github-ci/index.d.ts.map +1 -1
  6. package/dist/github-ci/index.js +7 -10
  7. package/dist/lib/secret-names.d.ts +35 -0
  8. package/dist/lib/secret-names.d.ts.map +1 -0
  9. package/dist/lib/secret-names.js +61 -0
  10. package/dist/monorepo/ci-workflow.d.ts +56 -0
  11. package/dist/monorepo/ci-workflow.d.ts.map +1 -1
  12. package/dist/monorepo/ci-workflow.js +214 -8
  13. package/dist/monorepo/managed-files.d.ts +23 -0
  14. package/dist/monorepo/managed-files.d.ts.map +1 -1
  15. package/dist/monorepo/managed-files.js +39 -1
  16. package/dist/monorepo/publish-workflow.d.ts +10 -1
  17. package/dist/monorepo/publish-workflow.d.ts.map +1 -1
  18. package/dist/monorepo/publish-workflow.js +58 -20
  19. package/dist/release/github-release.d.ts +10 -0
  20. package/dist/release/github-release.d.ts.map +1 -1
  21. package/dist/release/github-release.js +11 -5
  22. package/dist/release/index.d.ts.map +1 -1
  23. package/dist/release/index.js +4 -5
  24. package/dist/secrets/commands.d.ts +84 -0
  25. package/dist/secrets/commands.d.ts.map +1 -0
  26. package/dist/secrets/commands.js +375 -0
  27. package/dist/secrets/index.d.ts +103 -0
  28. package/dist/secrets/index.d.ts.map +1 -0
  29. package/dist/secrets/index.js +255 -0
  30. package/dist/secrets/repository.d.ts +60 -0
  31. package/dist/secrets/repository.d.ts.map +1 -0
  32. package/dist/secrets/repository.js +145 -0
  33. package/dist/wrangler/cloudflare.d.ts +7 -0
  34. package/dist/wrangler/cloudflare.d.ts.map +1 -1
  35. package/dist/wrangler/cloudflare.js +10 -0
  36. package/dist/wrangler/deploy-stage.d.ts +13 -2
  37. package/dist/wrangler/deploy-stage.d.ts.map +1 -1
  38. package/dist/wrangler/deploy-stage.js +89 -78
  39. package/dist/wrangler/deployed-version.d.ts +44 -0
  40. package/dist/wrangler/deployed-version.d.ts.map +1 -0
  41. package/dist/wrangler/deployed-version.js +87 -0
  42. package/dist/wrangler/live-version.d.ts +146 -0
  43. package/dist/wrangler/live-version.d.ts.map +1 -0
  44. package/dist/wrangler/live-version.js +326 -0
  45. package/dist/wrangler/stage-secrets.d.ts +57 -0
  46. package/dist/wrangler/stage-secrets.d.ts.map +1 -0
  47. package/dist/wrangler/stage-secrets.js +178 -0
  48. package/managed/raw/tooling/direnv/devenv.smoo.nix +9 -1
  49. package/managed/raw/tooling/git-hooks/pre-push.sh +30 -29
  50. package/package.json +2 -2
  51. package/src/cli.ts +69 -1
  52. package/src/github-ci/index.test.ts +92 -8
  53. package/src/github-ci/index.ts +7 -10
  54. package/src/lib/secret-names.ts +70 -0
  55. package/src/monorepo/__tests__/ci-workflow.test.ts +188 -2
  56. package/src/monorepo/__tests__/publish-workflow.test.ts +33 -15
  57. package/src/monorepo/ci-workflow.ts +294 -8
  58. package/src/monorepo/managed-files.test.ts +28 -1
  59. package/src/monorepo/managed-files.ts +56 -2
  60. package/src/monorepo/package-policy.test.ts +1 -1
  61. package/src/monorepo/publish-workflow.ts +65 -19
  62. package/src/monorepo/secret-references.test.ts +1 -1
  63. package/src/release/__tests__/github-release.test.ts +8 -4
  64. package/src/release/__tests__/private-npm-status.test.ts +2 -2
  65. package/src/release/github-release.ts +13 -5
  66. package/src/release/index.ts +4 -4
  67. package/src/secrets/commands.test.ts +28 -0
  68. package/src/secrets/commands.ts +412 -0
  69. package/src/secrets/index.test.ts +209 -0
  70. package/src/secrets/index.ts +287 -0
  71. package/src/secrets/repository.test.ts +98 -0
  72. package/src/secrets/repository.ts +164 -0
  73. package/src/wrangler/cloudflare.ts +18 -0
  74. package/src/wrangler/deploy-stage.test.ts +448 -23
  75. package/src/wrangler/deploy-stage.ts +142 -83
  76. package/src/wrangler/deployed-version.test.ts +150 -0
  77. package/src/wrangler/deployed-version.ts +130 -0
  78. package/src/wrangler/live-version.ts +363 -0
  79. package/src/wrangler/stage-secrets.ts +146 -0
@@ -42,6 +42,16 @@ export declare function nxProjectChangelogArgs(pkg: ReleasePackageInfo, previous
42
42
  dryRun: boolean;
43
43
  firstRelease: boolean;
44
44
  };
45
+ /**
46
+ * The release body for a version that carries no commits of its own.
47
+ *
48
+ * Nx bumps a project when a dependency it depends on was bumped, and reports
49
+ * that reason itself ("because a dependency was bumped"). Such a version has no
50
+ * commits touching the project, so Nx generates no changelog for it — which is
51
+ * correct, not a failure. Treating the absence as fatal made one dependency-only
52
+ * package abort a whole publish after the versions had already been written.
53
+ */
54
+ export declare const DEPENDENCY_ONLY_RELEASE_NOTES = "_No changelog entries for this package: this version was released because one of its dependencies was bumped._";
45
55
  export declare function projectChangelogContents(result: ProjectChangelogLookupResult, projectName: string): string;
46
56
  export declare function githubReleaseLookupExists(tag: string, exitCode: number, stdout: string, stderr: string): boolean;
47
57
  export declare function githubReleaseCommandArgs(tag: string, notesFile: string, releaseExists: boolean, version: string): string[];
@@ -1 +1 @@
1
- {"version":3,"file":"github-release.d.ts","sourceRoot":"","sources":["../../src/release/github-release.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,KAAK,kBAAkB,EAAc,MAAM,WAAW,CAAC;AAYhE,UAAU,4BAA4B;IACpC,iBAAiB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAC1D;AAED,MAAM,WAAW,6BAA6B,CAAC,OAAO,SAAS,kBAAkB,GAAG,kBAAkB;IACpG,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,OAAO,CAAC;IACb,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,MAAM,EAAE,OAAO,CAAC;CACjB;AAED,MAAM,WAAW,uBAAuB;IACtC,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACnD,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5C,GAAG,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B;AAED,wBAAsB,gCAAgC,CAAC,KAAK,EAAE,6BAA6B,GAAG,OAAO,CAAC,MAAM,CAAC,CAS5G;AAED,wBAAsB,2BAA2B,CAC/C,GAAG,EAAE,kBAAkB,EACvB,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,uBAAuB,GAC7B,OAAO,CAAC,IAAI,CAAC,CAaf;AAED,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,kBAAkB,EAAE,WAAW,EAAE,MAAM,GAAG,IAAI,EAAE,MAAM,EAAE,OAAO;;;;;;;;;;;;;;;;;;;;;;;;EAc1G;AAED,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,4BAA4B,EAAE,WAAW,EAAE,MAAM,GAAG,MAAM,CAM1G;AAED,wBAAgB,yBAAyB,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAShH;AAED,wBAAgB,wBAAwB,CACtC,GAAG,EAAE,MAAM,EACX,SAAS,EAAE,MAAM,EACjB,aAAa,EAAE,OAAO,EACtB,OAAO,EAAE,MAAM,GACd,MAAM,EAAE,CAgBV;AAMD;;;;;;;;;GASG;AACH,UAAU,kBAAkB;IAC1B,6BAA6B,EAAE,cAAc,gCAAgC,CAAC,CAAC;IAC/E,qCAAqC,EAAE,cAAc,wCAAwC,CAAC,CAAC;IAC/F,uCAAuC,EAAE,cAAc,0CAA0C,CAAC,CAAC;CACpG;AAED,wBAAsB,iBAAiB,CAAC,OAAO,SAAS,MAAM,kBAAkB,EAC9E,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,OAAO,GACf,OAAO,CAAC,kBAAkB,CAAC,OAAO,CAAC,CAAC,CAMtC;AAED,wBAAsB,mBAAmB,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAmB5F"}
1
+ {"version":3,"file":"github-release.d.ts","sourceRoot":"","sources":["../../src/release/github-release.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,KAAK,kBAAkB,EAAc,MAAM,WAAW,CAAC;AAYhE,UAAU,4BAA4B;IACpC,iBAAiB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAC1D;AAED,MAAM,WAAW,6BAA6B,CAAC,OAAO,SAAS,kBAAkB,GAAG,kBAAkB;IACpG,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,OAAO,CAAC;IACb,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,MAAM,EAAE,OAAO,CAAC;CACjB;AAED,MAAM,WAAW,uBAAuB;IACtC,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACnD,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5C,GAAG,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B;AAED,wBAAsB,gCAAgC,CAAC,KAAK,EAAE,6BAA6B,GAAG,OAAO,CAAC,MAAM,CAAC,CAS5G;AAED,wBAAsB,2BAA2B,CAC/C,GAAG,EAAE,kBAAkB,EACvB,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,uBAAuB,GAC7B,OAAO,CAAC,IAAI,CAAC,CAaf;AAED,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,kBAAkB,EAAE,WAAW,EAAE,MAAM,GAAG,IAAI,EAAE,MAAM,EAAE,OAAO;;;;;;;;;;;;;;;;;;;;;;;;EAc1G;AAED;;;;;;;;GAQG;AACH,eAAO,MAAM,6BAA6B,mHACwE,CAAC;AAEnH,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,4BAA4B,EAAE,WAAW,EAAE,MAAM,GAAG,MAAM,CAE1G;AAED,wBAAgB,yBAAyB,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAShH;AAED,wBAAgB,wBAAwB,CACtC,GAAG,EAAE,MAAM,EACX,SAAS,EAAE,MAAM,EACjB,aAAa,EAAE,OAAO,EACtB,OAAO,EAAE,MAAM,GACd,MAAM,EAAE,CAgBV;AAMD;;;;;;;;;GASG;AACH,UAAU,kBAAkB;IAC1B,6BAA6B,EAAE,cAAc,gCAAgC,CAAC,CAAC;IAC/E,qCAAqC,EAAE,cAAc,wCAAwC,CAAC,CAAC;IAC/F,uCAAuC,EAAE,cAAc,0CAA0C,CAAC,CAAC;CACpG;AAED,wBAAsB,iBAAiB,CAAC,OAAO,SAAS,MAAM,kBAAkB,EAC9E,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,OAAO,GACf,OAAO,CAAC,kBAAkB,CAAC,OAAO,CAAC,CAAC,CAMtC;AAED,wBAAsB,mBAAmB,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAmB5F"}
@@ -49,12 +49,18 @@ export function nxProjectChangelogArgs(pkg, previousTag, dryRun) {
49
49
  };
50
50
  return previousTag ? { ...base, from: previousTag } : { ...base, firstRelease: true };
51
51
  }
52
+ /**
53
+ * The release body for a version that carries no commits of its own.
54
+ *
55
+ * Nx bumps a project when a dependency it depends on was bumped, and reports
56
+ * that reason itself ("because a dependency was bumped"). Such a version has no
57
+ * commits touching the project, so Nx generates no changelog for it — which is
58
+ * correct, not a failure. Treating the absence as fatal made one dependency-only
59
+ * package abort a whole publish after the versions had already been written.
60
+ */
61
+ export const DEPENDENCY_ONLY_RELEASE_NOTES = '_No changelog entries for this package: this version was released because one of its dependencies was bumped._';
52
62
  export function projectChangelogContents(result, projectName) {
53
- const changelog = result.projectChangelogs?.[projectName];
54
- if (!changelog) {
55
- throw new Error(`Nx did not generate a project changelog for ${projectName}.`);
56
- }
57
- return changelog.contents;
63
+ return result.projectChangelogs?.[projectName]?.contents ?? DEPENDENCY_ONLY_RELEASE_NOTES;
58
64
  }
59
65
  export function githubReleaseLookupExists(tag, exitCode, stdout, stderr) {
60
66
  if (exitCode === 0) {
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/release/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/release/index.ts"],"names":[],"mappings":"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"}
@@ -23,7 +23,7 @@ import { bootstrapNpmPackages, NPM_BOOTSTRAP_DIST_TAG, NPM_BOOTSTRAP_VERSION, }
23
23
  import { resolveBuildInputPatterns } from './build-inputs.js';
24
24
  import { parseReleaseProjectSelection, releaseCandidatePackages, } from './candidates.js';
25
25
  import { classifyReleaseBranchPush, collectOwnedReleaseTagRecords, divergedReleaseBranchMessage, pendingReleaseTargets, releaseTag, releaseTagAliases, } from './core.js';
26
- import { createOrUpdateGithubRelease, githubReleaseLookupExists, importWorkspaceNx, renderNxProjectChangelogContents, withNxWorkspaceRoot, } from './github-release.js';
26
+ import { createOrUpdateGithubRelease, DEPENDENCY_ONLY_RELEASE_NOTES, githubReleaseLookupExists, importWorkspaceNx, renderNxProjectChangelogContents, withNxWorkspaceRoot, } from './github-release.js';
27
27
  import { publishWithAuthDiagnostics } from './npm-auth.js';
28
28
  import { bumpStableReleaseToNext, collectRepairPlatformOutputs, completeReleaseAtHead as completeReleaseAtHeadWithShell, prepareReleaseCandidate, repairPendingTargets, runReleaseTag, runReleaseVersion, } from './orchestration.js';
29
29
  import { packReleaseTarball } from './pack.js';
@@ -1128,10 +1128,9 @@ async function writeReleasePreviewSummary(root, currentPackages, previewPackages
1128
1128
  lines.push(` - \`${pkg.name}\`: \`${currentByName.get(pkg.name)?.version ?? 'unknown'}\` -> \`${pkg.version}\``);
1129
1129
  }
1130
1130
  for (const pkg of previewPackages) {
1131
- const contents = releasePreviewChangelogs.get(pkg.projectName);
1132
- if (!contents) {
1133
- throw new Error(`Nx did not generate a project changelog for ${pkg.projectName}.`);
1134
- }
1131
+ // A dependency-only bump has no commits of its own, so Nx generates no
1132
+ // changelog for it. The preview says so rather than refusing to render.
1133
+ const contents = releasePreviewChangelogs.get(pkg.projectName) ?? DEPENDENCY_ONLY_RELEASE_NOTES;
1135
1134
  lines.push('', `### ${pkg.name} ${pkg.version}`, '', contents.trim());
1136
1135
  }
1137
1136
  }
@@ -0,0 +1,84 @@
1
+ /**
2
+ * `smoo secrets` — the operator side of the reconciliation in ./index.ts.
3
+ *
4
+ * Values are handed to `gh` over stdin, never through argv (a process list is
5
+ * world-readable) and never printed. Reading one from the terminal disables
6
+ * echo, so a pasted key does not end up in a scrollback buffer or a screen
7
+ * share.
8
+ *
9
+ * A value lives at repository scope or inside a named GitHub Environment, and
10
+ * a job bound to an environment resolves `secrets.X` from that environment
11
+ * first and from the repository second — which is how one name carries test
12
+ * credentials on a preview stage and live ones in production. Every scope the
13
+ * workflows bind is read before anything is called missing: reading only the
14
+ * repository reports values that do exist as absent, and sends an operator
15
+ * hunting for a secret that is already set.
16
+ */
17
+ import { type SecretRow } from './index.js';
18
+ /**
19
+ * Every secret this checkout declares that the repository does not hold, in
20
+ * the order an operator should fix them: the ones a workflow already promises
21
+ * break a deploy today, the rest break the next one.
22
+ */
23
+ export declare function secretsNeedingValues(rows: readonly SecretRow[]): SecretRow[];
24
+ /**
25
+ * Every secret this checkout declares that the named environment does not
26
+ * hold. A name the repository already holds is still offered: an environment
27
+ * value overrides the repository's for a job bound to it, and overriding one
28
+ * stage's credentials is the reason to write at environment scope at all. A
29
+ * repository secret no source declares is not offered — that is a stale value
30
+ * to review, not one to copy into an environment.
31
+ */
32
+ export declare function secretsMissingInEnvironment(rows: readonly SecretRow[], environment: string): SecretRow[];
33
+ /** Why a secret is wanted, so a prompt states what will break without it. */
34
+ export declare function describeNeed(row: SecretRow): string;
35
+ /**
36
+ * Secret names GitHub currently holds at the given scope, or a refusal naming
37
+ * what failed. With no environment that is repository scope; with one it is
38
+ * that environment's own secrets, which are the values a bound job reads
39
+ * first.
40
+ */
41
+ export declare function readRepositorySecrets(repo: string | undefined, environment?: string): {
42
+ ok: true;
43
+ names: string[];
44
+ } | {
45
+ ok: false;
46
+ reason: string;
47
+ };
48
+ /** Write one secret at the given scope, value over stdin so it never reaches argv. */
49
+ export declare function writeRepositorySecret(name: string, value: string, repo: string | undefined, environment?: string): Promise<{
50
+ ok: true;
51
+ } | {
52
+ ok: false;
53
+ reason: string;
54
+ }>;
55
+ /**
56
+ * Print every known secret and the scopes holding it. Exit code 1 when a
57
+ * workflow promises a value no scope a bound job reads can supply — that
58
+ * combination is the one that fails a deploy, and it fails talking about the
59
+ * value rather than the missing secret.
60
+ */
61
+ export declare function secretsStatus(root: string, options: {
62
+ repo?: string;
63
+ env?: string;
64
+ }): number;
65
+ /**
66
+ * Set one secret, or - with no name - walk every secret this checkout declares
67
+ * and the target scope does not hold, prompting once each. The walk is the
68
+ * point of the command: an operator should never have to transcribe names out
69
+ * of a status table to know what to paste.
70
+ */
71
+ export declare function secretsSet(root: string, name: string | undefined, options: {
72
+ repo?: string;
73
+ env?: string;
74
+ }): Promise<number>;
75
+ /**
76
+ * Push every locally fetchable secret to the repository, or to one environment.
77
+ * Only names `smoo.secrets` declares a command for: everything else has no
78
+ * source here and must be pasted with `smoo secrets set`.
79
+ */
80
+ export declare function secretsSync(root: string, options: {
81
+ repo?: string;
82
+ env?: string;
83
+ }): Promise<number>;
84
+ //# sourceMappingURL=commands.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"commands.d.ts","sourceRoot":"","sources":["../../src/secrets/commands.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAIH,OAAO,EAKL,KAAK,SAAS,EAOf,MAAM,YAAY,CAAC;AAGpB;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,SAAS,SAAS,EAAE,GAAG,SAAS,EAAE,CAG5E;AAED;;;;;;;GAOG;AACH,wBAAgB,2BAA2B,CAAC,IAAI,EAAE,SAAS,SAAS,EAAE,EAAE,WAAW,EAAE,MAAM,GAAG,SAAS,EAAE,CAOxG;AAED,6EAA6E;AAC7E,wBAAgB,YAAY,CAAC,GAAG,EAAE,SAAS,GAAG,MAAM,CAUnD;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,CACnC,IAAI,EAAE,MAAM,GAAG,SAAS,EACxB,WAAW,CAAC,EAAE,MAAM,GACnB;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,KAAK,EAAE,MAAM,EAAE,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAwB/D;AAED,sFAAsF;AACtF,wBAAsB,qBAAqB,CACzC,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,MAAM,GAAG,SAAS,EACxB,WAAW,CAAC,EAAE,MAAM,GACnB,OAAO,CAAC;IAAE,EAAE,EAAE,IAAI,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,CAYvD;AAyCD;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,MAAM,CAkF5F;AA8BD;;;;;GAKG;AACH,wBAAsB,UAAU,CAC9B,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,GAAG,SAAS,EACxB,OAAO,EAAE;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,GACvC,OAAO,CAAC,MAAM,CAAC,CAwDjB;AAuBD;;;;GAIG;AACH,wBAAsB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CA+BzG"}
@@ -0,0 +1,375 @@
1
+ /**
2
+ * `smoo secrets` — the operator side of the reconciliation in ./index.ts.
3
+ *
4
+ * Values are handed to `gh` over stdin, never through argv (a process list is
5
+ * world-readable) and never printed. Reading one from the terminal disables
6
+ * echo, so a pasted key does not end up in a scrollback buffer or a screen
7
+ * share.
8
+ *
9
+ * A value lives at repository scope or inside a named GitHub Environment, and
10
+ * a job bound to an environment resolves `secrets.X` from that environment
11
+ * first and from the repository second — which is how one name carries test
12
+ * credentials on a preview stage and live ones in production. Every scope the
13
+ * workflows bind is read before anything is called missing: reading only the
14
+ * repository reports values that do exist as absent, and sends an operator
15
+ * hunting for a secret that is already set.
16
+ */
17
+ import { spawn, spawnSync } from 'node:child_process';
18
+ import { getWorkspacePackages } from '../lib/workspace.js';
19
+ import { environmentsMissing, fetchLocalSecret, localSecretCommandNames, reconcileSecrets, secretNameMapping, unsatisfiedSecrets, unwiredSecrets, workerSecretNames, workflowEnvironments, workflowSecretNames, } from './index.js';
20
+ import { resolveRepository } from './repository.js';
21
+ /**
22
+ * Every secret this checkout declares that the repository does not hold, in
23
+ * the order an operator should fix them: the ones a workflow already promises
24
+ * break a deploy today, the rest break the next one.
25
+ */
26
+ export function secretsNeedingValues(rows) {
27
+ const missing = rows.filter((row) => !row.onRepository);
28
+ return [...missing.filter((row) => row.suppliedByWorkflow), ...missing.filter((row) => !row.suppliedByWorkflow)];
29
+ }
30
+ /**
31
+ * Every secret this checkout declares that the named environment does not
32
+ * hold. A name the repository already holds is still offered: an environment
33
+ * value overrides the repository's for a job bound to it, and overriding one
34
+ * stage's credentials is the reason to write at environment scope at all. A
35
+ * repository secret no source declares is not offered — that is a stale value
36
+ * to review, not one to copy into an environment.
37
+ */
38
+ export function secretsMissingInEnvironment(rows, environment) {
39
+ const missing = rows.filter((row) => !row.heldByEnvironment.includes(environment) &&
40
+ (row.declaredByWorkers.length > 0 || row.suppliedByWorkflow || row.fetchableLocally));
41
+ return [...missing.filter((row) => row.suppliedByWorkflow), ...missing.filter((row) => !row.suppliedByWorkflow)];
42
+ }
43
+ /** Why a secret is wanted, so a prompt states what will break without it. */
44
+ export function describeNeed(row) {
45
+ const parts = [];
46
+ if (row.declaredByWorkers.length > 0)
47
+ parts.push(`declared by ${row.declaredByWorkers.join(', ')}`);
48
+ if (row.suppliedByWorkflow)
49
+ parts.push('passed by a managed workflow');
50
+ if (row.fetchableLocally)
51
+ parts.push('fetchable locally via smoo.secrets');
52
+ if (row.repositorySecret !== row.name)
53
+ parts.push(`read as ${row.name}`);
54
+ // Naming the environments that already hold it stops an operator from
55
+ // answering a per-stage prompt with a repository-wide value.
56
+ if (row.heldByEnvironment.length > 0)
57
+ parts.push(`held by ${row.heldByEnvironment.join(', ')}`);
58
+ return parts.length > 0 ? ` - ${parts.join('; ')}` : '';
59
+ }
60
+ /**
61
+ * Secret names GitHub currently holds at the given scope, or a refusal naming
62
+ * what failed. With no environment that is repository scope; with one it is
63
+ * that environment's own secrets, which are the values a bound job reads
64
+ * first.
65
+ */
66
+ export function readRepositorySecrets(repo, environment) {
67
+ const args = ['secret', 'list', '--json', 'name'];
68
+ if (repo)
69
+ args.push('--repo', repo);
70
+ if (environment)
71
+ args.push('--env', environment);
72
+ const result = spawnSync('gh', args, { encoding: 'utf8' });
73
+ if (result.status !== 0) {
74
+ return {
75
+ ok: false,
76
+ reason: `gh ${args.join(' ')} exited ${result.status ?? 'without status'}: ${result.stderr.trim()}`,
77
+ };
78
+ }
79
+ const parsed = JSON.parse(result.stdout);
80
+ if (!Array.isArray(parsed)) {
81
+ return { ok: false, reason: 'gh secret list did not return a list' };
82
+ }
83
+ const names = [];
84
+ for (const row of parsed) {
85
+ if (typeof row !== 'object' || row === null || !('name' in row))
86
+ continue;
87
+ // `in` narrows the property to unknown, so the typeof check below is the
88
+ // only thing that admits it — no assertion about gh's output shape.
89
+ const name = row.name;
90
+ if (typeof name === 'string')
91
+ names.push(name);
92
+ }
93
+ return { ok: true, names: names.sort((left, right) => left.localeCompare(right)) };
94
+ }
95
+ /** Write one secret at the given scope, value over stdin so it never reaches argv. */
96
+ export async function writeRepositorySecret(name, value, repo, environment) {
97
+ const args = ['secret', 'set', name];
98
+ if (repo)
99
+ args.push('--repo', repo);
100
+ if (environment)
101
+ args.push('--env', environment);
102
+ const child = spawn('gh', args, { stdio: ['pipe', 'inherit', 'inherit'] });
103
+ child.stdin.end(value);
104
+ const status = await new Promise((resolvePromise) => {
105
+ child.on('close', (code) => resolvePromise(code));
106
+ });
107
+ return status === 0
108
+ ? { ok: true }
109
+ : { ok: false, reason: `gh ${args.join(' ')} exited ${status ?? 'without status'}` };
110
+ }
111
+ function collectSources(root, repositorySecrets, environmentSecrets = {}) {
112
+ const workspaceDirs = getWorkspacePackages(root).map((pkg) => pkg.path);
113
+ const workerSecrets = workerSecretNames(root, workspaceDirs);
114
+ const workflowSecrets = workflowSecretNames(root);
115
+ const localCommands = localSecretCommandNames(root);
116
+ const envNames = [
117
+ ...new Set([...Object.values(workerSecrets).flatMap((names) => [...names]), ...workflowSecrets, ...localCommands]),
118
+ ];
119
+ return {
120
+ workerSecrets,
121
+ workflowSecrets,
122
+ secretNames: secretNameMapping(root, envNames),
123
+ localCommands,
124
+ repositorySecrets,
125
+ environmentSecrets,
126
+ };
127
+ }
128
+ /** Every scope holding a value for this name, repository first. */
129
+ function heldBy(row) {
130
+ const scopes = row.onRepository ? ['repository', ...row.heldByEnvironment] : row.heldByEnvironment;
131
+ return scopes.length > 0 ? scopes.join(', ') : 'ABSENT';
132
+ }
133
+ function describe(row, scopeWidth) {
134
+ const where = row.declaredByWorkers.length > 0 ? row.declaredByWorkers.join(', ') : '—';
135
+ return [
136
+ heldBy(row).padEnd(scopeWidth),
137
+ row.name.padEnd(32),
138
+ row.suppliedByWorkflow ? 'workflow' : ' ',
139
+ row.fetchableLocally ? 'local' : ' ',
140
+ where,
141
+ ].join(' ');
142
+ }
143
+ /**
144
+ * Print every known secret and the scopes holding it. Exit code 1 when a
145
+ * workflow promises a value no scope a bound job reads can supply — that
146
+ * combination is the one that fails a deploy, and it fails talking about the
147
+ * value rather than the missing secret.
148
+ */
149
+ export function secretsStatus(root, options) {
150
+ const resolved = resolveRepository(root, options.repo);
151
+ if (!resolved.ok) {
152
+ console.error(resolved.reason);
153
+ return 1;
154
+ }
155
+ const { repo, source } = resolved.choice;
156
+ const repositorySecrets = readRepositorySecrets(repo);
157
+ if (!repositorySecrets.ok) {
158
+ console.error(repositorySecrets.reason);
159
+ return 1;
160
+ }
161
+ // The workflows decide which environments a job reads; an explicitly asked
162
+ // for one is shown too, so an operator can inspect a scope before a workflow
163
+ // binds it.
164
+ const boundEnvironments = workflowEnvironments(root);
165
+ const asked = options.env !== undefined && !boundEnvironments.includes(options.env) ? [options.env] : [];
166
+ const environmentSecrets = {};
167
+ const unreadable = [];
168
+ for (const environment of [...boundEnvironments, ...asked]) {
169
+ const held = readRepositorySecrets(repo, environment);
170
+ if (held.ok)
171
+ environmentSecrets[environment] = held.names;
172
+ else
173
+ unreadable.push({ environment, reason: held.reason });
174
+ }
175
+ // An environment that cannot be read is reported, never assumed empty:
176
+ // guessing produces a refusal about a value that may well be set.
177
+ const readable = boundEnvironments.filter((environment) => environment in environmentSecrets);
178
+ const rows = reconcileSecrets(collectSources(root, repositorySecrets.names, environmentSecrets));
179
+ console.log(`repository ${repo} (${source}), holding ${repositorySecrets.names.length} secrets`);
180
+ for (const [environment, names] of Object.entries(environmentSecrets)) {
181
+ console.log(`environment ${environment}, holding ${names.length} secrets`);
182
+ }
183
+ for (const { environment, reason } of unreadable) {
184
+ console.log(`environment ${environment} could not be read, so nothing below claims what it holds: ${reason}`);
185
+ }
186
+ const scopeWidth = Math.max('held by'.length, ...rows.map((row) => heldBy(row).length));
187
+ console.log(`${'held by'.padEnd(scopeWidth)} ${'name'.padEnd(32)} workflow local declared by`);
188
+ for (const row of rows)
189
+ console.log(describe(row, scopeWidth));
190
+ const unwired = unwiredSecrets(rows);
191
+ if (unwired.length > 0) {
192
+ console.log('');
193
+ for (const row of unwired) {
194
+ console.log(`note: ${row.name} is declared by ${row.declaredByWorkers.join(', ')} but no managed workflow passes it; ` +
195
+ 'declare it in smoo.github.deploySecrets to have CI supply it.');
196
+ }
197
+ }
198
+ // A value an environment holds is not a value to duplicate at repository
199
+ // scope: offering it invites two sources of truth for one credential, and
200
+ // the table above already shows where it lives. Only a name no scope holds
201
+ // is a name to set here.
202
+ const needed = secretsNeedingValues(rows).filter((row) => !row.onRepository && row.heldByEnvironment.length === 0);
203
+ if (needed.length > 0) {
204
+ console.log('');
205
+ console.log(`${needed.length} secret(s) have no value in any scope on ${repo}:`);
206
+ for (const row of needed)
207
+ console.log(` ${row.repositorySecret}${describeNeed(row)}`);
208
+ console.log('');
209
+ console.log(`set them all, one prompt each: smoo secrets set -R ${repo}`);
210
+ console.log(`set one: smoo secrets set ${needed[0]?.repositorySecret ?? 'NAME'} -R ${repo}`);
211
+ }
212
+ const unsatisfied = unsatisfiedSecrets(rows, readable);
213
+ if (unsatisfied.length === 0)
214
+ return 0;
215
+ console.log('');
216
+ for (const row of unsatisfied) {
217
+ const missing = environmentsMissing(row, readable);
218
+ const scopes = missing.length > 0 ? missing.join(', ') : 'the repository';
219
+ console.error(`missing: ${row.name} — a workflow passes secrets.${row.name} and no value exists in ${scopes}.`);
220
+ if (missing.length === 0) {
221
+ console.error(` smoo secrets set ${row.repositorySecret} -R ${repo}`);
222
+ continue;
223
+ }
224
+ for (const environment of missing) {
225
+ const named = environment.includes(' ') ? `'${environment}'` : environment;
226
+ console.error(` smoo secrets set ${row.repositorySecret} -R ${repo} --env ${named}`);
227
+ }
228
+ console.error(` or one value for every environment: smoo secrets set ${row.repositorySecret} -R ${repo}`);
229
+ }
230
+ return 1;
231
+ }
232
+ /** Read a value with echo disabled when stdin is a terminal; otherwise read piped input. */
233
+ async function readSecretValue(prompt) {
234
+ if (!process.stdin.isTTY) {
235
+ const chunks = [];
236
+ for await (const chunk of process.stdin)
237
+ chunks.push(Buffer.from(chunk));
238
+ return Buffer.concat(chunks).toString('utf8').replace(/\n$/, '');
239
+ }
240
+ process.stderr.write(prompt);
241
+ process.stdin.setRawMode(true);
242
+ let value = '';
243
+ try {
244
+ for await (const chunk of process.stdin) {
245
+ const text = Buffer.from(chunk).toString('utf8');
246
+ if (text === '\r' || text === '\n' || text === '\u0004')
247
+ break;
248
+ if (text === '\u0003')
249
+ throw new Error('cancelled');
250
+ if (text === '\u007f') {
251
+ value = value.slice(0, -1);
252
+ continue;
253
+ }
254
+ value += text;
255
+ }
256
+ }
257
+ finally {
258
+ process.stdin.setRawMode(false);
259
+ process.stderr.write('\n');
260
+ }
261
+ return value;
262
+ }
263
+ /**
264
+ * Set one secret, or - with no name - walk every secret this checkout declares
265
+ * and the target scope does not hold, prompting once each. The walk is the
266
+ * point of the command: an operator should never have to transcribe names out
267
+ * of a status table to know what to paste.
268
+ */
269
+ export async function secretsSet(root, name, options) {
270
+ const resolved = resolveRepository(root, options.repo);
271
+ if (!resolved.ok) {
272
+ console.error(resolved.reason);
273
+ return 1;
274
+ }
275
+ const { repo, source } = resolved.choice;
276
+ const environment = options.env;
277
+ if (name !== undefined)
278
+ return setOne(name, name, repo, environment);
279
+ const held = readRepositorySecrets(repo);
280
+ if (!held.ok) {
281
+ console.error(held.reason);
282
+ return 1;
283
+ }
284
+ const environmentSecrets = {};
285
+ if (environment !== undefined) {
286
+ const inEnvironment = readRepositorySecrets(repo, environment);
287
+ if (!inEnvironment.ok) {
288
+ console.error(inEnvironment.reason);
289
+ return 1;
290
+ }
291
+ environmentSecrets[environment] = inEnvironment.names;
292
+ }
293
+ const rows = reconcileSecrets(collectSources(root, held.names, environmentSecrets));
294
+ const needed = environment === undefined ? secretsNeedingValues(rows) : secretsMissingInEnvironment(rows, environment);
295
+ const target = environment === undefined ? `${repo} (${source})` : `environment ${environment} on ${repo} (${source})`;
296
+ if (needed.length === 0) {
297
+ console.log(`${target} already holds every secret this checkout declares.`);
298
+ return 0;
299
+ }
300
+ console.log(`${needed.length} secret(s) to set on ${target}. Empty input skips one.`);
301
+ let failed = 0;
302
+ let set = 0;
303
+ for (const row of needed) {
304
+ console.log('');
305
+ console.log(`${row.repositorySecret}${describeNeed(row)}`);
306
+ const value = await readSecretValue(' paste value (not echoed, empty to skip): ');
307
+ if (value.length === 0) {
308
+ console.log(`skip ${row.repositorySecret}`);
309
+ continue;
310
+ }
311
+ const written = await writeRepositorySecret(row.repositorySecret, value, repo, environment);
312
+ if (!written.ok) {
313
+ console.error(`fail ${row.repositorySecret}: ${written.reason}`);
314
+ failed += 1;
315
+ continue;
316
+ }
317
+ console.log(`set ${row.repositorySecret}`);
318
+ set += 1;
319
+ }
320
+ console.log('');
321
+ console.log(`${set} set, ${needed.length - set - failed} skipped, ${failed} failed.`);
322
+ return failed === 0 ? 0 : 1;
323
+ }
324
+ async function setOne(envName, repositorySecret, repo, environment) {
325
+ const scope = environment === undefined ? '' : ` in ${environment}`;
326
+ const value = await readSecretValue(`Value for ${repositorySecret}${scope} (not echoed): `);
327
+ if (value.length === 0) {
328
+ console.error(`${repositorySecret}: refusing to set an empty value.`);
329
+ return 1;
330
+ }
331
+ const written = await writeRepositorySecret(repositorySecret, value, repo, environment);
332
+ if (!written.ok) {
333
+ console.error(written.reason);
334
+ return 1;
335
+ }
336
+ console.log(`set ${repositorySecret}${scope}${envName === repositorySecret ? '' : ` (reads as ${envName})`}`);
337
+ return 0;
338
+ }
339
+ /**
340
+ * Push every locally fetchable secret to the repository, or to one environment.
341
+ * Only names `smoo.secrets` declares a command for: everything else has no
342
+ * source here and must be pasted with `smoo secrets set`.
343
+ */
344
+ export async function secretsSync(root, options) {
345
+ const resolved = resolveRepository(root, options.repo);
346
+ if (!resolved.ok) {
347
+ console.error(resolved.reason);
348
+ return 1;
349
+ }
350
+ const { repo } = resolved.choice;
351
+ const environment = options.env;
352
+ const names = localSecretCommandNames(root);
353
+ if (names.length === 0) {
354
+ console.error('smoo.secrets declares no fetch commands; nothing to sync.');
355
+ return 1;
356
+ }
357
+ const scope = environment === undefined ? '' : ` in ${environment}`;
358
+ let failed = 0;
359
+ for (const name of names) {
360
+ const fetched = fetchLocalSecret(root, name);
361
+ if (!fetched.ok) {
362
+ console.error(`skip ${name}: ${fetched.reason}`);
363
+ failed += 1;
364
+ continue;
365
+ }
366
+ const written = await writeRepositorySecret(name, fetched.value, repo, environment);
367
+ if (!written.ok) {
368
+ console.error(`fail ${name}: ${written.reason}`);
369
+ failed += 1;
370
+ continue;
371
+ }
372
+ console.log(`set ${name}${scope}`);
373
+ }
374
+ return failed === 0 ? 0 : 1;
375
+ }