Skip to content

Safely retry concurrent documentation pushes - #110

Open
jamesfredley wants to merge 2 commits into
asffrom
fix/parallel-docs-publish-retry
Open

Safely retry concurrent documentation pushes#110
jamesfredley wants to merge 2 commits into
asffrom
fix/parallel-docs-publish-retry

Conversation

@jamesfredley

@jamesfredley jamesfredley commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Summary

Make concurrent documentation publishers converge through normal Git fast-forward semantics instead of requiring a global workflow queue.

When another publisher advances the destination branch first, this action now:

  • recognizes only a genuine non-fast-forward push rejection as retryable;
  • fetches the new tip into the existing shallow checkout;
  • requires the fetched tip to descend from the tip originally observed;
  • rebases the unpublished local deployment commit; and
  • retries a normal push, with at most five total attempts.

There is no force-push. A rejected push has not changed the remote branch, and the retry only rebases the unpublished local commit before attempting another normal fast-forward push. Rebase conflicts, non-descendant history, and unrelated push failures fail without changing the remote.

This supersedes #98 with a substantially narrower implementation. It removes path ownership, version arbitration, and automatic conflict resolution from the design.

Why the Queue Is Insufficient

The destination branch is shared by publishers in multiple repositories, including Grails Core and grails-static-website, with Grails Forge expected to publish there in the future. GitHub Actions concurrency groups are scoped to one repository, so a mutex in Grails Core cannot coordinate those other publishers. It also cannot coordinate older workflow revisions that do not contain the same group.

Queueing all Grails Core documentation jobs also turns otherwise independent release work into a serial process. In the August 6 release sequence:

The supplied grails-static-website run is not counted as queue-delay evidence because it was manually cancelled before publishing. It does demonstrate the cross-repository boundary: that publisher targets the same destination but cannot participate in a Grails Core concurrency group.

The action already pays the cost of cloning the large destination repository. Retrying in that existing checkout avoids another clone while allowing disjoint publishers to proceed in parallel. Git's normal non-fast-forward rejection remains the serialization point for the branch tip.

Failure Policy

  • Disjoint concurrent commits are rebased and preserved.
  • A true content conflict aborts the rebase and fails the action.
  • Authentication, transport, hook, and other non-contention failures fail immediately.
  • Repeated contention stops after five total push attempts.
  • Concurrent creation of a previously missing destination branch fails closed.

Verification

  • DeployGithubPagesSpec: 16/16 tests passed against Testcontainers and a real Gitea remote.
  • Tests cover first-push success, a real disjoint non-fast-forward race, conflict abort with winner preservation, immediate non-contention failure, and five consecutive remote advances.
  • bash -n deploy-github-pages/entrypoint.sh
  • git diff --check origin/asf...HEAD

Follow-up

Companion PR apache/grails-core#16110 removes the repository-local grails-docs-publish queue. It is explicitly blocked from merging until this PR reaches asf and is verified.

Retry only genuine non-fast-forward races by fetching and rebasing the
unpublished deployment commit in the existing shallow checkout. Fail safely
on conflicts, rewritten history, and unrelated push errors.

Assisted-by: opencode:gpt-5.6-sol
Clarify the five-attempt limit and the fail-closed conditions for
concurrent documentation publishing.

Assisted-by: opencode:gpt-5.6-sol
Copilot AI lite review requested due to automatic review settings August 6, 2026 18:05

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants