Skip to content

feat: replace the sg-cli passthrough with the Tirith Policy Check action - #182

Open
refeed wants to merge 3 commits into
mainfrom
feat/tirith-policy-check
Open

feat: replace the sg-cli passthrough with the Tirith Policy Check action#182
refeed wants to merge 3 commits into
mainfrom
feat/tirith-policy-check

Conversation

@refeed

@refeed refeed commented Aug 3, 2026

Copy link
Copy Markdown
Member

Replaces this repository's contents with an action that evaluates StackGuardian policies against a
terraform plan in CI and reports the outcome as a sticky pull-request comment and a check run.

What it supersedes

The previous action was 15 lines: a docker passthrough running sg-cli <operation> on alpine:3.10
(EOL since 2021), which wget'd the CLI's releases/latest at image build time — so pinning the
action did not pin what it ran. One free-form input, no outputs.

Anyone depending on that should pin @v1.0.0-beta; the README says so. Suggest tagging this v2
rather than moving @main.

The action is deliberately thin

Everything that talks to StackGuardian — masking, packing, uploading, polling, rendering — is
tirith platform check (StackGuardian/tirith#272). What is left here is only what is genuinely
GitHub-specific: reading the event payload, posting the comment and check run, setting outputs.

Keeping the split at that line is the point: a GitLab or Jenkins integration reuses the CLI
unchanged, and this file never becomes the place where platform behaviour is decided.

Two choices that are easy to get wrong

  • TriggerDetails.type is github_action, never github_webhook. The run controller posts its
    own comment and check on the webhook path (common/vcs.py), so claiming it would double-post on
    every pull request. commentsUrl / checksApiUrl are likewise omitted.
  • neutral satisfies a required check, so it is only safe for warnings. Anything unresolved maps
    to failure; approval-required maps to action_required.

Testing

The integration tests run the action as a real subprocess against a stub serving both the SG and
GitHub APIs on one port, with a real tirith installed, and assert that a secret in the plan appears
in no recorded request body and nowhere inside the uploaded archive. That end-to-end shape is
deliberate: testing the masking function in isolation is what let a leak through once already.

13 tests. Writing them caught a real defect — the trigger details were built and never passed to the
CLI, so every run would have been recorded as type: cli.

Depends on StackGuardian/tirith#272 (and the 1.2.0 tag), StackGuardian/core#1235,
StackGuardian/api#1708, StackGuardian/auth#201, StackGuardian/workflow-step-templates#310.

Evaluates an organization's StackGuardian policies against a terraform plan in
CI and reports the outcome as a sticky pull-request comment and a check run.

This supersedes the previous contents of this repository -- a 15-line docker
passthrough that ran `sg-cli <operation>` on alpine:3.10 and resolved the CLI
version at image build time, so pinning the action did not pin what it ran.
Anyone depending on that should pin @v1.0.0-beta; there is no automatic
migration, and the README says so.

The action is deliberately thin. Everything that talks to StackGuardian --
masking, packing, uploading, polling, rendering -- is `tirith platform check` in
StackGuardian/tirith, so the same behaviour is available from GitLab, a Makefile
or a laptop. What is left here is only what is genuinely GitHub-specific:
reading the event payload, posting the comment and check run, and setting
outputs.

Notes on two choices that are easy to get wrong:

  * TriggerDetails.type is github_action, never github_webhook. The run
    controller posts its own comment and check on the webhook path, so claiming
    it would double-post on every pull request.
  * `neutral` satisfies a required check, so it is only safe for warnings.
    Anything unresolved maps to failure, and approval-required maps to
    action_required.

The integration tests run the action as a real subprocess against a stub serving
both APIs, with a real tirith installed, and assert that a secret in the plan
appears in no recorded request body and nowhere inside the uploaded archive.
That end-to-end shape is deliberate: testing the masking function in isolation
is what let a leak through once already.
Copilot AI review requested due to automatic review settings August 3, 2026 03:51

Copilot AI 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.

Pull request overview

This PR replaces the previous sg-cli docker passthrough action with a composite GitHub Action wrapper around tirith platform check, adding GitHub-native reporting (sticky PR comment + check run), safer output handling, and end-to-end integration tests.

Changes:

  • Replace the Docker-based sg-cli passthrough with a composite action that installs and runs Tirith.
  • Add a stdlib-only Python entrypoint that translates GitHub event context into Tirith inputs and posts GitHub comment/check results.
  • Add end-to-end integration tests plus updated docs/examples describing usage and migration.

Reviewed changes

Copilot reviewed 13 out of 14 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
action.yml Redefines the action as a composite action with explicit inputs/outputs and a Tirith install + execution flow.
scripts/main.py Implements the action entrypoint: event parsing, trigger details, invoking Tirith, setting outputs, posting comment/check.
scripts/tirith_action/gh_client.py Adds a minimal GitHub REST client to upsert a sticky comment and create a check run.
scripts/tirith_action/__init__.py Declares the helper package.
tests/test_action_integration.py Adds end-to-end tests validating masking/leak prevention, run creation, reporting, and exit codes.
README.md Updates documentation for Tirith Policy Check usage, permissions, inputs/outputs, exit codes, and migration notes.
examples/basic.yml Provides a minimal workflow example demonstrating plan → JSON → policy check.
examples/monorepo-matrix.yml Shows recommended matrix usage with per-stack workflow-id + comment-tag.
examples/with-state.yml Adds an example for pre-apply plan gating + post-apply state checking.
docs/terragrunt.md Adds terragrunt pipeline guidance and phased approaches for multi-unit policy evaluation.
.github/workflows/test.yml Updates CI to run pytest integration tests and adds a smoke test for failure behavior.
.gitignore Ignores Python caches and pytest cache.
Dockerfile Removed (no longer a Docker action).
entrypoint.sh Removed (no longer used).

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread action.yml
Comment on lines +121 to +123
- name: Install tirith
shell: bash
run: pip install --quiet "py-tirith @ git+https://github.com/StackGuardian/tirith@${{ inputs.tirith-version }}"
Comment thread scripts/main.py
set_output("failed", str(counts.get("failed", 0)))
set_output("warned", str(counts.get("warned", 0)))
set_output("results", json.dumps(result.get("policy_results") or {}))
set_output("results-file", result_path)
Comment thread scripts/main.py
Comment on lines +267 to +271
try:
with open(markdown_path) as f:
body = f.read()
except OSError:
body = result.get("headline", "Tirith policy check")
refeed added 2 commits August 3, 2026 11:30
TEMPORARY -- set tirith-version back to a tag before release. A moving ref means
a green pipeline can turn red with nothing in the repository changing.
source-dir defaults to "." and the archive packs it, so the action's own
tirith-trigger.json -- carrying the PR title, repo URL and actor -- was uploaded
to the platform. Verified in a live run.

Also documents plainly that the terraform source is uploaded as written, so a
secret hardcoded in HCL reaches StackGuardian regardless of masking. The
previous wording implied masking covered everything leaving the runner.
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