@devdogsuga/devtools 0.1.25 → 0.1.27

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 (271) hide show
  1. package/README.md +72 -11
  2. package/bin/devtools.mjs +4 -4
  3. package/dist/cli-CZKkNHK5.js +6619 -0
  4. package/dist/failure-log-BBPGejQv.js +256 -0
  5. package/dist/launch.js +267 -281
  6. package/dist/local-env-D8HC2JK_.js +351 -0
  7. package/dist/local-env-DrBCXqD8.js +2 -0
  8. package/dist/menu-CQBsQRgH.js +1684 -0
  9. package/dist/peer-redirect-hooks.js +15 -0
  10. package/dist/peers-RBplwovA.js +229 -0
  11. package/dist/root-C2OmZDwD.js +94 -0
  12. package/dist/stack-Bx-55oi_.js +2 -0
  13. package/dist/stack-BynCgRVS.js +181 -0
  14. package/dist/supabase-project-0knM0mqd.js +426 -0
  15. package/dist/telemetry-C4foD8MD.js +249 -0
  16. package/dist/telemetry.js +2 -148
  17. package/package.json +10 -29
  18. package/bin/devtools-ci-bare.mjs +0 -48
  19. package/bin/devtools-ci.mjs +0 -31
  20. package/dist/args.d.ts +0 -26
  21. package/dist/args.js +0 -78
  22. package/dist/bws/bw.d.ts +0 -18
  23. package/dist/bws/bw.js +0 -70
  24. package/dist/bws/client.d.ts +0 -43
  25. package/dist/bws/client.js +0 -363
  26. package/dist/bws/environments.d.ts +0 -96
  27. package/dist/bws/environments.js +0 -111
  28. package/dist/bws/pace.d.ts +0 -25
  29. package/dist/bws/pace.js +0 -43
  30. package/dist/bws/retry.d.ts +0 -12
  31. package/dist/bws/retry.js +0 -39
  32. package/dist/bws/store.d.ts +0 -1
  33. package/dist/bws/store.js +0 -32
  34. package/dist/bws/token.d.ts +0 -31
  35. package/dist/bws/token.js +0 -88
  36. package/dist/bws/vault.d.ts +0 -29
  37. package/dist/bws/vault.js +0 -200
  38. package/dist/catalog.d.ts +0 -57
  39. package/dist/catalog.js +0 -57
  40. package/dist/cf/build.d.ts +0 -13
  41. package/dist/cf/build.js +0 -65
  42. package/dist/cf/commands.d.ts +0 -1
  43. package/dist/cf/commands.js +0 -172
  44. package/dist/cf/local-env.d.ts +0 -58
  45. package/dist/cf/local-env.js +0 -177
  46. package/dist/ci.d.ts +0 -1
  47. package/dist/ci.js +0 -328
  48. package/dist/cli.d.ts +0 -11
  49. package/dist/cli.js +0 -1094
  50. package/dist/commands.d.ts +0 -261
  51. package/dist/commands.js +0 -1318
  52. package/dist/completions.d.ts +0 -4
  53. package/dist/completions.js +0 -110
  54. package/dist/cron/commands.d.ts +0 -47
  55. package/dist/cron/commands.js +0 -372
  56. package/dist/cron/discovery.d.ts +0 -55
  57. package/dist/cron/discovery.js +0 -85
  58. package/dist/cron/schema.d.ts +0 -45
  59. package/dist/cron/schema.js +0 -35
  60. package/dist/db/config-push.d.ts +0 -8
  61. package/dist/db/config-push.js +0 -11
  62. package/dist/db/connection.d.ts +0 -79
  63. package/dist/db/connection.js +0 -114
  64. package/dist/db/exec.d.ts +0 -1
  65. package/dist/db/exec.js +0 -11
  66. package/dist/db/generate-types.d.ts +0 -1
  67. package/dist/db/generate-types.js +0 -4
  68. package/dist/db/generated-env.d.ts +0 -69
  69. package/dist/db/generated-env.js +0 -113
  70. package/dist/db/introspect.d.ts +0 -1
  71. package/dist/db/introspect.js +0 -122
  72. package/dist/db/local-env.d.ts +0 -65
  73. package/dist/db/local-env.js +0 -23
  74. package/dist/db/new-migration.d.ts +0 -8
  75. package/dist/db/new-migration.js +0 -69
  76. package/dist/db/pooler.d.ts +0 -23
  77. package/dist/db/pooler.js +0 -55
  78. package/dist/db/run.d.ts +0 -68
  79. package/dist/db/run.js +0 -156
  80. package/dist/db/seed-buckets.d.ts +0 -2
  81. package/dist/db/seed-buckets.js +0 -4
  82. package/dist/db/seed-production.d.ts +0 -8
  83. package/dist/db/seed-production.js +0 -79
  84. package/dist/db/seed-roles.d.ts +0 -7
  85. package/dist/db/seed-roles.js +0 -33
  86. package/dist/db/session-refresh.d.ts +0 -43
  87. package/dist/db/session-refresh.js +0 -40
  88. package/dist/deploy/migrations.d.ts +0 -20
  89. package/dist/deploy/migrations.js +0 -68
  90. package/dist/deploy/orphans.d.ts +0 -20
  91. package/dist/deploy/orphans.js +0 -177
  92. package/dist/deploy/preflight.d.ts +0 -53
  93. package/dist/deploy/preflight.js +0 -189
  94. package/dist/deploy/report.d.ts +0 -26
  95. package/dist/deploy/report.js +0 -70
  96. package/dist/deploy/require-planner.d.ts +0 -2
  97. package/dist/deploy/require-planner.js +0 -60
  98. package/dist/deploy/require-token.d.ts +0 -15
  99. package/dist/deploy/require-token.js +0 -78
  100. package/dist/deploy/secrets-file.d.ts +0 -17
  101. package/dist/deploy/secrets-file.js +0 -166
  102. package/dist/deploy/write-env.d.ts +0 -40
  103. package/dist/deploy/write-env.js +0 -395
  104. package/dist/docs/index-pages.d.ts +0 -38
  105. package/dist/docs/index-pages.js +0 -186
  106. package/dist/emails/commands.d.ts +0 -15
  107. package/dist/emails/commands.js +0 -130
  108. package/dist/emails/email-types.d.ts +0 -13
  109. package/dist/emails/email-types.js +0 -1
  110. package/dist/emails/fixtures.d.ts +0 -20
  111. package/dist/emails/fixtures.js +0 -20
  112. package/dist/env/audit.d.ts +0 -206
  113. package/dist/env/audit.js +0 -436
  114. package/dist/env/cloudflare.d.ts +0 -21
  115. package/dist/env/cloudflare.js +0 -61
  116. package/dist/env/commands.d.ts +0 -99
  117. package/dist/env/commands.js +0 -613
  118. package/dist/env/discovery.d.ts +0 -15
  119. package/dist/env/discovery.js +0 -239
  120. package/dist/env/document.d.ts +0 -119
  121. package/dist/env/document.js +0 -323
  122. package/dist/env/example.d.ts +0 -68
  123. package/dist/env/example.js +0 -605
  124. package/dist/env/selection.d.ts +0 -105
  125. package/dist/env/selection.js +0 -228
  126. package/dist/env-entry.d.ts +0 -49
  127. package/dist/env-entry.js +0 -204
  128. package/dist/environment-doctor.d.ts +0 -45
  129. package/dist/environment-doctor.js +0 -429
  130. package/dist/environment.d.ts +0 -92
  131. package/dist/environment.js +0 -185
  132. package/dist/fingerprint.d.ts +0 -10
  133. package/dist/fingerprint.js +0 -18
  134. package/dist/gen/campus-map.d.ts +0 -1
  135. package/dist/gen/campus-map.js +0 -473
  136. package/dist/gen/commands.d.ts +0 -1
  137. package/dist/gen/commands.js +0 -25
  138. package/dist/gen/hypno.d.ts +0 -1
  139. package/dist/gen/hypno.js +0 -134
  140. package/dist/gh/client.d.ts +0 -80
  141. package/dist/gh/client.js +0 -259
  142. package/dist/gh/environments.d.ts +0 -83
  143. package/dist/gh/environments.js +0 -178
  144. package/dist/gh/rulesets/actors.d.ts +0 -14
  145. package/dist/gh/rulesets/actors.js +0 -69
  146. package/dist/gh/rulesets/api.d.ts +0 -15
  147. package/dist/gh/rulesets/api.js +0 -104
  148. package/dist/gh/rulesets/commands.d.ts +0 -3
  149. package/dist/gh/rulesets/commands.js +0 -203
  150. package/dist/gh/rulesets/desired.d.ts +0 -112
  151. package/dist/gh/rulesets/desired.js +0 -271
  152. package/dist/gh/rulesets/diff.d.ts +0 -65
  153. package/dist/gh/rulesets/diff.js +0 -170
  154. package/dist/gh/rulesets/types.d.ts +0 -76
  155. package/dist/gh/rulesets/types.js +0 -13
  156. package/dist/gh/settings/api.d.ts +0 -40
  157. package/dist/gh/settings/api.js +0 -180
  158. package/dist/gh/settings/commands.d.ts +0 -1
  159. package/dist/gh/settings/commands.js +0 -254
  160. package/dist/gh/settings/desired.d.ts +0 -53
  161. package/dist/gh/settings/desired.js +0 -43
  162. package/dist/gh/settings/diff.d.ts +0 -57
  163. package/dist/gh/settings/diff.js +0 -161
  164. package/dist/gh/settings/types.d.ts +0 -92
  165. package/dist/gh/settings/types.js +0 -11
  166. package/dist/gh/settings/workflows.d.ts +0 -41
  167. package/dist/gh/settings/workflows.js +0 -128
  168. package/dist/grantRoot.d.ts +0 -42
  169. package/dist/grantRoot.js +0 -89
  170. package/dist/help.d.ts +0 -18
  171. package/dist/help.js +0 -169
  172. package/dist/images/commands.d.ts +0 -32
  173. package/dist/images/commands.js +0 -231
  174. package/dist/images/events.d.ts +0 -47
  175. package/dist/images/events.js +0 -116
  176. package/dist/images/graphics.d.ts +0 -70
  177. package/dist/images/graphics.js +0 -234
  178. package/dist/images/open-graph-types.d.ts +0 -75
  179. package/dist/images/open-graph-types.js +0 -1
  180. package/dist/images/prompts.d.ts +0 -34
  181. package/dist/images/prompts.js +0 -100
  182. package/dist/images/render.d.ts +0 -21
  183. package/dist/images/render.js +0 -61
  184. package/dist/images/select.d.ts +0 -64
  185. package/dist/images/select.js +0 -96
  186. package/dist/instance.d.ts +0 -69
  187. package/dist/instance.js +0 -102
  188. package/dist/invocation.d.ts +0 -49
  189. package/dist/invocation.js +0 -78
  190. package/dist/launch-ci.d.ts +0 -1
  191. package/dist/launch-ci.js +0 -90
  192. package/dist/launch.d.ts +0 -18
  193. package/dist/menu.d.ts +0 -43
  194. package/dist/menu.js +0 -300
  195. package/dist/moderation.d.ts +0 -70
  196. package/dist/moderation.js +0 -128
  197. package/dist/oauth/browser.d.ts +0 -1
  198. package/dist/oauth/browser.js +0 -13
  199. package/dist/oauth/config.d.ts +0 -24
  200. package/dist/oauth/config.js +0 -24
  201. package/dist/oauth/connect-url.d.ts +0 -17
  202. package/dist/oauth/connect-url.js +0 -23
  203. package/dist/oauth/db.d.ts +0 -37
  204. package/dist/oauth/db.js +0 -108
  205. package/dist/oauth/device.d.ts +0 -93
  206. package/dist/oauth/device.js +0 -198
  207. package/dist/oauth/discovery.d.ts +0 -28
  208. package/dist/oauth/discovery.js +0 -54
  209. package/dist/oauth/env-file.d.ts +0 -6
  210. package/dist/oauth/env-file.js +0 -35
  211. package/dist/oauth/exchange.d.ts +0 -43
  212. package/dist/oauth/exchange.js +0 -93
  213. package/dist/oauth/label.d.ts +0 -1
  214. package/dist/oauth/label.js +0 -25
  215. package/dist/oauth/loopback.d.ts +0 -52
  216. package/dist/oauth/loopback.js +0 -168
  217. package/dist/oauth/pkce.d.ts +0 -6
  218. package/dist/oauth/pkce.js +0 -34
  219. package/dist/oauth/target.d.ts +0 -54
  220. package/dist/oauth/target.js +0 -102
  221. package/dist/oauth/transport.d.ts +0 -41
  222. package/dist/oauth/transport.js +0 -32
  223. package/dist/oauth/wizard.d.ts +0 -40
  224. package/dist/oauth/wizard.js +0 -578
  225. package/dist/persona.d.ts +0 -18
  226. package/dist/persona.js +0 -221
  227. package/dist/pick.d.ts +0 -6
  228. package/dist/pick.js +0 -64
  229. package/dist/pipes.d.ts +0 -1
  230. package/dist/pipes.js +0 -25
  231. package/dist/planner/checks.d.ts +0 -26
  232. package/dist/planner/checks.js +0 -104
  233. package/dist/planner/commands.d.ts +0 -10
  234. package/dist/planner/commands.js +0 -367
  235. package/dist/planner/db.d.ts +0 -15
  236. package/dist/planner/db.js +0 -33
  237. package/dist/planner/role.d.ts +0 -33
  238. package/dist/planner/role.js +0 -68
  239. package/dist/repo/peer-redirect-hooks.d.ts +0 -16
  240. package/dist/repo/peer-redirect-hooks.js +0 -10
  241. package/dist/repo/peer-redirect.d.ts +0 -4
  242. package/dist/repo/peer-redirect.js +0 -26
  243. package/dist/repo/peers.d.ts +0 -39
  244. package/dist/repo/peers.js +0 -139
  245. package/dist/repo/resolve.d.ts +0 -41
  246. package/dist/repo/resolve.js +0 -152
  247. package/dist/repo/root.d.ts +0 -23
  248. package/dist/repo/root.js +0 -91
  249. package/dist/repo/source.d.ts +0 -14
  250. package/dist/repo/source.js +0 -54
  251. package/dist/repo/supabase-project.d.ts +0 -60
  252. package/dist/repo/supabase-project.js +0 -124
  253. package/dist/repo/tsx-loader.d.ts +0 -6
  254. package/dist/repo/tsx-loader.js +0 -41
  255. package/dist/run/pick.d.ts +0 -90
  256. package/dist/run/pick.js +0 -664
  257. package/dist/setup.d.ts +0 -1
  258. package/dist/setup.js +0 -280
  259. package/dist/stack.d.ts +0 -48
  260. package/dist/stack.js +0 -403
  261. package/dist/telemetry.d.ts +0 -59
  262. package/dist/tier.d.ts +0 -40
  263. package/dist/tier.js +0 -96
  264. package/dist/ui.d.ts +0 -40
  265. package/dist/ui.js +0 -71
  266. package/dist/version.d.ts +0 -7
  267. package/dist/version.js +0 -17
  268. package/dist/workers.d.ts +0 -5
  269. package/dist/workers.js +0 -33
  270. package/dist/workflows/commands.d.ts +0 -54
  271. package/dist/workflows/commands.js +0 -763
