redlinegate 0.0.1

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 (198) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +188 -0
  3. package/commands/redline-init.md +54 -0
  4. package/commands/redline-verify.md +69 -0
  5. package/dist/bin/redline.js +605 -0
  6. package/dist/bin/redline.js.map +1 -0
  7. package/dist/commands/exempt.js +45 -0
  8. package/dist/commands/exempt.js.map +1 -0
  9. package/dist/commands/init.js +615 -0
  10. package/dist/commands/init.js.map +1 -0
  11. package/dist/commands/policy.js +25 -0
  12. package/dist/commands/policy.js.map +1 -0
  13. package/dist/commands/remove.js +427 -0
  14. package/dist/commands/remove.js.map +1 -0
  15. package/dist/commands/review.js +72 -0
  16. package/dist/commands/review.js.map +1 -0
  17. package/dist/commands/sync.js +42 -0
  18. package/dist/commands/sync.js.map +1 -0
  19. package/dist/commands/verify.js +478 -0
  20. package/dist/commands/verify.js.map +1 -0
  21. package/dist/config/redline-json.js +187 -0
  22. package/dist/config/redline-json.js.map +1 -0
  23. package/dist/core/errors.js +25 -0
  24. package/dist/core/errors.js.map +1 -0
  25. package/dist/core/git.js +152 -0
  26. package/dist/core/git.js.map +1 -0
  27. package/dist/core/log.js +22 -0
  28. package/dist/core/log.js.map +1 -0
  29. package/dist/core/severity.js +16 -0
  30. package/dist/core/severity.js.map +1 -0
  31. package/dist/core/version.js +15 -0
  32. package/dist/core/version.js.map +1 -0
  33. package/dist/detect/scan.js +79 -0
  34. package/dist/detect/scan.js.map +1 -0
  35. package/dist/detect/stack.js +71 -0
  36. package/dist/detect/stack.js.map +1 -0
  37. package/dist/enforce/ladder.js +148 -0
  38. package/dist/enforce/ladder.js.map +1 -0
  39. package/dist/exempt/parse.js +86 -0
  40. package/dist/exempt/parse.js.map +1 -0
  41. package/dist/metrics/options.js +257 -0
  42. package/dist/metrics/options.js.map +1 -0
  43. package/dist/metrics/run.js +72 -0
  44. package/dist/metrics/run.js.map +1 -0
  45. package/dist/platforms/azure/client.js +53 -0
  46. package/dist/platforms/azure/client.js.map +1 -0
  47. package/dist/platforms/azure/index.js +69 -0
  48. package/dist/platforms/azure/index.js.map +1 -0
  49. package/dist/platforms/azure/install.js +917 -0
  50. package/dist/platforms/azure/install.js.map +1 -0
  51. package/dist/platforms/azure/policy-types.js +70 -0
  52. package/dist/platforms/azure/policy-types.js.map +1 -0
  53. package/dist/platforms/azure/verify.js +309 -0
  54. package/dist/platforms/azure/verify.js.map +1 -0
  55. package/dist/platforms/detect.js +40 -0
  56. package/dist/platforms/detect.js.map +1 -0
  57. package/dist/platforms/github/client.js +49 -0
  58. package/dist/platforms/github/client.js.map +1 -0
  59. package/dist/platforms/github/index.js +54 -0
  60. package/dist/platforms/github/index.js.map +1 -0
  61. package/dist/platforms/github/install.js +558 -0
  62. package/dist/platforms/github/install.js.map +1 -0
  63. package/dist/platforms/github/push.js +102 -0
  64. package/dist/platforms/github/push.js.map +1 -0
  65. package/dist/platforms/github/remote.js +36 -0
  66. package/dist/platforms/github/remote.js.map +1 -0
  67. package/dist/platforms/github/verify.js +366 -0
  68. package/dist/platforms/github/verify.js.map +1 -0
  69. package/dist/platforms/http.js +58 -0
  70. package/dist/platforms/http.js.map +1 -0
  71. package/dist/platforms/pull-request-templates.js +157 -0
  72. package/dist/platforms/pull-request-templates.js.map +1 -0
  73. package/dist/platforms/remote.js +21 -0
  74. package/dist/platforms/remote.js.map +1 -0
  75. package/dist/platforms/resolve.js +69 -0
  76. package/dist/platforms/resolve.js.map +1 -0
  77. package/dist/platforms/shape.js +19 -0
  78. package/dist/platforms/shape.js.map +1 -0
  79. package/dist/platforms/types.js +24 -0
  80. package/dist/platforms/types.js.map +1 -0
  81. package/dist/policy/checks.js +91 -0
  82. package/dist/policy/checks.js.map +1 -0
  83. package/dist/policy/diff.js +50 -0
  84. package/dist/policy/diff.js.map +1 -0
  85. package/dist/registry/discover.js +74 -0
  86. package/dist/registry/discover.js.map +1 -0
  87. package/dist/registry/serialize.js +41 -0
  88. package/dist/registry/serialize.js.map +1 -0
  89. package/dist/registry/types.js +2 -0
  90. package/dist/registry/types.js.map +1 -0
  91. package/dist/remove/host.js +294 -0
  92. package/dist/remove/host.js.map +1 -0
  93. package/dist/render/commands.js +139 -0
  94. package/dist/render/commands.js.map +1 -0
  95. package/dist/render/manifest.js +82 -0
  96. package/dist/render/manifest.js.map +1 -0
  97. package/dist/render/markers.js +166 -0
  98. package/dist/render/markers.js.map +1 -0
  99. package/dist/render/profile.js +23 -0
  100. package/dist/render/profile.js.map +1 -0
  101. package/dist/render/standards.js +169 -0
  102. package/dist/render/standards.js.map +1 -0
  103. package/dist/render/vendors.js +199 -0
  104. package/dist/render/vendors.js.map +1 -0
  105. package/dist/review/engines/api.js +95 -0
  106. package/dist/review/engines/api.js.map +1 -0
  107. package/dist/review/engines/embedded.js +20 -0
  108. package/dist/review/engines/embedded.js.map +1 -0
  109. package/dist/review/engines/types.js +2 -0
  110. package/dist/review/engines/types.js.map +1 -0
  111. package/dist/review/glob.js +29 -0
  112. package/dist/review/glob.js.map +1 -0
  113. package/dist/review/prompt.js +45 -0
  114. package/dist/review/prompt.js.map +1 -0
  115. package/dist/review/rules.js +25 -0
  116. package/dist/review/rules.js.map +1 -0
  117. package/dist/review/schema.js +84 -0
  118. package/dist/review/schema.js.map +1 -0
  119. package/dist/review/scope.js +27 -0
  120. package/dist/review/scope.js.map +1 -0
  121. package/dist/sarif/map.js +58 -0
  122. package/dist/sarif/map.js.map +1 -0
  123. package/dist/sarif/parse.js +88 -0
  124. package/dist/sarif/parse.js.map +1 -0
  125. package/dist/sarif/types.js +2 -0
  126. package/dist/sarif/types.js.map +1 -0
  127. package/dist/sync/host.js +21 -0
  128. package/dist/sync/host.js.map +1 -0
  129. package/dist/sync/plan.js +47 -0
  130. package/dist/sync/plan.js.map +1 -0
  131. package/dist/sync/render.js +57 -0
  132. package/dist/sync/render.js.map +1 -0
  133. package/dist/sync/run.js +118 -0
  134. package/dist/sync/run.js.map +1 -0
  135. package/dist/verify/host.js +52 -0
  136. package/dist/verify/host.js.map +1 -0
  137. package/dist/verify/remote.js +146 -0
  138. package/dist/verify/remote.js.map +1 -0
  139. package/package.json +52 -0
  140. package/platforms/azure/gate-template.yml +105 -0
  141. package/rulesets/redline-org-ruleset.json +46 -0
  142. package/rulesets/redline-ruleset.json +49 -0
  143. package/scripts/assign-rule-ids.mjs +129 -0
  144. package/scripts/build-baseline.mjs +124 -0
  145. package/scripts/build-correlation.mjs +74 -0
  146. package/scripts/build-dashboard.mjs +584 -0
  147. package/scripts/build-digest.mjs +164 -0
  148. package/scripts/build-inbox.mjs +133 -0
  149. package/scripts/build-registry.mjs +42 -0
  150. package/scripts/build-roi.mjs +181 -0
  151. package/scripts/check-pins.mjs +88 -0
  152. package/scripts/collect-telemetry.mjs +300 -0
  153. package/scripts/lib/__tests__/baseline.test.mjs +132 -0
  154. package/scripts/lib/__tests__/correlate.test.mjs +131 -0
  155. package/scripts/lib/__tests__/dora.test.mjs +110 -0
  156. package/scripts/lib/__tests__/exemptions.test.mjs +85 -0
  157. package/scripts/lib/__tests__/metrics-scanner.test.mjs +65 -0
  158. package/scripts/lib/__tests__/sarif.test.mjs +125 -0
  159. package/scripts/lib/__tests__/spend.test.mjs +64 -0
  160. package/scripts/lib/baseline.mjs +156 -0
  161. package/scripts/lib/correlate.mjs +125 -0
  162. package/scripts/lib/dora.mjs +133 -0
  163. package/scripts/lib/exemptions.mjs +79 -0
  164. package/scripts/lib/metrics.mjs +172 -0
  165. package/scripts/lib/rules.mjs +107 -0
  166. package/scripts/lib/sarif.mjs +108 -0
  167. package/scripts/lib/spend.mjs +69 -0
  168. package/scripts/measure-context.mjs +101 -0
  169. package/scripts/render-self.mjs +18 -0
  170. package/scripts/score-seeds.mjs +294 -0
  171. package/scripts/validate.mjs +394 -0
  172. package/standards/core.md +124 -0
  173. package/standards/manifest.json +214 -0
  174. package/standards/stacks/csharp.md +29 -0
  175. package/standards/stacks/go.md +30 -0
  176. package/standards/stacks/java.md +30 -0
  177. package/standards/stacks/javascript.md +42 -0
  178. package/standards/stacks/kotlin.md +27 -0
  179. package/standards/stacks/microservices.md +37 -0
  180. package/standards/stacks/nodejs.md +34 -0
  181. package/standards/stacks/python.md +29 -0
  182. package/standards/stacks/react-native.md +29 -0
  183. package/standards/stacks/react.md +46 -0
  184. package/standards/stacks/swift.md +26 -0
  185. package/standards/stacks/terraform.md +27 -0
  186. package/templates/CODEOWNERS +29 -0
  187. package/templates/azure/pull_request_template.md +72 -0
  188. package/templates/github/pull_request_template.md +72 -0
  189. package/templates/redline.yml +35 -0
  190. package/templates/repo-context.md +64 -0
  191. package/workflows/dashboard.yml +119 -0
  192. package/workflows/inbox.yml +97 -0
  193. package/workflows/redline-collect.yml +61 -0
  194. package/workflows/redline-gate.yml +301 -0
  195. package/workflows/redline-sync.yml +79 -0
  196. package/workflows/seed-canary.yml +231 -0
  197. package/workflows/verify-onboarding.yml +115 -0
  198. package/workflows/weekly-digest.yml +78 -0
