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
36 changes: 36 additions & 0 deletions .github/workflows/check-updates.yml
Original file line number Diff line number Diff line change
Expand Up @@ -493,6 +493,42 @@ jobs:

echo "Committed: $COMMIT_SHA (verified)"

# Before the issue step: an open issue of a package holds back a new
# one, so the check issue of a package whose update now fails has to
# be closed first. The step does not run after a failed step, so a
# status that publish did not write closes nothing.
- name: Close issues of recovered packages
env:
GH_TOKEN: ${{ github.token }}
RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
run: |
STATUS_DIR=".status"
[[ -d "$STATUS_DIR" ]] || exit 0

has_issues=$(gh api "repos/$GITHUB_REPOSITORY" --jq .has_issues)
[[ "$has_issues" != "false" ]] || exit 0
open=$(gh issue list --label "auto-update" --state open --limit 1000 --json number,title --jq '.[] | "\(.number) \(.title)"')

while read -r number title; do
[[ -n "$number" ]] || continue
for status_file in "$STATUS_DIR"/*; do
[[ -f "$status_file" ]] || continue
pkg_name=$(basename "$status_file")
[[ "$pkg_name" == _* ]] && continue
status=$(cat "$status_file")
# A check issue closes once the check passes, an update issue
# once the version is in the AUR
case "$title:$status" in
"❌ $pkg_name: check failed:check_failed:"*) continue ;;
"❌ $pkg_name: check failed:"*) ;;
"❌ $pkg_name: update failed:up_to_date" | "❌ $pkg_name: update failed:updated: "* | \
"❌ $pkg_name: update failed:downgrade: "*) ;;
*) continue ;;
esac
gh issue close "$number" --comment "The status of $pkg_name is \`$status\` in $RUN_URL."
done
done <<<"$open"

- name: Create issues for failed packages
if: always()
env:
Expand Down
7 changes: 5 additions & 2 deletions docs/WORKFLOW.md
Original file line number Diff line number Diff line change
Expand Up @@ -721,9 +721,10 @@ The four steps that run `check-all.sh` as `builder` stop workflow commands while
7. **Publish updates:** runs `check-all.sh --publish handoff` as `builder`, with `GPG_KEY_ID`, `GIT_AUTHOR_NAME` and `GIT_AUTHOR_EMAIL`, and without `GITHUB_TOKEN`, as it runs no strategy. It keeps the statuses of `check` and checks the prepared files before it pushes, see [4.4](#44-prepare-and-publish). Workflow commands are stopped while it runs.
8. **Update README status table:** runs `update-readme.sh` as `builder`.
9. **Commit changes via API:** see [5.5](#55-verified-commit).
10. **Create issues for failed packages:** see [5.6](#56-failure-issues).
10. **Close issues of recovered packages:** see [5.6](#56-failure-issues).
11. **Create issues for failed packages:** see [5.6](#56-failure-issues).

Steps 9 and 10 run as root with `GH_TOKEN` set to the workflow token, `github.token`.
Steps 9 to 11 run as root with `GH_TOKEN` set to the workflow token, `github.token`.

### 5.4 README update

Expand Down Expand Up @@ -788,6 +789,8 @@ For every `.status/` entry with `check_failed:` or `update_failed:`, the step:

The step belongs to `publish`, which runs when the status table changes. A package that keeps failing gets its issue in the run where it starts failing, and a closed issue comes back only after its status changes again.

The step before it, `Close issues of recovered packages`, closes the issue of a package that recovered, with a comment that names the new status and the run. An open `❌ <package>: check failed` closes when the status is anything but `check_failed`. An open `❌ <package>: update failed` closes when the status is `up_to_date`, `updated` or `downgrade`, so the version is in the AUR. The close step comes first because an open issue of a package holds back a new one: a package whose check passes and whose update fails gets its check issue closed and an update issue opened in the same run. It does not run after a failed step, so a status that `publish` did not write closes nothing, and with Issues turned off it does nothing.

### 5.7 Failed runs

The issue step of `publish` reports failed packages, and a run that fails in `image` or `check` never reaches it. `notify-failure.yml` runs on `workflow_run` when a run of `Check AUR Package Updates` completes, and its job runs only when the conclusion of that run is `failure` or `timed_out`. It opens one issue titled `❌ Check AUR Package Updates failed`, with a link to the run, its branch and the names of the failed jobs, and opens no second one while that issue is open. A repository with Issues turned off gets a notice instead, as in [5.6](#56-failure-issues). The workflow has `actions: read` to list the jobs and `issues: write`, and checks out nothing.
Expand Down
2 changes: 1 addition & 1 deletion scripts/lib.sh
Original file line number Diff line number Diff line change
Expand Up @@ -931,7 +931,7 @@ failure_issue_body() {
done
# The backticks are Markdown code around the package name
# shellcheck disable=SC2016
printf 'Package: `%s`\nStatus: %s\nRun: %s\n\n%s\n%s\n%s\n\nAuto-created by update workflow. Close after fixing.\n' \
printf 'Package: `%s`\nStatus: %s\nRun: %s\n\n%s\n%s\n%s\n\nAuto-created by update workflow. A run closes it when the package recovers.\n' \
"${1##*/}" "${kind//_/ }" "$2" "$fence" "$error" "$fence"
}

