jdaugherty commented on PR #15988: URL: https://github.com/apache/grails-core/pull/15988#issuecomment-5131988382
I had AI look at this, and it's because the change wasn't made on the affected branch: # Why PR #15988's docs-publish mutex did not prevent concurrent publishes **Analysed:** 2026-07-30 **PR under review:** [apache/grails-core#15988](https://github.com/apache/grails-core/pull/15988) — "Prevent multiple publishing from running at the same time" **Verdict:** The claim is correct. The mutex itself is sound and *is* working on the branches that contain it. It failed because it was absent from the file the racing jobs actually used, and because it structurally cannot cover every writer of the shared resource. --- ## 1. What the PR did Merged into `7.0.x` on 2026-07-18T15:42:03Z from branch `ci/document-publishing-limit`. | | | |---|---| | Fix commit | `876db2e1a59b206a3f00fceb0247556e6233c08d` (2026-07-16 08:30:27 -0400) | | Merge commit into `7.0.x` | `7eb9a5e98d57966377d7b2a8aed4853ad67772fc` (2026-07-18 11:42:02 -0400) | | Files changed | `.github/workflows/gradle.yml` (+6), `.github/workflows/release.yml` (+6) | It adds a job-level mutex to the `docs` job in both workflows: ```yaml concurrency: group: grails-docs-publish cancel-in-progress: false ``` - `.github/workflows/gradle.yml:894` — snapshot docs (`docs` job, push events) - `.github/workflows/release.yml:652` — release docs (`VOTE SUCCEEDED - Publish Documentation`) It was chosen **instead of** [apache/grails-github-actions#98](https://github.com/apache/grails-github-actions/pull/98), which hardens the push-retry loop inside `deploy-github-pages/entrypoint.sh`. That PR is still **OPEN**. ### The shared resource Every docs publish pushes to the same place: | Setting | Value | |---|---| | Action | `apache/grails-github-actions/deploy-github-pages@asf` | | `TARGET_REPOSITORY` | `apache/grails-website` | | `DOCUMENTATION_BRANCH` | `asf-site-production` | | `TARGET_FOLDER` | `docs` | --- ## 2. The race that still happened — 2026-07-23 Two `docs` jobs, **both inside `apache/grails-core`**, ran at the same time: | Branch | Run | Docs job window (UTC) | `Publish to Github Pages` step | Result | |---|---|---|---|---| | `8.1.x` | [30005838660](https://github.com/apache/grails-core/actions/runs/30005838660) | 14:14:51 → 14:32:42 | 14:24:02 → 14:31:16 | success | | `9.0.x` | [30005853541](https://github.com/apache/grails-core/actions/runs/30005853541) | 14:19:03 → 14:35:04 | rejected at 14:34:46 | **failure** | `9.0.x` started while `8.1.x` was still running, `8.1.x` landed its push first, and `9.0.x` died exactly as PR #98 describes. Log excerpt (job `89233189977`, step 13 `🚀 Publish to Github Pages`): ``` 14:34:46 ! [rejected] asf-site-production -> asf-site-production (fetch first) 14:34:46 hint: Updates were rejected because the remote contains work that you do not 14:34:46 hint: have locally. This is usually caused by another repository pushing to 14:34:46 hint: the same ref. ... 14:34:46 Push rejected, pulling remote changes and retrying... 14:35:01 CONFLICT (content): Merge conflict in docs/snapshot/guide/GORM.html 14:35:01 CONFLICT (content): Merge conflict in docs/snapshot/guide/REST.html 14:35:01 CONFLICT (content): Merge conflict in docs/snapshot/guide/async.html ... (many more under docs/snapshot/guide/) 14:35:01 hint: "git add/rm <conflicted_files>", then run "git rebase --continue". ``` Both branches publish snapshot docs into the shared `docs/snapshot` folder, so the naive `git pull --rebase` conflicted there and — under `set -e` — killed the job. --- ## 3. Root cause: the mutex was not on those refs **GitHub Actions reads job-level `concurrency` from the workflow file on the ref being built.** It cannot be applied centrally or retroactively. A branch whose `gradle.yml` predates the fix runs with no mutex at all. Both `8.1.x` and `9.0.x` point at the same commit and **neither contains the group**: ``` $ git ls-remote --heads upstream | grep -E "8\.1\.x|9\.0\.x" c6ce3fe8bdc4ced884d6f6a669b98ea85722d7eb refs/heads/8.1.x c6ce3fe8bdc4ced884d6f6a669b98ea85722d7eb refs/heads/9.0.x # occurrences of "grails-docs-publish" in .github/workflows/gradle.yml 8.1.x: 0 9.0.x: 0 ``` `c6ce3fe8` is the merge of [#16004](https://github.com/apache/grails-core/pull/16004) (`test/plugin-registry-contract`, 2026-07-21 18:02:16 -0400). It **is** contained in `8.0.x`, but it sits *before* the merge-up that carried the fix in — the fix commit is not an ancestor of it: ``` $ git merge-base --is-ancestor 876db2e1 c6ce3fe8 → no ``` ### Merge-up timing gap | Date | Event | |---|---| | 2026-07-18 | fix merges to `7.0.x` (`7eb9a5e98d`) | | 2026-07-21 | `c6ce3fe8` lands on `8.0.x`; **`8.1.x` and `9.0.x` are cut from here** | | 2026-07-22 | merge-up `ac08770cff` (Merge PR #16043, 16:48:17 -0500) finally carries the fix into `8.0.x` | | 2026-07-23 | `8.1.x` and `9.0.x` docs publishes race and one fails | The two new branches were cut inside that four-day window, so they never inherited the mutex. Both are **still parked at `c6ce3fe8`**, so the hole stays open if anything is pushed to them. ### The mutex does work where it exists `7.0.x`, `7.1.x`, `7.2.x`, `8.0.x` all carry it in both workflows, and it demonstrably serialises. On 2026-07-29: | Branch | Docs job window | |---|---| | `7.0.x` (run 30455935021) | 15:10:20 → 15:17:29 | | `8.0.x` (run 30456328895) | 15:18:54 → 15:33:27 — waited 85s for the first to finish | --- ## 4. Hole still open #1 — `release-publish-docs.yml` has no mutex `.github/workflows/release-publish-docs.yml:32` defines a `docs` job that pushes to the **same** repo/branch/folder with the **same** action, and has **no `concurrency` block on any maintained branch**. Occurrences of `grails-docs-publish` per branch: | Branch | `gradle.yml` | `release.yml` | `release-publish-docs.yml` | |---|---|---|---| | `7.0.x` | 1 | 1 | **0** | | `7.1.x` | 1 | 1 | **0** | | `7.2.x` | 1 | 1 | **0** | | `8.0.x` | 1 | 1 | **0** | | `8.1.x` | **0** | — | **0** | | `9.0.x` | **0** | — | **0** | It is `workflow_dispatch`-only, so a manual docs re-publish can race a snapshot or release publish at any time. --- @jamesfredley -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: [email protected] For queries about this service, please contact Infrastructure at: [email protected]
