Skip to content

Commit f6936af

Browse files
committed
ci: Add clang-tidy comments for PR's
There are two seperate actions: One for running clang-tidy with no secrets, and another with secrets. This ensures that if the former is compromised, the latter secrets should be safe. Signed-off-by: Urmas Rist <urmas@leil.io>
1 parent 58fe69f commit f6936af

2 files changed

Lines changed: 132 additions & 0 deletions

File tree

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
# Secure workflow with access to repository secrets and GitHub token for posting analysis results
2+
name: Post the static analysis results
3+
4+
on:
5+
workflow_run:
6+
workflows: [ "Static analysis" ]
7+
types: [ completed ]
8+
9+
jobs:
10+
clang-tidy-results:
11+
# Trigger the job only if the previous (insecure) workflow completed successfully
12+
if: ${{ github.event.workflow_run.event == 'pull_request' && github.event.workflow_run.conclusion == 'success' }}
13+
runs-on: ubuntu-22.04
14+
permissions:
15+
pull-requests: write
16+
# OPTIONAL: auto-closing conversations requires the `contents` permission
17+
contents: write
18+
steps:
19+
- name: Download analysis results
20+
uses: actions/github-script@v7
21+
with:
22+
script: |
23+
const artifacts = await github.rest.actions.listWorkflowRunArtifacts({
24+
owner: context.repo.owner,
25+
repo: context.repo.repo,
26+
run_id: ${{ github.event.workflow_run.id }},
27+
});
28+
const matchArtifact = artifacts.data.artifacts.filter((artifact) => {
29+
return artifact.name == "clang-tidy-result"
30+
})[0];
31+
const download = await github.rest.actions.downloadArtifact({
32+
owner: context.repo.owner,
33+
repo: context.repo.repo,
34+
artifact_id: matchArtifact.id,
35+
archive_format: "zip",
36+
});
37+
const fs = require("fs");
38+
fs.writeFileSync("${{ github.workspace }}/clang-tidy-result.zip", Buffer.from(download.data));
39+
- name: Extract analysis results
40+
run: |
41+
mkdir clang-tidy-result
42+
unzip -j clang-tidy-result.zip -d clang-tidy-result
43+
- name: Set environment variables
44+
uses: actions/github-script@v7
45+
with:
46+
script: |
47+
const assert = require("node:assert").strict;
48+
const fs = require("fs");
49+
function exportVar(varName, fileName, regEx) {
50+
const val = fs.readFileSync("${{ github.workspace }}/clang-tidy-result/" + fileName, {
51+
encoding: "ascii"
52+
}).trimEnd();
53+
assert.ok(regEx.test(val), "Invalid value format for " + varName);
54+
core.exportVariable(varName, val);
55+
}
56+
exportVar("PR_ID", "pr-id.txt", /^[0-9]+$/);
57+
exportVar("PR_HEAD_REPO", "pr-head-repo.txt", /^[-./0-9A-Z_a-z]+$/);
58+
exportVar("PR_HEAD_SHA", "pr-head-sha.txt", /^[0-9A-Fa-f]+$/);
59+
- uses: actions/checkout@v4
60+
with:
61+
repository: ${{ env.PR_HEAD_REPO }}
62+
ref: ${{ env.PR_HEAD_SHA }}
63+
persist-credentials: false
64+
- name: Redownload analysis results
65+
uses: actions/github-script@v7
66+
with:
67+
script: |
68+
const artifacts = await github.rest.actions.listWorkflowRunArtifacts({
69+
owner: context.repo.owner,
70+
repo: context.repo.repo,
71+
run_id: ${{ github.event.workflow_run.id }},
72+
});
73+
const matchArtifact = artifacts.data.artifacts.filter((artifact) => {
74+
return artifact.name == "clang-tidy-result"
75+
})[0];
76+
const download = await github.rest.actions.downloadArtifact({
77+
owner: context.repo.owner,
78+
repo: context.repo.repo,
79+
artifact_id: matchArtifact.id,
80+
archive_format: "zip",
81+
});
82+
const fs = require("fs");
83+
fs.writeFileSync("${{ github.workspace }}/clang-tidy-result.zip", Buffer.from(download.data));
84+
- name: Extract analysis results
85+
run: |
86+
mkdir clang-tidy-result
87+
unzip -j clang-tidy-result.zip -d clang-tidy-result
88+
- name: Run clang-tidy-pr-comments action
89+
uses: platisd/clang-tidy-pr-comments@v1
90+
with:
91+
github_token: ${{ secrets.GITHUB_TOKEN }}
92+
clang_tidy_fixes: clang-tidy-result/fixes.yml
93+
pull_request_id: ${{ env.PR_ID }}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
# Insecure workflow with limited permissions that should provide analysis results through an artifact
2+
name: Static analysis
3+
4+
on: pull_request
5+
6+
jobs:
7+
clang-tidy:
8+
runs-on: ubuntu-22.04
9+
steps:
10+
- uses: actions/checkout@v4
11+
with:
12+
ref: ${{ github.event.pull_request.head.sha }}
13+
fetch-depth: 0
14+
- name: Fetch base branch
15+
run: |
16+
git remote add upstream "https://github.com/${{ github.event.pull_request.base.repo.full_name }}"
17+
git fetch --no-tags --no-recurse-submodules upstream "${{ github.event.pull_request.base.ref }}"
18+
- name: Install clang-tidy
19+
run: |
20+
sudo apt-get update
21+
sudo apt-get install -y clang-tidy
22+
- name: Prepare compile_commands.json
23+
run: |
24+
cmake -B build -DCMAKE_EXPORT_COMPILE_COMMANDS=ON
25+
- name: Create results directory
26+
run: |
27+
mkdir clang-tidy-result
28+
- name: Analyze
29+
run: |
30+
git diff -U0 "$(git merge-base HEAD "upstream/${{ github.event.pull_request.base.ref }}")" | clang-tidy-diff -p1 -path build -export-fixes clang-tidy-result/fixes.yml
31+
- name: Save PR metadata
32+
run: |
33+
echo "${{ github.event.number }}" > clang-tidy-result/pr-id.txt
34+
echo "${{ github.event.pull_request.head.repo.full_name }}" > clang-tidy-result/pr-head-repo.txt
35+
echo "${{ github.event.pull_request.head.sha }}" > clang-tidy-result/pr-head-sha.txt
36+
- uses: actions/upload-artifact@v4
37+
with:
38+
name: clang-tidy-result
39+
path: clang-tidy-result/

0 commit comments

Comments
 (0)