@vivswan/github-settings-as-code 2.0.1-main.1081.gc25bbfe → 2.0.1-main.1089.g2dd2ba5

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.

Potentially problematic release.


This version of @vivswan/github-settings-as-code might be problematic. Click here for more details.

package/LICENSE.md CHANGED
@@ -557,5 +557,5 @@ software incorporates into what it produces are output. The software's
557
557
  **Use** means anything you do with the software requiring one of your
558
558
  licenses.
559
559
 
560
- <!-- The license text between the BEGIN/END markers is managed by Vivswan/repo-platform and replaced on every sync. Repository-specific license notices (third-party components, differently licensed paths) go below the END marker; they are this repository's own and survive every sync. -->
560
+ <!-- The license text between the BEGIN/END markers is managed by the platform and replaced on every sync. Repository-specific license notices (third-party components, differently licensed paths) go below the END marker; they are this repository's own and survive every sync. -->
561
561
  <!-- END REPO-PLATFORM MANAGED -->
package/lib/pkg/cli.js CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { C as SNAPSHOT_ONLY_INPUTS, E as parseSnapshotFileConfig, F as concludeMerge, I as concludeRun, Kt as redactRanges, L as failRun, Rt as GithubApi, S as SNAPSHOT_INPUTS, T as parseConfig, U as PRIVATE_REPORT_CHANNELS, Wt as maskRegistry, _ as INPUT_DECLS, d as runSnapshot, f as runSingle, j as readSettingsFile, k as runMulti, o as snapshotRepository, pt as SECTIONS, s as validateSettings, t as runMerge, tt as describeProblem, u as concludeSnapshot, v as MERGE_INPUTS, y as MERGE_ONLY_INPUTS, yt as sectionGrant } from "./src-BFMt0V_E.js";
2
+ import { C as SNAPSHOT_ONLY_INPUTS, E as parseSnapshotFileConfig, F as concludeMerge, I as concludeRun, Kt as redactRanges, L as failRun, Rt as GithubApi, S as SNAPSHOT_INPUTS, T as parseConfig, U as PRIVATE_REPORT_CHANNELS, Wt as maskRegistry, _ as INPUT_DECLS, d as runSnapshot, f as runSingle, j as readSettingsFile, k as runMulti, o as snapshotRepository, pt as SECTIONS, s as validateSettings, t as runMerge, tt as describeProblem, u as concludeSnapshot, v as MERGE_INPUTS, y as MERGE_ONLY_INPUTS, yt as sectionGrant } from "./src-L_4G-LmP.js";
3
3
  import { appendFileSync, existsSync, mkdirSync, writeFileSync } from "node:fs";
4
4
  import { dirname } from "node:path";
5
5
  import { ResultAsync, err, ok } from "neverthrow";
