From 5a1783076b66e1b1398b7370368dac266f825ea5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=BCller?= <323649642+oc-tmueller@users.noreply.github.com> Date: Wed, 16 Sep 2026 17:36:29 +0200 Subject: [PATCH 1/2] ci: re-run the PR-title lint on every push MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A check-run belongs to the head sha it ran against and is never carried forward, while required status checks are evaluated per sha. With `synchronize` missing from this workflow's trigger list, every push therefore leaves the new head with no `lint` check-run at all -- which GitHub reads as perpetually pending. That is harmless while `lint` is advisory, which it still is today: `build` is the only required check on `main`. It becomes a permanent merge block the moment `lint` joins the docs.owncloud.com-status-checks ruleset in owncloud/admin, so this is the prerequisite for that follow-up rather than a fix for a live outage. Three PRs here have already been in the pending-forever state without paying for it: * #116 -- lint ran when Dependabot opened it, Dependabot then rebased to 78e0a21b, and the head that merged carried no lint. Re-running the workflow would not have recovered that PR: a re-run replays the original event and reports back to the original sha. Only an event against the new head does. * #110 -- force-pushed to a0bcc073; no lint run on that sha. * #63 -- pushed 5bfb225e on 2026-09-14; no lint run on that sha. The comment claiming `synchronize` was redundant reasoned only about the title, which a push indeed cannot change. That holds while the check is advisory; it does not once the check is required, because the re-run exists to re-post the check-run on the new sha, not to re-judge the title. Two neighbouring comments are corrected in the same breath, since both would have misled the next person deciding whether a trigger is droppable: * `reopened` was justified as stopping the check from going "absent" on reopen. It does not go absent -- reopening does not move the head sha, so the existing check-run still applies. What actually makes `reopened` load-bearing is that a push while the PR is CLOSED emits no pull_request event at all, leaving it the only event that reports against the head the PR comes back with. It is therefore not redundant with `synchronize`. * the concurrency block was credited with preventing a stale red check. Cancellation is asynchronous, so the superseded run still lands, as `cancelled`; what keeps the current verdict authoritative is GitHub resolving duplicate check-run names to the newest. Head 3a91054c carries exactly that cancelled/success pair today. This also aligns the repo with the rest of the org -- activity, client, richdocuments, testing and wopi all trigger on `synchronize`. Co-Authored-By: Claude Opus 5 (1M context) Signed-off-by: Thomas Müller <323649642+oc-tmueller@users.noreply.github.com> --- .github/workflows/lint-pr-title.yml | 37 ++++++++++++++++++++++------- 1 file changed, 29 insertions(+), 8 deletions(-) diff --git a/.github/workflows/lint-pr-title.yml b/.github/workflows/lint-pr-title.yml index 3935f1c..c50c2c8 100644 --- a/.github/workflows/lint-pr-title.yml +++ b/.github/workflows/lint-pr-title.yml @@ -2,18 +2,39 @@ name: Lint PR title on: pull_request: - # `reopened` is required: without it, closing and reopening a PR leaves the - # check absent rather than carrying it over. `synchronize` is deliberately - # omitted -- a push cannot change the title, so it can only re-run a lint - # whose outcome is already known. - types: [opened, edited, reopened] + # A check-run belongs to the head sha it ran against and is never carried + # forward, while required status checks are evaluated per sha. So the two + # non-obvious types here are not about re-judging a title -- a push cannot + # change one -- but about guaranteeing that some event fires against whatever + # head the PR ends up with. Neither is redundant with the other: + # + # * `synchronize` (a push). Without it the pushed head carries NO `lint` + # check-run, which GitHub reads as perpetually pending. #116 is the case + # that matters: lint ran when Dependabot opened it, Dependabot then + # rebased, and the head that merged had no lint at all. Re-running the + # workflow would not have recovered it -- a re-run replays the original + # event and reports back to the original sha. + # * `reopened`. A push while the PR is closed emits no `pull_request` event + # at all, so this is the only event that reports against the head the PR + # comes back with. + # + # A missing check is harmless while this context is advisory -- as of this + # commit `build` is the only required check on `main` -- and a permanent merge + # block once `lint` joins the docs.owncloud.com-status-checks ruleset in + # owncloud/admin, which is the follow-up this change unblocks. + types: [opened, edited, reopened, synchronize] permissions: pull-requests: read -# Scoped per ref, as in ci.yml. Two quick title edits would otherwise race, and -# a superseded failing run finishing last would leave a red check on a title -# that is already valid. +# Scoped per ref, as in ci.yml -- for a pull_request event that is +# refs/pull//merge, so the group is per PR. Two quick title edits, or an edit +# racing a push, would otherwise overlap. Note what this does and does not buy: +# cancellation is asynchronous, so the superseded run still lands, as +# `cancelled`, and only GitHub resolving duplicate check-run names to the newest +# keeps the surviving verdict the current one (head 3a91054c carries exactly that +# cancelled/success pair). If a stale `cancelled` ever did win, re-running it is +# enough -- its original sha is still head, unlike the #116 case above. concurrency: group: lint-pr-title-${{ github.ref }} cancel-in-progress: true From c10aef4576fcd06cf39f18b23b4699d5002cba13 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=BCller?= <323649642+oc-tmueller@users.noreply.github.com> Date: Wed, 16 Sep 2026 17:55:27 +0200 Subject: [PATCH 2/2] ci: empty commit to prove the synchronize trigger fires MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Verification probe for the change in this PR, not content. A `lint` check-run must now appear on this new head sha; before the change, a push produced none (see #116, #110, #63 in the PR description). Co-Authored-By: Claude Opus 5 (1M context) Signed-off-by: Thomas Müller <323649642+oc-tmueller@users.noreply.github.com>