Expand Down
2 changes: 1 addition & 1 deletion tests/publish.bats
Original file line number Diff line number Diff line change
Expand Up @@ -1300,7 +1300,7 @@ Run: https://github.com/owner/repo/actions/runs/1
rejected '1.0.1 @octocat https://evil.example'
```

Auto-created by update workflow. Close after fixing.
Auto-created by update workflow. A run closes it when the package recovers.
EOF
)
[ "$output" = "$expected" ]
Expand Down
105 changes: 105 additions & 0 deletions tests/workflows.bats
Original file line number Diff line number Diff line change
Expand Up @@ -484,6 +484,111 @@ EOF
[ ! -s "$GH_LOG" ]
}

# Runs the close step of publish in a workspace with the statuses given as
# name=status pairs. gh lists the open issues in $GH_OPEN, one
# "<number> <title>" per line, the first 30 unless --limit says otherwise,
# answers has_issues with $GH_HAS_ISSUES (true by default), fails the list
# when $GH_FAIL is set and logs every call to $GH_LOG.
run_close_step() {
local ws="$BATS_TEST_TMPDIR/ws" bin="$BATS_TEST_TMPDIR/bin" pair
mkdir -p "$ws/.status" "$bin"
for pair in "$@"; do
printf '%s\n' "${pair#*=}" >"$ws/.status/${pair%%=*}"
done
cat >"$bin/gh" <<'EOF'
#!/usr/bin/env bash
printf '%s\n' "$*" >>"$GH_LOG"
case "$1 $2" in
"api repos/owner/fork") echo "${GH_HAS_ISSUES:-true}" ;;
"issue list")
[[ -z "${GH_FAIL:-}" ]] || exit 1
limit=30
while (($#)); do
[[ "$1" != --limit ]] || limit=$2
shift
done
[[ -z "${GH_OPEN:-}" ]] || head -n "$limit" <<<"$GH_OPEN"
;;
esac
EOF
chmod +x "$bin/gh"
GH_LOG="$BATS_TEST_TMPDIR/gh.log"
: >"$GH_LOG"
step_run publish "Close issues of recovered packages" >"$BATS_TEST_TMPDIR/step.sh"
run env -C "$ws" PATH="$bin:$PATH" GH_LOG="$GH_LOG" GH_OPEN="${GH_OPEN:-}" GH_FAIL="${GH_FAIL:-}" \
GH_HAS_ISSUES="${GH_HAS_ISSUES:-}" GITHUB_REPOSITORY=owner/fork \
RUN_URL=https://example.invalid/run sh -e "$BATS_TEST_TMPDIR/step.sh"
}

@test "check-updates: the close step runs before the issue step, and not after a failed step" {
run yq '[.jobs.publish.steps[].name] | join(",")' "$CHECK_UPDATES"
[[ "$output" == *"Close issues of recovered packages,Create issues for failed packages" ]]
run yq '.jobs.publish.steps[] | select(.name == "Close issues of recovered packages") | has("if")' "$CHECK_UPDATES"
[ "$output" = "false" ]
}

@test "check-updates: the close step finds an open issue behind 30 others" {
local i
GH_OPEN="$(for i in {1..30}; do echo "$i ❌ pkg$i: check failed"; done)"$'\n31 ❌ foo: check failed' \
run_close_step foo=up_to_date
[ "$status" -eq 0 ]
grep -q '^issue close 31 ' "$GH_LOG"
}

@test "check-updates: closes the check issue of a package whose check passed" {
GH_OPEN=$'12 ❌ foo: check failed\n13 ❌ bar: check failed' \
run_close_step foo=up_to_date bar="new_version: 2.0.0" _error_count=0
[ "$status" -eq 0 ]
grep -qx 'issue close 12 --comment The status of foo is `up_to_date` in https://example.invalid/run.' "$GH_LOG"
grep -qx 'issue close 13 --comment The status of bar is `new_version: 2.0.0` in https://example.invalid/run.' "$GH_LOG"
}

@test "check-updates: closes the update issue of a package that reached the AUR" {
GH_OPEN=$'12 ❌ foo: update failed\n13 ❌ bar: update failed\n14 ❌ baz: update failed' \
run_close_step foo=up_to_date bar="updated: 2.0.0" baz="downgrade: 1.0.0"
[ "$status" -eq 0 ]
run grep -c '^issue close' "$GH_LOG"
[ "$output" = "3" ]
}

@test "check-updates: keeps the update issue of a package that has not reached the AUR" {
GH_OPEN=$'12 ❌ foo: update failed\n13 ❌ bar: update failed\n14 ❌ baz: update failed' \
run_close_step foo="new_version: 2.0.0" bar="downgrade_skipped: 1.0.0" baz="update_failed: boom"
[ "$status" -eq 0 ]
run grep -c '^issue close' "$GH_LOG"
[ "$output" = "0" ]
}

@test "check-updates: keeps the issues of a package whose check still fails" {
GH_OPEN=$'12 ❌ foo: check failed\n13 ❌ foo: update failed' \
run_close_step foo="check_failed: API down"
[ "$status" -eq 0 ]
run grep -c '^issue close' "$GH_LOG"
[ "$output" = "0" ]
}

@test "check-updates: closes no issue of another package" {
GH_OPEN=$'12 ❌ foo-bar: check failed\n13 ❌ Check AUR Package Updates failed' \
run_close_step foo=up_to_date
[ "$status" -eq 0 ]
run grep -c '^issue close' "$GH_LOG"
[ "$output" = "0" ]
}

@test "check-updates: the close step lists nothing when Issues are disabled" {
GH_HAS_ISSUES=false GH_FAIL=1 run_close_step foo=up_to_date
[ "$status" -eq 0 ]
run grep -c '^issue ' "$GH_LOG"
[ "$output" = "0" ]
}

@test "check-updates: the close step fails when the open issues cannot be listed" {
GH_FAIL=1 run_close_step foo=up_to_date
[ "$status" -ne 0 ]
run grep -c '^issue close' "$GH_LOG"
[ "$output" = "0" ]
}

@test "check-updates: verify runs as builder and hands over as prepare does" {
run step_run verify "Verify checksums"
[[ "$output" == *"sudo -u builder ./scripts/check-all.sh --verify handoff"* ]]
Expand Down
Loading