microsoft/TypeAgent

Public

mirrored fromhttps://github.com/microsoft/TypeAgentAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
copilot/remove-deprecated-dependencies

Branches

Tags

  • No tags available.
0Branches0Tags
Go to file
Add file
Code

Clone

HTTPS

Download ZIP

.github/workflows/fix-dependabot-alerts.yml

322lines · modecode

1# Copyright (c) Microsoft Corporation.
2# Licensed under the MIT License.
3
4# Automatically remediate Dependabot security alerts by running the
5# fix-dependabot-alerts script, verifying the build for each fix,
6# and opening a pull request with the passing changes.
7#
8# Requires security-events: write permission for the GITHUB_TOKEN
9# to access the Dependabot alerts REST API.
10
11name: fix-dependabot-alerts
12
13on:
14 schedule:
15 # Run weekly on Monday at 9:00 UTC
16 - cron: "0 9 * * 1"
17 workflow_dispatch:
18 inputs:
19 dry-run:
20 description: "Dry run — analyse only, don't apply fixes"
21 type: boolean
22 default: false
23 auto-fix-args:
24 description: "Extra flags for the script (e.g. --apply-overrides=pkg1,pkg2)"
25 type: string
26 default: "--auto-fix"
27
28concurrency:
29 group: ${{ github.workflow }}
30 cancel-in-progress: true
31
32permissions:
33 contents: write
34 pull-requests: write
35 security-events: write
36
37env:
38 ELECTRON_CACHE: ${{ github.workspace }}/.cache/electron
39 ELECTRON_BUILDER_CACHE: ${{ github.workspace }}/.cache/electron-builder
40
41jobs:
42 fix-alerts:
43 runs-on: ubuntu-latest
44
45 steps:
46 - uses: actions/checkout@v4
47 with:
48 fetch-depth: 0
49
50 - uses: pnpm/action-setup@v4
51 name: Install pnpm
52 with:
53 package_json_file: ts/package.json
54
55 - uses: actions/setup-node@v4
56 with:
57 node-version: 22
58 cache: "pnpm"
59 cache-dependency-path: ts/pnpm-lock.yaml
60
61 - name: Install dependencies
62 working-directory: ts
63 run: pnpm install --frozen-lockfile --strict-peer-dependencies
64
65 - name: Verify gh authentication
66 env:
67 GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
68 run: |
69 gh auth status
70 echo "---"
71 gh api repos/${{ github.repository }}/dependabot/alerts?per_page=1 --jq 'length' || echo "API call failed with exit $?"
72
73 # ── Analyse and fix alerts (bisect) ─────────────────────────────
74 #
75 # 1. Dry-run to discover fixable packages
76 # 2. Apply each package's fix individually
77 # 3. Build-check after each; rollback failures
78 # 4. Only passing fixes survive into the PR
79 - name: Analyse and fix alerts
80 id: fix
81 working-directory: ts
82 env:
83 GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
84 run: |
85 # ── Step 1: Discover fixable packages ───────────────────────
86 echo "::group::Analysing alerts"
87 node tools/scripts/fix-dependabot-alerts.mjs --dry-run --json --skip-install > /tmp/dep-analysis.json 2>/tmp/dep-analysis.log || true
88
89 if ! jq -e '.summary' /tmp/dep-analysis.json > /dev/null 2>&1; then
90 echo "::error::Script produced no valid JSON output"
91 echo "--- stderr log ---"
92 cat /tmp/dep-analysis.log || true
93 echo "--- end stderr log ---"
94 echo "resolved=0" >> "$GITHUB_OUTPUT"
95 echo "blocked=0" >> "$GITHUB_OUTPUT"
96 echo "failed=0" >> "$GITHUB_OUTPUT"
97 echo "changes=false" >> "$GITHUB_OUTPUT"
98 echo "::endgroup::"
99 exit 0
100 fi
101
102 TOTAL_BLOCKED=$(jq '.summary.blocked' /tmp/dep-analysis.json)
103 TOTAL_NO_PATCH=$(jq '.summary.noPatch' /tmp/dep-analysis.json)
104
105 # Get the list of packages the script would resolve
106 FIXABLE=$(jq -r '.resolved[].package' /tmp/dep-analysis.json | sort -u)
107 FIXABLE_COUNT=$(echo "$FIXABLE" | grep -c . || true)
108 echo "Fixable packages ($FIXABLE_COUNT): $FIXABLE"
109 echo "::endgroup::"
110
111 if [ "$FIXABLE_COUNT" -eq 0 ]; then
112 echo "No fixable alerts"
113 echo "resolved=0" >> "$GITHUB_OUTPUT"
114 echo "blocked=$TOTAL_BLOCKED" >> "$GITHUB_OUTPUT"
115 echo "failed=0" >> "$GITHUB_OUTPUT"
116 echo "changes=false" >> "$GITHUB_OUTPUT"
117 exit 0
118 fi
119
120 if [ "${{ inputs.dry-run }}" == "true" ]; then
121 echo "resolved=$FIXABLE_COUNT" >> "$GITHUB_OUTPUT"
122 echo "blocked=$TOTAL_BLOCKED" >> "$GITHUB_OUTPUT"
123 echo "failed=0" >> "$GITHUB_OUTPUT"
124 echo "changes=false" >> "$GITHUB_OUTPUT"
125 exit 0
126 fi
127
128 # ── Step 2: Apply fixes one package at a time ───────────────
129 APPLIED=""
130 ROLLED_BACK=""
131 FLAGS="${{ inputs.auto-fix-args || '--auto-fix' }}"
132
133 for PKG in $FIXABLE; do
134 echo "::group::Fixing $PKG"
135
136 # Save a rollback point
137 cp package.json /tmp/pkg-backup.json
138 cp pnpm-lock.yaml /tmp/lock-backup.yaml 2>/dev/null || true
139
140 # Apply fix for this specific package — always use --auto-fix=$PKG
141 # regardless of other flags, so each package is targeted individually
142 read -r -a extra_args <<< "$FLAGS"
143 filtered_args=()
144 for arg in "${extra_args[@]}"; do
145 case "$arg" in
146 --auto-fix|--auto-fix=*)
147 ;;
148 *)
149 filtered_args+=("$arg")
150 ;;
151 esac
152 done
153 filtered_args+=("--auto-fix=$PKG")
154 filtered_args+=("--skip-install")
155 echo "Running: fix-dependabot-alerts.mjs ${filtered_args[*]}"
156 set +e
157 node tools/scripts/fix-dependabot-alerts.mjs "${filtered_args[@]}" 2>&1
158 fix_exit=$?
159 set -e
160
161 if ! git diff --quiet; then
162 # Changes were made — verify build
163 echo "Changes detected, verifying build..."
164 node tools/scripts/repo-policy-check.mjs --fix 2>/dev/null || true
165
166 set +e
167 pnpm run build 2>&1
168 build_exit=$?
169 set -e
170
171 if [ "$build_exit" -ne 0 ]; then
172 echo "::warning::Build failed after fixing $PKG — rolling back"
173 # Rollback: restore package.json and lockfile, reinstall
174 cp /tmp/pkg-backup.json package.json
175 cp /tmp/lock-backup.yaml pnpm-lock.yaml 2>/dev/null || true
176 pnpm install 2>&1 || true
177 git checkout -- . 2>/dev/null || true
178 ROLLED_BACK="$ROLLED_BACK $PKG"
179 else
180 echo "✅ $PKG fixed and build passed"
181 APPLIED="$APPLIED $PKG"
182 fi
183 else
184 echo "No changes for $PKG (may already be fixed)"
185 fi
186
187 echo "::endgroup::"
188 done
189
190 # ── Step 3: Report results ──────────────────────────────────
191 APPLIED_COUNT=$(echo "$APPLIED" | wc -w | tr -d ' ')
192 ROLLED_BACK_COUNT=$(echo "$ROLLED_BACK" | wc -w | tr -d ' ')
193
194 echo "resolved=$APPLIED_COUNT" >> "$GITHUB_OUTPUT"
195 echo "blocked=$TOTAL_BLOCKED" >> "$GITHUB_OUTPUT"
196 echo "failed=$ROLLED_BACK_COUNT" >> "$GITHUB_OUTPUT"
197 echo "applied_packages=$APPLIED" >> "$GITHUB_OUTPUT"
198 echo "rolled_back_packages=$ROLLED_BACK" >> "$GITHUB_OUTPUT"
199
200 cd ..
201 if git diff --quiet; then
202 echo "changes=false" >> "$GITHUB_OUTPUT"
203 else
204 echo "changes=true" >> "$GITHUB_OUTPUT"
205 echo "Files changed:"
206 git diff --stat
207 fi
208
209 # ── Final build + shell packaging verification ──────────────────
210 - name: Final build verification
211 if: ${{ steps.fix.outputs.changes == 'true' }}
212 id: build
213 working-directory: ts
214 run: |
215 node tools/scripts/repo-policy-check.mjs --fix || true
216 pnpm run build
217 echo "build_ok=true" >> "$GITHUB_OUTPUT"
218 env:
219 DEBUG_DEMB: true
220
221 - name: Package - shell
222 if: ${{ steps.build.outputs.build_ok == 'true' }}
223 id: shell
224 working-directory: ts
225 run: |
226 pnpm run shell:package
227 echo "shell_ok=true" >> "$GITHUB_OUTPUT"
228
229 # ── Create PR ───────────────────────────────────────────────────
230 - name: Create pull request
231 if: ${{ steps.fix.outputs.changes == 'true' && steps.build.outputs.build_ok == 'true' }}
232 env:
233 GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
234 run: |
235 BRANCH="automated/fix-dependabot-alerts-$(date +%Y%m%d)"
236
237 git config user.name "github-actions[bot]"
238 git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
239
240 git checkout -b "$BRANCH"
241 git add -A
242 git commit -m "fix: remediate Dependabot security alerts
243
244 Automated by fix-dependabot-alerts workflow.
245
246 Applied:${{ steps.fix.outputs.applied_packages }}
247 Rolled back:${{ steps.fix.outputs.rolled_back_packages || ' (none)' }}
248 Blocked: ${{ steps.fix.outputs.blocked }} package(s)
249 Shell packaging: ${{ steps.shell.outputs.shell_ok == 'true' && 'passed' || 'skipped' }}
250
251 Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>"
252
253 git push origin "$BRANCH"
254
255 # Build PR body from step outputs
256 APPLIED="${{ steps.fix.outputs.applied_packages }}"
257 ROLLED="${{ steps.fix.outputs.rolled_back_packages }}"
258 RESOLVED="${{ steps.fix.outputs.resolved }}"
259 BLOCKED="${{ steps.fix.outputs.blocked }}"
260 FAILED="${{ steps.fix.outputs.failed }}"
261
262 BODY=$(cat <<'PREOF'
263 ## Automated Dependabot Alert Remediation
264
265 This PR was automatically generated by the `fix-dependabot-alerts` workflow.
266 Each fix was applied individually and build-verified before inclusion.
267
268 ### Summary
269 PREOF
270 )
271
272 BODY="$BODY
273 - **Applied ($RESOLVED):**$APPLIED
274 - **Blocked:** $BLOCKED package(s) (require manual intervention)
275 - **Rolled back ($FAILED):**${ROLLED:- (none)}
276 - **Build:** ✅ Passed
277 - **Shell packaging:** ${{ steps.shell.outputs.shell_ok == 'true' && '✅ Passed' || '⚠️ Skipped' }}
278
279 ### How this works
280 1. Analyses all open Dependabot alerts
281 2. Applies each fix **individually** with build verification
282 3. Rolls back any fix that breaks the build
283 4. Only passing fixes are included in this PR
284
285 ### Review checklist
286 - [ ] Check that no breaking changes were introduced
287 - [ ] Verify rolled-back packages are investigated separately
288 - [ ] Run tests locally if concerned about specific packages"
289
290 gh pr create \
291 --base main \
292 --head "$BRANCH" \
293 --title "fix: remediate Dependabot security alerts ($(date +%Y-%m-%d))" \
294 --body "$BODY" \
295 --label "dependencies,security"
296
297 # ── Summary ─────────────────────────────────────────────────────
298 - name: Job summary
299 if: always()
300 run: |
301 echo "## Dependabot Alert Remediation" >> "$GITHUB_STEP_SUMMARY"
302 echo "" >> "$GITHUB_STEP_SUMMARY"
303 echo "| Metric | Count |" >> "$GITHUB_STEP_SUMMARY"
304 echo "|--------|-------|" >> "$GITHUB_STEP_SUMMARY"
305 echo "| Applied | ${{ steps.fix.outputs.resolved || '0' }} |" >> "$GITHUB_STEP_SUMMARY"
306 echo "| Blocked | ${{ steps.fix.outputs.blocked || '0' }} |" >> "$GITHUB_STEP_SUMMARY"
307 echo "| Rolled back | ${{ steps.fix.outputs.failed || '0' }} |" >> "$GITHUB_STEP_SUMMARY"
308 echo "" >> "$GITHUB_STEP_SUMMARY"
309 if [ -n "${{ steps.fix.outputs.applied_packages }}" ]; then
310 echo "**Applied:**${{ steps.fix.outputs.applied_packages }}" >> "$GITHUB_STEP_SUMMARY"
311 fi
312 if [ -n "${{ steps.fix.outputs.rolled_back_packages }}" ]; then
313 echo "**Rolled back:**${{ steps.fix.outputs.rolled_back_packages }}" >> "$GITHUB_STEP_SUMMARY"
314 fi
315 echo "" >> "$GITHUB_STEP_SUMMARY"
316 if [ "${{ steps.fix.outputs.changes }}" == "true" ]; then
317 echo "✅ Changes applied and PR created" >> "$GITHUB_STEP_SUMMARY"
318 elif [ "${{ inputs.dry-run }}" == "true" ]; then
319 echo "ℹ️ Dry run — no changes applied" >> "$GITHUB_STEP_SUMMARY"
320 else
321 echo "ℹ️ No fixable alerts found" >> "$GITHUB_STEP_SUMMARY"
322 fi
323