From 1d02400e90256264359c4a9027bd97bd138a414f Mon Sep 17 00:00:00 2001 From: Nawid3333 <102778966+Nawid3333@users.noreply.github.com> Date: Mon, 9 Nov 2026 19:11:21 +0100 Subject: [PATCH 1/2] ci: one email a day: a daily digest, and no other workflow mentions or assigns The owner's wish (2026-11-09): "just one email per repo that gets sent daily, which summarizes all the stuff". GitHub emails an @mention and an assignment, so: - No workflow @mentions or assigns the owner any more (16 workflows, their comments and issues written plainly); tests/workflows/no-mentions.test.sh fails if one does again. - daily-digest.yml, daily at 10:00 UTC, comments once on the pinned issue "Daily digest", @mentioning the owner once: failed runs that ended in the window (by workflow id and branch, with how each stands now), the open PRs with update-prs.yml's verdict, PRs merged or closed, issues opened or closed, releases, comments from others (review comments too), open and new security alerts. The window runs from the last digest's marker to ten minutes before the run; a quiet day posts nothing. Its own failure @mentions the owner. - docs/maintenance.md, "Email: one a day": the GitHub settings that send the rest. Reviewed by Codex (8 findings, all taken) and GLM (unmention diff: none; digest: 3 of 4). Co-Authored-By: Claude Opus 5.5 --- .github/actionlint.yaml | 5 +- .github/workflows/amo-signing-failsafe.yml | 27 +- .github/workflows/auto-release.yml | 22 +- .github/workflows/ci.yml | 22 +- .github/workflows/daily-digest.yml | 230 ++++++++++++ .github/workflows/firefox-beta.yml | 9 +- .github/workflows/firefox-stable.yml | 13 +- .github/workflows/flaky-specs.yml | 19 +- .github/workflows/keepalive.yml | 15 +- .github/workflows/live-streams.yml | 20 +- .github/workflows/mpv-updates.yml | 36 +- .github/workflows/outage-retry.yml | 5 +- .github/workflows/patched-libraries.yml | 24 +- .github/workflows/runner-images.yml | 13 +- .github/workflows/security-alerts.yml | 50 ++- .github/workflows/toolchain-updates.yml | 20 +- .github/workflows/update-prs.yml | 42 ++- .github/workflows/upstream-watch.yml | 24 +- .github/workflows/vendored-updates.yml | 17 +- docs/maintenance.md | 46 ++- docs/notes/build-and-release.md | 2 +- docs/notes/mpv.md | 4 +- docs/notes/workflows.md | 64 ++-- docs/updating-patched-libraries.md | 4 +- tests/workflows/amo-signing-failsafe.test.sh | 2 +- tests/workflows/auto-release.test.sh | 4 +- tests/workflows/daily-digest.test.sh | 326 ++++++++++++++++++ tests/workflows/flaky-specs.test.sh | 14 +- tests/workflows/keepalive.test.sh | 12 +- tests/workflows/live-streams-hand-off.test.sh | 2 +- tests/workflows/no-mentions.test.sh | 31 ++ tests/workflows/outage-retry.test.sh | 2 +- tests/workflows/release-hand-off.test.sh | 16 +- tests/workflows/security-alerts.test.sh | 26 +- .../security-overrides-demote.test.sh | 2 +- tests/workflows/toolchain-updates.test.sh | 4 +- tests/workflows/update-prs-decide.test.sh | 28 +- tests/workflows/update-prs-hand-off.test.sh | 6 +- tests/workflows/update-watch.test.sh | 8 +- tests/workflows/upstream-watch.test.sh | 16 +- tests/workflows/vendored-updates.test.sh | 2 +- 41 files changed, 918 insertions(+), 316 deletions(-) create mode 100644 .github/workflows/daily-digest.yml create mode 100644 tests/workflows/daily-digest.test.sh create mode 100644 tests/workflows/no-mentions.test.sh diff --git a/.github/actionlint.yaml b/.github/actionlint.yaml index 80df1cdbb..8ad169d6f 100644 --- a/.github/actionlint.yaml +++ b/.github/actionlint.yaml @@ -1,7 +1,7 @@ # Read by actionlint from here, in ci.yml's workflows job and in the WSL verify. # # GitHub's `vulnerability-alerts` permission (read the Dependabot alerts, which -# security-alerts.yml needs) is newer than actionlint 1.7.12, the latest release; the +# security-alerts.yml and daily-digest.yml need) is newer than actionlint 1.7.12, the latest release; the # request for it is rhysd/actionlint#666. Checked 2026-09-30: without it the token's # request for the alerts is refused (HTTP 403), with it they are listed. Drop this once # .github/actionlint/Dockerfile pins an actionlint that knows the permission. @@ -9,3 +9,6 @@ paths: .github/workflows/security-alerts.yml: ignore: - 'unknown permission scope "vulnerability-alerts"' + .github/workflows/daily-digest.yml: + ignore: + - 'unknown permission scope "vulnerability-alerts"' diff --git a/.github/workflows/amo-signing-failsafe.yml b/.github/workflows/amo-signing-failsafe.yml index 391ccdc0f..8eb8896b8 100644 --- a/.github/workflows/amo-signing-failsafe.yml +++ b/.github/workflows/amo-signing-failsafe.yml @@ -30,8 +30,8 @@ name: Release failsafe # version and the add-on id. Broken: the issue "Update path broken: ". A check # the network stopped is not reported; the next run makes it again. # -# Every issue is assigned to the owner and @mentions them, so it arrives by email; it is -# opened once, and closed by the first run that finds it resolved. A run of this workflow +# Every issue goes into the daily digest (daily-digest.yml), the owner's one email a day; it +# is opened once, and closed by the first run that finds it resolved. A run of this workflow # that fails opens "Release failsafe failed" (the runs release.yml starts email no one). on: @@ -61,7 +61,6 @@ jobs: timeout-minutes: 75 env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - OWNER: ${{ github.repository_owner }} SIGNING_PREFIX: 'AMO signing incomplete: ' PATH_PREFIX: 'Update path broken: ' @@ -218,11 +217,11 @@ jobs: # The backticks are Markdown for the issue body, not command substitution. # shellcheck disable=SC2016 { - printf '@%s %s is tagged but has no release: release.yml failed %s times for it, the last of them started by this failsafe. Firefox does not get this version until it is published.\n\n' "$OWNER" "$ORPHAN" "$attempts" + printf '%s is tagged but has no release: release.yml failed %s times for it, the last of them started by this failsafe. Firefox does not get this version until it is published.\n\n' "$ORPHAN" "$attempts" printf 'The runs: %s\n\n' "$runs_url" printf 'Fix what they report, then run release.yml for the tag again: `gh workflow run release.yml --ref %s`. This issue closes on its own once %s has its release.\n' "$ORPHAN" "$ORPHAN" } > "$RUNNER_TEMP/body.md" - gh issue create --repo "$GITHUB_REPOSITORY" --title "$title" --assignee "$OWNER" --body-file "$RUNNER_TEMP/body.md" + gh issue create --repo "$GITHUB_REPOSITORY" --title "$title" --body-file "$RUNNER_TEMP/body.md" # --- The latest release is complete: check the update path Firefox follows. @@ -264,12 +263,12 @@ jobs: fi run_url="$GITHUB_SERVER_URL/$GITHUB_REPOSITORY/actions/runs/$GITHUB_RUN_ID" { - printf '@%s Firefox cannot update to %s: its update check follows the latest release'"'"'s updates.json, and a link on that path does not hold.\n\n' "$OWNER" "$TAG" + printf 'Firefox cannot update to %s: its update check follows the latest release'"'"'s updates.json, and a link on that path does not hold.\n\n' "$TAG" printf '```\n' cat "$RUNNER_TEMP/path.txt" printf '```\n\nThe run: %s\nThis issue closes on its own once a run finds the path whole.\n' "$run_url" } > "$RUNNER_TEMP/body.md" - gh issue create --repo "$GITHUB_REPOSITORY" --title "$title" --assignee "$OWNER" --body-file "$RUNNER_TEMP/body.md" + gh issue create --repo "$GITHUB_REPOSITORY" --title "$title" --body-file "$RUNNER_TEMP/body.md" # --- The latest release is incomplete: collect what AMO signed. @@ -370,17 +369,17 @@ jobs: # The backticks are Markdown for the issue body, not command substitution. # shellcheck disable=SC2016 { - printf '@%s %s has no signed xpi and updates.json yet, %s hours after it was published. AMO says: **%s**.\n\n' "$OWNER" "$TAG" "$AGE" "$STATE" + printf '%s has no signed xpi and updates.json yet, %s hours after it was published. AMO says: **%s**.\n\n' "$TAG" "$AGE" "$STATE" printf 'Until it does, Firefox finds no update: its update check reads the latest release'"'"'s updates.json.\n\n' printf -- '- `pending`: still in AMO review. This workflow keeps checking every 3 hours and closes this issue once it is attached.\n' printf -- '- `rejected`: AMO disabled or rejected the version. Look it up on the AMO developer hub; waiting will not help.\n' printf -- '- `error`: see the run: %s\n' "$run_url" } > "$RUNNER_TEMP/body.md" - gh issue create --repo "$GITHUB_REPOSITORY" --title "$title" --assignee "$OWNER" --body-file "$RUNNER_TEMP/body.md" + gh issue create --repo "$GITHUB_REPOSITORY" --title "$title" --body-file "$RUNNER_TEMP/body.md" # A failed run is emailed only to whoever started it: for the runs release.yml starts, # this repository's token, so no one; for the schedule, whoever last changed the cron - # line. One issue while it is open, assigned and @mentioning the owner, as the other + # line. One issue while it is open, which the daily digest lists, as the other # workflows do; a failure while it is open adds a comment to it. - name: Report this workflow's own failure if: failure() @@ -393,10 +392,10 @@ jobs: number=$(gh issue list --repo "$GITHUB_REPOSITORY" --state open --limit 200 --json number,title \ --jq '[.[] | select(.title == env.TITLE)][0].number // empty') || number='' if [ -n "$number" ]; then - printf '@%s Failed again: %s\n' "$OWNER" "$RUN_URL" > "$RUNNER_TEMP/comment.md" + printf 'Failed again: %s\n' "$RUN_URL" > "$RUNNER_TEMP/comment.md" gh issue comment "$number" --repo "$GITHUB_REPOSITORY" --body-file "$RUNNER_TEMP/comment.md" exit 0 fi - printf '@%s amo-signing-failsafe.yml (Release failsafe) failed: %s\n\nThe run log names the failed command. Until it runs cleanly, a release that did not publish or sign, or a broken update path, may go unnoticed. Close this issue once it is fixed; a later failure opens a new one.\n' \ - "$OWNER" "$RUN_URL" > "$RUNNER_TEMP/body.md" - gh issue create --repo "$GITHUB_REPOSITORY" --title "$TITLE" --assignee "$OWNER" --body-file "$RUNNER_TEMP/body.md" + printf 'amo-signing-failsafe.yml (Release failsafe) failed: %s\n\nThe run log names the failed command. Until it runs cleanly, a release that did not publish or sign, or a broken update path, may go unnoticed. Close this issue once it is fixed; a later failure opens a new one.\n' \ + "$RUN_URL" > "$RUNNER_TEMP/body.md" + gh issue create --repo "$GITHUB_REPOSITORY" --title "$TITLE" --body-file "$RUNNER_TEMP/body.md" diff --git a/.github/workflows/auto-release.yml b/.github/workflows/auto-release.yml index 49bc61240..0b5aaf917 100644 --- a/.github/workflows/auto-release.yml +++ b/.github/workflows/auto-release.yml @@ -358,7 +358,7 @@ jobs: done exit 1 - # A failed release: one issue while it is open, assigned and @mentioning the owner. A + # A failed release: one issue while it is open, which the daily digest lists. A # run ci.yml started emails no one. A run on CI's workflow_run event should email the # one who pushed, but no failed run there has ever shown it (none of 91 runs failed), # and a release missed in silence leaves main unreleased; so it reports too. A @@ -370,7 +370,6 @@ jobs: env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} GH_REPO: ${{ github.repository }} - OWNER: ${{ github.repository_owner }} EVENT: ${{ github.event_name }} SELF_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} TITLE: 'Auto release failed' @@ -382,7 +381,7 @@ jobs: number=$(gh issue list --state open --limit 200 --json number,title \ --jq '[.[] | select(.title == env.TITLE)][0].number // empty') || number='' if [ -n "$number" ]; then - printf '@%s Auto release failed again: %s\n' "$OWNER" "$SELF_URL" > "$RUNNER_TEMP/comment.md" + printf 'Auto release failed again: %s\n' "$SELF_URL" > "$RUNNER_TEMP/comment.md" gh issue comment "$number" --body-file "$RUNNER_TEMP/comment.md" exit 0 fi @@ -391,9 +390,9 @@ jobs: else what='A CI run on main that this repository started (a re-run, or a run for pushes that start none) did not lead to a release' fi - printf '@%s %s: %s\n\nThe CI run was red, or a release step failed; the log says which. main'"'"'s newest changes are not released until this is fixed. Close this issue once it is fixed; a failure while it is open adds a comment.\n' \ - "$OWNER" "$what" "$SELF_URL" > "$RUNNER_TEMP/body.md" - gh issue create --title "$TITLE" --assignee "$OWNER" --body-file "$RUNNER_TEMP/body.md" + printf '%s: %s\n\nThe CI run was red, or a release step failed; the log says which. main'"'"'s newest changes are not released until this is fixed. Close this issue once it is fixed; a failure while it is open adds a comment.\n' \ + "$what" "$SELF_URL" > "$RUNNER_TEMP/body.md" + gh issue create --title "$TITLE" --body-file "$RUNNER_TEMP/body.md" # Restarts a CI run on main that a later run cancelled (see the header). After every CI # push run on main ends, whatever it concluded, it finds the newest commit on main with a @@ -434,7 +433,6 @@ jobs: env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} GH_REPO: ${{ github.repository }} - OWNER: ${{ github.repository_owner }} TITLE: 'CI on main needs a re-run' run: | set -euo pipefail @@ -457,14 +455,14 @@ jobs: number=$(gh issue list --state open --limit 200 --json number,title \ --jq '[.[] | select(.title == env.TITLE)][0].number // empty') || number='' if [ -n "$number" ]; then - printf '@%s CI on main for %s cannot finish either: %s. The run: %s\n' \ - "$OWNER" "${sha:0:8}" "$1" "$GITHUB_SERVER_URL/$GH_REPO/actions/runs/$id" > "$RUNNER_TEMP/comment.md" + printf 'CI on main for %s cannot finish either: %s. The run: %s\n' \ + "${sha:0:8}" "$1" "$GITHUB_SERVER_URL/$GH_REPO/actions/runs/$id" > "$RUNNER_TEMP/comment.md" gh issue comment "$number" --body-file "$RUNNER_TEMP/comment.md" exit 0 fi - printf '@%s CI on main for %s cannot finish: %s. Its changes are not released until it passes.\n\nThe run: %s\n\nRe-run it from that page (a re-run you start releases as usual once it is green), or push. This issue closes on its own at the next green CI run on main.\n' \ - "$OWNER" "${sha:0:8}" "$1" "$GITHUB_SERVER_URL/$GH_REPO/actions/runs/$id" > "$RUNNER_TEMP/body.md" - gh issue create --title "$TITLE" --assignee "$OWNER" --body-file "$RUNNER_TEMP/body.md" + printf 'CI on main for %s cannot finish: %s. Its changes are not released until it passes.\n\nThe run: %s\n\nRe-run it from that page (a re-run you start releases as usual once it is green), or push. This issue closes on its own at the next green CI run on main.\n' \ + "${sha:0:8}" "$1" "$GITHUB_SERVER_URL/$GH_REPO/actions/runs/$id" > "$RUNNER_TEMP/body.md" + gh issue create --title "$TITLE" --body-file "$RUNNER_TEMP/body.md" exit 0 } sha='' diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a075f53c0..198aedcfa 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -527,7 +527,6 @@ jobs: env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} GH_REPO: ${{ github.repository }} - OWNER: ${{ github.repository_owner }} RUN_ID: ${{ github.run_id }} # A pull_request run's ref is the pull request's merge ref; its branch is head_ref. BRANCH: ${{ github.head_ref || github.ref_name }} @@ -543,13 +542,13 @@ jobs: number=$(gh issue list --state open --limit 200 --json number,title \ --jq '[.[] | select(.title == env.TITLE)][0].number // empty') || number='' if [ -n "$number" ]; then - printf '@%s GitHub refused three times again, for CI run %s on %s (%s): start update-prs.yml by hand with run_id %s and branch %s.\n' \ - "$OWNER" "$RUN_ID" "$BRANCH" "$SELF_URL" "$RUN_ID" "$BRANCH" > "$RUNNER_TEMP/comment.md" + printf 'GitHub refused three times again, for CI run %s on %s (%s): start update-prs.yml by hand with run_id %s and branch %s.\n' \ + "$RUN_ID" "$BRANCH" "$SELF_URL" "$RUN_ID" "$BRANCH" > "$RUNNER_TEMP/comment.md" gh issue comment "$number" --body-file "$RUNNER_TEMP/comment.md" || true else - printf '@%s GitHub refused three times to start update-prs.yml for CI run %s on %s (%s), a run this repository started, so that pull request waits without a word. Start it from the Actions tab: Update PRs, Run workflow, run_id %s and branch %s. Close this issue once it has run.\n' \ - "$OWNER" "$RUN_ID" "$BRANCH" "$SELF_URL" "$RUN_ID" "$BRANCH" > "$RUNNER_TEMP/body.md" - gh issue create --title "$TITLE" --assignee "$OWNER" --body-file "$RUNNER_TEMP/body.md" || true + printf 'GitHub refused three times to start update-prs.yml for CI run %s on %s (%s), a run this repository started, so that pull request waits without a word. Start it from the Actions tab: Update PRs, Run workflow, run_id %s and branch %s. Close this issue once it has run.\n' \ + "$RUN_ID" "$BRANCH" "$SELF_URL" "$RUN_ID" "$BRANCH" > "$RUNNER_TEMP/body.md" + gh issue create --title "$TITLE" --body-file "$RUNNER_TEMP/body.md" || true fi exit 1 @@ -586,7 +585,6 @@ jobs: env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} GH_REPO: ${{ github.repository }} - OWNER: ${{ github.repository_owner }} RUN_ID: ${{ github.run_id }} SHA: ${{ github.sha }} SELF_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} @@ -601,12 +599,12 @@ jobs: number=$(gh issue list --state open --limit 200 --json number,title \ --jq '[.[] | select(.title == env.TITLE)][0].number // empty') || number='' if [ -n "$number" ]; then - printf '@%s GitHub refused three times to start auto-release.yml, for CI run %s on main (%s): %s. Start it from the Actions tab: Auto release, Run workflow, run_id %s.\n' \ - "$OWNER" "$RUN_ID" "${SHA:0:8}" "$SELF_URL" "$RUN_ID" > "$RUNNER_TEMP/comment.md" + printf 'GitHub refused three times to start auto-release.yml, for CI run %s on main (%s): %s. Start it from the Actions tab: Auto release, Run workflow, run_id %s.\n' \ + "$RUN_ID" "${SHA:0:8}" "$SELF_URL" "$RUN_ID" > "$RUNNER_TEMP/comment.md" gh issue comment "$number" --body-file "$RUNNER_TEMP/comment.md" || true else - printf '@%s GitHub refused three times to start auto-release.yml for CI run %s on main (%s), a run this repository started. Start it from the Actions tab: Auto release, Run workflow, run_id %s. Close this issue once it has run.\n' \ - "$OWNER" "$RUN_ID" "${SHA:0:8}" "$RUN_ID" > "$RUNNER_TEMP/body.md" - gh issue create --title "$TITLE" --assignee "$OWNER" --body-file "$RUNNER_TEMP/body.md" || true + printf 'GitHub refused three times to start auto-release.yml for CI run %s on main (%s), a run this repository started. Start it from the Actions tab: Auto release, Run workflow, run_id %s. Close this issue once it has run.\n' \ + "$RUN_ID" "${SHA:0:8}" "$RUN_ID" > "$RUNNER_TEMP/body.md" + gh issue create --title "$TITLE" --body-file "$RUNNER_TEMP/body.md" || true fi exit 1 diff --git a/.github/workflows/daily-digest.yml b/.github/workflows/daily-digest.yml new file mode 100644 index 000000000..ef208fd0e --- /dev/null +++ b/.github/workflows/daily-digest.yml @@ -0,0 +1,230 @@ +name: Daily digest + +# One email a day instead of one for every event (the owner's wish, 2026-11-09: "just one +# email per repo that gets sent daily, which summarizes all the stuff"). Every other workflow +# writes its comments and issues without an @mention and assigns nobody, so GitHub emails +# none of them; this one comments once a day on the pinned issue "Daily digest", @mentioning +# the owner once, with everything since its last comment: +# +# - workflow runs that failed, by workflow and branch, and how each stands now; +# - the open pull requests, each with update-prs.yml's latest verdict; +# - pull requests merged or closed, issues opened or closed, releases published; +# - comments from anyone but the owner and the bots; +# - the open Dependabot and code scanning alerts, and which are new. +# +# A day with nothing of that posts nothing. GitHub's own emails (a failed run you started, a +# Dependabot alert) are the owner's settings, not this file's: docs/maintenance.md, "Email". +# The issue is made, and pinned, when there is none open; closing it starts a new one. + +on: + schedule: + # Daily, 10:00 UTC: after Dependabot's and toolchain-updates.yml's morning runs (07:00) + # and update-prs.yml's verdicts on their CI. + - cron: '0 10 * * *' + workflow_dispatch: + +permissions: {} # the job asks for what it needs + +concurrency: + group: daily-digest + cancel-in-progress: false + +jobs: + digest: + name: Post the daily digest + permissions: + issues: write # the digest issue and its comment + pull-requests: read # the pull requests and their verdicts + actions: read # the failed runs + contents: read # the releases + vulnerability-alerts: read # the open Dependabot alerts + security-events: read # the open code scanning alerts + runs-on: ubuntu-latest + timeout-minutes: 10 + env: + GH_TOKEN: ${{ github.token }} + GH_REPO: ${{ github.repository }} + OWNER: ${{ github.repository_owner }} + SELF_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} + steps: + - name: Post the digest + run: | + set -euo pipefail + title='Daily digest' + t=$RUNNER_TEMP + # The window is [since, now): now is taken before anything is read, and goes into + # this digest's marker, so what happens while it runs comes in the next one. Ten + # minutes back: GitHub's search finds an issue closed seconds ago only a little later, + # and the next digest has those ten minutes. + now=$(date -u -d '-10 minutes' '+%Y-%m-%dT%H:%M:%SZ') + + # --- The digest issues, by search (not among the newest issues): the open one, made + # and pinned when there is none, and the closed ones, newest first, for the cursor. + gh issue list --state all --limit 100 --search "\"$title\" in:title" --json number,title,state,author \ + --jq "[.[] | select(.title == \"$title\" and .author.login == \"app/github-actions\")] | + sort_by(.state != \"OPEN\", -.number) | .[] | \"\(.number) \(.state)\"" > "$t/digests.txt" + issue=$(awk '$2 == "OPEN" { print $1; exit }' "$t/digests.txt") + if [ -z "$issue" ]; then + # shellcheck disable=SC2016 # backticks are Markdown + printf 'Once a day, %s, a comment here sums up what happened in this repository: failed workflow runs, pull requests and their verdicts, issues, releases, comments, security alerts (`.github/workflows/daily-digest.yml`). The other workflows mention nobody, so this is the one email a day.\n\nClosing this issue starts a new one, which goes on from where this one stopped.\n' \ + '10:00 UTC' > "$t/issue.md" + url=$(gh issue create --title "$title" --body-file "$t/issue.md") + issue=${url##*/} + gh issue pin "$issue" > /dev/null 2>&1 || echo 'Could not pin the digest issue.' + fi + + # --- Since: where the last digest stopped, read from its marker (in the open issue, + # else the newest closed one that has one); at most a week back; else a day. + since='' + while read -r n _; do + since=$(gh api --paginate "repos/$GH_REPO/issues/$n/comments?per_page=100" \ + --jq '.[] | select(.user.login == "github-actions[bot]") | .body' < /dev/null | + grep -oE '' | tail -n 1 | sed -E 's//\1/' || true) + if [ -n "$since" ]; then break; fi + done < "$t/digests.txt" + week=$(date -u -d '-7 days' '+%Y-%m-%dT%H:%M:%SZ') + if [ -z "$since" ]; then since=$(date -u -d '-1 day' '+%Y-%m-%dT%H:%M:%SZ'); fi + if [[ $since < $week ]]; then since=$week; fi + echo "From $since to $now." + new() { [[ ! $1 < $since && $1 < $now ]]; } # an ISO time in [since, now) + + # --- Workflow runs that failed: those that ended in the window (a run started a day + # before it and still going at the last digest counts now), fetched by conclusion so + # the runs API's 1000-result cap is far off. Grouped by workflow (its id: a renamed + # one stays one) and branch, each with the newest run there and how it stands. + back=$(date -u -d "@$(($(date -u -d "$since" +%s) - 86400))" '+%Y-%m-%dT%H:%M:%SZ') + for c in failure timed_out startup_failure; do + gh api --paginate "repos/$GH_REPO/actions/runs?status=$c&created=%3E%3D$back&per_page=100" \ + --jq '.workflow_runs[] | {id, workflow_id, name, path, branch: (.head_branch // "-"), updated_at}' + done | jq -s --arg since "$since" --arg now "$now" \ + '[.[] | select(.updated_at >= $since and .updated_at < $now and .path != ".github/workflows/daily-digest.yml")] | + unique_by(.id) | group_by([.workflow_id, .branch]) | + map({workflow_id: .[0].workflow_id, branch: .[0].branch, name: (max_by(.id) | .name), failed: length})' > "$t/failed.json" + : > "$t/failed.md" + # (No field is empty: tab is whitespace to read, and an empty one would shift the rest. + # A run with no branch has "-", and its workflow's newest run is taken on any branch.) + while IFS=$'\t' read -r wid branch name failed; do + on_branch='' + if [ "$branch" != - ]; then on_branch="branch=$(jq -rn --arg b "$branch" '$b | @uri')&"; fi + newest=$(gh api "repos/$GH_REPO/actions/workflows/$wid/runs?${on_branch}per_page=1" \ + --jq '.workflow_runs[0] | "\(if .status != "completed" then "running" elif .conclusion == "success" then "passed since" else .conclusion end)\t\(.html_url)"' < /dev/null || true) + state=${newest%%$'\t'*} + # A | in a name or branch would split the table's cell. + # shellcheck disable=SC2016 # backticks are Markdown + printf '| %s | `%s` | %s | [%s](%s) |\n' "${name//|/\\|}" "${branch//|/\\|}" "$failed" "${state:-unknown}" "${newest#*$'\t'}" >> "$t/failed.md" + done < <(jq -r 'sort_by(.branch != "main", .name, .branch)[] | [.workflow_id, .branch, (.name // "-"), .failed] | @tsv' "$t/failed.json") + + # --- Open pull requests, each with update-prs.yml's latest verdict. A verdict is new + # when its comment was made in the window: update-prs.yml edits a comment for the same + # verdict again and makes a new one for a new verdict. + gh pr list --state open --limit 1000 --json number,title,url,createdAt,labels > "$t/prs.json" + : > "$t/open-prs.md" + changed=0 + while IFS=$'\t' read -r n prtitle url created labels; do + key=$(gh api --paginate "repos/$GH_REPO/issues/$n/comments?per_page=100" \ + --jq '.[] | select(.user.login == "github-actions[bot]") | {u: .created_at, b: .body}' < /dev/null | + jq -rs 'map(select(.b | test(""))) | last // empty | + "\(.u)\t\(.b | [scan("")] | last | .[0])"') + when=${key%%$'\t'*} + key=${key#*$'\t'} + case $key in + '') verdict='no verdict yet' ;; + ready) verdict='ready to merge' ;; + look) verdict='look at it before merging' ;; + red*) verdict='CI fails' ;; + 'outage stuck'*) verdict='outage: start CI on it by hand' ;; + outage*) verdict='outage: CI runs again later' ;; + *) verdict=$key ;; + esac + fresh='' + if new "$created"; then fresh=' (new)'; changed=1 + elif [ -n "$when" ] && new "$when"; then fresh=' (verdict changed)'; changed=1; fi + printf -- '- [#%s](%s) %s: **%s**%s%s\n' "$n" "$url" "$prtitle" "$verdict" "${labels:+ [$labels]}" "$fresh" >> "$t/open-prs.md" + done < <(jq -r '.[] | [.number, .title, .url, .createdAt, ([.labels[].name] | join(", "))] | @tsv' "$t/prs.json") + + # --- Merged or closed pull requests, opened or closed issues (not the digest's), + # releases: each in [since, now). + in_window=". >= \"$since\" and . < \"$now\"" + gh pr list --state closed --limit 1000 --search "closed:>=$since" --json number,title,url,mergedAt,closedAt \ + --jq ".[] | select(.closedAt | $in_window) | \"- [#\(.number)](\(.url)) \(.title): \(if .mergedAt then \"merged\" else \"closed\" end)\"" > "$t/closed-prs.md" + gh issue list --state all --limit 1000 --search "created:>=$since" --json number,title,url,state,createdAt \ + --jq ".[] | select(.number != $issue and .title != \"$title\" and (.createdAt | $in_window)) | \"- [#\(.number)](\(.url)) \(.title)\(if .state == \"CLOSED\" then \" (closed since)\" else \"\" end)\"" > "$t/opened-issues.md" + gh issue list --state closed --limit 1000 --search "closed:>=$since" --json number,title,url,closedAt \ + --jq ".[] | select(.number != $issue and .title != \"$title\" and (.closedAt | $in_window)) | \"- [#\(.number)](\(.url)) \(.title)\"" > "$t/closed-issues.md" + gh release list --limit 100 --json tagName,publishedAt,isDraft \ + --jq ".[] | select((.isDraft | not) and (.publishedAt | $in_window)) | \"- [\(.tagName)]($GITHUB_SERVER_URL/$GH_REPO/releases/tag/\(.tagName))\"" > "$t/releases.md" + + # --- Comments from anyone but the owner and the bots: on issues and pull requests, + # and on a pull request's lines (review comments, which the first list leaves out). + for kind in issues/comments pulls/comments; do + gh api --paginate "repos/$GH_REPO/$kind?since=$since&per_page=100" \ + --jq ".[] | select(.user.type != \"Bot\" and .user.login != \"$OWNER\" and (.created_at | $in_window)) | + \"- \(.user.login) on [#\((.issue_url // .pull_request_url) | split(\"/\") | last)](\(.html_url))\"" + done > "$t/comments.md" + + # --- Open security alerts, and the new ones. Unreadable: said so, not fatal. + alerts() { # + local all + if all=$(gh api --paginate "repos/$GH_REPO/$1?state=open&per_page=100" --jq '.[] | .created_at' 2> /dev/null); then + local open fresh + open=$(grep -c . <<< "$all" || true) + fresh=$(while read -r c; do if [ -n "$c" ] && new "$c"; then echo x; fi; done <<< "$all" | grep -c . || true) + if [ "$open" != 0 ]; then + printf -- '- %s: %s open, %s new\n' "$2" "$open" "$fresh" + if [ "$fresh" != 0 ]; then changed=1; fi + fi + else + printf -- '- %s: could not be read\n' "$2" + fi + } + { alerts dependabot/alerts 'Dependabot alerts'; alerts code-scanning/alerts 'Code scanning alerts'; } > "$t/alerts.md" + + # --- Nothing new: no comment, no email. + if [ "$changed" = 0 ] && ! [ -s "$t/failed.md" ] && ! [ -s "$t/closed-prs.md" ] && ! [ -s "$t/opened-issues.md" ] && + ! [ -s "$t/closed-issues.md" ] && ! [ -s "$t/releases.md" ] && ! [ -s "$t/comments.md" ]; then + echo 'Nothing new since the last digest: no comment.' + exit 0 + fi + section() { # <file> + if [ -s "$2" ]; then printf '\n### %s\n\n' "$1"; cat "$2"; fi + } + # Where the next digest goes on from. + marker="<!-- daily-digest: $now -->" + { + printf '@%s What happened in this repository from %s to %s UTC.\n' "$OWNER" "${since/T/ }" "${now/T/ }" + if [ -s "$t/failed.md" ]; then + printf '\n### Failed workflow runs\n\n| Workflow | Branch | Failed | Newest run |\n| --- | --- | --- | --- |\n' + cat "$t/failed.md" + fi + section 'Open pull requests' "$t/open-prs.md" + section 'Pull requests merged or closed' "$t/closed-prs.md" + section 'New issues' "$t/opened-issues.md" + section 'Issues closed' "$t/closed-issues.md" + section 'Releases' "$t/releases.md" + section 'Comments from others' "$t/comments.md" + section 'Security alerts' "$t/alerts.md" + printf '\n<sub>%s</sub>\n%s\n' "$SELF_URL" "$marker" + } > "$t/digest.md" + # GitHub takes 65536 characters in a comment. + if [ "$(wc -c < "$t/digest.md")" -gt 60000 ]; then + { head -c 59000 "$t/digest.md"; printf '\n\n(cut here: the full digest is in the run log, %s)\n%s\n' "$SELF_URL" "$marker"; } > "$t/cut.md" + cat "$t/digest.md" + mv "$t/cut.md" "$t/digest.md" + fi + gh issue comment "$issue" --body-file "$t/digest.md" + + # The digest is the one email: when it fails, nothing else would say so (the other + # workflows mention no one, and the owner turns GitHub's Actions emails off). Its next + # run starts at the last digest, so it covers this day as well. + - name: Say the digest failed + if: failure() + run: | + set -euo pipefail + printf '@%s The daily digest failed: %s. The next one also covers what this one would have.\n' "$OWNER" "$SELF_URL" > "$RUNNER_TEMP/failed.md" + issue=$(gh issue list --state open --limit 200 --json number,title,author \ + --jq '[.[] | select(.title == "Daily digest" and .author.login == "app/github-actions")][0].number // empty') + if [ -n "$issue" ]; then + gh issue comment "$issue" --body-file "$RUNNER_TEMP/failed.md" + else + gh issue create --title 'Daily digest failed' --body-file "$RUNNER_TEMP/failed.md" + fi diff --git a/.github/workflows/firefox-beta.yml b/.github/workflows/firefox-beta.yml index de91f2b17..1f72f3d74 100644 --- a/.github/workflows/firefox-beta.yml +++ b/.github/workflows/firefox-beta.yml @@ -7,8 +7,8 @@ name: Firefox Beta # week, so each Beta gets several runs and "the Firefox update broke FastStream" arrives # as a two-week warning instead of on the day the browser updates. # -# A scheduled failure opens one issue, assigned to the owner with an @mention and naming -# the Beta version; the first green scheduled run closes it. A run that failed only because +# A scheduled failure opens one issue naming the Beta version, which the daily digest +# lists; the first green scheduled run closes it. A run that failed only because # GitHub or a download host did not answer (tools/ci-outage.sh) opens none. It also runs on a pull # request that changes this workflow or the e2e configs, so a change to it is tested # before it lands, and by hand. Nothing here releases or blocks a release: it is not @@ -120,7 +120,6 @@ jobs: GH_TOKEN: ${{ github.token }} RESULT: ${{ needs.e2e.result }} VERSION: ${{ needs.e2e.outputs.firefox-version }} - OWNER: ${{ github.repository_owner }} RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} run: | set -euo pipefail @@ -174,9 +173,9 @@ jobs: failed=$(gh run view "$GITHUB_RUN_ID" --repo "$GITHUB_REPOSITORY" --json jobs \ --jq '[.jobs[] | select(.conclusion == "failure" or .conclusion == "cancelled" or .conclusion == "timed_out") | "\(.name) (\(.conclusion))"] | join(", ")') { - printf '@%s The e2e suites fail on **Firefox %s**, which becomes the release in about two weeks: %s\n\n' "$OWNER" "${VERSION:-Beta}" "$RUN_URL" + printf 'The e2e suites fail on **Firefox %s**, which becomes the release in about two weeks: %s\n\n' "${VERSION:-Beta}" "$RUN_URL" printf 'The same suites pass on the current release in CI, so this is a change in Firefox. The run log and its e2e-logs artifact show which spec failed. tests/e2e/specs/firefox.e2e.mjs pins the Firefox behaviour FastStream relies on; a failure there means that behaviour changed.\n\n' printf 'This issue closes on its own once a scheduled run passes again.\n' printf 'Failed: %s\n' "${failed:-see the run}" } > "$RUNNER_TEMP/body.md" - gh issue create --repo "$GITHUB_REPOSITORY" --title "$prefix: Firefox ${VERSION:-Beta}" --assignee "$OWNER" --body-file "$RUNNER_TEMP/body.md" + gh issue create --repo "$GITHUB_REPOSITORY" --title "$prefix: Firefox ${VERSION:-Beta}" --body-file "$RUNNER_TEMP/body.md" diff --git a/.github/workflows/firefox-stable.yml b/.github/workflows/firefox-stable.yml index d269c3eeb..57799b1e7 100644 --- a/.github/workflows/firefox-stable.yml +++ b/.github/workflows/firefox-stable.yml @@ -10,7 +10,7 @@ name: Firefox stable # Each version is tested once: after a green run its version is recorded as an Actions # cache entry (firefox-stable-tested-<version>), and later runs find it and stop. The # playback and installed-extension suites run against exactly that version. A failure -# opens one issue per version, assigned to the owner with an @mention; the next green +# opens one issue per version, which the daily digest lists; the next green # run closes it. Run by hand to test the current stable again regardless. on: @@ -163,7 +163,6 @@ jobs: DETECT: ${{ needs.detect.result }} RESULT: ${{ needs.e2e.result }} VERSION: ${{ needs.detect.outputs.version }} - OWNER: ${{ github.repository_owner }} RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} run: | set -euo pipefail @@ -174,9 +173,9 @@ jobs: echo 'Already reported.' exit 0 fi - printf '@%s The daily Firefox stable check could not read the current release from Mozilla, so no suite ran: %s\n\nThis issue closes on its own once a run reads it again.\n' \ - "$OWNER" "$RUN_URL" > "$RUNNER_TEMP/body.md" - gh issue create --repo "$GITHUB_REPOSITORY" --title "$unreadable" --assignee "$OWNER" --body-file "$RUNNER_TEMP/body.md" + printf 'The daily Firefox stable check could not read the current release from Mozilla, so no suite ran: %s\n\nThis issue closes on its own once a run reads it again.\n' \ + "$RUN_URL" > "$RUNNER_TEMP/body.md" + gh issue create --repo "$GITHUB_REPOSITORY" --title "$unreadable" --body-file "$RUNNER_TEMP/body.md" exit 0 fi gh issue list --repo "$GITHUB_REPOSITORY" --state open --limit 100 --json number,title \ @@ -206,9 +205,9 @@ jobs: failed=$(gh run view "$GITHUB_RUN_ID" --repo "$GITHUB_REPOSITORY" --json jobs \ --jq '[.jobs[] | select(.conclusion == "failure" or .conclusion == "cancelled" or .conclusion == "timed_out") | "\(.name) (\(.conclusion))"] | join(", ")') { - printf '@%s The e2e suites fail on **Firefox %s**, the current stable release - already on your machine once it updates: %s\n\n' "$OWNER" "$VERSION" "$RUN_URL" + printf 'The e2e suites fail on **Firefox %s**, the current stable release - already on your machine once it updates: %s\n\n' "$VERSION" "$RUN_URL" printf 'The run log and its e2e-logs artifact show which spec failed. tests/e2e/specs/firefox.e2e.mjs pins the Firefox behaviour FastStream relies on; a failure there means that behaviour changed.\n\n' printf 'This issue closes on its own once a run on the stable release passes.\n' printf 'Failed: %s\n' "${failed:-see the run}" } > "$RUNNER_TEMP/body.md" - gh issue create --repo "$GITHUB_REPOSITORY" --title "$title" --assignee "$OWNER" --body-file "$RUNNER_TEMP/body.md" + gh issue create --repo "$GITHUB_REPOSITORY" --title "$title" --body-file "$RUNNER_TEMP/body.md" diff --git a/.github/workflows/flaky-specs.yml b/.github/workflows/flaky-specs.yml index d19fe32d2..64b2799b7 100644 --- a/.github/workflows/flaky-specs.yml +++ b/.github/workflows/flaky-specs.yml @@ -4,7 +4,7 @@ name: Flaky e2e specs # run. ci.yml lists each one in its run's summary and uploads the list, one artifact an e2e # job, e2e-retried-<os>-<suite>-<group> (tests/e2e/retriedSpecs.mjs writes it). Once a week # this folds the last 7 days of those lists, all branches, into one issue, "Flaky e2e -# specs: week to <date>", assigned to the owner and @mentioning them: each spec file, how +# specs: week to <date>", which the daily digest lists: each spec file, how # often it was run again, how often its retry passed, and the runs. A spec that keeps # passing only on its retry is a flaky test, or a race like #67's. # @@ -33,7 +33,6 @@ jobs: env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} GH_REPO: ${{ github.repository }} - OWNER: ${{ github.repository_owner }} PREFIX: 'Flaky e2e specs: week to ' steps: - name: Fold the week's retried specs into one issue @@ -105,7 +104,7 @@ jobs: ' "$RUNNER_TEMP/retried.jsonl" > "$RUNNER_TEMP/rows.md" { - printf '@%s E2e spec files that failed once and were run again in the CI runs of the 7 days to %s, all branches.\n\n' "$OWNER" "$today" + printf 'E2e spec files that failed once and were run again in the CI runs of the 7 days to %s, all branches.\n\n' "$today" printf '| Spec file | Run again | Passed on retry | Failed twice | Suite (OS) | Branches | Runs |\n' printf '| --- | --- | --- | --- | --- | --- | --- |\n' cat "$RUNNER_TEMP/rows.md" @@ -122,7 +121,7 @@ jobs: gh issue edit "$current" --body-file "$RUNNER_TEMP/body.md" echo "Updated #$current" else - url=$(gh issue create --title "$title" --assignee "$OWNER" --body-file "$RUNNER_TEMP/body.md") + url=$(gh issue create --title "$title" --body-file "$RUNNER_TEMP/body.md") current=${url##*/} echo "Opened $url" fi @@ -135,8 +134,8 @@ jobs: done < "$RUNNER_TEMP/mine.tsv" # A failed run of this workflow is emailed only to whoever started it (for the schedule, - # whoever last changed the cron line): one issue while it is open, assigned and - # @mentioning the owner, as toolchain-updates.yml does. A failure while it is open adds a + # whoever last changed the cron line): one issue while it is open, which the daily + # digest lists, as toolchain-updates.yml does. A failure while it is open adds a # comment to it. - name: Report this workflow's own failure if: failure() @@ -148,10 +147,10 @@ jobs: number=$(gh issue list --state open --limit 200 --json number,title \ --jq '[.[] | select(.title == env.TITLE)][0].number // empty') || number='' if [ -n "$number" ]; then - printf '@%s Failed again: %s\n' "$OWNER" "$RUN_URL" > "$RUNNER_TEMP/comment.md" + printf 'Failed again: %s\n' "$RUN_URL" > "$RUNNER_TEMP/comment.md" gh issue comment "$number" --body-file "$RUNNER_TEMP/comment.md" exit 0 fi - printf '@%s flaky-specs.yml failed: %s\n\nThe run log names the failed command. Close this issue once it is fixed; a later failure opens a new one.\n' \ - "$OWNER" "$RUN_URL" > "$RUNNER_TEMP/body.md" - gh issue create --title "$TITLE" --assignee "$OWNER" --body-file "$RUNNER_TEMP/body.md" + printf 'flaky-specs.yml failed: %s\n\nThe run log names the failed command. Close this issue once it is fixed; a later failure opens a new one.\n' \ + "$RUN_URL" > "$RUNNER_TEMP/body.md" + gh issue create --title "$TITLE" --body-file "$RUNNER_TEMP/body.md" diff --git a/.github/workflows/keepalive.yml b/.github/workflows/keepalive.yml index 61d30e145..c2399a37b 100644 --- a/.github/workflows/keepalive.yml +++ b/.github/workflows/keepalive.yml @@ -25,8 +25,8 @@ name: Keepalive # 60th day. If GitHub disables scheduled workflows anyway, the owner re-enables each # from its page in the Actions tab, the Enable workflow button. # -# A failed run opens one issue "Keepalive failed", assigned to and @mentioning the -# owner; while it is open, a later failure comments on it. Close the issue once this +# A failed run opens one issue "Keepalive failed", which the daily digest lists; +# while it is open, a later failure comments on it. Close the issue once this # workflow runs green again. on: @@ -58,7 +58,6 @@ jobs: env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} GH_REPO: ${{ github.repository }} - OWNER: ${{ github.repository_owner }} steps: # The schedule: triggers are read from the checkout, so the step needs the files. - name: Check out the workflows @@ -147,7 +146,7 @@ jobs: # A failed run of this workflow is emailed only to whoever started it (for the # schedule, whoever last changed the cron line): one issue while it is open, - # assigned and @mentioning the owner, as toolchain-updates.yml does. A failure while + # which the daily digest lists, as toolchain-updates.yml does. A failure while # it is open adds a comment to it. - name: Report this workflow's own failure if: failure() @@ -162,10 +161,10 @@ jobs: number=$(gh issue list --state open --limit 200 --json number,title \ --jq '[.[] | select(.title == env.TITLE)][0].number // empty') || number='' if [ -n "$number" ]; then - printf '@%s Failed again: %s\n' "$OWNER" "$RUN_URL" > "$RUNNER_TEMP/comment.md" + printf 'Failed again: %s\n' "$RUN_URL" > "$RUNNER_TEMP/comment.md" gh issue comment "$number" --body-file "$RUNNER_TEMP/comment.md" exit 0 fi - printf '@%s keepalive.yml failed: %s\n\nThe run log names the failed command. Close this issue once it is fixed; a later failure opens a new one.\n' \ - "$OWNER" "$RUN_URL" > "$RUNNER_TEMP/body.md" - gh issue create --title "$TITLE" --assignee "$OWNER" --body-file "$RUNNER_TEMP/body.md" + printf 'keepalive.yml failed: %s\n\nThe run log names the failed command. Close this issue once it is fixed; a later failure opens a new one.\n' \ + "$RUN_URL" > "$RUNNER_TEMP/body.md" + gh issue create --title "$TITLE" --body-file "$RUNNER_TEMP/body.md" diff --git a/.github/workflows/live-streams.yml b/.github/workflows/live-streams.yml index 3d8b25e5a..02a12bab1 100644 --- a/.github/workflows/live-streams.yml +++ b/.github/workflows/live-streams.yml @@ -11,8 +11,8 @@ name: Real streams # else. Its first runs found three bugs (#40, #41, a file played without a request). # # Not part of CI, on purpose: it depends on third-party servers, and an outage there must -# not hold back a release. A scheduled failure opens one issue, assigned to the owner with -# an @mention; the next green scheduled run closes it. It also runs on a pull request that +# not hold back a release. A scheduled failure opens one issue, which the daily digest +# lists; the next green scheduled run closes it. It also runs on a pull request that # changes this suite or its setup, and by hand; and on the player libraries' update pull # request (toolchain-updates.yml dispatches it there), which update-prs.yml calls ready only # once this suite passed on its commit. @@ -114,7 +114,6 @@ jobs: env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} GH_REPO: ${{ github.repository }} - OWNER: ${{ github.repository_owner }} BRANCH: ${{ github.head_ref || github.ref_name }} SHA: ${{ github.event.pull_request.head.sha || github.sha }} SELF_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} @@ -141,13 +140,13 @@ jobs: number=$(gh issue list --state open --limit 200 --json number,title \ --jq '[.[] | select(.title == env.TITLE)][0].number // empty') || number='' if [ -n "$number" ]; then - printf '@%s GitHub refused three times again, after the live suite on %s (%s): start update-prs.yml by hand with run_id %s and branch %s.\n' \ - "$OWNER" "$BRANCH" "$SELF_URL" "$run_id" "$BRANCH" > "$RUNNER_TEMP/comment.md" + printf 'GitHub refused three times again, after the live suite on %s (%s): start update-prs.yml by hand with run_id %s and branch %s.\n' \ + "$BRANCH" "$SELF_URL" "$run_id" "$BRANCH" > "$RUNNER_TEMP/comment.md" gh issue comment "$number" --body-file "$RUNNER_TEMP/comment.md" || true else - printf '@%s GitHub refused three times to start update-prs.yml after the live suite on %s (%s), so that pull request waits without a word. Start it from the Actions tab: Update PRs, Run workflow, run_id %s and branch %s. Close this issue once it has run.\n' \ - "$OWNER" "$BRANCH" "$SELF_URL" "$run_id" "$BRANCH" > "$RUNNER_TEMP/body.md" - gh issue create --title "$TITLE" --assignee "$OWNER" --body-file "$RUNNER_TEMP/body.md" || true + printf 'GitHub refused three times to start update-prs.yml after the live suite on %s (%s), so that pull request waits without a word. Start it from the Actions tab: Update PRs, Run workflow, run_id %s and branch %s. Close this issue once it has run.\n' \ + "$BRANCH" "$SELF_URL" "$run_id" "$BRANCH" > "$RUNNER_TEMP/body.md" + gh issue create --title "$TITLE" --body-file "$RUNNER_TEMP/body.md" || true fi exit 1 @@ -164,7 +163,6 @@ jobs: env: GH_TOKEN: ${{ github.token }} RESULT: ${{ needs.live.result }} - OWNER: ${{ github.repository_owner }} RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} run: | set -euo pipefail @@ -191,9 +189,9 @@ jobs: # The backticks are Markdown for the issue body, not command substitution. # shellcheck disable=SC2016 { - printf '@%s The weekly check against real streams and sites failed: %s\n\n' "$OWNER" "$RUN_URL" + printf 'The weekly check against real streams and sites failed: %s\n\n' "$RUN_URL" printf 'Failed (or timed out): %s. The run log names the case, and its logs artifact has the browser logs.\n\n' "${failed:-see the run}" printf 'A stream or site the check uses may just be down: run it again by hand (Actions > Real streams > Run workflow). If it fails again, a CDN or site changed how it serves video, or FastStream stopped coping with it - `pnpm run test:live` reproduces it locally.\n\n' printf 'This issue closes on its own once a scheduled run passes.\n' } > "$RUNNER_TEMP/body.md" - gh issue create --repo "$GITHUB_REPOSITORY" --title "$prefix ($(date -u +%Y-%m-%d))" --assignee "$OWNER" --body-file "$RUNNER_TEMP/body.md" + gh issue create --repo "$GITHUB_REPOSITORY" --title "$prefix ($(date -u +%Y-%m-%d))" --body-file "$RUNNER_TEMP/body.md" diff --git a/.github/workflows/mpv-updates.yml b/.github/workflows/mpv-updates.yml index 798d209ad..4b3c5f425 100644 --- a/.github/workflows/mpv-updates.yml +++ b/.github/workflows/mpv-updates.yml @@ -14,7 +14,7 @@ name: mpv updates # this workflow's token starts no workflow, a dispatch does. When it fails, its failed # jobs run once more, so one flaky test does not count. # - Passed: the commit CI passed goes to branch mpv-update, and ONE pull request from it -# offers the build, assigned to the owner. Nothing merges itself (the owner's choice, +# offers the build; the daily digest lists it. Nothing merges itself (the owner's choice, # 2026-10-02): he merges it when he likes. It keeps up: a newer build that passes moves # the same branch and retitles the same pull request, so a build a day is not an email # a day. Closing it skips that build; the next one that passes opens a new pull @@ -25,8 +25,8 @@ name: mpv updates # - Failed (an attempt still running after 65 minutes is cancelled, and counts as # failed): the branch stays, to look at and as the record that this build was tried: # it is not tried again. Delete the branch to try it again. The failure goes on ONE -# issue, "mpv update failed: <what>", assigned to the owner and @mentioning them, so -# GitHub emails it: opened, or retitled and commented while it is open. CI keeps the +# issue, "mpv update failed: <what>", which the daily digest lists: +# opened, or retitled and commented while it is open. CI keeps the # pinned build meanwhile. # # Every run also checks that the pinned release still exists upstream. Once it is gone, @@ -86,7 +86,6 @@ jobs: env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} GH_REPO: ${{ github.repository }} - OWNER: ${{ github.repository_owner }} EVENT: ${{ github.event_name }} UPSTREAM: shinchiro/mpv-winbuild-cmake PREFIX: 'mpv update failed: ' @@ -202,10 +201,10 @@ jobs: fi printf '\n<sub>Reported by %s</sub>\n<!-- mpv-updates: %s -->\n' "$RUN_URL" "$1" >> "$3" if [ -n "$issue" ]; then - gh issue edit "$issue" --title "$PREFIX$2" --add-assignee "$OWNER" + gh issue edit "$issue" --title "$PREFIX$2" gh issue comment "$issue" --body-file "$3" else - url=$(gh issue create --title "$PREFIX$2" --assignee "$OWNER" --body-file "$3") + url=$(gh issue create --title "$PREFIX$2" --body-file "$3") issue=${url##*/} fi } @@ -292,7 +291,7 @@ jobs: title="ci: mpv $latest (shinchiro)" # shellcheck disable=SC2016 # backticks are Markdown { - printf '@%s shinchiro'"'"'s mpv %s passed CI (%s), whose Windows job runs the MPV-mode e2e specs with it. **Ready for you to merge.**\n\n' "$OWNER" "$latest" "$run_url" + printf 'shinchiro'"'"'s mpv %s passed CI (%s), whose Windows job runs the MPV-mode e2e specs with it. **Ready for you to merge.**\n\n' "$latest" "$run_url" printf -- '- Release: https://github.com/%s/releases/tag/%s\n' "$UPSTREAM" "$latest" printf -- '- The change: `.github/mpv-build.json` pins %s instead of %s, and nothing else.\n' "$latest" "$pinned" printf -- '- Merging it releases nothing: only CI installs this mpv.\n\n' @@ -302,7 +301,7 @@ jobs: gh pr edit "$pr" --title "$title" --body-file "$RUNNER_TEMP/pr.md" > /dev/null echo "#$pr offers $latest now." else - url=$(gh pr create --base main --head "$PR_BRANCH" --title "$title" --body-file "$RUNNER_TEMP/pr.md" --assignee "$OWNER") + url=$(gh pr create --base main --head "$PR_BRANCH" --title "$title" --body-file "$RUNNER_TEMP/pr.md") pr=${url##*/} echo "Opened #$pr for $latest." fi @@ -322,11 +321,11 @@ jobs: # shellcheck disable=SC2016 # backticks are Markdown { if [ "$conclusion" = timed_out ]; then - printf '@%s CI with mpv %s from shinchiro was still running after 65 minutes, so it was cancelled.\n\n' "$OWNER" "$latest" + printf 'CI with mpv %s from shinchiro was still running after 65 minutes, so it was cancelled.\n\n' "$latest" elif [ "$rerun" = 1 ]; then - printf '@%s mpv %s from shinchiro failed CI. Its failed jobs were run once more, in case a test was flaky, and that ended in %s.\n\n' "$OWNER" "$latest" "$conclusion" + printf 'mpv %s from shinchiro failed CI. Its failed jobs were run once more, in case a test was flaky, and that ended in %s.\n\n' "$latest" "$conclusion" else - printf '@%s mpv %s from shinchiro failed CI (%s), and its failed jobs could not be run again.\n\n' "$OWNER" "$latest" "$conclusion" + printf 'mpv %s from shinchiro failed CI (%s), and its failed jobs could not be run again.\n\n' "$latest" "$conclusion" fi printf -- '- CI run: %s\n' "$run_url" printf -- '- Branch: `%s`, which is main with the pin moved and nothing else\n' "$branch" @@ -342,7 +341,7 @@ jobs: if [ "$gone" = 1 ]; then # shellcheck disable=SC2016 # backticks are Markdown { - printf '@%s The pinned mpv build, %s from shinchiro, is gone upstream: shinchiro keeps only about the 30 newest releases. CI downloads it from there, so every CI run now fails at "Install mpv and the native host", and nothing is released.\n\n' "$OWNER" "$pinned" + printf 'The pinned mpv build, %s from shinchiro, is gone upstream: shinchiro keeps only about the 30 newest releases. CI downloads it from there, so every CI run now fails at "Install mpv and the native host", and nothing is released.\n\n' "$pinned" if [ -n "$pr" ]; then printf 'Merge #%s, which offers a newer build that passed CI, to fix it.\n' "$pr" else @@ -353,8 +352,8 @@ jobs: fi # A failed run of this workflow is emailed only to whoever started it (for the schedule, - # whoever last changed the cron line): one issue while it is open, assigned and - # @mentioning the owner, as toolchain-updates.yml does. A failure while it is open adds a + # whoever last changed the cron line): one issue while it is open, which the daily + # digest lists, as toolchain-updates.yml does. A failure while it is open adds a # comment to it. # Not for a pull request's dry run: its failure shows on the pull request. - name: Report this workflow's own failure @@ -362,7 +361,6 @@ jobs: env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} GH_REPO: ${{ github.repository }} - OWNER: ${{ github.repository_owner }} RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} TITLE: 'mpv updates workflow failed' run: | @@ -371,10 +369,10 @@ jobs: number=$(gh issue list --state open --limit 200 --json number,title \ --jq '[.[] | select(.title == env.TITLE)][0].number // empty') || number='' if [ -n "$number" ]; then - printf '@%s Failed again: %s\n' "$OWNER" "$RUN_URL" > "$RUNNER_TEMP/comment.md" + printf 'Failed again: %s\n' "$RUN_URL" > "$RUNNER_TEMP/comment.md" gh issue comment "$number" --body-file "$RUNNER_TEMP/comment.md" exit 0 fi - printf '@%s mpv-updates.yml failed: %s\n\nThe run log names the failed command. Close this issue once it is fixed; a later failure opens a new one.\n' \ - "$OWNER" "$RUN_URL" > "$RUNNER_TEMP/body.md" - gh issue create --title "$TITLE" --assignee "$OWNER" --body-file "$RUNNER_TEMP/body.md" + printf 'mpv-updates.yml failed: %s\n\nThe run log names the failed command. Close this issue once it is fixed; a later failure opens a new one.\n' \ + "$RUN_URL" > "$RUNNER_TEMP/body.md" + gh issue create --title "$TITLE" --body-file "$RUNNER_TEMP/body.md" diff --git a/.github/workflows/outage-retry.yml b/.github/workflows/outage-retry.yml index 55ab931b4..3e4ca5a0d 100644 --- a/.github/workflows/outage-retry.yml +++ b/.github/workflows/outage-retry.yml @@ -48,7 +48,6 @@ jobs: env: GH_TOKEN: ${{ github.token }} GH_REPO: ${{ github.repository }} - OWNER: ${{ github.repository_owner }} SELF_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} BRANCH: ${{ inputs.branch }} SHA: ${{ inputs.sha }} @@ -100,6 +99,6 @@ jobs: if: failure() && steps.retry.outputs.valid == 'true' run: | set -euo pipefail - printf '@%s CI was to run again on this pull request after an outage, but could not be started (%s). Start it on this branch (Actions, CI, Run workflow); that run decides.\n' \ - "$OWNER" "$SELF_URL" > "$RUNNER_TEMP/body.md" + printf 'CI was to run again on this pull request after an outage, but could not be started (%s). Start it on this branch (Actions, CI, Run workflow); that run decides.\n' \ + "$SELF_URL" > "$RUNNER_TEMP/body.md" gh pr comment "$BRANCH" --body-file "$RUNNER_TEMP/body.md" diff --git a/.github/workflows/patched-libraries.yml b/.github/workflows/patched-libraries.yml index 08b3913e0..4aab32c73 100644 --- a/.github/workflows/patched-libraries.yml +++ b/.github/workflows/patched-libraries.yml @@ -15,7 +15,7 @@ name: Patched libraries # by hand. # - Too new for pnpm's minimumReleaseAge: nothing yet; the next daily run tries again. # -# Either is assigned to the repo owner and @mentions them, so GitHub emails it. A title +# The daily digest (daily-digest.yml) lists either, the owner's one email a day. A title # is never raised twice, as an issue or a pull request, open or closed: closing one is how # a version is skipped (a major rewrite not worth porting, say), and it stays closed. # Both are closed on their own once the patched version has caught up with them, however @@ -74,7 +74,6 @@ jobs: - name: Sync update pull requests and issues with the patched versions env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - OWNER: ${{ github.repository_owner }} PREFIX: 'Patched library update: ' EVENT: ${{ github.event_name }} RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} @@ -195,7 +194,7 @@ jobs: # The backticks are Markdown for the body, not command substitution. # shellcheck disable=SC2016 { - printf '@%s %s %s is out: %s\n\n' "$OWNER" "$name" "$latest" "$url" + printf '%s %s is out: %s\n\n' "$name" "$latest" "$url" printf 'tools/recut-patch.mjs moved the patch from %s onto %s and its checks passed - each patched file parses, and no name is undefined or unused that neither the new release nor the old patch has. Its report:\n\n```\n' "$current" "$latest" cat "$RUNNER_TEMP/report.txt" printf '```\n\nThat is not the proof: CI is, and this workflow has started it on this branch - the full `pnpm run verify` suite, playback included - with "Review dependency changes" for what the lockfile adds. Green: merge, and it releases. Red, or to look closer: `docs/updating-patched-libraries.md`. Closing this pull request skips %s %s for good.\n' "$name" "$latest" @@ -204,7 +203,7 @@ jobs: fi printf '\n<sub>Opened by %s</sub>\n' "$RUN_URL" } > "$RUNNER_TEMP/body.md" - gh pr create --base main --head "$branch" --title "$title" --assignee "$OWNER" \ + gh pr create --base main --head "$branch" --title "$title" \ --body-file "$RUNNER_TEMP/body.md" # The pull request's own runs wait for an approval (this token opened it). gh workflow run ci.yml --ref "$branch" @@ -212,7 +211,7 @@ jobs: else # shellcheck disable=SC2016 { - printf '@%s %s %s is out: %s\n\n' "$OWNER" "$name" "$latest" "$url" + printf '%s %s is out: %s\n\n' "$name" "$latest" "$url" printf 'The patch in this repository is cut against %s %s, and tools/recut-patch.mjs could not move it onto %s on its own:\n\n```\n' "$name" "$current" "$latest" cat "$RUNNER_TEMP/report.txt" printf '```\n\nTo take it: `node tools/recut-patch.mjs %s %s`, resolve what it lists, then `--resume --apply` and `pnpm run verify` (`docs/updating-patched-libraries.md`). This issue closes on its own once the patch is cut against %s or newer; closing it by hand skips %s for good.\n' "$name" "$latest" "$latest" "$latest" @@ -221,7 +220,7 @@ jobs: fi printf '\n<sub>Opened by %s</sub>\n' "$RUN_URL" } > "$RUNNER_TEMP/body.md" - gh issue create --title "$title" --assignee "$OWNER" --body-file "$RUNNER_TEMP/body.md" + gh issue create --title "$title" --body-file "$RUNNER_TEMP/body.md" fi git checkout -q -f "$start" git clean -q -fd @@ -229,15 +228,14 @@ jobs: supersede # A failed run of this workflow is emailed only to whoever started it (for the schedule, - # whoever last changed the cron line): one issue while it is open, assigned and - # @mentioning the owner, as toolchain-updates.yml does. A failure while it is open adds a + # whoever last changed the cron line): one issue while it is open, which the daily + # digest lists, as toolchain-updates.yml does. A failure while it is open adds a # comment to it. - name: Report this workflow's own failure if: failure() env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} GH_REPO: ${{ github.repository }} - OWNER: ${{ github.repository_owner }} RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} TITLE: 'Patched libraries workflow failed' run: | @@ -246,10 +244,10 @@ jobs: number=$(gh issue list --state open --limit 200 --json number,title \ --jq '[.[] | select(.title == env.TITLE)][0].number // empty') || number='' if [ -n "$number" ]; then - printf '@%s Failed again: %s\n' "$OWNER" "$RUN_URL" > "$RUNNER_TEMP/comment.md" + printf 'Failed again: %s\n' "$RUN_URL" > "$RUNNER_TEMP/comment.md" gh issue comment "$number" --body-file "$RUNNER_TEMP/comment.md" exit 0 fi - printf '@%s patched-libraries.yml failed: %s\n\nThe run log names the failed command. Close this issue once it is fixed; a later failure opens a new one.\n' \ - "$OWNER" "$RUN_URL" > "$RUNNER_TEMP/body.md" - gh issue create --title "$TITLE" --assignee "$OWNER" --body-file "$RUNNER_TEMP/body.md" + printf 'patched-libraries.yml failed: %s\n\nThe run log names the failed command. Close this issue once it is fixed; a later failure opens a new one.\n' \ + "$RUN_URL" > "$RUNNER_TEMP/body.md" + gh issue create --title "$TITLE" --body-file "$RUNNER_TEMP/body.md" diff --git a/.github/workflows/runner-images.yml b/.github/workflows/runner-images.yml index 2d90aa818..d58eb2859 100644 --- a/.github/workflows/runner-images.yml +++ b/.github/workflows/runner-images.yml @@ -15,7 +15,7 @@ name: Runner images # Each is tested once: a green run is recorded as an Actions cache entry # (runner-image-next-... / runner-image-latest-...) and later runs find it and stop. A # failing image is retried every day until it passes. A failure opens one issue per -# image, assigned to the owner with an @mention; the first green run on that image +# image, which the daily digest lists; the first green run on that image # closes it. Run by hand to test both again regardless. on: @@ -220,7 +220,6 @@ jobs: NEXT: ${{ needs.next.result }} LATEST_IMAGES: ${{ needs.detect.outputs.latest-images }} NEXT_IMAGES: ${{ needs.detect.outputs.next-images }} - OWNER: ${{ github.repository_owner }} RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} run: | set -euo pipefail @@ -249,19 +248,19 @@ jobs: return fi { - printf '@%s CI fails on **%s**, %s: %s\n\n' "$OWNER" "$images" "$role" "$RUN_URL" + printf 'CI fails on **%s**, %s: %s\n\n' "$images" "$role" "$RUN_URL" printf 'The same workflow passes on the previous image, so something on the new one differs: a tool or package removed or updated, a path moved, a smaller disk or /tmp. The run log names the failing step; the image'"'"'s release notes at https://github.com/actions/runner-images/releases list what changed, and its announcements are the issues there.\n\n' printf 'This issue closes on its own once a run on %s passes (the check retries daily).\n' "$images" } > "$RUNNER_TEMP/body.md" - gh issue create --repo "$GITHUB_REPOSITORY" --title "$title" --assignee "$OWNER" --body-file "$RUNNER_TEMP/body.md" + gh issue create --repo "$GITHUB_REPOSITORY" --title "$title" --body-file "$RUNNER_TEMP/body.md" } if [ "$DETECT" != success ]; then title='Runner images check cannot read the image list' if [ -z "$(open_issue "$title")" ]; then - printf '@%s The daily runner images check could not work out which images -latest gives, so it is not watching for a switch: %s\n\nThe runner-images README table it reads has probably changed format; the detect step in .github/workflows/runner-images.yml needs adjusting. This issue does not close on its own.\n' \ - "$OWNER" "$RUN_URL" > "$RUNNER_TEMP/body.md" - gh issue create --repo "$GITHUB_REPOSITORY" --title "$title" --assignee "$OWNER" --body-file "$RUNNER_TEMP/body.md" + printf 'The daily runner images check could not work out which images -latest gives, so it is not watching for a switch: %s\n\nThe runner-images README table it reads has probably changed format; the detect step in .github/workflows/runner-images.yml needs adjusting. This issue does not close on its own.\n' \ + "$RUN_URL" > "$RUNNER_TEMP/body.md" + gh issue create --repo "$GITHUB_REPOSITORY" --title "$title" --body-file "$RUNNER_TEMP/body.md" fi exit 0 fi diff --git a/.github/workflows/security-alerts.yml b/.github/workflows/security-alerts.yml index b37d7fca5..3ff06d434 100644 --- a/.github/workflows/security-alerts.yml +++ b/.github/workflows/security-alerts.yml @@ -7,7 +7,7 @@ name: Security alerts # as the only notice. So once a day this reads the repository's open Dependabot alerts. A # package with an alert over 6 hours old (Dependabot's pull request comes within minutes) # and no open Dependabot pull request naming it gets one issue, "Security alert: <package>", -# assigned to the owner and @mentioning them, listing its alerts and how to fix them by +# which the daily digest lists, listing its alerts and how to fix them by # hand. A later alert for that package is added to the open issue with a comment. # # The issue closes itself once none of the package's alerts is open: fixed on main (GitHub @@ -77,7 +77,6 @@ jobs: env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} GH_REPO: ${{ github.repository }} - OWNER: ${{ github.repository_owner }} EVENT: ${{ github.event_name }} PREFIX: 'Security alert: ' BRANCH: security/overrides @@ -308,7 +307,7 @@ jobs: { # shellcheck disable=SC2016 # backticks are Markdown - printf '@%s Dependabot has open security alerts for `%s` and no pull request that fixes them:\n\n' "$OWNER" "$package" + printf 'Dependabot has open security alerts for `%s` and no pull request that fixes them:\n\n' "$package" printf '| Alert | Severity | In | Vulnerable | Fixed in | Advisory |\n| --- | --- | --- | --- | --- | --- |\n' cat "$T/rows.md" cat <<EOF @@ -331,10 +330,10 @@ jobs: gh issue edit "$open" --body-file "$T/body.md" links=$(for n in $new; do printf '[alert %s](%s/%s) ' "$n" "$ALERTS" "$n"; done) if [ "$(wc -w <<< "$new")" -eq 1 ]; then what='Another security alert'; else what='More security alerts'; fi - gh issue comment "$open" --body "@$OWNER $what for \`$package\`, with no pull request that fixes it: ${links% }. The list above now includes it." + gh issue comment "$open" --body "$what for \`$package\`, with no pull request that fixes it: ${links% }. The list above now includes it." echo "Added alert(s) $new to #$open ($package)" else - url=$(gh issue create --title "$PREFIX$package" --assignee "$OWNER" --body-file "$T/body.md") + url=$(gh issue create --title "$PREFIX$package" --body-file "$T/body.md") echo "Opened $url ($package: alerts $list)" fi done < "$T/due.tsv" @@ -352,8 +351,8 @@ jobs: retention-days: 1 # A failed run of this workflow is emailed only to whoever started it (for the schedule, - # whoever last changed the cron line): one issue while it is open, assigned and - # @mentioning the owner, as toolchain-updates.yml does. A failure while it is open adds a + # whoever last changed the cron line): one issue while it is open, which the daily + # digest lists, as toolchain-updates.yml does. A failure while it is open adds a # comment to it. - name: Report this workflow's own failure if: failure() @@ -368,13 +367,13 @@ jobs: number=$(gh issue list --state open --limit 200 --json number,title \ --jq '[.[] | select(.title == env.TITLE)][0].number // empty') || number='' if [ -n "$number" ]; then - printf '@%s Failed again: %s\n' "$OWNER" "$RUN_URL" > "$RUNNER_TEMP/comment.md" + printf 'Failed again: %s\n' "$RUN_URL" > "$RUNNER_TEMP/comment.md" gh issue comment "$number" --body-file "$RUNNER_TEMP/comment.md" exit 0 fi - printf '@%s security-alerts.yml failed: %s\n\nThe run log names the failed command. Until it is fixed, a Dependabot alert with no pull request raises no issue. Close this issue once it is fixed; a later failure opens a new one.\n' \ - "$OWNER" "$RUN_URL" > "$RUNNER_TEMP/body.md" - gh issue create --title "$TITLE" --assignee "$OWNER" --body-file "$RUNNER_TEMP/body.md" + printf 'security-alerts.yml failed: %s\n\nThe run log names the failed command. Until it is fixed, a Dependabot alert with no pull request raises no issue. Close this issue once it is fixed; a later failure opens a new one.\n' \ + "$RUN_URL" > "$RUNNER_TEMP/body.md" + gh issue create --title "$TITLE" --body-file "$RUNNER_TEMP/body.md" # The plan's floors are tried here: pnpm resolves the lockfile with the overrides applied, # and the module verifies the result against the plan. The job deliberately has no token @@ -507,7 +506,6 @@ jobs: env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} GH_REPO: ${{ github.repository }} - OWNER: ${{ github.repository_owner }} PREFIX: 'Security alert: ' BRANCH: security/overrides ALERTS_URL: https://github.com/${{ github.repository }}/security/dependabot @@ -629,7 +627,7 @@ jobs: marker=$(jq -r '.handled | map(tostring) | join(" ")' result/routing.json) { # shellcheck disable=SC2016 # backticks are Markdown - printf '@%s These alerts can be fixed without touching any package.json: a floor for each package in the `overrides` of `pnpm-workspace.yaml` makes pnpm take the newest release that floor allows, in place of the ranges its dependents ask for. Only the packages below move.\n\n' "$OWNER" + printf 'These alerts can be fixed without touching any package.json: a floor for each package in the `overrides` of `pnpm-workspace.yaml` makes pnpm take the newest release that floor allows, in place of the ranges its dependents ask for. Only the packages below move.\n\n' printf '| Alert | Severity | In | Vulnerable | Fixed in | Advisory |\n| --- | --- | --- | --- | --- | --- |\n' cat "$T/pr-rows.md" printf '\n| Package | Selector | Change | From | Floor |\n| --- | --- | --- | --- | --- |\n' @@ -705,7 +703,7 @@ jobs: result/plan.json > "$T/rows.md" { # shellcheck disable=SC2016 # backticks are Markdown - printf '@%s A floor was planned for `%s`, but the package manager would not install it, so these alerts come back to you:\n\n' "$OWNER" "$package" + printf 'A floor was planned for `%s`, but the package manager would not install it, so these alerts come back to you:\n\n' "$package" printf '| Alert | Severity | In | Vulnerable | Fixed in | Advisory |\n| --- | --- | --- | --- | --- | --- |\n' cat "$T/rows.md" printf '\nWhy: %s\n\n' "$reason" @@ -734,10 +732,10 @@ jobs: fi gh issue edit "$number" --body-file "$T/issue-body.md" > /dev/null links=$(for n in $list; do printf '[alert %s](%s/%s) ' "$n" "$ALERTS_URL" "$n"; done) - gh issue comment "$number" --body "@$OWNER ${links% }could not be installed automatically: $reason. The list above now includes them." + gh issue comment "$number" --body "${links% }could not be installed automatically: $reason. The list above now includes them." echo "Demoted alerts $list onto #$number ($package)" else - url=$(gh issue create --title "$PREFIX$package" --assignee "$OWNER" --body-file "$T/body.md") + url=$(gh issue create --title "$PREFIX$package" --body-file "$T/body.md") echo "Demoted alerts $list to $url ($package)" fi done < "$T/demote.tsv" @@ -817,7 +815,6 @@ jobs: env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} GH_REPO: ${{ github.repository }} - OWNER: ${{ github.repository_owner }} steps: - name: Report the route's own failure env: @@ -833,15 +830,15 @@ jobs: number=$(gh issue list --state open --limit 200 --json number,title \ --jq '[.[] | select(.title == env.TITLE)][0].number // empty') || number='' if [ -n "$number" ]; then - printf '@%s Failed again: %s\n' "$OWNER" "$RUN_URL" > "$RUNNER_TEMP/comment.md" + printf 'Failed again: %s\n' "$RUN_URL" > "$RUNNER_TEMP/comment.md" gh issue comment "$number" --body-file "$RUNNER_TEMP/comment.md" exit 0 fi { - printf '@%s The override route failed (job(s):%s): %s\n\n' "$OWNER" "$failed" "$RUN_URL" + printf 'The override route failed (job(s):%s): %s\n\n' "$failed" "$RUN_URL" printf '%s\n' "The run log names the failed command. This failure takes no action on any alert by design: the alerts the failed run's plan had claimed are recorded nowhere, so the next run offers them again from scratch - a transient registry failure must not demote an alert for good. Until it is fixed, no override pull request can move, and the alerts it would have taken raise nothing that day. Close this issue once it is fixed; a later failure opens a new one." } > "$RUNNER_TEMP/body.md" - gh issue create --title "$TITLE" --assignee "$OWNER" --body-file "$RUNNER_TEMP/body.md" + gh issue create --title "$TITLE" --body-file "$RUNNER_TEMP/body.md" # The update pull requests, once a day. Dependabot fails quietly in another way too: once an # entry of .github/dependabot.yml has its open-pull-requests-limit of pull requests open, it @@ -859,7 +856,6 @@ jobs: env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} GH_REPO: ${{ github.repository }} - OWNER: ${{ github.repository_owner }} steps: - name: Close the migration issues of closed pull requests run: | @@ -981,7 +977,7 @@ jobs: fi # shellcheck disable=SC2016 # backticks are Markdown { - printf '@%s Update pull requests are piling up, and Dependabot tells no one.\n\n' "$OWNER" + printf 'Update pull requests are piling up, and Dependabot tells no one.\n\n' if [ -s "$T/full.md" ]; then printf '**Near the limit.** Once an entry of `.github/dependabot.yml` has its `open-pull-requests-limit` of pull requests open, Dependabot opens no more for it, not even for a new release, until some are merged or closed. This warns at the limit minus 2 (at least 2).\n\n' printf '| Ecosystem | Directories | Open | Limit | Pull requests |\n| --- | --- | --- | --- | --- |\n' @@ -1001,7 +997,7 @@ jobs: gh issue edit "$open" --body-file "$T/body.md" > /dev/null echo "Updated #$open." else - gh issue create --title "$TITLE" --assignee "$OWNER" --body-file "$T/body.md" + gh issue create --title "$TITLE" --body-file "$T/body.md" fi # As the check's report above, for this job: one issue while it is open, a comment on it @@ -1016,10 +1012,10 @@ jobs: number=$(gh issue list --state open --limit 200 --json number,title \ --jq '[.[] | select(.title == env.TITLE)][0].number // empty') || number='' if [ -n "$number" ]; then - printf '@%s Failed again: %s\n' "$OWNER" "$RUN_URL" > "$RUNNER_TEMP/comment.md" + printf 'Failed again: %s\n' "$RUN_URL" > "$RUNNER_TEMP/comment.md" gh issue comment "$number" --body-file "$RUNNER_TEMP/comment.md" exit 0 fi - printf '@%s The daily watch of the update pull requests (security-alerts.yml, job "Watch the update pull requests") failed: %s\n\nThe run log names the failed command. Until it is fixed, migration issues of closed pull requests stay open, pull requests held for new releases are not decided again, and piling-up pull requests raise no issue. Close this issue once it is fixed; a later failure opens a new one.\n' \ - "$OWNER" "$RUN_URL" > "$RUNNER_TEMP/body.md" - gh issue create --title "$TITLE" --assignee "$OWNER" --body-file "$RUNNER_TEMP/body.md" + printf 'The daily watch of the update pull requests (security-alerts.yml, job "Watch the update pull requests") failed: %s\n\nThe run log names the failed command. Until it is fixed, migration issues of closed pull requests stay open, pull requests held for new releases are not decided again, and piling-up pull requests raise no issue. Close this issue once it is fixed; a later failure opens a new one.\n' \ + "$RUN_URL" > "$RUNNER_TEMP/body.md" + gh issue create --title "$TITLE" --body-file "$RUNNER_TEMP/body.md" diff --git a/.github/workflows/toolchain-updates.yml b/.github/workflows/toolchain-updates.yml index aff13710b..3833993f3 100644 --- a/.github/workflows/toolchain-updates.yml +++ b/.github/workflows/toolchain-updates.yml @@ -80,7 +80,6 @@ jobs: timeout-minutes: 13 env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - OWNER: ${{ github.repository_owner }} EVENT: ${{ github.event_name }} PREFIX: 'Toolchain update: ' RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} @@ -262,7 +261,7 @@ jobs: printf '\nRanges that do not hold on `main` already (nothing here can move them):\n\n' jq -r '.unresolved[] | "- \(.name) \(.version) asks for \(.needs) `\(.range)`"' "$players" fi - printf '\nCI and the live suite (`live-streams.yml`: every library plays its page on its own, then FastStream takes it over, on Linux and on Windows) have been started on it. `update-prs.yml` tells @%s once both have run whether it is ready to merge, and the merge is theirs.\n\n' "$OWNER" + printf '\nCI and the live suite (`live-streams.yml`: every library plays its page on its own, then FastStream takes it over, on Linux and on Windows) have been started on it. `update-prs.yml` says here once both have run whether it is ready to merge (and the daily digest lists it), and the merge is the owner'"'"'s.\n\n' printf 'This pull request is rebuilt in place when the set changes. Closing it skips this set; it comes again with the next one.\n' printf '\nNothing here is part of the extension: its build should come out unchanged, and a merge then releases nothing (auto-release.yml compares the builds).\n' printf '\n<sub>Opened by %s</sub>\n<!-- player-libraries: %s -->\n' "$RUN_URL" "$state" @@ -366,9 +365,9 @@ jobs: printf '%s %s -> %s (%s).\n\n' "$name" "$was" "$latest" "$(jq -r .note <<< "$update")" printf 'This pull request changes only %s, and CI has been started on it: the whole of `pnpm run verify`, on Linux and on Windows.\n\n' "$what" if [ "$(jq -r .routine <<< "$update")" = true ]; then - printf 'Routine: `update-prs.yml` tells @%s once CI has run whether it is ready to merge, and the merge is theirs.\n' "$OWNER" + printf 'Routine: `update-prs.yml` says here once CI has run whether it is ready to merge (and the daily digest lists it), and the merge is the owner'"'"'s.\n' else - printf 'A decision: `update-prs.yml` tells @%s when CI is green or red, and the merge is theirs. Closing this pull request skips %s %s for good.\n' "$OWNER" "$name" "$latest" + printf 'A decision: `update-prs.yml` says here when CI is green or red (and the daily digest lists it), and the merge is the owner'"'"'s. Closing this pull request skips %s %s for good.\n' "$name" "$latest" fi if [ "$name" = 'uBlock Origin' ]; then printf '\nNothing in CI downloads the add-on: this run downloaded %s and checked it against the SHA-256 AMO gives for it, the check `pnpm run profile:setup` makes too.\n' "$(jq -r .file.url <<< "$update")" @@ -485,14 +484,13 @@ jobs: [ "$failed" = 0 ] || exit 1 # A failed run of this workflow is emailed only to whoever started it (for the schedule, - # whoever last changed the cron line): one issue while it is open, assigned and - # @mentioning the owner, as update-prs.yml does. A failure while it is open adds a + # whoever last changed the cron line): one issue while it is open, which the daily + # digest lists, as update-prs.yml does. A failure while it is open adds a # comment to it. - name: Report this workflow's own failure if: failure() env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - OWNER: ${{ github.repository_owner }} RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} TITLE: 'Toolchain updates workflow failed' run: | @@ -503,10 +501,10 @@ jobs: number=$(gh issue list --state open --limit 200 --json number,title \ --jq '[.[] | select(.title == env.TITLE)][0].number // empty') || number='' if [ -n "$number" ]; then - printf '@%s Failed again: %s\n' "$OWNER" "$RUN_URL" > "$RUNNER_TEMP/comment.md" + printf 'Failed again: %s\n' "$RUN_URL" > "$RUNNER_TEMP/comment.md" gh issue comment "$number" --body-file "$RUNNER_TEMP/comment.md" exit 0 fi - printf '@%s toolchain-updates.yml failed: %s\n\nThe run log names the failed command; a pull request it was raising or rebuilding may be half done (the next run finishes it). Close this issue once it is fixed; a later failure opens a new one.\n' \ - "$OWNER" "$RUN_URL" > "$RUNNER_TEMP/body.md" - gh issue create --title "$TITLE" --assignee "$OWNER" --body-file "$RUNNER_TEMP/body.md" + printf 'toolchain-updates.yml failed: %s\n\nThe run log names the failed command; a pull request it was raising or rebuilding may be half done (the next run finishes it). Close this issue once it is fixed; a later failure opens a new one.\n' \ + "$RUN_URL" > "$RUNNER_TEMP/body.md" + gh issue create --title "$TITLE" --body-file "$RUNNER_TEMP/body.md" diff --git a/.github/workflows/update-prs.yml b/.github/workflows/update-prs.yml index 913d187d1..b6296ad30 100644 --- a/.github/workflows/update-prs.yml +++ b/.github/workflows/update-prs.yml @@ -15,7 +15,7 @@ name: Update PRs # # CI failed CI runs once more on the same commit, in case a test was flaky, and that # run decides. Still red: the pull request stays open, is labelled -# ci-failed and assigned to the owner, and a comment @mentions them with the +# ci-failed, and a comment (which the daily digest sums up) gives the # failed jobs and steps, what each step checks, the last lines of each # failed log, and how main's own CI stands (red there too points away from # the update). Nothing is guessed about the cause beyond that. Red twice, the @@ -25,12 +25,11 @@ name: Update PRs # on the pull request (here; the hold label set here comes off with it), or # once it is merged or closed (security-alerts.yml's daily watch). # A run that failed only because GitHub or a download host did not answer -# (tools/ci-outage.sh) is an outage, not the update: a comment without an -# @mention says so, and outage-retry.yml runs CI again on the commit three hours -# later, up to three times; then the owner is @mentioned. No label, no hold, no +# (tools/ci-outage.sh) is an outage, not the update: a comment says so, and outage-retry.yml runs CI again on the commit three hours +# later, up to three times; then a comment says to start it by hand. No label, no hold, no # migration issue, and an outage run is no first try. A failed job without a log # (a lost runner) opens no migration issue either. -# CI passed a comment @mentions the owner and assigns them. "Ready to merge" when +# CI passed a comment, which the daily digest sums up. "Ready to merge" when # every one of these holds: opened by that bot and only its commits on it; it # changes only what that kind of update changes (package.json and # pnpm-lock.yaml; fsaunpack's two files; .nvmrc; workflow files; the two @@ -100,7 +99,7 @@ jobs: decide: permissions: contents: write # bring a branch up to date - pull-requests: write # comment, assign, label + pull-requests: write # comment, label issues: write # the ci-failed label, the migration issues, and the issue when this workflow fails actions: write # start CI once more, or on an updated branch checks: read # the dependency review's check runs @@ -381,9 +380,9 @@ jobs: fi { if [ "$outages" -le 3 ]; then - printf '@%s CI could not do its checks on this commit: GitHub or a download host did not answer (an outage, not this update), and outage-retry.yml could not be started to run it again later. ' "$OWNER" + printf 'CI could not do its checks on this commit: GitHub or a download host did not answer (an outage, not this update), and outage-retry.yml could not be started to run it again later. ' else - printf '@%s CI failed on this commit %s times, each time because GitHub or a download host did not answer (an outage, not this update). ' "$OWNER" "$outages" + printf 'CI failed on this commit %s times, each time because GitHub or a download host did not answer (an outage, not this update). ' "$outages" fi printf 'Nothing here needs changes for it: once GitHub answers again (https://www.githubstatus.com), start CI on this branch (Actions, CI, Run workflow); that run decides.\n\n' cat "$RUNNER_TEMP/diagnosis.md" @@ -470,8 +469,8 @@ jobs: gh pr edit "$number" --add-label hold > /dev/null fi { - printf '@%s CI failed twice on #%s (%s): the new version most likely needs changes to the code here, which no rerun or rebase brings. #%s is labelled `hold` meanwhile.\n\n' \ - "$OWNER" "$number" "$(jq -r .title <<< "$pr")" "$number" + printf 'CI failed twice on #%s (%s): the new version most likely needs changes to the code here, which no rerun or rebase brings. #%s is labelled `hold` meanwhile.\n\n' \ + "$number" "$(jq -r .title <<< "$pr")" "$number" printf '### What failed\n\n' cat "$RUNNER_TEMP/diagnosis.md" printf '\n### What to do\n\n' @@ -491,7 +490,7 @@ jobs: gh issue edit "$found" --title "Migration: $what" --body-file "$RUNNER_TEMP/migration.md" > /dev/null migration=$found else - migration=$(gh issue create --title "Migration: $what" --assignee "$OWNER" --body-file "$RUNNER_TEMP/migration.md") + migration=$(gh issue create --title "Migration: $what" --body-file "$RUNNER_TEMP/migration.md") migration=${migration##*/} fi } @@ -503,9 +502,9 @@ jobs: # shellcheck disable=SC2016 { if [ "$again" = 1 ]; then - printf '@%s CI fails on this pull request. It failed twice on this commit, so a flaky test is an unlikely cause.\n\n' "$OWNER" + printf 'CI fails on this pull request. It failed twice on this commit, so a flaky test is an unlikely cause.\n\n' else - printf '@%s CI fails on this pull request (%s).\n\n' "$OWNER" "$CONCLUSION" + printf 'CI fails on this pull request (%s).\n\n' "$CONCLUSION" fi cat "$RUNNER_TEMP/diagnosis.md" if [ -n "$migration" ]; then @@ -517,7 +516,7 @@ jobs: hint } > "$RUNNER_TEMP/body.md" gh label create "$LABEL" --color B60205 --description 'CI fails on this update; see the comment' --force > /dev/null - gh pr edit "$number" --add-assignee "$OWNER" --add-label "$LABEL" > /dev/null + gh pr edit "$number" --add-label "$LABEL" > /dev/null say "$key" "$RUNNER_TEMP/body.md" exit 0 fi @@ -891,24 +890,23 @@ jobs: # shellcheck disable=SC2016 { if [ "$key" = ready ]; then - printf '@%s CI passes on this pull request (%s), and every check holds: **ready for you to merge**.\n\n' "$OWNER" "$RUN_URL" + printf 'CI passes on this pull request (%s), and every check holds: **ready for you to merge**.\n\n' "$RUN_URL" if [ "$ships" = 1 ]; then printf 'It changes what the extension ships: once you merge it, CI runs on main, and a green run there releases it to Firefox.\n' else printf 'Its build of the extension is file for file the one in %s, the version number aside: merging it releases nothing.\n' "${tag:-the latest release}" fi else - printf '@%s CI passes on this pull request (%s). Look at this before merging:\n\n' "$OWNER" "$RUN_URL" + printf 'CI passes on this pull request (%s). Look at this before merging:\n\n' "$RUN_URL" printf -- '- %s\n' "${reasons[@]}" fi printf '\n' hint } > "$RUNNER_TEMP/body.md" - gh pr edit "$number" --add-assignee "$OWNER" > /dev/null say "$key" "$RUNNER_TEMP/body.md" # Bot-started runs are emailed to no one, so a failure of this workflow itself would go - # unseen: one issue while it is open, assigned and @mentioning the owner. A failure + # unseen: one issue while it is open, which the daily digest lists. A failure # while it is open adds a comment to it. - name: Report this workflow's own failure if: failure() @@ -922,10 +920,10 @@ jobs: number=$(gh issue list --state open --limit 200 --json number,title \ --jq '[.[] | select(.title == env.TITLE)][0].number // empty') || number='' if [ -n "$number" ]; then - printf '@%s Failed again: %s\n' "$OWNER" "$SELF_URL" > "$RUNNER_TEMP/comment.md" + printf 'Failed again: %s\n' "$SELF_URL" > "$RUNNER_TEMP/comment.md" gh issue comment "$number" --body-file "$RUNNER_TEMP/comment.md" exit 0 fi - printf '@%s update-prs.yml failed while deciding on an update pull request: %s\n\nThat pull request is not reported on until this is fixed. The run log names the failed command. Close this issue once it is fixed; a later failure opens a new one.\n' \ - "$OWNER" "$SELF_URL" > "$RUNNER_TEMP/body.md" - gh issue create --title "$TITLE" --assignee "$OWNER" --body-file "$RUNNER_TEMP/body.md" + printf 'update-prs.yml failed while deciding on an update pull request: %s\n\nThat pull request is not reported on until this is fixed. The run log names the failed command. Close this issue once it is fixed; a later failure opens a new one.\n' \ + "$SELF_URL" > "$RUNNER_TEMP/body.md" + gh issue create --title "$TITLE" --body-file "$RUNNER_TEMP/body.md" diff --git a/.github/workflows/upstream-watch.yml b/.github/workflows/upstream-watch.yml index d4f99801a..14de66361 100644 --- a/.github/workflows/upstream-watch.yml +++ b/.github/workflows/upstream-watch.yml @@ -8,9 +8,8 @@ name: Upstream watch # what was taken.) # # Once a day this lists upstream's commits that were not reviewed yet in one issue, -# "Upstream: N commits to review", assigned to the owner and @mentioning them (an assigned -# issue is what GitHub emails), updated in place when upstream moves again, with a comment -# that mentions the owner. Each commit names the files it changes and marks the ones this +# "Upstream: N commits to review", which the daily digest (daily-digest.yml) lists, +# updated in place when upstream moves again, with a comment saying how many more. Each commit names the files it changes and marks the ones this # fork still has, and "fix?" when its message speaks of a fix, a bug, a crash, a leak or # security; a commit that touches only files this fork does not have (Chrome-only code, # removed features) is listed apart. @@ -68,7 +67,6 @@ jobs: - name: Report the upstream commits to review env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - OWNER: ${{ github.repository_owner }} PREFIX: 'Upstream: ' # At most this many commits are listed; the rest are a link to compare. MAX_COMMITS: '60' @@ -176,8 +174,8 @@ jobs: COMPARE="https://github.com/$UPSTREAM_REPO/compare/$BASE...$UP" { - printf '@%s upstream ([%s](https://github.com/%s)) has %s commit(s) this fork has not reviewed yet ([all of them](%s)).\n\n' \ - "$OWNER" "$UPSTREAM_REPO" "$UPSTREAM_REPO" "$N" "$COMPARE" + printf 'upstream ([%s](https://github.com/%s)) has %s commit(s) this fork has not reviewed yet ([all of them](%s)).\n\n' \ + "$UPSTREAM_REPO" "$UPSTREAM_REPO" "$N" "$COMPARE" cat <<'EOF' This fork no longer merges upstream (the owner's decision of 2026-10-09: Firefox only, maintained on its own). Read these for fixes this fork's code may need too, and port what helps by hand. **Close this issue once they are reviewed:** the next run lists only the commits that came after. @@ -198,17 +196,17 @@ jobs: TITLE="${PREFIX}${N} commit(s) to review" if [ -n "$OPEN" ]; then - gh issue edit "$OPEN" --title "$TITLE" --body-file "$T/body.md" --add-assignee "$OWNER" - # Editing an issue notifies no one; a comment that mentions the owner does. + gh issue edit "$OPEN" --title "$TITLE" --body-file "$T/body.md" + # Editing an issue shows nowhere; a comment says it moved (the daily digest lists it). if [ -n "$OPEN_HEAD" ] && git cat-file -e "$OPEN_HEAD^{commit}" 2> /dev/null && git merge-base --is-ancestor "$OPEN_HEAD" "$UP"; then MORE=$(git rev-list --no-merges --count "$OPEN_HEAD..$UP") else MORE=$N fi - gh issue comment "$OPEN" --body "@${OWNER} upstream moved: ${MORE} more commit(s), ${N} to review now." + gh issue comment "$OPEN" --body "Upstream moved: ${MORE} more commit(s), ${N} to review now." echo "Updated #$OPEN: $N commit(s) to review." >> "$GITHUB_STEP_SUMMARY" else - gh issue create --title "$TITLE" --body-file "$T/body.md" --assignee "$OWNER" + gh issue create --title "$TITLE" --body-file "$T/body.md" echo "Opened an issue: $N commit(s) to review." >> "$GITHUB_STEP_SUMMARY" fi @@ -228,7 +226,7 @@ jobs: gh issue close "$issue" --comment "The upstream watch ran cleanly again: $RUN_URL" done < "$RUNNER_TEMP/failed-issues.txt" - # Assigning an issue emails the owner. One issue by title, commented on when it fails + # The daily digest lists it. One issue by title, commented on when it fails # again, so daily failures do not pile up. - name: Notify on failure if: failure() @@ -236,7 +234,6 @@ jobs: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} TITLE: 'Upstream watch failed' RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} - OWNER: ${{ github.repository_owner }} run: | set -euo pipefail # When the issues cannot be read, a second issue is better than none. @@ -246,6 +243,5 @@ jobs: gh issue comment "$EXISTING" --body "Failed again: $RUN_URL" else gh issue create --title "$TITLE" \ - --body "The upstream watch failed. Run: $RUN_URL" \ - --assignee "$OWNER" + --body "The upstream watch failed. Run: $RUN_URL" fi diff --git a/.github/workflows/vendored-updates.yml b/.github/workflows/vendored-updates.yml index d5913ef26..99fe0e506 100644 --- a/.github/workflows/vendored-updates.yml +++ b/.github/workflows/vendored-updates.yml @@ -12,7 +12,7 @@ name: Vendored updates # StreamSaver.js's API name: its body is FastStream's own OPFS and memory sinks, so an # upstream change had nothing to port.) # -# The issue is assigned to the owner and @mentions them. A title is never raised twice, open or +# The daily digest lists the issue. A title is never raised twice, open or # closed: closing one skips that release for good. Each closes itself once its pin is at # its tag or later, and a newer release's item closes the older ones. (The silero-vad model # pull request went with the speech detector, 2026-10-10.) @@ -39,7 +39,6 @@ jobs: env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} GH_REPO: ${{ github.repository }} - OWNER: ${{ github.repository_owner }} RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} UPSTREAM: 'Dash-Industry-Forum/dash.js' VTT_PATH: 'contrib/videojs-vtt.js/vtt.js' @@ -121,9 +120,9 @@ jobs: exit 1 fi # shellcheck disable=SC2016 # backticks are Markdown - printf '@%s dash.js %s is out and %s.\n\n`chrome/player/modules/vtt.mjs` is that file at dash.js %s with the changes `tools/verify-vtt.mjs` re-applies. Move the tag in its UPSTREAM line to %s and run `pnpm run verify:vtt`: it says where a change no longer applies. This issue closes itself once the pin is at %s or later; closing it skips %s.\n' \ - "$OWNER" "$latest" "$what" "$pinned_tag" "$latest" "$latest" "$latest" > "$RUNNER_TEMP/body.md" - url=$(gh issue create --title "$title" --assignee "$OWNER" --body-file "$RUNNER_TEMP/body.md") + printf 'dash.js %s is out and %s.\n\n`chrome/player/modules/vtt.mjs` is that file at dash.js %s with the changes `tools/verify-vtt.mjs` re-applies. Move the tag in its UPSTREAM line to %s and run `pnpm run verify:vtt`: it says where a change no longer applies. This issue closes itself once the pin is at %s or later; closing it skips %s.\n' \ + "$latest" "$what" "$pinned_tag" "$latest" "$latest" "$latest" > "$RUNNER_TEMP/body.md" + url=$(gh issue create --title "$title" --body-file "$RUNNER_TEMP/body.md") echo "Opened $url" close_pass "$url" @@ -136,10 +135,10 @@ jobs: number=$(gh issue list --state open --limit 200 --json number,title \ --jq '[.[] | select(.title == env.TITLE)][0].number // empty') || number='' if [ -n "$number" ]; then - printf '@%s Failed again: %s\n' "$OWNER" "$RUN_URL" > "$RUNNER_TEMP/comment.md" + printf 'Failed again: %s\n' "$RUN_URL" > "$RUNNER_TEMP/comment.md" gh issue comment "$number" --body-file "$RUNNER_TEMP/comment.md" exit 0 fi - printf '@%s vendored-updates.yml failed: %s\n\nThe run log names the failed command. Close this issue once it is fixed; a later failure opens a new one.\n' \ - "$OWNER" "$RUN_URL" > "$RUNNER_TEMP/body.md" - gh issue create --title "$TITLE" --assignee "$OWNER" --body-file "$RUNNER_TEMP/body.md" + printf 'vendored-updates.yml failed: %s\n\nThe run log names the failed command. Close this issue once it is fixed; a later failure opens a new one.\n' \ + "$RUN_URL" > "$RUNNER_TEMP/body.md" + gh issue create --title "$TITLE" --body-file "$RUNNER_TEMP/body.md" diff --git a/docs/maintenance.md b/docs/maintenance.md index 0fcf6641e..453d99b54 100644 --- a/docs/maintenance.md +++ b/docs/maintenance.md @@ -3,12 +3,13 @@ Nothing merges itself (your choice, 2026-10-02): every update arrives as a PR and waits for your merge. `update-prs.yml` runs after every completed CI run for this repository's `dependabot/*`, `toolchain/*` and `patched/*` branches and comments -on the PR, assigned to you: **ready to merge** once CI is green and its checks hold (only +on the PR without a mention and with no one assigned: **ready to merge** once CI is green and its checks hold (only that bot's commits, only the files that kind of update changes, the dependency review passed, and the build unchanged when it is not meant to ship), or what to look at before merging, or why CI failed; red twice, it is parked with a migration issue that says what to do. The comment says whether your merge releases: an update that ships starts CI on `main` with your merge, -and a green run there releases it to Firefox. The mpv pin comes as one PR that keeps up with +and a green run there releases it to Firefox. None of those comments or issues mentions you +or assigns you, and the daily digest lists them (below). The mpv pin comes as one PR that keeps up with shinchiro's builds. Updates without a PR - a runner image, a new Firefox - are watched by their own workflows, which stay silent while they pass. Until 2026-10-02 green updates merged themselves. @@ -65,9 +66,11 @@ the fix is on `main`, and a push that changes a lockfile waits up to 10 minutes (the daily run closes it otherwise). Dismissing an alert on the Security tab closes it too. Closing the issue yourself skips the alerts it lists; a new alert for that package still opens a new one. -In the table, "ready to merge" means one comment that @mentions you - CI is green and -every check holds - with the PR assigned to you; a comment with the same verdict as the -last one is edited in place, so a repeat sends no second email. +In the table, "ready to merge" means one comment on the PR - CI is green and every check +holds - written without a mention and with no one assigned, so it does not email you; the +daily digest lists the PR. A comment with the same verdict as the last one is edited in +place, so a repeat makes no new digest entry: the PR keeps one line, with nothing marked +new. ## Your PC @@ -98,7 +101,20 @@ WSL (`pnpm run verify:linux` updates them). Run the check whenever you like: it changes nothing, and lists everything out of date with what to do about it. -## When you get an email +## Email: one a day + +Every day at 10:00 UTC the pinned issue **Daily digest** gets one comment that @mentions +you: that is the one email a day (`daily-digest.yml`). The comments and issues below +mention no one; the digest lists them. + +Your GitHub settings for one email a day: + +- **Settings > Notifications > System > Actions**: untick Email (keep Web), so failed runs + of workflows you started or scheduled send none. +- **Settings > Notifications > Dependabot alerts**: untick Email, or pick the weekly email + digest. +- Unwatch pull requests and issues you were mentioned on before (each one's Unsubscribe + button), or they keep emailing on every comment until closed. ### CI red on an update PR @@ -108,7 +124,7 @@ comment says it failed twice. (If GitHub refused to start that second run, the c reports the one failure.) It holds a table of each failed job, the step that failed and what that step checks, the last 40 lines of each failed log, and `main`'s latest CI status; the PR carries the `ci-failed` -label and is assigned to you. +label. It mentions no one, so it emails no one: the daily digest lists the red PR. Failed twice, the new version most likely needs changes to the code here, which no rerun or rebase brings (your call, 2026-10-20). So the PR is also labelled `hold`, and one issue, @@ -123,15 +139,16 @@ or rebase brings (your call, 2026-10-20). So the PR is also labelled `hold`, and The issue closes itself when CI passes on the PR, which also takes off the `hold` label the workflow set (one you set yourself stays), or when the PR is merged or closed (checked once -a day). Failing twice again edits the same issue: no second email. +a day). Failing twice again edits the same issue: no new digest entry. A run that failed only because GitHub or a download host did not answer (a 5xx on geckodriver's, mpv's or the npm registry's download, after their own tries) is no failure of -the update: `update-prs.yml` comments without an @mention that it was an outage, and +the update: `update-prs.yml` comments that it was an outage, and `outage-retry.yml` runs CI again on the commit three hours later, up to three times. No `ci-failed`, no `hold`, no migration issue, and an outage run is no first try. Only when the -third retry meets an outage too, or the retry cannot be started, does a comment @mention you: -start CI on the branch by hand once GitHub answers again. A failed job that left no log (a +third retry meets an outage too, or the retry cannot be started, does the comment ask you to +start CI on the branch by hand once GitHub answers again - still without a mention, so the +daily digest is where you read it. A failed job that left no log (a lost runner) opens no migration issue either; the comment says so. Or, by hand: @@ -150,9 +167,10 @@ Or, by hand: Otherwise the comment names the check the PR failed - a tooling update whose build differs from the latest release, a file that kind of update does not change, a GitHub Actions update while `UPDATE_PRS_TOKEN` is missing, the `hold` label - and the PR is -assigned to you. Look at that, then merge it or close it as above. The comment is -edited in place while the PR keeps waiting, even if the reasons change: a new email -comes when it turns red, turns green, is merged, or fails at a different step. +written with no one assigned and no mention, so the daily digest is where you read it. Look +at that, then merge it or close it as above. The comment is edited in place while the PR +keeps waiting, even if the reasons change: a new digest entry comes when it turns red, turns +green, is merged, or fails at a different step. A CI run that was cancelled or skipped decides nothing. The next completed run does: rerun it from the Actions tab, or push. (The daily toolchain run starts CI on diff --git a/docs/notes/build-and-release.md b/docs/notes/build-and-release.md index 3fdfa199b..c8c7018d1 100644 --- a/docs/notes/build-and-release.md +++ b/docs/notes/build-and-release.md @@ -125,7 +125,7 @@ latest release and, if incomplete, builds that tag and asks AMO for the version tag's): signed -> download (byte-identical to web-ext's file, checked on 1.3.82.27; the regenerated `updates.json` matched the published one exactly) and attach both; pending -> next run; missing (never uploaded) -> sign now; -rejected, or still incomplete after 24 h -> one issue, assigned + @mention, closed when the +rejected, or still incomplete after 24 h -> one issue (the daily digest lists it), closed when the release is complete. Re-running `web-ext sign` cannot do this: AMO refuses a second upload of a version. v1.3.79.0 and v1.3.82.2, the two releases without an xpi, are both `public` on AMO - the failsafe would have collected them. `release.yml`'s `timeout-minutes: 45` diff --git a/docs/notes/mpv.md b/docs/notes/mpv.md index 705b8da23..533c55879 100644 --- a/docs/notes/mpv.md +++ b/docs/notes/mpv.md @@ -642,11 +642,11 @@ if the registry key or its manifest is missing rather than letting the specs skip. `mpv-updates.yml` offers each new pin: each day it pushes shinchiro's newest build as `mpv/<release>`, dispatches CI on it, reruns failed jobs once, and on a pass moves branch `mpv-update` to that commit, with ONE pull request from -it that keeps up (retitled per build, assigned to the owner, who merges it; the +it that keeps up (retitled per build, listed by the daily digest; the owner merges it; the pin ships nothing, so his merge releases nothing). Until 2026-10-02 the pin commit went onto main directly. A failed build, or a pinned release gone upstream (shinchiro keeps about 30), opens or updates -one assigned issue, "mpv update failed: ...", which a later pass closes. A +one issue, "mpv update failed: ...", which a later pass closes. A failed build keeps its branch, so it is not tried again; delete the branch to retry it. The host's own functions are unit tested (`tests/unit/MpvNativeHost`, `mpvHostPath`, diff --git a/docs/notes/workflows.md b/docs/notes/workflows.md index 527ef9bd3..62db874b7 100644 --- a/docs/notes/workflows.md +++ b/docs/notes/workflows.md @@ -209,11 +209,13 @@ on the same commit, and that run decides (a new run, not a re-run: a re-run by this workflow's token would reach no workflow when it ends); not when CI already failed on that commit (an earlier run, or this run was re-run by hand), and when GitHub refuses - to start it, this run decides. Still red, one comment @mentions the owner with a - table of failed job, step and what the step checks, the last 40 lines of each failed - log and `main`'s latest CI status, and the PR is labelled `ci-failed` and assigned to them. CI green: + to start it, this run decides. Still red, one comment on the PR + without a mention and with no one assigned (since 2026-11-09) names + the failed job, step and what the step checks, the last 40 lines of each failed + log and `main`'s latest CI status, and the PR is labelled `ci-failed`; the daily digest + lists it. CI green: **nothing merges itself** (the owner's choice, 2026-10-02; from 2026-10-01 every kind - did). One comment @mentions the owner, assigned: "ready to merge" for any kind (Dependabot + did). One comment, no mention, no assignment: "ready to merge" for any kind (Dependabot npm, fsaunpack, GitHub Actions, the actionlint/zizmor images, toolchain Node and pnpm, patched libraries) when it is not labelled `hold`, and only when its bot opened it (not a draft, @@ -238,15 +240,16 @@ (Contents, Pull requests, Workflows: write; docs/maintenance.md, "A token for workflow updates"), used only to bring such a branch up to date. His merge is his push: CI runs on `main`, and a green run of a merge that ships releases. A green PR that - fails a check gets one comment @mentioning the owner - CI is green, and what to look at - before merging - and is assigned to them. A comment with the same verdict - as the last one is edited in place, so it sends no new mail: the owner hears when a + fails a check gets one comment - CI is green, and what to look at + before merging - written without a mention and with no one assigned; the daily digest lists + it. A comment with the same verdict as the last one is edited in place, so the digest + marks nothing new: the owner hears when a verdict changes. The "behind main" check is the last one: a branch behind `main` is brought up to date and CI decides again, so what he merges is what CI tested. A branch update refused because the branch moved on meanwhile (Dependabot rebased it), or the PR was closed, is no failure: the new commit's CI run decides, or there is nothing to decide. A cancelled or skipped CI run decides nothing. The comment keeps one key per - verdict, so a changed reason edits it without a new email. GitHub deletes a merged + verdict, so a changed reason edits it without a new digest entry. GitHub deletes a merged branch. `main`'s ruleset blocks force-pushes and deletion only, no required checks. If `update-prs.yml` itself fails, it opens one issue "Update PRs workflow failed" (the decide step has its own `timeout-minutes` under the job's, so running out of time fails the step and still @@ -276,14 +279,35 @@ unknown (no failed job, or one without a log); never a bare "504", which specs test. update-prs.yml reads it at its own commit through the API (no checkout), classifies the run and the newest six earlier failed runs on the commit: an outage run is no first try, and an - outage comments (key `outage <sha12>`, no @mention, no label) and starts + outage comments (key `outage <sha12>`, no label) and starts `outage-retry.yml` (workflow_dispatch on main: branch, sha, delay; per-branch concurrency, cancel-in-progress; it sleeps, checks an open PR is still at the sha, starts ci.yml, each - call tried again 1, 2, 5 and 10 min later, and comments on the PR with an @mention when CI - still could not start) up to three times; then an @mention (key `outage stuck <sha12>`). Unknown never migrates. + call tried again 1, 2, 5 and 10 min later, and comments on the PR when CI + still could not start) up to three times; then a comment to start it by hand (key + `outage stuck <sha12>`). Unknown never migrates. firefox-beta.yml's report reads each failed job's log through the API (its run is still going) and opens no issue on an outage. Tests: `tests/workflows/ci-outage.test.sh`, the outage scenarios in `update-prs-decide.test.sh`. +- **One email a day** (2026-11-09, the owner's wish: "just one email per repo that gets + sent daily"). No workflow @mentions or assigns the owner any more (both email); its + issues, PRs and comments are written plainly, and `daily-digest.yml` (daily, 10:00 UTC) + comments once on the pinned issue "Daily digest", @mentioning him once. The window is + [the last digest's `<!-- daily-digest: <time> -->` marker, now): the marker holds the time + the run started, read from the open digest issue or else the newest closed one (closing the + issue starts a new one that goes on from there), at most 7 days back. In it: failed runs + that *ended* in the window (fetched per conclusion, failure/timed_out/startup_failure, + created up to a day before it), grouped by workflow id and branch with that workflow's + newest run there (passed since / failure / running); the open PRs with update-prs.yml's + latest key (new when that comment was *created* in the window: a same-key edit is not + news); PRs merged or closed, issues opened or closed, releases; issue and review comments + from anyone but the owner and bots; open and new Dependabot and code scanning alerts. A + quiet day posts nothing and keeps the old marker. If the digest run fails, its second + step @mentions the owner on the digest issue. `tests/workflows/no-mentions.test.sh` fails + on any other workflow that mentions or assigns again; `daily-digest.test.sh` tests the + script. GitHub's own emails (Actions runs he started or scheduled, Dependabot alerts) are + his settings: docs/maintenance.md, "Email: one a day". (Reviewed by Codex: 8 findings, + all taken: the start-time cursor, runs by end time, by workflow id, review comments, the + cursor across a closed issue, the created-not-updated verdict time, no silent list limits.) - **Update guards** (2026-10-20, the owner's calls, so that a green update PR can be merged without a second look). (1) **Red twice: hold + a migration issue.** When the second try fails too (again=1) on a dependabot/, toolchain/ @@ -574,7 +598,7 @@ (every artifact named `e2e-retried*`, all of this repository's branches, the last 7 days; never a fork's pull request, whose CI run writes the list with its own code, and the texts are cut down to a path's characters, #166) in one - issue "Flaky e2e specs: week to <date>", assigned + @mention: per spec, how often it was + issue "Flaky e2e specs: week to <date>" (the daily digest lists it): per spec, how often it was run again, how often its retry passed, suites, branches and runs. It finds the artifacts through the repository's artifact list (`actions/artifacts?name=`), not run by run. The next week's issue closes it, and so does a week with no retry. Permissions: `actions: @@ -584,8 +608,8 @@ brace-expansion (three majors in the lockfile), its failed run emailed no one, and 12 alerts sat on the Security tab unseen. For an open alert over 6 hours old with no open Dependabot PR naming the package (title, the links before the first `<details>`, a - grouped update's "Updates" lines), it opens "Security alert: <package>", assigned + - @mention; a later alert edits it and comments. The body's `<!-- alerts: ... -->` line + grouped update's "Updates" lines), it opens "Security alert: <package>" (the daily + digest lists it); a later alert edits it and comments. The body's `<!-- alerts: ... -->` line records what it listed: a listed alert is never raised again, so closing by hand skips. The advisory summary's `<` is escaped in the table, as a marker in it would hide that line from the next run (which would comment every day). Closes itself when no alert for the package is open. `vulnerability-alerts: read` is @@ -649,8 +673,8 @@ `vendored/silero-vad-<tag>` for a newer model, went with the speech detector on 2026-10-10.) **vtt.js:** a dash.js release that changes `contrib/videojs-vtt.js/vtt.js` gets an issue, "vtt.js changed in dash.js <tag>", with the changed-line count and a compare link: a - person moves `tools/verify-vtt.mjs`'s tag and re-runs `verify:vtt`. Each item is assigned - + @mention, is never raised twice (closing it skips that release), closes itself once its + person moves `tools/verify-vtt.mjs`'s tag and re-runs `verify:vtt`. The daily digest lists + each item; it is never raised twice (closing it skips that release), closes itself once its pin reaches its tag, and a newer release's item closes older ones. On 2026-10-01 both were current in effect: silero-vad v6.2.3 and dash.js v5.2.1 publish the same bytes as the pins (v6.2.1, v5.1.0). `tests/workflows/vendored-updates.test.sh`: 10 scenarios @@ -692,9 +716,9 @@ yt" has no code to apply to, V1.3.78 is a version bump; the aspect-ratio option (#552) is a feature for his decision. `docs/upstream-sync-log.md` keeps the record of the merge years. - **`upstream-watch.yml`** (2026-10-09) runs daily (06:00 UTC) and lists upstream's commits - not reviewed yet in one issue, "Upstream: N commit(s) to review", assigned to the owner - and @mentioning him; when upstream moves again the issue is updated and a comment with - the mention says how many came. Each commit names its files, those this fork still has in + not reviewed yet in one issue, "Upstream: N commit(s) to review", which the daily + digest lists; when upstream moves again the issue is updated and a comment says how many + came. Each commit names its files, those this fork still has in bold, and "fix?" when its message speaks of a fix, a bug, a crash, a leak or security; a commit touching only files this fork does not have is listed apart. **Closing the issue marks its commits reviewed**: the next run starts at the closed issues' upstream commit @@ -719,7 +743,7 @@ Clean, checks passed: a PR from `patched/<name>-<version>`, with CI dispatched on it (a push by `GITHUB_TOKEN` starts no workflow; `gh workflow run` does). Conflict or failed check: an issue with the tool's report. Both - assigned + @mention, never raised twice (issue or PR titles, any state), closed when + listed by the daily digest, never raised twice (issue or PR titles, any state), closed when the patch is cut against that version or newer; closing one by hand skips the version. Runbook: `docs/updating-patched-libraries.md`. The tool reproduces every current patch byte for byte and replays the hand-cut hls.js 1.7.2 -> 1.7.3 upgrade diff --git a/docs/updating-patched-libraries.md b/docs/updating-patched-libraries.md index 7249798d2..d26819565 100644 --- a/docs/updating-patched-libraries.md +++ b/docs/updating-patched-libraries.md @@ -7,8 +7,8 @@ these alone and the **Patched libraries** workflow watches them instead, once a ## When a new version comes out -The workflow tries the update itself, with `tools/recut-patch.mjs`, and tells you by -email (assigned to you, with an @mention) in one of two ways: +The workflow tries the update itself, with `tools/recut-patch.mjs`, and tells you, +through the daily digest (`daily-digest.yml`), in one of two ways: - **A pull request, "Patched library update: `<name>` `<version>`"** - the patch moved onto the new release cleanly and passed the tool's checks. The workflow starts CI on diff --git a/tests/workflows/amo-signing-failsafe.test.sh b/tests/workflows/amo-signing-failsafe.test.sh index a29b4b3d1..c6f75e68f 100644 --- a/tests/workflows/amo-signing-failsafe.test.sh +++ b/tests/workflows/amo-signing-failsafe.test.sh @@ -188,7 +188,7 @@ report() { echo 'the failsafe failed, no issue open' fresh check 'the step passes' report '[{"number": 3, "title": "Other"}]' -check 'opens "Release failsafe failed"' contains "$LOG" 'issue create --repo me/fs --title Release failsafe failed --assignee nawid' +check 'opens "Release failsafe failed"' contains "$LOG" 'issue create --repo me/fs --title Release failsafe failed' echo 'the failsafe failed again, its issue open' fresh diff --git a/tests/workflows/auto-release.test.sh b/tests/workflows/auto-release.test.sh index c5a5ac932..8d280cdcd 100644 --- a/tests/workflows/auto-release.test.sh +++ b/tests/workflows/auto-release.test.sh @@ -145,11 +145,11 @@ scenario restart 'attempt 2 cancelled by another late run' COMMITS="$(on_main "$ RUNS="$(arr "$(mk 4 $B push 10:10 in_progress null 1)" "$(mk 2 $A push 10:09 completed '"cancelled"' 2)")" EXPECT='(attempt 2)|>> rerun 2' LACKS='issue create' scenario restart 'attempt 3 cancelled by a late run: issue' COMMITS="$(on_main "$(cm $A 'fix: a')")" \ RUNS="$(arr "$(mk 5 $B push 10:12 in_progress null 1)" "$(mk 2 $A push 10:11 completed '"cancelled"' 3)")" \ - EXPECT='>> issue create --title CI on main needs a re-run --assignee owner|@owner CI on main for aaaa1111 cannot finish: its attempt 3 was cancelled by a later run too|/actions/runs/2' LACKS='>> rerun' + EXPECT='>> issue create --title CI on main needs a re-run|CI on main for aaaa1111 cannot finish: its attempt 3 was cancelled by a later run too|/actions/runs/2' LACKS='>> rerun' scenario restart 'attempt 3 cancelled, the issue already open: a comment on it' COMMITS="$(on_main "$(cm $A 'fix: a')")" \ ISSUES='[{"number":8,"title":"Other"},{"number":7,"title":"CI on main needs a re-run"}]' \ RUNS="$(arr "$(mk 5 $B push 10:12 in_progress null 1)" "$(mk 2 $A push 10:11 completed '"cancelled"' 3)")" \ - EXPECT='>> issue comment 7 --body-file|@owner CI on main for aaaa1111 cannot finish either: its attempt 3 was cancelled by a later run too. The run: https://github.com/o/r/actions/runs/2' \ + EXPECT='>> issue comment 7 --body-file|CI on main for aaaa1111 cannot finish either: its attempt 3 was cancelled by a later run too. The run: https://github.com/o/r/actions/runs/2' \ LACKS='issue create|>> rerun' scenario restart 'rerun refused, but started meanwhile' RERUN_RC=1 COMMITS="$(on_main "$(cm $A 'fix: a')")" \ RUNS="$(arr "$(mk 3 $B push 10:06 in_progress null 1)" "$(mk 2 $A push 10:05 completed '"cancelled"' 1)")" \ diff --git a/tests/workflows/daily-digest.test.sh b/tests/workflows/daily-digest.test.sh new file mode 100644 index 000000000..911028c7a --- /dev/null +++ b/tests/workflows/daily-digest.test.sh @@ -0,0 +1,326 @@ +#!/usr/bin/env bash +# daily-digest.yml's "Post the digest": one comment a day on the pinned "Daily digest" issue, +# @mentioning the owner once, with what happened since its last comment; nothing on a quiet +# day. gh and date's "now" are real but gh is stubbed: it answers from $STATE and logs calls. +source "$(dirname "$0")/lib.sh" +STEP=$(mktemp) +step_script daily-digest.yml 'Post the digest' > "$STEP" || exit 1 +TDIRS=() +cleanup() { rm -f "$STEP"; rm -rf "${TDIRS[@]}"; } +trap cleanup EXIT +checks=0 +fails=0 +scenario= + +check() { + local desc=$1 + shift + checks=$((checks + 1)) + if "$@"; then + printf 'PASS %s: %s\n' "$scenario" "$desc" + else + printf 'FAIL %s: %s\n' "$scenario" "$desc" + tail -n 20 "$T/out" + fails=$((fails + 1)) + fi +} + +setup() { + scenario=${FUNCNAME[1]} + T=$(mktemp -d) + TDIRS+=("$T") + STATE=$T/state BIN=$T/bin + mkdir -p "$STATE" "$BIN" "$T/tmp" + export STATE PATH="$BIN:$PATH" RUNNER_TEMP=$T/tmp GH_TOKEN=x GH_REPO=me/fs OWNER=nawid + export GITHUB_SERVER_URL=https://github.com GITHUB_WORKFLOW='Daily digest' + export SELF_URL=https://github.com/me/fs/actions/runs/9000 + # An hour ago, two days ago: times inside and outside the window of a digest a day ago. + HOUR_AGO=$(date -u -d '-1 hour' '+%Y-%m-%dT%H:%M:%SZ') + OLD=$(date -u -d '-2 days' '+%Y-%m-%dT%H:%M:%SZ') + DAY_AGO=$(date -u -d '-1 day' '+%Y-%m-%dT%H:%M:%SZ') + echo '[{"number": 900, "title": "Daily digest", "author": {"login": "app/github-actions"}, "state": "OPEN"}]' > "$STATE/issues.json" + jq -n --arg t "$DAY_AGO" '[{user: {login: "github-actions[bot]"}, created_at: $t, body: "yesterday\n<!-- daily-digest: \($t) -->"}]' > "$STATE/comments-900.json" + echo '[]' > "$STATE/runs.json" + echo '[]' > "$STATE/review-comments.json" + echo '[]' > "$STATE/prs.json" + echo '[]' > "$STATE/closed-prs.json" + echo '[]' > "$STATE/created-issues.json" + echo '[]' > "$STATE/closed-issues.json" + echo '[]' > "$STATE/releases.json" + echo '[]' > "$STATE/all-comments.json" + echo '[]' > "$STATE/dependabot.json" + echo '[]' > "$STATE/code-scanning.json" + : > "$STATE/gh.log" + cat > "$BIN/gh" <<'GHSTUB' +#!/usr/bin/env bash +set -e +printf '%s\n' "$*" >> "$STATE/gh.log" +ORIG=("$@") +opt() { local flag=$1 prev= a; shift; for a in "$@"; do if [ "$prev" = "$flag" ]; then printf '%s\n' "$a"; return 0; fi; prev=$a; done; return 1; } +out() { local e; if e=$(opt --jq "${ORIG[@]}"); then jq -r "$e" "$1"; else cat "$1"; fi; } +case "$1 $2" in + 'issue list') + s=$(opt --search "$@" || true) + case $s in + created:*) out "$STATE/created-issues.json" ;; + closed:*) out "$STATE/closed-issues.json" ;; + *'in:title') out "$STATE/issues.json" ;; + *) jq '[.[] | select(.state == "OPEN")]' "$STATE/issues.json" > "$STATE/il.json"; out "$STATE/il.json" ;; + esac + ;; + 'issue create') + jq --arg t "$(opt --title "$@")" '. + [{number: 901, title: $t, author: {login: "app/github-actions"}, state: "OPEN"}]' \ + "$STATE/issues.json" > "$STATE/i" && mv "$STATE/i" "$STATE/issues.json" + echo '[]' > "$STATE/comments-901.json" + echo 'https://github.com/me/fs/issues/901' + ;; + 'issue pin') : > "$STATE/pinned-$3" ;; + 'issue comment') cp "$(opt --body-file "$@")" "$STATE/posted-$3.md" ;; + 'pr list') + if [ "$(opt --state "$@")" = closed ]; then out "$STATE/closed-prs.json"; else out "$STATE/prs.json"; fi + ;; + 'release list') out "$STATE/releases.json" ;; + *) + [ "$1" = api ] || { echo "stub gh: unhandled: $*" >&2; exit 99; } + path=$2; [ "$path" = --paginate ] && path=$3 + case $path in + repos/me/fs/issues/comments\?*) out "$STATE/all-comments.json" ;; + repos/me/fs/issues/*/comments\?*) + n=${path#repos/me/fs/issues/}; n=${n%%/*} + [ -f "$STATE/comments-$n.json" ] || echo '[]' > "$STATE/comments-$n.json" + out "$STATE/comments-$n.json" + ;; + repos/me/fs/pulls/comments\?*) out "$STATE/review-comments.json" ;; + repos/me/fs/actions/runs\?*) + # ?status=<conclusion>&created=>=...: the runs that ended so. + c=${path#*status=}; c=${c%%&*} + jq --arg c "$c" '{workflow_runs: [.[] | select(.conclusion == $c)]}' "$STATE/runs.json" > "$STATE/r.json"; out "$STATE/r.json" + ;; + repos/me/fs/actions/workflows/*/runs\?*) + # One workflow's newest run on a branch (?branch=<uri-encoded>&per_page=1). + w=${path#repos/me/fs/actions/workflows/}; w=${w%%/*} + b=''; case $path in *branch=*) b=${path#*branch=}; b=${b%%&*} ;; esac + jq --argjson w "$w" --arg b "$b" '{workflow_runs: [.[] | select(.workflow_id == $w and ($b == "" or (.head_branch | @uri) == $b))] | sort_by(-.id)}' \ + "$STATE/runs.json" > "$STATE/w.json"; out "$STATE/w.json" + ;; + repos/me/fs/dependabot/alerts\?*) [ -f "$STATE/alerts_unreadable" ] && exit 1; out "$STATE/dependabot.json" ;; + repos/me/fs/code-scanning/alerts\?*) out "$STATE/code-scanning.json" ;; + *) echo "stub gh: unhandled api: $*" >&2; exit 99 ;; + esac + ;; +esac +GHSTUB + chmod +x "$BIN/gh" + printf '\n=== %s ===\n' "$scenario" +} +run_step() { rc=0; (cd "$T" && timeout 60 bash -e "$STEP") > "$T/out" 2>&1 || rc=$?; } +posted() { cat "$STATE/posted-900.md"; } +has() { grep -qF -- "$1" "$STATE/posted-900.md"; } + +run() { # <workflow name> <branch> <conclusion> <id> [ended at] [workflow id] [status]: a run + local file=${1,,} + jq --arg n "$1" --arg b "$2" --arg c "$3" --argjson id "$4" --arg u "${5:-$HOUR_AGO}" \ + --argjson w "${6:-$(printf '%s' "$1" | cksum | cut -d' ' -f1)}" --arg s "${7:-completed}" --arg p ".github/workflows/${file// /-}.yml" \ + '. + [{id: $id, workflow_id: $w, name: $n, path: $p, head_branch: $b, event: "push", status: $s, conclusion: (if $s == "completed" then $c else null end), + updated_at: $u, html_url: "https://github.com/me/fs/actions/runs/\($id)"}]' \ + "$STATE/runs.json" > "$STATE/x" && mv "$STATE/x" "$STATE/runs.json" +} +pr() { # <number> <title> <createdAt> [labels json] + jq --argjson n "$1" --arg t "$2" --arg c "$3" --argjson l "${4:-[]}" \ + '. + [{number: $n, title: $t, url: "https://github.com/me/fs/pull/\($n)", createdAt: $c, labels: $l}]' \ + "$STATE/prs.json" > "$STATE/x" && mv "$STATE/x" "$STATE/prs.json" +} +verdict() { # <pr> <key> <created_at> [updated_at]: update-prs.yml's comment + [ -f "$STATE/comments-$1.json" ] || echo '[]' > "$STATE/comments-$1.json" + jq --arg k "$2" --arg c "$3" --arg u "${4:-$3}" '. + [{user: {login: "github-actions[bot]"}, created_at: $c, updated_at: $u, body: "text\n<!-- update-prs: \($k) -->"}]' \ + "$STATE/comments-$1.json" > "$STATE/x" && mv "$STATE/x" "$STATE/comments-$1.json" +} + +quiet_day() { + setup + pr 42 'Bump eslint' "$OLD" + verdict 42 ready "$OLD" + run CI main success 1 + run_step + check 'exit 0' test "$rc" -eq 0 + check 'no comment' test ! -f "$STATE/posted-900.md" + check 'says so' grep -qF 'Nothing new since the last digest' "$T/out" +} + +busy_day() { + setup + run CI main failure 10 + run CI main success 11 + run CI dependabot/npm_and_yarn/x failure 12 + run CI dependabot/npm_and_yarn/x failure 13 + run 'Daily digest' main failure 14 + pr 42 'Bump eslint' "$OLD" + verdict 42 'red 0123456789ab' "$HOUR_AGO" + pr 43 'Toolchain update: pnpm 12.18.0' "$HOUR_AGO" + verdict 43 ready "$HOUR_AGO" + pr 44 'Bump mediabunny' "$OLD" '[{"name": "hold"}]' + verdict 44 "outage aaaaaaaaaaaa" "$OLD" + pr 45 'ci: mpv' "$OLD" + jq -n --arg t "$HOUR_AGO" --arg o "$OLD" '[{number: 40, title: "Bump x", url: "https://github.com/me/fs/pull/40", mergedAt: $t, closedAt: $t}, + {number: 39, title: "Bump y, closed before the window", url: "u", mergedAt: null, closedAt: $o}]' > "$STATE/closed-prs.json" + jq -n --arg t "$HOUR_AGO" '[{number: 900, title: "Daily digest", url: "u", state: "OPEN", createdAt: $t}, + {number: 50, title: "Bug", url: "https://github.com/me/fs/issues/50", state: "OPEN", createdAt: $t}]' > "$STATE/created-issues.json" + jq -n --arg t "$HOUR_AGO" '[{number: 48, title: "Migration: x", url: "https://github.com/me/fs/issues/48", closedAt: $t}]' > "$STATE/closed-issues.json" + jq -n --arg t "$HOUR_AGO" '[{user: {login: "bob", type: "User"}, created_at: $t, pull_request_url: "https://api.github.com/repos/me/fs/pulls/43", html_url: "https://github.com/me/fs/pull/43#r1"}]' > "$STATE/review-comments.json" + jq -n --arg t "$HOUR_AGO" '[{tagName: "v1.3.82.176", publishedAt: $t, isDraft: false}, {tagName: "v1.3.82.175", publishedAt: "2026-01-01T00:00:00Z", isDraft: false}]' > "$STATE/releases.json" + jq -n --arg t "$HOUR_AGO" '[{user: {login: "alice", type: "User"}, created_at: $t, issue_url: "https://api.github.com/repos/me/fs/issues/50", html_url: "https://github.com/me/fs/issues/50#c1"}, + {user: {login: "nawid", type: "User"}, created_at: $t, issue_url: "x/51", html_url: "h"}, + {user: {login: "dependabot[bot]", type: "Bot"}, created_at: $t, issue_url: "x/52", html_url: "h"}]' > "$STATE/all-comments.json" + jq -n --arg t "$HOUR_AGO" --arg o "$OLD" '[{created_at: $t}, {created_at: $o}]' > "$STATE/dependabot.json" + run_step + check 'exit 0' test "$rc" -eq 0 + check 'one comment on the digest issue' test -f "$STATE/posted-900.md" + check '@mentions the owner once' test "$(grep -o '@nawid' "$STATE/posted-900.md" | wc -l)" -eq 1 + check 'its marker: where the next one goes on from' grep -qE '^<!-- daily-digest: [0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9:]{8}Z -->$' "$STATE/posted-900.md" + check 'a review comment on a line' has 'bob on [#43](https://github.com/me/fs/pull/43#r1)' + check 'not a PR closed before the window' bash -c '! grep -qF "Bump y" "$0"' "$STATE/posted-900.md" + check 'main failed, passed since' has '| CI | `main` | 1 | [passed since](https://github.com/me/fs/actions/runs/11) |' + check 'the branch failed twice, still failing' has '| CI | `dependabot/npm_and_yarn/x` | 2 | [failure](https://github.com/me/fs/actions/runs/13) |' + check 'not its own runs' bash -c '! grep -qF "| Daily digest |" "$0"' "$STATE/posted-900.md" + check 'red PR, verdict changed' has '[#42](https://github.com/me/fs/pull/42) Bump eslint: **CI fails** (verdict changed)' + check 'new PR, ready' has '[#43](https://github.com/me/fs/pull/43) Toolchain update: pnpm 12.18.0: **ready to merge** (new)' + check 'outage, with its label' has '[#44](https://github.com/me/fs/pull/44) Bump mediabunny: **outage: CI runs again later** [hold]' + check 'no verdict yet' has '**no verdict yet**' + check 'merged PR' has '[#40](https://github.com/me/fs/pull/40) Bump x: merged' + check 'new issue, not the digest' bash -c 'grep -qF "[#50](https://github.com/me/fs/issues/50) Bug" "$0" && ! grep -qF "Daily digest" <(grep "^- " "$0")' "$STATE/posted-900.md" + check 'closed issue' has '[#48](https://github.com/me/fs/issues/48) Migration: x' + check 'the new release only' bash -c 'grep -qF "v1.3.82.176" "$0" && ! grep -qF "v1.3.82.175" "$0"' "$STATE/posted-900.md" + check "alice's comment only" bash -c 'grep -qF "alice on [#50]" "$0" && ! grep -qE "(nawid|dependabot\[bot\]) on" "$0"' "$STATE/posted-900.md" + check 'alerts: 2 open, 1 new' has '- Dependabot alerts: 2 open, 1 new' + check 'no line for no code scanning alerts' bash -c '! grep -qF "Code scanning" "$0"' "$STATE/posted-900.md" +} + +same_verdict_edited() { + # update-prs.yml edited its old "ready" comment again (same verdict): nothing new. + setup + pr 42 'Bump eslint' "$OLD" + verdict 42 ready "$OLD" "$HOUR_AGO" + run_step + check 'exit 0' test "$rc" -eq 0 + check 'no comment' test ! -f "$STATE/posted-900.md" +} + +runs_by_when_they_ended() { + setup + # Ended in the window, though started before it (it was running at the last digest). + run CI main failure 10 "$HOUR_AGO" + # Ended before the window: the last digest had it. + run CI feature/x failure 11 "$OLD" + # Renamed since: the same workflow (id 7), "CI" failed and "Build" passed after. + run CI fix/y failure 12 "$HOUR_AGO" 7 + run Build fix/y success 13 "$HOUR_AGO" 7 + # Its newest run on the branch is still going. + run Live dependabot/z failure 14 "$HOUR_AGO" 8 + run Live dependabot/z failure 15 "$HOUR_AGO" 8 in_progress + run_step + check 'exit 0' test "$rc" -eq 0 + check 'the run that ended in the window' has '| CI | `main` | 1 | [failure](https://github.com/me/fs/actions/runs/10) |' + check 'not the one that ended before' bash -c '! grep -qF "feature/x" "$0"' "$STATE/posted-900.md" + check 'a renamed workflow is one: passed since' has '| CI | `fix/y` | 1 | [passed since](https://github.com/me/fs/actions/runs/13) |' + check 'its newest run still going' has '| Live | `dependabot/z` | 1 | [running](https://github.com/me/fs/actions/runs/15) |' +} + +odd_runs_and_late_issue() { + setup + # A run with no branch, and a workflow whose name has a |. + run Nightly '' failure 20 + jq '(.[] | select(.id == 20) | .head_branch) = null' "$STATE/runs.json" > "$STATE/x" && mv "$STATE/x" "$STATE/runs.json" + run 'build | prod' main failure 21 + # Closed five minutes ago: the next digest's (GitHub's search may not have it yet). + jq -n --arg t "$(date -u -d '-5 minutes' '+%Y-%m-%dT%H:%M:%SZ')" '[{number: 47, title: "Late", url: "u47", closedAt: $t}]' > "$STATE/closed-issues.json" + run_step + check 'exit 0' test "$rc" -eq 0 + check 'no branch: a "-", its newest run anywhere' has '| Nightly | `-` | 1 | [failure](https://github.com/me/fs/actions/runs/20) |' + check 'the | escaped' has '| build \| prod | `main` | 1 |' + check 'not the issue closed five minutes ago' bash -c '! grep -qF "Late" "$0"' "$STATE/posted-900.md" +} + +closed_digest_issue() { + # The digest issue was closed by hand after a digest three days ago: a new one goes on + # from there. + setup + three=$(date -u -d '-3 days' '+%Y-%m-%dT%H:%M:%SZ') + echo '[{"number": 900, "title": "Daily digest", "author": {"login": "app/github-actions"}, "state": "CLOSED"}]' > "$STATE/issues.json" + jq -n --arg t "$three" '[{user: {login: "github-actions[bot]"}, created_at: $t, body: "<!-- daily-digest: \($t) -->"}]' > "$STATE/comments-900.json" + pr 42 'Bump eslint' "$OLD" + run_step + check 'exit 0' test "$rc" -eq 0 + check 'a new issue' grep -q '^issue create --title Daily digest' "$STATE/gh.log" + check 'the two-day-old PR is new: the window starts three days back' grep -qF '(new)' "$STATE/posted-901.md" +} + +only_a_new_alert() { + setup + jq -n --arg t "$HOUR_AGO" '[{created_at: $t}]' > "$STATE/dependabot.json" + run_step + check 'a comment' test -f "$STATE/posted-900.md" + check 'the alert' has '- Dependabot alerts: 1 open, 1 new' +} + +alerts_unreadable() { + setup + : > "$STATE/alerts_unreadable" + run CI main failure 10 + run_step + check 'exit 0' test "$rc" -eq 0 + check 'says so' has '- Dependabot alerts: could not be read' +} + +no_issue_yet() { + setup + echo '[]' > "$STATE/issues.json" + run CI main failure 10 + run_step + check 'exit 0' test "$rc" -eq 0 + check 'makes the issue' grep -q '^issue create --title Daily digest' "$STATE/gh.log" + check 'pins it' test -f "$STATE/pinned-901" + check 'comments on it' test -f "$STATE/posted-901.md" +} + +window_from_last_digest() { + # The last digest was three days ago (two were skipped): the window starts there. + setup + jq -n --arg t "$(date -u -d '-3 days' '+%Y-%m-%dT%H:%M:%SZ')" '[{user: {login: "github-actions[bot]"}, created_at: $t, body: "<!-- daily-digest: \($t) -->"}]' > "$STATE/comments-900.json" + pr 42 'Bump eslint' "$OLD" + run_step + check 'the two-day-old PR is new' has '(new)' +} + +FAILED_STEP=$(mktemp) +step_script daily-digest.yml 'Say the digest failed' > "$FAILED_STEP" || exit 1 +run_failed_step() { rc=0; (cd "$T" && timeout 60 bash -e "$FAILED_STEP") > "$T/out" 2>&1 || rc=$?; } +digest_failed() { + setup + run_failed_step + check 'exit 0' test "$rc" -eq 0 + check 'comments on the digest issue, @mentioning the owner' grep -qF '@nawid The daily digest failed: https://github.com/me/fs/actions/runs/9000.' "$STATE/posted-900.md" +} +digest_failed_no_issue() { + setup + echo '[]' > "$STATE/issues.json" + run_failed_step + check 'exit 0' test "$rc" -eq 0 + check 'opens "Daily digest failed"' grep -q '^issue create --title Daily digest failed' "$STATE/gh.log" +} + +quiet_day +busy_day +only_a_new_alert +alerts_unreadable +no_issue_yet +window_from_last_digest +same_verdict_edited +runs_by_when_they_ended +closed_digest_issue +odd_runs_and_late_issue +digest_failed +digest_failed_no_issue +rm -f "$FAILED_STEP" + +printf '%d checks, %d failed\n' "$checks" "$fails" +[ "$fails" -eq 0 ] diff --git a/tests/workflows/flaky-specs.test.sh b/tests/workflows/flaky-specs.test.sh index 2fdfc1a1c..7952aae0f 100644 --- a/tests/workflows/flaky-specs.test.sh +++ b/tests/workflows/flaky-specs.test.sh @@ -146,9 +146,9 @@ zip_list 22 "$(rec download-names pbm-github win32 true) zip_list 14 "$(rec 'fork | @Nawid3333 ready to merge' web linux true)" run "s1 three lists this week (and a fork's) -> opens this week's issue, closes last week's" "$fold_step" check 'succeeds' test "$status" -eq 0 -check "opens \"${PREFIX}$today\", assigned to the owner" contains "$LOG" \ - "CREATE [--title] [${PREFIX}$today] [--assignee] [Nawid3333]" -check 'mentions the owner' contains "$LOG" "@Nawid3333 E2e spec files that failed once and were run again in the CI runs of the 7 days to $today" +check "opens \"${PREFIX}$today\"" contains "$LOG" \ + "CREATE [--title] [${PREFIX}$today]" +check 'its text' contains "$LOG" "E2e spec files that failed once and were run again in the CI runs of the 7 days to $today" check 'download-names: run again 3 times, passed 3, failed twice 0, both OSes, both branches, 3 runs' contains "$LOG" \ '| `tests/e2e/specs/download-names.e2e.mjs` | 3 | 3 | 0 | ext-amo (linux), ext-amo (win32), pbm-github (win32) | claude/y, main | [501](https://github.com/Nawid3333/FastStream/actions/runs/501) [503](https://github.com/Nawid3333/FastStream/actions/runs/503) |' check 'save-fmp4: run again once, failed twice once' contains "$LOG" \ @@ -212,10 +212,10 @@ check 'opens nothing' lacks "$LOG" 'CREATE' fixtures '[]' '[]' '[]' '[{"title":"Other"}]' run 's6 failure report, none open -> opens the failure issue' "$report_step" check 'succeeds' test "$status" -eq 0 -check 'opens "Flaky specs workflow failed", assigned to the owner' contains "$LOG" \ - 'CREATE [--title] [Flaky specs workflow failed] [--assignee] [Nawid3333]' -check 'mentions the owner and the run URL' contains "$LOG" \ - '@Nawid3333 flaky-specs.yml failed: https://github.com/Nawid3333/FastStream/actions/runs/1' +check 'opens "Flaky specs workflow failed"' contains "$LOG" \ + 'CREATE [--title] [Flaky specs workflow failed]' +check 'names the run URL' contains "$LOG" \ + 'flaky-specs.yml failed: https://github.com/Nawid3333/FastStream/actions/runs/1' fixtures '[]' '[]' '[]' '[{"number":3,"title":"Other"},{"number":12,"title":"Flaky specs workflow failed"}]' run 's7 failure report, already open -> a comment on it' "$report_step" diff --git a/tests/workflows/keepalive.test.sh b/tests/workflows/keepalive.test.sh index fe6fff01b..eedd35f6c 100644 --- a/tests/workflows/keepalive.test.sh +++ b/tests/workflows/keepalive.test.sh @@ -251,18 +251,18 @@ check 'the stub named the failure' contains "$FIX/out" 'stub gh: enable failed f scenario 's5 failure report, none open: opens "Keepalive failed"' \ "$report_step" '{}' false '{}' '[{"title":"Other"}]' '' check 'succeeds' test "$status" -eq 0 -check 'opens it assigned to the owner' contains "$LOG" \ - 'CREATE [--title] [Keepalive failed] [--assignee] [Nawid3333]' -check 'mentions the owner and links this run' contains "$LOG" \ - '@Nawid3333 keepalive.yml failed: https://github.com/Nawid3333/FastStream/actions/runs/1' +check 'opens it' contains "$LOG" \ + 'CREATE [--title] [Keepalive failed]' +check 'links this run' contains "$LOG" \ + 'keepalive.yml failed: https://github.com/Nawid3333/FastStream/actions/runs/1' scenario 's6 failure report, already open: comments on it' \ "$report_step" '{}' false '{}' '[{"number":3,"title":"Other"},{"number":12,"title":"Keepalive failed"}]' '' check 'succeeds' test "$status" -eq 0 check 'creates nothing' lacks "$LOG" 'CREATE' check 'comments on the open one' contains "$LOG" 'COMMENT [12] [--body-file]' -check 'the comment mentions the owner and links this run' contains "$LOG" \ - '@Nawid3333 Failed again: https://github.com/Nawid3333/FastStream/actions/runs/1' +check 'the comment links this run' contains "$LOG" \ + 'Failed again: https://github.com/Nawid3333/FastStream/actions/runs/1' scenario 's7 a committer date that is not an ISO date: refused, nothing enabled' \ "$enable_step" '{"commit":{"committer":{"date":"whenever the wind blows"}}}' false "$workflows" '[]' '' diff --git a/tests/workflows/live-streams-hand-off.test.sh b/tests/workflows/live-streams-hand-off.test.sh index e89b8e619..f1d499bf5 100644 --- a/tests/workflows/live-streams-hand-off.test.sh +++ b/tests/workflows/live-streams-hand-off.test.sh @@ -107,7 +107,7 @@ export START_FAILS=3 scenario 'refused three times' check 'fails' test "$status" -ne 0 check 'tries three times' test "$(starts)" -eq 3 -check 'opens the issue, assigned' contains "$FIX/calls" 'issue create --title Update PRs hand-off failed --assignee owner' +check 'opens the issue' contains "$FIX/calls" 'issue create --title Update PRs hand-off failed' check 'the issue says how to start it by hand' contains "$FIX/create" \ "Start it from the Actions tab: Update PRs, Run workflow, run_id 777 and branch $BRANCH." diff --git a/tests/workflows/no-mentions.test.sh b/tests/workflows/no-mentions.test.sh new file mode 100644 index 000000000..285707c0b --- /dev/null +++ b/tests/workflows/no-mentions.test.sh @@ -0,0 +1,31 @@ +#!/usr/bin/env bash +# One email a day (the owner's wish, 2026-11-09): only daily-digest.yml @mentions the owner. +# Every other workflow writes its issues, pull requests and comments without an @mention and +# assigns no one, since GitHub emails both. Fails on any workflow that does either again. +source "$(dirname "$0")/lib.sh" +checks=0 +fails=0 +expect() { # <description> <file with offending lines> + checks=$((checks + 1)) + if [ -s "$2" ]; then + printf ' FAIL %s:\n' "$1"; sed 's/^/ /' "$2"; fails=$((fails + 1)) + else + printf ' ok %s\n' "$1" + fi +} +here=$(mktemp -d) +trap 'rm -rf "$here"' EXIT +cd "$WORKFLOWS_DIR" || exit 1 +others=() +for f in *.yml; do [ "$f" = daily-digest.yml ] || others+=("$f"); done +grep -nE -- '--(add-)?assignee([ =]|$)|--add-reviewer' "${others[@]}" > "$here/assign" || true +expect 'no workflow but the digest assigns anyone' "$here/assign" +# An @ before a format's %s or an $OWNER/${OWNER}/${{ github.repository_owner }}, in a script +# or a string. (date's "@<seconds>", "body=@<file>" and package@version are no mentions.) +grep -nE "@(%s|\\\$OWNER|\\\$\{OWNER\}|\\\$\{\{ *github\.(repository_owner|actor)|Nawid3333)" "${others[@]}" > "$here/mention" || true +expect 'no workflow but the digest @mentions anyone' "$here/mention" +checks=$((checks + 1)) +if grep -qF "printf '@%s What happened in this repository" daily-digest.yml; then printf ' ok the digest @mentions the owner\n'; else + printf ' FAIL the digest no longer @mentions the owner\n'; fails=$((fails + 1)); fi +printf '%s checks, %s failed\n' "$checks" "$fails" +[ "$fails" = 0 ] diff --git a/tests/workflows/outage-retry.test.sh b/tests/workflows/outage-retry.test.sh index 6ddaac8b3..8f1715d8a 100644 --- a/tests/workflows/outage-retry.test.sh +++ b/tests/workflows/outage-retry.test.sh @@ -86,7 +86,7 @@ expect 'five tries' test "$(grep -c '^workflow run ci.yml' "$here/gh.log")" = 5 expect 'valid inputs: the comment step runs' valid run "$here/tell.sh" expect 'the comment is posted' called "pr comment $branch --body-file $here/body.md" -expect 'it @mentions the owner and says what to do' grep -qF '@nawid CI was to run again on this pull request after an outage, but could not be started (https://github.com/me/fs/actions/runs/9). Start it on this branch' "$here/body.md" +expect 'it says what to do' grep -qF 'CI was to run again on this pull request after an outage, but could not be started (https://github.com/me/fs/actions/runs/9). Start it on this branch' "$here/body.md" for bad in 'main' 'feature/x' 'dependabot/../main' 'dependabot/x y' 'dependabot/$(id)' '42'; do setup "$bad" "$sha" 10800 diff --git a/tests/workflows/release-hand-off.test.sh b/tests/workflows/release-hand-off.test.sh index eb175308d..c4f950923 100644 --- a/tests/workflows/release-hand-off.test.sh +++ b/tests/workflows/release-hand-off.test.sh @@ -99,9 +99,9 @@ scenario hand-off 'refused three times' check 'fails' test "$status" -ne 0 check 'tries three times' test "$(starts)" -eq 3 check 'looks for the issue among all open ones' contains "$FIX/calls" "$LIST" -check 'opens the issue, assigned' contains "$FIX/calls" 'issue create --title Auto release failed --assignee owner' +check 'opens the issue' contains "$FIX/calls" 'issue create --title Auto release failed' check 'the issue names the run, the commit and how to start it' contains "$FIX/create" \ - '@owner GitHub refused three times to start auto-release.yml for CI run 777 on main (abcdef12), a run this repository started. Start it from the Actions tab: Auto release, Run workflow, run_id 777.' + 'GitHub refused three times to start auto-release.yml for CI run 777 on main (abcdef12), a run this repository started. Start it from the Actions tab: Auto release, Run workflow, run_id 777.' export OPEN_ISSUES='[{"number":4,"title":"Other"},{"number":7,"title":"Auto release failed"}]' scenario hand-off 'refused three times, the issue already open' @@ -109,7 +109,7 @@ check 'fails' test "$status" -ne 0 check 'opens no second issue' lacks "$FIX/calls" 'issue create' check 'comments on the open one' contains "$FIX/calls" 'issue comment 7 --body-file' check 'the comment names this run and how to start it' contains "$FIX/comment" \ - "@owner GitHub refused three times to start auto-release.yml, for CI run 777 on main (abcdef12): $SELF_URL. Start it from the Actions tab: Auto release, Run workflow, run_id 777." + "GitHub refused three times to start auto-release.yml, for CI run 777 on main (abcdef12): $SELF_URL. Start it from the Actions tab: Auto release, Run workflow, run_id 777." export OPEN_ISSUES='[{"number":8,"title":"Auto release failed again"}]' scenario hand-off 'refused three times, an issue with a longer title open' @@ -125,22 +125,22 @@ export START_FAILS=0 OPEN_ISSUES='[{"number":4,"title":"Other"}]' LIST_FAILS='' scenario report 'auto-release failed, no issue open' check 'succeeds' test "$status" -eq 0 check 'looks for the issue among all open ones' contains "$FIX/calls" "$LIST" -check 'opens the issue, assigned' contains "$FIX/calls" 'issue create --title Auto release failed --assignee owner' +check 'opens the issue' contains "$FIX/calls" 'issue create --title Auto release failed' check 'the issue names the owner and links the failed run' contains "$FIX/create" \ - "@owner A CI run on main that this repository started (a re-run, or a run for pushes that start none) did not lead to a release: $SELF_URL" + "A CI run on main that this repository started (a re-run, or a run for pushes that start none) did not lead to a release: $SELF_URL" export EVENT=workflow_run scenario report 'auto-release failed on CI'"'"'s workflow_run event' check 'succeeds' test "$status" -eq 0 -check 'opens the issue, assigned' contains "$FIX/calls" 'issue create --title Auto release failed --assignee owner' +check 'opens the issue' contains "$FIX/calls" 'issue create --title Auto release failed' check 'the issue says CI passed and the release failed' contains "$FIX/create" \ - "@owner CI on main passed, and releasing it failed: $SELF_URL" + "CI on main passed, and releasing it failed: $SELF_URL" export EVENT='' OPEN_ISSUES='[{"number":7,"title":"Auto release failed"}]' scenario report 'auto-release failed, the issue already open' check 'succeeds' test "$status" -eq 0 check 'opens no second issue' lacks "$FIX/calls" 'issue create' check 'comments on the open one' contains "$FIX/calls" 'issue comment 7 --body-file' -check 'the comment links this run' contains "$FIX/comment" "@owner Auto release failed again: $SELF_URL" +check 'the comment links this run' contains "$FIX/comment" "Auto release failed again: $SELF_URL" finish diff --git a/tests/workflows/security-alerts.test.sh b/tests/workflows/security-alerts.test.sh index fb086c426..a8a822fca 100644 --- a/tests/workflows/security-alerts.test.sh +++ b/tests/workflows/security-alerts.test.sh @@ -252,13 +252,13 @@ check 'touches no issue' test ! -s "$LOG" scenario 's2 the 2026-09-30 case: two packages, no pull request -> one issue each' "$main_step" \ FX_ALERTS="$(arr "$b43" "$b44" "$b45" "$ip52")" check 'succeeds' test "$status" -eq 0 -check 'opens "Security alert: brace-expansion", assigned to the owner' contains "$LOG" \ - 'CREATE [--title] [Security alert: brace-expansion] [--assignee] [Nawid3333] [--body-file]' +check 'opens "Security alert: brace-expansion"' contains "$LOG" \ + 'CREATE [--title] [Security alert: brace-expansion] [--body-file]' check 'opens "Security alert: ip-address"' contains "$LOG" \ - 'CREATE [--title] [Security alert: ip-address] [--assignee] [Nawid3333] [--body-file]' + 'CREATE [--title] [Security alert: ip-address] [--body-file]' check 'opens two issues' count "$LOG" 'CREATE [' 2 -check 'mentions the owner' contains "$LOG" \ - '@Nawid3333 Dependabot has open security alerts for `brace-expansion` and no pull request that fixes them:' +check 'its text' contains "$LOG" \ + 'Dependabot has open security alerts for `brace-expansion` and no pull request that fixes them:' check 'a table row per alert, linked' contains "$LOG" \ "| [43]($base/43) | high | \`pnpm-lock.yaml\` | \`< 1.2.3\` | 1.2.3 | A flaw in brace-expansion |" check 'the last alert has its row too' contains "$LOG" "| [45]($base/45) |" @@ -373,8 +373,8 @@ scenario 's21 a new alert for an open issue -> the issue edited, one comment' "$ check 'succeeds' test "$status" -eq 0 check 'edits #5' contains "$LOG" 'EDIT [5] [--body-file]' check 'its list gains 45' contains "$LOG" '<!-- alerts: 43 44 45 -->' -check 'comments, mentioning the owner and linking the alert' contains "$LOG" \ - "COMMENT [5] [--body] [@Nawid3333 Another security alert for \`brace-expansion\`, with no pull request that fixes it: [alert 45]($base/45). The list above now includes it.]" +check 'comments, linking the alert' contains "$LOG" \ + "COMMENT [5] [--body] [Another security alert for \`brace-expansion\`, with no pull request that fixes it: [alert 45]($base/45). The list above now includes it.]" check 'opens nothing' lacks "$LOG" 'CREATE' check 'closes nothing' lacks "$LOG" 'CLOSE' @@ -475,18 +475,18 @@ check 'touches no issue' test ! -s "$LOG" scenario 's36 failure report, none open -> opens the failure issue' "$report_step" FX_OPEN='[{"title":"Other"}]' check 'succeeds' test "$status" -eq 0 -check 'opens "Security alerts workflow failed", assigned to the owner' contains "$LOG" \ - 'CREATE [--title] [Security alerts workflow failed] [--assignee] [Nawid3333]' -check 'mentions the owner and the run URL' contains "$LOG" \ - '@Nawid3333 security-alerts.yml failed: https://github.com/Nawid3333/FastStream/actions/runs/1' +check 'opens "Security alerts workflow failed"' contains "$LOG" \ + 'CREATE [--title] [Security alerts workflow failed]' +check 'names the run URL' contains "$LOG" \ + 'security-alerts.yml failed: https://github.com/Nawid3333/FastStream/actions/runs/1' scenario 's37 failure report, already open -> a comment on it' "$report_step" \ FX_OPEN='[{"number":3,"title":"Other"},{"number":12,"title":"Security alerts workflow failed"}]' check 'succeeds' test "$status" -eq 0 check 'opens nothing' lacks "$LOG" 'CREATE' check 'comments on the open one' contains "$LOG" 'COMMENT [12] [--body-file]' -check 'the comment mentions the owner and links this run' contains "$LOG" \ - '@Nawid3333 Failed again: https://github.com/Nawid3333/FastStream/actions/runs/1' +check 'the comment links this run' contains "$LOG" \ + 'Failed again: https://github.com/Nawid3333/FastStream/actions/runs/1' scenario 's38 failure report, the open issues unreadable -> opens one anyway' "$report_step" FX_OPEN='not json' check 'succeeds' test "$status" -eq 0 diff --git a/tests/workflows/security-overrides-demote.test.sh b/tests/workflows/security-overrides-demote.test.sh index 3e442770e..99a6cd018 100644 --- a/tests/workflows/security-overrides-demote.test.sh +++ b/tests/workflows/security-overrides-demote.test.sh @@ -72,7 +72,7 @@ scenario() { # <name> <old pull request number> [NAME=value]... scenario 'd1 pnpm cannot install the floor -> the alerts get their issue, with the marker' 0 check 'succeeds' test "$status" -eq 0 -check 'opens "Security alert: minimist", assigned' contains "$LOG" 'CREATE [--title] [Security alert: minimist] [--assignee] [owner]' +check 'opens "Security alert: minimist"' contains "$LOG" 'CREATE [--title] [Security alert: minimist]' check 'a row per alert, from the plan' contains "$LOG" '| [60](https://github.com/o/r/security/dependabot/60) | high |' check '(the second too)' contains "$LOG" '| [61](https://github.com/o/r/security/dependabot/61) | low |' check 'says why' contains "$LOG" 'Why: nothing published satisfies it' diff --git a/tests/workflows/toolchain-updates.test.sh b/tests/workflows/toolchain-updates.test.sh index 2210d2258..e8dbd86a5 100644 --- a/tests/workflows/toolchain-updates.test.sh +++ b/tests/workflows/toolchain-updates.test.sh @@ -617,7 +617,7 @@ weekly_pnpm() { check 'main untouched' test "$(origin rev-list --count main)" = 1 check 'pull request' has_call 'pr create --base main --head toolchain/pnpm-11.27.1 --title Toolchain update: pnpm 11.27.1 --label dependencies' check 'CI started once' test "$(n_calls '^workflow run ci.yml --ref toolchain/pnpm-11.27.1$')" = 1 - check 'routine body' grep -qF 'Routine: `update-prs.yml` tells @nawid once CI has run whether it is ready to merge' "$STATE/created.body" + check 'routine body' grep -qF 'Routine: `update-prs.yml` says here once CI has run whether it is ready to merge (and the daily digest lists it)' "$STATE/created.body" check 'names the change' grep -qF 'changes only `packageManager` in `package.json`' "$STATE/created.body" check 'lockfile hint' grep -qF 'The lockfile is left as it is' "$STATE/created.body" check 'every title, paginated' has_call 'api --paginate repos/me/fs/issues?state=all&per_page=100' @@ -633,7 +633,7 @@ weekly_node() { check 'only .nvmrc' test "$(branch_diff toolchain/node-26)" = .nvmrc check '.nvmrc is 26' test "$(origin show toolchain/node-26:.nvmrc)" = 26 check 'commit message' test "$(origin log -1 --format=%s toolchain/node-26)" = 'build: Node.js 22 -> 26' - check 'a decision' grep -qF 'A decision: `update-prs.yml` tells @nawid' "$STATE/created.body" + check 'a decision' grep -qF 'A decision: `update-prs.yml` says here when CI is green or red' "$STATE/created.body" check 'no lockfile hint' bash -c '! grep -qF "lockfile" "$0"' "$STATE/created.body" } diff --git a/tests/workflows/update-prs-decide.test.sh b/tests/workflows/update-prs-decide.test.sh index 9502cd86a..1ffa0e1e8 100644 --- a/tests/workflows/update-prs-decide.test.sh +++ b/tests/workflows/update-prs-decide.test.sh @@ -461,7 +461,7 @@ fork_pr_same_branch_and_sha() { fork_pr run_step check 'exit 0' test "$rc" -eq 0 - check 'reports on #42' has_call 'pr edit 42 --add-assignee nawid' + check 'reports on #42' has_call 'pr comment 42 --body-file' check 'ready to merge' ready check "the fork's #43 untouched" untouched_43 } @@ -473,7 +473,7 @@ fork_pr_red() { fork_pr run_step check 'exit 0' test "$rc" -eq 0 - check 'labels #42' has_call 'pr edit 42 --add-assignee nawid --add-label ci-failed' + check 'labels #42' has_call 'pr edit 42 --add-label ci-failed' check "the fork's #43 untouched" untouched_43 } @@ -558,7 +558,7 @@ red_second() { check 'exit 0' test "$rc" -eq 0 check 'a run run again by hand gets no third try' bash -c '! grep -qE "run rerun|workflow run" "$0"' "$STATE/gh.log" check 'one comment' test "$(comments_n)" -eq 1 - check '@mentions the owner' grep -qF '@nawid CI fails' <(last_comment) + check 'says CI fails' grep -qF 'CI fails on this pull request' <(last_comment) check 'says the rerun failed too' grep -qF 'failed twice' <(last_comment) check 'table row: job, step, meaning' grep -qF '| Lint, test and build | Unit tests | the unit tests (Vitest) |' <(last_comment) check 'a job that passed is not listed' bash -c '! grep -qF "| Lint workflows |" <<< "$0"' "$(last_comment)" @@ -568,7 +568,7 @@ red_second() { check "names main's CI" grep -qF 'main, the latest push: success: https://github.com/me/fs/actions/runs/77' <(last_comment) check 'carries a red key' grep -qE '<!-- update-prs: red [0-9a-f]{12} -->' <(last_comment) check 'labelled ci-failed' grep -qx 'ci-failed' "$STATE/labels" - check 'assigned to the owner' grep -qx 'nawid' "$STATE/assignees" + check 'assigns no one' test ! -s "$STATE/assignees" check 'not called ready' not_ready check 'Dependabot hint' grep -qF '@dependabot ignore' <(last_comment) } @@ -678,7 +678,7 @@ outage_fourth() { run_step check 'exit 0' test "$rc" -eq 0 check 'no retry, no second try' bash -c '! grep -q "^workflow run" "$0"' "$STATE/gh.log" - check '@mentions the owner: four times an outage' grep -qF '@nawid CI failed on this commit 4 times, each time because GitHub or a download host did not answer' <(last_comment) + check 'says so: four times an outage' grep -qF 'CI failed on this commit 4 times, each time because GitHub or a download host did not answer' <(last_comment) check 'says how to start CI' grep -qF 'start CI on this branch (Actions, CI, Run workflow)' <(last_comment) check 'its key' grep -qF "<!-- update-prs: outage stuck ${sha:0:12} -->" <(last_comment) check 'no label, assignee or issue' no_report_marks @@ -692,7 +692,7 @@ outage_retry_refused() { run_step check 'exit 0' test "$rc" -eq 0 check 'tried outage-retry.yml' retried - check '@mentions the owner: could not be started' grep -qF 'and outage-retry.yml could not be started' <(last_comment) + check 'says it could not be started' grep -qF 'and outage-retry.yml could not be started' <(last_comment) check 'no label, assignee or issue' no_report_marks } @@ -771,9 +771,9 @@ green_ready() { check 'ready to merge' ready # Only the review's own check run: without the name, CI's green run would pass for it. check 'asks for the review check by name' has_call 'check-runs?check_name=Review%20dependency%20changes' - check '@mentions the owner' grep -qF '@nawid CI passes on this pull request (https://github.com/me/fs/actions/runs/555), and every check holds' <(last_comment) + check 'says so' grep -qF 'CI passes on this pull request (https://github.com/me/fs/actions/runs/555), and every check holds' <(last_comment) check 'says merging releases nothing' grep -qF 'file for file the one in v1.3.82.40, the version number aside: merging it releases nothing' <(last_comment) - check 'assigned' grep -qx nawid "$STATE/assignees" + check 'assigns no one' test ! -s "$STATE/assignees" check 'no branch update' bash -c '! grep -q update-branch "$0"' "$STATE/gh.log" check 'branch kept' bash -c '! grep -q "api -X DELETE" "$0"' "$STATE/gh.log" check 'starts no CI on main' bash -c '! grep -qF "workflow run ci.yml --ref main" "$0"' "$STATE/gh.log" @@ -795,8 +795,8 @@ green_ships() { check 'exit 0' test "$rc" -eq 0 check 'not called ready' not_ready check 'says it changes what ships' grep -qF 'changes what the extension ships' <(last_comment) - check '@mentions and assigns' grep -qF '@nawid CI passes' <(last_comment) - check 'assigned' grep -qx nawid "$STATE/assignees" + check 'says CI passes' grep -qF 'CI passes' <(last_comment) + check 'assigns no one' test ! -s "$STATE/assignees" check 'a look key' grep -qF '<!-- update-prs: look -->' <(last_comment) check 'says to look first' grep -qF 'Look at this before merging' <(last_comment) } @@ -1198,8 +1198,8 @@ node_ready() { check 'ready to merge' ready check 'build compared' has_call 'release download' check 'starts no CI on main' bash -c '! grep -qF "workflow run ci.yml --ref main" "$0"' "$STATE/gh.log" - check '@mentions the owner' grep -qF '@nawid CI passes' <(last_comment) - check 'assigned' grep -qx nawid "$STATE/assignees" + check 'says CI passes' grep -qF 'CI passes' <(last_comment) + check 'assigns no one' test ! -s "$STATE/assignees" } node_other_file_waits() { @@ -1933,8 +1933,8 @@ migration_opened() { check 'and ci-failed' grep -qx ci-failed "$STATE/labels" check 'one issue' test "$(issues_n)" -eq 1 check 'its title' test "$(issue_of 700 title)" = 'Migration: eslint 10.12.0' - check 'assigned to the owner' test "$(issue_of 700 assignee)" = nawid - check '@mentions the owner, links the pull request' grep -qF '@nawid CI failed twice on #42 (build(deps-dev): bump eslint)' <(issue_of 700 body) + check 'assigns no one' test -z "$(issue_of 700 assignee)" + check 'links the pull request' grep -qF 'CI failed twice on #42 (build(deps-dev): bump eslint)' <(issue_of 700 body) check 'the failed step' grep -qF '| Lint, test and build | Unit tests | the unit tests (Vitest) |' <(issue_of 700 body) check 'the log' grep -qF 'FAIL tests/unit/x.test.mjs > it breaks' <(issue_of 700 body) check "main's CI" grep -qF 'main, the latest push: success' <(issue_of 700 body) diff --git a/tests/workflows/update-prs-hand-off.test.sh b/tests/workflows/update-prs-hand-off.test.sh index 8e2e3bfc8..06f78085c 100644 --- a/tests/workflows/update-prs-hand-off.test.sh +++ b/tests/workflows/update-prs-hand-off.test.sh @@ -78,10 +78,10 @@ export START_FAILS=3 scenario 'refused three times' check 'fails' test "$status" -ne 0 check 'tries three times' test "$(starts)" -eq 3 -check 'opens the issue, assigned' contains "$FIX/calls" 'issue create --title Update PRs hand-off failed --assignee owner' +check 'opens the issue' contains "$FIX/calls" 'issue create --title Update PRs hand-off failed' check 'the issue says how to start update-prs.yml by hand' contains "$FIX/create" \ "Start it from the Actions tab: Update PRs, Run workflow, run_id 777 and branch $BRANCH." -check 'and links the run' contains "$FIX/create" "@owner GitHub refused three times to start update-prs.yml for CI run 777 on $BRANCH ($SELF_URL)" +check 'and links the run' contains "$FIX/create" "GitHub refused three times to start update-prs.yml for CI run 777 on $BRANCH ($SELF_URL)" export OPEN_ISSUES='[{"number":4,"title":"Update PRs workflow failed"},{"number":9,"title":"Update PRs hand-off failed"}]' scenario 'refused three times, the issue already open' @@ -89,7 +89,7 @@ check 'fails' test "$status" -ne 0 check 'opens no second issue' lacks "$FIX/calls" 'issue create' check 'comments on the open one, not on update-prs.yml'"'"'s own' contains "$FIX/calls" 'issue comment 9 --body-file' check 'the comment says how to start it' contains "$FIX/comment" \ - "@owner GitHub refused three times again, for CI run 777 on $BRANCH ($SELF_URL): start update-prs.yml by hand with run_id 777 and branch $BRANCH." + "GitHub refused three times again, for CI run 777 on $BRANCH ($SELF_URL): start update-prs.yml by hand with run_id 777 and branch $BRANCH." export OPEN_ISSUES='[]' LIST_FAILS=1 scenario 'refused three times, the open issues unreadable' diff --git a/tests/workflows/update-watch.test.sh b/tests/workflows/update-watch.test.sh index ae9f39323..2fa6acc0d 100644 --- a/tests/workflows/update-watch.test.sh +++ b/tests/workflows/update-watch.test.sh @@ -239,10 +239,10 @@ check 'ignore list and labels are no directories' lacks "$FIX/out" 'dependency-n scenario 'p3 npm / at its limit minus 2 -> one issue' "$pile_step" \ FX_PRS="[$(prs 1 8 dependabot/npm_and_yarn/pkg)]" check 'succeeds' test "$status" -eq 0 -check 'opens the issue, assigned to the owner' contains "$LOG" \ - 'CREATE [--title] [Update pull requests piling up] [--assignee] [Nawid3333] [--body-file]' +check 'opens the issue' contains "$LOG" \ + 'CREATE [--title] [Update pull requests piling up] [--body-file]' check 'once' count "$LOG" 'CREATE [' 1 -check 'mentions the owner' contains "$LOG" '@Nawid3333 Update pull requests are piling up' +check 'its text' contains "$LOG" 'Update pull requests are piling up' check 'the row' contains "$LOG" '| npm | `/` | 8 | 10 | #1, #2, #3, #4, #5, #6, #7, #8 |' check 'says what Dependabot does at the limit' contains "$LOG" 'Dependabot opens no more for it, not even for a new release' check 'no red table' lacks "$LOG" 'Red for more than 14 days' @@ -346,7 +346,7 @@ check 'touches no issue' test ! -s "$LOG" scenario 'f1 failure -> opens "Update PR watch failed"' "$report_step" TITLE='Update PR watch failed' check 'succeeds' test "$status" -eq 0 -check 'opens it, assigned' contains "$LOG" 'CREATE [--title] [Update PR watch failed] [--assignee] [Nawid3333] [--body-file]' +check 'opens it' contains "$LOG" 'CREATE [--title] [Update PR watch failed] [--body-file]' check 'with the run' contains "$LOG" 'https://github.com/Nawid3333/FastStream/actions/runs/1' scenario 'f2 failure while it is open -> a comment' "$report_step" TITLE='Update PR watch failed' \ diff --git a/tests/workflows/upstream-watch.test.sh b/tests/workflows/upstream-watch.test.sh index 4d55d71a8..eb3c53cb7 100644 --- a/tests/workflows/upstream-watch.test.sh +++ b/tests/workflows/upstream-watch.test.sh @@ -108,11 +108,11 @@ git -C "$UPSTREAM" checkout -q -b side up_commit chrome/player/b.mjs b 'Add b' git -C "$UPSTREAM" checkout -q main git -C "$UPSTREAM" -c user.name=Andrew -c user.email=a@example.com merge -q --no-ff -m 'Merge pull request #1 from x/side' side -watch 'w1 first run, three new commits and a merge -> one issue from what main merged, assigned' +watch 'w1 first run, three new commits and a merge -> one issue from what main merged' check 'succeeds' test "$status" -eq 0 -check 'opens one issue, assigned' grep -qF 'ISSUE_CREATE [--title] [Upstream: 3 commit(s) to review] [--body-file]' "$LOG" -check 'assigned to the owner' grep -qF '[--assignee] [Nawid3333]' "$LOG" -check '@mentions the owner' contains "$LOG" '@Nawid3333 upstream' +check 'opens one issue' grep -qF 'ISSUE_CREATE [--title] [Upstream: 3 commit(s) to review] [--body-file]' "$LOG" +check 'assigns no one' lacks "$LOG" 'assignee' +check 'mentions no one' lacks "$LOG" '@Nawid3333' check 'the fix touches a file this fork has, in bold, flagged' contains "$LOG" 'Fix a crash when seeking (Andrews54757/FastStream#567) (' check '... flagged fix?' grep -qE 'seeking .* - \*\*fix\?\*\*$' "$LOG" check '... its file in bold' contains "$LOG" '**`chrome/player/a.mjs`**' @@ -166,8 +166,8 @@ up_commit chrome/player/a.mjs a3 'Came after' watch 'w6 upstream moved past the open issue -> it is updated and the owner hears of it' check 'succeeds' test "$status" -eq 0 check 'edits #8, all of them' grep -qF 'ISSUE_EDIT [8] [--title] [Upstream: 2 commit(s) to review]' "$LOG" -check 'keeps the owner assigned' grep -qF '[--add-assignee] [Nawid3333]' "$LOG" -check 'comments, with the mention, how many came' grep -qF 'ISSUE_COMMENT [8] [--body] [@Nawid3333 upstream moved: 1 more commit(s), 2 to review now.]' "$LOG" +check 'assigns no one' lacks "$LOG" 'assignee' +check 'comments how many came' grep -qF 'ISSUE_COMMENT [8] [--body] [Upstream moved: 1 more commit(s), 2 to review now.]' "$LOG" check 'opens none' bash -c '! grep -q "^ISSUE_CREATE" "$0"' "$LOG" fixtures @@ -238,10 +238,10 @@ check 'no issue opened' bash -c '! grep -q "^ISSUE_CREATE" "$0"' "$LOG" fixtures TITLE='Upstream watch failed' export TITLE -echo 'n1 a failure with none open -> one issue, assigned' +echo 'n1 a failure with none open -> one issue' run "$notify_step" check 'succeeds' test "$status" -eq 0 -check 'opens it, assigned' grep -qF "ISSUE_CREATE [--title] [Upstream watch failed] [--body] [The upstream watch failed. Run: $RUN_URL] [--assignee] [Nawid3333]" "$LOG" +check 'opens it' grep -qF "ISSUE_CREATE [--title] [Upstream watch failed] [--body] [The upstream watch failed. Run: $RUN_URL]" "$LOG" fixtures echo '[{"number":12,"title":"Upstream watch failed"},{"number":13,"title":"Something else"}]' > "$FIX/open.json" diff --git a/tests/workflows/vendored-updates.test.sh b/tests/workflows/vendored-updates.test.sh index a9c07ebc1..55d959ae9 100644 --- a/tests/workflows/vendored-updates.test.sh +++ b/tests/workflows/vendored-updates.test.sh @@ -107,7 +107,7 @@ serve "$(vtt_url v5.1.0)" $'a\nb\nc\n' serve "$(vtt_url v5.3.0)" $'a\nB\nc\nd\n' run 'v1 dash.js changed vtt.js -> an issue with the change, older ones closed' "$vtt_step" check 'succeeds' test "$status" -eq 0 -check 'opens "vtt.js changed in dash.js v5.3.0"' contains "$LOG" 'ISSUECREATE [--title] [vtt.js changed in dash.js v5.3.0] [--assignee] [Nawid3333]' +check 'opens "vtt.js changed in dash.js v5.3.0"' contains "$LOG" 'ISSUECREATE [--title] [vtt.js changed in dash.js v5.3.0]' check 'counts the changed lines and links the compare' contains "$LOG" 'changes 3 lines of it: https://github.com/Dash-Industry-Forum/dash.js/compare/v5.1.0...v5.3.0' check 'closes the older #60, pointing at the new one' contains "$LOG" "ISSUECLOSE [60] [--reason] [not planned] [--comment] [dash.js v5.3.0 is out: https://github.com/Nawid3333/FastStream/issues/99 replaces this one.]" check 'closes #61, whose release the pin has passed' contains "$LOG" 'ISSUECLOSE [61] [--comment] [tools/verify-vtt.mjs pins dash.js v5.1.0 now.]' From a132a0b682ce44ad6e00909b976e79ebe952191e Mon Sep 17 00:00:00 2001 From: Nawid3333 <102778966+Nawid3333@users.noreply.github.com> Date: Mon, 9 Nov 2026 20:37:16 +0100 Subject: [PATCH 2/2] ci: re-run checks after the GitHub outage Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>