Skip to content

feat(ci): add composite actions for reqstool validate/status - #41

Merged
jimisola merged 3 commits into
mainfrom
feat/composite-validate-reqstool
Jun 21, 2026
Merged

feat(ci): add composite actions for reqstool validate/status#41
jimisola merged 3 commits into
mainfrom
feat/composite-validate-reqstool

Conversation

@jimisola

Copy link
Copy Markdown
Member

What

Two composite actions, both installing reqstool from either PyPI or reqstool-client@main:

  • validate-reqstool — runs reqstool validate --strict (spec completeness: every requirement has SVCs, manual SVCs have MVRs).
  • reqstool-status — runs reqstool status --verbosity compact, optionally gated on --check-all-reqs-met via the fail-if-incomplete input (default false, so repos that intentionally have incomplete requirements — e.g. demo/fixture repos — don't have to fail CI on it; repos aiming for 100% completeness, like reqstool-client, can opt in).

Why composite actions, not a reusable workflow

common-validate-openspec.yml (#40) is a reusable workflow — it runs as its own job with a fresh checkout, which works because validating OpenSpec specs has no build dependency.

reqstool status/reqstool validate need build-time artifacts (annotations.yml, test results) that only exist within the job that already ran the repo's build step (mvn clean verify, etc. — different per language). A reusable workflow can't see those without an explicit artifact upload/download round-trip. A composite action runs as steps inside the calling job, so it can simply be inserted right after the existing build step and pick up whatever was just produced.

Usage

jobs:
  build:
    strategy:
      matrix:
        reqstool-source: [pypi, main]
    steps:
      - uses: actions/checkout@v6
      - run: mvn clean verify
      - uses: reqstool/.github/.github/actions/validate-reqstool@<pinned-sha>
        with:
          reqstool-source: ${{ matrix.reqstool-source }}
      - uses: reqstool/.github/.github/actions/reqstool-status@<pinned-sha>
        with:
          reqstool-source: ${{ matrix.reqstool-source }}
          fail-if-incomplete: "true"  # omit/false for intentionally-incomplete repos

First consumer: reqstool-demo#104.

🤖 Generated with Claude Code

Two separate composite actions for the OpenSpec dogfooding rollout
(reqstool/PLAN_dog_fooding.md), each installing reqstool from either
PyPI or reqstool-client@main:

- validate-reqstool: runs `reqstool validate --strict` (spec completeness
  — every requirement has SVCs, manual SVCs have MVRs).
- reqstool-status: runs `reqstool status --verbosity compact`, optionally
  gated on `--check-all-reqs-met` via fail-if-incomplete (left off by
  default for repos that intentionally have incomplete requirements, e.g.
  demo/fixture repos).

These are composite actions, not reusable workflows (unlike
common-validate-openspec.yml, #40): they run as steps within the calling
job, after that job's own build step, since reqstool status/validate
needs build-time artifacts (annotations.yml, test results) that a
separate reusable-workflow job wouldn't have access to.

First consumer: reqstool-demo#104.

Signed-off-by: Jimisola Laursen <jimisola@jimisola.com>
Comment thread .github/actions/reqstool-status/action.yml Fixed
Comment thread .github/actions/reqstool-status/action.yml Fixed
Comment thread .github/actions/reqstool-status/action.yml Fixed
Comment thread .github/actions/reqstool-status/action.yml Fixed
Comment thread .github/actions/validate-reqstool/action.yml Fixed
Comment thread .github/actions/validate-reqstool/action.yml Fixed
validate-reqstool and reqstool-status both duplicated the same
"set up Python + pip install reqstool from pypi/main" logic. Extract it
into a third composite action both now call via a nested
`uses: ./.github/actions/install-reqstool`, so the install logic lives in
one place.

Found via self-review on #41.

Signed-off-by: Jimisola Laursen <jimisola@jimisola.com>
Comment thread .github/actions/install-reqstool/action.yml Fixed
…erpolation

CodeQL flagged code-injection risk: ${{ inputs.x }} interpolated directly
into run: shell blocks substitutes literal script text before execution,
so a malicious input value could inject arbitrary shell commands. Pass
all inputs used in run: blocks via env: instead and reference them as
shell variables, which the shell expands safely without re-parsing as
script.

Affects install-reqstool, validate-reqstool, reqstool-status.

Signed-off-by: Jimisola Laursen <jimisola@jimisola.com>
@jimisola

Copy link
Copy Markdown
Member Author

Addressed all 7 CodeQL code-injection findings: `${{ inputs.x }}` was interpolated directly into `run:` shell blocks across all three action files, which is a script-injection risk. Fixed by passing every such input via `env:` and referencing it as a shell variable instead — the shell expands env vars safely without re-parsing them as script text.

All 7 review threads auto-resolved once CodeQL re-scanned the fix commit. Confirmed no open alerts remain in any of the 3 new files (`install-reqstool`, `validate-reqstool`, `reqstool-status`); the org's other open CodeQL alerts are all in unrelated pre-existing workflows, out of scope here.

Also did a self-review pass (no separate review-pr tool available in this environment) and found+fixed one duplication issue before any of this: both `validate-reqstool` and `reqstool-status` duplicated the same install logic, now extracted into a shared `install-reqstool` composite action both call.

All checks pass.

@jimisola
jimisola merged commit cd3b5e8 into main Jun 21, 2026
2 checks passed
@jimisola
jimisola deleted the feat/composite-validate-reqstool branch June 21, 2026 17:18
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