diff --git a/.github/actions/workflow-build/action.yml b/.github/actions/workflow-build/action.yml index 19c25d55e1..92bb8a6f43 100644 --- a/.github/actions/workflow-build/action.yml +++ b/.github/actions/workflow-build/action.yml @@ -32,11 +32,8 @@ inputs: outputs: workflow: - description: "The dispatchable workflow matrix" - value: ${{ steps.build-workflow.outputs.workflow }} - workflow_keys: - description: "The keys of the parsed workflow" - value: ${{ steps.build-workflow.outputs.workflow_keys }} + description: "The dispatchable workflows" + value: ${{ steps.outputs.outputs.WORKFLOW }} runs: using: "composite" @@ -85,13 +82,21 @@ runs: cat workflow/job_list.txt echo "::endgroup::" - echo "Setting outputs..." - echo "::group::GHA Output: WORKFLOW" - printf "WORKFLOW=%s\n" "$(cat workflow/workflow.json | jq -c '.')" | tee -a "${GITHUB_OUTPUT}" + - name: Create dispatch workflows + shell: bash --noprofile --norc -euo pipefail {0} + run: | + "${GITHUB_ACTION_PATH}/prepare-workflow-dispatch.py" workflow/workflow.json + + echo "::group::Dispatch Workflows" + cat workflow/dispatch.json echo "::endgroup::" - echo "::group::GHA Output: WORKFLOW_KEYS" - printf "WORKFLOW_KEYS=%s\n" "$(cat workflow/workflow_keys.json | jq -c '.')" | tee -a "${GITHUB_OUTPUT}" + - name: Set outputs + id: outputs + shell: bash --noprofile --norc -euo pipefail {0} + run: | + echo "::group::GHA Output: WORKFLOW" + printf "WORKFLOW=%s\n" "$(cat workflow/dispatch.json | jq -c '.')" | tee -a "${GITHUB_OUTPUT}" echo "::endgroup::" - name: Upload artifacts diff --git a/.github/actions/workflow-build/prepare-workflow-dispatch.py b/.github/actions/workflow-build/prepare-workflow-dispatch.py new file mode 100755 index 0000000000..fbdde69101 --- /dev/null +++ b/.github/actions/workflow-build/prepare-workflow-dispatch.py @@ -0,0 +1,95 @@ +#!/usr/bin/env python3 + +""" +This script prepares a full workflow for GHA dispatch. + +To avoid skipped jobs from cluttering the GHA UI, this script splits the full workflow.json into multiple workflows +that don't require large numbers of skipped jobs in the workflow implementation. +""" + +import argparse +import json +import os +import sys + + +def write_json_file(filename, json_object): + with open(filename, 'w') as f: + json.dump(json_object, f, indent=2) + + +def is_windows(job): + return job['runner'].startswith('windows') + + +def split_workflow(workflow): + linux_standalone = {} + linux_two_stage = {} + windows_standalone = {} + windows_two_stage = {} + + def strip_extra_info(job): + del job['origin'] + + for group_name, group_json in workflow.items(): + standalone = group_json['standalone'] if 'standalone' in group_json else [] + two_stage = group_json['two_stage'] if 'two_stage' in group_json else [] + + if len(standalone) > 0: + for job in standalone: + strip_extra_info(job) + + if is_windows(standalone[0]): + windows_standalone[group_name] = standalone + else: + linux_standalone[group_name] = standalone + + if len(two_stage) > 0: + for ts in two_stage: + for job in ts['producers']: + strip_extra_info(job) + for job in ts['consumers']: + strip_extra_info(job) + + if is_windows(two_stage[0]['producers'][0]): + windows_two_stage[group_name] = two_stage + else: + linux_two_stage[group_name] = two_stage + + dispatch = { + 'linux_standalone': { + 'keys': list(linux_standalone.keys()), + 'jobs': linux_standalone}, + 'linux_two_stage': { + 'keys': list(linux_two_stage.keys()), + 'jobs': linux_two_stage}, + 'windows_standalone': { + 'keys': list(windows_standalone.keys()), + 'jobs': windows_standalone}, + 'windows_two_stage': { + 'keys': list(windows_two_stage.keys()), + 'jobs': windows_two_stage} + } + + os.makedirs('workflow', exist_ok=True) + write_json_file('workflow/dispatch.json', dispatch) + + +def main(): + parser = argparse.ArgumentParser(description='Prepare a full workflow for GHA dispatch.') + parser.add_argument('workflow_json', help='Path to the full workflow.json file') + args = parser.parse_args() + + # Check if the workflow file exists + if not os.path.isfile(args.workflow_json): + print(f"Error: Matrix file '{args.workflow_json}' not found.") + sys.exit(1) + + with open(args.workflow_json) as f: + workflow = json.load(f) + + split_workflow(workflow) + + +if __name__ == '__main__': + main() diff --git a/.github/actions/workflow-results/parse-job-times.py b/.github/actions/workflow-results/parse-job-times.py index 79723bf308..b90bd227a3 100755 --- a/.github/actions/workflow-results/parse-job-times.py +++ b/.github/actions/workflow-results/parse-job-times.py @@ -32,17 +32,14 @@ def generate_job_id_map(workflow): for group_name, group_json in workflow.items(): standalone = group_json['standalone'] if 'standalone' in group_json else [] for job in standalone: - name = f"{group_name} / s.{job['id']} / {job['name']}" + name = f"{group_name} / {job['name']}" job_id_map[name] = job['id'] two_stage = group_json['two_stage'] if 'two_stage' in group_json else [] for pc in two_stage: producers = pc['producers'] - for job in producers: - name = f"{group_name} / t.{pc['id']} / p.{job['id']} / {job['name']}" - job_id_map[name] = job['id'] consumers = pc['consumers'] - for job in consumers: - name = f"{group_name} / t.{pc['id']} / c.{job['id']} / {job['name']}" + for job in producers + consumers: + name = f"{group_name} / {pc['id']} / {job['name']}" job_id_map[name] = job['id'] return job_id_map diff --git a/.github/actions/workflow-run-job-linux/action.yml b/.github/actions/workflow-run-job-linux/action.yml new file mode 100644 index 0000000000..d10d1dca2e --- /dev/null +++ b/.github/actions/workflow-run-job-linux/action.yml @@ -0,0 +1,197 @@ +name: "Run Linux Job" +description: "Run a job on a Linux runner." + +# This job now uses a docker-outside-of-docker (DOOD) strategy. +# +# The GitHub Actions runner application mounts the host's docker socket `/var/run/docker.sock` into the +# container. By using a container with the `docker` CLI, this container can launch docker containers +# using the host's docker daemon. +# +# This allows us to run actions that require node v20 in the `cruizba/ubuntu-dind:jammy-26.1.3` container, and +# then launch our Ubuntu18.04-based GCC 6/7 containers to build and test CCCL. +# +# The main inconvenience to this approach is that any container mounts have to match the paths of the runner host, +# not the paths as seen in the intermediate (`cruizba/ubuntu-dind`) container. +# +# Note: I am using `cruizba/ubuntu-dind:jammy-26.1.3` instead of `docker:latest`, because GitHub doesn't support +# JS actions in alpine aarch64 containers, instead failing actions with this error: +# ``` +# Error: JavaScript Actions in Alpine containers are only supported on x64 Linux runners. Detected Linux Arm64 +# ``` + +inputs: + id: + description: "A unique identifier." + required: true + command: + description: "The command to run." + required: true + image: + description: "The Docker image to use." + required: true + runner: + description: "The GHA runs-on value." + required: true + cuda: + description: "The CUDA version to use when selecting a devcontainer." + required: true + host: + description: "The host compiler to use when selecting a devcontainer." + required: true + +runs: + using: "composite" + steps: + - name: Install dependencies + shell: sh + run: | + # Install script dependencies + apt update + apt install -y --no-install-recommends tree git + - name: Checkout repo + uses: actions/checkout@v4 + with: + path: ${{github.event.repository.name}} + persist-credentials: false + - name: Add NVCC problem matcher + shell: bash --noprofile --norc -euo pipefail {0} + run: | + echo "::add-matcher::${{github.event.repository.name}}/.github/problem-matchers/problem-matcher.json" + - name: Get AWS credentials for sccache bucket + uses: aws-actions/configure-aws-credentials@v4 + with: + role-to-assume: arn:aws:iam::279114543810:role/gha-oidc-NVIDIA + aws-region: us-east-2 + role-duration-seconds: 43200 # 12 hours + - name: Run command # Do not change this step's name, it is checked in parse-job-times.py + shell: bash --noprofile --norc -euo pipefail {0} + env: + CI: true + RUNNER: "${{inputs.runner}}" + # Dereferencing the command from an env var instead of a GHA input avoids issues with escaping + # semicolons and other special characters (e.g. `-arch "60;70;80"`). + COMMAND: "${{inputs.command}}" + AWS_ACCESS_KEY_ID: "${{env.AWS_ACCESS_KEY_ID}}" + AWS_SESSION_TOKEN: "${{env.AWS_SESSION_TOKEN}}" + AWS_SECRET_ACCESS_KEY: "${{env.AWS_SECRET_ACCESS_KEY}}" + run: | + echo "[host] github.workspace: ${{github.workspace}}" + echo "[container] GITHUB_WORKSPACE: ${GITHUB_WORKSPACE:-}" + echo "[container] PWD: $(pwd)" + + # Necessary because we're doing docker-outside-of-docker: + # Make a symlink in the container that matches the host's ${{github.workspace}}, so that way `$(pwd)` + # in `.devcontainer/launch.sh` constructs volume paths relative to the hosts's ${{github.workspace}}. + mkdir -p "$(dirname "${{github.workspace}}")" + ln -s "$(pwd)" "${{github.workspace}}" + cd "${{github.workspace}}" + + cat <<"EOF" > ci.sh + #! /usr/bin/env bash + set -eo pipefail + echo -e "\e[1;34mRunning as '$(whoami)' user in $(pwd):\e[0m" + echo -e "\e[1;34m${COMMAND}\e[0m" + eval "${COMMAND}" || exit_code=$? + if [ ! -z "$exit_code" ]; then + echo -e "::group::️❗ \e[1;31mInstructions to Reproduce CI Failure Locally\e[0m" + echo "::error:: To replicate this failure locally, follow the steps below:" + echo "1. Clone the repository, and navigate to the correct branch and commit:" + echo " git clone --branch $GITHUB_REF_NAME --single-branch https://github.com/$GITHUB_REPOSITORY.git && cd $(echo $GITHUB_REPOSITORY | cut -d'/' -f2) && git checkout $GITHUB_SHA" + echo "" + echo "2. Run the failed command inside the same Docker container used by this CI job:" + echo " .devcontainer/launch.sh -d -c ${{inputs.cuda}} -H ${{inputs.host}} -- ${COMMAND}" + echo "" + echo "For additional information, see:" + echo " - DevContainer Documentation: https://github.com/NVIDIA/cccl/blob/main/.devcontainer/README.md" + echo " - Continuous Integration (CI) Overview: https://github.com/NVIDIA/cccl/blob/main/ci-overview.md" + exit $exit_code + fi + EOF + + chmod +x ci.sh + + mkdir "$RUNNER_TEMP/.aws"; + + cat < "$RUNNER_TEMP/.aws/config" + [default] + bucket=rapids-sccache-devs + region=us-east-2 + EOF + + cat < "$RUNNER_TEMP/.aws/credentials" + [default] + aws_access_key_id=$AWS_ACCESS_KEY_ID + aws_session_token=$AWS_SESSION_TOKEN + aws_secret_access_key=$AWS_SECRET_ACCESS_KEY + EOF + + chmod 0600 "$RUNNER_TEMP/.aws/credentials" + chmod 0664 "$RUNNER_TEMP/.aws/config" + + declare -a gpu_request=() + + # Explicitly pass which GPU to use if on a GPU runner + if [[ "${RUNNER}" = *"-gpu-"* ]]; then + gpu_request+=(--gpus "device=${NVIDIA_VISIBLE_DEVICES}") + fi + + host_path() { + sed "s@/__w@$(dirname "$(dirname "${{github.workspace}}")")@" <<< "$1" + } + + # Launch this container using the host's docker daemon + set -x + ${{github.event.repository.name}}/.devcontainer/launch.sh \ + --docker \ + --cuda ${{inputs.cuda}} \ + --host ${{inputs.host}} \ + "${gpu_request[@]}" \ + --env "CI=$CI" \ + --env "VAULT_HOST=" \ + --env "COMMAND=$COMMAND" \ + --env "GITHUB_ENV=$GITHUB_ENV" \ + --env "GITHUB_SHA=$GITHUB_SHA" \ + --env "GITHUB_PATH=$GITHUB_PATH" \ + --env "GITHUB_OUTPUT=$GITHUB_OUTPUT" \ + --env "GITHUB_ACTIONS=$GITHUB_ACTIONS" \ + --env "GITHUB_REF_NAME=$GITHUB_REF_NAME" \ + --env "GITHUB_WORKSPACE=$GITHUB_WORKSPACE" \ + --env "GITHUB_REPOSITORY=$GITHUB_REPOSITORY" \ + --env "GITHUB_STEP_SUMMARY=$GITHUB_STEP_SUMMARY" \ + --volume "${{github.workspace}}/ci.sh:/ci.sh" \ + --volume "$(host_path "$RUNNER_TEMP")/.aws:/root/.aws" \ + --volume "$(dirname "$(dirname "${{github.workspace}}")"):/__w" \ + -- /ci.sh + + - name: Prepare job artifacts + shell: bash --noprofile --norc -euo pipefail {0} + run: | + echo "Prepare job artifacts" + result_dir="jobs/${{inputs.id}}" + mkdir -p "$result_dir" + + touch "$result_dir/success" + + # Finds a matching file in the repo directory and copies it to the results directory. + find_and_copy() { + filename="$1" + filepath="$(find ${{github.event.repository.name}} -name "${filename}" -print -quit)" + if [[ -z "$filepath" ]]; then + echo "${filename} does not exist in repo directory." + return 1 + fi + cp -v "$filepath" "$result_dir" + } + + find_and_copy "sccache_stats.json" || true # Ignore failures + + echo "::group::Job artifacts" + tree "$result_dir" + echo "::endgroup::" + + - name: Upload job artifacts + uses: actions/upload-artifact@v4 + with: + name: jobs-${{inputs.id}} + path: jobs + compression-level: 0 diff --git a/.github/actions/workflow-run-job-windows/action.yml b/.github/actions/workflow-run-job-windows/action.yml new file mode 100644 index 0000000000..805beff344 --- /dev/null +++ b/.github/actions/workflow-run-job-windows/action.yml @@ -0,0 +1,98 @@ +name: "Run Windows Job" +description: "Run a job on a Windows runner." + +inputs: + id: + description: "A unique identifier." + required: true + command: + description: "The command to run." + required: true + image: + description: "The Docker image to use." + required: true + +runs: + using: "composite" + steps: + - name: Configure env + shell: bash --noprofile --norc -euo pipefail {0} + run: | + echo "SCCACHE_BUCKET=rapids-sccache-devs" | tee -a "${GITHUB_ENV}" + echo "SCCACHE_REGION=us-east-2" | tee -a "${GITHUB_ENV}" + echo "SCCACHE_IDLE_TIMEOUT=0" | tee -a "${GITHUB_ENV}" + echo "SCCACHE_S3_USE_SSL=true" | tee -a "${GITHUB_ENV}" + echo "SCCACHE_S3_NO_CREDENTIALS=false" | tee -a "${GITHUB_ENV}" + - name: Get AWS credentials for sccache bucket + uses: aws-actions/configure-aws-credentials@v4 + with: + role-to-assume: arn:aws:iam::279114543810:role/gha-oidc-NVIDIA + aws-region: us-east-2 + role-duration-seconds: 43200 # 12 hours + - name: Checkout repo + uses: actions/checkout@v4 + with: + path: ${{github.event.repository.name}} + persist-credentials: false + - name: Fetch ${{ inputs.image }} + shell: bash --noprofile --norc -euo pipefail {0} + run: docker pull ${{ inputs.image }} + - name: Prepare paths for docker + shell: powershell + id: paths + run: | + echo "HOST_REPO=${{ github.workspace }}\${{ github.event.repository.name }}".Replace('\', '/') | Out-File -FilePath $env:GITHUB_OUTPUT -Append + echo "MOUNT_REPO=C:/${{ github.event.repository.name }}" | Out-File -FilePath $env:GITHUB_OUTPUT -Append + cat $env:GITHUB_OUTPUT + - name: Run command # Do not change this step's name, it is checked in parse-job-times.py + shell: bash --noprofile --norc -euo pipefail {0} + run: | + docker run \ + --mount type=bind,source="${{steps.paths.outputs.HOST_REPO}}",target="${{steps.paths.outputs.MOUNT_REPO}}" \ + --workdir "${{steps.paths.outputs.MOUNT_REPO}}" \ + ${{ inputs.image }} \ + powershell -c " + [System.Environment]::SetEnvironmentVariable('AWS_ACCESS_KEY_ID','${{env.AWS_ACCESS_KEY_ID}}'); + [System.Environment]::SetEnvironmentVariable('AWS_SECRET_ACCESS_KEY','${{env.AWS_SECRET_ACCESS_KEY}}'); + [System.Environment]::SetEnvironmentVariable('AWS_SESSION_TOKEN','${{env.AWS_SESSION_TOKEN }}'); + [System.Environment]::SetEnvironmentVariable('SCCACHE_BUCKET','${{env.SCCACHE_BUCKET}}'); + [System.Environment]::SetEnvironmentVariable('SCCACHE_REGION','${{env.SCCACHE_REGION}}'); + [System.Environment]::SetEnvironmentVariable('SCCACHE_IDLE_TIMEOUT','${{env.SCCACHE_IDLE_TIMEOUT}}'); + [System.Environment]::SetEnvironmentVariable('SCCACHE_S3_USE_SSL','${{env.SCCACHE_S3_USE_SSL}}'); + [System.Environment]::SetEnvironmentVariable('SCCACHE_S3_NO_CREDENTIALS','${{env.SCCACHE_S3_NO_CREDENTIALS}}'); + git config --global --add safe.directory '${{steps.paths.outputs.MOUNT_REPO}}'; + ${{inputs.command}}" + - name: Prepare job artifacts + shell: bash --noprofile --norc -euo pipefail {0} + id: done + run: | + echo "SUCCESS=true" | tee -a "${GITHUB_OUTPUT}" + + result_dir="jobs/${{inputs.id}}" + mkdir -p "$result_dir" + + touch "$result_dir/success" + + # Finds a matching file in the repo directory and copies it to the results directory. + find_and_copy() { + filename="$1" + filepath="$(find ${{github.event.repository.name}} -name "${filename}" -print -quit)" + if [[ -z "$filepath" ]]; then + echo "${filename} does not exist in repo directory." + return 1 + fi + cp -v "$filepath" "$result_dir" + } + + find_and_copy "sccache_stats.json" || true # Ignore failures + + echo "::group::Job artifacts" + find "$result_dir" # Tree not available in this image. + echo "::endgroup::" + + - name: Upload job artifacts + uses: actions/upload-artifact@v4 + with: + name: jobs-${{inputs.id}} + path: jobs + compression-level: 0 diff --git a/.github/workflows/ci-workflow-nightly.yml b/.github/workflows/ci-workflow-nightly.yml index 79167de80a..c9ff468990 100644 --- a/.github/workflows/ci-workflow-nightly.yml +++ b/.github/workflows/ci-workflow-nightly.yml @@ -36,7 +36,6 @@ jobs: contents: read outputs: workflow: ${{ steps.build-workflow.outputs.workflow }} - workflow_keys: ${{ steps.build-workflow.outputs.workflow_keys }} steps: - name: Checkout repo uses: actions/checkout@v4 @@ -48,8 +47,9 @@ jobs: with: workflows: nightly - run-workflow: + dispatch-groups-linux-two-stage: name: ${{ matrix.name }} + if: ${{ toJSON(fromJSON(needs.build-workflow.outputs.workflow)['linux_two_stage']['keys']) != '[]' }} needs: build-workflow permissions: id-token: write @@ -57,20 +57,65 @@ jobs: strategy: fail-fast: false matrix: - name: ${{ fromJSON(needs.build-workflow.outputs.workflow_keys) }} - uses: ./.github/workflows/workflow-dispatch.yml + name: ${{ fromJSON(needs.build-workflow.outputs.workflow)['linux_two_stage']['keys'] }} + uses: ./.github/workflows/workflow-dispatch-two-stage-group-linux.yml with: - name: ${{ matrix.name }} - jobs: ${{ toJSON(fromJSON(needs.build-workflow.outputs.workflow)[matrix.name]) }} + pc-array: ${{ toJSON(fromJSON(needs.build-workflow.outputs.workflow)['linux_two_stage']['jobs'][matrix.name]) }} + + dispatch-groups-windows-two-stage: + name: ${{ matrix.name }} + if: ${{ toJSON(fromJSON(needs.build-workflow.outputs.workflow)['windows_two_stage']['keys']) != '[]' }} + needs: build-workflow + permissions: + id-token: write + contents: read + strategy: + fail-fast: false + matrix: + name: ${{ fromJSON(needs.build-workflow.outputs.workflow)['windows_two_stage']['keys'] }} + uses: ./.github/workflows/workflow-dispatch-two-stage-group-windows.yml + with: + pc-array: ${{ toJSON(fromJSON(needs.build-workflow.outputs.workflow)['windows_two_stage']['jobs'][matrix.name]) }} + + dispatch-groups-linux-standalone: + name: ${{ matrix.name }} + if: ${{ toJSON(fromJSON(needs.build-workflow.outputs.workflow)['linux_standalone']['keys']) != '[]' }} + needs: build-workflow + permissions: + id-token: write + contents: read + strategy: + fail-fast: false + matrix: + name: ${{ fromJSON(needs.build-workflow.outputs.workflow)['linux_standalone']['keys'] }} + uses: ./.github/workflows/workflow-dispatch-standalone-group-linux.yml + with: + job-array: ${{ toJSON(fromJSON(needs.build-workflow.outputs.workflow)['linux_standalone']['jobs'][matrix.name]) }} + + dispatch-groups-windows-standalone: + name: ${{ matrix.name }} + if: ${{ toJSON(fromJSON(needs.build-workflow.outputs.workflow)['windows_standalone']['keys']) != '[]' }} + needs: build-workflow + permissions: + id-token: write + contents: read + strategy: + fail-fast: false + matrix: + name: ${{ fromJSON(needs.build-workflow.outputs.workflow)['windows_standalone']['keys'] }} + uses: ./.github/workflows/workflow-dispatch-standalone-group-windows.yml + with: + job-array: ${{ toJSON(fromJSON(needs.build-workflow.outputs.workflow)['windows_standalone']['jobs'][matrix.name]) }} - # This job acts as a sentry and will fail if any leaf job in the workflow tree fails, as - # run-workflow always succeeds. Use this job when checking for successful matrix workflow job completion. verify-workflow: name: Verify and summarize workflow results if: ${{ always() && !cancelled() }} needs: - build-workflow - - run-workflow + - dispatch-groups-linux-two-stage + - dispatch-groups-windows-two-stage + - dispatch-groups-linux-standalone + - dispatch-groups-windows-standalone permissions: contents: read runs-on: ubuntu-latest diff --git a/.github/workflows/ci-workflow-pull-request.yml b/.github/workflows/ci-workflow-pull-request.yml index c388a96784..cd838f6090 100644 --- a/.github/workflows/ci-workflow-pull-request.yml +++ b/.github/workflows/ci-workflow-pull-request.yml @@ -39,7 +39,6 @@ jobs: pull-requests: read outputs: workflow: ${{ steps.build-workflow.outputs.workflow }} - workflow_keys: ${{ steps.build-workflow.outputs.workflow_keys }} steps: - name: Checkout repo uses: actions/checkout@v4 @@ -63,8 +62,9 @@ jobs: ${{ env.pr_worflow }} ${{ env.nightly_workflow }} - run-workflow: + dispatch-groups-linux-two-stage: name: ${{ matrix.name }} + if: ${{ toJSON(fromJSON(needs.build-workflow.outputs.workflow)['linux_two_stage']['keys']) != '[]' }} needs: build-workflow permissions: id-token: write @@ -72,20 +72,65 @@ jobs: strategy: fail-fast: false matrix: - name: ${{ fromJSON(needs.build-workflow.outputs.workflow_keys) }} - uses: ./.github/workflows/workflow-dispatch.yml + name: ${{ fromJSON(needs.build-workflow.outputs.workflow)['linux_two_stage']['keys'] }} + uses: ./.github/workflows/workflow-dispatch-two-stage-group-linux.yml with: - name: ${{ matrix.name }} - jobs: ${{ toJSON(fromJSON(needs.build-workflow.outputs.workflow)[matrix.name]) }} + pc-array: ${{ toJSON(fromJSON(needs.build-workflow.outputs.workflow)['linux_two_stage']['jobs'][matrix.name]) }} + + dispatch-groups-windows-two-stage: + name: ${{ matrix.name }} + if: ${{ toJSON(fromJSON(needs.build-workflow.outputs.workflow)['windows_two_stage']['keys']) != '[]' }} + needs: build-workflow + permissions: + id-token: write + contents: read + strategy: + fail-fast: false + matrix: + name: ${{ fromJSON(needs.build-workflow.outputs.workflow)['windows_two_stage']['keys'] }} + uses: ./.github/workflows/workflow-dispatch-two-stage-group-windows.yml + with: + pc-array: ${{ toJSON(fromJSON(needs.build-workflow.outputs.workflow)['windows_two_stage']['jobs'][matrix.name]) }} + + dispatch-groups-linux-standalone: + name: ${{ matrix.name }} + if: ${{ toJSON(fromJSON(needs.build-workflow.outputs.workflow)['linux_standalone']['keys']) != '[]' }} + needs: build-workflow + permissions: + id-token: write + contents: read + strategy: + fail-fast: false + matrix: + name: ${{ fromJSON(needs.build-workflow.outputs.workflow)['linux_standalone']['keys'] }} + uses: ./.github/workflows/workflow-dispatch-standalone-group-linux.yml + with: + job-array: ${{ toJSON(fromJSON(needs.build-workflow.outputs.workflow)['linux_standalone']['jobs'][matrix.name]) }} + + dispatch-groups-windows-standalone: + name: ${{ matrix.name }} + if: ${{ toJSON(fromJSON(needs.build-workflow.outputs.workflow)['windows_standalone']['keys']) != '[]' }} + needs: build-workflow + permissions: + id-token: write + contents: read + strategy: + fail-fast: false + matrix: + name: ${{ fromJSON(needs.build-workflow.outputs.workflow)['windows_standalone']['keys'] }} + uses: ./.github/workflows/workflow-dispatch-standalone-group-windows.yml + with: + job-array: ${{ toJSON(fromJSON(needs.build-workflow.outputs.workflow)['windows_standalone']['jobs'][matrix.name]) }} - # This job acts as a sentry and will fail if any leaf job in the workflow tree fails, as - # run-workflow always succeeds. Use this job when checking for successful matrix workflow job completion. verify-workflow: name: Verify and summarize workflow results if: ${{ always() && !cancelled() }} needs: - build-workflow - - run-workflow + - dispatch-groups-linux-two-stage + - dispatch-groups-windows-two-stage + - dispatch-groups-linux-standalone + - dispatch-groups-windows-standalone permissions: contents: read pull-requests: write # Posts a comment back to the PR. @@ -109,6 +154,7 @@ jobs: verify-devcontainers: name: Verify Dev Containers + if: ${{ !contains(github.event.head_commit.message, '[skip-vdc]') }} permissions: id-token: write contents: read diff --git a/.github/workflows/verify-devcontainers.yml b/.github/workflows/verify-devcontainers.yml index d33be23f99..5c69d0f86f 100644 --- a/.github/workflows/verify-devcontainers.yml +++ b/.github/workflows/verify-devcontainers.yml @@ -72,7 +72,7 @@ jobs: with: role-to-assume: arn:aws:iam::279114543810:role/gha-oidc-NVIDIA aws-region: us-east-2 - role-duration-seconds: 43200 # 12 hours) + role-duration-seconds: 43200 # 12 hours - name: Set environment variables run: | echo "SCCACHE_BUCKET=rapids-sccache-devs" >> $GITHUB_ENV diff --git a/.github/workflows/workflow-dispatch-job.yml b/.github/workflows/workflow-dispatch-job.yml deleted file mode 100644 index 2bd0db5473..0000000000 --- a/.github/workflows/workflow-dispatch-job.yml +++ /dev/null @@ -1,295 +0,0 @@ -name: "Workflow/Dispatch/Job" - -defaults: - run: - shell: bash --noprofile --norc -euo pipefail {0} - -on: - workflow_call: - outputs: - success: - value: ${{ contains(toJSON(jobs.*.outputs.success), 'true') }} - inputs: - cuda: {type: string, required: true} - host: {type: string, required: true} - name: {type: string, required: true} - image: {type: string, required: true} - runner: {type: string, required: true} - command: {type: string, required: true} - id: {type: string, required: true} - env: {type: string, required: false} - -permissions: - contents: read - -jobs: - linux: - name: ${{inputs.name}} - if: ${{ startsWith(inputs.runner, 'linux') }} - outputs: - success: ${{ steps.done.outputs.SUCCESS }} - permissions: - id-token: write - contents: read - runs-on: ${{inputs.runner}} - container: - # This job now uses a docker-outside-of-docker (DOOD) strategy. - # - # The GitHub Actions runner application mounts the host's docker socket `/var/run/docker.sock` into the - # container. By using a container with the `docker` CLI, this container can launch docker containers - # using the host's docker daemon. - # - # This allows us to run actions that require node v20 in the `cruizba/ubuntu-dind:jammy-26.1.3` container, and - # then launch our Ubuntu18.04-based GCC 6/7 containers to build and test CCCL. - # - # The main inconvenience to this approach is that any container mounts have to match the paths of the runner host, - # not the paths as seen in the intermediate (`cruizba/ubuntu-dind`) container. - # - # Note: I am using `cruizba/ubuntu-dind:jammy-26.1.3` instead of `docker:latest`, because GitHub doesn't support - # JS actions in alpine aarch64 containers, instead failing actions with this error: - # ``` - # Error: JavaScript Actions in Alpine containers are only supported on x64 Linux runners. Detected Linux Arm64 - # ``` - image: cruizba/ubuntu-dind:jammy-26.1.3 - env: - NVIDIA_VISIBLE_DEVICES: ${{env.NVIDIA_VISIBLE_DEVICES}} - steps: - - name: Install dependencies - shell: sh - run: | - # Install script dependencies - apt update - apt install -y --no-install-recommends tree git - - name: Checkout repo - uses: actions/checkout@v4 - with: - path: ${{github.event.repository.name}} - persist-credentials: false - - name: Add NVCC problem matcher - run: | - echo "::add-matcher::${{github.event.repository.name}}/.github/problem-matchers/problem-matcher.json" - - name: Get AWS credentials for sccache bucket - uses: aws-actions/configure-aws-credentials@v4 - with: - role-to-assume: arn:aws:iam::279114543810:role/gha-oidc-NVIDIA - aws-region: us-east-2 - role-duration-seconds: 43200 # 12 hours) - - name: Run command # Do not change this step's name, it is checked in parse-job-times.py - env: - CI: true - RUNNER: "${{inputs.runner}}" - # Dereferencing the command from an env var instead of a GHA input avoids issues with escaping - # semicolons and other special characters (e.g. `-arch "60;70;80"`). - COMMAND: "${{inputs.command}}" - AWS_ACCESS_KEY_ID: "${{env.AWS_ACCESS_KEY_ID}}" - AWS_SESSION_TOKEN: "${{env.AWS_SESSION_TOKEN}}" - AWS_SECRET_ACCESS_KEY: "${{env.AWS_SECRET_ACCESS_KEY}}" - run: | - - echo "[host] github.workspace: ${{github.workspace}}" - echo "[container] GITHUB_WORKSPACE: ${GITHUB_WORKSPACE:-}" - echo "[container] PWD: $(pwd)" - - # Necessary because we're doing docker-outside-of-docker: - # Make a symlink in the container that matches the host's ${{github.workspace}}, so that way `$(pwd)` - # in `.devcontainer/launch.sh` constructs volume paths relative to the hosts's ${{github.workspace}}. - mkdir -p "$(dirname "${{github.workspace}}")" - ln -s "$(pwd)" "${{github.workspace}}" - cd "${{github.workspace}}" - - cat <<"EOF" > ci.sh - #! /usr/bin/env bash - set -eo pipefail - echo -e "\e[1;34mRunning as '$(whoami)' user in $(pwd):\e[0m" - echo -e "\e[1;34m${COMMAND}\e[0m" - eval "${COMMAND}" || exit_code=$? - if [ ! -z "$exit_code" ]; then - echo -e "::group::️❗ \e[1;31mInstructions to Reproduce CI Failure Locally\e[0m" - echo "::error:: To replicate this failure locally, follow the steps below:" - echo "1. Clone the repository, and navigate to the correct branch and commit:" - echo " git clone --branch $GITHUB_REF_NAME --single-branch https://github.com/$GITHUB_REPOSITORY.git && cd $(echo $GITHUB_REPOSITORY | cut -d'/' -f2) && git checkout $GITHUB_SHA" - echo "" - echo "2. Run the failed command inside the same Docker container used by this CI job:" - echo " .devcontainer/launch.sh -d -c ${{inputs.cuda}} -H ${{inputs.host}} -- ${COMMAND}" - echo "" - echo "For additional information, see:" - echo " - DevContainer Documentation: https://github.com/NVIDIA/cccl/blob/main/.devcontainer/README.md" - echo " - Continuous Integration (CI) Overview: https://github.com/NVIDIA/cccl/blob/main/ci-overview.md" - exit $exit_code - fi - EOF - - chmod +x ci.sh - - mkdir "$RUNNER_TEMP/.aws"; - - cat < "$RUNNER_TEMP/.aws/config" - [default] - bucket=rapids-sccache-devs - region=us-east-2 - EOF - - cat < "$RUNNER_TEMP/.aws/credentials" - [default] - aws_access_key_id=$AWS_ACCESS_KEY_ID - aws_session_token=$AWS_SESSION_TOKEN - aws_secret_access_key=$AWS_SECRET_ACCESS_KEY - EOF - - chmod 0600 "$RUNNER_TEMP/.aws/credentials" - chmod 0664 "$RUNNER_TEMP/.aws/config" - - declare -a gpu_request=() - - # Explicitly pass which GPU to use if on a GPU runner - if [[ "${RUNNER}" = *"-gpu-"* ]]; then - gpu_request+=(--gpus "device=${NVIDIA_VISIBLE_DEVICES}") - fi - - host_path() { - sed "s@/__w@$(dirname "$(dirname "${{github.workspace}}")")@" <<< "$1" - } - - # Launch this container using the host's docker daemon - ${{github.event.repository.name}}/.devcontainer/launch.sh \ - --docker \ - --cuda ${{inputs.cuda}} \ - --host ${{inputs.host}} \ - "${gpu_request[@]}" \ - --env "CI=$CI" \ - --env "VAULT_HOST=" \ - --env "COMMAND=$COMMAND" \ - --env "GITHUB_ENV=$GITHUB_ENV" \ - --env "GITHUB_SHA=$GITHUB_SHA" \ - --env "GITHUB_PATH=$GITHUB_PATH" \ - --env "GITHUB_OUTPUT=$GITHUB_OUTPUT" \ - --env "GITHUB_ACTIONS=$GITHUB_ACTIONS" \ - --env "GITHUB_REF_NAME=$GITHUB_REF_NAME" \ - --env "GITHUB_WORKSPACE=$GITHUB_WORKSPACE" \ - --env "GITHUB_REPOSITORY=$GITHUB_REPOSITORY" \ - --env "GITHUB_STEP_SUMMARY=$GITHUB_STEP_SUMMARY" \ - --volume "${{github.workspace}}/ci.sh:/ci.sh" \ - --volume "$(host_path "$RUNNER_TEMP")/.aws:/root/.aws" \ - --volume "$(dirname "$(dirname "${{github.workspace}}")"):/__w" \ - -- /ci.sh - - - name: Prepare job artifacts - id: done - run: | - echo "SUCCESS=true" | tee -a "${GITHUB_OUTPUT}" - - result_dir="jobs/${{inputs.id}}" - mkdir -p "$result_dir" - - touch "$result_dir/success" - - # Finds a matching file in the repo directory and copies it to the results directory. - find_and_copy() { - filename="$1" - filepath="$(find ${{github.event.repository.name}} -name "${filename}" -print -quit)" - if [[ -z "$filepath" ]]; then - echo "${filename} does not exist in repo directory." - return 1 - fi - cp -v "$filepath" "$result_dir" - } - - find_and_copy "sccache_stats.json" || true # Ignore failures - - echo "::group::Job artifacts" - tree "$result_dir" - echo "::endgroup::" - - name: Upload job artifacts - uses: actions/upload-artifact@v4 - with: - name: jobs-${{inputs.id}} - path: jobs - compression-level: 0 - - - windows: - name: ${{inputs.name}} - if: ${{ startsWith(inputs.runner, 'windows') }} - outputs: - success: ${{ steps.done.outputs.SUCCESS }} - permissions: - id-token: write - contents: read - runs-on: ${{inputs.runner}} - env: - SCCACHE_BUCKET: rapids-sccache-devs - SCCACHE_REGION: us-east-2 - SCCACHE_IDLE_TIMEOUT: 0 - SCCACHE_S3_USE_SSL: true - SCCACHE_S3_NO_CREDENTIALS: false - steps: - - name: Get AWS credentials for sccache bucket - uses: aws-actions/configure-aws-credentials@v4 - with: - role-to-assume: arn:aws:iam::279114543810:role/gha-oidc-NVIDIA - aws-region: us-east-2 - role-duration-seconds: 43200 # 12 hours - - name: Checkout repo - uses: actions/checkout@v4 - with: - path: ${{github.event.repository.name}} - persist-credentials: false - - name: Fetch ${{ inputs.image }} - run: docker pull ${{ inputs.image }} - - name: Prepare paths for docker - id: paths - run: | - echo "HOST_REPO=${{ github.workspace }}\${{ github.event.repository.name }}".Replace('\', '/') | Out-File -FilePath $env:GITHUB_OUTPUT -Append - echo "MOUNT_REPO=C:/${{ github.event.repository.name }}" | Out-File -FilePath $env:GITHUB_OUTPUT -Append - cat $env:GITHUB_OUTPUT - shell: powershell - - name: Run command # Do not change this step's name, it is checked in parse-job-times.py - run: | - docker run \ - --mount type=bind,source="${{steps.paths.outputs.HOST_REPO}}",target="${{steps.paths.outputs.MOUNT_REPO}}" \ - --workdir "${{steps.paths.outputs.MOUNT_REPO}}" \ - ${{ inputs.image }} \ - powershell -c " - [System.Environment]::SetEnvironmentVariable('AWS_ACCESS_KEY_ID','${{env.AWS_ACCESS_KEY_ID}}'); - [System.Environment]::SetEnvironmentVariable('AWS_SECRET_ACCESS_KEY','${{env.AWS_SECRET_ACCESS_KEY}}'); - [System.Environment]::SetEnvironmentVariable('AWS_SESSION_TOKEN','${{env.AWS_SESSION_TOKEN }}'); - [System.Environment]::SetEnvironmentVariable('SCCACHE_BUCKET','${{env.SCCACHE_BUCKET}}'); - [System.Environment]::SetEnvironmentVariable('SCCACHE_REGION','${{env.SCCACHE_REGION}}'); - [System.Environment]::SetEnvironmentVariable('SCCACHE_IDLE_TIMEOUT','${{env.SCCACHE_IDLE_TIMEOUT}}'); - [System.Environment]::SetEnvironmentVariable('SCCACHE_S3_USE_SSL','${{env.SCCACHE_S3_USE_SSL}}'); - [System.Environment]::SetEnvironmentVariable('SCCACHE_S3_NO_CREDENTIALS','${{env.SCCACHE_S3_NO_CREDENTIALS}}'); - git config --global --add safe.directory '${{steps.paths.outputs.MOUNT_REPO}}'; - ${{inputs.command}}" - - name: Prepare job artifacts - id: done - run: | - echo "SUCCESS=true" | tee -a "${GITHUB_OUTPUT}" - - result_dir="jobs/${{inputs.id}}" - mkdir -p "$result_dir" - - touch "$result_dir/success" - - # Finds a matching file in the repo directory and copies it to the results directory. - find_and_copy() { - filename="$1" - filepath="$(find ${{github.event.repository.name}} -name "${filename}" -print -quit)" - if [[ -z "$filepath" ]]; then - echo "${filename} does not exist in repo directory." - return 1 - fi - cp -v "$filepath" "$result_dir" - } - - find_and_copy "sccache_stats.json" || true # Ignore failures - - echo "::group::Job artifacts" - find "$result_dir" # Tree not available in this image. - echo "::endgroup::" - - - name: Upload job artifacts - uses: actions/upload-artifact@v4 - with: - name: jobs-${{inputs.id}} - path: jobs - compression-level: 0 diff --git a/.github/workflows/workflow-dispatch-standalone-group-linux.yml b/.github/workflows/workflow-dispatch-standalone-group-linux.yml new file mode 100644 index 0000000000..e0216b7c6d --- /dev/null +++ b/.github/workflows/workflow-dispatch-standalone-group-linux.yml @@ -0,0 +1,43 @@ +name: "Workflow/Dispatch/StandaloneGroup/Linux" + +defaults: + run: + shell: bash --noprofile --norc -euo pipefail {0} + +on: + workflow_call: + inputs: + job-array: + description: "The dispatch.json's linux_standalone.jobs. array of dispatch jobs." + type: string + required: true + +jobs: + run-jobs: + name: "${{ matrix.name }}" + strategy: + fail-fast: false + matrix: + include: ${{ fromJSON(inputs.job-array) }} + permissions: + id-token: write + contents: read + runs-on: ${{ matrix.runner }} + container: + image: cruizba/ubuntu-dind:jammy-26.1.3 # See workflow-run-job-linux for details + env: + NVIDIA_VISIBLE_DEVICES: ${{env.NVIDIA_VISIBLE_DEVICES}} + steps: + - name: Checkout repo + uses: actions/checkout@v4 + with: + persist-credentials: false + - name: Run job + uses: ./.github/actions/workflow-run-job-linux + with: + id: ${{ matrix.id }} + command: ${{ matrix.command }} + image: ${{ matrix.image }} + runner: ${{ matrix.runner }} + cuda: ${{ matrix.cuda }} + host: ${{ matrix.host }} diff --git a/.github/workflows/workflow-dispatch-standalone-group-windows.yml b/.github/workflows/workflow-dispatch-standalone-group-windows.yml new file mode 100644 index 0000000000..454ad6345d --- /dev/null +++ b/.github/workflows/workflow-dispatch-standalone-group-windows.yml @@ -0,0 +1,36 @@ +name: "Workflow/Dispatch/StandaloneGroup/Windows" + +defaults: + run: + shell: bash --noprofile --norc -euo pipefail {0} + +on: + workflow_call: + inputs: + job-array: + description: "The dispatch.json's windows_standalone.jobs. array of dispatch jobs." + type: string + required: true + +jobs: + run-jobs: + name: ${{ matrix.name }} + runs-on: ${{ matrix.runner }} + permissions: + id-token: write + contents: read + strategy: + fail-fast: false + matrix: + include: ${{ fromJSON(inputs.job-array) }} + steps: + - name: Checkout repo + uses: actions/checkout@v4 + with: + persist-credentials: false + - name: Run job + uses: ./.github/actions/workflow-run-job-windows + with: + id: ${{ matrix.id }} + command: ${{ matrix.command }} + image: ${{ matrix.image }} diff --git a/.github/workflows/workflow-dispatch-two-stage-group-linux.yml b/.github/workflows/workflow-dispatch-two-stage-group-linux.yml new file mode 100644 index 0000000000..c84f5c4af6 --- /dev/null +++ b/.github/workflows/workflow-dispatch-two-stage-group-linux.yml @@ -0,0 +1,28 @@ +name: "Workflow/Dispatch/TwoStageGroup/Linux" + +defaults: + run: + shell: bash --noprofile --norc -euo pipefail {0} + +on: + workflow_call: + inputs: + pc-array: + description: "The dispatch.json's linux_two_stage.jobs. array of producer/consumer chains." + type: string + required: true + +jobs: + dispatch-pcs: + name: ${{ matrix.id }} + permissions: + id-token: write + contents: read + strategy: + fail-fast: false + matrix: + include: ${{ fromJSON(inputs.pc-array) }} + uses: ./.github/workflows/workflow-dispatch-two-stage-linux.yml + with: + producers: ${{ toJSON(matrix.producers) }} + consumers: ${{ toJSON(matrix.consumers) }} diff --git a/.github/workflows/workflow-dispatch-two-stage-group-windows.yml b/.github/workflows/workflow-dispatch-two-stage-group-windows.yml new file mode 100644 index 0000000000..b6c6f1c25a --- /dev/null +++ b/.github/workflows/workflow-dispatch-two-stage-group-windows.yml @@ -0,0 +1,28 @@ +name: "Workflow/Dispatch/TwoStageGroup/Windows" + +defaults: + run: + shell: bash --noprofile --norc -euo pipefail {0} + +on: + workflow_call: + inputs: + pc-array: + description: "The dispatch.json's windows_two_stage.jobs. array of producer/consumer chains." + type: string + required: true + +jobs: + dispatch-pcs: + name: ${{ matrix.id }} + permissions: + id-token: write + contents: read + strategy: + fail-fast: false + matrix: + include: ${{ fromJSON(inputs.pc-array) }} + uses: ./.github/workflows/workflow-dispatch-two-stage-windows.yml + with: + producers: ${{ toJSON(matrix.producers) }} + consumers: ${{ toJSON(matrix.consumers) }} diff --git a/.github/workflows/workflow-dispatch-two-stage-linux.yml b/.github/workflows/workflow-dispatch-two-stage-linux.yml new file mode 100644 index 0000000000..f1d9d518d3 --- /dev/null +++ b/.github/workflows/workflow-dispatch-two-stage-linux.yml @@ -0,0 +1,75 @@ +name: "Workflow/Dispatch/TwoStage/Linux" + +defaults: + run: + shell: bash --noprofile --norc -euo pipefail {0} + +on: + workflow_call: + inputs: + producers: + description: "The dispatch.json's linux_two_stage.jobs.[*].producers array." + type: string + required: true + consumers: + description: "The dispatch.json's linux_two_stage.jobs.[*].consumers array." + type: string + required: true + +jobs: + # Accumulating results from multiple producers is not easily implemented. For now, only a single producer is supported. + # The build-workflow.py script will emit an error if more than one producer is specified. + producer: + name: ${{ fromJSON(inputs.producers)[0].name }} + runs-on: ${{ fromJSON(inputs.producers)[0].runner }} + permissions: + id-token: write + contents: read + container: + image: cruizba/ubuntu-dind:jammy-26.1.3 # See workflow-run-job-linux for details + env: + NVIDIA_VISIBLE_DEVICES: ${{env.NVIDIA_VISIBLE_DEVICES}} + steps: + - name: Checkout repo + uses: actions/checkout@v4 + with: + persist-credentials: false + - name: Run job + uses: ./.github/actions/workflow-run-job-linux + with: + id: ${{ fromJSON(inputs.producers)[0].id }} + command: ${{ fromJSON(inputs.producers)[0].command }} + image: ${{ fromJSON(inputs.producers)[0].image }} + runner: ${{ fromJSON(inputs.producers)[0].runner }} + cuda: ${{ fromJSON(inputs.producers)[0].cuda }} + host: ${{ fromJSON(inputs.producers)[0].host }} + + consumers: + name: "${{ matrix.name }}" + needs: producer + runs-on: ${{ matrix.runner }} + permissions: + id-token: write + contents: read + container: + image: cruizba/ubuntu-dind:jammy-26.1.3 # See workflow-run-job-linux for details + env: + NVIDIA_VISIBLE_DEVICES: ${{env.NVIDIA_VISIBLE_DEVICES}} + strategy: + fail-fast: false + matrix: + include: ${{ fromJSON(inputs.consumers) }} + steps: + - name: Checkout repo + uses: actions/checkout@v4 + with: + persist-credentials: false + - name: Run job + uses: ./.github/actions/workflow-run-job-linux + with: + id: ${{ matrix.id }} + command: ${{ matrix.command }} + image: ${{ matrix.image }} + runner: ${{ matrix.runner }} + cuda: ${{ matrix.cuda }} + host: ${{ matrix.host }} diff --git a/.github/workflows/workflow-dispatch-two-stage-windows.yml b/.github/workflows/workflow-dispatch-two-stage-windows.yml new file mode 100644 index 0000000000..22a5bb4ed2 --- /dev/null +++ b/.github/workflows/workflow-dispatch-two-stage-windows.yml @@ -0,0 +1,61 @@ +name: "Workflow/Dispatch/TwoStage/Windows" + +defaults: + run: + shell: bash --noprofile --norc -euo pipefail {0} + +on: + workflow_call: + inputs: + producers: + description: "The dispatch.json's windows_two_stage.jobs.[*].producers array." + type: string + required: true + consumers: + description: "The dispatch.json's windows_two_stage.jobs.[*].consumers array." + type: string + required: true + +jobs: + # Accumulating results from multiple producers is not easily implemented. For now, only a single producer is supported. + # The build-workflow.py script will emit an error if more than one producer is specified. + producer: + name: ${{ fromJSON(inputs.producers)[0].name }} + runs-on: ${{ fromJSON(inputs.producers)[0].runner }} + permissions: + id-token: write + contents: read + steps: + - name: Checkout repo + uses: actions/checkout@v4 + with: + persist-credentials: false + - name: Run job + uses: ./.github/actions/workflow-run-job-windows + with: + id: ${{ fromJSON(inputs.producers)[0].id }} + command: ${{ fromJSON(inputs.producers)[0].command }} + image: ${{ fromJSON(inputs.producers)[0].image }} + + consumers: + name: ${{ matrix.name }} + needs: producer + runs-on: ${{ matrix.runner }} + permissions: + id-token: write + contents: read + strategy: + fail-fast: false + matrix: + include: ${{ fromJSON(inputs.consumers) }} + steps: + - name: Checkout repo + uses: actions/checkout@v4 + with: + persist-credentials: false + - name: Run job + uses: ./.github/actions/workflow-run-job-windows + with: + id: ${{ matrix.id }} + command: ${{ matrix.command }} + image: ${{ matrix.image }} diff --git a/.github/workflows/workflow-dispatch-two-stage.yml b/.github/workflows/workflow-dispatch-two-stage.yml deleted file mode 100644 index 2374a7b4a4..0000000000 --- a/.github/workflows/workflow-dispatch-two-stage.yml +++ /dev/null @@ -1,66 +0,0 @@ -name: "Workflow/Dispatch/TwoStage" - -defaults: - run: - shell: bash --noprofile --norc -euo pipefail {0} - -on: - workflow_call: - inputs: - producers: {type: string, required: true} - consumers: {type: string, required: true} - -permissions: - contents: read - -jobs: - # It is impossible to accumulate output variables across a matrix, - # and we cannot rely on the results of the dispatch-job workflow to determine success. - # See the note in ci-dispatch-job.yml for more information. - # - # Since we cannot accumulate results from multiple producers, only support a single producer for now. - # This is enforced by compute-matrix.py. - producers: - # This is an internal dispatch job and the name is not important. - # Give the job a short and unique name, otherwise github will bloat the job name with the matrix values. - # This keeps the UI from getting cluttered. - name: "p.${{ matrix.id }}" - permissions: - id-token: write - contents: read - strategy: - fail-fast: false - matrix: - include: ${{fromJSON(inputs.producers)}} - uses: ./.github/workflows/workflow-dispatch-job.yml - with: - cuda: ${{ matrix.cuda }} - host: ${{ matrix.host }} - name: ${{ matrix.name }} - runner: ${{ matrix.runner }} - image: ${{ matrix.image }} - command: ${{ matrix.command }} - id: ${{ matrix.id }} - - consumers: - # This is an internal dispatch job and the name is not important. - # Give the job a short and unique name, otherwise github will bloat the job name with the matrix values. - # This keeps the UI from getting cluttered. - name: "c.${{ matrix.id }}" - needs: producers - permissions: - id-token: write - contents: read - strategy: - fail-fast: false - matrix: - include: ${{fromJSON(inputs.consumers)}} - uses: ./.github/workflows/workflow-dispatch-job.yml - with: - cuda: ${{ matrix.cuda }} - host: ${{ matrix.host }} - name: ${{ matrix.name }} - runner: ${{ matrix.runner }} - image: ${{ matrix.image }} - command: ${{ matrix.command }} - id: ${{ matrix.id }} diff --git a/.github/workflows/workflow-dispatch.yml b/.github/workflows/workflow-dispatch.yml deleted file mode 100644 index 42035c28fb..0000000000 --- a/.github/workflows/workflow-dispatch.yml +++ /dev/null @@ -1,57 +0,0 @@ -name: "Workflow/Dispatch/Group" - -defaults: - run: - shell: bash --noprofile --norc -euo pipefail {0} - -on: - workflow_call: - inputs: - name: {type: string, required: true} - jobs: {type: string, required: true} - -permissions: - contents: read - -jobs: - - standlone-jobs: - # This is an internal dispatch job and the name is not important. - # Give the job a short and unique name, otherwise github will bloat the job name with the matrix values. - # This keeps the UI from getting cluttered. - name: "s.${{ matrix.id }}" - if: ${{ fromJSON(inputs.jobs)['standalone'] != null }} - permissions: - id-token: write - contents: read - strategy: - fail-fast: false - matrix: - include: ${{fromJSON(inputs.jobs)['standalone']}} - uses: ./.github/workflows/workflow-dispatch-job.yml - with: - cuda: ${{ matrix.cuda }} - host: ${{ matrix.host }} - name: ${{ matrix.name }} - runner: ${{ matrix.runner }} - image: ${{ matrix.image }} - command: ${{ matrix.command }} - id: ${{ matrix.id }} - - two-stage-jobs: - # This is an internal dispatch job and the name is not important. - # Give the job a short and unique name, otherwise github will bloat the job name with the matrix values. - # This keeps the UI from getting cluttered. - name: "t.${{ matrix.id }}" - if: ${{ fromJSON(inputs.jobs)['two_stage'] != null }} - permissions: - id-token: write - contents: read - strategy: - fail-fast: false - matrix: - include: ${{fromJSON(inputs.jobs)['two_stage']}} - uses: ./.github/workflows/workflow-dispatch-two-stage.yml - with: - producers: ${{ toJSON(matrix.producers) }} - consumers: ${{ toJSON(matrix.consumers) }}