Skip to content

Commit a5e99b2

Browse files
committed
ci: add NVSkills CI request workflow (signing pipeline)
Adds .github/workflows/request-nvskills-ci.yml — the source-repo handler for the NVSkills CI / NVCARPS signing pipeline. Triggered by maintainer or admin commenting /nvskills-ci on a PR that changes skills/, and by the signature-push back from nv-nvskill-ci[bot]. Companion to NVBug 6209970 (App install + onboarding + dispatch token) and PR #603 (canonical skills/ layout migration), ahead of the 2026-05-27 Computex cutover for the NVIDIA Verified Skills catalog. Signed-off-by: Pranjal Doshi <pranjald@nvidia.com>
1 parent 102d649 commit a5e99b2

1 file changed

Lines changed: 207 additions & 0 deletions

File tree

Lines changed: 207 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,207 @@
1+
# SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
# Source repo request workflow for NVSkills CI (signing pipeline).
5+
# Triggered by a maintainer/admin commenting `/nvskills-ci` on a PR
6+
# that changes skills/, OR by the signature-push back from
7+
# nv-nvskill-ci[bot].
8+
#
9+
# Inlined version (provided by NVCARPS team) — no `uses:` reusable
10+
# workflow call to avoid the public→internal access restriction.
11+
# Instead, dispatches NVIDIA/nvskills-ci:nvskills-ci.yml via the GitHub
12+
# REST API with the NVSKILLS_CI_DISPATCH_TOKEN secret.
13+
#
14+
# Onboarding tracked in NVBug 6209970.
15+
16+
name: Request NVSkills CI
17+
18+
on:
19+
issue_comment:
20+
types: [created]
21+
push:
22+
23+
permissions:
24+
contents: read
25+
pull-requests: read
26+
27+
jobs:
28+
request:
29+
if: >
30+
(github.event_name == 'issue_comment' &&
31+
github.event.issue.pull_request &&
32+
startsWith(github.event.comment.body, '/nvskills-ci')) ||
33+
(github.event_name == 'push' &&
34+
github.actor == (vars.NVSKILLS_SIGNATURE_PUSH_ACTOR || 'nv-nvskill-ci[bot]') &&
35+
startsWith(github.event.head_commit.message, vars.NVSKILLS_SIGNATURE_COMMIT_TITLE || 'Attach NVSkills validation signatures'))
36+
runs-on: ubuntu-latest
37+
concurrency:
38+
group: nvskills-ci-request-${{ github.repository }}-${{ github.event.issue.number || github.sha }}
39+
cancel-in-progress: true
40+
steps:
41+
- name: Validate requester permission
42+
if: ${{ github.event_name == 'issue_comment' }}
43+
env:
44+
GH_TOKEN: ${{ github.token }}
45+
REPO: ${{ github.repository }}
46+
ACTOR: ${{ github.actor }}
47+
run: |
48+
set -euo pipefail
49+
permission="$(curl -fsSL \
50+
-H "Authorization: Bearer ${GH_TOKEN}" \
51+
-H "Accept: application/vnd.github+json" \
52+
"https://api.github.com/repos/${REPO}/collaborators/${ACTOR}/permission" \
53+
| jq -r '.permission')"
54+
case "${permission}" in
55+
admin|maintain) ;;
56+
*) echo "Requester must have maintain or admin permission"; exit 1 ;;
57+
esac
58+
59+
- name: Resolve request context
60+
id: context
61+
env:
62+
GH_TOKEN: ${{ github.token }}
63+
EVENT_NAME: ${{ github.event_name }}
64+
REPO: ${{ github.repository }}
65+
ISSUE_PR_NUMBER: ${{ github.event.issue.number || '' }}
66+
HEAD_SHA: ${{ github.sha }}
67+
HEAD_COMMIT_MESSAGE: ${{ github.event.head_commit.message || '' }}
68+
SIGNATURE_COMMIT_TITLE: ${{ vars.NVSKILLS_SIGNATURE_COMMIT_TITLE || 'Attach NVSkills validation signatures' }}
69+
SIGNATURE_PUSH_ACTOR: ${{ vars.NVSKILLS_SIGNATURE_PUSH_ACTOR || 'nv-nvskill-ci[bot]' }}
70+
ACTOR: ${{ github.actor }}
71+
run: |
72+
set -euo pipefail
73+
owner="${REPO%%/*}"
74+
repo="${REPO#*/}"
75+
pr_number="${ISSUE_PR_NUMBER}"
76+
commit_title="$(printf '%s' "${HEAD_COMMIT_MESSAGE}" | sed -n '1p')"
77+
78+
if [ "${EVENT_NAME}" = "push" ]; then
79+
if [ "${commit_title}" != "${SIGNATURE_COMMIT_TITLE}" ]; then
80+
echo "Push is not the configured NVSkills signature commit; skipping dispatch."
81+
exit 0
82+
fi
83+
if [ "${ACTOR}" != "${SIGNATURE_PUSH_ACTOR}" ]; then
84+
echo "Push actor ${ACTOR} is not the configured NVSkills signing actor; skipping dispatch."
85+
exit 0
86+
fi
87+
88+
prs_json="$(curl -fsSL \
89+
-H "Authorization: Bearer ${GH_TOKEN}" \
90+
-H "Accept: application/vnd.github+json" \
91+
"https://api.github.com/repos/${owner}/${repo}/commits/${HEAD_SHA}/pulls")"
92+
pr_number="$(printf '%s' "${prs_json}" | jq -r '[.[] | select(.state == "open")][0].number // empty')"
93+
if [ -z "${pr_number}" ]; then
94+
echo "No open pull request is associated with the signature commit; skipping dispatch."
95+
exit 0
96+
fi
97+
fi
98+
99+
if [ -z "${pr_number}" ]; then
100+
echo "Pull request number could not be resolved."
101+
exit 1
102+
fi
103+
104+
pr_json="$(curl -fsSL \
105+
-H "Authorization: Bearer ${GH_TOKEN}" \
106+
-H "Accept: application/vnd.github+json" \
107+
"https://api.github.com/repos/${owner}/${repo}/pulls/${pr_number}")"
108+
head_sha="$(printf '%s' "${pr_json}" | jq -r '.head.sha')"
109+
base_ref="$(printf '%s' "${pr_json}" | jq -r '.base.ref')"
110+
111+
if [ "${EVENT_NAME}" != "push" ]; then
112+
commit_json="$(curl -fsSL \
113+
-H "Authorization: Bearer ${GH_TOKEN}" \
114+
-H "Accept: application/vnd.github+json" \
115+
"https://api.github.com/repos/${owner}/${repo}/commits/${head_sha}")"
116+
commit_title="$(printf '%s' "${commit_json}" | jq -r '.commit.message | split("\n")[0]')"
117+
fi
118+
119+
has_watched_change=false
120+
page=1
121+
while true; do
122+
files_json="$(curl -fsSL \
123+
-H "Authorization: Bearer ${GH_TOKEN}" \
124+
-H "Accept: application/vnd.github+json" \
125+
"https://api.github.com/repos/${owner}/${repo}/pulls/${pr_number}/files?per_page=100&page=${page}")"
126+
if printf '%s' "${files_json}" | jq -e '
127+
any(.[]; .filename |
128+
startswith("skills/") or
129+
startswith("team-skills/") or
130+
startswith("rules/team-rules/") or
131+
startswith("plugins/")
132+
)
133+
' >/dev/null; then
134+
has_watched_change=true
135+
break
136+
fi
137+
if [ "$(printf '%s' "${files_json}" | jq 'length')" -lt 100 ]; then
138+
break
139+
fi
140+
page=$((page + 1))
141+
done
142+
143+
if [ "${has_watched_change}" != "true" ]; then
144+
{
145+
echo "## NVSkills CI request"
146+
echo
147+
echo "Skipped: no changes under \`skills/\`, \`team-skills/\`, \`rules/team-rules/\`, or \`plugins/\`."
148+
} >> "${GITHUB_STEP_SUMMARY}"
149+
exit 0
150+
fi
151+
152+
{
153+
echo "should_dispatch=true"
154+
echo "pr_number=${pr_number}"
155+
echo "head_sha=${head_sha}"
156+
echo "base_ref=${base_ref}"
157+
echo "commit_title=${commit_title}"
158+
} >> "${GITHUB_OUTPUT}"
159+
160+
- name: Dispatch NVSkills CI
161+
if: steps.context.outputs.should_dispatch == 'true'
162+
env:
163+
DISPATCH_TOKEN: ${{ secrets.NVSKILLS_CI_DISPATCH_TOKEN }}
164+
REPO: ${{ github.repository }}
165+
PR_NUMBER: ${{ steps.context.outputs.pr_number }}
166+
REQUEST_HEAD_SHA: ${{ steps.context.outputs.head_sha }}
167+
REQUEST_BASE_REF: ${{ steps.context.outputs.base_ref }}
168+
REQUEST_COMMIT_TITLE: ${{ steps.context.outputs.commit_title }}
169+
REQUEST_COMMENT_ID: ${{ github.event.comment.id || '' }}
170+
REQUEST_RUN_ID: ${{ github.run_id }}
171+
REQUESTED_BY: ${{ github.actor }}
172+
run: |
173+
set -euo pipefail
174+
if [ -z "${DISPATCH_TOKEN}" ]; then
175+
echo "Missing NVSKILLS_CI_DISPATCH_TOKEN secret."
176+
exit 1
177+
fi
178+
179+
owner="${REPO%%/*}"
180+
repo="${REPO#*/}"
181+
182+
curl -fsSL -X POST \
183+
-H "Authorization: Bearer ${DISPATCH_TOKEN}" \
184+
-H "Accept: application/vnd.github+json" \
185+
"https://api.github.com/repos/NVIDIA/nvskills-ci/actions/workflows/nvskills-ci.yml/dispatches" \
186+
-d "$(jq -n \
187+
--arg ref "main" \
188+
--arg source_owner "${owner}" \
189+
--arg source_repo "${repo}" \
190+
--arg pr_number "${PR_NUMBER}" \
191+
--arg request_run_id "${REQUEST_RUN_ID}" \
192+
--arg request_head_sha "${REQUEST_HEAD_SHA}" \
193+
--arg request_base_ref "${REQUEST_BASE_REF}" \
194+
--arg request_commit_title "${REQUEST_COMMIT_TITLE}" \
195+
--arg request_comment_id "${REQUEST_COMMENT_ID}" \
196+
--arg requested_by "${REQUESTED_BY}" \
197+
'{ref: $ref, inputs: {
198+
source_owner: $source_owner,
199+
source_repo: $source_repo,
200+
pr_number: $pr_number,
201+
request_run_id: $request_run_id,
202+
request_head_sha: $request_head_sha,
203+
request_base_ref: $request_base_ref,
204+
request_commit_title: $request_commit_title,
205+
request_comment_id: $request_comment_id,
206+
requested_by: $requested_by
207+
}}')"

0 commit comments

Comments
 (0)