@devdogsuga/devtools 0.1.9 → 0.1.11

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.
@@ -92,7 +92,9 @@ export async function createRuleset(r, desired) {
92
92
  return writeJson("POST", path(r), desired);
93
93
  }
94
94
  export async function updateRuleset(r, id, desired) {
95
- return writeJson("PATCH", path(r, `/${id}`), desired);
95
+ // PUT, not PATCH: GitHub's rulesets API has no PATCH route and answers one
96
+ // with a 404, which reads like a missing ruleset rather than a wrong verb.
97
+ return writeJson("PUT", path(r, `/${id}`), desired);
96
98
  }
97
99
  export async function deleteRuleset(r, id) {
98
100
  await writeJson("DELETE", path(r, `/${id}`), {});
@@ -24,9 +24,22 @@
24
24
  */
25
25
  import { DELETE_RULESET_NAMES, LEGACY_NAME_ALIASES, isPerTeamRulesetName, } from "./desired.js";
26
26
  /**
27
- * Fills in defaults GitHub omits on the wire so an absent field and an
28
- * explicit default-valued one compare equal — see `UpdateRule.parameters`'s
29
- * doc in `types.ts`.
27
+ * Reduces a rule to the fields this reconciler manages, in a fixed key order,
28
+ * so a live rule and its desired twin compare equal whenever they protect
29
+ * the same way.
30
+ *
31
+ * Two ways GitHub's copy differs from what was sent, both seen live on
32
+ * 2026-09-27 right after creating `production`:
33
+ *
34
+ * - it omits parameters that hold their default, so `update` comes back with
35
+ * no `update_allows_fetch_and_merge` (see `UpdateRule.parameters`' doc in
36
+ * `types.ts`);
37
+ * - it adds `pull_request` parameters `desired.ts` never sets
38
+ * (`dismissal_restriction`, `require_extra_approval_for_unattributed_changes`,
39
+ * `required_reviewers`), in its own key order.
40
+ *
41
+ * Without this, every ruleset with a `pull_request` rule planned an update on
42
+ * every run, so the reconciler never settled.
30
43
  */
31
44
  function canonicalizeRule(rule) {
32
45
  if (rule.type === "update") {
@@ -37,15 +50,36 @@ function canonicalizeRule(rule) {
37
50
  },
38
51
  };
39
52
  }
40
- return rule;
53
+ if (rule.type === "pull_request") {
54
+ const p = rule.parameters;
55
+ return {
56
+ type: "pull_request",
57
+ parameters: {
58
+ allowed_merge_methods: [...p.allowed_merge_methods].sort(),
59
+ dismiss_stale_reviews_on_push: p.dismiss_stale_reviews_on_push,
60
+ require_code_owner_review: p.require_code_owner_review,
61
+ require_last_push_approval: p.require_last_push_approval,
62
+ required_approving_review_count: p.required_approving_review_count,
63
+ required_review_thread_resolution: p.required_review_thread_resolution,
64
+ },
65
+ };
66
+ }
67
+ return { type: rule.type };
41
68
  }
42
69
  function sortRules(rules) {
43
70
  return [...rules]
44
71
  .map(canonicalizeRule)
45
72
  .sort((a, b) => a.type.localeCompare(b.type));
46
73
  }
74
+ /** Sorted, and each actor rebuilt in a fixed key order for the same reason as `canonicalizeRule`. */
47
75
  function sortActors(actors) {
48
- return [...actors].sort((a, b) => a.actor_type.localeCompare(b.actor_type) || a.actor_id - b.actor_id);
76
+ return [...actors]
77
+ .sort((a, b) => a.actor_type.localeCompare(b.actor_type) || a.actor_id - b.actor_id)
78
+ .map((a) => ({
79
+ actor_id: a.actor_id,
80
+ actor_type: a.actor_type,
81
+ bypass_mode: a.bypass_mode,
82
+ }));
49
83
  }
50
84
  /**
51
85
  * A canonical, order-independent string for one ruleset's PROTECTION shape —
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@devdogsuga/devtools",
3
- "version": "0.1.9",
3
+ "version": "0.1.11",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "description": "Contributor CLI for a DevDogsUGA checkout: database, env, images, emails, cron, deploy. Ships built JS; run via `pnpm dlx @devdogsuga/devtools` (or the `devtools`/`devtools-ci` bins once installed) from inside a DevDogsUGA clone.",
@@ -63,9 +63,9 @@
63
63
  "react": "^19.2.8",
64
64
  "vitest": "^4.1.11",
65
65
  "@devdogsuga/config": "0.1.1",
66
+ "@devdogsuga/brand": "0.1.1",
66
67
  "@devdogsuga/db": "0.1.1",
67
- "@devdogsuga/env": "0.1.1",
68
- "@devdogsuga/brand": "0.1.1"
68
+ "@devdogsuga/env": "0.1.1"
69
69
  },
70
70
  "scripts": {
71
71
  "build": "tsc && node scripts/write-build-info.mjs",