-
Notifications
You must be signed in to change notification settings - Fork 35
Generate and verify cmake target dependency diagram #630
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
9aa1d99
ffccb62
4c89631
f0486e8
7f954e0
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,122 @@ | ||
| # SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. | ||
| # SPDX-License-Identifier: Apache-2.0 | ||
| # | ||
| # Verify that cmake/cmake-target-layers.md matches the live CMake target graph. | ||
| # | ||
| # The graph is read straight from CMake: we configure with the SAME `ci-linux` preset that the | ||
| # primary Linux build uses (CMakePresets.json is the single source of the graph-affecting build | ||
| # options), ask CMake to emit its own dependency graph (`cmake --graphviz`), and regenerate the | ||
| # layered diagram. If the committed copy drifts from what CMake reports, the job fails — and the | ||
| # freshly-generated diagram is always uploaded as the `cmake-target-layers` artifact, so the fix | ||
| # is to download it and commit it in place of the stale file (no local full build required). | ||
| # | ||
| # Only graph-NEUTRAL build gates are relaxed here (ENABLE_CLOUDXR_BUNDLE_CHECK and | ||
| # ENABLE_CLANG_FORMAT_CHECK gate python/utility/custom targets that `cmake --graphviz` excludes), | ||
| # which keeps this job free of the CloudXR SDK / NGC secret so it also runs on fork PRs. Every | ||
| # build-affecting flag still comes from the shared preset, so the diagram cannot diverge from the | ||
| # real build's target graph. | ||
|
|
||
| name: Verify CMake target layers | ||
|
|
||
| on: | ||
| push: | ||
| branches: [ main, 'release/*.*.x' ] | ||
| paths: | ||
| - '**/CMakeLists.txt' | ||
| - '**/*.cmake' | ||
| - 'CMakePresets.json' | ||
| - 'scripts/cmake_target_layers.py' | ||
| - 'cmake/cmake-target-layers.md' | ||
| - '.github/workflows/cmake-target-layers.yaml' | ||
| pull_request: | ||
| paths: | ||
| - '**/CMakeLists.txt' | ||
| - '**/*.cmake' | ||
| - 'CMakePresets.json' | ||
| - 'scripts/cmake_target_layers.py' | ||
| - 'cmake/cmake-target-layers.md' | ||
| - '.github/workflows/cmake-target-layers.yaml' | ||
| workflow_dispatch: | ||
|
|
||
| concurrency: | ||
| group: ${{ github.workflow }}-${{ github.ref }} | ||
| cancel-in-progress: ${{ github.event_name == 'pull_request' }} | ||
|
|
||
| permissions: | ||
| contents: read | ||
|
|
||
| jobs: | ||
| verify-target-layers: | ||
| runs-on: ubuntu-22.04 | ||
| steps: | ||
| - name: Checkout code | ||
| uses: actions/checkout@v6 | ||
| with: | ||
| fetch-depth: 0 # Full history for accurate git describe during configure | ||
|
|
||
| - name: Install uv | ||
| uses: ./.github/actions/setup-uv | ||
|
|
||
| # viz_core links libcudart, so find_package(CUDAToolkit) must resolve at configure time | ||
| # for BUILD_VIZ=ON (set by the ci-linux preset). | ||
| - name: Install CUDA toolkit | ||
| uses: ./.github/actions/setup-cuda | ||
|
|
||
| - name: Install Apt dependencies | ||
| run: | | ||
| sudo apt-get update | ||
| sudo apt-get install -y build-essential cmake libx11-dev libvulkan-dev glslang-tools \ | ||
| libxrandr-dev libxinerama-dev libxcursor-dev libxi-dev libxext-dev libxkbcommon-dev \ | ||
| libwayland-dev wayland-protocols | ||
|
|
||
| # BUILD_PLUGIN_OAK_CAMERA=ON (ci-linux preset) pulls DepthAI via Hunter at configure time. | ||
| - name: Cache Hunter packages | ||
| uses: actions/cache@v5 | ||
| with: | ||
| path: ~/.hunter | ||
| key: hunter-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('cmake/SetupHunter.cmake') }} | ||
| restore-keys: | | ||
| hunter-${{ runner.os }}-${{ runner.arch }}- | ||
|
|
||
| # Configure only (no build) with the shared preset; emit CMake's own dependency graph. | ||
| # The two -D overlays are graph-neutral (see header) and drop the CloudXR/clang-format needs. | ||
| - name: Configure CMake and emit dependency graph | ||
| run: | | ||
| cmake --preset ci-linux \ | ||
| -DENABLE_CLOUDXR_BUNDLE_CHECK=OFF \ | ||
| -DENABLE_CLANG_FORMAT_CHECK=OFF \ | ||
| --graphviz=build/cmake-target-graph.dot | ||
|
|
||
| - name: Regenerate diagram (artifact) | ||
| run: | | ||
| python3 scripts/cmake_target_layers.py \ | ||
| --dot build/cmake-target-graph.dot \ | ||
| --out artifact/cmake-target-layers.md | ||
|
|
||
| # Upload regardless of the check result so a stale committed copy can be replaced by this file. | ||
| - name: Upload diagram artifact | ||
| if: always() | ||
| uses: actions/upload-artifact@v6 | ||
| with: | ||
| name: cmake-target-layers | ||
| path: artifact/cmake-target-layers.md | ||
| if-no-files-found: error | ||
|
|
||
| - name: Verify committed diagram is up to date | ||
| id: verify-diagram | ||
| run: | | ||
| python3 scripts/cmake_target_layers.py \ | ||
| --dot build/cmake-target-graph.dot \ | ||
| --check | ||
|
|
||
| - name: Hint on stale diagram | ||
| if: failure() && steps.verify-diagram.conclusion == 'failure' | ||
| env: | ||
| PR_URL: ${{ github.event.pull_request.html_url }} | ||
| run: | | ||
| if [ -n "$PR_URL" ]; then | ||
| HINT="Comment \`/update-cmake-target-layers\` on the PR to auto-commit the refreshed diagram." | ||
| else | ||
| HINT="Download the \`cmake-target-layers\` artifact from this run and commit it in place of \`cmake/cmake-target-layers.md\`." | ||
| fi | ||
| echo "::error title=cmake-target-layers.md is stale::$HINT" | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,186 @@ | ||
| # SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. | ||
| # SPDX-License-Identifier: Apache-2.0 | ||
| # | ||
| # Slash-command handler: /update-cmake-target-layers | ||
| # | ||
| # When a maintainer with write access comments `/update-cmake-target-layers` on a PR, | ||
| # this workflow downloads the cmake-target-layers artifact that the "Verify CMake target | ||
| # layers" CI job already generated on its most recent successful run and commits it to | ||
| # the PR branch via the GitHub Contents API. No checkout of PR code, no cmake re-run. | ||
| # | ||
| # Only same-repo PRs are supported; fork PRs cannot be pushed to from this context. | ||
| # | ||
| # The workflow file is always loaded from the default branch, so a PR cannot override | ||
| # it to gain elevated write-access. | ||
|
|
||
| name: Update CMake target layers | ||
|
|
||
| on: | ||
| issue_comment: | ||
| types: [created] | ||
|
|
||
| jobs: | ||
| update: | ||
| name: Commit refreshed cmake-target-layers.md | ||
| if: >- | ||
| github.event_name == 'issue_comment' | ||
| && github.event.issue.pull_request != null | ||
| && (github.event.comment.body == '/update-cmake-target-layers' | ||
| || startsWith(github.event.comment.body, '/update-cmake-target-layers ')) | ||
| runs-on: ubuntu-latest | ||
| concurrency: | ||
| group: update-cmake-target-layers-pr-${{ github.event.issue.number }} | ||
| cancel-in-progress: true | ||
| permissions: | ||
| actions: read # download artifact from the verify run | ||
| contents: write # push to the PR branch | ||
| pull-requests: write # comment on the PR | ||
| issues: write # react to the triggering comment | ||
| env: | ||
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||
| REPO: ${{ github.repository }} | ||
| COMMENT_ID: ${{ github.event.comment.id }} | ||
| PR_NUMBER: ${{ github.event.issue.number }} | ||
|
|
||
| steps: | ||
| - name: Verify commenter has write access | ||
| env: | ||
| USER: ${{ github.event.comment.user.login }} | ||
| run: | | ||
| PERM=$(gh api "repos/$REPO/collaborators/$USER/permission" --jq '.permission') | ||
| case "$PERM" in | ||
| admin|maintain|write) | ||
| echo "$USER has $PERM access — proceeding." | ||
| ;; | ||
| *) | ||
| gh api -X POST "repos/$REPO/issues/comments/$COMMENT_ID/reactions" \ | ||
| -f content='-1' >/dev/null | ||
| echo "::error::User $USER has '$PERM' access; /update-cmake-target-layers requires write or higher." >&2 | ||
| exit 1 | ||
| ;; | ||
| esac | ||
|
|
||
| - name: Acknowledge command | ||
| run: | | ||
| gh api -X POST "repos/$REPO/issues/comments/$COMMENT_ID/reactions" \ | ||
| -f content='eyes' >/dev/null | ||
|
|
||
| - name: Get PR info | ||
| id: pr | ||
| run: | | ||
| read -r HEAD_SHA HEAD_REF HEAD_REPO < <( | ||
| gh api "repos/$REPO/pulls/$PR_NUMBER" \ | ||
| --jq '[.head.sha, .head.ref, .head.repo.full_name] | @tsv' | ||
| ) | ||
|
|
||
| echo "head_sha=$HEAD_SHA" >> "$GITHUB_OUTPUT" | ||
| echo "head_ref=$HEAD_REF" >> "$GITHUB_OUTPUT" | ||
|
Comment on lines
+68
to
+77
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. PR head TOCTOU can commit a stale diagram onto a newer branch tip. The workflow resolves a verify artifact for one Suggested fix - name: Commit refreshed diagram via API
id: commit
env:
COMMENTER: ${{ github.event.comment.user.login }}
COMMENTER_ID: ${{ github.event.comment.user.id }}
HEAD_SHA: ${{ steps.pr.outputs.head_sha }}
HEAD_REF: ${{ steps.pr.outputs.head_ref }}
RUN_ID: ${{ steps.find-run.outputs.run_id }}
run: |
+ CURRENT_HEAD_SHA=$(gh api "repos/$REPO/pulls/$PR_NUMBER" --jq '.head.sha')
+ if [ "$CURRENT_HEAD_SHA" != "$HEAD_SHA" ]; then
+ echo "::error::PR head moved from ${HEAD_SHA:0:8} to ${CURRENT_HEAD_SHA:0:8}; rerun /update-cmake-target-layers."
+ exit 1
+ fi
+
# Get blob SHA of the currently-committed file (required to update it via API).
FILE_META=$(gh api "repos/$REPO/contents/cmake/cmake-target-layers.md?ref=$HEAD_REF" \
2>/dev/null || echo '{}')Also applies to: 91-113, 126-164 🤖 Prompt for AI Agents |
||
|
|
||
| if [ "$HEAD_REPO" != "$REPO" ]; then | ||
| gh api -X POST "repos/$REPO/issues/comments/$COMMENT_ID/reactions" \ | ||
| -f content='-1' >/dev/null | ||
| gh pr comment "$PR_NUMBER" --repo "$REPO" --body \ | ||
| "❌ \`/update-cmake-target-layers\` only works for same-repository PRs. Fork PRs cannot be pushed to from this context. Download the \`cmake-target-layers\` artifact from the failing CI run and commit it manually." | ||
| echo "::error::Fork PR — cannot push to $HEAD_REPO from $REPO context." >&2 | ||
| exit 1 | ||
| fi | ||
|
|
||
| # Reuse the artifact the verify job already uploaded on its successful run. | ||
| # Only success runs are eligible: a failure run may have crashed before the | ||
| # Regenerate step and therefore has no artifact to download. | ||
| - name: Find verify run for PR head SHA | ||
| id: find-run | ||
| env: | ||
| HEAD_SHA: ${{ steps.pr.outputs.head_sha }} | ||
| run: | | ||
| RUN_ID=$(gh run list --repo "$REPO" \ | ||
| --workflow=cmake-target-layers.yaml \ | ||
| --commit "$HEAD_SHA" \ | ||
| --limit 10 \ | ||
| --json databaseId,conclusion \ | ||
| --jq '[.[] | select(.conclusion == "success")] | .[0].databaseId // ""') | ||
|
|
||
| if [ -z "$RUN_ID" ]; then | ||
| gh api -X POST "repos/$REPO/issues/comments/$COMMENT_ID/reactions" \ | ||
| -f content='-1' >/dev/null | ||
| gh pr comment "$PR_NUMBER" --repo "$REPO" --body \ | ||
| "❌ No successful **Verify CMake target layers** run found for commit \`${HEAD_SHA:0:8}\`. Wait for the CI job to pass, then re-comment \`/update-cmake-target-layers\`." | ||
| echo "::error::No successful cmake-target-layers.yaml run for $HEAD_SHA" >&2 | ||
| exit 1 | ||
| fi | ||
|
|
||
| echo "run_id=$RUN_ID" >> "$GITHUB_OUTPUT" | ||
| echo "Reusing artifact from cmake-target-layers.yaml run #$RUN_ID for SHA \`${HEAD_SHA:0:8}\`" \ | ||
| >> "$GITHUB_STEP_SUMMARY" | ||
|
|
||
| - name: Download cmake-target-layers artifact | ||
| uses: actions/download-artifact@v7 | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Pin This workflow uses a mutable major tag ( 🧰 Tools🪛 zizmor (1.25.2)[error] 117-117: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy) (unpinned-uses) 🤖 Prompt for AI Agents |
||
| with: | ||
| name: cmake-target-layers | ||
| path: ./artifact | ||
| run-id: ${{ steps.find-run.outputs.run_id }} | ||
| github-token: ${{ secrets.GITHUB_TOKEN }} | ||
|
|
||
| # Commit via the Contents API — no checkout of PR code required. | ||
| # This avoids the "untrusted checkout in a privileged context" security pattern. | ||
| - name: Commit refreshed diagram via API | ||
| id: commit | ||
| env: | ||
| COMMENTER: ${{ github.event.comment.user.login }} | ||
| COMMENTER_ID: ${{ github.event.comment.user.id }} | ||
| HEAD_SHA: ${{ steps.pr.outputs.head_sha }} | ||
| HEAD_REF: ${{ steps.pr.outputs.head_ref }} | ||
| RUN_ID: ${{ steps.find-run.outputs.run_id }} | ||
| run: | | ||
| # Get blob SHA of the currently-committed file (required to update it via API). | ||
| FILE_META=$(gh api "repos/$REPO/contents/cmake/cmake-target-layers.md?ref=$HEAD_REF" \ | ||
| 2>/dev/null || echo '{}') | ||
| CURRENT_SHA=$(echo "$FILE_META" | jq -r '.sha // ""') | ||
|
|
||
| # Compare base64 strings directly — API returns base64 with line breaks, strip them. | ||
| ARTIFACT_B64=$(base64 -w 0 ./artifact/cmake-target-layers.md) | ||
| CURRENT_B64=$(echo "$FILE_META" | jq -r '.content // ""' | tr -d '\n') | ||
| if [ "$ARTIFACT_B64" = "$CURRENT_B64" ]; then | ||
| echo "already_current=true" >> "$GITHUB_OUTPUT" | ||
| exit 0 | ||
| fi | ||
|
|
||
| COMMIT_MSG=$(printf \ | ||
| 'docs(cmake): regenerate cmake-target-layers.md\n\nAuto-committed by /update-cmake-target-layers (PR #%s).\nSource: cmake-target-layers.yaml run #%s, head %s.' \ | ||
| "$PR_NUMBER" "$RUN_ID" "$HEAD_SHA") | ||
|
|
||
| jq -n \ | ||
| --arg message "$COMMIT_MSG" \ | ||
| --arg content "$ARTIFACT_B64" \ | ||
| --arg sha "$CURRENT_SHA" \ | ||
| --arg branch "$HEAD_REF" \ | ||
| --arg name "$COMMENTER" \ | ||
| --arg email "${COMMENTER_ID}+${COMMENTER}@users.noreply.github.com" \ | ||
| '{message: $message, content: $content, branch: $branch, | ||
| committer: {name: $name, email: $email}, | ||
| author: {name: $name, email: $email}} | ||
| | if $sha != "" then . + {sha: $sha} else . end' \ | ||
| | gh api -X PUT "repos/$REPO/contents/cmake/cmake-target-layers.md" --input - | ||
|
|
||
| - name: Confirm update | ||
| if: always() | ||
| env: | ||
| ALREADY_CURRENT: ${{ steps.commit.outputs.already_current }} | ||
| COMMIT_OUTCOME: ${{ steps.commit.outcome }} | ||
| run: | | ||
| if [ "$COMMIT_OUTCOME" = "success" ]; then | ||
| gh api -X POST "repos/$REPO/issues/comments/$COMMENT_ID/reactions" \ | ||
| -f content='+1' >/dev/null | ||
| if [ "$ALREADY_CURRENT" = "true" ]; then | ||
| gh pr comment "$PR_NUMBER" --repo "$REPO" --body \ | ||
| "✅ \`cmake/cmake-target-layers.md\` is already up to date with the latest CI run — nothing to commit." | ||
| else | ||
| gh pr comment "$PR_NUMBER" --repo "$REPO" --body \ | ||
| "✅ \`cmake/cmake-target-layers.md\` has been refreshed and committed to this branch." | ||
| fi | ||
| else | ||
| gh api -X POST "repos/$REPO/issues/comments/$COMMENT_ID/reactions" \ | ||
| -f content='-1' >/dev/null | ||
| gh pr comment "$PR_NUMBER" --repo "$REPO" --body \ | ||
| "❌ Failed to commit \`cmake/cmake-target-layers.md\` — check the [workflow run](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}) for details." | ||
| fi | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,25 @@ | ||
| { | ||
| "version": 3, | ||
| "cmakeMinimumRequired": { | ||
| "major": 3, | ||
| "minor": 21, | ||
| "patch": 0 | ||
| }, | ||
| "configurePresets": [ | ||
| { | ||
| "name": "ci-linux", | ||
| "displayName": "Linux CI build (full)", | ||
| "description": "Canonical Linux CI configuration. Single source of truth for the graph-affecting build options, shared with the cmake-target-layers diagram workflow.", | ||
| "binaryDir": "${sourceDir}/build", | ||
| "cacheVariables": { | ||
| "BUILD_VIZ": "ON", | ||
| "BUILD_PLUGINS": "ON", | ||
| "BUILD_PLUGIN_OAK_CAMERA": "ON", | ||
| "BUILD_EXAMPLES": "ON", | ||
| "BUILD_TESTING": "ON", | ||
| "BUILD_PYTHON_BINDINGS": "ON", | ||
| "ENABLE_CLOUDXR_BUNDLE_CHECK": "ON" | ||
| } | ||
| } | ||
| ] | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
| SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. | ||
| SPDX-License-Identifier: Apache-2.0 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
Repository: NVIDIA/IsaacTeleop
Length of output: 179
🏁 Script executed:
Repository: NVIDIA/IsaacTeleop
Length of output: 179
🏁 Script executed:
Repository: NVIDIA/IsaacTeleop
Length of output: 3951
Pin GitHub Actions
usesto immutable commit SHAs (supply-chain hardening).In
.github/workflows/cmake-target-layers.yaml, theseuses:use mutable tags—pin each to a full commit SHA instead of@v*:actions/checkout@v6(line 53)actions/cache@v5(line 74)actions/upload-artifact@v6(line 99)🧰 Tools
🪛 zizmor (1.25.2)
[error] 53-53: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)
(unpinned-uses)
🤖 Prompt for AI Agents
Source: Linters/SAST tools