@@ -1619,12 +1619,12 @@ interface SectionSnapshot<K extends SectionKey = SectionKey> {
1619
1619
  * repository; the engine renders them as drift in check mode and executes them in apply mode.
1620
1620
  * Modules register in ../registry.ts.
1621
1621
  *
1622
- * snapshot() present -> reads through the same port, so it cannot write either
1622
+ * snapshot() present -> reads through the same port (plus the run's denial policy), so it cannot write either
1623
1623
  * snapshot() absent -> the section is unsupported by snapshot (snapshotUnsupportedNote)
1624
1624
  */
1625
1625
  interface SectionModule<K extends SectionKey = SectionKey, E extends EndpointDict = EndpointDict, G extends GraphqlDict = GraphqlDict> extends SectionModuleBase<K, E, G> {
1626
1626
  plan(ctx: PlanContext<E, G>, desired: SectionInput<K>): Promise<SectionPlan<PlannedOp<E, G>>>;
1627
- snapshot?(ctx: PlanContext<E, G>): Promise<SectionSnapshot<K>>;
1627
+ snapshot?(ctx: SnapshotContext<E, G>): Promise<SectionSnapshot<K>>;
1628
1628
  /** Pinned so a non-literal object carrying a run() handler is not assignable either. */
1629
1629
  run?: never;
1630
1630
  }
@@ -1799,6 +1799,24 @@ interface PlanContext<E extends EndpointDict = EndpointDict, G extends GraphqlDi
1799
1799
  readonly repo: RepoRef;
1800
1800
  readonly read: BoundReads<E, G>;
1801
1801
  }
1802
+ /**
1803
+ * The policy as a snapshot() sees it. Only snapshotContext() mints one: the constructor is private
1804
+ * and the class nominal, so a section cannot hand readOrNote a literal "warn" and turn a denial the
1805
+ * run should fail on into a note.
1806
+ */
1807
+ declare class DenialPolicy {
1808
+ private readonly input;
1809
+ private constructor();
1810
+ /** Under warn a denied sub-read is noted and left out; under fail it propagates. */
1811
+ get notesDenials(): boolean;
1812
+ }
1813
+ /**
1814
+ * What snapshot() reads through: the plan port plus the run's denial policy, so a helper over one
1815
+ * sub-read (readOrNote) classifies a denial where it happens instead of noting it under both.
1816
+ */
1817
+ interface SnapshotContext<E extends EndpointDict = EndpointDict, G extends GraphqlDict = GraphqlDict> extends PlanContext<E, G> {
1818
+ readonly onMissingPermission: DenialPolicy;
1819
+ }
1802
1820
  /**
1803
1821
  * `D` is the drift type its arm demands: an ordinary operation must justify itself with at least one
1804
1822
  * drift line (DriftFor), so "check reported clean while apply mutated" is unrepresentable.
@@ -2095,7 +2113,7 @@ interface ListSectionModule<K extends ListSectionKey, Ends extends ListEndpoints
2095
2113
  readonly secretValues?: (declared: Declared<K>) => DeclaredSecretValue[];
2096
2114
  readonly layering?: KeyedListLayering;
2097
2115
  readonly plan: (ctx: PlanContext<Ends>, desired: Declared<K>) => Promise<SectionPlan<PlannedOp<Ends>>>;
2098
- readonly snapshot: (ctx: PlanContext<Ends>) => Promise<SectionSnapshot<K>>;
2116
+ readonly snapshot: (ctx: SnapshotContext<Ends>) => Promise<SectionSnapshot<K>>;
2099
2117
  /** The declaration, for the harness derivations (the mock's transformers, the fuzz witness). */
2100
2118
  readonly decl: ListSectionDecl<K, Ends, Live, F>;
2101
2119
  }
@@ -2626,7 +2644,7 @@ interface RepoSecretsSectionModule<K extends RepoSecretsKey> {
2626
2644
  readonly secretValues: typeof listSecretValues;
2627
2645
  readonly closedSurface: typeof CLOSED_SURFACE;
2628
2646
  readonly plan: RepoSecretsPlan<K>;
2629
- readonly snapshot: (ctx: PlanContext<RepoSecretsEndpoints<SecretsSegment<K>>>) => Promise<SectionSnapshot<K>>;
2647
+ readonly snapshot: (ctx: SnapshotContext<RepoSecretsEndpoints<SecretsSegment<K>>>) => Promise<SectionSnapshot<K>>;
2630
2648
  }
2631
2649
  //#endregion
2632
2650
  //#region src/sections/shared/setup-section.d.ts
@@ -2721,7 +2739,7 @@ interface SetupSectionModule<K extends SetupKey> {
2721
2739
  readonly endpoints: SetupEndpoints<K>;
2722
2740
  readonly shape: z.ZodType;
2723
2741
  readonly plan: SetupPlan<K>;
2724
- readonly snapshot: (ctx: PlanContext<SetupEndpoints<K>>) => Promise<SectionSnapshot<K>>;
2742
+ readonly snapshot: (ctx: SnapshotContext<SetupEndpoints<K>>) => Promise<SectionSnapshot<K>>;
2725
2743
  }
2726
2744
  //#endregion
2727
2745
  //#region src/sections/shared/repo-variables.d.ts
@@ -2788,7 +2806,7 @@ interface RepoVariablesSectionModule<K extends RepoVariablesKey> {
2788
2806
  readonly endpoints: RepoVariablesEndpoints<VariablesSegment<K>>;
2789
2807
  readonly shape: z.ZodType;
2790
2808
  readonly plan: RepoVariablesPlan<K>;
2791
- readonly snapshot: (ctx: PlanContext<RepoVariablesEndpoints<VariablesSegment<K>>>) => Promise<SectionSnapshot<K>>;
2809
+ readonly snapshot: (ctx: SnapshotContext<RepoVariablesEndpoints<VariablesSegment<K>>>) => Promise<SectionSnapshot<K>>;
2792
2810
  }
2793
2811
  //#endregion
2794
2812
  //#region src/sections/registry.d.ts
@@ -3296,7 +3314,7 @@ declare const byKey: {
3296
3314
  }> | undefined;
3297
3315
  readonly variables?: never;
3298
3316
  })>>;
