Skip to content

Extract publish preconditions into a dedicated gate job #325

@MariusStorhaug

Description

The Publish-Module and Publish-Site jobs in workflow.yml use multi-clause if: expressions that are difficult to read, review, and modify. A single misplaced parenthesis or incorrect result check silently changes publishing behavior.

Request

Current experience

The Publish-Module job condition (line 267) reads:

if: >-
  fromJson(needs.Get-Settings.outputs.Settings).Run.PublishModule
  && needs.Get-Settings.result == 'success'
  && !cancelled()
  && (needs.Get-TestResults.result == 'success' || needs.Get-TestResults.result == 'skipped')
  && (needs.Get-CodeCoverage.result == 'success' || needs.Get-CodeCoverage.result == 'skipped')
  && (needs.Build-Site.result == 'success' || needs.Build-Site.result == 'skipped')

Publish-Site (line 313) has a similarly complex expression with four boolean clauses. These expressions encode critical business logic (when is it safe to publish?) in an inline YAML expression with no test coverage, no reuse, and no documentation beyond the expression itself.

Desired experience

A dedicated "gate" job evaluates all preconditions and exposes a single boolean output (shouldPublish: true/false). Downstream jobs check only that one output, making the workflow readable and the gate logic testable in isolation.

Acceptance criteria

  • Publishing preconditions are evaluated in a single, named gate job
  • Publish-Module and Publish-Site jobs reference only the gate job output
  • The gate job clearly documents what conditions must hold for publishing to proceed
  • No change in actual publishing behavior — same conditions, different structure

Note

This is a Clean Code concern: complex boolean expressions should be extracted into named, testable functions (or in this case, named jobs with descriptive outputs).


Technical decisions

Approach: Add a Publish-Gate job that depends on Get-Settings, Get-TestResults, Get-CodeCoverage, and Build-Site. It runs a single step that evaluates all preconditions and sets outputs like publishModule and publishSite. The Publish-Module and Publish-Site jobs then depend only on Publish-Gate and check its outputs.

Gate job structure:

Publish-Gate:
  if: always() && !cancelled()
  needs: [Get-Settings, Get-TestResults, Get-CodeCoverage, Build-Site]
  runs-on: ubuntu-latest
  outputs:
    publishModule: ${{ steps.gate.outputs.publishModule }}
    publishSite: ${{ steps.gate.outputs.publishSite }}
  steps:
    - id: gate
      run: |
        # Named, readable conditions
        ...

Alternative considered: Keep inline if: but extract to a reusable expression via anchors — rejected because YAML anchors are not supported in GitHub Actions workflow files.


Implementation plan

workflow.yml changes

  • Add Publish-Gate job with needs: on all prerequisite jobs and if: always() && !cancelled()
  • Implement gate logic as a step that evaluates conditions and sets publishModule and publishSite outputs
  • Simplify Publish-Module job if: to needs.Publish-Gate.outputs.publishModule == 'true'
  • Simplify Publish-Site job if: to needs.Publish-Gate.outputs.publishSite == 'true'
  • Update needs: on Publish-Module and Publish-Site to include Publish-Gate

Testing

  • Verify publish still triggers on merged PRs with passing tests
  • Verify publish is skipped when tests fail
  • Verify publish is skipped when settings disable it
  • Verify skipped upstream jobs (e.g., no site configured) still allow module publish

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions