Skip to content

ci: run the existing test suite on every pull request - #86

Merged
mashkovd merged 3 commits into
mainfrom
ci/pr-validation
Aug 2, 2026
Merged

mashkovd merged 3 commits into
mainfrom
ci/pr-validation

Conversation

@mashkovd

@mashkovd mashkovd commented Aug 2, 2026

Copy link
Copy Markdown
Member

What

Adds .github/workflows/pr-validation.yml: a tests job that runs the
existing suite on every PR and on pushes to main, plus a Docker smoke build
gated behind it.

Why

This repository has 234 tests and nothing runs them. .github/workflows/
contained only claude-review.yml and release-please.yml, so a change could
break the entire suite and still merge green. Of everything surfaced by the
recent architecture review, this was the highest-severity finding — higher
than the language question that prompted the review.

Verified locally before writing the workflow: pytest -q → 234 passed in
4.71s
. The suite is healthy; it was simply never wired to CI.

Scope

Deliberately narrow — run the tests that already exist, on the current
dependency set. No dependency file changes, no linter, no new style
requirements. Those are separate follow-ups, so that a red run here is
unambiguous about its cause rather than a mix of "the tests broke" and "the
linter has opinions".

Notes on specific choices

  • pytest is installed inline. It is not in requirements.txt, which pins
    only the runtime harness. A follow-up moves both into a pyproject.toml
    dev dependency group with a lockfile, at which point this step becomes
    uv sync --frozen.
  • The Docker build is gated on the tests (needs: tests) rather than run
    beside them. The image installs Node, the gh CLI and a ~210 MB SDK wheel,
    so it is far slower than the suite; this keeps fast feedback fast and skips
    a costly build when the tests are already red. It is a no-push smoke build —
    image publishing stays in mctl-gitops.
  • Actions are pinned by commit SHA, matching claude-review.yml in this
    repository. It is public and handles GitHub App and OAuth tokens.
  • cancel-in-progress applies to pull requests only. Runs on main are
    each the record for a distinct merged commit and should not be cancelled.
  • No Telegram notification. Unlike mctl-api, this repository has no
    TELEGRAM_BOT_TOKEN or TELEGRAM_CHAT_ID secret (only CLAUDE_CODE_OAUTH_TOKEN,
    CLAUDE_CODE_OAUTH_TOKEN_2, GITOPS_TOKEN), so the step would fail on its
    first use. Adding one requires provisioning those secrets and is out of
    scope. Worth noting it would only ever fire on main anyway — on PRs it is
    noise, since a developer may push several intermediate commits.

This PR alone does not close the risk

Adding the workflow makes the tests run; it does not make them required.
Until the tests job is a required status check on main, a PR can still be
merged without it. That is a repository setting rather than a file, so it
needs a separate, explicit step — flagged here so it does not get lost.

Verification

  • pytest -q locally → 234 passed
  • Workflow YAML parses; jobs resolve as tests and docker-build (needs: tests)
  • CI green on this PR
  • Deliberately broken test turns CI red (proves the gate actually works)
  • tests set as a required status check, and merge blocked when it is
    failing or skipped

The repository has 234 tests and no workflow that runs them. Only
claude-review.yml and release-please.yml exist, so a change could break the
entire suite and still merge green. This adds the missing gate.

Scope is deliberately narrow: run the tests that already exist, on the
current dependency set. No dependency file changes, no linter, no style
requirements — those are separate follow-ups, so that a failure here is
unambiguous about its cause.

Details worth noting:

- pytest is not in requirements.txt, which pins only the runtime harness, so
  it is installed inline. A follow-up moves both into a pyproject dev group
  with a lockfile and this becomes `uv sync --frozen`.
- The Docker smoke build is gated on the tests rather than run beside them.
  The image installs Node, the gh CLI and a large SDK wheel, so it is much
  slower than the suite; this keeps fast feedback fast and skips a costly
  build when the tests are already red.
- Actions are pinned by commit SHA, matching claude-review.yml in this
  repository. It is public and handles GitHub App and OAuth tokens.
- cancel-in-progress applies to pull requests only. Runs on main are each
  the record for a distinct merged commit and should not be cancelled.
- No Telegram notification: unlike mctl-api, this repository has no
  TELEGRAM_BOT_TOKEN or TELEGRAM_CHAT_ID secret, so the step would fail.
  Adding one needs those secrets first and is out of scope here.
@claude

claude Bot commented Aug 2, 2026 •

Copy link
Copy Markdown

Claude encountered an error after 1s —— View job


I'll analyze this and get back to you.

@claude

claude Bot commented Aug 2, 2026 •

Copy link
Copy Markdown

Claude finished @mashkovd's task in 1m 49s —— View job


Review complete

  • Read PR context and diff
  • Read .github/workflows/pr-validation.yml
  • Check repository CLAUDE.md conventions
  • Post inline findings
  • Submit PR review verdict

Verdict: approved — No P1/P2 findings (1 P3).

The workflow is well-scoped and internally consistent:

  • permissions: contents: read at the top level, no secrets used — appropriate for a workflow that runs on untrusted fork PRs.
  • Actions pinned by commit SHA, matching the precedent in claude-review.yml.
  • Python version (3.12) matches the Dockerfile base image (python:3.12-slim), and a comment flags the coupling.
  • docker-build correctly gates on needs: tests and doesn't push, matching the stated smoke-test intent.
  • concurrency correctly cancels superseded PR runs but lets sequential main runs queue rather than cancel, matching the PR description's reasoning.

One P3 nit posted inline: neither job sets timeout-minutes, so a hang falls back to GitHub's 6-hour default job timeout instead of failing fast. Not blocking.

Note flagged in the PR body itself and worth restating: this workflow makes the tests run, but tests still needs to be turned into a required status check on main (a repo setting, not something this file can do) before the gate is actually enforced.

jobs:
tests:
name: tests
runs-on: ubuntu-latest

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P3: neither job sets timeout-minutes, so a hung pytest run or a stuck docker buildx build falls back to the GitHub default 6-hour job timeout. Not a correctness bug, but worth capping (e.g. timeout-minutes: 10 here, timeout-minutes: 20 on docker-build) so a hang doesn't silently tie up a runner slot / block the concurrency group for hours instead of failing fast.

claude[bot]
claude Bot previously approved these changes Aug 2, 2026

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

No P1/P2 findings (1 P3). Good to merge.

Review finding (P3): neither job set timeout-minutes, so a wedged pytest or
a stuck buildx build would fall back to GitHub's 6-hour default. That ties
up a runner slot and, because the workflow uses a concurrency group, blocks
subsequent runs on the same ref for hours instead of failing fast.

10 minutes for the tests (the suite runs in seconds) and 20 for the image
build, which pulls apt packages, a global npm install and a large SDK wheel.
@claude

claude Bot commented Aug 2, 2026 •

Copy link
Copy Markdown

Claude encountered an error after 1s —— View job


I'll analyze this and get back to you.

@claude

claude Bot commented Aug 2, 2026 •

Copy link
Copy Markdown

Claude finished @mashkovd's task in 49s —— View job


Reviewing PR #86 (synchronize event)

  • Read PR context and previous review history
  • Diff against origin/main to see what changed since last review
  • Read current .github/workflows/pr-validation.yml
  • Post inline findings (if any)
  • Submit PR review verdict

Verdict: approved — No P1/P2 findings (0 P3).

