Skip to content

Dev Community CI · PR #1379 · head 51c96af9f2127e4ea4bfa72414495c3a6c46102e · merge a11eab5af98bd33b03b79651f0667329871809d1 #585

Dev Community CI · PR #1379 · head 51c96af9f2127e4ea4bfa72414495c3a6c46102e · merge a11eab5af98bd33b03b79651f0667329871809d1

Dev Community CI · PR #1379 · head 51c96af9f2127e4ea4bfa72414495c3a6c46102e · merge a11eab5af98bd33b03b79651f0667329871809d1 #585

Workflow file for this run

# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0
name: Community CI
# Community CI GPU promotion v1
run-name: >-
${{ (github.event_name == 'pull_request_target' || (github.event_name == 'workflow_dispatch' && inputs.source_snapshot == '')) && format('Community CI · PR #{0}', github.event.pull_request.number || inputs.pr_number) || github.event_name == 'pull_request' && format('PR #{0} · community CI · head {1} · merge {2}', github.event.pull_request.number, github.event.pull_request.head.sha, github.sha) || format('{0} Community CI · PR #{1} · head {2} · merge {3}', inputs.ci_lane == 'dev' && 'Dev' || 'Stable', inputs.pr_number, fromJSON(inputs.source_snapshot || '{}').head_sha, fromJSON(inputs.source_snapshot || '{}').merge_sha) }}
# Stable keeps the existing pull_request pipeline. Trusted metadata jobs pair
# its immutable snapshot with the optional Dev execution of this same workflow.
on:
pull_request_target:
branches: [main]
types: [opened, synchronize, reopened, ready_for_review]
pull_request:
branches: [main]
types: [opened, synchronize, reopened, ready_for_review]
workflow_dispatch:
inputs:
pr_number:
description: Open pull request number to test
required: true
type: string
run_gpu_smoke:
description: Manually enable the experimental GPU smoke test
required: false
default: false
type: boolean
ci_lane:
description: CI implementation role (filled automatically)
required: false
type: choice
default: stable
options: [stable, dev]
source_snapshot:
description: Frozen PR snapshot (filled automatically)
required: false
type: string
request_id:
description: CI request identifier (filled automatically)
required: false
type: string
permissions: {}
# Experimental branch policy; Stable main keeps automatic GPU disabled.
env:
COMMUNITY_GPU_EXECUTION_ENABLED: "true"
concurrency:
group: community-ci-${{ inputs.source_snapshot != '' && inputs.ci_lane || github.event_name == 'pull_request' && 'stable-pr' || github.ref_name == 'main' && 'start-main' || 'start-dev' }}-${{ github.event.pull_request.number || inputs.pr_number }}
cancel-in-progress: true
jobs:
snapshot:
name: Capture the Community CI snapshot
if: >-
github.repository == 'NVIDIA/TensorRT-Model-Connect' &&
(github.event_name == 'pull_request_target' ||
(github.event_name == 'workflow_dispatch' && inputs.source_snapshot == ''))
runs-on: ubuntu-24.04
timeout-minutes: 5
permissions:
contents: read
pull-requests: read
statuses: write
actions: read
outputs:
pr_number: ${{ steps.snapshot.outputs.pr_number }}
head_sha: ${{ steps.snapshot.outputs.head_sha }}
source_snapshot: ${{ steps.snapshot.outputs.source_snapshot }}
lanes: ${{ steps.lanes.outputs.lanes }}
stable_run_id: ${{ steps.stable.outputs.run_id }}
steps:
- name: Select the Community CI branches
id: lanes
env:
DUAL_RUN: ${{ vars.TRTMC_COMMUNITY_CI_DUAL_RUN }}
CI_BRANCH: ${{ github.ref_name }}
run: |
set -euo pipefail
lanes='["stable"]'
if [ "$DUAL_RUN" = true ]; then
lanes='["stable","dev"]'
elif [ "$CI_BRANCH" != main ]; then
lanes='["dev"]'
fi
echo "lanes=$lanes" >> "$GITHUB_OUTPUT"
- name: Authorize a manual request
if: ${{ github.event_name == 'workflow_dispatch' }}
env:
GH_TOKEN: ${{ github.token }}
ACTOR: ${{ github.actor }}
run: |
set -euo pipefail
role="$(gh api "/repos/$GITHUB_REPOSITORY/collaborators/$ACTOR/permission" --jq '.role_name')"
case "$role" in
maintain|admin) ;;
*) echo "::error::Only maintain or admin may dispatch Community CI."; exit 1 ;;
esac
- name: Find the existing Stable PR snapshot
id: stable
if: ${{ github.event_name == 'pull_request_target' || github.ref_name != 'main' }}
env:
GH_TOKEN: ${{ github.token }}
PR_NUMBER: ${{ github.event.pull_request.number || inputs.pr_number }}
EVENT_HEAD_SHA: ${{ github.event.pull_request.head.sha }}
run: |
set -euo pipefail
[[ "$PR_NUMBER" =~ ^[1-9][0-9]*$ ]]
pull="$(gh api "/repos/$GITHUB_REPOSITORY/pulls/$PR_NUMBER")"
head="$(jq -er '.head.sha' <<< "$pull")"
[[ "$head" =~ ^[0-9a-f]{40}$ ]]
if [ -n "$EVENT_HEAD_SHA" ]; then test "$head" = "$EVENT_HEAD_SHA"; fi
prefix="PR #$PR_NUMBER · community CI · head $head · merge "
# Reuse the existing PR pipeline, without rerunning Stable or changing
# the event/job contract consumed by the unchanged Internal CI bridge.
for attempt in $(seq 1 24); do
runs="$(gh api "/repos/$GITHUB_REPOSITORY/actions/workflows/community-ci.yml/runs?event=pull_request&head_sha=$head&per_page=100")"
candidate="$(jq -c --arg head "$head" --arg prefix "$prefix" '
[.workflow_runs[]
| select(.event == "pull_request" and .head_sha == $head)
| select(.path == ".github/workflows/community-ci.yml")
| select(.display_title | startswith($prefix))]
| max_by(.id) // empty' <<< "$runs")"
if [ -n "$candidate" ]; then
run_id="$(jq -er '.id' <<< "$candidate")"
merge_sha="$(jq -er --arg prefix "$prefix" '.display_title | ltrimstr($prefix)' <<< "$candidate")"
[[ "$run_id" =~ ^[1-9][0-9]*$ ]]
[[ "$merge_sha" =~ ^[0-9a-f]{40}$ ]]
{
echo "run_id=$run_id"
echo "merge_sha=$merge_sha"
} >> "$GITHUB_OUTPUT"
exit 0
fi
if [ "$attempt" -lt 24 ]; then sleep 5; fi
done
echo "::error::No Stable PR snapshot is available for this head."
exit 1
# Trigger metadata only: all validation belongs to each complete CI run.
- name: Capture the exact pull-request snapshot
id: snapshot
env:
GH_TOKEN: ${{ github.token }}
PR_NUMBER: ${{ github.event.pull_request.number || inputs.pr_number }}
EVENT_HEAD_SHA: ${{ github.event.pull_request.head.sha }}
STABLE_MERGE_SHA: ${{ steps.stable.outputs.merge_sha }}
run: |
set -euo pipefail
[[ "$PR_NUMBER" =~ ^[1-9][0-9]*$ ]]
pr_number="$PR_NUMBER"
merge_sha=""
for attempt in 1 2 3 4 5 6; do
pull="$(gh api "/repos/$GITHUB_REPOSITORY/pulls/$pr_number")"
jq -e --arg repo "$GITHUB_REPOSITORY" \
'.state == "open" and .base.repo.full_name == $repo and .base.ref == "main"' \
<<< "$pull" > /dev/null
head_sha="$(jq -er '.head.sha' <<< "$pull")"
[[ "$head_sha" =~ ^[0-9a-f]{40}$ ]]
if [ -n "$EVENT_HEAD_SHA" ]; then
test "$head_sha" = "$EVENT_HEAD_SHA"
fi
echo "head_sha=$head_sha" >> "$GITHUB_OUTPUT"
merge_sha="${STABLE_MERGE_SHA:-$(jq -r '.merge_commit_sha // empty' <<< "$pull")}"
if [[ "$merge_sha" =~ ^[0-9a-f]{40}$ ]]; then break; fi
if [ "$attempt" -lt 6 ]; then sleep 5; fi
done
[[ "$merge_sha" =~ ^[0-9a-f]{40}$ ]]
merge="$(gh api "/repos/$GITHUB_REPOSITORY/git/commits/$merge_sha")"
jq -e --arg head "$head_sha" --arg merge "$merge_sha" \
'.sha == $merge and (.parents | length) == 2 and .parents[1].sha == $head' \
<<< "$merge" > /dev/null
base_sha="$(jq -er '.parents[0].sha' <<< "$merge")"
tree="$(jq -er '.tree.sha' <<< "$merge")"
[[ "$base_sha" =~ ^[0-9a-f]{40}$ ]]
[[ "$tree" =~ ^[0-9a-f]{40}$ ]]
snapshot="$(jq -cn --arg head "$head_sha" --arg base "$base_sha" \
--arg merge "$merge_sha" --arg tree "$tree" \
'{head_sha:$head,base_sha:$base,merge_sha:$merge,source_tree:$tree}')"
{
echo "pr_number=$pr_number"
echo "source_snapshot=$snapshot"
} >> "$GITHUB_OUTPUT"
- name: Report a failed snapshot
if: ${{ failure() }}
env:
GH_TOKEN: ${{ github.token }}
HEAD_SHA: ${{ steps.snapshot.outputs.head_sha }}
STATUS_CONTEXT: ${{ github.ref_name == 'main' && 'Stable Community CI' || 'Dev Community CI' }}
run: |
set -euo pipefail
[[ "$HEAD_SHA" =~ ^[0-9a-f]{40}$ ]] || exit 0
gh api --silent --method POST \
"/repos/$GITHUB_REPOSITORY/statuses/$HEAD_SHA" \
-f state=failure -f context="$STATUS_CONTEXT" \
-f description="Community CI snapshot failed" \
-f target_url="$GITHUB_SERVER_URL/$GITHUB_REPOSITORY/actions/runs/$GITHUB_RUN_ID"
dispatch:
name: Start ${{ matrix.lane }} Community CI
needs: snapshot
if: ${{ needs.snapshot.result == 'success' }}
strategy:
fail-fast: false
matrix:
lane: ${{ fromJSON(needs.snapshot.outputs.lanes) }}
# Dev cannot cancel or queue a stable lane from another PR trigger.
concurrency:
group: community-ci-dispatch-${{ matrix.lane }}-${{ needs.snapshot.outputs.pr_number }}
cancel-in-progress: false
continue-on-error: ${{ matrix.lane == 'dev' && contains(fromJSON(needs.snapshot.outputs.lanes), 'stable') }}
runs-on: ubuntu-24.04
timeout-minutes: 360
permissions:
actions: write
statuses: write
env:
STATUS_CONTEXT: ${{ matrix.lane == 'stable' && 'Stable Community CI' || 'Dev Community CI' }}
steps:
- name: Mark the selected CI pending
env:
GH_TOKEN: ${{ github.token }}
HEAD_SHA: ${{ needs.snapshot.outputs.head_sha }}
run: |
set -euo pipefail
gh api --silent --method POST \
"/repos/$GITHUB_REPOSITORY/statuses/$HEAD_SHA" \
-f state=pending -f context="$STATUS_CONTEXT" \
-f description="Community CI is starting" \
-f target_url="$GITHUB_SERVER_URL/$GITHUB_REPOSITORY/actions/runs/$GITHUB_RUN_ID"
- name: Dispatch the selected Community CI implementation
id: execution
env:
GH_TOKEN: ${{ github.token }}
PR_NUMBER: ${{ needs.snapshot.outputs.pr_number }}
CI_REF: ${{ matrix.lane == 'stable' && 'main' || github.ref_name != 'main' && github.ref_name || vars.TRTMC_COMMUNITY_CI_DEV_REF || 'main' }}
LANE: ${{ matrix.lane }}
SOURCE_SNAPSHOT: ${{ needs.snapshot.outputs.source_snapshot }}
HEAD_SHA: ${{ needs.snapshot.outputs.head_sha }}
STABLE_RUN_ID: ${{ needs.snapshot.outputs.stable_run_id }}
CI_ENTRY_BRANCH: ${{ github.ref_name }}
AUTOMATIC_GPU: ${{ env.COMMUNITY_GPU_EXECUTION_ENABLED }}
RUN_GPU_SMOKE: ${{ inputs.run_gpu_smoke || false }}
run: |
set -euo pipefail
if [ "$LANE" = stable ] && [ -n "${STABLE_RUN_ID:-}" ] \
&& { [ "${CI_ENTRY_BRANCH:-}" != main ] || [ "${AUTOMATIC_GPU:-false}" != true ]; }; then
[[ "$STABLE_RUN_ID" =~ ^[1-9][0-9]*$ ]]
echo "run_id=$STABLE_RUN_ID" >> "$GITHUB_OUTPUT"
echo "ci_ref=main" >> "$GITHUB_OUTPUT"
echo "existing_stable=true" >> "$GITHUB_OUTPUT"
exit 0
fi
umask 077
dispatch_nonce="$(openssl rand -hex 16)"
[[ "$dispatch_nonce" =~ ^[0-9a-f]{32}$ ]]
payload="$RUNNER_TEMP/community-ci-start-dispatch.json"
trap 'rm -f "$payload"' EXIT
jq -n \
--arg pr_number "$PR_NUMBER" \
--arg ref "$CI_REF" \
--arg lane "$LANE" \
--arg snapshot "$SOURCE_SNAPSHOT" \
--arg dispatch_nonce "$dispatch_nonce" \
--argjson run_gpu_smoke "${RUN_GPU_SMOKE:-false}" \
'{
ref: $ref,
return_run_details: true,
inputs: {
pr_number: $pr_number,
ci_lane: $lane,
source_snapshot: $snapshot,
request_id: $dispatch_nonce,
run_gpu_smoke: $run_gpu_smoke
}
}' > "$payload"
dispatched="$(gh api --method POST \
-H 'X-GitHub-Api-Version: 2026-03-10' \
"/repos/$GITHUB_REPOSITORY/actions/workflows/community-ci.yml/dispatches" \
--input "$payload")"
run_id="$(jq -er '.workflow_run_id' <<< "$dispatched")"
[[ "$run_id" =~ ^[1-9][0-9]*$ ]]
echo "run_id=$run_id" >> "$GITHUB_OUTPUT"
echo "ci_ref=$CI_REF" >> "$GITHUB_OUTPUT"
gh api --silent --method POST \
"/repos/$GITHUB_REPOSITORY/statuses/$HEAD_SHA" \
-f state=pending -f context="$STATUS_CONTEXT" \
-f description="Community CI is running" \
-f target_url="$GITHUB_SERVER_URL/$GITHUB_REPOSITORY/actions/runs/$run_id"
# Follow the exact run returned by GitHub, so every stage in that CI
# branch contributes to the verdict, including newly added Dev stages.
- name: Publish the complete workflow conclusion
id: verdict
env:
GH_TOKEN: ${{ github.token }}
PIPELINE_RUN_ID: ${{ steps.execution.outputs.run_id }}
EXISTING_STABLE: ${{ steps.execution.outputs.existing_stable }}
CI_BRANCH: ${{ steps.execution.outputs.ci_ref }}
LANE: ${{ matrix.lane }}
PR_NUMBER: ${{ needs.snapshot.outputs.pr_number }}
HEAD_SHA: ${{ needs.snapshot.outputs.head_sha }}
SOURCE_SNAPSHOT: ${{ needs.snapshot.outputs.source_snapshot }}
run: |
set -euo pipefail
[[ "$PIPELINE_RUN_ID" =~ ^[1-9][0-9]*$ ]]
role=Stable
if [ "$LANE" = dev ]; then role=Dev; else test "$CI_BRANCH" = main; fi
merge_sha="$(jq -er '.merge_sha' <<< "$SOURCE_SNAPSHOT")"
expected_title="$role Community CI · PR #$PR_NUMBER · head $HEAD_SHA · merge $merge_sha"
if [ "${EXISTING_STABLE:-}" = true ]; then
test "$LANE" = stable
expected_title="PR #$PR_NUMBER · community CI · head $HEAD_SHA · merge $merge_sha"
fi
deadline=$((SECONDS + 18000))
while [ "$SECONDS" -lt "$deadline" ]; do
run="$(gh api "/repos/$GITHUB_REPOSITORY/actions/runs/$PIPELINE_RUN_ID")"
if [ "$(jq -r '.status' <<< "$run")" = completed ]; then
# GitHub may return a placeholder title while a newly dispatched
# run is initializing. Verify identity before publishing its result.
if ! jq -e --arg title "$expected_title" --arg branch "$CI_BRANCH" \
--arg head "$HEAD_SHA" --arg existing "${EXISTING_STABLE:-false}" \
'.path == ".github/workflows/community-ci.yml" and .display_title == $title
and (if $existing == "true" then .event == "pull_request" and .head_sha == $head
else .event == "workflow_dispatch" and .head_branch == $branch end)' \
<<< "$run" > /dev/null; then
echo "::error::Completed Community CI does not match the requested branch and snapshot."
jq '{path,event,head_branch,display_title,status}' <<< "$run"
exit 1
fi
state=failure
if [ "$(jq -r '.conclusion' <<< "$run")" = success ]; then state=success; fi
gh api --silent --method POST \
"/repos/$GITHUB_REPOSITORY/statuses/$HEAD_SHA" \
-f state="$state" -f context="$STATUS_CONTEXT" \
-f description="Complete Community CI: $state" \
-f target_url="$GITHUB_SERVER_URL/$GITHUB_REPOSITORY/actions/runs/$PIPELINE_RUN_ID"
echo "reported=true" >> "$GITHUB_OUTPUT"
test "$state" = success
exit 0
fi
sleep 30
done
echo "::error::Community CI did not complete within five hours."
exit 1
- name: Report a failed CI request
if: ${{ failure() && steps.verdict.outputs.reported != 'true' }}
env:
GH_TOKEN: ${{ github.token }}
HEAD_SHA: ${{ needs.snapshot.outputs.head_sha }}
run: |
set -euo pipefail
gh api --silent --method POST \
"/repos/$GITHUB_REPOSITORY/statuses/$HEAD_SHA" \
-f state=failure -f context="$STATUS_CONTEXT" \
-f description="Community CI could not complete" \
-f target_url="$GITHUB_SERVER_URL/$GITHUB_REPOSITORY/actions/runs/$GITHUB_RUN_ID"
authorize:
name: Community CI / Authorize
if: >-
github.repository == 'NVIDIA/TensorRT-Model-Connect' &&
(github.event_name == 'pull_request' ||
(github.event_name == 'workflow_dispatch' && inputs.source_snapshot != ''))
runs-on: ubuntu-24.04
timeout-minutes: 5
permissions:
contents: read
pull-requests: read
outputs:
pr_number: ${{ steps.snapshot.outputs.pr_number }}
head_sha: ${{ steps.snapshot.outputs.head_sha }}
base_sha: ${{ steps.snapshot.outputs.base_sha }}
merge_sha: ${{ steps.snapshot.outputs.merge_sha }}
enabled: ${{ steps.snapshot.outputs.enabled }}
reuse_stable_cpu: ${{ steps.snapshot.outputs.reuse_stable_cpu }}
steps:
# This job consumes trusted GitHub metadata only. It pins the PR head,
# base, and synthetic merge before any untrusted code can run.
- name: Capture the exact pull-request snapshot
id: snapshot
env:
GH_TOKEN: ${{ github.token }}
PR_NUMBER: ${{ github.event.pull_request.number || inputs.pr_number }}
EVENT_NAME: ${{ github.event_name }}
EVENT_HEAD_SHA: ${{ github.event.pull_request.head.sha }}
EVENT_BASE_SHA: ${{ github.event.pull_request.base.sha }}
EVENT_MERGE_SHA: ${{ github.sha }}
SOURCE_SNAPSHOT: ${{ inputs.source_snapshot }}
REQUEST_ID: ${{ inputs.request_id }}
CI_LANE: ${{ inputs.ci_lane }}
CI_REF: ${{ github.ref }}
ACTOR: ${{ github.actor }}
run: |
set -euo pipefail
[[ "$PR_NUMBER" =~ ^[1-9][0-9]*$ ]]
if [ "$EVENT_NAME" = workflow_dispatch ]; then
if [ "$ACTOR" != 'github-actions[bot]' ]; then
role="$(gh api "/repos/$GITHUB_REPOSITORY/collaborators/$ACTOR/permission" --jq '.role_name')"
case "$role" in
maintain|admin) ;;
*) echo "::error::Only maintain or admin may dispatch Community CI."; exit 1 ;;
esac
fi
case "$CI_LANE" in
stable) test "$CI_REF" = refs/heads/main ;;
dev) ;;
*) echo "::error::Unknown Community CI lane"; exit 1 ;;
esac
fi
pull="$(gh api --method GET "/repos/$GITHUB_REPOSITORY/pulls/$PR_NUMBER")"
state="$(jq -er '.state' <<<"$pull")"
base_repo="$(jq -er '.base.repo.full_name' <<<"$pull")"
base_ref="$(jq -er '.base.ref' <<<"$pull")"
live_head_sha="$(jq -er '.head.sha' <<<"$pull")"
test "$state" = "open"
test "$base_repo" = "$GITHUB_REPOSITORY"
test "$base_ref" = "main"
[[ "$live_head_sha" =~ ^[0-9a-f]{40}$ ]]
if [ "$EVENT_NAME" = pull_request ]; then
head_sha="$EVENT_HEAD_SHA"
merge_sha="$EVENT_MERGE_SHA"
else
[[ "$REQUEST_ID" =~ ^[0-9a-f]{32}$ ]]
head_sha="$(jq -er '.head_sha' <<< "$SOURCE_SNAPSHOT")"
merge_sha="$(jq -er '.merge_sha' <<< "$SOURCE_SNAPSHOT")"
approved_base="$(jq -er '.base_sha' <<< "$SOURCE_SNAPSHOT")"
approved_tree="$(jq -er '.source_tree' <<< "$SOURCE_SNAPSHOT")"
[[ "$approved_base" =~ ^[0-9a-f]{40}$ ]]
[[ "$approved_tree" =~ ^[0-9a-f]{40}$ ]]
fi
[[ "$head_sha" =~ ^[0-9a-f]{40}$ ]]
[[ "$merge_sha" =~ ^[0-9a-f]{40}$ ]]
test "$head_sha" = "$live_head_sha"
merge="$(gh api --method GET "/repos/$GITHUB_REPOSITORY/git/commits/$merge_sha")"
resolved_merge="$(jq -er '.sha' <<<"$merge")"
parent_count="$(jq -er '.parents | length' <<<"$merge")"
merge_base="$(jq -er '.parents[0].sha' <<<"$merge")"
merge_head="$(jq -er '.parents[1].sha' <<<"$merge")"
test "$resolved_merge" = "$merge_sha"
test "$parent_count" = "2"
test "$merge_head" = "$head_sha"
base_sha="$merge_base"
[[ "$base_sha" =~ ^[0-9a-f]{40}$ ]]
if [ "$EVENT_NAME" = workflow_dispatch ]; then
test "$base_sha" = "$approved_base"
test "$(jq -er '.tree.sha' <<< "$merge")" = "$approved_tree"
fi
reuse_stable_cpu=false
if [ "$EVENT_NAME" = pull_request ]; then
# Enable the compatibility view only after promotion is in the
# trusted base. The promotion PR itself still runs normal CPU CI.
base_workflow="$(gh api "/repos/$GITHUB_REPOSITORY/contents/.github/workflows/community-ci.yml?ref=$base_sha" --jq '.content' | base64 --decode)"
if grep -Fxq '# Community CI GPU promotion v1' <<< "$base_workflow" \
&& grep -Fxq ' COMMUNITY_GPU_EXECUTION_ENABLED: "true"' <<< "$base_workflow"; then
reuse_stable_cpu=true
fi
fi
{
echo "enabled=true"
echo "reuse_stable_cpu=$reuse_stable_cpu"
echo "pr_number=$PR_NUMBER"
echo "head_sha=$head_sha"
echo "base_sha=$base_sha"
echo "merge_sha=$merge_sha"
} >> "$GITHUB_OUTPUT"
# These jobs intentionally execute untrusted PR code. Each receives only a
# read-only token and no repository/environment secret, and every job runs on
# a fresh hosted runner with no cross-job artifact or cache.
source-quality:
name: Community CPU / Source quality
needs: authorize
if: ${{ needs.authorize.outputs.reuse_stable_cpu != 'true' }}
runs-on: ubuntu-24.04
timeout-minutes: 15
permissions:
contents: read
env:
CI_BASE_REF: ${{ needs.authorize.outputs.merge_sha }}^1
steps:
- name: Check out the exact PR merge
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803
with:
ref: ${{ needs.authorize.outputs.merge_sha }}
fetch-depth: 0
persist-credentials: false
- name: Set up Python
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405
with:
python-version: "3.12"
- name: Install public CPU quality tools
run: python3 -m pip install --disable-pip-version-check --requirement requirements/community-ci.txt
- name: Run source quality
run: python3 -m tools.community_ci source-quality --base "$CI_BASE_REF"
docs:
name: Community CPU / Docs
needs: authorize
if: ${{ needs.authorize.outputs.reuse_stable_cpu != 'true' }}
runs-on: ubuntu-24.04
timeout-minutes: 15
permissions:
contents: read
steps:
- name: Check out the exact PR merge
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803
with:
ref: ${{ needs.authorize.outputs.merge_sha }}
fetch-depth: 0
persist-credentials: false
- name: Set up Node
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version: "20"
- name: Install website dependencies
working-directory: website
run: npm ci
- name: Test generated model support inventory
working-directory: website
run: npm run test:model-support
- name: Build production documentation
working-directory: website
env:
SITE_URL: https://nvidia.github.io
BASE_URL: /TensorRT-Model-Connect/
run: npm run build
ownership-impact:
name: Community CPU / Ownership and impact
needs: authorize
if: ${{ needs.authorize.outputs.reuse_stable_cpu != 'true' }}
runs-on: ubuntu-24.04
timeout-minutes: 10
permissions:
contents: read
env:
CI_BASE_REF: ${{ needs.authorize.outputs.merge_sha }}^1
steps:
- name: Check out the exact PR merge
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803
with:
ref: ${{ needs.authorize.outputs.merge_sha }}
fetch-depth: 0
persist-credentials: false
- name: Set up Python
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405
with:
python-version: "3.12"
- name: Resolve source ownership and CPU scope
id: impact
run: python3 -m tools.community_ci impact --base "$CI_BASE_REF"
unit:
name: Community CPU / Unit / C++ and Python
needs: authorize
if: ${{ needs.authorize.outputs.reuse_stable_cpu != 'true' }}
runs-on: ubuntu-24.04
timeout-minutes: 45
permissions:
contents: read
steps:
- name: Check out the exact PR merge
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803
with:
ref: ${{ needs.authorize.outputs.merge_sha }}
fetch-depth: 0
persist-credentials: false
- name: Set up Python
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405
with:
python-version: "3.12"
- name: Run hardened source-only units
run: python3 -m tools.community_ci unit
required:
name: Community CPU / Required
if: ${{ !cancelled() && (github.event_name == 'pull_request' || (github.event_name == 'workflow_dispatch' && inputs.source_snapshot != '')) }}
needs:
- authorize
- source-quality
- docs
- ownership-impact
- unit
runs-on: ubuntu-24.04
timeout-minutes: 60
permissions:
actions: read
steps:
- name: Read the promoted Stable CPU result
if: ${{ needs.authorize.outputs.reuse_stable_cpu == 'true' }}
env:
GH_TOKEN: ${{ github.token }}
PR_NUMBER: ${{ needs.authorize.outputs.pr_number }}
HEAD_SHA: ${{ needs.authorize.outputs.head_sha }}
MERGE_SHA: ${{ needs.authorize.outputs.merge_sha }}
run: |
set -euo pipefail
expected_title="Stable Community CI · PR #$PR_NUMBER · head $HEAD_SHA · merge $MERGE_SHA"
# Preserve the legacy PR check consumed by Internal CI, using the
# actual CPU result from the trusted main pipeline. No tests repeat.
for attempt in $(seq 1 110); do
runs="$(gh api "/repos/$GITHUB_REPOSITORY/actions/workflows/community-ci.yml/runs?event=workflow_dispatch&branch=main&per_page=100")"
run_id="$(jq -r --arg title "$expected_title" '
[.workflow_runs[]
| select(.event == "workflow_dispatch" and .head_branch == "main")
| select(.path == ".github/workflows/community-ci.yml" and .display_title == $title)]
| max_by(.id) | .id // empty' <<< "$runs")"
if [[ "$run_id" =~ ^[1-9][0-9]*$ ]]; then
job="$(gh api "/repos/$GITHUB_REPOSITORY/actions/runs/$run_id/jobs?filter=latest&per_page=100" \
--jq '[.jobs[] | select(.name == "Community CPU / Required")] | last // {}')"
if [ "$(jq -r '.status // empty' <<< "$job")" = completed ]; then
test "$(jq -r '.conclusion // empty' <<< "$job")" = success
echo "Stable CPU result: $GITHUB_SERVER_URL/$GITHUB_REPOSITORY/actions/runs/$run_id"
exit 0
fi
fi
if [ "$attempt" -lt 110 ]; then sleep 30; fi
done
echo "::error::The promoted Stable CPU result did not arrive."
exit 1
- name: Require every public CPU stage
if: ${{ needs.authorize.outputs.reuse_stable_cpu != 'true' }}
env:
SOURCE_QUALITY_RESULT: ${{ needs.source-quality.result }}
DOCS_RESULT: ${{ needs.docs.result }}
OWNERSHIP_IMPACT_RESULT: ${{ needs.ownership-impact.result }}
UNIT_RESULT: ${{ needs.unit.result }}
run: |
set -euo pipefail
printf '%s\n' \
"Source quality: $SOURCE_QUALITY_RESULT" \
"Docs: $DOCS_RESULT" \
"Ownership and impact: $OWNERSHIP_IMPACT_RESULT" \
"Unit / C++ and Python: $UNIT_RESULT"
test "$SOURCE_QUALITY_RESULT" = success
test "$DOCS_RESULT" = success
test "$OWNERSHIP_IMPACT_RESULT" = success
test "$UNIT_RESULT" = success
gpu-authorize:
name: Community GPU / Authorize
needs: [authorize, required]
if: ${{ always() && github.event_name == 'workflow_dispatch' && needs.authorize.result == 'success' && needs.required.result == 'success' }}
runs-on: ubuntu-24.04
timeout-minutes: 5
permissions:
contents: read
outputs:
pr_number: ${{ needs.authorize.outputs.pr_number }}
head_sha: ${{ needs.authorize.outputs.head_sha }}
base_sha: ${{ needs.authorize.outputs.base_sha }}
merge_sha: ${{ needs.authorize.outputs.merge_sha }}
families: ${{ steps.impact.outputs.families }}
direct_families: ${{ steps.impact.outputs.direct_families }}
added_families: ${{ steps.impact.outputs.added_families }}
scope: ${{ steps.impact.outputs.scope }}
gpu_enabled: ${{ steps.impact.outputs.gpu_enabled }}
run_gpu: ${{ steps.impact.outputs.run_gpu }}
steps:
# Run only the immutable base's classifier. The PR head is a git diff
# operand, never a checkout or Python import on this orchestration job.
- name: Check out the exact trusted base
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803
with:
ref: ${{ needs.authorize.outputs.base_sha }}
fetch-depth: 0
persist-credentials: false
- name: Fetch the exact pull-request head for diffing only
env:
HEAD_SHA: ${{ needs.authorize.outputs.head_sha }}
run: |
set -euo pipefail
git fetch --no-tags origin "$HEAD_SHA"
- name: Set up Python
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405
with:
python-version: "3.12"
- name: Resolve the changed model families
id: impact
env:
HEAD_SHA: ${{ needs.authorize.outputs.head_sha }}
BASE_SHA: ${{ needs.authorize.outputs.base_sha }}
GPU_EXECUTION_ENABLED: ${{ env.COMMUNITY_GPU_EXECUTION_ENABLED }}
MANUAL_GPU_EXECUTION_ENABLED: ${{ inputs.run_gpu_smoke || false }}
EVENT_NAME: ${{ github.event_name }}
run: |
set -euo pipefail
python3 -m tools.test_impact --base "$BASE_SHA" --head "$HEAD_SHA" \
| tee "$RUNNER_TEMP/trtmc-gpu-impact.json"
python3 - <<'PY'
import json
import os
import re
import subprocess
from pathlib import Path
from pathlib import PurePosixPath
summary = json.loads(
(Path(os.environ["RUNNER_TEMP"]) / "trtmc-gpu-impact.json").read_text()
)
gpu_enabled = os.environ["GPU_EXECUTION_ENABLED"].lower() == "true"
manual_gpu_enabled = (
os.environ["EVENT_NAME"] == "workflow_dispatch"
and os.environ["MANUAL_GPU_EXECUTION_ENABLED"].lower() == "true"
)
gpu_enabled = gpu_enabled or manual_gpu_enabled
added_families = set()
if summary["scope"] == "all":
trusted_families = set(summary["families"])
for changed_path in summary["changed_files"]:
parts = PurePosixPath(changed_path).parts
if len(parts) < 2 or parts[0] != "families":
continue
family = parts[1]
if not re.fullmatch(r"[a-z][a-z0-9_]*", family):
raise SystemExit(f"invalid changed family name: {family!r}")
if family in trusted_families:
continue
model = f"{os.environ['HEAD_SHA']}:families/{family}/model.py"
exists = subprocess.run(
["git", "cat-file", "-e", model],
check=False,
).returncode == 0
if exists:
added_families.add(family)
with open(os.environ["GITHUB_OUTPUT"], "a", encoding="utf-8") as output:
print(
"families="
+ json.dumps(summary["families"], separators=(",", ":")),
file=output,
)
print(
"direct_families="
+ json.dumps(summary["direct_families"], separators=(",", ":")),
file=output,
)
print(
"added_families="
+ json.dumps(sorted(added_families), separators=(",", ":")),
file=output,
)
print("scope=" + summary["scope"], file=output)
print("gpu_enabled=" + str(gpu_enabled).lower(), file=output)
print(
"run_gpu="
+ (
"true"
if gpu_enabled and summary["scope"] in {"all", "families"}
else "false"
),
file=output,
)
PY
# Selection and policy values cross into the secret-bearing job.
- name: Validate the GPU selection
env:
FAMILIES: ${{ steps.impact.outputs.families }}
DIRECT_FAMILIES: ${{ steps.impact.outputs.direct_families }}
ADDED_FAMILIES: ${{ steps.impact.outputs.added_families }}
SCOPE: ${{ steps.impact.outputs.scope }}
GPU_ENABLED: ${{ steps.impact.outputs.gpu_enabled }}
RUN_GPU: ${{ steps.impact.outputs.run_gpu }}
run: |
set -euo pipefail
python3 - <<'PY'
import json
import os
import re
pattern = re.compile(r"^[a-z][a-z0-9_]*$")
families = json.loads(os.environ["FAMILIES"])
direct_families = json.loads(os.environ["DIRECT_FAMILIES"])
added_families = json.loads(os.environ["ADDED_FAMILIES"])
scope = os.environ["SCOPE"]
gpu_enabled = os.environ["GPU_ENABLED"]
run_gpu = os.environ["RUN_GPU"]
if scope not in {"all", "families", "docs", "none"}:
raise SystemExit(f"invalid impact scope: {scope!r}")
if gpu_enabled not in {"true", "false"}:
raise SystemExit(f"invalid GPU execution policy: {gpu_enabled!r}")
if run_gpu not in {"true", "false"}:
raise SystemExit(f"invalid GPU run decision: {run_gpu!r}")
expected_run_gpu = gpu_enabled == "true" and scope in {"all", "families"}
if (run_gpu == "true") != expected_run_gpu:
raise SystemExit("GPU run decision does not match policy and impact")
if not isinstance(families, list) or not all(
isinstance(name, str) and pattern.fullmatch(name) for name in families
):
raise SystemExit("invalid family selection")
if families != sorted(set(families)):
raise SystemExit("family selection must be sorted and unique")
if not isinstance(direct_families, list) or not all(
isinstance(name, str) and pattern.fullmatch(name) for name in direct_families
):
raise SystemExit("invalid direct-family selection")
if direct_families != sorted(set(direct_families)):
raise SystemExit("direct-family selection must be sorted and unique")
if not set(direct_families) <= set(families):
raise SystemExit("direct families must belong to the trusted family inventory")
if not isinstance(added_families, list) or not all(
isinstance(name, str) and pattern.fullmatch(name) for name in added_families
):
raise SystemExit("invalid added-family selection")
if added_families != sorted(set(added_families)):
raise SystemExit("added-family selection must be sorted and unique")
if set(added_families) & set(families):
raise SystemExit("added families must not be in the trusted family inventory")
if scope != "all" and added_families:
raise SystemExit("only all scope may select added families")
if scope == "families" and (not families or direct_families != families):
raise SystemExit("family scope requires its directly changed families")
if scope in {"docs", "none"} and (families or direct_families):
raise SystemExit(f"{scope} scope must not select families")
PY
provision-and-test:
name: Community GPU / Provision and test
needs: gpu-authorize
if: ${{ needs.gpu-authorize.outputs.run_gpu == 'true' }}
runs-on: ubuntu-24.04
environment:
name: gpu-ci-dispatch
deployment: false
# Upper runtime bound for one externally reserved GPU instance. The
# separate cleanup job remains eligible if this job times out.
timeout-minutes: 90
concurrency:
group: trtmc-community-gpu-${{ inputs.ci_lane }}-${{ needs.gpu-authorize.outputs.pr_number }}
cancel-in-progress: true
permissions:
contents: read
outputs:
conclusion: ${{ steps.result.outputs.conclusion }}
steps:
- name: Check out trusted GPU orchestration
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803
with:
ref: ${{ github.sha }}
persist-credentials: false
- name: Install the Brev CLI
env:
BREV_VERSION: 0.6.335
BREV_ARCHIVE_SHA256: 89d778e6f1e5e52495f3e0f10393f1666a1b16d180001b13faec6f906955e6f8
run: |
set -euo pipefail
archive="$RUNNER_TEMP/brev-cli.tar.gz"
curl -fsSL \
"https://github.com/brevdev/brev-cli/releases/download/v${BREV_VERSION}/brev-cli_${BREV_VERSION}_linux_amd64.tar.gz" \
-o "$archive"
echo "$BREV_ARCHIVE_SHA256 $archive" | sha256sum --check --strict
mkdir -p "$HOME/.local/bin"
tar -xzf "$archive" -C "$RUNNER_TEMP" brev
install -m 0755 "$RUNNER_TEMP/brev" "$HOME/.local/bin/brev"
echo "$HOME/.local/bin" >> "$GITHUB_PATH"
- name: Log in to Brev
env:
BREV_API_KEY: ${{ secrets.BREV_API_KEY }}
run: brev login --api-key "$BREV_API_KEY"
- name: Reserve a GPU instance
id: reserve
env:
GPU_TYPE: ${{ vars.TRTMC_COMMUNITY_GPU_TYPE || 'L40' }}
run: |
set -euo pipefail
instance_name="trtmc-gpu-ci-${GITHUB_RUN_ID}-${GITHUB_RUN_ATTEMPT}"
# Publish the deterministic name before creation so a timeout after
# allocation cannot suppress best-effort cleanup.
echo "instance_name=$instance_name" >> "$GITHUB_OUTPUT"
brev create "$instance_name" -g "$GPU_TYPE" --timeout 600
- name: Build the GPU image and validate the exact PR merge
id: test
env:
INSTANCE_NAME: ${{ steps.reserve.outputs.instance_name }}
MERGE_SHA: ${{ needs.gpu-authorize.outputs.merge_sha }}
PR_NUMBER: ${{ needs.gpu-authorize.outputs.pr_number }}
FAMILIES: ${{ needs.gpu-authorize.outputs.families }}
DIRECT_FAMILIES: ${{ needs.gpu-authorize.outputs.direct_families }}
ADDED_FAMILIES: ${{ needs.gpu-authorize.outputs.added_families }}
SCOPE: ${{ needs.gpu-authorize.outputs.scope }}
CUDA_ARCHITECTURES: ${{ vars.TRTMC_COMMUNITY_CUDA_ARCHITECTURES || '89' }}
CI_SHA: ${{ github.sha }}
run: |
set -euo pipefail
brev exec "$INSTANCE_NAME" "git init /tmp/model_connect && cd /tmp/model_connect && git remote add origin https://github.com/$GITHUB_REPOSITORY.git && git fetch --depth 2 origin $MERGE_SHA && test \"\$(git rev-parse FETCH_HEAD)\" = $MERGE_SHA && git checkout --detach $MERGE_SHA"
# The host coordinator and container entrypoint belong to the chosen
# CI commit. Neither imports contributor code outside the container.
[[ "$CI_SHA" =~ ^[0-9a-f]{40}$ ]]
brev exec "$INSTANCE_NAME" "cd /tmp/model_connect && git fetch --depth 1 origin $CI_SHA && test \"\$(git rev-parse FETCH_HEAD)\" = $CI_SHA && git show $CI_SHA:tools/community_gpu_ci.py > /tmp/community_gpu_ci.py"
# A freshly created instance's SSH session may not have its Docker
# group membership yet. The first live GPU run reached this point
# and failed on docker.sock, so do not depend on propagation timing.
brev exec "$INSTANCE_NAME" "cd /tmp/model_connect && sudo docker build -f Dockerfile.dev.x86-gpu -t trtmc-quickstart-gpu requirements"
# Each selected family gets a fresh container on this one VM. The
# coordinator waits for its removal before starting the next family,
# and aggregates failures after all families have been attempted.
# Repository credentials are not forwarded to contributor code.
echo "Directly changed families: $DIRECT_FAMILIES; added families: $ADDED_FAMILIES (scope: $SCOPE)"
python3 -m tools.brev_exec \
--instance "$INSTANCE_NAME" \
--log /tmp/gpu-ci-output.log \
--result-file "/tmp/trtmc-community-gpu-result-${GITHUB_RUN_ID}-${GITHUB_RUN_ATTEMPT}" \
-- sudo env \
"TRTMC_GPU_SCOPE=$SCOPE" \
"TRTMC_GPU_FAMILIES=$FAMILIES" \
"TRTMC_GPU_DIRECT_FAMILIES=$DIRECT_FAMILIES" \
"TRTMC_GPU_ADDED_FAMILIES=$ADDED_FAMILIES" \
"CMAKE_CUDA_ARCHITECTURES=$CUDA_ARCHITECTURES" \
python3 -I /tmp/community_gpu_ci.py --containers --repository /tmp/model_connect
echo "conclusion=success" >> "$GITHUB_OUTPUT"
- name: Record the step conclusion
if: always()
id: result
env:
JOB_STATUS: ${{ job.status }}
TEST_OUTCOME: ${{ steps.test.outcome }}
TEST_CONCLUSION: ${{ steps.test.outputs.conclusion }}
run: |
set -euo pipefail
conclusion=failure
if [ "$JOB_STATUS" = "cancelled" ]; then
conclusion=cancelled
elif [ "$JOB_STATUS" = "success" ] \
&& [ "$TEST_OUTCOME" = "success" ] \
&& [ "$TEST_CONCLUSION" = "success" ]; then
conclusion=success
fi
echo "conclusion=$conclusion" >> "$GITHUB_OUTPUT"
- name: Always tear down the GPU instance
if: ${{ always() && steps.reserve.outputs.instance_name != '' }}
env:
INSTANCE_NAME: ${{ steps.reserve.outputs.instance_name }}
run: brev delete "$INSTANCE_NAME" || true
- name: Upload the smoke-test log
if: always()
continue-on-error: true
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with:
name: gpu-ci-output-${{ needs.gpu-authorize.outputs.pr_number }}
path: /tmp/gpu-ci-output.log
if-no-files-found: ignore
retention-days: 7
cleanup:
name: Community GPU / Cleanup
needs: [gpu-authorize, provision-and-test]
if: >-
${{ always() && needs.gpu-authorize.result == 'success' &&
needs.gpu-authorize.outputs.run_gpu == 'true' }}
runs-on: ubuntu-24.04
environment:
name: gpu-ci-dispatch
deployment: false
timeout-minutes: 10
permissions: {}
steps:
- name: Install the pinned Brev CLI
env:
BREV_VERSION: 0.6.335
BREV_ARCHIVE_SHA256: 89d778e6f1e5e52495f3e0f10393f1666a1b16d180001b13faec6f906955e6f8
run: |
set -euo pipefail
archive="$RUNNER_TEMP/brev-cli.tar.gz"
curl -fsSL \
"https://github.com/brevdev/brev-cli/releases/download/v${BREV_VERSION}/brev-cli_${BREV_VERSION}_linux_amd64.tar.gz" \
-o "$archive"
echo "$BREV_ARCHIVE_SHA256 $archive" | sha256sum --check --strict
mkdir -p "$HOME/.local/bin"
tar -xzf "$archive" -C "$RUNNER_TEMP" brev
install -m 0755 "$RUNNER_TEMP/brev" "$HOME/.local/bin/brev"
echo "$HOME/.local/bin" >> "$GITHUB_PATH"
- name: Log in to Brev
env:
BREV_API_KEY: ${{ secrets.BREV_API_KEY }}
run: brev login --api-key "$BREV_API_KEY"
- name: Delete the deterministic GPU instance
run: brev delete "trtmc-gpu-ci-${GITHUB_RUN_ID}-${GITHUB_RUN_ATTEMPT}" || true
publish:
name: ${{ github.event_name == 'pull_request' && 'Stable Community CI' || inputs.ci_lane == 'dev' && 'Dev Community CI' || 'Stable Community CI' }} result
needs: [authorize, required, gpu-authorize, provision-and-test, cleanup]
if: ${{ always() && needs.authorize.outputs.enabled == 'true' }}
runs-on: ubuntu-24.04
timeout-minutes: 5
permissions: {}
steps:
- name: Require the complete Community CI result
env:
EVENT_NAME: ${{ github.event_name }}
RUN_GPU: ${{ needs.gpu-authorize.outputs.run_gpu }}
AUTHORIZED_RESULT: ${{ needs.authorize.result }}
CPU_RESULT: ${{ needs.required.result }}
GPU_AUTHORIZED: ${{ needs.gpu-authorize.result }}
GPU_RESULT: ${{ needs.provision-and-test.result }}
TEST_RESULT: ${{ needs.provision-and-test.outputs.conclusion }}
CLEANUP_RESULT: ${{ needs.cleanup.result }}
run: |
set -euo pipefail
test "$AUTHORIZED_RESULT" = success
test "$CPU_RESULT" = success
if [ "$EVENT_NAME" = pull_request ]; then
# Preserve the existing PR CPU contract during Dev qualification.
exit 0
fi
test "$GPU_AUTHORIZED" = success
if [ "$RUN_GPU" = false ]; then
test "$GPU_RESULT" = skipped
test "$CLEANUP_RESULT" = skipped
else
test "$RUN_GPU" = true
test "$GPU_RESULT" = success
test "$TEST_RESULT" = success
test "$CLEANUP_RESULT" = success
fi