Dev Community CI · PR #1379 · head 51c96af9f2127e4ea4bfa72414495c3a6c46102e · merge a11eab5af98bd33b03b79651f0667329871809d1 #585
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # 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 |