@vivswan/github-settings-as-code 2.0.1-main.1089.g2dd2ba5 → 2.0.1-main.1103.g624f45a
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/lib/pkg/cli.js +1 -1
- package/lib/pkg/index.d.ts +41 -26
- package/lib/pkg/index.js +1 -1
- package/lib/pkg/{src-L_4G-LmP.js → src-BhuD5Ycj.js} +50 -39
- package/package.json +1 -1
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-
|
|
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-BhuD5Ycj.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";
|
package/lib/pkg/index.d.ts
CHANGED
|
@@ -1461,6 +1461,8 @@ interface UndeclaredPolicyList<E> {
|
|
|
1461
1461
|
_layering?: "merge" | "replace";
|
|
1462
1462
|
}
|
|
1463
1463
|
type MustBeNever<T extends never> = T;
|
|
1464
|
+
/** Readonly through every nested object and array; functions pass untouched. The type twin of a deep Object.freeze. */
|
|
1465
|
+
type DeepReadonly<T> = T extends ((...args: never[]) => unknown) ? T : T extends object ? { readonly [P in keyof T]: DeepReadonly<T[P]>; } : T;
|
|
1464
1466
|
//#endregion
|
|
1465
1467
|
//#region src/sections/contract/module.d.ts
|
|
1466
1468
|
type EndpointDict = Readonly<Record<string, EndpointDecl>>;
|
|
@@ -2012,12 +2014,15 @@ type Address<Ends extends ListEndpoints> = Ends extends {
|
|
|
2012
2014
|
readonly route: infer U extends string;
|
|
2013
2015
|
};
|
|
2014
2016
|
} ? SameParams<U, Ends["remove"]["route"]> : Readonly<Record<PathParams<Ends["remove"]["route"]>, string>>;
|
|
2015
|
-
/**
|
|
2016
|
-
|
|
2017
|
+
/**
|
|
2018
|
+
* Declared fields only: an omitted optional stays OUT (never undefined), so it is neither written nor compared.
|
|
2019
|
+
* A section narrows it to pin a folded field's brand on its lens (labels' HexColor).
|
|
2020
|
+
*/
|
|
2021
|
+
type ListWrite<F extends string> = { readonly [P in F]: string; } & {
|
|
2017
2022
|
readonly [key: string]: PlainData;
|
|
2018
2023
|
};
|
|
2019
2024
|
/** A live item in the same terms, each field normalized as GitHub stores it. */
|
|
2020
|
-
type
|
|
2025
|
+
type ListComparable<F extends string> = { readonly [P in F]: string; } & Readonly<Record<string, unknown>>;
|
|
2021
2026
|
type NoteWording = Pick<Parameters<typeof undeclaredNote>[0], "state" | "add" | "manage">;
|
|
2022
2027
|
type DriftWording = Pick<Parameters<typeof undeclaredDrift>[1], "state" | "add" | "keep">;
|
|
2023
2028
|
/** `unpaginated` also drives the derived mock's list handler (test/e2e/mock/list-fragment.ts). */
|
|
@@ -2027,7 +2032,11 @@ interface Listing {
|
|
|
2027
2032
|
/** GitHub serves the whole list in one response and ignores page params (autolinks), so the page loop is skipped. */
|
|
2028
2033
|
readonly unpaginated?: true;
|
|
2029
2034
|
}
|
|
2030
|
-
|
|
2035
|
+
/**
|
|
2036
|
+
* `Key` is the fold's output: the planner keys every live-versus-declared lookup by it, so an unfolded
|
|
2037
|
+
* name cannot be looked up, and a section's brand (labels' NameKey) survives to `decl`.
|
|
2038
|
+
*/
|
|
2039
|
+
interface ListSectionDecl<K extends ListSectionKey, Ends extends ListEndpoints, Live extends object, F extends string, Key extends string> {
|
|
2031
2040
|
readonly key: K;
|
|
2032
2041
|
readonly permission: SectionPermission;
|
|
2033
2042
|
readonly undeclaredDefault: UndeclaredPolicy;
|
|
@@ -2042,8 +2051,8 @@ interface ListSectionDecl<K extends ListSectionKey, Ends extends ListEndpoints,
|
|
|
2042
2051
|
readonly identity: {
|
|
2043
2052
|
/** The write field naming the resource, as the live item carries it ("name", "title"). */
|
|
2044
2053
|
readonly field: F;
|
|
2045
|
-
/** Folds a name to
|
|
2046
|
-
readonly fold
|
|
2054
|
+
/** Folds a name to the key GitHub matches it by; `exactName` when GitHub matches exactly. */
|
|
2055
|
+
readonly fold: (name: string) => Key;
|
|
2047
2056
|
/**
|
|
2048
2057
|
* Names an entry also answers to (a label's pre-rename `name`), so a live item under one is this
|
|
2049
2058
|
* entry's, renamed by the update, not undeclared.
|
|
@@ -2062,7 +2071,7 @@ interface ListSectionDecl<K extends ListSectionKey, Ends extends ListEndpoints,
|
|
|
2062
2071
|
readonly address: [Address<Ends>] extends [never] ? never : (live: Live) => Address<Ends>;
|
|
2063
2072
|
readonly lens: {
|
|
2064
2073
|
/** The entry in wire terms: the create body, and what a converged live item reads back as. */
|
|
2065
|
-
readonly toWrite: (entry: Entry<K>) =>
|
|
2074
|
+
readonly toWrite: (entry: Entry<K>) => ListWrite<F>;
|
|
2066
2075
|
/**
|
|
2067
2076
|
* A live item in the same terms as toWrite, so the two compare field by field.
|
|
2068
2077
|
*
|
|
@@ -2070,7 +2079,7 @@ interface ListSectionDecl<K extends ListSectionKey, Ends extends ListEndpoints,
|
|
|
2070
2079
|
* other declared fields -> normalized as GitHub stores them (a color lowercased without "#", a null description as "")
|
|
2071
2080
|
* every other live field -> kept, so declared passthrough keys compare against what the API echoed
|
|
2072
2081
|
*/
|
|
2073
|
-
readonly fromLive: (live: Live) =>
|
|
2082
|
+
readonly fromLive: (live: Live) => ListComparable<F>;
|
|
2074
2083
|
/** Per entry field holding a list, the item key to pair by (see DeltaOptions.matchBy); `{}` when none does. */
|
|
2075
2084
|
readonly matchBy: Readonly<Partial<Record<keyof Entry<K> & string, string>>>;
|
|
2076
2085
|
};
|
|
@@ -2078,7 +2087,7 @@ interface ListSectionDecl<K extends ListSectionKey, Ends extends ListEndpoints,
|
|
|
2078
2087
|
* The body recreating a drifted item of a resource GitHub cannot edit (no update role), when the
|
|
2079
2088
|
* write alone would drop a live field the file leaves undeclared (a deploy key's read_only).
|
|
2080
2089
|
*/
|
|
2081
|
-
readonly recreate?: "update" extends keyof Ends ? never : (live: Live, write:
|
|
2090
|
+
readonly recreate?: "update" extends keyof Ends ? never : (live: Live, write: ListWrite<F>) => ListWrite<F>;
|
|
2082
2091
|
/**
|
|
2083
2092
|
* Conflicts the identities cannot show, one line each naming the fix; any line fails the section.
|
|
2084
2093
|
*
|
|
@@ -2086,8 +2095,8 @@ interface ListSectionDecl<K extends ListSectionKey, Ends extends ListEndpoints,
|
|
|
2086
2095
|
* `live` -> runs after the read and before any write (a deploy key's material held by another key)
|
|
2087
2096
|
*/
|
|
2088
2097
|
readonly conflicts?: {
|
|
2089
|
-
readonly declared?: (writes: readonly
|
|
2090
|
-
readonly live?: (writes: readonly
|
|
2098
|
+
readonly declared?: (writes: readonly ListWrite<F>[]) => readonly string[];
|
|
2099
|
+
readonly live?: (writes: readonly ListWrite<F>[], live: readonly ListComparable<F>[]) => readonly string[];
|
|
2091
2100
|
};
|
|
2092
2101
|
readonly prose: {
|
|
2093
2102
|
/** What apply does to an undeclared live resource, as the note and drift spell it ("DELETE it"). */
|
|
@@ -2104,7 +2113,7 @@ interface ListSectionDecl<K extends ListSectionKey, Ends extends ListEndpoints,
|
|
|
2104
2113
|
readonly layering?: Pick<KeyedListLayering, "combine">;
|
|
2105
2114
|
}
|
|
2106
2115
|
/** The module listSection() mints: SectionModule<K, Ends> at the registry, plus its declaration. */
|
|
2107
|
-
interface ListSectionModule<K extends ListSectionKey, Ends extends ListEndpoints, Live extends object, F extends string> {
|
|
2116
|
+
interface ListSectionModule<K extends ListSectionKey, Ends extends ListEndpoints, Live extends object, F extends string, Key extends string> {
|
|
2108
2117
|
readonly key: K;
|
|
2109
2118
|
readonly permission: SectionPermission;
|
|
2110
2119
|
readonly undeclaredDefault: UndeclaredPolicy;
|
|
@@ -2115,9 +2124,16 @@ interface ListSectionModule<K extends ListSectionKey, Ends extends ListEndpoints
|
|
|
2115
2124
|
readonly plan: (ctx: PlanContext<Ends>, desired: Declared<K>) => Promise<SectionPlan<PlannedOp<Ends>>>;
|
|
2116
2125
|
readonly snapshot: (ctx: SnapshotContext<Ends>) => Promise<SectionSnapshot<K>>;
|
|
2117
2126
|
/** The declaration, for the harness derivations (the mock's transformers, the fuzz witness). */
|
|
2118
|
-
readonly decl: ListSectionDecl<K, Ends, Live, F>;
|
|
2127
|
+
readonly decl: ListSectionDecl<K, Ends, Live, F, Key>;
|
|
2119
2128
|
}
|
|
2120
2129
|
//#endregion
|
|
2130
|
+
//#region src/sections/labels/index.d.ts
|
|
2131
|
+
/** Case-insensitive matching is the section's whole contract; the brand marks a name as already folded. */
|
|
2132
|
+
declare const labelNameKey: unique symbol;
|
|
2133
|
+
type NameKey = string & {
|
|
2134
|
+
readonly [labelNameKey]: true;
|
|
2135
|
+
};
|
|
2136
|
+
//#endregion
|
|
2121
2137
|
//#region src/sections/environments/endpoints.d.ts
|
|
2122
2138
|
declare const ENDPOINTS$1: {
|
|
2123
2139
|
readonly list: {
|
|
@@ -3502,7 +3518,7 @@ declare const byKey: {
|
|
|
3502
3518
|
name: string;
|
|
3503
3519
|
color: string;
|
|
3504
3520
|
description: string | null;
|
|
3505
|
-
}, "name">;
|
|
3521
|
+
}, "name", NameKey>;
|
|
3506
3522
|
rulesets: {
|
|
3507
3523
|
key: "rulesets";
|
|
3508
3524
|
undeclaredDefault: "keep";
|
|
@@ -5039,7 +5055,7 @@ declare const byKey: {
|
|
|
5039
5055
|
[x: string]: unknown;
|
|
5040
5056
|
id: number;
|
|
5041
5057
|
key_prefix: string;
|
|
5042
|
-
}, "key_prefix">;
|
|
5058
|
+
}, "key_prefix", string>;
|
|
5043
5059
|
actions: {
|
|
5044
5060
|
key: "actions";
|
|
5045
5061
|
undeclaredDefault: "untouched";
|
|
@@ -7259,8 +7275,8 @@ declare const byKey: {
|
|
|
7259
7275
|
id: number;
|
|
7260
7276
|
title: string;
|
|
7261
7277
|
key: string;
|
|
7262
|
-
read_only
|
|
7263
|
-
}, "title">;
|
|
7278
|
+
read_only: boolean;
|
|
7279
|
+
}, "title", string>;
|
|
7264
7280
|
secret_scanning_custom_patterns: {
|
|
7265
7281
|
key: "secret_scanning_custom_patterns";
|
|
7266
7282
|
undeclaredDefault: "keep";
|
|
@@ -7507,26 +7523,25 @@ type SectionGraphqlKey<K extends SectionKey = SectionKey> = { [S in SectionKey]:
|
|
|
7507
7523
|
export declare const SECTIONS: readonly SectionModule[];
|
|
7508
7524
|
/** The section module for a key (validate.ts reads shape + closedSurface). */
|
|
7509
7525
|
export declare function sectionModule<K extends SectionKey>(key: K): SectionModule<K>;
|
|
7510
|
-
type TaggedEndpoint = EndpointDecl & {
|
|
7526
|
+
type TaggedEndpoint = DeepReadonly<EndpointDecl & {
|
|
7511
7527
|
readonly section: SectionKey;
|
|
7512
7528
|
readonly role: string;
|
|
7513
|
-
}
|
|
7529
|
+
}>;
|
|
7514
7530
|
/**
|
|
7515
7531
|
* The single view the e2e mock's route table and USED_PATHS iterate, keyed by the exact SectionEndpointKey
|
|
7516
|
-
* union so an undeclared lookup does not compile.
|
|
7517
|
-
*
|
|
7518
|
-
*
|
|
7519
|
-
* `sections` injectable -> the scope-free assert is testable; an injected list keeps string keys
|
|
7532
|
+
* union so an undeclared lookup does not compile. Deep-frozen, and the source declaration with it: the
|
|
7533
|
+
* entries reference the declarations, which must never mutate. `sections` is injectable so the scope-free
|
|
7534
|
+
* assert is testable; an injected list keeps string keys.
|
|
7520
7535
|
*/
|
|
7521
7536
|
export declare function allEndpoints(): Readonly<Record<SectionEndpointKey, TaggedEndpoint>>;
|
|
7522
7537
|
export declare function allEndpoints(sections: ReadonlyArray<Pick<SectionModule, "key" | "endpoints">>): Readonly<Record<string, TaggedEndpoint>>;
|
|
7523
|
-
type TaggedGraphqlOp = GraphqlOpDecl & {
|
|
7538
|
+
type TaggedGraphqlOp = DeepReadonly<GraphqlOpDecl & {
|
|
7524
7539
|
readonly section: SectionKey;
|
|
7525
7540
|
readonly role: string;
|
|
7526
|
-
}
|
|
7541
|
+
}>;
|
|
7527
7542
|
/**
|
|
7528
7543
|
* The allEndpoints() sibling for the mock's dispatch table, the coverage tripwire, and the fault-key
|
|
7529
|
-
* universe; frozen for the same reason. Operation NAMES must be globally unique (the wire dispatch key),
|
|
7544
|
+
* universe; deep-frozen for the same reason. Operation NAMES must be globally unique (the wire dispatch key),
|
|
7530
7545
|
* and a role never collides with a REST role in the same section (fault directives share one "section.role" key space).
|
|
7531
7546
|
*/
|
|
7532
7547
|
export declare function allGraphqlOps(): Readonly<Record<SectionGraphqlKey, TaggedGraphqlOp>>;
|
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-
|
|
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-BhuD5Ycj.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 };
|
|
@@ -3609,8 +3609,8 @@ const LiveVariable = z.looseObject({
|
|
|
3609
3609
|
name: z.string(),
|
|
3610
3610
|
value: z.string()
|
|
3611
3611
|
});
|
|
3612
|
-
function missingVariableDrift(
|
|
3613
|
-
return `${label}: missing - declared in the settings file but not on
|
|
3612
|
+
function missingVariableDrift(label) {
|
|
3613
|
+
return `${label}: missing - declared in the settings file but not on the repo; apply will create it`;
|
|
3614
3614
|
}
|
|
3615
3615
|
function valueDriftLine(label, declared, live) {
|
|
3616
3616
|
return `${label}.value: declared ${JSON.stringify(declared)} != live ${JSON.stringify(live)}; apply will set the declared value`;
|
|
@@ -3618,7 +3618,7 @@ function valueDriftLine(label, declared, live) {
|
|
|
3618
3618
|
function undeclaredVariableNote(scope, liveName) {
|
|
3619
3619
|
return undeclaredNote({
|
|
3620
3620
|
subject: `${scope.noun} "${liveName}"`,
|
|
3621
|
-
state:
|
|
3621
|
+
state: "exists on the repo but is not declared",
|
|
3622
3622
|
action: "DELETE it"
|
|
3623
3623
|
});
|
|
3624
3624
|
}
|
|
@@ -3635,8 +3635,6 @@ function liveVariablesByKey(live) {
|
|
|
3635
3635
|
}
|
|
3636
3636
|
async function planVariables$1(scope, opts) {
|
|
3637
3637
|
const { entries, policy, defaultPolicy } = opts;
|
|
3638
|
-
const changeSuffix = scope.changeSuffix ?? "";
|
|
3639
|
-
const removeSuffix = scope.removeSuffix ?? "";
|
|
3640
3638
|
const plan = {
|
|
3641
3639
|
ops: [],
|
|
3642
3640
|
notes: [],
|
|
@@ -3650,14 +3648,13 @@ async function planVariables$1(scope, opts) {
|
|
|
3650
3648
|
const { name: _name, value: _value, ...extraKeys } = variable;
|
|
3651
3649
|
if (!existing) {
|
|
3652
3650
|
plan.ops.push(scope.create({
|
|
3653
|
-
name: variable.name,
|
|
3654
3651
|
payload: {
|
|
3655
3652
|
name: variable.name,
|
|
3656
3653
|
value: variable.value,
|
|
3657
3654
|
...extraKeys
|
|
3658
3655
|
},
|
|
3659
|
-
drift: [missingVariableDrift(
|
|
3660
|
-
change: `created ${scope.noun} "${variable.name}"
|
|
3656
|
+
drift: [missingVariableDrift(label)],
|
|
3657
|
+
change: `created ${scope.noun} "${variable.name}"`
|
|
3661
3658
|
}));
|
|
3662
3659
|
continue;
|
|
3663
3660
|
}
|
|
@@ -3666,16 +3663,13 @@ async function planVariables$1(scope, opts) {
|
|
|
3666
3663
|
const phantom = phantomKeys(extraKeys, existing);
|
|
3667
3664
|
if (phantom.length > 0) plan.notes.push(phantomNote(label, phantom, "variable", "this update will re-run"));
|
|
3668
3665
|
plan.ops.push(scope.update({
|
|
3669
|
-
|
|
3670
|
-
declared: variable.name,
|
|
3671
|
-
live: existing.name
|
|
3672
|
-
},
|
|
3666
|
+
liveName: existing.name,
|
|
3673
3667
|
payload: {
|
|
3674
3668
|
value: variable.value,
|
|
3675
3669
|
...extraKeys
|
|
3676
3670
|
},
|
|
3677
3671
|
drift: [first, ...rest],
|
|
3678
|
-
change: `updated ${scope.noun} "${variable.name}"
|
|
3672
|
+
change: `updated ${scope.noun} "${variable.name}"`
|
|
3679
3673
|
}));
|
|
3680
3674
|
}
|
|
3681
3675
|
for (const variable of liveByKey.values()) {
|
|
@@ -3684,7 +3678,7 @@ async function planVariables$1(scope, opts) {
|
|
|
3684
3678
|
else plan.ops.push(scope.remove({
|
|
3685
3679
|
name: variable.name,
|
|
3686
3680
|
drift: [undeclaredVariableDrift(scope, defaultPolicy, variable.name)],
|
|
3687
|
-
change: `DELETED undeclared ${scope.noun} "${variable.name}"
|
|
3681
|
+
change: `DELETED undeclared ${scope.noun} "${variable.name}"`
|
|
3688
3682
|
}));
|
|
3689
3683
|
}
|
|
3690
3684
|
return plan;
|
|
@@ -3760,7 +3754,7 @@ function repoVariablesSection(family) {
|
|
|
3760
3754
|
}),
|
|
3761
3755
|
update: (write) => ({
|
|
3762
3756
|
role: "update",
|
|
3763
|
-
params: { name: write.
|
|
3757
|
+
params: { name: write.liveName },
|
|
3764
3758
|
payload: write.payload,
|
|
3765
3759
|
drift: write.drift,
|
|
3766
3760
|
change: write.change
|
|
@@ -3841,6 +3835,10 @@ const agentsVariablesSection = repoVariablesSection({
|
|
|
3841
3835
|
function updateRole(endpoints) {
|
|
3842
3836
|
return "update" in endpoints ? endpoints.update : void 0;
|
|
3843
3837
|
}
|
|
3838
|
+
/** The fold of a section GitHub matches exactly: the key IS the name. */
|
|
3839
|
+
function exactName(name) {
|
|
3840
|
+
return name;
|
|
3841
|
+
}
|
|
3844
3842
|
const UPDATE_REMEDIES = {
|
|
3845
3843
|
value: "; apply will set the declared value",
|
|
3846
3844
|
rename: "apply will rename it",
|
|
@@ -3857,7 +3855,7 @@ const RECREATE_REMEDIES = {
|
|
|
3857
3855
|
* which the merge refuses and a validated entry never is.
|
|
3858
3856
|
*/
|
|
3859
3857
|
function identityClaims(identity, entry) {
|
|
3860
|
-
const { field, renameKey, fold
|
|
3858
|
+
const { field, renameKey, fold } = identity;
|
|
3861
3859
|
const names = [(renameKey === void 0 ? void 0 : entry[renameKey]) ?? entry[field], ...identity.aliases?.(entry) ?? []];
|
|
3862
3860
|
if (!names.every((name) => typeof name === "string")) return null;
|
|
3863
3861
|
return [...new Set(names.map(fold))];
|
|
@@ -3895,7 +3893,7 @@ async function readList(decl, ctx) {
|
|
|
3895
3893
|
}
|
|
3896
3894
|
async function planList(decl, section, ctx, declared) {
|
|
3897
3895
|
const { key, noun, identity, lens, prose, endpoints } = decl;
|
|
3898
|
-
const fold = identity
|
|
3896
|
+
const { fold } = identity;
|
|
3899
3897
|
const update = updateRole(endpoints);
|
|
3900
3898
|
const remedies = update === void 0 ? RECREATE_REMEDIES : UPDATE_REMEDIES;
|
|
3901
3899
|
const defaultPolicy = defaultUndeclaredPolicy(section);
|
|
@@ -4019,10 +4017,9 @@ async function snapshotList(decl, section, ctx) {
|
|
|
4019
4017
|
value: void 0,
|
|
4020
4018
|
notes: []
|
|
4021
4019
|
};
|
|
4022
|
-
const fold = decl.identity.fold ?? ((name) => name);
|
|
4023
4020
|
const comparable = live.map((item) => decl.lens.fromLive(item));
|
|
4024
4021
|
const nameOfItem = (item) => nameOf(item, decl.identity.field);
|
|
4025
|
-
rejectLiveDuplicates(section, decl.noun, comparable, (item) => fold(nameOfItem(item)), nameOfItem);
|
|
4022
|
+
rejectLiveDuplicates(section, decl.noun, comparable, (item) => decl.identity.fold(nameOfItem(item)), nameOfItem);
|
|
4026
4023
|
return {
|
|
4027
4024
|
value: knobbedSnapshot(section, comparable.map((item) => projectOntoSchema(decl.entry, item))),
|
|
4028
4025
|
notes: []
|
|
@@ -4083,7 +4080,10 @@ const autolinksSection = listSection({
|
|
|
4083
4080
|
}
|
|
4084
4081
|
},
|
|
4085
4082
|
listing: { unpaginated: true },
|
|
4086
|
-
identity: {
|
|
4083
|
+
identity: {
|
|
4084
|
+
field: "key_prefix",
|
|
4085
|
+
fold: exactName
|
|
4086
|
+
},
|
|
4087
4087
|
address: (live) => ({ autolink_id: String(live.id) }),
|
|
4088
4088
|
lens: {
|
|
4089
4089
|
toWrite: ({ key_prefix, url_template, is_alphanumeric, ...passthrough }) => ({
|
|
@@ -5608,7 +5608,7 @@ const LiveDeployKey = z.looseObject({
|
|
|
5608
5608
|
id: z.number(),
|
|
5609
5609
|
title: z.string(),
|
|
5610
5610
|
key: z.string(),
|
|
5611
|
-
read_only: z.boolean()
|
|
5611
|
+
read_only: z.boolean()
|
|
5612
5612
|
});
|
|
5613
5613
|
const ENDPOINTS$11 = {
|
|
5614
5614
|
list: {
|
|
@@ -5652,7 +5652,10 @@ const deployKeysSection = listSection({
|
|
|
5652
5652
|
entry: DeployKeyConfig,
|
|
5653
5653
|
live: LiveDeployKey,
|
|
5654
5654
|
endpoints: ENDPOINTS$11,
|
|
5655
|
-
identity: {
|
|
5655
|
+
identity: {
|
|
5656
|
+
field: "title",
|
|
5657
|
+
fold: exactName
|
|
5658
|
+
},
|
|
5656
5659
|
address: (live) => ({ key_id: String(live.id) }),
|
|
5657
5660
|
lens: {
|
|
5658
5661
|
toWrite: ({ title, key, read_only, ...passthrough }) => ({
|
|
@@ -5663,13 +5666,20 @@ const deployKeysSection = listSection({
|
|
|
5663
5666
|
}),
|
|
5664
5667
|
fromLive: (live) => ({
|
|
5665
5668
|
...live,
|
|
5666
|
-
key: liveMaterial(live)
|
|
5667
|
-
read_only: live.read_only ?? false
|
|
5669
|
+
key: liveMaterial(live)
|
|
5668
5670
|
}),
|
|
5669
5671
|
matchBy: {}
|
|
5670
5672
|
},
|
|
5673
|
+
/**
|
|
5674
|
+
* GitHub creates a key READ/WRITE when the body omits read_only, and this file does not manage an
|
|
5675
|
+
* undeclared flag, so the flag reaches a create body only from a source that holds it:
|
|
5676
|
+
*
|
|
5677
|
+
* declared -> the declared value, on a create and a recreate alike (the write is spread last)
|
|
5678
|
+
* undeclared, create -> omitted: GitHub's default, never compared afterwards
|
|
5679
|
+
* undeclared, recreate -> the LIVE flag re-sent, so rotating a read-only key never widens its access
|
|
5680
|
+
*/
|
|
5671
5681
|
recreate: (live, write) => ({
|
|
5672
|
-
read_only: live.read_only
|
|
5682
|
+
read_only: live.read_only,
|
|
5673
5683
|
...write
|
|
5674
5684
|
}),
|
|
5675
5685
|
conflicts: {
|
|
@@ -8629,19 +8639,25 @@ function sectionModule(key) {
|
|
|
8629
8639
|
function assertScopeFree(kind, value) {
|
|
8630
8640
|
if (value.includes(":")) throw new Error(`BUG: ${kind} "${value}" contains ":", which the "section.role" key space reserves for a future scope prefix ("<scope>:<section>.<role>"); rename it without a colon`);
|
|
8631
8641
|
}
|
|
8642
|
+
/**
|
|
8643
|
+
* Freezes in place through every nested object and array, so the declarations the tagged views share
|
|
8644
|
+
* cannot mutate under any consumer; functions are left as they are (nothing reads their properties).
|
|
8645
|
+
*/
|
|
8646
|
+
function deepFreeze(value) {
|
|
8647
|
+
if (typeof value === "object" && value !== null) {
|
|
8648
|
+
Object.freeze(value);
|
|
8649
|
+
for (const child of Object.values(value)) deepFreeze(child);
|
|
8650
|
+
}
|
|
8651
|
+
return value;
|
|
8652
|
+
}
|
|
8632
8653
|
function allEndpoints(sections = SECTIONS) {
|
|
8633
8654
|
const out = {};
|
|
8634
8655
|
for (const section of sections) {
|
|
8635
8656
|
assertScopeFree("section key", section.key);
|
|
8636
8657
|
for (const [role, endpoint] of Object.entries(section.endpoints)) {
|
|
8637
8658
|
assertScopeFree("role", role);
|
|
8638
|
-
|
|
8639
|
-
|
|
8640
|
-
Object.freeze(endpoint.permission);
|
|
8641
|
-
Object.freeze(endpoint.permission.repo);
|
|
8642
|
-
}
|
|
8643
|
-
out[`${section.key}.${role}`] = Object.freeze({
|
|
8644
|
-
...endpoint,
|
|
8659
|
+
out[`${section.key}.${role}`] = deepFreeze({
|
|
8660
|
+
...deepFreeze(endpoint),
|
|
8645
8661
|
section: section.key,
|
|
8646
8662
|
role
|
|
8647
8663
|
});
|
|
@@ -8661,13 +8677,8 @@ function allGraphqlOps(sections = SECTIONS) {
|
|
|
8661
8677
|
const holder = byName.get(op.name);
|
|
8662
8678
|
if (holder !== void 0) throw new Error(`BUG: GraphQL operation name "${op.name}" is declared by both ${holder} and ${key}; operation names are the wire dispatch key and must be globally unique`);
|
|
8663
8679
|
byName.set(op.name, key);
|
|
8664
|
-
|
|
8665
|
-
|
|
8666
|
-
Object.freeze(op.permission);
|
|
8667
|
-
Object.freeze(op.permission.repo);
|
|
8668
|
-
}
|
|
8669
|
-
out[key] = Object.freeze({
|
|
8670
|
-
...op,
|
|
8680
|
+
out[key] = deepFreeze({
|
|
8681
|
+
...deepFreeze(op),
|
|
8671
8682
|
section: section.key,
|
|
8672
8683
|
role
|
|
8673
8684
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vivswan/github-settings-as-code",
|
|
3
|
-
"version": "2.0.1-main.
|
|
3
|
+
"version": "2.0.1-main.1103.g624f45a",
|
|
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",
|