Skip to content

[build] Release workflow improvements#16947

Merged
titusfortner merged 8 commits intotrunkfrom
pr/release-workflow-improvements
Jan 19, 2026
Merged

[build] Release workflow improvements#16947
titusfortner merged 8 commits intotrunkfrom
pr/release-workflow-improvements

Conversation

@titusfortner
Copy link
Member

@titusfortner titusfortner commented Jan 19, 2026

User description

🔗 Related Issues

#16946 should be merged first

💥 What does this PR do?

  • Make failure Slack message stand out better
  • During release delete nightly release as well as tag
  • Release workflow to use the get-approval workflow
  • Docs must wait for GitHub release to create the tag
  • Can update version on trunk before docs have been updated since it is a different branch
  • If release build breaks, do not try to unlock trunk (which waits for approval) — ensure slack notification sent
  • Run nightly and mirror workflows after release

🔧 Implementation Notes

Uses the get-approval.yml workflow created for pre-release PR.

💡 Additional Considerations

I'd like to make this so we aren't building and releasing in separate jobs, but this works

🔄 Types of changes

  • Bug fix (backwards compatible)
  • New feature (non-breaking change which adds functionality)

PR Type

Enhancement, Bug fix


Description

  • Replace manual approval job with reusable get-approval workflow

  • Delete nightly GitHub release alongside tag during release process

  • Add authentication token to checkout step for secure operations

  • Improve Slack failure notification formatting with bullet points

  • Run nightly and mirror workflows after successful release completion

  • Decouple docs update from publish job, allow version update before docs

  • Remove unnecessary approval wait on release build failure


Diagram Walkthrough

flowchart LR
  stage["Stage Release"]
  approval["Get Approval<br/>Reusable Workflow"]
  publish["Publish Packages"]
  docs["Update Docs"]
  release["GitHub Release"]
  unrestrict["Unrestrict Trunk"]
  version["Update Version"]
  nightly["Publish Nightly"]
  mirror["Mirror Releases"]
  failure["Failure Notification"]
  
  stage -- "success" --> approval
  approval -- "approved" --> publish
  stage -- "success" --> release
  publish --> docs
  release --> unrestrict
  unrestrict --> version
  version --> nightly
  nightly --> mirror
  
  stage -.-> failure
  publish -.-> failure
  docs -.-> failure
  release -.-> failure
  version -.-> failure
  nightly -.-> failure
  mirror -.-> failure
Loading

File Walkthrough

Relevant files
Enhancement
mirror-selenium-releases.yml
Enable mirror workflow as reusable workflow                           

.github/workflows/mirror-selenium-releases.yml

  • Add workflow_call trigger to enable reuse as called workflow
  • Allows mirror workflow to be invoked from release workflow
+1/-0     
release.yml
Refactor release workflow with approval reuse and post-release
automation

.github/workflows/release.yml

  • Add SELENIUM_CI_TOKEN to checkout step for authenticated operations
  • Replace manual approval job with get-approval reusable workflow
  • Change nightly deletion to use gh release delete command alongside tag
    deletion
  • Decouple docs job from publish dependency, only require stage and
    publish
  • Remove unnecessary always() condition from unrestrict-trunk job
  • Remove docs dependency from update-version job for earlier version
    updates
  • Add nightly and mirror workflow calls after version update
  • Improve Slack failure message formatting with bullet points and add
    nightly/mirror status
  • Update on-release-failure job dependencies to include new nightly and
    mirror jobs
+39/-22 

@selenium-ci selenium-ci added the B-build Includes scripting, bazel and CI integrations label Jan 19, 2026
@qodo-code-review
Copy link
Contributor

qodo-code-review bot commented Jan 19, 2026

PR Compliance Guide 🔍

Below is a summary of compliance checks for this PR:

Security Compliance
🟢
No security concerns identified No security vulnerabilities detected by AI analysis. Human verification advised for critical code.
Ticket Compliance
🎫 No ticket provided
  • Create ticket/issue
Codebase Duplication Compliance
Codebase context is not defined

Follow the guide to enable codebase context checks.

Custom Compliance
🟢
Generic: Meaningful Naming and Self-Documenting Code

