Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
197 changes: 188 additions & 9 deletions .github/workflows/request-nvskills-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,22 +10,201 @@ name: Request NVSkills CI
on:
issue_comment:
types: [created]
pull_request:
types: [opened, synchronize, reopened]
push:

permissions:
contents: read
pull-requests: read
statuses: read

jobs:
require-nvskills-ci:
if: github.event_name == 'pull_request'
permissions:
contents: read
pull-requests: read
statuses: read
uses: NVIDIA/skills/.github/workflows/require-nvskills-status.yml@main

request:
if: >
(github.event_name == 'issue_comment' &&
github.event.issue.pull_request &&
contains(fromJSON('["OWNER","MEMBER","COLLABORATOR"]'), github.event.comment.author_association) &&
startsWith(github.event.comment.body, '/nvskills-ci')) ||
(github.event_name == 'push' &&
github.actor == (vars.NVSKILLS_SIGNATURE_PUSH_ACTOR || 'nv-nvskill-ci[bot]') &&
github.actor == (vars.NVSKILLS_SIGNATURE_PUSH_ACTOR || 'nv-skills-ci[bot]') &&
startsWith(github.event.head_commit.message, vars.NVSKILLS_SIGNATURE_COMMIT_TITLE || 'Attach NVSkills validation signatures'))
permissions:
contents: read
pull-requests: read
statuses: read
uses: NVIDIA/skills/.github/workflows/team-request.yml@main
secrets:
NVSKILLS_CI_DISPATCH_TOKEN: ${{ secrets.NVSKILLS_CI_DISPATCH_TOKEN }}
runs-on: ubuntu-latest
concurrency:
group: nvskills-ci-request-${{ github.repository }}-${{ github.event.issue.number || github.sha }}
cancel-in-progress: true
steps:
- name: Validate requester permission
if: ${{ github.event_name == 'issue_comment' }}
env:
GH_TOKEN: ${{ github.token }}
REPO: ${{ github.repository }}
ACTOR: ${{ github.actor }}
run: |
set -euo pipefail
role_name="$(curl -fsSL \
-H "Authorization: Bearer ${GH_TOKEN}" \
-H "Accept: application/vnd.github+json" \
"https://api.github.com/repos/${REPO}/collaborators/${ACTOR}/permission" \
| jq -r '.role_name // ""')"
Comment on lines +52 to +56

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You might find it easier to use gh api with with the GitHub CLI than this curl. That'd keep GH_TOKEN out of the code entirely (gh would just silently read the variable and use it).

case "${role_name}" in
admin|maintain) ;;
*) echo "Requester must have maintain or admin permission"; exit 1 ;;
esac

- name: Resolve request context
id: context
env:
GH_TOKEN: ${{ github.token }}
EVENT_NAME: ${{ github.event_name }}
REPO: ${{ github.repository }}
ISSUE_PR_NUMBER: ${{ github.event.issue.number || '' }}
HEAD_SHA: ${{ github.sha }}
HEAD_COMMIT_MESSAGE: ${{ github.event.head_commit.message || '' }}
SIGNATURE_COMMIT_TITLE: ${{ vars.NVSKILLS_SIGNATURE_COMMIT_TITLE || 'Attach NVSkills validation signatures' }}
SIGNATURE_PUSH_ACTOR: ${{ vars.NVSKILLS_SIGNATURE_PUSH_ACTOR || 'nv-skills-ci[bot]' }}
ACTOR: ${{ github.actor }}
run: |
set -euo pipefail
owner="${REPO%%/*}"
repo="${REPO#*/}"
pr_number="${ISSUE_PR_NUMBER}"
commit_title="$(printf '%s' "${HEAD_COMMIT_MESSAGE}" | sed -n '1p')"

if [ "${EVENT_NAME}" = "push" ]; then
if [ "${commit_title}" != "${SIGNATURE_COMMIT_TITLE}" ]; then
echo "Push is not the configured NVSkills signature commit; skipping dispatch."
exit 0
fi
if [ "${ACTOR}" != "${SIGNATURE_PUSH_ACTOR}" ]; then
echo "Push actor ${ACTOR} is not the configured NVSkills signing actor; skipping dispatch."
exit 0
fi
Comment thread
coderabbitai[bot] marked this conversation as resolved.

prs_json="$(curl -fsSL \
-H "Authorization: Bearer ${GH_TOKEN}" \
-H "Accept: application/vnd.github+json" \
"https://api.github.com/repos/${owner}/${repo}/commits/${HEAD_SHA}/pulls")"
pr_number="$(printf '%s' "${prs_json}" | jq -r '[.[] | select(.state == "open")][0].number // empty')"
if [ -z "${pr_number}" ]; then
echo "No open pull request is associated with the signature commit; skipping dispatch."
exit 0
fi
fi

if [ -z "${pr_number}" ]; then
echo "Pull request number could not be resolved."
exit 1
fi

