jevgate-action
September 21, 2026 · View on GitHub
jevgate-action runs the jevgate CLI for two exact Git commits and exposes its validated decision to later GitHub Actions steps. It installs a pinned CLI release, verifies its checksum and archive, reads configuration from the base commit, and rejects malformed or inconsistent results.
The first release supports GitHub-hosted Linux x64 runners. It requires Bash, Git, curl, tar, sha256sum, and jq, all of which are present on current Ubuntu GitHub-hosted runners. The caller remains responsible for checkout and fetch.
Status
The implementation targets jevgate CLI v0.1.0. This Action has not been published yet. The files in examples/ therefore contain an explicit REPLACE_WITH_PUBLISHED_40_CHARACTER_COMMIT_SHA marker. Replace it only with the full commit SHA of a published Action release; the marker is not a working ref.
Usage
Start from examples/pull-request.yml. It handles non-draft pull requests from the same repository and excludes Dependabot because GitHub does not normally pass Actions secrets to fork or Dependabot pull-request workflows.
The essential step is:
- name: Evaluate change
id: gate
uses: ktsu2i/jevgate-action@<published-40-character-commit-sha>
with:
version: v0.1.0
env:
JEV_API_KEY: ${{ secrets.JEV_API_KEY }}
Use actions/checkout with fetch-depth: 0 and persist-credentials: false, check out the PR base SHA, and verify that the exact PR head SHA fetched by checkout is present before this step. If the Action is skipped or fails, its missing output must never be interpreted as permission:
if: >-
needs.gate.result == 'success' &&
needs.gate.outputs.ai-approval-allowed == 'true'
The Action does not approve a pull request, run an AI reviewer, comment, label, fetch commits, or merge code.
Inputs
| Input | Required | Contract |
|---|---|---|
version | yes | Exact stable CLI tag in vX.Y.Z form. latest, branches, prereleases, and URLs are rejected. The CLI version is independent of the Action version. |
base | conditional | Full 40-character commit SHA. When both SHA inputs are omitted on pull_request, this comes from pull_request.base.sha. |
head | conditional | Full 40-character commit SHA. When both SHA inputs are omitted on pull_request, this comes from pull_request.head.sha. |
config | no | Repository-root-relative path in the base commit. Omission means .jevgate.yml. |
threshold | no | Finite number in [0,1]. An explicit 0 is preserved. |
fail-on-human-review | no | true or false; default false. When true, a valid human-review decision fails the step. |
base and head must either both be specified or both omitted. Both are required outside a pull_request event. pull_request_target is rejected. JEV_API_KEY is required through the environment; there is deliberately no API-key input.
Threshold precedence remains input > base configuration > CLI default.
Outputs and results
Outputs are written only after a valid CLI decision is received.
| Output | Value |
|---|---|
ai-approval-allowed | Validated true or false |
confidence | Confidence returned by the CLI |
threshold | Threshold actually applied by the CLI |
base-sha | Evaluated base SHA |
head-sha | Evaluated head SHA |
| CLI result | Default Action result | With fail-on-human-review: true |
|---|---|---|
exit 0 and valid true | success, outputs available | success, outputs available |
exit 1 and valid false | success, outputs available | failure after outputs are written |
exit 2, another exit, signal, invalid JSON, or inconsistent boolean | failure, no decision outputs | failure, no decision outputs |
Do not design cross-job control flow around outputs from a failed step or job. Use the default mode when a later job needs the valid false decision. The strict mode is useful as a required check, but a human review cannot turn that JevGate check green; the change must be reevaluated and allowed by JevGate or the check configuration must change.
Comparison and configuration trust
JevGate directly compares base to head. It does not substitute their merge base and it does not use GitHub's synthetic test merge commit. This can differ from the three-dot diff shown in a pull request.
Both objects must already exist as commits in the current repository. Missing history causes an actionable failure rather than a fallback to another commit. The Action does not support a repository subdirectory.
The configuration is taken from the base tree, not the checkout or head tree:
- The path is validated as a repository-root-relative path. Absolute paths,
.or..components, empty components, and control characters are rejected. - Only a normal Git blob is accepted. Symlinks, directories, and submodules are rejected.
- The blob is copied into the runner temporary directory and always passed with
--config. - Only when the input was omitted and base has no
.jevgate.yml, an empty{}configuration is supplied. A missing explicitly named path is an error.
Using base configuration does not establish trust for the complete workflow. Any automation that acts on an allow decision must also protect the workflow, the pinned Action ref, and any explicitly provided base SHA or threshold through a trusted change path.
Download and data handling
The Action downloads jevgate_<version>_linux_amd64.tar.gz and checksums.txt from the matching public ktsu2i/jevgate GitHub Release. It requires exactly one checksum entry, verifies SHA-256, accepts an archive containing only one normal file named jevgate, and verifies jevgate --version before execution. A release checksum proves consistency between artifacts from that release; it is not an independent signature against compromise of the release source.
The CLI sends the complete collected patch, changed paths and file metadata, and the base configuration's context to the Jev API. The CLI does not send a truncated partial change. Empty diffs, binary changes, submodule changes, and input above the CLI limits fail without a decision. The Action does not log or save the diff, context, or API response as an artifact.
Required-check example
examples/required-check.yml shows fail-on-human-review: true. Both official examples grant only contents: read, pin third-party Actions by commit SHA, exclude fork and Dependabot PRs, and avoid treating skip or failure as permission.
Development
Run the offline suite with:
tests/test.sh
It uses Git, CLI, download, checksum, and archive fixtures; it does not call the Jev API or download a release. CI additionally invokes the repository through uses: ./ and runs ShellCheck, actionlint, and zizmor.
Maintainers can run the Live API smoke test workflow manually after configuring the JEV_API_KEY repository secret. It creates a disposable two-commit repository in runner temporary storage, uses the public CLI release, accepts either valid gate decision, and checks the returned SHAs without asserting an API score.
Releasing
The initial candidate is v0.1.0. Before publishing:
- Review the release contents, including the MIT
LICENSEfile. - Run the test and lint workflows and perform a manual test in a small fixture repository with a real Jev API key.
- Recheck the public CLI release's checksum,
--version, and--helpon Linux x64. - Create and push a complete
vX.Y.Ztag. The release workflow tests that tagged tree and creates a GitHub Release. - Replace the marker in both examples with the published Action commit SHA, then update the CLI documentation to point here.
Publishing tags or releases remains a separate operation and is not performed by this repository setup.