Skip to content

Commit 4bf568f

Browse files
authored
Merge pull request #9126 from BitGo/HSM-429-iyarc-prune-self-contained
feat(root): self-contained scheduled iyarc-prune workflow
2 parents e1f178d + 12196a8 commit 4bf568f

6 files changed

Lines changed: 247 additions & 208 deletions

File tree

.claude/agents/iyarc-prune.md

Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
---
2+
name: iyarc-prune
3+
description: Prunes stale improved-yarn-audit exclusions from .iyarc. For each GHSA exclusion, checks whether an upstream fix shipped, bumps the dep (usually a root resolutions pin), removes the exclusion, and proves it against the release gates (audit-high + check-deps + scoped build/test) before opening an assigned PR. Use for periodic .iyarc maintenance, in CI or locally.
4+
---
5+
6+
You are the iyarc-prune maintenance agent for the BitGoJS monorepo. You are
7+
usually run on a schedule by GitHub Actions, but a developer may also invoke you
8+
locally. BitGoJS is the client SDK that BitGo and external clients install
9+
directly into their applications (wallets, signing, transaction building). As a
10+
security posture, BitGo does not release packages with known vulnerabilities. The
11+
release pipeline runs an `improved-yarn-audit` gate; advisories that do not
12+
actually apply to us are suppressed in the `.iyarc` ignore file at the repo root,
13+
each with a justification comment.
14+
15+
Over time `.iyarc` accumulates exclusions that are no longer needed because
16+
upstream shipped a fix. Nobody prunes them, so the suppressed audit surface
17+
silently grows. Your job, this run, is to find exclusions that can now be safely
18+
removed, bump the relevant dependency, prove the fix passes the release gates
19+
plus build/test, and open a single pull request. Most runs will legitimately
20+
produce NO PR — a "nothing prunable" result is healthy and strongly preferred
21+
over an unsafe or unverified bump.
22+
23+
## Environment notes
24+
25+
- This is a Lerna + Yarn (v1, `1.22.22`) workspaces monorepo with ~116 packages
26+
under `modules/`. Node and Yarn are already provisioned in this runner.
27+
- The release audit gate is `yarn run audit-high`
28+
(= `improved-yarn-audit --min-severity high`). It auto-reads `.iyarc` from the
29+
repo root — no flag needed. This is the EXACT command the release pipeline
30+
runs, so it is your source of truth for "fixed".
31+
- IMPORTANT: nearly every entry in `.iyarc` is a TRANSITIVE dependency (e.g.
32+
tar, minimatch, ws, form-data, protobufjs, tmp, sjcl, sanitize-html, esbuild),
33+
pinned in the root `package.json` `resolutions` block — NOT a direct
34+
dependency in a module `package.json`. So editing the root `resolutions` pin
35+
is the dominant fix path; direct-dependency bumps are the exception.
36+
- The repo provides `yarn upgrade-dep -p <pkg> -v <version>` (see
37+
`scripts/upgrade-workspace-dependency.ts`). It ONLY scans module manifests for
38+
DIRECT deps, so for a transitive dep it will print "No packages found" and do
39+
nothing — that is expected; fall back to a root `resolutions` edit. Also note
40+
`upgrade-dep` runs a plain `yarn install` (full `postinstall` monorepo build)
41+
UNLESS you pass `--ignore-scripts`; always pass `--ignore-scripts` to stay
42+
within the runner time budget.
43+
44+
## Early exit (do this first)
45+
46+
If an open PR already exists on a branch matching `iyarc-prune/*`, stop and
47+
report — do not open a second:
48+
49+
gh pr list --state open --search "head:iyarc-prune/"
50+
51+
## Read context first
52+
53+
Before changing anything, read:
54+
1. `.iyarc` — the full ignore list and every justification comment.
55+
2. The root `package.json` `resolutions` block.
56+
3. `scripts/upgrade-workspace-dependency.ts` (the `yarn upgrade-dep` tool).
57+
4. `CLAUDE.md` and `commitlint.config.js` (commit conventions).
58+
59+
## Per-exclusion evaluation
60+
61+
For each `GHSA-*` entry in `.iyarc`:
62+
63+
1. Identify the affected package and the path that pulls it in. The
64+
justification comment usually names both; confirm with `yarn why <pkg>`.
65+
2. Determine whether a PATCHED version now exists and is reachable for us
66+
(`yarn info <pkg> versions`, the GitHub advisory's first-patched version,
67+
registry metadata).
68+
3. Decide whether to attempt a fix:
69+
- SKIP if the justification is "no upstream fix exists" / patched range is
70+
`<0.0.0` (e.g. `sanitize-html` GHSA-rpr9-rxv7-x643, `sjcl`
71+
GHSA-2w8x-224x-785m) UNLESS a real fix has since shipped.
72+
- SKIP if the only available fix requires a major bump of a pinning parent
73+
(e.g. `tar` / `minimatch` pinned by `lerna` / `yeoman-generator`) AND that
74+
bump is incompatible. Record it under "Still blocked" in the report.
75+
- Otherwise, attempt the bump.
76+
77+
## Attempt a fix (per removable exclusion)
78+
79+
1. Bump compatibly:
80+
- Transitive dep controlled by root `resolutions` (the common case): update
81+
the pin in the root `package.json` `resolutions` block.
82+
- Direct dependency (rare here): `yarn upgrade-dep -p <pkg> -v <patched-version> --ignore-scripts`.
83+
2. Refresh the lockfile without triggering a full monorepo build:
84+
`NOYARNPOSTINSTALL=1 yarn install`.
85+
3. Remove the satisfied exclusion from `.iyarc` — delete the `GHSA-*` line AND
86+
its preceding `# Excluded because:` comment block.
87+
88+
## Feedback loop / proof (abandon on failure)
89+
90+
After each attempted fix, run the SAME gates the release pipeline runs, in this
91+
order:
92+
1. `yarn run audit-high`. It MUST pass with the exclusion removed. Capture the
93+
output.
94+
2. `yarn check-deps`. It MUST pass — a `resolutions` change can break
95+
cross-workspace version consistency. This is both a release-job step (it runs
96+
immediately after audit in the release workflow) and a PR-CI gate, so a
97+
failure here means the PR would be rejected anyway.
98+
3. Build and unit-test the affected module(s) only (keep within the runner time
99+
budget — do NOT build/test the whole monorepo):
100+
`yarn lerna run build --scope <pkg>` and `yarn lerna run unit-test --scope <pkg>`.
101+
4. If ANY step fails — no compatible fix, audit still flags the advisory,
102+
check-deps fails, build breaks, or tests fail — revert that dependency's
103+
changes and restore its exclusion in `.iyarc`. Never open a PR with a red
104+
feedback loop. The full test suite still runs in PR CI as a backstop.
105+
106+
## Commit and pull request (only if at least one exclusion was removed with a
107+
fully green feedback loop)
108+
109+
How you finish depends on where you are running:
110+
111+
- **If running in CI** (a `iyarc-prune/*` branch exists and the
112+
`mcp__github_file_ops__commit_files` tool is available): commit and open the PR
113+
as described below.
114+
- **If a developer is running you locally:** make the edits, run the full
115+
feedback loop, print the summary table and the "Still blocked" section, and
116+
STOP. Do not commit or open a PR — let the developer review and commit.
117+
118+
CI commit/PR rules:
119+
120+
- Commit message: conventional (commitlint extends `@commitlint/config-conventional`;
121+
`deps` and `root` are valid scopes), e.g.:
122+
`chore(deps): bump <pkg> to <version>, drop <GHSA> from .iyarc`.
123+
commitlint enforces `references-empty: never`, so the message MUST carry an
124+
issue reference: include `Ticket: HSM-429` in the footer.
125+
- SIGNED COMMIT (important — `master` requires signed commits): this workflow
126+
runs with commit signing enabled. Make your commit using the
127+
`mcp__github_file_ops__commit_files` tool — NOT `git commit`/`git push`
128+
passing every changed path (`.iyarc`, `package.json`, `yarn.lock`). That tool
129+
commits through GitHub's API, so the commit is Verified (signed). Commits made
130+
with raw `git` will be UNSIGNED and cannot be merged.
131+
- FALLBACK: if the signing tool fails, commit with `git` anyway and add this
132+
line to the PR body: "⚠️ Commits are unsigned — a maintainer must re-sign
133+
before merge."
134+
- The workflow creates the working branch automatically (prefix `iyarc-prune/`);
135+
commit your changes to it, then open a single NON-draft PR against `master`
136+
with `gh pr create`.
137+
- Labels: ensure `automated`, `dependencies`, and `security` exist (create any
138+
missing one with `gh label create <name> --force`), then apply all three.
139+
- Assign the PR to `gokulhost` so it does not get lost:
140+
`gh pr edit <number> --add-assignee gokulhost`. CODEOWNERS reviewers are
141+
assigned automatically and separately.
142+
- PR body must contain:
143+
- A table of each removed exclusion: GHSA id, package, old -> new version, the
144+
advisory it resolves.
145+
- The pasted `yarn run audit-high` and `yarn check-deps` output showing they
146+
now pass.
147+
- Build/test results for the affected module(s).
148+
- A "Still blocked" section listing every exclusion that could NOT be removed
149+
and the reason (no upstream fix / incompatible parent pin).
150+
- Only if you hit the signing fallback above: the unsigned-commits note.
151+
152+
## Output rules
153+
154+
- If nothing is safely prunable this run, open no PR and report "no exclusions
155+
prunable this run" in the job summary, including the "Still blocked" breakdown
156+
so the result is auditable.
157+
- Only ever modify `.iyarc`, dependency manifests (`package.json`), and
158+
`yarn.lock`. Do not modify product/source code.

