Skip to content
Merged
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
2 changes: 1 addition & 1 deletion .github/scripts/apply-mirror-patches.sh
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ for prefix in "${prefixes[@]}"; do

# None of the variants apply. Re-run the first one verbosely so the
# failure (and any conflict markers left by --3way) end up in the log for
# whoever rewrites the patch, human or Copilot.
# whoever rewrites the patch.
if [[ -z "$chosen" ]]; then
variant="${variants[0]}"
echo "Applying $variant"
Expand Down
76 changes: 7 additions & 69 deletions .github/scripts/report-stale-patch.sh
Original file line number Diff line number Diff line change
@@ -1,26 +1,14 @@
#!/usr/bin/env bash
# Called when a mirror patch stops applying to an upstream tag. Files an issue
# describing what broke, then tries to put GitHub Copilot on it.
#
# The issue is the point of this script: it is the notification and the record,
# and it is filed with whatever token is available. Assigning Copilot is a
# best-effort extra, because it needs a PAT (GITHUB_TOKEN cannot assign the
# agent) and because the fix is a judgement call either way — Copilot opens a
# pull request against main, and a human merges it. Nothing here auto-merges:
# a silently wrong reapplication would ship a build whose update button is
# quietly dead.
# describing what broke and mentions the repository owner. The issue is both the
# notification and the record. Nothing here tries to fix the patch: a silently
# wrong reapplication would ship a build whose update button is quietly dead.
set -euo pipefail

: "${TAG:?}" "${RUN_URL:?}" "${GITHUB_REPOSITORY:?}"
: "${TAG:?}" "${RUN_URL:?}" "${GITHUB_REPOSITORY:?}" "${GITHUB_REPOSITORY_OWNER:?}"

git checkout -- . 2>/dev/null || true

request_copilot_fix() {
local issue_number="$1"
gh issue comment "$issue_number" --repo "$GITHUB_REPOSITORY" --body \
"@copilot Please update the stale mirror patch so \`.github/scripts/apply-mirror-patches.sh --check\` passes for \`$TAG\`. See the failed run: $RUN_URL"
}

# Name the patches that no longer apply, so the issue points at a file rather
# than at a log the reader has to scroll.
shopt -s nullglob
Expand All @@ -47,12 +35,13 @@ existing="$(gh issue list --repo "$GITHUB_REPOSITORY" --state open \
if [[ -n "$existing" ]]; then
gh issue comment "$existing" --repo "$GITHUB_REPOSITORY" \
--body "Still failing on \`$TAG\`. [Run]($RUN_URL)."
request_copilot_fix "$existing"
echo "Commented on existing issue #$existing."
exit 0
fi

{
echo "@$GITHUB_REPOSITORY_OWNER"
echo
echo "Building upstream \`$TAG\` stopped because a patch in \`mirror-patches/\` no longer applies."
echo
echo "Stale:"
Expand All @@ -76,7 +65,7 @@ fi
done
echo '```'
echo
echo "Open a pull request against \`main\` with the updated patch file. Do not merge it: a patch that applies but no longer does the right thing ships a build whose in-app updater is silently broken, so a human reviews this one. Once it is merged, re-run the build for this tag from Actions → Mirror Linux build."
echo "Check the result carefully: a patch that applies but no longer does the right thing ships a build whose in-app updater is silently broken. Once the fix is on \`main\`, re-run the build for this tag from Actions → Mirror Linux build."
echo
echo "No upstream file is edited on \`main\` — only the patch file changes."
} > /tmp/mirror-patch-issue.md
Expand All @@ -87,54 +76,3 @@ gh label create mirror-patch --repo "$GITHUB_REPOSITORY" \
issue_url="$(gh issue create --repo "$GITHUB_REPOSITORY" \
--title "$title" --label mirror-patch --body-file /tmp/mirror-patch-issue.md)"
echo "Filed $issue_url"

issue_number="${issue_url##*/}"
request_copilot_fix "$issue_number"

if [[ -z "${COPILOT_TOKEN:-}" ]]; then
echo "MIRROR_COPILOT_TOKEN is not set, so Copilot was not assigned. Fix the patch by hand, or add the secret." >&2
exit 0
fi

owner="${GITHUB_REPOSITORY%%/*}"
name="${GITHUB_REPOSITORY##*/}"

# Copilot is started by assigning it the issue; there is no workflow trigger
# for it. It only appears in suggestedActors when the coding agent is enabled
# for the repository and the token has the right scopes.
export GH_TOKEN="$COPILOT_TOKEN"

bot_id="$(gh api graphql -f owner="$owner" -f name="$name" \
-f query='
query($owner: String!, $name: String!) {
repository(owner: $owner, name: $name) {
suggestedActors(capabilities: [CAN_BE_ASSIGNED], first: 100) {
nodes { login ... on Bot { id } ... on User { id } }
}
}
}' \
--jq '.data.repository.suggestedActors.nodes[]
| select(.login == "Copilot" or .login == "copilot-swe-agent") | .id' 2>/dev/null || true)"

issue_id="$(gh api graphql -f owner="$owner" -f name="$name" -F number="$issue_number" \
-f query='
query($owner: String!, $name: String!, $number: Int!) {
repository(owner: $owner, name: $name) { issue(number: $number) { id } }
}' \
--jq '.data.repository.issue.id' 2>/dev/null || true)"

if [[ -z "$bot_id" || -z "$issue_id" ]]; then
echo "Copilot is not assignable in this repository; the issue stands on its own." >&2
exit 0
fi

gh api graphql \
-f assignableId="$issue_id" -f actorId="$bot_id" \
-f query='
mutation($assignableId: ID!, $actorId: ID!) {
replaceActorsForAssignable(input: { assignableId: $assignableId, actorIds: [$actorId] }) {
assignable { ... on Issue { number } }
}
}' >/dev/null

echo "Assigned Copilot to $issue_url"
10 changes: 3 additions & 7 deletions .github/workflows/mirror-linux-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ jobs:
# The fork's patches (see mirror-patches/README.md) are applied to upstream's
# source at build time. Checking them here first means a patch that has
# gone stale costs one two-minute job instead of four long ones, and gives
# a single place to hand the problem to Copilot.
# a single place to file an issue about it.
preflight:
name: Check patches apply
runs-on: ubuntu-26.04
Expand Down Expand Up @@ -94,15 +94,11 @@ jobs:
.github/scripts/report-stale-patch.sh
.github/scripts/apply-mirror-patches.sh --check

- name: Hand the stale patch to Copilot
- name: File an issue about the stale patch
if: failure() && steps.patches.outcome == 'failure'
shell: bash
env:
# GITHUB_TOKEN cannot assign the Copilot agent, so the assignment
# step needs a PAT. The issue itself is filed with either token, so
# the notification still arrives when the PAT is absent or expired.
GH_TOKEN: ${{ secrets.MIRROR_COPILOT_TOKEN || github.token }}
COPILOT_TOKEN: ${{ secrets.MIRROR_COPILOT_TOKEN }}
GH_TOKEN: ${{ github.token }}
TAG: ${{ inputs.tag }}
RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
run: .github/scripts/report-stale-patch.sh
Expand Down
Loading