Objective: Ensure all identifiers clearly express their purpose and intent, making code
self-documenting

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Secure Error Handling

Objective: To prevent the leakage of sensitive system information through error messages while
providing sufficient detail for internal debugging.

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Secure Logging Practices

Objective: To ensure logs are useful for debugging and auditing without exposing sensitive
information like PII, PHI, or cardholder data.

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Security-First Input Validation and Data Handling

Objective: Ensure all data inputs are validated, sanitized, and handled securely to prevent
vulnerabilities

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

🔴
Generic: Robust Error Handling and Edge Case Management

Objective: Ensure comprehensive error handling that provides meaningful context and graceful
degradation

Status:
Errors are swallowed: The nightly release/tag deletion commands unconditionally ignore failures via || true,
which can mask real problems and provide no actionable context when deletion fails.

Referred Code
- name: Delete nightly release and tag
  env:
    GH_TOKEN: ${{ secrets.SELENIUM_CI_TOKEN }}
  run: |
    gh release delete nightly --yes || true
    git push origin --delete refs/tags/nightly || true

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Comprehensive Audit Trails

Objective: To create a detailed and reliable record of critical system actions for security analysis
and compliance.

Status:
Incomplete action outcomes: The workflow performs critical delete operations (GitHub release and tag deletion) while
suppressing errors, which may prevent reliably reconstructing whether the actions
succeeded or failed from logs alone.

Referred Code
- name: Delete nightly release and tag
  env:
    GH_TOKEN: ${{ secrets.SELENIUM_CI_TOKEN }}
  run: |
    gh release delete nightly --yes || true
    git push origin --delete refs/tags/nightly || true

Learn more about managing compliance generic rules or creating your own custom rules

  • Update
Compliance status legend 🟢 - Fully Compliant
🟡 - Partial Compliant
🔴 - Not Compliant
⚪ - Requires Further Human Verification
🏷️ - Compliance label

@qodo-code-review
Copy link
Contributor

qodo-code-review bot commented Jan 19, 2026

PR Code Suggestions ✨

Explore these optional code suggestions:

CategorySuggestion                                                                                                                                    Impact
Possible issue
Ensure failure job runs correctly

Update the on-release-failure job to include get-approval and unrestrict-trunk
in its needs list and the Slack notification message to ensure complete failure
reporting.

.github/workflows/release.yml [194-217]

 on-release-failure:
   name: On Release Failure
   runs-on: ubuntu-latest
-  needs: [stage, publish, docs, github-release, update-version, nightly, mirror]
+  needs: [stage, get-approval, publish, docs, github-release, unrestrict-trunk, update-version, nightly, mirror]
   if: failure()
   steps:
     - uses: actions/checkout@v4
     - name: Slack Notification
       uses: rtCamp/action-slack-notify@v2
       env:
         SLACK_ICON_EMOJI: ":rotating_light:"
         SLACK_COLOR: failure
         SLACK_CHANNEL: selenium-tlc
         SLACK_USERNAME: GitHub Workflows
         SLACK_TITLE: Release failed
         SLACK_MESSAGE: |
+          • Approval Given: ${{ needs.get-approval.result }}
           • Selenium Published: ${{ needs.publish.result }}
           • Docs Updated: ${{ needs.docs.result }}
           • GitHub Release Published: ${{ needs.github-release.result }}
+          • Trunk Unlocked: ${{ needs.unrestrict-trunk.result }}
           • Nightly Version Updated: ${{ needs.update-version.result }}
           • Nightly Packages: ${{ needs.nightly.result }}
           • Mirror Updated: ${{ needs.mirror.result }}
         MSG_MINIMAL: actions url
         SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_URL }}
  • Apply / Chat
Suggestion importance[1-10]: 9

__

Why: The suggestion correctly identifies that the on-release-failure job is missing dependencies on get-approval and unrestrict-trunk, which would prevent failure notifications if those specific jobs fail, making this a critical fix for workflow reliability.

High
High-level
Consider simplifying the release workflow

The release workflow is a long sequential chain, increasing failure risk. It is
suggested to run post-release tasks like nightly and mirror in a separate,
triggered workflow to improve resilience.