pr_json="$(curl -fsSL \
-H "Authorization: Bearer ${GH_TOKEN}" \
-H "Accept: application/vnd.github+json" \
"https://api.github.com/repos/${owner}/${repo}/pulls/${pr_number}")"
head_sha="$(printf '%s' "${pr_json}" | jq -r '.head.sha')"
base_ref="$(printf '%s' "${pr_json}" | jq -r '.base.ref')"

if [ "${EVENT_NAME}" != "push" ]; then
commit_json="$(curl -fsSL \
-H "Authorization: Bearer ${GH_TOKEN}" \
-H "Accept: application/vnd.github+json" \
"https://api.github.com/repos/${owner}/${repo}/commits/${head_sha}")"
commit_title="$(printf '%s' "${commit_json}" | jq -r '.commit.message | split("\n")[0]')"
fi

has_watched_change=false
page=1
while true; do
files_json="$(curl -fsSL \
-H "Authorization: Bearer ${GH_TOKEN}" \
-H "Accept: application/vnd.github+json" \
"https://api.github.com/repos/${owner}/${repo}/pulls/${pr_number}/files?per_page=100&page=${page}")"
if printf '%s' "${files_json}" | jq -e '
any(.[]; (.filename, .previous_filename // "") |
startswith("skills/") or
startswith("team-skills/") or
startswith("rules/team-rules/") or
startswith("plugins/")
)
' >/dev/null; then
Comment thread
coderabbitai[bot] marked this conversation as resolved.
has_watched_change=true
break
fi
if [ "$(printf '%s' "${files_json}" | jq 'length')" -lt 100 ]; then
break
fi
page=$((page + 1))
done

if [ "${has_watched_change}" != "true" ]; then
{
echo "## NVSkills CI request"
echo
echo "Skipped: no changes under \`skills/\`, \`team-skills/\`, \`rules/team-rules/\`, or \`plugins/\`."
} >> "${GITHUB_STEP_SUMMARY}"
exit 0
fi

{
echo "should_dispatch=true"
echo "pr_number=${pr_number}"
echo "head_sha=${head_sha}"
echo "base_ref=${base_ref}"
echo "commit_title=${commit_title}"
} >> "${GITHUB_OUTPUT}"

- name: Dispatch NVSkills CI
if: steps.context.outputs.should_dispatch == 'true'
env:
DISPATCH_TOKEN: ${{ secrets.NVSKILLS_CI_DISPATCH_TOKEN }}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check whether the new secret and vars are documented anywhere in the repo.
set -euo pipefail
for name in NVSKILLS_CI_DISPATCH_TOKEN NVSKILLS_SIGNATURE_PUSH_ACTOR NVSKILLS_SIGNATURE_COMMIT_TITLE; do
  echo "=== ${name} ==="
  rg -n --hidden -g '!.git' "${name}" || echo "  (no references)"
done
echo "=== docs/README mentions of nvskills ==="
rg -ni --hidden -g '!.git' -g '*.md' 'nvskills' || echo "  (no markdown references)"

Repository: NVIDIA/cuopt

Length of output: 317


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== workflow context ==="
sed -n '130,185p' .github/workflows/request-nvskills-ci.yml

echo "=== workflow references and repository documentation files ==="
rg -n --hidden -g '!.git' \
  'NVSKILLS|repository_dispatch|workflow_dispatch|DISPATCH_TOKEN|SIGNATURE_PUSH|SIGNATURE_COMMIT' \
  .github README* docs 2>/dev/null || true

echo "=== tracked documentation/configuration files ==="
git ls-files '*.md' '*.yaml' '*.yml' '*.json' | sed -n '1,160p'

echo "=== available repository changes ==="
git diff --stat
git diff -- .github/workflows/request-nvskills-ci.yml

Repository: NVIDIA/cuopt

Length of output: 10741


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== complete dispatch step ==="
sed -n '1,115p' .github/workflows/request-nvskills-ci.yml
sed -n '160,225p' .github/workflows/request-nvskills-ci.yml

echo "=== all documentation/configuration references to related setup terms ==="
rg -ni --hidden -g '!.git' -g '*.md' -g '*.yaml' -g '*.yml' \
  'NVSkills|nvskills-ci|repository dispatch|repository_dispatch|workflow dispatch|workflow_dispatch|fine-grained|contents: write|actions: write|personal access token|PAT|secret|vars\.' \
  .github README.md CONTRIBUTING.md CONVENTIONS.md docs 2>/dev/null || true

Repository: NVIDIA/cuopt

Length of output: 34831


🌐 Web query:

GitHub REST API create a repository dispatch event fine-grained token required permissions Actions workflows NVIDIA nvskills-ci

💡 Result:

To create a repository dispatch event using the GitHub REST API, you must send a POST request to the /repos/{owner}/{repo}/dispatches endpoint [1][2]. When using a fine-grained personal access token (PAT), the following permissions are required on the target repository: - Contents: Read and write [3][4][5] - Metadata: Read-only (this is automatically selected when you select Contents: Read and write) [3][4] If your workflow also needs to discover the Run ID of the dispatched workflow, you must additionally include Actions: read permission [5]. Important Considerations: - Repository dispatch events will only trigger a workflow run if the workflow file is committed to the default branch of the repository [4][6]. - While a classic PAT requires the full repo scope [1][7][3], fine-grained PATs allow for more limited, repository-specific access [3][8]. - If you are dispatching a workflow within the same repository that is executing the action, you can use the default GITHUB_TOKEN [3][4]. For cross-repository dispatching, you must use a PAT or a GitHub App installation token [3][9][5].

Citations:


🌐 Web query:

site:docs.github.com/en/rest/actions/workflows "Create a workflow dispatch event" fine-grained access tokens Actions write

💡 Result:

To trigger a workflow dispatch event using the GitHub REST API, you must use a token that has the repository scope [1]. Official documentation indicates that personal access tokens (classic) require the repo scope to interact with the Create a workflow dispatch event endpoint [1]. While fine-grained personal access tokens allow for more granular control, the current documentation for the workflow dispatch endpoint specifically emphasizes the necessity of the repo scope for classic tokens [1]. Fine-grained tokens are generally managed through specific repository permissions; if a fine-grained token does not support the workflow dispatch event, it is because the required Actions-related permissions for that specific endpoint are not mapped to that token type. In such cases, a classic personal access token with the repo scope remains the standard requirement for programmatically triggering workflows [1]. To trigger the event via the API, you would typically use a request similar to the following: POST /repos/{owner}/{repo}/actions/workflows/{workflow_id}/dispatches The request body must include the ref (the git reference, such as a branch or tag) and any required inputs defined in your workflow [1].

Citations:


Document the NVSkills configuration

Document secrets.NVSKILLS_CI_DISPATCH_TOKEN and its required permissions for dispatching nvskills-ci.yml in NVIDIA/nvskills-ci. Document vars.NVSKILLS_SIGNATURE_PUSH_ACTOR and vars.NVSKILLS_SIGNATURE_COMMIT_TITLE, including their default values.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/request-nvskills-ci.yml at line 166, Document the NVSkills
configuration associated with the workflow: add documentation for
secrets.NVSKILLS_CI_DISPATCH_TOKEN, including the permissions required to
dispatch nvskills-ci.yml in NVIDIA/nvskills-ci, and document
vars.NVSKILLS_SIGNATURE_PUSH_ACTOR and vars.NVSKILLS_SIGNATURE_COMMIT_TITLE with
their default values. Place the documentation alongside the repository’s
existing configuration documentation and do not change the workflow behavior.

Source: Path instructions

REPO: ${{ github.repository }}
PR_NUMBER: ${{ steps.context.outputs.pr_number }}
REQUEST_HEAD_SHA: ${{ steps.context.outputs.head_sha }}
REQUEST_BASE_REF: ${{ steps.context.outputs.base_ref }}
REQUEST_COMMIT_TITLE: ${{ steps.context.outputs.commit_title }}
REQUEST_COMMENT_ID: ${{ github.event.comment.id || '' }}
REQUEST_RUN_ID: ${{ github.run_id }}
REQUESTED_BY: ${{ github.actor }}
run: |
set -euo pipefail
if [ -z "${DISPATCH_TOKEN}" ]; then
echo "Missing NVSKILLS_CI_DISPATCH_TOKEN secret."
exit 1
fi

owner="${REPO%%/*}"
repo="${REPO#*/}"

curl -fsSL -X POST \
-H "Authorization: Bearer ${DISPATCH_TOKEN}" \
-H "Accept: application/vnd.github+json" \
"https://api.github.com/repos/NVIDIA/nvskills-ci/actions/workflows/nvskills-ci.yml/dispatches" \

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The GitHub API has a gh workflow run which would be simpler than curl-ing against api.github.com, I think you should consider that.

-d "$(jq -n \
--arg ref "main" \
--arg source_owner "${owner}" \
--arg source_repo "${repo}" \
--arg pr_number "${PR_NUMBER}" \
--arg request_run_id "${REQUEST_RUN_ID}" \
--arg request_head_sha "${REQUEST_HEAD_SHA}" \
--arg request_base_ref "${REQUEST_BASE_REF}" \
--arg request_commit_title "${REQUEST_COMMIT_TITLE}" \
--arg request_comment_id "${REQUEST_COMMENT_ID}" \
--arg requested_by "${REQUESTED_BY}" \
'{ref: $ref, inputs: {
source_owner: $source_owner,
source_repo: $source_repo,
pr_number: $pr_number,
request_run_id: $request_run_id,
request_head_sha: $request_head_sha,
request_base_ref: $request_base_ref,
request_commit_title: $request_commit_title,
request_comment_id: $request_comment_id,
requested_by: $requested_by
}}')"