Skip to content

feat(empty-pr-guard): reusable empty-PR-description gate + notify alert cards - #19

Closed
arnavchachra wants to merge 4 commits into
mainfrom
feat/empty-pr-guard
Closed

feat(empty-pr-guard): reusable empty-PR-description gate + notify alert cards#19
arnavchachra wants to merge 4 commits into
mainfrom
feat/empty-pr-guard

Conversation

@arnavchachra

@arnavchachra arnavchachra commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

Supersedes #17 — ships the notify alert inputs and the reusable guard that
uses them together, so the guard and the card it renders release as one unit.

What

1. actions/notify — alert-card inputs. Three optional inputs — header,
color, status — let the same Slack card render a general alert instead of a
deploy result. With none set, the payload is byte-identical to the current
deploy card.

2. .github/workflows/empty-pr-guard.yml — new reusable workflow. Merge-time
gate: on a push, resolve the PR behind github.sha and, if its body is
empty/whitespace-only, revert the merge, edit the PR to explain, and emit
block=true so the caller stands its release down.

  • Reverts a merge commit via git revert -m 1; a squash/rebase merge via the
    before..HEAD range — all three merge styles fully undone.
  • Fails safe on a PR-fetch API error (skips, never false-reverts); keeps
    block=true and reports "revert by hand" on a rejected push or conflict.
  • Needs no secrets (revert + PR edit use GITHUB_TOKEN), so it's safe
    across an org boundary.
  • Emits the alert content (outcome/header/color/reason) as outputs; the
    caller renders the Slack card in a notify job (see the README example).

Compatibility

Additive and backward-compatible — the new notify inputs default to ""
(deploy card unchanged) and the guard is a new file. Per CONTRIBUTING, ships by
moving the v3 tag forward; no new major.

Test plan

Validated against a real consumer (fajardo-trucking/backend-api, guard gated
to dev):

  • Control — non-empty body merges, guard no-ops, release proceeds
  • Empty body, merge commit — reverted via -m 1, block=true, release stood down
  • Empty body, multi-commit merge — full before..HEAD range reverted
  • Empty body, squash merge — single squash commit reverted
  • Body edited to empty then merged — auto-revert notice written into the PR
  • Slack alert rendered from a caller job declaring the dev environment
  • actionlint clean

Notes

  • Consumers pin @feat/empty-pr-guard during validation; switch to @v3 once
    the tag moves.

The notify card was locked to a deploy result: a green/red header derived
from `result`, a Version field, and a pass/fail color bar. Callers that need
the same card shape for a non-deploy alert (e.g. a merge-revert / guard notice)
had to hand-roll the whole jq payload and curl themselves.

Add three optional, backward-compatible inputs:

- `header` — full header line, verbatim, overriding the default
  "<emoji> <label> — <status>". May carry Slack emoji shortcodes.
- `color`  — hex color bar overriding the result-derived green/red.
- `status` — when set, the first field renders as "Status" with this text
  in place of the "Version" field.

PR/commit resolution, the body section, and the context line are untouched,
so an alert card still shows the PR (or commit) that `target-sha` traces to.
With none of the three set the payload is byte-identical to before, so existing
deploy callers are unaffected. Documented in the README notify table.
Extract the empty-description merge guard (previously an inline ~100-line job
in a consumer's deploy pipeline) into a reusable workflow so every repo can
adopt it with one `uses:` instead of copy-pasting the bash.

On a push, it resolves the PR behind HEAD and, if the body is empty/whitespace,
reverts the merge (via `-m 1` for merge commits or the `before..HEAD` range for
squash/rebase), edits the PR to explain, posts a Slack card via the shared
notify action, and emits `block=true` so the caller stands its release down.
Fails safe on a PR-fetch API error; keeps block=true and asks for a manual
revert on a rejected push or conflict.

Because the Slack webhook is an environment secret and a reusable-workflow
caller job can't declare an environment, the workflow declares it from the
`environment` input and the caller passes `secrets: inherit`. `label` sets the
alert header name.

The notify call is pinned to feat/empty-pr-guard for now (same branch); bump to
@V3 on release. Documented in the README.
@tarpanpathak

Copy link
Copy Markdown
Contributor

Closing in favor of #20, which delivers the same behavior packaged as a composite action instead of a reusable workflow.

Composite shape lets callers declare the guard as one job with two steps (guard + alert) rather than two jobs plumbed together with cross-job outputs. Same underlying revert/edit script, same fail-safe semantics, roughly ten lines less caller boilerplate and one fewer node in the pipeline graph.

The notify alert-card inputs (header, color, status) you added here are carried over unchanged into #20, so nothing is lost.

Thanks for the original.

arnavchachra pushed a commit that referenced this pull request Jul 17, 2026
Adds actions/enforce (composite merge-time empty-PR-description gate) and header/color/status alert inputs to actions/notify. Supersedes #19. Additive; ships by moving v3 forward + cutting v3.4.0.
@arnavchachra
arnavchachra deleted the feat/empty-pr-guard branch July 17, 2026 08:22
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