diff --git a/.github/workflows/auto-start-ci.yml b/.github/workflows/auto-start-ci.yml index c8b090fdc044..85f3e92f2ffb 100644 --- a/.github/workflows/auto-start-ci.yml +++ b/.github/workflows/auto-start-ci.yml @@ -1,6 +1,9 @@ # This action uses the following secrets: # JENKINS_USER: GitHub user whose Jenkins token is defined below # JENKINS_TOKEN: Jenkins token, to be used to start or resume CI +# Optional repository variables: +# AUTO_START_CI_BATCH_SIZE: maximum PRs to process per run (default: 5) +# AUTO_START_CI_MAX_WORKLOAD: pause at this many running or queued PR jobs (default: 15) name: Auto Start CI on: @@ -30,16 +33,25 @@ jobs: steps: - name: Get Pull Requests id: get_prs_for_ci + # Explicit bash enables pipefail so jq cannot hide a failed gh query. + shell: bash run: | + if ! [[ "$BATCH_SIZE" =~ ^[1-9][0-9]*$ ]]; then + echo '::error::AUTO_START_CI_BATCH_SIZE must be a strictly positive integer' + exit 1 + fi + # Filter before selecting the batch; UNKNOWN mergeability is retried later. numbers=$(gh pr list \ --repo "$GITHUB_REPOSITORY" \ - --json 'number' \ + --json 'number,mergeable' \ --search 'review:approved label:request-ci,resume-ci' \ - -t '{{ range . }}{{ .number }} {{ end }}' \ - --limit 5) + --limit 100 \ + | jq --argjson limit "$BATCH_SIZE" \ + '[.[] | select(.mergeable == "MERGEABLE") | .number] | .[:$limit] | map(tostring) | join(" ")' -r) echo "numbers=$numbers" >> "$GITHUB_OUTPUT" env: GH_TOKEN: ${{ github.token }} + BATCH_SIZE: ${{ vars.AUTO_START_CI_BATCH_SIZE || '5' }} start-ci: permissions: checks: read @@ -78,3 +90,4 @@ jobs: env: GH_TOKEN: ${{ github.token }} PULL_REQUESTS: ${{ needs.get-prs-for-ci.outputs.numbers }} + MAX_WORKLOAD: ${{ vars.AUTO_START_CI_MAX_WORKLOAD || '15' }} diff --git a/tools/actions/start-ci.sh b/tools/actions/start-ci.sh index 4573b17761c9..6482f366fa31 100755 --- a/tools/actions/start-ci.sh +++ b/tools/actions/start-ci.sh @@ -3,6 +3,14 @@ set -xe cqurl="${GITHUB_SERVER_URL:?}/${GITHUB_REPOSITORY:?}/actions/runs/${GITHUB_RUN_ID:?}" +max_workload=${MAX_WORKLOAD:-15} +case $max_workload in + *[!0-9]*) + echo '::error::AUTO_START_CI_MAX_WORKLOAD must be a positive integer' + exit 1 + ;; + *) ;; +esac escape_code_block_or_line() { case $1 in @@ -19,6 +27,30 @@ escape_code_block_or_line() { } for pr in "$@"; do + # Sleep in between. + [ "$pr" = "$1" ] || sleep 20 + + # Keep request labels until Jenkins is ready to accept more work. + if ! ncu-ci available; then + echo '::notice::CI is unavailable; leaving CI requests for a later run.' + break + fi + if ! workload=$(ncu-ci workload); then + echo '::notice::CI workload could not be checked; leaving CI requests for a later run.' + break + fi + case $workload in + ''|*[!0-9]*) + echo '::error::ncu-ci workload did not return a positive integer' + exit 1 + ;; + *) ;; + esac + if ! [ "$workload" -lt "$max_workload" ]; then + echo "::notice::CI workload is $workload (limit: $max_workload); leaving CI requests for a later run." + break + fi + request_labels=$(gh -R "$GITHUB_REPOSITORY" pr view "$pr" --json labels \ --jq '[.labels[].name | select(. == "request-ci" or . == "resume-ci")] | sort | join(",")') case "$request_labels" in