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
6 changes: 4 additions & 2 deletions .github/workflows/check-updates.yml
Original file line number Diff line number Diff line change
Expand Up @@ -420,8 +420,10 @@ jobs:

gh label create "auto-update" --color "d73a4a" --description "Automated update failure" 2>/dev/null || true

existing=$(gh issue list --label "auto-update" --search "$pkg_name failed" --state open --json number --jq 'length')
if [[ "$existing" -gt 0 ]]; then
# Exact titles: search splits foo-bar into words, so a
# search for foo also finds the issue of foo-bar
titles=$(gh issue list --label "auto-update" --state open --limit 1000 --json title --jq '.[].title')
if grep -qxF -e "❌ $pkg_name: check failed" -e "❌ $pkg_name: update failed" <<<"$titles"; then
echo "Issue already open for $pkg_name, skipping"
continue
fi
Expand Down
2 changes: 1 addition & 1 deletion docs/WORKFLOW.md
Original file line number Diff line number Diff line change
Expand Up @@ -766,7 +766,7 @@ The message is `chore: update package versions [skip ci]`. `[skip ci]` keeps the
The step runs with `if: always()`. For every `.status/` entry with `check_failed:` or `update_failed:`, it:

1. Creates the `auto-update` label when it is missing.
2. Skips the package when an open `auto-update` issue already matches `<package> failed`.
2. Skips the package when an open `auto-update` issue already has the title `❌ <package>: check failed` or `❌ <package>: update failed`, so a package never gets a second issue while one is open. The step compares the titles of up to 1000 open issues and uses no search, because a search splits `foo-bar` into words and finds the issue of `foo-bar` for `foo`.
3. Opens an issue titled `❌ <package>: check failed` or `❌ <package>: update failed`, after the status, with the package, the status, a link to the run and the error. The error holds text from upstream, a rejected tag name for example, so it stands in a code block, where links, images and mentions do not render, and is cut to its last 4000 characters. `builder` writes the body with `failure_issue_body` from `lib.sh`, so the step runs no code from the workspace as root.

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.
Expand Down
67 changes: 67 additions & 0 deletions tests/workflows.bats
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,73 @@ EOF
[[ "$output" == *"::error::"* ]]
}

# Runs the issue step of publish in a workspace with the statuses given as
# name=status pairs. gh lists the titles in $GH_TITLES, one per line, as
# every open issue whatever --search asks for, the first 30 unless --limit
# says otherwise, and logs every call to $GH_LOG.
run_issue_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"
[[ "$1 $2" == "issue list" ]] || exit 0
[[ -z "${GH_FAIL:-}" ]] || exit 1
limit=30 jq_expr=""
while (($#)); do
case "$1" in
--limit) limit=$2 && shift ;;
--jq) jq_expr=$2 && shift ;;
esac
shift
done
titles=$(grep -v '^$' <<<"${GH_TITLES:-}" | head -n "$limit")
case "$jq_expr" in
length) grep -c . <<<"$titles" ;;
".[].title") [[ -z "$titles" ]] || printf '%s\n' "$titles" ;;
*) exit 2 ;;
esac
EOF
printf '#!/bin/sh\necho body\n' >"$bin/sudo"
chmod +x "$bin/gh" "$bin/sudo"
GH_LOG="$BATS_TEST_TMPDIR/gh.log"
: >"$GH_LOG"
step_run publish "Create issues for failed packages" >"$BATS_TEST_TMPDIR/step.sh"
run env -C "$ws" PATH="$bin:$PATH" GH_LOG="$GH_LOG" GH_TITLES="${GH_TITLES:-}" \
GH_FAIL="${GH_FAIL:-}" RUN_URL=https://example.invalid/run sh -e "$BATS_TEST_TMPDIR/step.sh"
}

@test "check-updates: an open issue of another package holds back no issue" {
GH_TITLES="❌ foo-bar: update failed" run_issue_step foo="update_failed: boom"
[ "$status" -eq 0 ]
grep -q '^issue create --title ❌ foo: update failed ' "$GH_LOG"
}

@test "check-updates: an open issue of the package holds back another one" {
GH_TITLES="❌ foo: check failed" run_issue_step foo="update_failed: boom"
[ "$status" -eq 0 ]
run grep -c '^issue create' "$GH_LOG"
[ "$output" = "0" ]
}

@test "check-updates: the issue step finds an open issue behind 30 others" {
GH_TITLES="$(printf '❌ pkg%s: check failed\n' {1..30})"$'\n❌ foo: update failed' \
run_issue_step foo="update_failed: boom"
[ "$status" -eq 0 ]
run grep -c '^issue create' "$GH_LOG"
[ "$output" = "0" ]
}

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

@test "check-updates: publish takes the statuses from the check job" {
run yq '.jobs.publish.steps[] | select(.with.name == "checked") | .with.path' "$CHECK_UPDATES"
[ "$output" = ".status" ]
Expand Down
Loading