@@ -0,0 +1,115 @@
1
+ # Lives in the Redline source repo. Re-verifies every onboarded repo on a schedule and
2
+ # opens one tracking issue when any of them has drifted — a ruleset edited by hand, a
3
+ # renamed caller job, push protection switched off, artifacts left stale by an ignored
4
+ # sync pull request.
5
+ #
6
+ # Drift is silent by nature: a repository whose gate stopped publishing looks exactly
7
+ # like one whose gate is green and quiet. This loop is what makes the difference visible
8
+ # without anyone going to look.
9
+ #
10
+ # Targets come from registry.json — the same derived register sync uses. A check that
11
+ # genuinely needs a working tree reports `??` rather than passing, so a repository is
12
+ # never reported healthy on the strength of a check that did not run.
13
+ name: Redline Verify Onboarding
14
+
15
+ on:
16
+ schedule:
17
+ - cron: '0 6 * * 2'
18
+ workflow_dispatch:
19
+ inputs:
20
+ only:
21
+ description: Verify a single repo, as owner/name
22
+ type: string
23
+ required: false
24
+
25
+ permissions:
26
+ contents: read
27
+ issues: write
28
+
29
+ concurrency:
30
+ group: redline-verify-onboarding
31
+ cancel-in-progress: false
32
+
33
+ jobs:
34
+ verify:
35
+ runs-on: ubuntu-latest
36
+ timeout-minutes: 45
37
+ steps:
38
+ - uses: actions/checkout@v4
39
+ - uses: actions/setup-node@v4
40
+ with:
41
+ node-version: 22
42
+
43
+ - name: Install
44
+ run: npm ci
45
+
46
+ - name: Build CLI
47
+ run: npm run build
48
+
49
+ - name: Verify every registered repository
50
+ id: verify
51
+ env:
52
+ GH_TOKEN: ${{ secrets.REDLINE_ORG_READ_TOKEN }}
53
+ ONLY: ${{ inputs.only }}
54
+ run: |
55
+ set -uo pipefail
56
+ if [ ! -f registry.json ]; then
57
+ echo "::error::registry.json is missing — the register has not been derived yet."
58
+ exit 1
59
+ fi
60
+
61
+ if [ -n "$ONLY" ]; then
62
+ repos="$ONLY"
63
+ else
64
+ # String concatenation rather than a template literal: `${...}` inside
65
+ # single quotes is ambiguous to a reader and to shellcheck, which
66
+ # cannot know the braces are JavaScript's. Avoiding the construct
67
+ # beats suppressing the warning about it.
68
+ repos=$(node -e 'const r=require("./registry.json");for(const e of r.entries)if(e.host==="github")console.log(e.org+"/"+e.repo)')
69
+ fi
70
+
71
+ : > drift.txt
72
+ # `set -e` is deliberately NOT on for this loop: a drifted repository
73
+ # exits non-zero by design, and aborting on the first one would leave
74
+ # the rest of the estate unverified every week.
75
+ while read -r repo; do
76
+ [ -z "$repo" ] && continue
77
+ echo "--- $repo"
78
+ if ! node dist/bin/redline.js verify --repo "$repo" 2>&1 | tee /tmp/out.txt; then
79
+ {
80
+ echo "### $repo"
81
+ echo '```'
82
+ grep -E '^(FAIL| \?\?)' /tmp/out.txt || cat /tmp/out.txt
83
+ echo '```'
84
+ } >> drift.txt
85
+ fi
86
+ done <<< "$repos"
87
+
88
+ if [ -s drift.txt ]; then
89
+ echo "drift=true" >> "$GITHUB_OUTPUT"
90
+ else
91
+ echo "drift=false" >> "$GITHUB_OUTPUT"
92
+ fi
93
+
94
+ - name: Open or update the drift issue
95
+ if: steps.verify.outputs.drift == 'true'
96
+ env:
97
+ GH_TOKEN: ${{ github.token }}
98
+ TITLE: 'Redline: onboarded repositories have drifted'
99
+ run: |
100
+ set -euo pipefail
101
+ # One issue, updated in place. A new issue per run turns a standing
102
+ # problem into a backlog nobody reads.
103
+ body=$(cat drift.txt)
104
+ existing=$(gh issue list --state open --search "$TITLE in:title" --json number --jq '.[0].number // empty')
105
+ if [ -n "$existing" ]; then
106
+ gh issue comment "$existing" --body "$body"
107
+ else
108
+ gh issue create --title "$TITLE" --body "$body"
109
+ fi
110
+
111
+ - name: Summarise
112
+ if: always()
113
+ run: |
114
+ if [ -s drift.txt ]; then cat drift.txt >> "$GITHUB_STEP_SUMMARY";
115
+ else echo "No drift detected." >> "$GITHUB_STEP_SUMMARY"; fi
@@ -0,0 +1,78 @@
1
+ # Lives in the redline-metrics repo. Monday-morning stakeholder digest.
2
+ #
3
+ # TEAMS_WEBHOOK_URL must point at a Power Automate "When a Teams webhook request is
4
+ # received" flow. The old Office 365 connector webhooks (the `{"text": "..."}` shape)
5
+ # were retired by Microsoft and silently stop delivering.
6
+ name: Redline Weekly Digest
7
+
8
+ on:
9
+ schedule:
10
+ - cron: '0 7 * * 1'
11
+ workflow_dispatch:
12
+
13
+ permissions:
14
+ contents: read
15
+
16
+ jobs:
17
+ digest:
18
+ runs-on: ubuntu-latest
19
+ timeout-minutes: 15
20
+ steps:
21
+ - uses: actions/checkout@v4
22
+ - uses: actions/setup-node@v4
23
+ with:
24
+ node-version: 22
25
+
26
+ - name: Count open and stale PRs
27
+ id: prs
28
+ env:
29
+ GH_TOKEN: ${{ secrets.REDLINE_ORG_READ_TOKEN }}
30
+ ORG: ${{ github.repository_owner }}
31
+ run: |
32
+ set -euo pipefail
33
+ cutoff=$(date -u -d '7 days ago' +%Y-%m-%d)
34
+
35
+ # gh search caps at 1000 results; --json + jq length would silently truncate.
36
+ # total_count from the REST search endpoint is exact.
37
+ count() {
38
+ gh api -X GET search/issues -f q="$1" -f per_page=1 --jq '.total_count'
39
+ }
40
+ open_prs=$(count "org:$ORG is:pr is:open draft:false")
41
+ stale_prs=$(count "org:$ORG is:pr is:open draft:false updated:<$cutoff")
42
+
43
+ echo "open=$open_prs" >> "$GITHUB_OUTPUT"
44
+ echo "stale=$stale_prs" >> "$GITHUB_OUTPUT"
45
+
46
+ - name: Build Adaptive Card
47
+ run: |
48
+ npx --yes "redlinegate@0.0.1" metrics digest \
49
+ --org "${{ github.repository_owner }}" \
50
+ --days 7 \
51
+ --open-prs "${{ steps.prs.outputs.open }}" \
52
+ --stale-prs "${{ steps.prs.outputs.stale }}" \
53
+ --seed-scores data/seed-scores.jsonl \
54
+ --dashboard-url "${{ vars.REDLINE_DASHBOARD_URL }}" \
55
+ --out digest.json
56
+
57
+ - name: Post to Teams
58
+ env:
59
+ WEBHOOK: ${{ secrets.TEAMS_WEBHOOK_URL }}
60
+ run: |
61
+ set -euo pipefail
62
+ if [[ -z "${WEBHOOK:-}" ]]; then
63
+ echo "::error::TEAMS_WEBHOOK_URL is not set — digest built but not delivered."
64
+ exit 1
65
+ fi
66
+ curl -sS --fail-with-body -X POST \
67
+ -H 'Content-Type: application/json' \
68
+ --data @digest.json "$WEBHOOK"
69
+
70
+ - name: Attach digest to the run
71
+ if: always()
72
+ run: |
73
+ {
74
+ echo '### Digest payload'
75
+ echo '```json'
76
+ cat digest.json 2>/dev/null || echo '{}'
77
+ echo '```'
78
+ } >> "$GITHUB_STEP_SUMMARY"