Mastering GitHub Releases: Avoiding Race Conditions for Enhanced Engineering Productivity

In the fast-paced world of continuous integration and delivery, reliable API interactions are paramount for maintaining high engineering productivity metrics. A recent discussion in the GitHub Community highlighted a critical race condition within the GitHub Releases REST API that can lead to lost release body updates, directly impacting the accuracy of your software project statistics and overall release quality.

Streamlined CI/CD pipeline with an aggregator job for release publishing, illustrating efficient software delivery.
Streamlined CI/CD pipeline with an aggregator job for release publishing, illustrating efficient software delivery.

The GitHub Releases API Race Condition Explained

The core issue arises when a PATCH request is sent to an existing draft release with the intention of simultaneously updating its body content and setting draft: false (publishing it). As user migueltempest discovered, the state transition to "published" succeeds, but the body text update is completely ignored or dropped by the backend.

This problem is particularly evident in CI/CD pipelines utilizing GitHub Actions, such as softprops/action-gh-release, especially when appending artifacts or URLs from a build matrix. The first job that triggers the draft: false transition often loses its body update, while subsequent jobs (updating an already-published release) process body updates without issue. This suggests an eventual consistency issue or a database lock during the draft-to-published transition, where the content update is outpaced by the state change.

Reproducing the Issue:

  1. Create a draft release:
    {"tag_name": "v1.0", "body": "Initial draft", "draft": true}
  2. Send a simultaneous update and publish request:
    {"body": "Updated text with new links", "draft": false}
  3. Observe: The release is published, but the body remains "Initial draft". The intended text update is lost.
Visualizing a race condition in an API update process, with two concurrent actions vying for a single resource.
Visualizing a race condition in an API update process, with two concurrent actions vying for a single resource.

Immediate Workaround: The Two-Step API Call

For those encountering this race condition, a straightforward workaround at the raw REST API level involves a two-step process:

  • First, update the release body while keeping draft: true.
  • Second, send a separate request to set draft: false.

This ensures the content update is committed before the state transition, preventing the race condition.

The Robust Solution: The Aggregator Pattern for CI/CD

While the two-step API call is effective, for complex CI/CD pipelines, especially those leveraging build matrices, a more robust and efficient pattern emerges: The Aggregator Pattern. This approach not only bypasses the specific backend bug but also centralizes release management, preventing multiple concurrent jobs from racing to update the same release and potentially hitting API secondary rate limits.

Here’s how to implement it to significantly improve your development analytics related to release consistency:

  1. Upload Artifacts in Matrix Jobs:

    Your individual matrix jobs should focus solely on building and uploading their respective assets using actions/upload-artifact. Crucially, these jobs should not interact with the GitHub Releases API.

  2. Create an Aggregator Job:

    Define a final, separate job (e.g., publish_release) that explicitly depends on all your matrix jobs using needs: [your_matrix_job_name]. This ensures the aggregator job only runs after all artifacts are ready.

  3. Download & Publish in Aggregator:

    Within this final job, use actions/download-artifact to gather all the outputs from the matrix jobs. Then, make a single, consolidated API call (or use a release action once) to:

    • Compile the final, complete body text for the release.
    • Upload all collected assets.
    • Finally, set draft: false to publish the release.

This pattern centralizes the release logic, guarantees all components are present before publishing, and eliminates the race condition entirely. It's a prime example of how thoughtful CI/CD design can directly enhance engineering productivity metrics and ensure the integrity of your software project statistics.

By understanding and implementing these strategies, developers can overcome API quirks, streamline their release processes, and maintain high confidence in their deployment pipelines. This ensures that every release accurately reflects the intended updates and contributes positively to your overall development analytics.

Track, Analyze and Optimize Your Software DeveEx!

Effortlessly implement gamification, pre-generated performance reviews and retrospective, work quality analytics, alerts on top of your code repository activity

 Install GitHub App to Start
devActivity Screenshot