Examples:

.github/workflows/release.yml [182-192]
  nightly:
    name: Publish Nightly Packages
    needs: [update-version]
    uses: ./.github/workflows/nightly.yml
    secrets: inherit

  mirror:
    name: Update Release Mirror
    needs: [nightly]
    uses: ./.github/workflows/mirror-selenium-releases.yml

 ... (clipped 1 lines)

Solution Walkthrough:

Before:

# .github/workflows/release.yml
jobs:
  # ... other release jobs ...
  
  update-version:
    needs: [stage, unrestrict-trunk]
    # ... steps to reset version to nightly ...

  nightly:
    name: Publish Nightly Packages
    needs: [update-version]
    uses: ./.github/workflows/nightly.yml

  mirror:
    name: Update Release Mirror
    needs: [nightly]
    uses: ./.github/workflows/mirror-selenium-releases.yml

  on-release-failure:
    needs: [..., update-version, nightly, mirror]
    if: failure()
    # ...

After:

# .github/workflows/release.yml
jobs:
  # ... other release jobs ...
  
  update-version:
    needs: [stage, unrestrict-trunk]
    # ... steps to reset version to nightly ...
  
  trigger-post-release-jobs:
    needs: [update-version]
    steps:
      - name: Trigger nightly and mirror workflow
        # Use repository_dispatch to trigger a separate workflow

# .github/workflows/post-release.yml
on:
  repository_dispatch:
    types: [release-completed]

jobs:
  nightly:
    # ...
  mirror:
    needs: [nightly]
    # ...
Suggestion importance[1-10]: 7

__

Why: The suggestion correctly identifies that adding nightly and mirror jobs sequentially makes the release workflow more monolithic and less resilient, which is a valid architectural concern for a critical pipeline.

Medium
Learned
best practice
Declare required workflow secrets

Declare required secrets (and/or inputs) under on.workflow_call so callers can
reliably pass them and failures are explicit when they are missing.

.github/workflows/mirror-selenium-releases.yml [3-7]

 on:
   schedule:
     - cron:  '0 */12 * * *'
   workflow_dispatch:
   workflow_call:
+    secrets:
+      SELENIUM_CI_TOKEN:
+        required: true

[To ensure code accuracy, apply this suggestion manually]

Suggestion importance[1-10]: 6

__

Why:
Relevant best practice - Add explicit validation/availability guards at integration boundaries (e.g., reusable workflows) by declaring required inputs/secrets instead of assuming they exist.

Low
  • Update

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR improves the release workflow by replacing manual approval with a reusable workflow, enhancing failure notifications, and automating post-release tasks. The changes streamline the release process while ensuring proper sequencing and better visibility into release status.

Changes:

  • Replaced manual approval job with reusable get-approval workflow for consistency
  • Enhanced nightly cleanup to delete both GitHub release and tag instead of just the tag
  • Added automatic nightly build and mirror update after release completion
  • Improved failure notification formatting with bullet points and added status for new jobs

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
.github/workflows/release.yml Refactored release workflow to use reusable approval workflow, enhanced nightly cleanup, restructured job dependencies to allow docs and version updates to run in parallel, and added automated nightly and mirror jobs post-release
.github/workflows/mirror-selenium-releases.yml Enabled workflow to be called as a reusable workflow by adding workflow_call trigger

@titusfortner titusfortner marked this pull request as draft January 19, 2026 17:24
Base automatically changed from pr/pre-release-improvements to trunk January 19, 2026 18:01
@titusfortner titusfortner force-pushed the pr/release-workflow-improvements branch from b05914a to 2e989bb Compare January 19, 2026 18:04
@titusfortner titusfortner requested a review from Copilot January 19, 2026 18:05
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

@titusfortner titusfortner marked this pull request as ready for review January 19, 2026 18:18
@qodo-code-review
Copy link
Contributor

PR Compliance Guide 🔍

Below is a summary of compliance checks for this PR:

Security Compliance
Overprivileged CI token

