Skip to content

hypatia: detect read-only check workflows missing concurrency block (from #333 cohort, pattern 6) #365

@hyperpolymath

Description

@hyperpolymath

Detector spec (from hypatia#333 Pattern 6)

Pattern 6 — Read-only check workflow missing concurrency: block

Severity: low (runner-cost waste on rapid-push PRs)

Detection (Hypatia.Rules.WorkflowAudit):

  • For each workflow file:
    • If on: includes pull_request or push, AND no top-level concurrency: block, AND permissions: is read-all or only has read scopes, flag.
    • The read-only condition is the safety gate — cancel-in-progress: true is only safe when the workflow doesn't publish/mutate.

Worked examples (this session):

  • affinescript/.github/workflows/{ci,semgrep,stdlib-naming,spark-theatre-gate,workflow-linter}.yml all lacked concurrency blocks. Fix: affinescript#379 added the estate-standard {group: workflow-ref, cancel-in-progress: true} pattern.

Remediation guidance to emit:

Read-only check workflows should add the canonical block:

concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: true

Skip for workflows that publish (release, JSR, npm) — superseding a publish is unsafe.

Implementation pointers

  • Detection algorithm: For each workflow, if on: includes pull_request/push AND no top-level concurrency: AND permissions: is read-all or read-only scopes, flag. Read-only condition is the safety gate.
  • Real-world example: affinescript/.github/workflows/{ci,semgrep,stdlib-naming,spark-theatre-gate,workflow-linter}.yml all lacked concurrency blocks.
  • Landed fix (reference): affinescript#379 (added estate-standard {group: workflow-ref, cancel-in-progress: true} pattern).
  • Rule statement: Read-only check workflows should add the canonical concurrency: { group: ${{ github.workflow }}-${{ github.ref }}, cancel-in-progress: true } block. Skip for workflows that publish (release, JSR, npm) — superseding a publish is unsafe.

Acceptance

  • Rule encoded in hypatia (file path follows existing rule naming convention — lib/rules/<name>.ex if Elixir, or matching the repo's rule DSL)
  • Test fixture exercising the positive case + at least one negative case
  • Smoke test passes against the cited landed-fix repo

Source cohort: hypatia#333.

Metadata

Metadata

Assignees

No one assigned

    Labels

    cicdCI/CD pipeline, GitHub Actions, workflows, rulesets, releasesenhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions