diff --git a/.github/workflows/pr-test.yml b/.github/workflows/pr-test.yml index 88036d7a70..7804e814d9 100644 --- a/.github/workflows/pr-test.yml +++ b/.github/workflows/pr-test.yml @@ -71,7 +71,6 @@ jobs: run: python -m tests.ci.ci_policy docker-paths: - if: github.event_name == 'pull_request' runs-on: ubuntu-latest outputs: changed: ${{ steps.diff.outputs.changed }} @@ -81,10 +80,17 @@ jobs: persist-credentials: false - id: diff shell: bash + env: + BASE_SHA: ${{ github.event.pull_request.base.sha || '' }} run: | - git fetch --depth 1 origin "${{ github.event.pull_request.base.sha }}" + # Cron and manual runs have no PR diff and never build a PR image. + if [ -z "$BASE_SHA" ]; then + echo "changed=false" >> "$GITHUB_OUTPUT" + exit 0 + fi + git fetch --depth 1 origin "$BASE_SHA" # Dockerfile.rocm doesn't feed the cu13 multi-arch build this pipeline produces. - if git diff --name-only "${{ github.event.pull_request.base.sha }}" HEAD \ + if git diff --name-only "$BASE_SHA" HEAD \ | grep -qE '^(docker/(Dockerfile|build\.py|verify_transformer_engine\.py)|requirements\.txt)$|^docker/patch/'; then echo "changed=true" >> "$GITHUB_OUTPUT" else @@ -92,37 +98,55 @@ jobs: fi # Docker PRs: build the multi-arch image first and run every suite inside it. + # Always runs: with nothing to build every step is a no-op and it stays off the + # GPU fleet. docker-build: needs: [docker-paths] - if: needs.docker-paths.outputs.changed == 'true' && github.event.pull_request.head.repo.full_name == github.repository - runs-on: ["h200", "2gpu"] + if: always() && !cancelled() + runs-on: ${{ fromJSON((needs.docker-paths.outputs.changed == 'true' && github.event.pull_request.head.repo.full_name == github.repository) && '["h200", "2gpu"]' || '["ubuntu-latest"]') }} timeout-minutes: 180 + outputs: + built: ${{ steps.build.outputs.built || 'false' }} + env: + # Forks cannot push to the registry, so they keep the released image. + BUILD: ${{ needs.docker-paths.outputs.changed == 'true' && github.event.pull_request.head.repo.full_name == github.repository }} steps: - name: Checkout repository + if: env.BUILD == 'true' uses: actions/checkout@v4 - name: Set up Docker Buildx + if: env.BUILD == 'true' uses: docker/setup-buildx-action@v3 with: driver-opts: | image=moby/buildkit:latest network=host - name: Install Python + dependencies + if: env.BUILD == 'true' run: | apt-get update && apt-get install -y python3 python3-pip pip3 install --break-system-packages typer - name: Login to Docker Hub + if: env.BUILD == 'true' uses: docker/login-action@v3 with: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} - name: Build and push PR tag + id: build + if: env.BUILD == 'true' run: | python3 docker/build.py --variant cu13 --image-tag custom \ --custom-tag pr-${{ github.event.pull_request.number }} --push + echo "built=true" >> "$GITHUB_OUTPUT" + - name: Nothing to build + if: env.BUILD != 'true' + run: echo "No docker-relevant changes; suites use the released image." + # No `if:` guard: a real build failure must stop the suites rather than run them + # against a stale image. resolve-ci-image: - needs: [docker-paths, docker-build] - if: always() && !cancelled() && (needs.docker-build.result == 'success' || needs.docker-build.result == 'skipped') + needs: [docker-build] runs-on: ubuntu-latest outputs: container_image: ${{ steps.resolve.outputs.container_image }} @@ -133,7 +157,7 @@ jobs: env: PR_BODY: ${{ github.event.pull_request.body || '' }} INPUT_CI_IMAGE_TAG: ${{ github.event.inputs.ci_image_tag || '' }} - DOCKER_BUILT: ${{ needs.docker-build.result == 'success' }} + DOCKER_BUILT: ${{ needs.docker-build.outputs.built }} PR_NUMBER: ${{ github.event.pull_request.number || '' }} run: | CI_IMAGE_TAG="${INPUT_CI_IMAGE_TAG}"