This push adds timeout-minutes: 10 on tests and timeout-minutes: 20 on docker-build (workflows/pr-validation.yml:31,66), which resolves the only nit from the prior review (hung job previously falling back to GitHub's 6-hour default). Comments explain the chosen values and why docker-build's is higher.

No new issues found. The rest of the file is unchanged from the previously-approved version: read-only permissions, SHA-pinned actions, correct needs: tests gating, and PR-only cancel-in-progress.

claude[bot]
claude Bot previously approved these changes Aug 2, 2026

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

No P1/P2 findings (0 P3). Good to merge.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 0a13982c1a

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread .github/workflows/pr-validation.yml Outdated
contents: read

concurrency:
group: pr-validation-${{ github.ref }}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Give main pushes distinct concurrency groups

When three or more pushes to main arrive before validation drains, this shared github.ref group does not preserve every run: GitHub concurrency retains at most one running and one pending run per group, and a newly queued run replaces the existing pending run even when cancel-in-progress is false. Consequently, an intermediate merged commit can have its validation cancelled despite the stated requirement that each main commit retain its own run; include the SHA in the group for push events or omit concurrency for them.

Useful? React with 👍 / 👎.

Comment on lines +10 to +11
on:
pull_request:

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Trigger validation for release-please PRs

For release PRs created or updated by .github/workflows/release-please.yml, this trigger never runs: that workflow invokes release-please-action without a custom token, so it uses GITHUB_TOKEN, and GitHub suppresses subsequent workflow runs caused by events generated with that token. Once tests is configured as required as instructed here, release-please PRs will therefore have no satisfiable tests check; create those PRs with the existing GitHub App token or another non-GITHUB_TOKEN credential.

Useful? React with 👍 / 👎.

Review finding (P2). The previous group keyed only on github.ref, with
cancel-in-progress disabled for pushes, and a comment claiming each main
commit keeps its own run. It does not: GitHub retains at most one running
and one pending run per group, and a newly queued run evicts the pending one
regardless of cancel-in-progress. Three merges landing close together would
silently drop the middle commit's validation.

Pushes now get one group per commit, so no main commit's run can be evicted.
Pull requests keep the shared per-ref group, where cancelling a superseded
run is the desired behaviour.

Also documents, at the top of the file, why this workflow cannot be made a
required check before release-please is dealt with: release-please opens its
PR with the default GITHUB_TOKEN, and GitHub suppresses workflow triggers for
events raised by that token, so release PRs can never satisfy a required
`tests` check. They are merged today with `gh pr merge --admin`, which
bypasses required checks, so releases keep working while the release commit
goes unvalidated. Fixing that means giving release-please-action an App
token, which belongs in release-please.yml rather than here.
@mashkovd

mashkovd commented Aug 2, 2026

Copy link
Copy Markdown
Member Author

Both Codex P2s were correct and are addressed. Thanks — the second one in particular would have bitten later.

P2 "Give main pushes distinct concurrency groups" — fixed.
The finding is right and my comment in the file was simply wrong: I asserted that each main commit keeps its own run, but GitHub retains at most one running and one pending run per group, and a newly queued run evicts the pending one regardless of cancel-in-progress. Three merges landing close together would have silently dropped the middle commit's validation. Pushes now key the group on github.sha; pull requests keep the shared per-ref group, where cancelling a superseded run is what you actually want.

P2 "Trigger validation for release-please PRs" — confirmed, documented, deliberately not fixed here.
Verified against release-please.yml: release-please-action@v5 is invoked with no token: input, so it uses GITHUB_TOKEN. The App token generated further down is only used for the gh workflow run dispatch to mctl-gitops, not for creating the release PR. So the conclusion holds — release PRs will never run this workflow.

One correction to the impact, though: release PRs in this org are merged with gh pr merge --admin, which bypasses required checks, so making tests required would not hard-block releases. The real cost is narrower but still real: the release commit merges unvalidated.

I have not changed release-please.yml in this PR. Handing release-please-action an App token is a change to release infrastructure, and modifying that workflow has broken releases on main here before. It also does not belong in a PR whose entire point is "run the tests that already exist" — mixing them would make a failure ambiguous. Instead it is now documented at the top of pr-validation.yml and recorded as a prerequisite for the separate step that makes tests required.

@claude

claude Bot commented Aug 2, 2026 •

Copy link
Copy Markdown

Claude encountered an error after 1s —— View job


I'll analyze this and get back to you.

@claude

claude Bot commented Aug 2, 2026 •

Copy link
Copy Markdown

Claude finished @mashkovd's task in 1m 21s —— View job


Reviewing PR #86 (synchronize event)

  • Read PR context and previous review history
  • Diff against origin/main to see what changed since last review
  • Read current .github/workflows/pr-validation.yml
  • Post inline findings (if any)
  • Submit PR review verdict

Verdict: approved — No P1/P2 findings (0 P3).

This push (974767c) addresses the Codex P2 "Give main pushes distinct concurrency groups" finding: concurrency.group (workflows/pr-validation.yml:36) now keys on github.sha for push events and falls back to github.ref for pull_request events, with cancel-in-progress staying scoped to pull requests only. Since each push gets its own unique group, GitHub's "one running + one pending run per group" eviction behavior can no longer drop an intermediate main commit's validation — the fix is correct and stronger than a partial mitigation.

The commit also documents, in the file header, why tests can't yet be made a required check: release-please.yml opens release PRs with the default GITHUB_TOKEN, so GitHub's anti-recursion rule suppresses this workflow's triggers on them, and they're merged today via gh pr merge --admin (bypassing required checks). This is a deliberate, documented scope decision — not fixed in this PR, per the author's comment — and reasonable given fixing it means touching release-please.yml, which is out of scope here and has broken releases before.

No new issues found. All previously flagged items (timeout-minutes P3, concurrency-group P2) are now resolved.

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

No P1/P2 findings (0 P3). Good to merge.

@mashkovd
mashkovd merged commit 1877837 into main Aug 2, 2026
3 checks passed
@mashkovd
mashkovd deleted the ci/pr-validation branch August 2, 2026 09:58
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.

1 participant