@codyswann/lisa 3.48.4 → 3.48.5
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.
- package/dist/core/upstream-evidence-manifest.js +2 -2
- package/package.json +1 -1
- package/plugins/lisa/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa/.codex-plugin/skills/lisa-setup-github-repo/SKILL.md +1 -0
- package/plugins/lisa/skills/lisa-setup-github-repo/SKILL.md +1 -0
- package/plugins/lisa-agy/plugin.json +1 -1
- package/plugins/lisa-agy/skills/lisa-setup-github-repo/SKILL.md +1 -0
- package/plugins/lisa-cdk/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk-agy/plugin.json +1 -1
- package/plugins/lisa-cdk-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-copilot/skills/lisa-setup-github-repo/SKILL.md +1 -0
- package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cursor/skills/lisa-setup-github-repo/SKILL.md +1 -0
- package/plugins/lisa-expo/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-expo/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-expo-agy/plugin.json +1 -1
- package/plugins/lisa-expo-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-expo-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-agy/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs-agy/plugin.json +1 -1
- package/plugins/lisa-nestjs-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw-agy/plugin.json +1 -1
- package/plugins/lisa-openclaw-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser-agy/plugin.json +1 -1
- package/plugins/lisa-phaser-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-rails-agy/plugin.json +1 -1
- package/plugins/lisa-rails-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript-agy/plugin.json +1 -1
- package/plugins/lisa-typescript-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki-agy/plugin.json +1 -1
- package/plugins/lisa-wiki-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/src/base/skills/lisa-setup-github-repo/SKILL.md +1 -0
- package/scripts/lisa-github-rulesets.sh +97 -14
|
@@ -663,7 +663,7 @@ export const UPSTREAM_EVIDENCE_MANIFEST = Object.freeze({
|
|
|
663
663
|
"plugins/src/base/skills/lisa-setup-automations/SKILL.md": "4bf86639bca29641686fd124d7d4b6c177775063930a309e2c33f808e33d505f",
|
|
664
664
|
"plugins/src/base/skills/lisa-setup-automations/scripts/generate-workflow.mjs": "c13e287c8beb6f847b03c93298b8a3fb211e65ce897e63692933377f8c4303f1",
|
|
665
665
|
"plugins/src/base/skills/lisa-setup-confluence/SKILL.md": "e92d762dbdaeae671c3e52dfe8e10d40a10b606b5e050589181a3898649faedc",
|
|
666
|
-
"plugins/src/base/skills/lisa-setup-github-repo/SKILL.md": "
|
|
666
|
+
"plugins/src/base/skills/lisa-setup-github-repo/SKILL.md": "1e343418a59e54ac1e2b6d542ddbecfa53adb370d6bfe558818d5960f9701bc8",
|
|
667
667
|
"plugins/src/base/skills/lisa-setup-github/SKILL.md": "75435e81130abd21c678d302b7a220da95a19868d122ca93168278628b200666",
|
|
668
668
|
"plugins/src/base/skills/lisa-setup-jira/SKILL.md": "261acaeb2923cc9238f468dcf986a0e1323a67e1e51d175b2c1b700dacab2fb8",
|
|
669
669
|
"plugins/src/base/skills/lisa-setup-kane/SKILL.md": "fe50024a4f896af01716f2435f57227b1f8de61803e80bf3e3c3247ec179b103",
|
|
@@ -1113,7 +1113,7 @@ export const UPSTREAM_EVIDENCE_MANIFEST = Object.freeze({
|
|
|
1113
1113
|
"scripts/lisa-github-environments.sh": "0a76e92f108519abaf3e29991299ec3b0db20ea533e69e6d2a53d802dba9c370",
|
|
1114
1114
|
"scripts/lisa-github-repo-settings.sh": "4afbccdfb08c62ba13afee42ec8561a28facf87c61f6f4b80000115328364d54",
|
|
1115
1115
|
"scripts/lisa-github-repo-setup.sh": "dc2f1c9d718aa34ba57161120d9319b5f78fe6b0b496d8e3c77226a08795aa36",
|
|
1116
|
-
"scripts/lisa-github-rulesets.sh": "
|
|
1116
|
+
"scripts/lisa-github-rulesets.sh": "5899206cb187d877026d7980356a28214dcad4f1d90d855206a95f448900b333",
|
|
1117
1117
|
"scripts/lisa-mutation.mjs": "74603e61f493d700318feb862f61ee1653430700b5edf232e70241ccdedac587",
|
|
1118
1118
|
"scripts/lisa-remote-env/session-start.sh": "cb63d08b14ab7aa2d405e6770e0cf7db5d6588ae1dcb924ea6224b026fcff496",
|
|
1119
1119
|
"scripts/lisa-remote-env/setup.sh": "c30d98a5645f033fc1d3a723043691b9ad997ae5666df539ff6aa19c563431b2",
|
package/package.json
CHANGED
|
@@ -141,7 +141,7 @@
|
|
|
141
141
|
"zod-validation-error": "^4.0.0"
|
|
142
142
|
},
|
|
143
143
|
"name": "@codyswann/lisa",
|
|
144
|
-
"version": "3.48.
|
|
144
|
+
"version": "3.48.5",
|
|
145
145
|
"description": "Claude Code governance framework that applies guardrails, guidance, and automated enforcement to projects",
|
|
146
146
|
"main": "dist/index.js",
|
|
147
147
|
"exports": {
|
|
@@ -28,6 +28,7 @@ Apply the fleet-standard GitHub repository configuration to this project's repo.
|
|
|
28
28
|
- `quality checks` — the stack's CI checks (TypeScript emoji names or Rails names)
|
|
29
29
|
- `prevent delete`, `protect tags` (`v*`), plus stack overlays (`cdk validation`, staging-only `playwright`)
|
|
30
30
|
- Repos without `.github/workflows/` get only app-based required checks — an Actions check that can never report would block every PR forever
|
|
31
|
+
- **A template change never reaches an already-configured repo on its own** — re-running this step is the migration. Reconciliation is additive (a live-only required context is preserved, never dropped) and legible: each context the run newly makes blocking is printed as `+ now required: <context>`, and a ruleset that already satisfies its template reports `nothing to do` and is not sent at all, so a second run is visibly a no-op
|
|
31
32
|
3. **Deploy key** (`scripts/setup-deploy-key.sh --yes`) — write-access deploy key + `DEPLOY_KEY` secret, skipped when already configured. The `base` ruleset's `DeployKey: always` bypass is what lets CI version-bump pushes through protected branches.
|
|
32
33
|
4. **Deployment environments** (`scripts/lisa-github-environments.sh`) — entirely optional; only runs when `.lisa.config.json` declares environments:
|
|
33
34
|
```json
|
|
@@ -28,6 +28,7 @@ Apply the fleet-standard GitHub repository configuration to this project's repo.
|
|
|
28
28
|
- `quality checks` — the stack's CI checks (TypeScript emoji names or Rails names)
|
|
29
29
|
- `prevent delete`, `protect tags` (`v*`), plus stack overlays (`cdk validation`, staging-only `playwright`)
|
|
30
30
|
- Repos without `.github/workflows/` get only app-based required checks — an Actions check that can never report would block every PR forever
|
|
31
|
+
- **A template change never reaches an already-configured repo on its own** — re-running this step is the migration. Reconciliation is additive (a live-only required context is preserved, never dropped) and legible: each context the run newly makes blocking is printed as `+ now required: <context>`, and a ruleset that already satisfies its template reports `nothing to do` and is not sent at all, so a second run is visibly a no-op
|
|
31
32
|
3. **Deploy key** (`scripts/setup-deploy-key.sh --yes`) — write-access deploy key + `DEPLOY_KEY` secret, skipped when already configured. The `base` ruleset's `DeployKey: always` bypass is what lets CI version-bump pushes through protected branches.
|
|
32
33
|
4. **Deployment environments** (`scripts/lisa-github-environments.sh`) — entirely optional; only runs when `.lisa.config.json` declares environments:
|
|
33
34
|
```json
|
|
@@ -28,6 +28,7 @@ Apply the fleet-standard GitHub repository configuration to this project's repo.
|
|
|
28
28
|
- `quality checks` — the stack's CI checks (TypeScript emoji names or Rails names)
|
|
29
29
|
- `prevent delete`, `protect tags` (`v*`), plus stack overlays (`cdk validation`, staging-only `playwright`)
|
|
30
30
|
- Repos without `.github/workflows/` get only app-based required checks — an Actions check that can never report would block every PR forever
|
|
31
|
+
- **A template change never reaches an already-configured repo on its own** — re-running this step is the migration. Reconciliation is additive (a live-only required context is preserved, never dropped) and legible: each context the run newly makes blocking is printed as `+ now required: <context>`, and a ruleset that already satisfies its template reports `nothing to do` and is not sent at all, so a second run is visibly a no-op
|
|
31
32
|
3. **Deploy key** (`scripts/setup-deploy-key.sh --yes`) — write-access deploy key + `DEPLOY_KEY` secret, skipped when already configured. The `base` ruleset's `DeployKey: always` bypass is what lets CI version-bump pushes through protected branches.
|
|
32
33
|
4. **Deployment environments** (`scripts/lisa-github-environments.sh`) — entirely optional; only runs when `.lisa.config.json` declares environments:
|
|
33
34
|
```json
|
|
@@ -28,6 +28,7 @@ Apply the fleet-standard GitHub repository configuration to this project's repo.
|
|
|
28
28
|
- `quality checks` — the stack's CI checks (TypeScript emoji names or Rails names)
|
|
29
29
|
- `prevent delete`, `protect tags` (`v*`), plus stack overlays (`cdk validation`, staging-only `playwright`)
|
|
30
30
|
- Repos without `.github/workflows/` get only app-based required checks — an Actions check that can never report would block every PR forever
|
|
31
|
+
- **A template change never reaches an already-configured repo on its own** — re-running this step is the migration. Reconciliation is additive (a live-only required context is preserved, never dropped) and legible: each context the run newly makes blocking is printed as `+ now required: <context>`, and a ruleset that already satisfies its template reports `nothing to do` and is not sent at all, so a second run is visibly a no-op
|
|
31
32
|
3. **Deploy key** (`scripts/setup-deploy-key.sh --yes`) — write-access deploy key + `DEPLOY_KEY` secret, skipped when already configured. The `base` ruleset's `DeployKey: always` bypass is what lets CI version-bump pushes through protected branches.
|
|
32
33
|
4. **Deployment environments** (`scripts/lisa-github-environments.sh`) — entirely optional; only runs when `.lisa.config.json` declares environments:
|
|
33
34
|
```json
|
|
@@ -28,6 +28,7 @@ Apply the fleet-standard GitHub repository configuration to this project's repo.
|
|
|
28
28
|
- `quality checks` — the stack's CI checks (TypeScript emoji names or Rails names)
|
|
29
29
|
- `prevent delete`, `protect tags` (`v*`), plus stack overlays (`cdk validation`, staging-only `playwright`)
|
|
30
30
|
- Repos without `.github/workflows/` get only app-based required checks — an Actions check that can never report would block every PR forever
|
|
31
|
+
- **A template change never reaches an already-configured repo on its own** — re-running this step is the migration. Reconciliation is additive (a live-only required context is preserved, never dropped) and legible: each context the run newly makes blocking is printed as `+ now required: <context>`, and a ruleset that already satisfies its template reports `nothing to do` and is not sent at all, so a second run is visibly a no-op
|
|
31
32
|
3. **Deploy key** (`scripts/setup-deploy-key.sh --yes`) — write-access deploy key + `DEPLOY_KEY` secret, skipped when already configured. The `base` ruleset's `DeployKey: always` bypass is what lets CI version-bump pushes through protected branches.
|
|
32
33
|
4. **Deployment environments** (`scripts/lisa-github-environments.sh`) — entirely optional; only runs when `.lisa.config.json` declares environments:
|
|
33
34
|
```json
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lisa-openclaw",
|
|
3
|
-
"version": "3.48.
|
|
3
|
+
"version": "3.48.5",
|
|
4
4
|
"description": "Connect staff roles to Telegram or Slack via OpenClaw — facilitator/specialist hub-and-spoke routing and repo-coding topics, for Claude Code and Codex",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Cody Swann"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lisa-openclaw",
|
|
3
|
-
"version": "3.48.
|
|
3
|
+
"version": "3.48.5",
|
|
4
4
|
"description": "Connect staff roles to Telegram or Slack via OpenClaw — facilitator/specialist hub-and-spoke routing and repo-coding topics, across Claude and Codex.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Cody Swann"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lisa-openclaw",
|
|
3
|
-
"version": "3.48.
|
|
3
|
+
"version": "3.48.5",
|
|
4
4
|
"description": "Connect staff roles to Telegram or Slack via OpenClaw — facilitator/specialist hub-and-spoke routing and repo-coding topics, for Claude Code and Codex",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Cody Swann"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lisa-openclaw",
|
|
3
|
-
"version": "3.48.
|
|
3
|
+
"version": "3.48.5",
|
|
4
4
|
"description": "Connect staff roles to Telegram or Slack via OpenClaw — facilitator/specialist hub-and-spoke routing and repo-coding topics, for Claude Code and Codex",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Cody Swann"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lisa-openclaw",
|
|
3
|
-
"version": "3.48.
|
|
3
|
+
"version": "3.48.5",
|
|
4
4
|
"description": "Connect staff roles to Telegram or Slack via OpenClaw — facilitator/specialist hub-and-spoke routing and repo-coding topics, for Claude Code and Codex",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Cody Swann"
|
|
@@ -28,6 +28,7 @@ Apply the fleet-standard GitHub repository configuration to this project's repo.
|
|
|
28
28
|
- `quality checks` — the stack's CI checks (TypeScript emoji names or Rails names)
|
|
29
29
|
- `prevent delete`, `protect tags` (`v*`), plus stack overlays (`cdk validation`, staging-only `playwright`)
|
|
30
30
|
- Repos without `.github/workflows/` get only app-based required checks — an Actions check that can never report would block every PR forever
|
|
31
|
+
- **A template change never reaches an already-configured repo on its own** — re-running this step is the migration. Reconciliation is additive (a live-only required context is preserved, never dropped) and legible: each context the run newly makes blocking is printed as `+ now required: <context>`, and a ruleset that already satisfies its template reports `nothing to do` and is not sent at all, so a second run is visibly a no-op
|
|
31
32
|
3. **Deploy key** (`scripts/setup-deploy-key.sh --yes`) — write-access deploy key + `DEPLOY_KEY` secret, skipped when already configured. The `base` ruleset's `DeployKey: always` bypass is what lets CI version-bump pushes through protected branches.
|
|
32
33
|
4. **Deployment environments** (`scripts/lisa-github-environments.sh`) — entirely optional; only runs when `.lisa.config.json` declares environments:
|
|
33
34
|
```json
|
|
@@ -39,6 +39,11 @@ YES_MODE=false
|
|
|
39
39
|
VERBOSE=false
|
|
40
40
|
PROJECT_PATH=""
|
|
41
41
|
|
|
42
|
+
# Rulesets that already matched and were therefore not sent, and the payload
|
|
43
|
+
# GitHub last accepted (set by apply_with_integration_fallback).
|
|
44
|
+
UNCHANGED_COUNT=0
|
|
45
|
+
APPLIED_PAYLOAD=""
|
|
46
|
+
|
|
42
47
|
# Project type hierarchy (child -> parent)
|
|
43
48
|
# Using a function to avoid associative array issues with set -u
|
|
44
49
|
get_parent_type() {
|
|
@@ -428,22 +433,14 @@ add_config_required_checks() {
|
|
|
428
433
|
}
|
|
429
434
|
|
|
430
435
|
preserve_live_required_checks() {
|
|
431
|
-
local
|
|
432
|
-
local
|
|
433
|
-
local json="$3"
|
|
434
|
-
local ruleset_name="$4"
|
|
436
|
+
local live="$1"
|
|
437
|
+
local json="$2"
|
|
435
438
|
|
|
436
|
-
if [[ -z "$
|
|
439
|
+
if [[ -z "$live" ]]; then
|
|
437
440
|
echo "$json"
|
|
438
441
|
return 0
|
|
439
442
|
fi
|
|
440
443
|
|
|
441
|
-
local live
|
|
442
|
-
if ! live=$(gh api -X GET "repos/$repo/rulesets/$existing_id" 2>/dev/null); then
|
|
443
|
-
log_warning "Could not read existing ruleset '$ruleset_name' details — refusing to silently replace required checks" >&2
|
|
444
|
-
return 1
|
|
445
|
-
fi
|
|
446
|
-
|
|
447
444
|
echo "$json" | jq --argjson live "$live" '
|
|
448
445
|
def required_checks:
|
|
449
446
|
(.rules // [])
|
|
@@ -475,6 +472,73 @@ preserve_live_required_checks() {
|
|
|
475
472
|
end'
|
|
476
473
|
}
|
|
477
474
|
|
|
475
|
+
# The contexts the outgoing payload requires that the live ruleset does not.
|
|
476
|
+
# This is the drift a template change opens: a context added to a template never
|
|
477
|
+
# reaches an already-configured repository on its own (#2641), so the operator
|
|
478
|
+
# needs the reconciliation to SAY which checks it just made blocking rather than
|
|
479
|
+
# to report an opaque "applied".
|
|
480
|
+
ruleset_added_contexts() {
|
|
481
|
+
local live="$1"
|
|
482
|
+
local payload="$2"
|
|
483
|
+
|
|
484
|
+
jq -r -n --argjson live "${live:-null}" --argjson want "$payload" '
|
|
485
|
+
def contexts:
|
|
486
|
+
[ (.rules // [])[]
|
|
487
|
+
| select(.type == "required_status_checks")
|
|
488
|
+
| (.parameters.required_status_checks // [])[]
|
|
489
|
+
| .context ];
|
|
490
|
+
|
|
491
|
+
(if $live == null then [] else ($live | contexts) end) as $have
|
|
492
|
+
| ($want | contexts)
|
|
493
|
+
| map(select(. as $context | ($have | index($context)) | not))
|
|
494
|
+
| .[]'
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
# True when everything the payload asks for is ALREADY true of the live ruleset.
|
|
498
|
+
#
|
|
499
|
+
# Deliberately a subset test rather than an equality test: GitHub echoes back
|
|
500
|
+
# parameters it filled in with its own defaults (`required_reviewers`,
|
|
501
|
+
# `require_extra_approval_for_unattributed_changes`, ...) which no template
|
|
502
|
+
# names. Comparing whole documents would report drift on every run and make
|
|
503
|
+
# "nothing to do" unreachable — the AC's second scenario would then never hold
|
|
504
|
+
# even on a repository that needs no change.
|
|
505
|
+
ruleset_is_current() {
|
|
506
|
+
local live="$1"
|
|
507
|
+
local payload="$2"
|
|
508
|
+
|
|
509
|
+
[[ -n "$live" ]] || return 1
|
|
510
|
+
|
|
511
|
+
jq -e -n \
|
|
512
|
+
--argjson live "$(strip_readonly_fields "$live")" \
|
|
513
|
+
--argjson want "$payload" '
|
|
514
|
+
def covers($have; $need):
|
|
515
|
+
if ($need | type) == "object" then
|
|
516
|
+
($have | type) == "object"
|
|
517
|
+
and ([$need | keys_unsorted[]]
|
|
518
|
+
| all(. as $key | ($have | has($key)) and covers($have[$key]; $need[$key])))
|
|
519
|
+
elif ($need | type) == "array" then
|
|
520
|
+
($have | type) == "array"
|
|
521
|
+
and ($have | length) == ($need | length)
|
|
522
|
+
and ([range(0; $need | length)]
|
|
523
|
+
| all(. as $index | covers($have[$index]; $need[$index])))
|
|
524
|
+
else $have == $need
|
|
525
|
+
end;
|
|
526
|
+
covers($live; $want)' > /dev/null
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
# Print one line per context this run makes blocking. Reads the payload that
|
|
530
|
+
# GitHub actually ACCEPTED, so a context dropped by the integration fallback is
|
|
531
|
+
# never reported as added.
|
|
532
|
+
report_added_contexts() {
|
|
533
|
+
local live="$1"
|
|
534
|
+
local payload="$2"
|
|
535
|
+
|
|
536
|
+
local context
|
|
537
|
+
while IFS= read -r context; do
|
|
538
|
+
[[ -n "$context" ]] && log_info " + now required: $context"
|
|
539
|
+
done < <(ruleset_added_contexts "$live" "$payload")
|
|
540
|
+
}
|
|
541
|
+
|
|
478
542
|
apply_ruleset() {
|
|
479
543
|
local repo="$1"
|
|
480
544
|
local template_file="$2"
|
|
@@ -499,10 +563,22 @@ apply_ruleset() {
|
|
|
499
563
|
return 0
|
|
500
564
|
fi
|
|
501
565
|
|
|
502
|
-
local existing_id
|
|
566
|
+
local existing_id live=""
|
|
503
567
|
existing_id=$(find_ruleset_by_name "$existing_rulesets" "$ruleset_name")
|
|
504
568
|
if [[ -n "$existing_id" ]]; then
|
|
505
|
-
|
|
569
|
+
if ! live=$(gh api -X GET "repos/$repo/rulesets/$existing_id" 2>/dev/null); then
|
|
570
|
+
log_warning "Could not read existing ruleset '$ruleset_name' details — refusing to silently replace required checks"
|
|
571
|
+
return 1
|
|
572
|
+
fi
|
|
573
|
+
clean_template=$(preserve_live_required_checks "$live" "$clean_template")
|
|
574
|
+
fi
|
|
575
|
+
|
|
576
|
+
# Idempotence: a run that would change nothing must say so and send nothing,
|
|
577
|
+
# so a second run is visibly a no-op rather than an indistinguishable write.
|
|
578
|
+
if ruleset_is_current "$live" "$clean_template"; then
|
|
579
|
+
log_success "Ruleset '$ruleset_name' already matches the template — nothing to do"
|
|
580
|
+
UNCHANGED_COUNT=$((UNCHANGED_COUNT + 1))
|
|
581
|
+
return 0
|
|
506
582
|
fi
|
|
507
583
|
|
|
508
584
|
if [[ "$DRY_RUN" == "true" ]]; then
|
|
@@ -511,6 +587,7 @@ apply_ruleset() {
|
|
|
511
587
|
else
|
|
512
588
|
log_info "[DRY RUN] Would create ruleset '$ruleset_name'"
|
|
513
589
|
fi
|
|
590
|
+
report_added_contexts "$live" "$clean_template"
|
|
514
591
|
log_verbose "Template: $template_file"
|
|
515
592
|
return 0
|
|
516
593
|
fi
|
|
@@ -521,7 +598,9 @@ apply_ruleset() {
|
|
|
521
598
|
log_info "Creating ruleset '$ruleset_name'..."
|
|
522
599
|
fi
|
|
523
600
|
|
|
601
|
+
APPLIED_PAYLOAD=""
|
|
524
602
|
if apply_with_integration_fallback "$repo" "$ruleset_name" "$clean_template" "$existing_id"; then
|
|
603
|
+
report_added_contexts "$live" "${APPLIED_PAYLOAD:-$clean_template}"
|
|
525
604
|
return 0
|
|
526
605
|
fi
|
|
527
606
|
log_error "Failed to apply ruleset '$ruleset_name'"
|
|
@@ -546,6 +625,7 @@ apply_with_integration_fallback() {
|
|
|
546
625
|
# Capture stdout AND stderr — gh prints the API error body (which carries
|
|
547
626
|
# the "Invalid integration ids" detail) on stdout, not stderr.
|
|
548
627
|
send_ruleset() {
|
|
628
|
+
APPLIED_PAYLOAD="$1"
|
|
549
629
|
echo "$1" > "$temp_file"
|
|
550
630
|
if [[ -n "$existing_id" ]]; then
|
|
551
631
|
error_output=$(gh api -X PUT "repos/$repo/rulesets/$existing_id" --input "$temp_file" 2>&1)
|
|
@@ -772,7 +852,10 @@ main() {
|
|
|
772
852
|
if [[ "$DRY_RUN" == "true" ]]; then
|
|
773
853
|
log_info "Dry run complete. ${#templates[@]} ruleset(s) would be applied."
|
|
774
854
|
else
|
|
775
|
-
log_success "Applied $success_count ruleset(s)"
|
|
855
|
+
log_success "Applied $((success_count - UNCHANGED_COUNT)) ruleset(s), $UNCHANGED_COUNT already current"
|
|
856
|
+
if [[ $UNCHANGED_COUNT -eq $success_count ]]; then
|
|
857
|
+
log_info "Nothing to do — every ruleset already matches its template"
|
|
858
|
+
fi
|
|
776
859
|
if [[ $fail_count -gt 0 ]]; then
|
|
777
860
|
log_warning "$fail_count ruleset(s) failed"
|
|
778
861
|
exit 1
|