Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ on:
push:
branches: [main]
pull_request:
# The gate skips drafts; ready_for_review runs it when a draft is marked ready.
types: [opened, synchronize, reopened, ready_for_review]

permissions:
contents: read
Expand Down
6 changes: 6 additions & 0 deletions log/devlog.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@

Newest entry first. Format: `standards/devlog.md` in emolysis/rules (private).

## 2026-10-23 · ci: a draft pull request runs its checks when it is marked ready (green)

- Landed: `ci.yml` lists `ready_for_review` in its `pull_request` types, so a draft runs the gate when it is marked ready instead of on its next push; `scripts/check.py` refuses a workflow whose jobs skip drafts without it (`draft_trigger_problems`), with three tests.
- Numbers: one workflow line, one check, 53 tests.
- Still open: none.

## 2026-10-20 · ci: minute saving in the workflows (green)

- Landed: `claude-review.yml` reviews once per pull request (`opened`, `ready_for_review`, the `review` label), never on every push, with a cancel group per pull request; `ci.yml` cancels the run a newer push supersedes on every ref and skips draft pull requests; the `review` label; the starter workflow ignores Markdown and `docs/` on push and pull request and cancels on every branch (spec updated); `scripts/check.py` refuses a review workflow that runs on push events (`review_trigger_problems`), with four tests. Founder decision of 2026-10-20 after the organization ran out of Action minutes.
Expand Down
19 changes: 19 additions & 0 deletions scripts/check.py
Original file line number Diff line number Diff line change
Expand Up @@ -325,9 +325,28 @@ def check_workflows() -> Problems:
problems.append(f"{path}: set top-level permissions explicitly")
if path == REVIEW_WORKFLOW:
problems += review_trigger_problems(path, doc)
problems += draft_trigger_problems(path, doc)
return problems


def draft_trigger_problems(path: Path, doc: Any) -> Problems:
"""A workflow whose jobs skip drafts lists ready_for_review in its pull_request types:
the default types never fire when a draft is marked ready, so it would run nothing until
the next push (emolysis/rules standards/ci.md, Minutes, Drafts)."""
doc = doc or {}
jobs = doc.get("jobs") or {}
skips = any(
"github.event.pull_request.draft" in str((job or {}).get("if", "")) for job in jobs.values()
)
on = workflow_on(doc)
if not skips or not isinstance(on, dict) or "pull_request" not in on:
return []
types = (on.get("pull_request") or {}).get("types") or []
if "ready_for_review" not in types:
return [f"{path}: jobs skip drafts, so pull_request.types must list ready_for_review"]
return []


def review_trigger_problems(path: Path, doc: Any) -> Problems:
"""The automated review runs once per pull request, never on every push: a review
spends runner minutes and API credit (emolysis/rules standards/ci.md, Minutes)."""
Expand Down
26 changes: 26 additions & 0 deletions tests/test_check.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,32 @@ def test_review_trigger_default_types() -> None:
assert "synchronize" in problem


# --- a draft marked ready runs the jobs that skip drafts ---------------------------------


DRAFT_JOB = """
jobs:
gate:
if: ${{ !github.event.pull_request.draft }}
runs-on: ubuntu-latest
"""
READY = "on:\n pull_request:\n types: [opened, synchronize, reopened, ready_for_review]\n"


def test_draft_trigger_ok() -> None:
assert check.draft_trigger_problems(P, doc(READY + DRAFT_JOB)) == []


def test_draft_trigger_default_types() -> None:
[problem] = check.draft_trigger_problems(P, doc("on:\n pull_request:\n" + DRAFT_JOB))
assert "ready_for_review" in problem


def test_draft_trigger_ignores_workflows_without_a_draft_guard() -> None:
plain = doc("on:\n pull_request:\njobs:\n gate:\n runs-on: ubuntu-latest\n")
assert check.draft_trigger_problems(P, plain) == []


# --- labels that start automation stay off issue forms ------------------------------------


Expand Down
Loading