Skip to content

Commit 00ed0e4

Browse files
committed
Report the version check to code scanning
The action warns when a newer open-java-format release is out than the version it runs, but a log warning is easy to miss. The check is now also uploaded to code scanning as a SARIF file: one alert while the version is behind, none once it is current, which closes the alert. It shows under Security → Code scanning and in the "Code scanning results" check of a pull request, placed on the line of the workflow that names the version, so it points at what to change. Alerts are told apart by a fingerprint of that line's text, so they survive the line moving. The upload goes straight to the REST API rather than through github/codeql-action/upload-sarif, which fails the job when the security-events: write permission is missing. This way the upload is best-effort like the check itself: without the permission, without GitHub Code Security on a private repository, or with the read-only token of a fork's pull request, it is skipped with a line in the log and the log warning stays. The new code-scanning input turns it off. Test Action grants the permission to the mode=changed job, so that run exercises the upload, and withholds it from the platform matrix, whose ten runs of the action would all report the same category and must do without the upload anyway.
1 parent b86c00b commit 00ed0e4

4 files changed

Lines changed: 187 additions & 0 deletions

File tree

‎.github/workflows/ci.yml‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@ jobs:
1212
test-changed:
1313
name: Test mode=changed
1414
runs-on: ubuntu-latest
15+
permissions:
16+
contents: read
17+
pull-requests: read # gh pr diff
18+
security-events: write # the action reports its version check to code scanning
1519
steps:
1620
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
1721
with:
@@ -25,6 +29,8 @@ jobs:
2529
test-platforms:
2630
name: Test on ${{ matrix.os }}
2731
runs-on: ${{ matrix.os }}
32+
permissions:
33+
contents: read # no security-events: the action has to do without the code scanning upload
2834
strategy:
2935
fail-fast: false
3036
matrix:

‎README.md‎

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ v2 is for open-java-format 2.98.0.2 and later, which has a single style. A workf
2222
|-------|----------|---------|-------------|
2323
| `version` | yes | `2.98.0.6` | Version of the open-java-format native binary, one of those on [Maven Central](https://repo1.maven.org/maven2/dev/openjavaformat/open-java-format-native/) |
2424
| `mode` | no | `changed` | `changed` — only files from PR or push; `all` — every `.java` file in repo |
25+
| `code-scanning` | no | `true` | Report the version check to [code scanning](#code-scanning) as well; `false` keeps it to the log warning |
2526

2627
## How `mode: changed` works
2728

@@ -33,6 +34,7 @@ v2 is for open-java-format 2.98.0.2 and later, which has a single style. A workf
3334

3435
- If all files are formatted correctly → passes ✅
3536
- If any file has formatting issues → lists unformatted files and fails ❌
37+
- If a newer open-java-format release is out → warns in the job log, and raises a [code scanning alert](#code-scanning) when the job allows it
3638

3739
Files are checked in the open-java-format style, the formatter's only style. When the check fails, fix locally with a native binary or the runnable jar from the [open-java-format releases](https://github.com/openjavaformat/open-java-format/releases):
3840

@@ -86,6 +88,38 @@ jobs:
8688
mode: ${{ github.event_name == 'push' && 'all' || 'changed' }}
8789
```
8890

91+
## Code scanning
92+
93+
The action compares the `version` it runs with the latest [open-java-format release](https://github.com/openjavaformat/open-java-format/releases) and warns in the job log when a newer one is out. A log warning is easy to miss, so the check is also reported to code scanning: the action uploads a SARIF file with one alert while the version is behind, and with none once it is current, which closes the alert. The alert shows up under **Security → Code scanning** and in the **Code scanning results** check on pull requests, and points at the line of the workflow that names the version.
94+
95+
The upload needs the `security-events: write` permission, which a job gets like this:
96+
97+
```yaml
98+
jobs:
99+
format:
100+
runs-on: ubuntu-latest
101+
permissions:
102+
contents: read
103+
pull-requests: read # mode: changed reads the files of the pull request
104+
security-events: write # the action reports its version check to code scanning
105+
steps:
106+
- uses: actions/checkout@v7
107+
108+
- uses: openjavaformat/open-java-format-action@v2
109+
with:
110+
version: '2.98.0.6'
111+
```
112+
113+
Without the permission the upload is skipped with a line in the log, the check passes as before, and the log warning stays. Code scanning is free on public repositories; a private repository needs [GitHub Code Security](https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/enabling-features-for-your-repository/managing-security-and-analysis-settings-for-your-repository) enabled. Pull requests from forks run with a read-only token and get the log warning only. To stop the upload altogether, set `code-scanning: false`.
114+
115+
One report per job is enough. When the job runs on a matrix, enable it on one leg only:
116+
117+
```yaml
118+
with:
119+
version: '2.98.0.6'
120+
code-scanning: ${{ matrix.os == 'ubuntu-latest' }}
121+
```
122+
89123
## Git pre-commit hook
90124

91125
You can also use the same formatter as a local git hook to catch formatting issues before they reach CI.

‎action.yml‎

Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,10 @@ inputs:
1515
description: 'Which files to check: "changed" for files from PR/push, "all" for every .java file'
1616
required: false
1717
default: 'changed'
18+
code-scanning:
19+
description: 'Report the version check to code scanning as well: "true" raises an alert under Security → Code scanning while a newer open-java-format is out, "false" keeps the check to the log warning. The upload needs the security-events: write permission and is skipped without it.'
20+
required: false
21+
default: 'true'
1822

1923
runs:
2024
using: 'composite'
@@ -52,6 +56,7 @@ runs:
5256
echo "binary=$BINARY" >> "$GITHUB_OUTPUT"
5357
5458
- name: Check for newer open-java-format version
59+
id: latest
5560
shell: bash
5661
env:
5762
GH_TOKEN: ${{ github.token }}
@@ -70,12 +75,150 @@ runs:
7075
https://api.github.com/repos/openjavaformat/open-java-format/releases/latest 2>/dev/null \
7176
| jq -r '.tag_name // empty' 2>/dev/null || true)
7277
78+
echo "version=$LATEST" >> "$GITHUB_OUTPUT"
7379
if [ -z "$LATEST" ]; then
7480
echo "Could not check the latest open-java-format version — skipping this check."
7581
elif [ "$LATEST" != "$VERSION" ]; then
7682
echo "::warning::A newer open-java-format version is available: $LATEST (you are using $VERSION)"
7783
fi
7884
85+
- name: Report the version check to code scanning
86+
if: inputs.code-scanning == 'true' && steps.latest.outputs.version != ''
87+
shell: bash
88+
env:
89+
GH_TOKEN: ${{ github.token }}
90+
VERSION: ${{ inputs.version }}
91+
LATEST: ${{ steps.latest.outputs.version }}
92+
run: |
93+
# A log warning is easy to miss, so the check is also handed to code scanning: a SARIF
94+
# file with one alert while a newer release is out, and with none once the version is
95+
# current, which closes the alert. The alert sits on the line of this repository's
96+
# workflow that names the version, so that it points at what to change. The upload goes
97+
# straight to the REST API: github/codeql-action/upload-sarif fails the job when the
98+
# permission is missing, and this must stay best-effort, like the check itself — nothing
99+
# here may fail the build.
100+
case "$GITHUB_REF" in
101+
refs/heads/*|refs/tags/*|refs/pull/*) ;;
102+
*) echo "Code scanning takes no results for ref '$GITHUB_REF' — not reporting the version check."; exit 0 ;;
103+
esac
104+
105+
# owner/repo/.github/workflows/ci.yml@refs/heads/main -> .github/workflows/ci.yml
106+
WORKFLOW="${GITHUB_WORKFLOW_REF#"$GITHUB_REPOSITORY/"}"
107+
WORKFLOW="${WORKFLOW%@*}"
108+
109+
RESULTS='[]'
110+
if [ "$LATEST" != "$VERSION" ]; then
111+
# The first line naming the version: in the workflow that runs this job, then in any
112+
# other workflow of the repository (a reusable one, say). Failing that, the line that
113+
# uses the action, then the top of the workflow.
114+
FILE=""
115+
LINE=""
116+
PATTERN="(^|[^0-9.])${VERSION//./\\.}([^0-9.]|$)"
117+
while IFS= read -r CANDIDATE; do
118+
[ -f "$CANDIDATE" ] || continue
119+
HIT=$(grep -nE -m1 "$PATTERN" "$CANDIDATE" | cut -d: -f1 || true)
120+
if [ -n "$HIT" ]; then
121+
FILE="$CANDIDATE"
122+
LINE="$HIT"
123+
break
124+
fi
125+
done < <(printf '%s\n' "$WORKFLOW"; find .github/workflows -maxdepth 1 \( -name '*.yml' -o -name '*.yaml' \) 2>/dev/null | sort)
126+
if [ -z "$FILE" ] && [ -f "$WORKFLOW" ]; then
127+
FILE="$WORKFLOW"
128+
LINE=$(grep -nE -m1 'uses:.*open-java-format-action' "$FILE" | cut -d: -f1 || true)
129+
LINE="${LINE:-1}"
130+
fi
131+
if [ -z "$FILE" ]; then
132+
echo "No workflow file to put the alert on — not reporting the version check."
133+
exit 0
134+
fi
135+
136+
# Code scanning tells alerts apart by this fingerprint, not by the line number. It is
137+
# made from the line's text without whitespace, so the alert survives the line moving.
138+
TEXT=$(sed -n "${LINE}p" "$FILE" | tr -d '\r')
139+
FINGERPRINT=$(printf '%s' "${TEXT//[[:space:]]/}" | git hash-object --stdin)
140+
RESULTS=$(jq -n \
141+
--arg text "open-java-format $LATEST is available; this workflow checks formatting with $VERSION." \
142+
--arg file "$FILE" --argjson line "$LINE" --argjson end "$(( ${#TEXT} + 1 ))" \
143+
--arg fingerprint "${FINGERPRINT:0:16}:1" \
144+
'[{
145+
ruleId: "outdated-version",
146+
level: "warning",
147+
message: {text: $text},
148+
locations: [{physicalLocation: {
149+
artifactLocation: {uri: $file},
150+
region: {startLine: $line, startColumn: 1, endLine: $line, endColumn: $end}
151+
}}],
152+
partialFingerprints: {primaryLocationLineHash: $fingerprint}
153+
}]')
154+
fi
155+
156+
HELP="$RUNNER_TEMP/open-java-format-version-help.md"
157+
cat > "$HELP" <<'HELP_END'
158+
A newer [open-java-format release](https://github.com/openjavaformat/open-java-format/releases) is out than the one this workflow checks formatting with. Releases fix formatting bugs, so code that passes with the pinned version may be formatted differently by the newer one.
159+
160+
To update:
161+
162+
- set the `version` input of `openjavaformat/open-java-format-action` to the new release;
163+
- if the repository uses the pre-commit hook, set `FORMATTER_VERSION` at the top of it to the same release.
164+
165+
The alert closes on its own once the workflow runs with the latest release.
166+
HELP_END
167+
168+
# One category per workflow and job, so that uploads from different jobs do not replace
169+
# one another: open-java-format-action/ci.yml/format/
170+
CATEGORY="open-java-format-action/${WORKFLOW:+${WORKFLOW##*/}/}${GITHUB_JOB:-job}/"
171+
SARIF=$(jq -n --rawfile help "$HELP" --arg category "$CATEGORY" --argjson results "$RESULTS" '{
172+
"$schema": "https://json.schemastore.org/sarif-2.1.0.json",
173+
version: "2.1.0",
174+
runs: [{
175+
tool: {driver: {
176+
name: "open-java-format-action",
177+
informationUri: "https://github.com/openjavaformat/open-java-format-action",
178+
rules: [{
179+
id: "outdated-version",
180+
name: "OutdatedVersion",
181+
shortDescription: {text: "A newer open-java-format release is available"},
182+
fullDescription: {text: "The workflow checks formatting with an open-java-format version older than the latest release. Releases fix formatting bugs, so code that passes with the pinned version may be formatted differently by the newer one."},
183+
help: {text: $help, markdown: $help},
184+
defaultConfiguration: {level: "warning"},
185+
properties: {tags: ["maintainability"], precision: "very-high", "problem.severity": "warning"}
186+
}]
187+
}},
188+
automationDetails: {id: $category},
189+
results: $results
190+
}]
191+
}')
192+
193+
RESPONSE="$RUNNER_TEMP/open-java-format-code-scanning-response.json"
194+
STATUS=$(jq -n --arg sha "$GITHUB_SHA" --arg ref "$GITHUB_REF" \
195+
--arg started "$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
196+
--arg sarif "$(printf '%s' "$SARIF" | gzip -c | base64 | tr -d '\n')" \
197+
'{commit_sha: $sha, ref: $ref, started_at: $started, tool_name: "open-java-format-action", sarif: $sarif}' \
198+
| curl -sS -o "$RESPONSE" -w '%{http_code}' -X POST \
199+
-H "Authorization: Bearer $GH_TOKEN" -H "Accept: application/vnd.github+json" \
200+
-H "X-GitHub-Api-Version: 2022-11-28" --data-binary @- \
201+
"${GITHUB_API_URL:-https://api.github.com}/repos/$GITHUB_REPOSITORY/code-scanning/sarifs" \
202+
|| true)
203+
STATUS="${STATUS:-000}"
204+
case "$STATUS" in
205+
202)
206+
if [ "$RESULTS" = "[]" ]; then
207+
echo "Reported to code scanning: open-java-format $VERSION is the latest release."
208+
else
209+
echo "Reported to code scanning: an alert on $FILE:$LINE, open-java-format $LATEST is available."
210+
fi
211+
;;
212+
403|404)
213+
# No permission, no GitHub Code Security on a private repository, or the read-only
214+
# token of a pull request from a fork. The log warning above stays.
215+
echo "Code scanning did not accept the version check (HTTP $STATUS: $(jq -r '.message // empty' "$RESPONSE" 2>/dev/null)). To get it as a code scanning alert, give the job the 'security-events: write' permission; a private repository also needs GitHub Code Security. Set the code-scanning input to 'false' to stop these uploads."
216+
;;
217+
*)
218+
echo "::warning::Could not report the version check to code scanning (HTTP $STATUS): $(cat "$RESPONSE" 2>/dev/null)"
219+
;;
220+
esac
221+
79222
- name: Cache open-java-format binary
80223
id: cache
81224
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0

‎example-usage.yml‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@ jobs:
1010
format-check:
1111
name: Check Java formatting
1212
runs-on: ubuntu-latest
13+
permissions:
14+
contents: read
15+
pull-requests: read # mode: changed reads the files of the pull request
16+
security-events: write # the action reports its version check to code scanning
1317

1418
steps:
1519
- uses: actions/checkout@v7

0 commit comments

Comments
 (0)