Description: The workflow introduces a high-privilege secret token (secrets.SELENIUM_CI_TOKEN) used for
actions/checkout and as GH_TOKEN (and also broadly propagates secrets via secrets: inherit
to called workflows), which could enable repository-wide destructive actions (e.g.,
deleting releases/tags) if the workflow can be triggered by an unauthorized actor or if
any step/action is compromised; prefer least-privilege GITHUB_TOKEN or a fine-grained
token scoped to only required permissions and restrict who can run
workflow_dispatch/environments.
release.yml [33-192]

Referred Code
- name: Checkout repo
  uses: actions/checkout@v4
  with:
    token: ${{ secrets.SELENIUM_CI_TOKEN }}
- name: Extract tag and version
  id: tag
  env:
    EVENT_NAME: ${{ github.event_name }}
    INPUT_TAG: ${{ inputs.tag }}
    PR_HEAD_REF: ${{ github.event.pull_request.head.ref }}
  run: |
    if [ "$EVENT_NAME" == "workflow_dispatch" ]; then
      TAG="$INPUT_TAG"
    else
      VERSION=$(echo "$PR_HEAD_REF" | grep -oE '[0-9]+\.[0-9]+\.[0-9]+')
      TAG="selenium-${VERSION}"
    fi
    echo "tag=$TAG" >> "$GITHUB_OUTPUT"
    echo "version=$(echo "$TAG" | grep -oE '[0-9]+\.[0-9]+\.[0-9]+')" >> "$GITHUB_OUTPUT"
- name: Prep git
  run: |


 ... (clipped 139 lines)
Ticket Compliance
🎫 No ticket provided
  • Create ticket/issue
Codebase Duplication Compliance
Codebase context is not defined

Follow the guide to enable codebase context checks.

Custom Compliance
🟢
Generic: Meaningful Naming and Self-Documenting Code

Objective: Ensure all identifiers clearly express their purpose and intent, making code
self-documenting

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Secure Error Handling

Objective: To prevent the leakage of sensitive system information through error messages while
providing sufficient detail for internal debugging.

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Secure Logging Practices

Objective: To ensure logs are useful for debugging and auditing without exposing sensitive
information like PII, PHI, or cardholder data.

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Security-First Input Validation and Data Handling

Objective: Ensure all data inputs are validated, sanitized, and handled securely to prevent
vulnerabilities

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

🔴
Generic: Robust Error Handling and Edge Case Management

Objective: Ensure comprehensive error handling that provides meaningful context and graceful
degradation

Status:
Errors are swallowed: The nightly release/tag deletion step unconditionally suppresses failures via || true,
which can mask real errors (e.g., permission/API failures) and prevent actionable failure
context.

Referred Code
- name: Delete nightly release and tag
  env:
    GH_TOKEN: ${{ secrets.SELENIUM_CI_TOKEN }}
  run: |
    gh release delete nightly --yes || true
    git push origin --delete refs/tags/nightly || true

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Comprehensive Audit Trails

Objective: To create a detailed and reliable record of critical system actions for security analysis
and compliance.

Status:
Limited audit context: The new automated deletion of the nightly GitHub release/tag relies on default GitHub
Actions logs and does not explicitly record structured audit context (actor, action,
outcome) beyond command output.

Referred Code
- name: Delete nightly release and tag
  env:
    GH_TOKEN: ${{ secrets.SELENIUM_CI_TOKEN }}
  run: |
    gh release delete nightly --yes || true
    git push origin --delete refs/tags/nightly || true

Learn more about managing compliance generic rules or creating your own custom rules

Compliance status legend 🟢 - Fully Compliant
🟡 - Partial Compliant
🔴 - Not Compliant
⚪ - Requires Further Human Verification
🏷️ - Compliance label

@qodo-code-review
Copy link
Contributor

PR Code Suggestions ✨

Explore these optional code suggestions:

CategorySuggestion                                                                                                                                    Impact
High-level
Serialize jobs that push to trunk

The docs and update-version jobs both push to the trunk branch and can run in
parallel, creating a race condition. To fix this, make the update-version job
dependent on the docs job to ensure they run sequentially.

Examples:

