-
Notifications
You must be signed in to change notification settings - Fork 2.9k
273 lines (255 loc) · 13.8 KB
/
Copy pathe2e-advisor.yaml
File metadata and controls
273 lines (255 loc) · 13.8 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0
name: E2E / Advisor
on:
pull_request:
types: [opened, synchronize, reopened, ready_for_review]
workflow_dispatch:
inputs:
base_ref:
description: Base ref to diff against
required: false
default: origin/main
head_ref:
description: Head ref to diff
required: false
default: HEAD
target_repo:
description: Optional repo to analyze, e.g. NVIDIA/NemoClaw
required: false
type: string
default: ""
target_pr:
description: Optional pull request number in target_repo to analyze
required: false
type: string
default: ""
target_base:
description: Base branch to use with target_repo/target_pr manual analysis
required: false
type: string
default: main
run_analysis:
description: Run E2E recommendation analysis
required: false
type: boolean
default: true
permissions:
contents: read
# `actions: write` lets the trusted advisor dispatcher trigger the selected
# E2E workflow for eligible NVIDIA-owned PRs after validating the requested
# jobs against the target workflow's own selective-dispatch predicates.
actions: write
# `pull-requests: write` is required for the advisor to post (or update) a
# comment on a PR via POST /repos/:o/:r/issues/:n/comments. Even though the
# endpoint lives under `/issues/`, for GITHUB_TOKEN the permission that
# actually gates PR comments is `pull-requests`, not `issues`. With only
# `pull-requests: read`, the endpoint returns 403 "Resource not accessible
# by integration" despite `issues: write`. See the comment step below and
# https://github.com/orgs/community/discussions/56632.
pull-requests: write
issues: write
concurrency:
group: e2e-advisor-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
jobs:
advise:
name: E2E recommendation
if: ${{ github.repository == 'NVIDIA/NemoClaw' && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'NVIDIA/NemoClaw') }}
runs-on: ubuntu-latest
timeout-minutes: 20
env:
# Pin the Pi SDK to a known-good version. Updates should go through
# the normal dependency-review path (e.g. Dependabot) so a compromised
# upstream release cannot execute automatically in this secret-bearing job.
PI_SDK_VERSION: "0.74.0"
# Keep the advisor timeout shorter than the GitHub job timeout so the
# analyzer can write failure artifacts, summaries, and PR comments.
E2E_ADVISOR_TIMEOUT_MS: "900000"
E2E_ADVISOR_HEARTBEAT_MS: "60000"
# Cap automatic fan-out from noisy/flaky advisor recommendations until
# the dispatcher has enough production history to justify a higher limit.
E2E_ADVISOR_AUTO_DISPATCH_MAX_JOBS: "6"
# The trusted checkout always lives at this path and is the only source
# of advisor implementation code executed in this job. The PR content,
# if any, is mounted as read-only analysis data under PR_WORKDIR.
ADVISOR_DIR: ${{ github.workspace }}/advisor
steps:
# Trusted-code boundary: the advisor implementation (analyze.mts,
# dispatch.mts, comment.mts, schema.json) is always fetched
# from the main branch of this repo, regardless of what the PR changed.
# This prevents a future PR from modifying advisor code and executing
# arbitrary Node in a job that holds API keys and a write-scoped token.
- name: Checkout trusted advisor code (main)
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
repository: NVIDIA/NemoClaw
ref: main
path: advisor
persist-credentials: false
# PR content is checked out as inert analysis data only. Nothing from
# this directory is invoked as code by the advisor scripts; it is read
# via `git diff` / `fs.readFileSync` from inside the trusted scripts.
- name: Checkout PR workspace (read-only data)
if: ${{ github.event_name == 'pull_request' }}
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
ref: ${{ github.event.pull_request.head.sha }}
path: pr-workdir
fetch-depth: 0
persist-credentials: false
# For manual workflow_dispatch against this repo (no target_repo given),
# check out the ref the maintainer dispatched against as the workdir.
# workflow_dispatch requires write access, so this ref is trusted for
# the purposes of analysis-data access, though we still never execute
# it as code.
- name: Checkout dispatch workspace (read-only data)
if: ${{ github.event_name == 'workflow_dispatch' && inputs.target_repo == '' }}
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
path: pr-workdir
fetch-depth: 0
persist-credentials: false
- name: Setup Node
uses: actions/setup-node@v6
with:
node-version: "22"
- name: Set default advisor workdir
run: echo "ADVISOR_WORKDIR=$GITHUB_WORKSPACE/pr-workdir" >> "$GITHUB_ENV"
- name: Prepare target PR checkout
if: ${{ github.event_name == 'workflow_dispatch' && inputs.target_repo != '' && inputs.target_pr != '' }}
env:
TARGET_REPO: ${{ inputs.target_repo }}
TARGET_PR: ${{ inputs.target_pr }}
TARGET_BASE: ${{ inputs.target_base }}
run: |
TARGET_DIR=/tmp/e2e-advisor-target
rm -rf "$TARGET_DIR"
mkdir -p "$TARGET_DIR"
git -C "$TARGET_DIR" init
git -C "$TARGET_DIR" remote add target "https://github.com/${TARGET_REPO}.git"
git -C "$TARGET_DIR" fetch --no-tags target "$TARGET_BASE"
git -C "$TARGET_DIR" fetch --no-tags target "pull/${TARGET_PR}/head:refs/remotes/target/pr-${TARGET_PR}"
git -C "$TARGET_DIR" checkout --detach "refs/remotes/target/pr-${TARGET_PR}"
echo "ADVISOR_WORKDIR=$TARGET_DIR" >> "$GITHUB_ENV"
# Pinned SDK install. The version is held in PI_SDK_VERSION above so
# the pin is reviewed as a code change, not silently inherited from
# whatever @latest points to at runtime. Install into an isolated temp
# prefix, then expose that dependency tree to the trusted advisor script.
- name: Install Pi SDK
run: |
PI_SDK_DIR="$RUNNER_TEMP/pi-sdk"
npm install --prefix "$PI_SDK_DIR" --ignore-scripts --no-save --package-lock=false --before=2026-05-14T00:00:00.000Z "@earendil-works/pi-coding-agent@${PI_SDK_VERSION}"
rm -rf "$ADVISOR_DIR/node_modules"
ln -s "$PI_SDK_DIR/node_modules" "$ADVISOR_DIR/node_modules"
- name: Run E2E recommendation advisor
id: analysis
continue-on-error: true
env:
BASE_REF: ${{ github.event_name == 'pull_request' && format('origin/{0}', github.base_ref) || (github.event_name == 'workflow_dispatch' && inputs.target_repo != '' && inputs.target_pr != '' && format('target/{0}', inputs.target_base) || inputs.base_ref) }}
HEAD_REF: ${{ github.event_name == 'pull_request' && 'HEAD' || (github.event_name == 'workflow_dispatch' && inputs.target_repo != '' && inputs.target_pr != '' && 'HEAD' || inputs.head_ref) }}
E2E_ADVISOR_RUN_ANALYSIS: ${{ github.event_name == 'workflow_dispatch' && inputs.run_analysis == false && '0' || '1' }}
# Preferred E2E advisor secret.
E2E_ADVISOR_API_KEY: ${{ secrets.PI_E2E_ADVISOR_API_KEY }}
# Optional local-provider-compatible fallback for future upstream/external-advisor use.
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
run: |
cd "$ADVISOR_WORKDIR"
node --experimental-strip-types "$ADVISOR_DIR/tools/e2e-advisor/analyze.mts" \
--base "$BASE_REF" \
--head "$HEAD_REF" \
--schema "$ADVISOR_DIR/tools/e2e-advisor/schema.json" \
--out-dir "$GITHUB_WORKSPACE/artifacts/e2e-advisor"
- name: Run deterministic scenario E2E advisor
id: scenario-analysis
continue-on-error: true
env:
BASE_REF: ${{ github.event_name == 'pull_request' && format('origin/{0}', github.base_ref) || (github.event_name == 'workflow_dispatch' && inputs.target_repo != '' && inputs.target_pr != '' && format('target/{0}', inputs.target_base) || inputs.base_ref) }}
HEAD_REF: ${{ github.event_name == 'pull_request' && 'HEAD' || (github.event_name == 'workflow_dispatch' && inputs.target_repo != '' && inputs.target_pr != '' && 'HEAD' || inputs.head_ref) }}
run: |
cd "$ADVISOR_WORKDIR"
node --experimental-strip-types "$ADVISOR_DIR/tools/e2e-advisor/scenarios.mts" \
--base "$BASE_REF" \
--head "$HEAD_REF" \
--out-dir "$GITHUB_WORKSPACE/artifacts/e2e-advisor"
- name: Publish job summary
if: always()
run: |
if [ -f "$GITHUB_WORKSPACE/artifacts/e2e-advisor/e2e-advisor-summary.md" ]; then
cat "$GITHUB_WORKSPACE/artifacts/e2e-advisor/e2e-advisor-summary.md" >> "$GITHUB_STEP_SUMMARY"
else
printf '# E2E Recommendation Advisor\n\nAdvisor analysis did not produce a summary. See raw artifacts/logs.\n' >> "$GITHUB_STEP_SUMMARY"
fi
if [ -f "$GITHUB_WORKSPACE/artifacts/e2e-advisor/e2e-scenario-advisor-summary.md" ]; then
cat "$GITHUB_WORKSPACE/artifacts/e2e-advisor/e2e-scenario-advisor-summary.md" >> "$GITHUB_STEP_SUMMARY"
fi
- name: Auto-dispatch required E2E jobs
if: ${{ always() && github.event_name == 'pull_request' }}
continue-on-error: true
env:
GH_TOKEN: ${{ github.token }}
# Optional comma-separated GitHub login allowlist for trusted authors whose
# private org membership appears as CONTRIBUTOR in pull_request payloads.
E2E_ADVISOR_AUTO_DISPATCH_ALLOWED_AUTHORS: ${{ secrets.E2E_ADVISOR_AUTO_DISPATCH_ALLOWED_AUTHORS }}
run: |
DISPATCH_RESULT="$GITHUB_WORKSPACE/artifacts/e2e-advisor/e2e-advisor-dispatch-result.json"
DISPATCH_SUMMARY="$GITHUB_WORKSPACE/artifacts/e2e-advisor/e2e-advisor-dispatch-summary.md"
if [ -f "$ADVISOR_DIR/tools/e2e-advisor/dispatch.mts" ]; then
node --experimental-strip-types "$ADVISOR_DIR/tools/e2e-advisor/dispatch.mts" \
--result "$GITHUB_WORKSPACE/artifacts/e2e-advisor/e2e-advisor-final-result.json" \
--workflow nightly-e2e.yaml \
--workflow-path "$ADVISOR_DIR/.github/workflows/nightly-e2e.yaml" \
--out-dir "$GITHUB_WORKSPACE/artifacts/e2e-advisor"
else
echo "Skipping E2E auto-dispatch: trusted main checkout does not yet contain dispatch.mts"
mkdir -p "$GITHUB_WORKSPACE/artifacts/e2e-advisor"
printf '{\n "status": "skipped",\n "reason": "trusted main checkout does not yet contain dispatch.mts",\n "workflow": "nightly-e2e.yaml"\n}\n' > "$DISPATCH_RESULT"
printf '# E2E Advisor Auto-dispatch\n\nStatus: **skipped**\nReason: trusted main checkout does not yet contain dispatch.mts\nWorkflow: `nightly-e2e.yaml`\n' > "$DISPATCH_SUMMARY"
fi
if [ -f "$DISPATCH_SUMMARY" ]; then
cat "$DISPATCH_SUMMARY" >> "$GITHUB_STEP_SUMMARY"
fi
- name: Post E2E advisor PR comment
if: ${{ always() && github.event_name == 'pull_request' }}
continue-on-error: true
env:
GH_TOKEN: ${{ secrets.E2E_ADVISOR_GITHUB_TOKEN || github.token }}
PR_NUMBER: ${{ github.event.pull_request.number }}
run: |
COMMENT_ARGS=(
--repo "$GITHUB_REPOSITORY"
--pr "$PR_NUMBER"
--summary "$GITHUB_WORKSPACE/artifacts/e2e-advisor/e2e-advisor-summary.md"
--result "$GITHUB_WORKSPACE/artifacts/e2e-advisor/e2e-advisor-final-result.json"
--dispatch "$GITHUB_WORKSPACE/artifacts/e2e-advisor/e2e-advisor-dispatch-result.json"
)
if [ -f "$ADVISOR_DIR/tools/e2e-advisor/comment.mts" ]; then
node --experimental-strip-types "$ADVISOR_DIR/tools/e2e-advisor/comment.mts" "${COMMENT_ARGS[@]}"
elif [ -f "$ADVISOR_DIR/tools/e2e-advisor/comment.mjs" ]; then
node "$ADVISOR_DIR/tools/e2e-advisor/comment.mjs" "${COMMENT_ARGS[@]}"
else
echo "Skipping E2E advisor comment: trusted main checkout does not contain comment.mts or comment.mjs"
fi
- name: Post E2E scenario advisor PR comment
if: ${{ always() && github.event_name == 'pull_request' }}
continue-on-error: true
env:
GH_TOKEN: ${{ secrets.E2E_ADVISOR_GITHUB_TOKEN || github.token }}
PR_NUMBER: ${{ github.event.pull_request.number }}
run: |
if [ -f "$GITHUB_WORKSPACE/artifacts/e2e-advisor/e2e-scenario-advisor-summary.md" ] && [ -f "$ADVISOR_DIR/tools/e2e-advisor/scenario-comment.mts" ]; then
node --experimental-strip-types "$ADVISOR_DIR/tools/e2e-advisor/scenario-comment.mts" \
--repo "$GITHUB_REPOSITORY" \
--pr "$PR_NUMBER" \
--summary "$GITHUB_WORKSPACE/artifacts/e2e-advisor/e2e-scenario-advisor-summary.md" \
--result "$GITHUB_WORKSPACE/artifacts/e2e-advisor/e2e-scenario-advisor-result.json"
else
echo "Skipping E2E scenario advisor comment: summary or scenario-comment.mts missing"
fi
- name: Upload advisor artifacts
if: always()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: e2e-advisor
path: artifacts/e2e-advisor/
if-no-files-found: warn