@@ -1,76 +0,0 @@
1
- /**
2
- * The shapes this reconciler reads and writes.
3
- *
4
- * Deliberately a narrower slice of GitHub's repository-ruleset schema than
5
- * Octokit's generated types (devtools has no Octokit dependency — every
6
- * GitHub call in this package goes through the `gh` CLI, see `../client.ts`'s
7
- * header for why). Only the rule types and bypass-actor shapes this
8
- * reconciler actually emits are represented; a rule type nothing here builds
9
- * is deliberately unrepresentable, the same reasoning
10
- * `apps/platform/src/server/github/rulesets.ts`'s `RulesetRule` gives for
11
- * doing the same with a smaller set.
12
- */
13
- export type RulesetTarget = "branch" | "tag";
14
- export interface BypassActor {
15
- actor_id: number;
16
- actor_type: "Team" | "Integration";
17
- bypass_mode: "always" | "pull_request";
18
- }
19
- export interface UpdateRule {
20
- type: "update";
21
- parameters?: {
22
- update_allows_fetch_and_merge: boolean;
23
- };
24
- }
25
- export interface DeletionRule {
26
- type: "deletion";
27
- }
28
- export interface NonFastForwardRule {
29
- type: "non_fast_forward";
30
- }
31
- export interface CreationRule {
32
- type: "creation";
33
- }
34
- export interface PullRequestRule {
35
- type: "pull_request";
36
- parameters: {
37
- allowed_merge_methods: ("merge" | "squash" | "rebase")[];
38
- dismiss_stale_reviews_on_push: boolean;
39
- require_code_owner_review: boolean;
40
- require_last_push_approval: boolean;
41
- required_approving_review_count: number;
42
- required_review_thread_resolution: boolean;
43
- };
44
- }
45
- export type Rule = UpdateRule | DeletionRule | NonFastForwardRule | CreationRule | PullRequestRule;
46
- export interface RulesetConditions {
47
- ref_name: {
48
- include: string[];
49
- exclude: string[];
50
- };
51
- }
52
- /** What this reconciler wants a ruleset to look like, name included. */
53
- export interface DesiredRuleset {
54
- name: string;
55
- target: RulesetTarget;
56
- enforcement: "active";
57
- bypass_actors: BypassActor[];
58
- conditions: RulesetConditions;
59
- rules: Rule[];
60
- }
61
- /** One entry from `GET /repos/{owner}/{repo}/rulesets` — name and id only. */
62
- export interface LiveRulesetSummary {
63
- id: number;
64
- name: string;
65
- target?: string;
66
- }
67
- /** The full shape from `GET /repos/{owner}/{repo}/rulesets/{id}`. */
68
- export interface LiveRuleset {
69
- id: number;
70
- name: string;
71
- target: RulesetTarget;
72
- enforcement: string;
73
- bypass_actors: BypassActor[];
74
- conditions: RulesetConditions;
75
- rules: Rule[];
76
- }
@@ -1,13 +0,0 @@
1
- /**
2
- * The shapes this reconciler reads and writes.
3
- *
4
- * Deliberately a narrower slice of GitHub's repository-ruleset schema than
5
- * Octokit's generated types (devtools has no Octokit dependency — every
6
- * GitHub call in this package goes through the `gh` CLI, see `../client.ts`'s
7
- * header for why). Only the rule types and bypass-actor shapes this
8
- * reconciler actually emits are represented; a rule type nothing here builds
9
- * is deliberately unrepresentable, the same reasoning
10
- * `apps/platform/src/server/github/rulesets.ts`'s `RulesetRule` gives for
11
- * doing the same with a smaller set.
12
- */
13
- export {};
@@ -1,40 +0,0 @@
1
- import type { LiveActionsPermissions, LiveAutomatedSecurityFixes, LiveBranchPolicy, LiveDeploymentBranchPolicy, LiveEnvironment, LiveRepo, LiveSecurityAndAnalysis, LiveSelectedActions, LiveWorkflowPermissions } from "./types.js";
2
- export declare class GhSettingsError extends Error {
3
- }
4
- /** `owner/repo`, e.g. `DevDogsUGA/DevDogsUGA`. */
5
- export interface Repo {
6
- owner: string;
7
- repo: string;
8
- }
9
- export declare function getRepo(r: Repo): Promise<LiveRepo>;
10
- export declare function patchSecurityAndAnalysis(r: Repo, fields: Partial<LiveSecurityAndAnalysis>): Promise<void>;
11
- /**
12
- * `GET /repos/{owner}/{repo}/vulnerability-alerts` returns 204 with an
13
- * empty body when Dependabot alerts are on, and 404 when they are off — the
14
- * one endpoint in this file with no JSON to parse either way.
15
- */
16
- export declare function getVulnerabilityAlertsEnabled(r: Repo): Promise<boolean>;
17
- export declare function setVulnerabilityAlertsEnabled(r: Repo, enabled: boolean): Promise<void>;
18
- export declare function getAutomatedSecurityFixes(r: Repo): Promise<LiveAutomatedSecurityFixes>;
19
- export declare function setAutomatedSecurityFixesEnabled(r: Repo, enabled: boolean): Promise<void>;
20
- export declare function getActionsPermissions(r: Repo): Promise<LiveActionsPermissions>;
21
- export declare function setActionsPermissions(r: Repo, body: LiveActionsPermissions): Promise<void>;
22
- /**
23
- * `GET .../actions/permissions/selected-actions` 409s outright when the
24
- * repository's `allowed_actions` is not currently `"selected"` — there is
25
- * no selected-actions allowlist to read when nothing restricts to one.
26
- * Read as the empty, nothing-allowed list rather than propagating the
27
- * error: `diff.ts` then reports the honest drift (every field disagrees
28
- * with `desired.actions`) instead of `commands.ts` failing outright before
29
- * it can report anything.
30
- */
31
- export declare function getSelectedActions(r: Repo): Promise<LiveSelectedActions>;
32
- export declare function setSelectedActions(r: Repo, body: LiveSelectedActions): Promise<void>;
33
- export declare function getWorkflowPermissions(r: Repo): Promise<LiveWorkflowPermissions>;
34
- export declare function setWorkflowPermissions(r: Repo, body: LiveWorkflowPermissions): Promise<void>;
35
- /** `null` when the environment does not exist yet — reported, never created here. */
36
- export declare function getEnvironment(r: Repo, name: string): Promise<LiveEnvironment | null>;
37
- export declare function getDeploymentBranchPolicies(r: Repo, name: string): Promise<LiveBranchPolicy[]>;
38
- export declare function setDeploymentBranchPolicyMode(r: Repo, name: string, mode: LiveDeploymentBranchPolicy): Promise<void>;
39
- export declare function addDeploymentBranchPolicy(r: Repo, name: string, pattern: string): Promise<void>;
40
- export declare function deleteDeploymentBranchPolicy(r: Repo, name: string, policyId: number): Promise<void>;
@@ -1,180 +0,0 @@
1
- /**
2
- * `gh api` calls this reconciler needs, over the repository-settings
3
- * endpoints TASK-342 governs: security-and-analysis, Dependabot, Actions
4
- * permissions, and environment deployment-branch policies.
5
- *
6
- * `gh`, not Octokit — same reasoning as `../rulesets/api.ts`'s header, which
7
- * this module otherwise mirrors closely (its own `run`/`writeJson` rather
8
- * than a shared import, matching `../environments.ts` and `../rulesets/
9
- * api.ts` each carrying their own — this package's established way of
10
- * keeping one `gh` wrapper's blast radius from the next).
11
- *
12
- * Reads are always allowed. Writes need `admin` on the repository, exactly
13
- * as they do today from the Settings UI, and `commands.ts` is the only
14
- * caller that reaches them, gated behind `--apply`.
15
- */
16
- import { execFile, spawn } from "node:child_process";
17
- import { promisify } from "node:util";
18
- const run = promisify(execFile);
19
- const MAX_BUFFER = 16 * 1024 * 1024;
20
- export class GhSettingsError extends Error {
21
- }
22
- function describe(err) {
23
- const e = err;
24
- const stderr = (e.stderr ?? "").trim();
25
- const message = e.message ?? "";
26
- return stderr || message || "gh failed with no output.";
27
- }
28
- function repoPath(r, suffix = "") {
29
- return `repos/${r.owner}/${r.repo}${suffix}`;
30
- }
31
- async function readJson(apiPath) {
32
- try {
33
- const { stdout } = await run("gh", ["api", apiPath], {
34
- maxBuffer: MAX_BUFFER,
35
- shell: false,
36
- });
37
- return JSON.parse(stdout);
38
- }
39
- catch (err) {
40
- throw new GhSettingsError(describe(err));
41
- }
42
- }
43
- /**
44
- * Runs `gh api <path> -X <method>`, writing `body` on stdin (JSON) when
45
- * given, and returning the parsed JSON response (or `{}` for a body-less
46
- * response). See `../rulesets/api.ts`'s `writeJson` for why stdin, not `-f`
47
- * field flags or a temp file.
48
- */
49
- async function writeJson(method, apiPath, body) {
50
- return new Promise((resolve, reject) => {
51
- const args = body === undefined
52
- ? ["api", apiPath, "-X", method]
53
- : ["api", apiPath, "-X", method, "--input", "-"];
54
- const child = spawn("gh", args, {
55
- stdio: ["pipe", "pipe", "pipe"],
56
- shell: false,
57
- });
58
- let stdout = "";
59
- let stderr = "";
60
- child.stdout.on("data", (c) => (stdout += c.toString()));
61
- child.stderr.on("data", (c) => (stderr += c.toString()));
62
- child.on("error", (err) => reject(new GhSettingsError(describe(err))));
63
- child.on("close", (code) => {
64
- if (code !== 0) {
65
- reject(new GhSettingsError(describe({ stderr })));
66
- return;
67
- }
68
- const trimmed = stdout.trim();
69
- resolve(trimmed === "" ? {} : JSON.parse(trimmed));
70
- });
71
- if (body === undefined)
72
- child.stdin.end();
73
- else
74
- child.stdin.end(JSON.stringify(body));
75
- });
76
- }
77
- // ── repository / security-and-analysis ──────────────────────────────────────
78
- export async function getRepo(r) {
79
- return readJson(repoPath(r));
80
- }
81
- export async function patchSecurityAndAnalysis(r, fields) {
82
- await writeJson("PATCH", repoPath(r), { security_and_analysis: fields });
83
- }
84
- // ── Dependabot ───────────────────────────────────────────────────────────────
85
- /**
86
- * `GET /repos/{owner}/{repo}/vulnerability-alerts` returns 204 with an
87
- * empty body when Dependabot alerts are on, and 404 when they are off — the
88
- * one endpoint in this file with no JSON to parse either way.
89
- */
90
- export async function getVulnerabilityAlertsEnabled(r) {
91
- try {
92
- await run("gh", ["api", repoPath(r, "/vulnerability-alerts")], {
93
- maxBuffer: MAX_BUFFER,
94
- shell: false,
95
- });
96
- return true;
97
- }
98
- catch (err) {
99
- if (/\b404\b/.test(describe(err)))
100
- return false;
101
- throw new GhSettingsError(describe(err));
102
- }
103
- }
104
- export async function setVulnerabilityAlertsEnabled(r, enabled) {
105
- await writeJson(enabled ? "PUT" : "DELETE", repoPath(r, "/vulnerability-alerts"));
106
- }
107
- export async function getAutomatedSecurityFixes(r) {
108
- return readJson(repoPath(r, "/automated-security-fixes"));
109
- }
110
- export async function setAutomatedSecurityFixesEnabled(r, enabled) {
111
- await writeJson(enabled ? "PUT" : "DELETE", repoPath(r, "/automated-security-fixes"));
112
- }
113
- // ── Actions permissions ──────────────────────────────────────────────────────
114
- export async function getActionsPermissions(r) {
115
- return readJson(repoPath(r, "/actions/permissions"));
116
- }
117
- export async function setActionsPermissions(r, body) {
118
- await writeJson("PUT", repoPath(r, "/actions/permissions"), body);
119
- }
120
- /**
121
- * `GET .../actions/permissions/selected-actions` 409s outright when the
122
- * repository's `allowed_actions` is not currently `"selected"` — there is
123
- * no selected-actions allowlist to read when nothing restricts to one.
124
- * Read as the empty, nothing-allowed list rather than propagating the
125
- * error: `diff.ts` then reports the honest drift (every field disagrees
126
- * with `desired.actions`) instead of `commands.ts` failing outright before
127
- * it can report anything.
128
- */
129
- export async function getSelectedActions(r) {
130
- try {
131
- return await readJson(repoPath(r, "/actions/permissions/selected-actions"));
132
- }
133
- catch (err) {
134
- if (err instanceof GhSettingsError &&
135
- (/\b409\b/.test(err.message) || /\(conflict\)/i.test(err.message))) {
136
- return {
137
- github_owned_allowed: false,
138
- verified_allowed: false,
139
- patterns_allowed: [],
140
- };
141
- }
142
- throw err;
143
- }
144
- }
145
- export async function setSelectedActions(r, body) {
146
- await writeJson("PUT", repoPath(r, "/actions/permissions/selected-actions"), body);
147
- }
148
- export async function getWorkflowPermissions(r) {
149
- return readJson(repoPath(r, "/actions/permissions/workflow"));
150
- }
151
- export async function setWorkflowPermissions(r, body) {
152
- await writeJson("PUT", repoPath(r, "/actions/permissions/workflow"), body);
153
- }
154
- // ── Environments ─────────────────────────────────────────────────────────────
155
- /** `null` when the environment does not exist yet — reported, never created here. */
156
- export async function getEnvironment(r, name) {
157
- try {
158
- return await readJson(repoPath(r, `/environments/${encodeURIComponent(name)}`));
159
- }
160
- catch (err) {
161
- if (err instanceof GhSettingsError && /\b404\b/.test(err.message))
162
- return null;
163
- throw err;
164
- }
165
- }
166
- export async function getDeploymentBranchPolicies(r, name) {
167
- const data = await readJson(repoPath(r, `/environments/${encodeURIComponent(name)}/deployment-branch-policies`));
168
- return data.branch_policies ?? [];
169
- }
170
- export async function setDeploymentBranchPolicyMode(r, name, mode) {
171
- await writeJson("PUT", repoPath(r, `/environments/${encodeURIComponent(name)}`), {
172
- deployment_branch_policy: mode,
173
- });
174
- }
175
- export async function addDeploymentBranchPolicy(r, name, pattern) {
176
- await writeJson("POST", repoPath(r, `/environments/${encodeURIComponent(name)}/deployment-branch-policies`), { name: pattern });
177
- }
178
- export async function deleteDeploymentBranchPolicy(r, name, policyId) {
179
- await writeJson("DELETE", repoPath(r, `/environments/${encodeURIComponent(name)}/deployment-branch-policies/${policyId}`));
180
- }
@@ -1 +0,0 @@
1
- export declare function runGithubSettings(argv: readonly string[]): Promise<number>;
@@ -1,254 +0,0 @@
1
- /**
2
- * `devtools github settings` — TASK-342.
3
- *
4
- * Prints the diff between the repository settings DevDogsUGA/DevDogsUGA
5
- * must carry (secret scanning, Dependabot, Actions permissions and SHA
6
- * pinning, and the three deploy environments' branch policies) and what
7
- * `gh api` actually reports. Defaults to a dry-run plan; `--apply` writes
8
- * FIXABLE drift, gated behind a confirmation unless `--yes`. Sibling of
9
- * `../rulesets/commands.ts`, same flag shape (`--org`, `--repo`, `--apply`,
10
- * `--yes`, `--json`).
11
- *
12
- * Never invents a required reviewer, and never creates a missing
13
- * environment — both are reported only. See `../diff.ts`'s header for why.
14
- */
15
- import { confirm } from "@clack/prompts";
16
- import { addDeploymentBranchPolicy, deleteDeploymentBranchPolicy, getActionsPermissions, getAutomatedSecurityFixes, getDeploymentBranchPolicies, getEnvironment, getRepo, getSelectedActions, getVulnerabilityAlertsEnabled, getWorkflowPermissions, patchSecurityAndAnalysis, setActionsPermissions, setAutomatedSecurityFixesEnabled, setDeploymentBranchPolicyMode, setSelectedActions, setVulnerabilityAlertsEnabled, setWorkflowPermissions, } from "./api.js";
17
- import { buildDesiredSettings } from "./desired.js";
18
- import { planHasFixableChanges, planSettings, } from "./diff.js";
19
- import { collectActionUses, computeActionPatterns, unpinnedActionUses, } from "./workflows.js";
20
- import { findRepoRoot } from "../../repo/root.js";
21
- import { unwrap } from "../../ui.js";
22
- const DEFAULT_ORG = "DevDogsUGA";
23
- const DEFAULT_REPO = "DevDogsUGA";
24
- function parseOptions(argv) {
25
- const opts = {
26
- org: DEFAULT_ORG,
27
- repo: DEFAULT_REPO,
28
- apply: false,
29
- yes: false,
30
- json: false,
31
- };
32
- for (let i = 0; i < argv.length; i++) {
33
- const arg = argv[i];
34
- if (arg === "--org")
35
- opts.org = argv[++i] ?? opts.org;
36
- else if (arg === "--repo")
37
- opts.repo = argv[++i] ?? opts.repo;
38
- else if (arg === "--apply")
39
- opts.apply = true;
40
- else if (arg === "--yes")
41
- opts.yes = true;
42
- else if (arg === "--json")
43
- opts.json = true;
44
- }
45
- return opts;
46
- }
47
- async function fetchSnapshot(r) {
48
- const [repo, vulnerabilityAlerts, automatedSecurityFixes, actionsPermissions, selectedActions, workflowPermissions, staging, production, productionApply,] = await Promise.all([
49
- getRepo(r),
50
- getVulnerabilityAlertsEnabled(r),
51
- getAutomatedSecurityFixes(r),
52
- getActionsPermissions(r),
53
- getSelectedActions(r),
54
- getWorkflowPermissions(r),
55
- fetchEnvironmentSnapshot(r, "staging"),
56
- fetchEnvironmentSnapshot(r, "production"),
57
- fetchEnvironmentSnapshot(r, "production-apply"),
58
- ]);
59
- return {
60
- repo,
61
- vulnerabilityAlerts,
62
- automatedSecurityFixes,
63
- actionsPermissions,
64
- selectedActions,
65
- workflowPermissions,
66
- environments: { staging, production, "production-apply": productionApply },
67
- };
68
- }
69
- async function fetchEnvironmentSnapshot(r, name) {
70
- const environment = await getEnvironment(r, name);
71
- if (!environment)
72
- return { environment: null, branchPolicies: [] };
73
- const branchPolicies = await getDeploymentBranchPolicies(r, name);
74
- return { environment, branchPolicies };
75
- }
76
- function renderPlan(plan) {
77
- const lines = [];
78
- if (plan.unpinnedActions.length > 0) {
79
- lines.push(`! ${plan.unpinnedActions.length} workflow uses: not pinned to a 40-hex SHA — sha_pinning_required is REFUSED:`);
80
- for (const use of plan.unpinnedActions) {
81
- lines.push(` ${use.file}:${use.line} uses: ${use.raw}`);
82
- }
83
- }
84
- for (const check of plan.checks) {
85
- const marker = check.status === "ok" ? "=" : check.status === "unsupported" ? "?" : "~";
86
- lines.push(`${marker} ${check.key}`);
87
- lines.push(` desired: ${check.desired}`);
88
- lines.push(` live: ${check.live}`);
89
- if (check.status === "drift" && !check.fixable) {
90
- lines.push(` (drift reported only — not auto-fixable)`);
91
- }
92
- }
93
- if (lines.length === 0)
94
- return "(nothing to report)";
95
- return lines.join("\n");
96
- }
97
- async function confirmApply(yes) {
98
- if (yes)
99
- return true;
100
- if (!process.stdin.isTTY) {
101
- process.stderr.write("devtools github settings: --yes is required to apply with no terminal available.\n");
102
- return false;
103
- }
104
- return unwrap(await confirm({
105
- message: "Apply this settings plan?",
106
- initialValue: false,
107
- }));
108
- }
109
- /**
110
- * Writes every drifted, fixable check in `plan`. Grouped by the GitHub
111
- * endpoint each check's fix lands on, so two checks that share one PUT body
112
- * (`actions.sha_pinning_required` and `actions.allowed_actions`, both on
113
- * `PUT .../actions/permissions`) are written together, once.
114
- */
115
- async function applyPlan(r, desired, live, plan) {
116
- const drift = new Set(plan.checks
117
- .filter((c) => c.status === "drift" && c.fixable)
118
- .map((c) => c.key));
119
- const secretScanningFields = {};
120
- if (drift.has("security_and_analysis.secret_scanning")) {
121
- secretScanningFields.secret_scanning = {
122
- status: desired.securityAndAnalysis.secretScanning
123
- ? "enabled"
124
- : "disabled",
125
- };
126
- }
127
- if (drift.has("security_and_analysis.secret_scanning_push_protection")) {
128
- secretScanningFields.secret_scanning_push_protection = {
129
- status: desired.securityAndAnalysis.secretScanningPushProtection
130
- ? "enabled"
131
- : "disabled",
132
- };
133
- }
134
- if (drift.has("security_and_analysis.secret_scanning_non_provider_patterns")) {
135
- secretScanningFields.secret_scanning_non_provider_patterns = {
136
- status: desired.securityAndAnalysis.secretScanningNonProviderPatterns
137
- ? "enabled"
138
- : "disabled",
139
- };
140
- }
141
- if (drift.has("security_and_analysis.secret_scanning_validity_checks")) {
142
- secretScanningFields.secret_scanning_validity_checks = {
143
- status: desired.securityAndAnalysis.secretScanningValidityChecks
144
- ? "enabled"
145
- : "disabled",
146
- };
147
- }
148
- if (Object.keys(secretScanningFields).length > 0) {
149
- await patchSecurityAndAnalysis(r, secretScanningFields);
150
- }
151
- if (drift.has("vulnerability_alerts")) {
152
- await setVulnerabilityAlertsEnabled(r, desired.vulnerabilityAlerts);
153
- }
154
- if (drift.has("dependabot_security_updates")) {
155
- await setAutomatedSecurityFixesEnabled(r, desired.dependabotSecurityUpdates);
156
- }
157
- if (drift.has("actions.sha_pinning_required") ||
158
- drift.has("actions.allowed_actions")) {
159
- await setActionsPermissions(r, {
160
- enabled: live.actionsPermissions.enabled,
161
- allowed_actions: drift.has("actions.allowed_actions")
162
- ? desired.actions.allowedActions
163
- : live.actionsPermissions.allowed_actions,
164
- // Never written true unless THIS run's own check approved it —
165
- // `plan.refuseShaPinning` already kept it out of `drift` when refused,
166
- // but guarded again here so a future caller building `drift` some
167
- // other way cannot accidentally turn this on unsafely.
168
- sha_pinning_required: drift.has("actions.sha_pinning_required")
169
- ? desired.actions.shaPinningRequired
170
- : live.actionsPermissions.sha_pinning_required,
171
- });
172
- }
173
- if (drift.has("actions.selected_actions")) {
174
- await setSelectedActions(r, {
175
- github_owned_allowed: desired.actions.githubOwnedAllowed,
176
- verified_allowed: desired.actions.verifiedAllowed,
177
- patterns_allowed: [...desired.actions.patterns],
178
- });
179
- }
180
- if (drift.has("actions.workflow_permissions")) {
181
- await setWorkflowPermissions(r, {
182
- default_workflow_permissions: desired.workflowPermissions.defaultWorkflowPermissions,
183
- can_approve_pull_request_reviews: desired.workflowPermissions.canApprovePullRequestReviews,
184
- });
185
- }
186
- for (const env of desired.environments) {
187
- if (!drift.has(`environments.${env.name}.branch_policy`))
188
- continue;
189
- await setDeploymentBranchPolicyMode(r, env.name, {
190
- protected_branches: false,
191
- custom_branch_policies: true,
192
- });
193
- const existing = await getDeploymentBranchPolicies(r, env.name);
194
- const wanted = new Set(env.allowedBranches);
195
- for (const policy of existing) {
196
- if (!wanted.has(policy.name))
197
- await deleteDeploymentBranchPolicy(r, env.name, policy.id);
198
- }
199
- const already = new Set(existing.map((p) => p.name));
200
- for (const branch of env.allowedBranches) {
201
- if (!already.has(branch))
202
- await addDeploymentBranchPolicy(r, env.name, branch);
203
- }
204
- }
205
- }
206
- export async function runGithubSettings(argv) {
207
- const opts = parseOptions(argv);
208
- const r = { owner: opts.org, repo: opts.repo };
209
- let uses;
210
- try {
211
- uses = collectActionUses(findRepoRoot());
212
- }
213
- catch (err) {
214
- process.stderr.write(`devtools github settings: ${err instanceof Error ? err.message : String(err)}\n`);
215
- return 1;
216
- }
217
- const unpinned = unpinnedActionUses(uses);
218
- const patterns = computeActionPatterns(uses);
219
- const desired = buildDesiredSettings(patterns);
220
- let live;
221
- try {
222
- live = await fetchSnapshot(r);
223
- }
224
- catch (err) {
225
- process.stderr.write(`devtools github settings: ${err instanceof Error ? err.message : String(err)}\n`);
226
- return 1;
227
- }
228
- const plan = planSettings(desired, live, unpinned);
229
- if (opts.json) {
230
- console.log(JSON.stringify(plan, null, 2));
231
- }
232
- else {
233
- console.log(renderPlan(plan));
234
- }
235
- if (!opts.apply) {
236
- if (!opts.json && planHasFixableChanges(plan)) {
237
- console.log("\n(dry run — pass --apply to write this plan)");
238
- }
239
- return 0;
240
- }
241
- if (!planHasFixableChanges(plan))
242
- return 0;
243
- if (!(await confirmApply(opts.yes)))
244
- return 1;
245
- try {
246
- await applyPlan(r, desired, live, plan);
247
- }
248
- catch (err) {
249
- process.stderr.write(`devtools github settings: apply failed: ${err instanceof Error ? err.message : String(err)}\n`);
250
- return 1;
251
- }
252
- console.log("Applied.");
253
- return 0;
254
- }
@@ -1,53 +0,0 @@
1
- /**
2
- * What TASK-342's `devtools github settings` wants DevDogsUGA/DevDogsUGA's
3
- * repository settings to look like.
4
- *
5
- * Sibling of `../rulesets/desired.ts` — same shape of module (a pure
6
- * builder, no network, so `desired.test.ts` can assert it directly) covering
7
- * a different slice of repository configuration: security-and-analysis,
8
- * Dependabot, Actions permissions, and the three deploy environments'
9
- * branch policies. `diff.ts` compares this against what `api.ts`'s reads
10
- * report; `commands.ts` is the `devtools github settings` command that
11
- * prints or applies the difference.
12
- */
13
- export type SettingsEnvironment = "staging" | "production" | "production-apply";
14
- export interface DesiredEnvironmentPolicy {
15
- name: SettingsEnvironment;
16
- /** Deployment-branch-policy patterns this environment allows — exact branch names here, not glob patterns. */
17
- allowedBranches: readonly string[];
18
- /** Whether this environment must carry at least one required reviewer (never auto-added — see `diff.ts`). */
19
- requireReviewers: boolean;
20
- }
21
- export interface DesiredSettings {
22
- securityAndAnalysis: {
23
- secretScanning: boolean;
24
- secretScanningPushProtection: boolean;
25
- /** Advanced-Security features GitHub grants free on PUBLIC repos; `diff.ts` treats an absent live field as "unsupported", never as drift. */
26
- secretScanningNonProviderPatterns: boolean;
27
- secretScanningValidityChecks: boolean;
28
- };
29
- vulnerabilityAlerts: boolean;
30
- dependabotSecurityUpdates: boolean;
31
- actions: {
32
- /** Gated on every workflow `uses:` already being SHA-pinned — see `workflows.ts` and `diff.ts`'s `refuseShaPinning`. */
33
- shaPinningRequired: boolean;
34
- allowedActions: "selected";
35
- githubOwnedAllowed: boolean;
36
- verifiedAllowed: boolean;
37
- /** `owner/repo@*`, computed from the repo's own workflows by `workflows.ts#computeActionPatterns`. */
38
- patterns: readonly string[];
39
- };
40
- workflowPermissions: {
41
- defaultWorkflowPermissions: "read";
42
- canApprovePullRequestReviews: false;
43
- };
44
- environments: readonly DesiredEnvironmentPolicy[];
45
- }
46
- /**
47
- * Builds the desired settings. `actionPatterns` comes from scanning the
48
- * repo's own workflow files (`workflows.ts#computeActionPatterns`) —
49
- * `commands.ts` is the only real caller, and passes the live scan result
50
- * rather than this module reading the filesystem itself, so `desired.test.ts`
51
- * can assert the shape with a fixture list and no repo on disk.
52
- */
53
- export declare function buildDesiredSettings(actionPatterns: readonly string[]): DesiredSettings;
@@ -1,43 +0,0 @@
1
- /**
2
- * Builds the desired settings. `actionPatterns` comes from scanning the
3
- * repo's own workflow files (`workflows.ts#computeActionPatterns`) —
4
- * `commands.ts` is the only real caller, and passes the live scan result
5
- * rather than this module reading the filesystem itself, so `desired.test.ts`
6
- * can assert the shape with a fixture list and no repo on disk.
7
- */
8
- export function buildDesiredSettings(actionPatterns) {
9
- return {
10
- securityAndAnalysis: {
11
- secretScanning: true,
12
- secretScanningPushProtection: true,
13
- secretScanningNonProviderPatterns: true,
14
- secretScanningValidityChecks: true,
15
- },
16
- vulnerabilityAlerts: true,
17
- dependabotSecurityUpdates: true,
18
- actions: {
19
- shaPinningRequired: true,
20
- allowedActions: "selected",
21
- githubOwnedAllowed: true,
22
- verifiedAllowed: true,
23
- patterns: [...actionPatterns].sort(),
24
- },
25
- workflowPermissions: {
26
- defaultWorkflowPermissions: "read",
27
- canApprovePullRequestReviews: false,
28
- },
29
- environments: [
30
- { name: "staging", allowedBranches: ["main"], requireReviewers: false },
31
- {
32
- name: "production",
33
- allowedBranches: ["production"],
34
- requireReviewers: false,
35
- },
36
- {
37
- name: "production-apply",
38
- allowedBranches: ["production"],
39
- requireReviewers: true,
40
- },
41
- ],
42
- };
43
- }