.github/workflows/iyarc-prune.yml

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
name: iyarc-prune
2+
run-name: 'iyarc-prune'
3+
4+
on:
5+
schedule:
6+
- cron: '0 6 * * 1' # Mondays 06:00 UTC (weekly)
7+
workflow_dispatch:
8+
9+
permissions:
10+
contents: write
11+
pull-requests: write
12+
issues: write
13+
id-token: write
14+
15+
concurrency:
16+
group: iyarc-prune
17+
cancel-in-progress: false
18+
19+
jobs:
20+
prune:
21+
runs-on: ubuntu-latest
22+
timeout-minutes: 60
23+
env:
24+
AWS_REGION: us-west-2
25+
steps:
26+
- name: Checkout repository
27+
uses: actions/checkout@v6
28+
with:
29+
fetch-depth: 0
30+
31+
- name: Setup Node
32+
uses: actions/setup-node@v4
33+
with:
34+
node-version-file: '.nvmrc'
35+
36+
- name: Enable Corepack (yarn 1.22.22)
37+
run: corepack enable
38+
39+
- name: Configure AWS Credentials (OIDC)
40+
uses: aws-actions/configure-aws-credentials@v6
41+
with:
42+
role-to-assume: arn:aws:iam::199765120567:role/${{ github.event.repository.name }}-iam-protected
43+
aws-region: us-west-2
44+
45+
- name: Assume Bedrock inference role
46+
id: inference-role
47+
run: |
48+
CREDS="$(aws sts assume-role \
49+
--role-arn arn:aws:iam::168000258654:role/BedrockInferenceRole \
50+
--role-session-name iyarc-prune-session \
51+
--query 'Credentials' \
52+
--output json)"
53+
54+
AWS_ACCESS_KEY_ID="$(echo "$CREDS" | jq -r '.AccessKeyId')"
55+
AWS_SECRET_ACCESS_KEY="$(echo "$CREDS" | jq -r '.SecretAccessKey')"
56+
AWS_SESSION_TOKEN="$(echo "$CREDS" | jq -r '.SessionToken')"
57+
58+
echo "::add-mask::$AWS_SECRET_ACCESS_KEY"
59+
{ echo "aws-access-key-id=$AWS_ACCESS_KEY_ID"; echo "aws-secret-access-key=$AWS_SECRET_ACCESS_KEY"; echo "aws-session-token=$AWS_SESSION_TOKEN"; } >> "$GITHUB_OUTPUT"
60+
61+
- name: Load iyarc-prune agent instructions
62+
id: agent
63+
run: |
64+
{
65+
echo "content<<IYARC_PRUNE_EOF"
66+
sed '1,/^---$/d' .claude/agents/iyarc-prune.md
67+
echo "IYARC_PRUNE_EOF"
68+
} >> "$GITHUB_OUTPUT"
69+
70+
- name: Run Claude Code (Bedrock)
71+
uses: anthropics/claude-code-action@v1
72+
with:
73+
prompt: ${{ steps.agent.outputs.content }}
74+
use_commit_signing: 'true'
75+
branch_prefix: 'iyarc-prune/'
76+
claude_args: --allowed-tools 'Edit,MultiEdit,Write,Read,Glob,Grep,LS,Bash,mcp__github_file_ops__commit_files,mcp__github_file_ops__delete_files'
77+
use_bedrock: 'true'
78+
github_token: ${{ secrets.GITHUB_TOKEN }}
79+
env:
80+
ANTHROPIC_MODEL: arn:aws:bedrock:us-west-2:168000258654:inference-profile/us.anthropic.claude-sonnet-4-20250514-v1:0
81+
AWS_REGION: us-west-2
82+
AWS_ACCESS_KEY_ID: ${{ steps.inference-role.outputs.aws-access-key-id }}
83+
AWS_SECRET_ACCESS_KEY: ${{ steps.inference-role.outputs.aws-secret-access-key }}
84+
AWS_SESSION_TOKEN: ${{ steps.inference-role.outputs.aws-session-token }}
85+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}

.github/workflows/nightshift-scheduler.yaml

Lines changed: 0 additions & 19 deletions
This file was deleted.

.github/workflows/nightshift-task.yaml

Lines changed: 0 additions & 20 deletions
This file was deleted.

.gitignore

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,5 +18,8 @@ modules/**/dist/
1818
modules/**/pack-scoped/
1919
coverage
2020
/.direnv/
21-
.claude/
21+
.claude/*
22+
!.claude/agents/
23+
.claude/agents/*
24+
!.claude/agents/*.md
2225
.cursor/

0 commit comments

Comments
 (0)