Skip to content

Commit 367e498

Browse files
kellyguo11mataylor-nvidiagreptile-apps[bot]
authored
Run Docker CI from PR comments (#7059)
## Summary - allow PR authors and repository users with write access to request Docker CI with `run-ci` - use the existing `isaaclab-bot` App token to trigger the normal pull request workflow - stop the base and cuRobo Docker builds from running on ordinary PR opens and pushes - preserve changed-path detection, downstream test dependencies, and maintainer dispatches ## Why The two self-hosted GPU Docker builds currently start automatically whenever relevant files change. PR authors need a self-service way to defer that expensive CI path without requiring a protected environment, a webhook service, or write access to launch `workflow_dispatch`. ## Impact Commenting `run-ci` on an open pull request starts Docker CI when the commenter is the PR author or has write access to the repository. The trusted command workflow never checks out PR code; it briefly applies `ci:run-docker` with `isaaclab-bot`, which triggers `Docker + Tests` as a normal `pull_request` workflow on the current merge ref. The label is removed immediately so the command can be used again after later pushes. GitHub cannot filter `pull_request` label names at workflow-trigger time. Any label applied manually therefore starts the same path-gated Docker workflow; the repository's automatic labeler uses `GITHUB_TOKEN`, whose label events do not create recursive workflow runs. Keeping the workflow unfiltered ensures that a manually labeled run cannot satisfy required checks by skipping them. Maintainers can also use the existing manual workflow dispatch. The upstream `ci:run-docker` label has been created with the description "Trigger the on-demand Docker and GPU CI workflow." ## Validation - `uv run isaaclab -f` before commit - `uv run isaaclab -f` after commit and before push - verified PR-author bypass plus write/admin and read-only permission handling - verified the release ruleset's required Docker/test contexts remain produced only by a real labeled or dispatched run --------- Signed-off-by: Matthew Taylor <mataylor@nvidia.com> Co-authored-by: Matthew Taylor <mataylor@nvidia.com> Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
1 parent 0862ab0 commit 367e498

3 files changed

Lines changed: 120 additions & 3 deletions

File tree

.github/PULL_REQUEST_TEMPLATE.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,9 @@ To upload images to a PR -- simply drag and drop an image while in edit mode and
5151

5252
## Checklist
5353

54+
Docker and GPU tests run on demand. Push the commits you want tested, then
55+
comment `run-ci` on the pull request.
56+
5457
- [ ] I have read and understood the [contribution guidelines](https://isaac-sim.github.io/IsaacLab/main/source/refs/contributing.html)
5558
- [ ] I have run the [`pre-commit` checks](https://pre-commit.com/) with `./isaaclab.sh --format`
5659
- [ ] I have made corresponding changes to the documentation

.github/workflows/build.yaml

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,14 @@
44
# SPDX-License-Identifier: BSD-3-Clause
55

66
# region help
7-
# Every test job runs on every PR.
7+
# Docker builds and their dependent tests run only on a labeled PR event or a
8+
# manual workflow dispatch. Do not filter the label with a job-level ``if``:
9+
# GitHub treats skipped required jobs as successful status checks.
10+
#
11+
# GitHub cannot filter label names at trigger time, so adding any label to a PR
12+
# starts this workflow. Only a ``ci:run-docker`` event cancels an in-flight run;
13+
# other label events queue behind it so that a triage label cannot cancel a
14+
# build whose required checks have already been reported.
815
#
916
# =============================================================================
1017
# CI DEBUGGING TIPS
@@ -47,7 +54,7 @@ name: Docker + Tests
4754

4855
on:
4956
pull_request:
50-
types: [opened, synchronize, reopened]
57+
types: [labeled]
5158
branches:
5259
- main
5360
- develop
@@ -57,7 +64,7 @@ on:
5764
# Concurrency control to prevent parallel runs on the same PR
5865
concurrency:
5966
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
60-
cancel-in-progress: true
67+
cancel-in-progress: ${{ github.event.label.name == 'ci:run-docker' }}
6168

6269
permissions:
6370
contents: read
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
# Copyright (c) 2022-2026, The Isaac Lab Project Developers (https://github.com/isaac-sim/IsaacLab/blob/main/CONTRIBUTORS.md).
2+
# All rights reserved.
3+
#
4+
# SPDX-License-Identifier: BSD-3-Clause
5+
6+
name: Run Docker CI Command
7+
run-name: Run Docker CI for PR #${{ github.event.issue.number }}
8+
9+
on:
10+
# GitHub always runs ``issue_comment`` workflows from the repository default
11+
# branch, so this file has to land there for the ``run-ci`` command to work.
12+
issue_comment:
13+
types: [created]
14+
15+
permissions:
16+
contents: read
17+
pull-requests: read
18+
19+
concurrency:
20+
group: run-docker-ci-${{ github.event.issue.number }}
21+
cancel-in-progress: false
22+
23+
jobs:
24+
request-docker-ci:
25+
name: Request Docker CI
26+
if: >-
27+
github.event.issue.pull_request &&
28+
github.event.comment.body == 'run-ci'
29+
runs-on: ubuntu-latest
30+
timeout-minutes: 5
31+
steps:
32+
- name: Authorize the request
33+
id: authorize
34+
env:
35+
COMMENT_AUTHOR: ${{ github.event.comment.user.login }}
36+
GH_TOKEN: ${{ github.token }}
37+
PR_NUMBER: ${{ github.event.issue.number }}
38+
REPOSITORY: ${{ github.repository }}
39+
run: |
40+
set -euo pipefail
41+
42+
read -r pr_author pr_state head_sha < <(
43+
gh api "repos/$REPOSITORY/pulls/$PR_NUMBER" --jq '[.user.login, .state, .head.sha] | @tsv'
44+
)
45+
if [ "$pr_state" != "open" ]; then
46+
echo "::error::PR #$PR_NUMBER is not open."
47+
exit 1
48+
fi
49+
if [ "${COMMENT_AUTHOR,,}" != "${pr_author,,}" ]; then
50+
commenter_permission="$(
51+
gh api "repos/$REPOSITORY/collaborators/$COMMENT_AUTHOR/permission" \
52+
--jq '.permission' 2>/dev/null || printf 'none'
53+
)"
54+
case "$commenter_permission" in
55+
admin|write) ;;
56+
*)
57+
echo "::error::Only PR author @$pr_author or a user with write access can request Docker CI."
58+
exit 1
59+
;;
60+
esac
61+
fi
62+
63+
echo "head_sha=$head_sha" >> "$GITHUB_OUTPUT"
64+
65+
- name: Create isaaclab-bot token
66+
id: app-token
67+
uses: actions/create-github-app-token@1b10c78c7865c340bc4f6099eb2f838309f1e8c3 # v3.1.1
68+
with:
69+
client-id: ${{ secrets.CHANGELOG_APP_CLIENT_ID }}
70+
private-key: ${{ secrets.CHANGELOG_APP_PRIVATE_KEY }}
71+
permission-pull-requests: write
72+
73+
- name: Trigger Docker CI
74+
env:
75+
EXPECTED_HEAD_SHA: ${{ steps.authorize.outputs.head_sha }}
76+
GH_TOKEN: ${{ steps.app-token.outputs.token }}
77+
LABEL: ci:run-docker
78+
PR_NUMBER: ${{ github.event.issue.number }}
79+
REPOSITORY: ${{ github.repository }}
80+
run: |
81+
set -euo pipefail
82+
83+
# GitHub resolves the head when it processes the label, so labeling a
84+
# PR whose head has already moved on builds a revision nobody asked
85+
# for. Skip the request instead of spending GPU minutes on it.
86+
head_sha="$(gh api "repos/$REPOSITORY/pulls/$PR_NUMBER" --jq '.head.sha')"
87+
if [ "$head_sha" != "$EXPECTED_HEAD_SHA" ]; then
88+
echo "::error::PR #$PR_NUMBER moved from ${EXPECTED_HEAD_SHA:0:7} to ${head_sha:0:7} while this request was processed. Comment 'run-ci' again to test the current head."
89+
exit 1
90+
fi
91+
92+
endpoint="repos/$REPOSITORY/issues/$PR_NUMBER/labels"
93+
gh api --method DELETE "$endpoint/$LABEL" --silent >/dev/null 2>&1 || true
94+
gh api --method POST "$endpoint" -f "labels[]=$LABEL" --silent
95+
gh api --method DELETE "$endpoint/$LABEL" --silent
96+
97+
# A push can still land between the check above and GitHub processing
98+
# the label; nothing can make those two steps atomic. Re-read the head
99+
# so the mismatch is reported rather than silently testing a revision
100+
# the requester never saw.
101+
built_sha="$(gh api "repos/$REPOSITORY/pulls/$PR_NUMBER" --jq '.head.sha')"
102+
if [ "$built_sha" != "$EXPECTED_HEAD_SHA" ]; then
103+
echo "::error::PR #$PR_NUMBER moved to ${built_sha:0:7} as the label was applied, so Docker CI is testing that revision and not the requested ${EXPECTED_HEAD_SHA:0:7}. Comment 'run-ci' again to test the current head."
104+
exit 1
105+
fi
106+
107+
echo "Requested Docker CI for PR #$PR_NUMBER at ${EXPECTED_HEAD_SHA:0:7}." >> "$GITHUB_STEP_SUMMARY"

0 commit comments

Comments
 (0)