diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index fb40964..2474fe4 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -25,6 +25,7 @@ jobs: with: args: --dry-run cache: "false" + sarif-file: jevgate.sarif - name: Check the outputs shell: bash env: @@ -32,3 +33,4 @@ jobs: run: | [ "$CODE" = 0 ] || { echo "::error::exit code $CODE"; exit 1; } jevgate --version + grep -q '"version": "2.1.0"' jevgate.sarif || { echo "::error::no SARIF log"; exit 1; } diff --git a/README.md b/README.md index 768cee7..806e850 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ jobs: - uses: Tech-Byte-Frontier/jevgate-action@v1 with: api-key: ${{ secrets.TYPESAFE_API_KEY }} - version: 0.17.0 + version: 0.18.0 ``` The action installs a release binary (checked against its SHA-256), keeps JevGate's answer cache in the Actions cache so unchanged code costs nothing, and runs `jevgate check --base --format github`. It needs no Rust toolchain and runs on Linux, macOS and Windows runners. @@ -30,7 +30,8 @@ The action installs a release binary (checked against its SHA-256), keeps JevGat | `version` | `latest` | JevGate version; pin one for repeatable results | | `base` | the pull request's base commit | Review only files changed since this revision; empty on other events, which review the whole repository | | `args` | | More `jevgate check` arguments, such as `--rule default --rule security --include-tests` | -| `format` | `github` | `github`, `agent`, `json` or `jsonl` | +| `format` | `github` | `github`, `agent`, `json`, `jsonl`, `sarif` or `gitlab` | +| `sarif-file` | | Also write the findings as SARIF to this path, for `upload-sarif` (JevGate 0.18.0 or later) | | `cache` | `true` | Keep answers in the Actions cache | | `working-directory` | `.` | Repository root to check | @@ -41,6 +42,32 @@ The action installs a release binary (checked against its SHA-256), keeps JevGat | `exit-code` | `0` gate passed, `1` gate failed, `2` run incomplete | | `report` | Path of the full JSON report (`.jevgate/latest.json`), to upload as an artifact | +## Code scanning + +`sarif-file` also writes the findings as SARIF, replayed from the answers the check just cached, so it costs nothing. Upload it to show them in the repository's Security tab and on pull requests (needs JevGate 0.18.0 or later): + +```yaml +permissions: + contents: read + security-events: write +jobs: + review: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v7 + with: + fetch-depth: 0 + - uses: Tech-Byte-Frontier/jevgate-action@v1 + with: + api-key: ${{ secrets.TYPESAFE_API_KEY }} + sarif-file: jevgate.sarif + - uses: github/codeql-action/upload-sarif@v4 + if: always() + with: + sarif_file: jevgate.sarif + category: jevgate +``` + ## Pull requests from forks GitHub doesn't give secrets to pull requests from forks, so the run there ends incomplete with "No API key configured". Skip the job for them: diff --git a/action.yml b/action.yml index 5d1efbb..ab91515 100644 --- a/action.yml +++ b/action.yml @@ -25,6 +25,10 @@ inputs: description: Output format. `github` annotates the changed lines and writes a job summary. required: false default: github + sarif-file: + description: Also write the findings as SARIF to this path, for github/codeql-action/upload-sarif. Replays the check from its cached answers, so it costs nothing. Needs JevGate 0.18.0 or later. + required: false + default: "" cache: description: Keep answers in the Actions cache, so unchanged code costs nothing on the next run. required: false @@ -64,4 +68,5 @@ runs: BASE: ${{ inputs.base || github.event.pull_request.base.sha }} FORMAT: ${{ inputs.format }} ARGS: ${{ inputs.args }} + SARIF_FILE: ${{ inputs.sarif-file }} run: bash "$GITHUB_ACTION_PATH/check.sh" diff --git a/check.sh b/check.sh index d063e0d..32da052 100755 --- a/check.sh +++ b/check.sh @@ -2,7 +2,7 @@ # Run `jevgate check` and pass its exit code on: 0 passed, 1 failed, 2 incomplete. set -uo pipefail -command=(jevgate check --format "$FORMAT") +command=(jevgate check) if [ -n "$BASE" ]; then if ! git cat-file -e "$BASE^{commit}" 2> /dev/null; then echo "::error::The base revision $BASE is not in the checkout. Check out with fetch-depth: 0 so --base can find the fork point." @@ -14,9 +14,18 @@ fi read -r -a extra <<< "$ARGS" command+=(${extra[@]+"${extra[@]}"}) -"${command[@]}" +"${command[@]}" --format "$FORMAT" code=$? +# The same check again from the answers just cached: no request is sent. +if [ -n "${SARIF_FILE:-}" ]; then + "${command[@]}" --cache-only --format sarif > "$SARIF_FILE" + replay=$? + if [ "$replay" -gt 1 ]; then + echo "::warning::Could not write $SARIF_FILE (exit $replay); --format sarif needs JevGate 0.18.0 or later." + fi +fi + echo "exit-code=$code" >> "$GITHUB_OUTPUT" echo "report=$PWD/.jevgate/latest.json" >> "$GITHUB_OUTPUT" if [ "$code" = 2 ] && [ -z "${TYPESAFE_API_KEY:-}" ]; then