3299
- snapshot(ctx: PlanContext<{
3317
+ snapshot(ctx: SnapshotContext<{
3300
3318
  readonly get: {
3301
3319
  readonly route: "GET /repos/{owner}/{repo}";
3302
3320
  readonly statuses: {
@@ -3663,7 +3681,7 @@ declare const byKey: {
3663
3681
  }> | undefined;
3664
3682
  readonly variables?: never;
3665
3683
  })>>;
3666
- snapshot(ctx: PlanContext<{
3684
+ snapshot(ctx: SnapshotContext<{
3667
3685
  readonly list: {
3668
3686
  readonly route: "GET /repos/{owner}/{repo}/rulesets";
3669
3687
  readonly statuses: {
@@ -4179,7 +4197,7 @@ declare const byKey: {
4179
4197
  }[];
4180
4198
  } | undefined;
4181
4199
  }[]): Promise<EnvironmentsPlan>;
4182
- snapshot(ctx: PlanContext<{
4200
+ snapshot(ctx: SnapshotContext<{
4183
4201
  readonly list: {
4184
4202
  readonly route: "GET /repos/{owner}/{repo}/environments";
4185
4203
  readonly statuses: {
@@ -4802,7 +4820,7 @@ declare const byKey: {
4802
4820
  } | null | undefined;
4803
4821
  } | null;
4804
4822
  }[]): Promise<BranchesPlan>;
4805
- snapshot(ctx: PlanContext<{
4823
+ snapshot(ctx: SnapshotContext<{
4806
4824
  readonly getProtection: {
4807
4825
  readonly route: "GET /repos/{owner}/{repo}/branches/{branch}/protection";
4808
4826
  readonly statuses: {
@@ -5517,7 +5535,7 @@ declare const byKey: {
5517
5535
  }> | undefined;
5518
5536
  readonly variables?: never;
5519
5537
  })>>;
5520
- snapshot(ctx: PlanContext<{
5538
+ snapshot(ctx: SnapshotContext<{
5521
5539
  readonly getPermissions: {
5522
5540
  readonly route: "GET /repos/{owner}/{repo}/actions/permissions";
5523
5541
  readonly statuses: {
@@ -5770,7 +5788,7 @@ declare const byKey: {
5770
5788
  }> | undefined;
5771
5789
  readonly variables?: never;
5772
5790
  })>>;
5773
- snapshot(ctx: PlanContext<{
5791
+ snapshot(ctx: SnapshotContext<{
5774
5792
  readonly list: {
5775
5793
  readonly route: "GET /repos/{owner}/{repo}/actions/workflows";
5776
5794
  readonly statuses: {
@@ -5961,7 +5979,7 @@ declare const byKey: {
5961
5979
  }> | undefined;
5962
5980
  readonly variables?: never;
5963
5981
  })>>;
5964
- snapshot(ctx: PlanContext<{
5982
+ snapshot(ctx: SnapshotContext<{
5965
5983
  readonly get: {
5966
5984
  readonly route: "GET /repos/{owner}/{repo}/pages";
5967
5985
  readonly statuses: {
@@ -6172,7 +6190,7 @@ declare const byKey: {
6172
6190
  }> | undefined;
6173
6191
  readonly variables?: never;
6174
6192
  })>>;
6175
- snapshot(ctx: PlanContext<{
6193
+ snapshot(ctx: SnapshotContext<{
6176
6194
  readonly list: {
6177
6195
  readonly route: "GET /repos/{owner}/{repo}/collaborators";
6178
6196
  readonly statuses: {
@@ -6321,7 +6339,7 @@ declare const byKey: {
6321
6339
  }> | undefined;
6322
6340
  readonly variables?: never;
6323
6341
  }>>;
6324
- snapshot(ctx: PlanContext<{
6342
+ snapshot(ctx: SnapshotContext<{
6325
6343
  readonly org: {
6326
6344
  readonly route: "GET /orgs/{org}";
6327
6345
  readonly statuses: {
@@ -6474,7 +6492,7 @@ declare const byKey: {
6474
6492
  }> | undefined;
6475
6493
  readonly variables?: never;
6476
6494
  })>>;
6477
- snapshot(ctx: PlanContext<{
6495
+ snapshot(ctx: SnapshotContext<{
6478
6496
  readonly list: {
6479
6497
  readonly route: "GET /repos/{owner}/{repo}/milestones";
6480
6498
  readonly statuses: {
@@ -6760,7 +6778,7 @@ declare const byKey: {
6760
6778
  }> | undefined;
6761
6779
  readonly variables?: never;
6762
6780
  })>>;
6763
- snapshot(ctx: PlanContext<{
6781
+ snapshot(ctx: SnapshotContext<{
6764
6782
  readonly get: {
6765
6783
  readonly route: "GET /repos/{owner}/{repo}/interaction-limits";
6766
6784
  readonly statuses: {
@@ -7012,7 +7030,7 @@ declare const byKey: {
7012
7030
  }> | undefined;
7013
7031
  readonly variables?: never;
7014
7032
  })>>;
7015
- snapshot(ctx: PlanContext<{
7033
+ snapshot(ctx: SnapshotContext<{
7016
7034
  readonly list: {
7017
7035
  readonly route: "GET /repos/{owner}/{repo}/hooks";
7018
7036
  readonly statuses: {
@@ -7171,7 +7189,7 @@ declare const byKey: {
7171
7189
  }> | undefined;
7172
7190
  readonly variables?: never;
7173
7191
  }>>;
7174
- snapshot(ctx: PlanContext<{
7192
+ snapshot(ctx: SnapshotContext<{
7175
7193
  readonly org: {
7176
7194
  readonly route: "GET /orgs/{org}";
7177
7195
  readonly statuses: {
@@ -7420,7 +7438,7 @@ declare const byKey: {
7420
7438
  }> | undefined;
7421
7439
  readonly variables?: never;
7422
7440
  })>>;
7423
- snapshot(ctx: PlanContext<{
7441
+ snapshot(ctx: SnapshotContext<{
7424
7442
  readonly list: {
7425
7443
  readonly route: "GET /repos/{owner}/{repo}/secret-scanning/custom-patterns";
7426
7444
  readonly statuses: {
package/lib/pkg/index.js CHANGED
@@ -1,2 +1,2 @@
1
- import { $ as renderSnapshotYaml, A as parseSettingsDoc, At as parseReposInput, B as planRedaction, Bt as SECRET_TRANSPORT_WITHHELD, C as SNAPSHOT_ONLY_INPUTS, Ct as endpointMethod, D as DEFAULT_SETTINGS_FILE, Dt as SECTION_KEYS, E as parseSnapshotFileConfig, Et as PROBOT_PARITY_KEYS, F as concludeMerge, Ft as VISIBILITY_FILTERS, G as openReportChannel, Gt as prefixedIo, H as toPublicView, Ht as isRateLimitError, I as concludeRun, It as discoverRepos, J as MARKER_LABEL_CONFIG, Jt as resolveCentralTargets, K as ISSUE_TITLE, Kt as redactRanges, L as failRun, Lt as DEFAULT_API_VERSION, M as createVisibilityResolver, Mt as ARCHIVED_FILTERS, N as getRepoFile, Nt as DEFAULT_DISCOVERY_FILTERS, O as resolveTargets, Ot as SettingsFile, P as MERGE_RESULT, Pt as FORKS_FILTERS, Q as parseRecipient, R as PRIVATE_REPOS_POLICIES, Rt as GithubApi, S as SNAPSHOT_INPUTS, St as grantFor, T as parseConfig, Tt as DOCUMENT_DIRECTIVE_KEYS, U as PRIVATE_REPORT_CHANNELS, Ut as collectingIo, V as publicDetail, Vt as isPermissionError, W as applyMarkerInjection, Wt as maskRegistry, X as deliverArtifactReport, Xt as parseRepoSlug, Y as composeReport, Yt as dedupeTargets, Z as encryptReport, _ as INPUT_DECLS, _t as denialPosture, a as snapshotRepositories, at as preflightProbe, b as MERGE_REJECTED_INPUTS, bt as sectionOperations, c as SNAPSHOT_RESULTS, ct as validateSettingsDoc, d as runSnapshot, dt as mergeLayers, et as RERUN_ADVICE, f as runSingle, ft as stripNulls, g as FILTER_INPUTS, gt as sectionModule, h as DEFAULT_PRIVATE_REPOS, ht as allGraphqlOps, i as renderMergedYaml, it as REPO_RESULTS, j as readSettingsFile, jt as AFFILIATIONS, k as runMulti, kt as UNDECLARED_POLICY_SECTIONS, l as SNAPSHOT_SCHEMA_URL, lt as worstOf, m as readLayerFiles, mt as allEndpoints, n as applyRepository, nt as quoteList, o as snapshotRepository, ot as runForRepo, p as foldLayers, pt as SECTIONS, q as MARKER_LABEL, qt as silentIo, r as checkRepository, rt as SectionSelection, s as validateSettings, st as skippedSectionKeys, t as runMerge, tt as describeProblem, u as concludeSnapshot, ut as describeOptOut, v as MERGE_INPUTS, vt as readGating, w as SNAPSHOT_REJECTED_INPUTS, wt as endpointPath, x as MODES, xt as writeGatedReads, y as MERGE_ONLY_INPUTS, yt as sectionGrant, z as capturingIo, zt as SECRET_RESPONSE_WITHHELD } from "./src-BFMt0V_E.js";
1
+ import { $ as renderSnapshotYaml, A as parseSettingsDoc, At as parseReposInput, B as planRedaction, Bt as SECRET_TRANSPORT_WITHHELD, C as SNAPSHOT_ONLY_INPUTS, Ct as endpointMethod, D as DEFAULT_SETTINGS_FILE, Dt as SECTION_KEYS, E as parseSnapshotFileConfig, Et as PROBOT_PARITY_KEYS, F as concludeMerge, Ft as VISIBILITY_FILTERS, G as openReportChannel, Gt as prefixedIo, H as toPublicView, Ht as isRateLimitError, I as concludeRun, It as discoverRepos, J as MARKER_LABEL_CONFIG, Jt as resolveCentralTargets, K as ISSUE_TITLE, Kt as redactRanges, L as failRun, Lt as DEFAULT_API_VERSION, M as createVisibilityResolver, Mt as ARCHIVED_FILTERS, N as getRepoFile, Nt as DEFAULT_DISCOVERY_FILTERS, O as resolveTargets, Ot as SettingsFile, P as MERGE_RESULT, Pt as FORKS_FILTERS, Q as parseRecipient, R as PRIVATE_REPOS_POLICIES, Rt as GithubApi, S as SNAPSHOT_INPUTS, St as grantFor, T as parseConfig, Tt as DOCUMENT_DIRECTIVE_KEYS, U as PRIVATE_REPORT_CHANNELS, Ut as collectingIo, V as publicDetail, Vt as isPermissionError, W as applyMarkerInjection, Wt as maskRegistry, X as deliverArtifactReport, Xt as parseRepoSlug, Y as composeReport, Yt as dedupeTargets, Z as encryptReport, _ as INPUT_DECLS, _t as denialPosture, a as snapshotRepositories, at as preflightProbe, b as MERGE_REJECTED_INPUTS, bt as sectionOperations, c as SNAPSHOT_RESULTS, ct as validateSettingsDoc, d as runSnapshot, dt as mergeLayers, et as RERUN_ADVICE, f as runSingle, ft as stripNulls, g as FILTER_INPUTS, gt as sectionModule, h as DEFAULT_PRIVATE_REPOS, ht as allGraphqlOps, i as renderMergedYaml, it as REPO_RESULTS, j as readSettingsFile, jt as AFFILIATIONS, k as runMulti, kt as UNDECLARED_POLICY_SECTIONS, l as SNAPSHOT_SCHEMA_URL, lt as worstOf, m as readLayerFiles, mt as allEndpoints, n as applyRepository, nt as quoteList, o as snapshotRepository, ot as runForRepo, p as foldLayers, pt as SECTIONS, q as MARKER_LABEL, qt as silentIo, r as checkRepository, rt as SectionSelection, s as validateSettings, st as skippedSectionKeys, t as runMerge, tt as describeProblem, u as concludeSnapshot, ut as describeOptOut, v as MERGE_INPUTS, vt as readGating, w as SNAPSHOT_REJECTED_INPUTS, wt as endpointPath, x as MODES, xt as writeGatedReads, y as MERGE_ONLY_INPUTS, yt as sectionGrant, z as capturingIo, zt as SECRET_RESPONSE_WITHHELD } from "./src-L_4G-LmP.js";
2
2
  export { AFFILIATIONS, ARCHIVED_FILTERS, DEFAULT_API_VERSION, DEFAULT_DISCOVERY_FILTERS, DEFAULT_PRIVATE_REPOS, DEFAULT_SETTINGS_FILE, DOCUMENT_DIRECTIVE_KEYS, FILTER_INPUTS, FORKS_FILTERS, GithubApi, INPUT_DECLS, ISSUE_TITLE, MARKER_LABEL, MARKER_LABEL_CONFIG, MERGE_INPUTS, MERGE_ONLY_INPUTS, MERGE_REJECTED_INPUTS, MERGE_RESULT, MODES, PRIVATE_REPORT_CHANNELS, PRIVATE_REPOS_POLICIES, PROBOT_PARITY_KEYS, REPO_RESULTS, RERUN_ADVICE, SECRET_RESPONSE_WITHHELD, SECRET_TRANSPORT_WITHHELD, SECTIONS, SECTION_KEYS, SNAPSHOT_INPUTS, SNAPSHOT_ONLY_INPUTS, SNAPSHOT_REJECTED_INPUTS, SNAPSHOT_RESULTS, SNAPSHOT_SCHEMA_URL, SectionSelection, SettingsFile, UNDECLARED_POLICY_SECTIONS, VISIBILITY_FILTERS, allEndpoints, allGraphqlOps, applyMarkerInjection, applyRepository, capturingIo, checkRepository, collectingIo, composeReport, concludeMerge, concludeRun, concludeSnapshot, createVisibilityResolver, dedupeTargets, deliverArtifactReport, denialPosture, describeOptOut, describeProblem, discoverRepos, encryptReport, endpointMethod, endpointPath, failRun, foldLayers, getRepoFile, grantFor, isPermissionError, isRateLimitError, maskRegistry, mergeLayers, openReportChannel, parseConfig, parseRecipient, parseRepoSlug, parseReposInput, parseSettingsDoc, parseSnapshotFileConfig, planRedaction, prefixedIo, preflightProbe, publicDetail, quoteList, readGating, readLayerFiles, readSettingsFile, redactRanges, renderMergedYaml, renderSnapshotYaml, resolveCentralTargets, resolveTargets, runForRepo, runMerge, runMulti, runSingle, runSnapshot, sectionGrant, sectionModule, sectionOperations, silentIo, skippedSectionKeys, snapshotRepositories, snapshotRepository, stripNulls, toPublicView, validateSettings, validateSettingsDoc, worstOf, writeGatedReads };
@@ -2635,6 +2635,20 @@ function plainData(value) {
2635
2635
  function gated(bound) {
2636
2636
  return Object.fromEntries(Object.entries(bound).map(([name, helper]) => [name, typeof helper === "function" ? (_exec, ...args) => helper(...args) : helper]));
2637
2637
  }
2638
+ let mintPolicy;
2639
+ (class DenialPolicy {
2640
+ input;
2641
+ constructor(input) {
2642
+ this.input = input;
2643
+ }
2644
+ static {
2645
+ mintPolicy = (input) => new DenialPolicy(input);
2646
+ }
2647
+ /** Under warn a denied sub-read is noted and left out; under fail it propagates. */
2648
+ get notesDenials() {
2649
+ return this.input === "warn";
2650
+ }
2651
+ });
2638
2652
  function driftOf(op) {
2639
2653
  return "unverifiable" in op.drift ? op.drift.lines : op.drift;
2640
2654
  }
@@ -2698,6 +2712,12 @@ function planContext(meta, api, repo) {
2698
2712
  read: boundReads(meta, api, repo)
2699
2713
  };
2700
2714
  }
2715
+ function snapshotContext(meta, api, repo, onMissingPermission) {
2716
+ return {
2717
+ ...planContext(meta, api, repo),
2718
+ onMissingPermission: mintPolicy(onMissingPermission)
2719
+ };
2720
+ }
2701
2721
  //#endregion
2702
2722
  //#region src/sections/shared/snapshot-helpers.ts
2703
2723
  function defOf(schema) {
@@ -2770,15 +2790,17 @@ function rejectLiveDuplicates(section, noun, items, keyOf, describe) {
2770
2790
  if (collisions.length > 0) throw new Error(`${section.key}: GitHub holds ${noun}s that resolve to one identity: ${collisions.join("; ")}. This section manages one ${noun} per identity, so the snapshot cannot declare them; delete all but one of each on GitHub, then snapshot again`);
2771
2791
  }
2772
2792
  /**
2773
- * One read of a snapshot that a denial may take out without failing the section: a
2774
- * PermissionDenied becomes a note naming the key left out and the grant advice, anything else
2775
- * propagates. For a section whose keys sit behind different grants (repository, actions).
2793
+ * One read of a snapshot whose denial is that read's alone, for a section whose keys sit behind
2794
+ * different grants (repository, actions, environments). Under `warn` a PermissionDenied becomes a
2795
+ * note naming the key left out and the grant advice; under `fail` it propagates, so the engine
2796
+ * fails the section exactly as it does a primary read's denial. Anything else propagates. The
2797
+ * policy arrives as the carrier only snapshotContext() mints, so a section cannot pick "warn".
2776
2798
  */
2777
- async function readOrNote(notes, label, read) {
2799
+ async function readOrNote(ctx, notes, label, read) {
2778
2800
  try {
2779
2801
  return { value: await read() };
2780
2802
  } catch (error) {
2781
- if (error instanceof PermissionDenied) {
2803
+ if (error instanceof PermissionDenied && ctx.onMissingPermission.notesDenials) {
2782
2804
  notes.push(`${label}: left out of the snapshot - ${error.detail}`);
2783
2805
  return { denied: true };
2784
2806
  }
@@ -3002,7 +3024,7 @@ const KEY_DESTINATION = {
3002
3024
  snapshot: async (ctx, _section, _base, notes) => {
3003
3025
  const limits = {};
3004
3026
  for (const [key, wiring] of Object.entries(CACHE_ENDPOINT_BY_KEY)) {
3005
- const read = await readOrNote(notes, `actions.cache.${key}`, () => ctx.read[wiring.get].call());
3027
+ const read = await readOrNote(ctx, notes, `actions.cache.${key}`, () => ctx.read[wiring.get].call());
3006
3028
  if (!("denied" in read)) Object.assign(limits, read.value);
3007
3029
  }
3008
3030
  return Object.keys(limits).length === 0 ? void 0 : sliceOf("cache")(limits);
@@ -3054,7 +3076,7 @@ async function planRouted(key, ctx, section, desired, plan) {
3054
3076
  }
3055
3077
  /** Read one routed key back; generic so the handler and the value stay correlated to one key. */
3056
3078
  async function snapshotRouted(key, ctx, section, base, notes) {
3057
- const read = await readOrNote(notes, `actions.${key}`, () => ROUTED_DESTINATIONS[key].snapshot(ctx, section, base, notes));
3079
+ const read = await readOrNote(ctx, notes, `actions.${key}`, () => ROUTED_DESTINATIONS[key].snapshot(ctx, section, base, notes));
3058
3080
  return "denied" in read ? void 0 : read.value;
3059
3081
  }
3060
3082
  function keysTo(destination) {
@@ -3114,7 +3136,7 @@ const actionsSection = {
3114
3136
  const notes = [];
3115
3137
  const base = projectOntoSchema(ActionsConfig, await ctx.read.getPermissions.call());
3116
3138
  const value = { ...base };
3117
- const workflow = await readOrNote(notes, `actions.${[...WORKFLOW_KEYS].join("/")}`, () => ctx.read.getWorkflow.call());
3139
+ const workflow = await readOrNote(ctx, notes, `actions.${[...WORKFLOW_KEYS].join("/")}`, () => ctx.read.getWorkflow.call());
3118
3140
  if (!("denied" in workflow)) Object.assign(value, projectOntoSchema(ActionsConfig, workflow.value));
3119
3141
  for (const key of ROUTED_KEYS) {
3120
3142
  const read = await snapshotRouted(key, ctx, this, base, notes);
@@ -5936,10 +5958,7 @@ function liveRuleId(rule, envName) {
5936
5958
  * spec, so an ABSENT list reads as empty, while a PRESENT off-shape value fails loudly in parseLive.
5937
5959
  */
5938
5960
  async function listProtectionRules(ctx, section, envName) {
5939
- return parseLive(section, ENDPOINTS$10.listProtectionRules, z.looseObject({ custom_deployment_protection_rules: z.array(LiveProtectionRule).optional() }).nullable(), await ctx.read.listProtectionRules.call({
5940
- params: { environment_name: envName },
5941
- describe: `listing deployment protection rules of environment "${envName}"`
5942
- }), `environment "${envName}"`)?.custom_deployment_protection_rules ?? [];
5961
+ return parseLive(section, ENDPOINTS$10.listProtectionRules, z.looseObject({ custom_deployment_protection_rules: z.array(LiveProtectionRule).optional() }).nullable(), await ctx.read.listProtectionRules.call({ params: { environment_name: envName } }), `environment "${envName}"`)?.custom_deployment_protection_rules ?? [];
5943
5962
  }
5944
5963
  /** An unlisted slug means the App is not installed, which nothing this section may call can change. */
5945
5964
  function resolveIntegrationId(apps, slug, envName) {
@@ -6498,7 +6517,7 @@ function wrapped(key, entries) {
6498
6517
  * flag enables them (the endpoint 404s otherwise); a disabled protection rule is not an active
6499
6518
  * gate, so it is not declared. Each secret becomes a `$NAME` reference with a note asking for it.
6500
6519
  * The policy and rule lists sit behind the Actions grant, not the section's, so a denial there
6501
- * leaves that key out with a note instead of taking the whole section down.
6520
+ * is the key's own: a note under the warn policy, the section's failure under fail.
6502
6521
  */
6503
6522
  async function snapshotNested(ctx, section, envName, liveEnv) {
6504
6523
  const nested = {};
@@ -6518,10 +6537,10 @@ async function snapshotNested(ctx, section, envName, liveEnv) {
6518
6537
  for (const { name, variable } of references) notes.push(`environments[${envName}].secrets[${name}]: value of ${name} is not readable; export it into the environment as ${variable} before apply`);
6519
6538
  }
6520
6539
  if (liveEnv.deployment_branch_policy?.custom_branch_policies === true) {
6521
- const policies = await readOrNote(notes, `environments[${envName}].deployment_branch_policies`, () => listBranchPolicies(ctx, section, envName));
6540
+ const policies = await readOrNote(ctx, notes, `environments[${envName}].deployment_branch_policies`, () => listBranchPolicies(ctx, section, envName));
6522
6541
  if ("value" in policies && policies.value.length > 0) nested.deployment_branch_policies = wrapped("deployment_branch_policies", policies.value.map((policy) => projectOntoSchema(DeploymentBranchPolicyConfig, policy)));
6523
6542
  }
6524
- const rules = await readOrNote(notes, `environments[${envName}].deployment_protection_rules`, () => listProtectionRules(ctx, section, envName));
6543
+ const rules = await readOrNote(ctx, notes, `environments[${envName}].deployment_protection_rules`, () => listProtectionRules(ctx, section, envName));
6525
6544
  if ("value" in rules) {
6526
6545
  const enabled = rules.value.filter((rule) => rule.enabled !== false);
6527
6546
  if (enabled.length > 0) nested.deployment_protection_rules = wrapped("deployment_protection_rules", enabled.map((rule) => ({ app: liveRuleSlug(rule, envName) })));
@@ -7647,7 +7666,7 @@ const repositorySection = {
7647
7666
  if (live.topics !== void 0 && live.topics !== null && live.topics.length > 0) value.topics = [...live.topics];
7648
7667
  const probes = [];
7649
7668
  for (const toggle of READABLE_TOGGLES) {
7650
- const read = await readOrNote(notes, `repository.${toggle.key}`, async () => {
7669
+ const read = await readOrNote(ctx, notes, `repository.${toggle.key}`, async () => {
7651
7670
  const answer = await ctx.read[toggle.get].tryCall();
7652
7671
  if ("error" in answer) return {
7653
7672
  live: void 0,
@@ -7670,7 +7689,7 @@ const repositorySection = {
7670
7689
  if (live !== void 0 && toggle.isEnforced?.(live) === true) notes.push(`repository.${toggle.key}: ${OWNER_ENFORCED}, so it reads back as ${enabled} but cannot be changed from the repository`);
7671
7690
  }
7672
7691
  for (const toggle of WRITE_ONLY_TOGGLES) notes.push(`repository.${toggle.key}: GitHub exposes no endpoint to read ${toggle.label} back, so the snapshot leaves it out; declare it yourself to manage it`);
7673
- const routed = await readOrNote(notes, GRAPHQL_ROUTED_KEYS.map((entry) => `repository.${entry.key}`).join(" and "), () => ctx.read.featuresQuery.call(repoVariables(ctx)));
7692
+ const routed = await readOrNote(ctx, notes, GRAPHQL_ROUTED_KEYS.map((entry) => `repository.${entry.key}`).join(" and "), () => ctx.read.featuresQuery.call(repoVariables(ctx)));
7674
7693
  if (!("denied" in routed)) {
7675
7694
  const repository = repositoryNode(routed.value);
7676
7695
  for (const entry of GRAPHQL_ROUTED_KEYS) {
@@ -9719,7 +9738,7 @@ async function snapshotRepository$1(api, opts, io) {
9719
9738
  const seen = { notFound: false };
9720
9739
  let snapshot;
9721
9740
  try {
9722
- snapshot = await section.snapshot(planContext(section, watchingNotFound(api, absentRead, seen), opts.repo));
9741
+ snapshot = await section.snapshot(snapshotContext(section, watchingNotFound(api, absentRead, seen), opts.repo, opts.onMissingPermission));
9723
9742
  } catch (error) {
9724
9743
  if (error instanceof PermissionDenied) {
9725
9744
  const status = opts.onMissingPermission === "warn" ? "skipped" : "failed";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vivswan/github-settings-as-code",
3
- "version": "2.0.1-main.1081.gc25bbfe",
3
+ "version": "2.0.1-main.1089.g2dd2ba5",
4
4
  "description": "GitHub Action applying declarative repository settings: rulesets, labels, branch protection, and more.",
5
5
  "license": "SEE LICENSE IN LICENSE.md",
6
6
  "type": "module",