.github/workflows/release.yml [117-157]
    needs: [stage, publish, github-release]
    permissions:
      contents: write
    strategy:
      fail-fast: false
      matrix:
        language: [java, py, rb, dotnet, node]
    uses: ./.github/workflows/update-documentation.yml
    with:
      tag: ${{ needs.stage.outputs.tag }}

 ... (clipped 31 lines)

Solution Walkthrough:

Before:

jobs:
  # ...
  docs:
    needs: [stage, publish, github-release]
    steps:
      - uses: actions/checkout@v4
        with:
          ref: 'trunk'
      # ...
      - run: git push

  update-version:
    needs: [stage, unrestrict-trunk] # Does not wait for 'docs'
    steps:
      # ...
      - run: git push
  # ...

After:

jobs:
  # ...
  docs:
    needs: [stage, publish, github-release]
    steps:
      - uses: actions/checkout@v4
        with:
          ref: 'trunk'
      # ...
      - run: git push

  update-version:
    needs: [stage, unrestrict-trunk, docs] # Waits for 'docs' to complete
    steps:
      # ...
      - run: git push
  # ...
Suggestion importance[1-10]: 9

__

Why: The suggestion correctly identifies a critical race condition introduced by the PR where the docs and update-version jobs push to the trunk branch in parallel, which will cause the release workflow to fail.

High
General
Declare secrets for reusable workflow

In the mirror-selenium-releases.yml file, declare the secrets the reusable
workflow accepts under the workflow_call trigger to allow it to receive them
from the calling workflow.

.github/workflows/mirror-selenium-releases.yml [7]

 workflow_call:
+  secrets:
+    SELENIUM_CI_TOKEN:
+      required: true
  • Apply / Chat
Suggestion importance[1-10]: 9

__

Why: The PR makes the workflow reusable but omits declaring the secrets it needs, which would cause the workflow to fail when called. This suggestion fixes a critical flaw in the PR's implementation of the reusable workflow.

High
Possible issue
Ensure failure notifications are always sent

Modify the on-release-failure job's trigger condition from if: failure() to if:
always() and add a check for non-successful job results to ensure failure
notifications are sent even for skipped jobs.

.github/workflows/release.yml [194-217]

 on-release-failure:
   name: On Release Failure
   runs-on: ubuntu-latest
-  needs: [stage, publish, docs, github-release, update-version, nightly, mirror]
-  if: failure()
+  needs: [stage, get-approval, publish, docs, github-release, unrestrict-trunk, update-version, nightly, mirror]
+  if: always() && (needs.stage.result != 'success' || needs.get-approval.result != 'success' || needs.publish.result != 'success' || needs.docs.result != 'success' || needs.github-release.result != 'success' || needs.unrestrict-trunk.result != 'success' || needs.update-version.result != 'success' || needs.nightly.result != 'success' || needs.mirror.result != 'success')
   steps:
     - uses: actions/checkout@v4
     - name: Slack Notification
       uses: rtCamp/action-slack-notify@v2
       env:
         SLACK_ICON_EMOJI: ":rotating_light:"
         SLACK_COLOR: failure
         SLACK_CHANNEL: selenium-tlc
         SLACK_USERNAME: GitHub Workflows
         SLACK_TITLE: Release failed
         SLACK_MESSAGE: |
           • Selenium Published: ${{ needs.publish.result }}
           • Docs Updated: ${{ needs.docs.result }}
           • GitHub Release Published: ${{ needs.github-release.result }}
           • Nightly Version Updated: ${{ needs.update-version.result }}
           • Nightly Packages: ${{ needs.nightly.result }}
           • Mirror Updated: ${{ needs.mirror.result }}
         MSG_MINIMAL: actions url
         SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_URL }}
  • Apply / Chat
Suggestion importance[1-10]: 7

__

Why: The suggestion correctly identifies that if: failure() will not trigger for skipped jobs, leading to missed failure notifications. The proposed change to if: always() with a condition checking job results is a robust pattern for ensuring notifications are always sent on any unsuccessful run.

Medium
  • More

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

@titusfortner titusfortner merged commit 9df0888 into trunk Jan 19, 2026
26 checks passed
This was referenced Feb 20, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

B-build Includes scripting, bazel and CI integrations Review effort 3/5

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants