Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 36 additions & 4 deletions .github/workflows/autopilot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,11 @@ name: Autopilot
# organisation's own repository from its
# pull-request.yml (#504), when the review of an
# `autopilot` pull request blocks: one round of fixes,
# pushed to its branch, which runs the pipeline again. After
# pushed to its branch, which runs the pipeline again;
# or when a check of the pipeline failed at its head (a
# gate, or a review without a verdict, #741): a check
# round, from the failed jobs' log (`autopilot.py
# checks-failed`, .dx-io/checks.md). After
# `autopilot-rounds` rounds (5) it hands the pull request to
# a person (`needs-human`, with a summary).
# Called on `dequeued` (#479, org-pull-request.yml's
Expand Down Expand Up @@ -606,12 +610,38 @@ jobs:
fi
fi
if [ "$MODE" = fix ]; then
# #741: a check of the pipeline that failed at the head (a gate,
# which skips the review, or a review job that ended with no
# verdict at the head) makes a check round, from the failed
# jobs' log; none, the review's own round. The jobs unread, no
# round starts and a person has it, so the board never waits
# on a round nothing runs.
checks=false
if [ "${QUEUE:-}" != true ]; then
# A draft's checks are its own run's to make pass (its `make
# pre-pr` before it is ready): no round runs beside it.
if [ "$(gh pr view "$PR" --repo "$GITHUB_REPOSITORY" --json isDraft --jq .isDraft)" = true ]; then
echo "#$PR is a draft: its own run makes its checks pass, no fix round."; echo "go=false" >> "$GITHUB_OUTPUT"; exit 0
fi
rc=0; why=$(python3 .dx-central/scripts/autopilot.py checks-failed) || rc=$?
case $rc in
0) checks=true; echo "A check of #$PR failed: $why" ;;
1) echo "#$PR: $why" ;;
*)
echo "::error::$why"
gh pr edit "$PR" --repo "$GITHUB_REPOSITORY" --add-label needs-human >/dev/null || true
gh pr comment "$PR" --repo "$GITHUB_REPOSITORY" --body "The autopilot could not read which checks failed ($why), so no fix round starts: a person takes it from here.

🤖 AI-generated · Claude Opus 5.5 (Anthropic)" >/dev/null
echo "go=false" >> "$GITHUB_OUTPUT"; exit 0 ;;
esac
fi
echo "checks=$checks" >> "$GITHUB_OUTPUT"
max=$(python3 .dx-central/scripts/autonomy.py policy | jq -r '."autopilot-rounds" // 3')
if [ "$ROUND" -gt "$max" ]; then
gh pr edit "$PR" --repo "$GITHUB_REPOSITORY" --add-label needs-human >/dev/null || true
gh pr comment "$PR" --repo "$GITHUB_REPOSITORY" --body "The autopilot spent its $max rounds and the review still blocks: a person takes it from here. The review's open threads say what is left.

🤖 AI-generated · Claude Opus 5.5 (Anthropic)" >/dev/null
# What still fails, the review or the checks (#741).
gh pr comment "$PR" --repo "$GITHUB_REPOSITORY" --body "$(MAX="$max" CHECKS="$checks" python3 .dx-central/scripts/autopilot.py rounds-spent)" >/dev/null
[ -z "${ISSUE:-}" ] || [ -z "${OPEN_TO_CLAIM:-}" ] || gh issue comment "$ISSUE" --repo "$GITHUB_REPOSITORY" --body "${OPEN_TO_CLAIM:-}" >/dev/null || true
echo "go=false" >> "$GITHUB_OUTPUT"; exit 0
fi
Expand Down Expand Up @@ -706,6 +736,8 @@ jobs:
# #479: a round for a pull request the queue dropped reads the
# failed jobs' log instead of the review.
QUEUE: ${{ github.event_name == 'pull_request' && github.event.action == 'dequeued' }}
# #741: a check round reads the failed jobs' log instead too.
CHECKS: ${{ steps.may.outputs.checks }}
RUN_URL: ${{ format('{0}/{1}/actions/runs/{2}', github.server_url, github.repository, github.run_id) }}
run: |
set -euo pipefail
Expand Down
8 changes: 5 additions & 3 deletions .github/workflows/org-pull-request.yml
Original file line number Diff line number Diff line change
Expand Up @@ -148,13 +148,15 @@ jobs:

# An autopilot pull request whose review blocks: one round of fixes by the
# autopilot (#83), which pushes and so runs this pipeline again; after its
# rounds it hands the pull request to a person.
# rounds it hands the pull request to a person. So does one whose
# conventions failed (the review then skipped) or whose review ended with
# no verdict (#741): its round reads the failed jobs' log.
autopilot-fix:
needs: review
needs: [conventions, review]
# Only dilux-bot's own pull requests, and only for a new commit (an edit
# of the description keeps the review's verdict and must not spend a
# round).
if: always() && github.event_name == 'pull_request' && github.repository != 'DiluxOne/.github' && needs.review.outputs.blocking == 'true' && contains(github.event.pull_request.labels.*.name, 'autopilot') && github.event.pull_request.user.login == vars.DX_BOT_LOGIN && github.event.action != 'edited'
if: always() && github.event_name == 'pull_request' && github.repository != 'DiluxOne/.github' && (needs.review.outputs.blocking == 'true' || needs.review.result == 'failure' || needs.conventions.result == 'failure') && contains(github.event.pull_request.labels.*.name, 'autopilot') && github.event.pull_request.user.login == vars.DX_BOT_LOGIN && github.event.action != 'edited'
# The agent's jobs read the repository with the job's own token (#287).
permissions:
contents: read
Expand Down
16 changes: 10 additions & 6 deletions .github/workflows/pull-request.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ name: Pull request
# the policy and the brief script are read from main, so a pull request cannot
# rewrite the rules that review it. The conventions script comes from the pull
# request itself: it can change conventions.yml anyway. An autopilot pull
# request whose review blocks gets its fix round here (autopilot-fix, #504),
# request whose review blocks, whose gate fails or whose review ends with no
# verdict (#741) gets its fix round here (autopilot-fix, #504),
# as org-pull-request.yml gives it everywhere else. A merge into main
# brings the open autopilot pull requests in step with it from issues.yml's
# `autopilot-sweep` job, here as in every repository (#561, #669).
Expand Down Expand Up @@ -278,16 +279,19 @@ jobs:

# An autopilot pull request here whose review blocks: one round of fixes
# (#504), as org-pull-request.yml runs it in every other repository, with
# the same condition but the repository (that one excludes this one, so
# the round never runs twice; scripts/autopilot.py --test keeps the two
# the same). Only dilux-bot's own branch matches, never a fork's. The
# the same condition but the repository and the gates (that one excludes
# this one, so the round never runs twice; scripts/autopilot.py --test
# keeps the two the same). So does one whose gate failed or whose review
# ended with no verdict (#741): its round reads the failed jobs' log. Only dilux-bot's own branch matches, never a fork's. The
# scripts are read from main, like the review's.
autopilot-fix:
needs: review
# #741: after every gate the review waits on, so a gate that failed
# (the review then skipped) starts a round too, from its log.
needs: [conventions, actionlint, scripts, review]
# Only dilux-bot's own pull requests, and only for a new commit (an edit
# of the description keeps the review's verdict and must not spend a
# round).
if: always() && github.event_name == 'pull_request' && github.repository == 'DiluxOne/.github' && needs.review.outputs.blocking == 'true' && contains(github.event.pull_request.labels.*.name, 'autopilot') && github.event.pull_request.user.login == vars.DX_BOT_LOGIN && github.event.action != 'edited'
if: always() && github.event_name == 'pull_request' && github.repository == 'DiluxOne/.github' && (needs.review.outputs.blocking == 'true' || needs.review.result == 'failure' || needs.conventions.result == 'failure' || needs.actionlint.result == 'failure' || needs.scripts.result == 'failure') && contains(github.event.pull_request.labels.*.name, 'autopilot') && github.event.pull_request.user.login == vars.DX_BOT_LOGIN && github.event.action != 'edited'
# The agent's jobs read the repository with the job's own token (#287).
permissions:
contents: read
Expand Down
Loading
Loading