Skip to content

Commit 2ddca8e

Browse files
committed
ci: add JSON validation workflow
1 parent 6413f03 commit 2ddca8e

1 file changed

Lines changed: 121 additions & 0 deletions

File tree

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
---
2+
name: Validate JSON
3+
on:
4+
# pull_request_target (not pull_request) so the token can comment on fork PRs.
5+
# The PR's own code is never executed: it is checked out into ./pr as data only,
6+
# and parsed by the validator script from the trusted base checkout.
7+
pull_request_target:
8+
types: [opened, synchronize, reopened]
9+
branches:
10+
- main
11+
- dev
12+
paths:
13+
- "Config/**/*.json"
14+
- "Tools/**/*.json"
15+
- "AddMSPApp/**/*.json"
16+
- ".github/workflows/Validate_JSON.yml"
17+
- ".github/scripts/validate-json.mjs"
18+
push:
19+
branches:
20+
- dev
21+
paths:
22+
- "Config/**/*.json"
23+
- "Tools/**/*.json"
24+
- "AddMSPApp/**/*.json"
25+
- ".github/workflows/Validate_JSON.yml"
26+
- ".github/scripts/validate-json.mjs"
27+
concurrency:
28+
group: ${{ github.workflow }}-${{ github.event_name == 'pull_request_target' && github.event.pull_request.number || github.ref }}
29+
cancel-in-progress: true
30+
permissions:
31+
contents: read
32+
pull-requests: write
33+
jobs:
34+
validate:
35+
name: Parse JSON in Config, Tools and AddMSPApp
36+
runs-on: ubuntu-latest
37+
steps:
38+
- name: Checkout base (trusted validator script)
39+
uses: actions/checkout@v6
40+
41+
- name: Checkout PR head (untrusted, data only)
42+
if: github.event_name == 'pull_request_target'
43+
uses: actions/checkout@v6
44+
with:
45+
repository: ${{ github.event.pull_request.head.repo.full_name }}
46+
ref: ${{ github.event.pull_request.head.sha }}
47+
path: pr
48+
persist-credentials: false
49+
50+
- name: Validate JSON files
51+
id: validate
52+
continue-on-error: true
53+
env:
54+
ROOT: ${{ github.event_name == 'pull_request_target' && 'pr/' || '' }}
55+
run: >-
56+
node .github/scripts/validate-json.mjs --strip "$ROOT"
57+
"${ROOT}Config" "${ROOT}Tools" "${ROOT}AddMSPApp"
58+
59+
- name: Comment on PR
60+
if: github.event_name == 'pull_request_target'
61+
uses: actions/github-script@v9
62+
with:
63+
github-token: ${{ secrets.GITHUB_TOKEN }}
64+
script: |
65+
const fs = require('fs');
66+
const marker = '<!-- validate-json -->';
67+
const resultsFile = 'json-validation-results.json';
68+
if (!fs.existsSync(resultsFile)) {
69+
// The validator crashed before reporting; let its own error stand.
70+
core.warning('No validation results found — skipping PR comment.');
71+
return;
72+
}
73+
const failures = JSON.parse(fs.readFileSync(resultsFile, 'utf8'));
74+
75+
// Find a previous comment from this workflow so we update instead of piling up.
76+
const { data: comments } = await github.rest.issues.listComments({
77+
...context.repo,
78+
issue_number: context.issue.number,
79+
per_page: 100,
80+
});
81+
const existing = comments.find(
82+
(c) => c.user.type === 'Bot' && c.body.includes(marker)
83+
);
84+
85+
let body;
86+
if (failures.length > 0) {
87+
const list = failures
88+
.map(({ file, message }) => `- \`${file}\`\n > ${message}`)
89+
.join('\n');
90+
body =
91+
`${marker}\n### ⚠️ Invalid JSON detected\n\n` +
92+
`${failures.length} JSON file(s) in this PR could not be parsed. ` +
93+
`These files are loaded directly by CIPP, so a syntax error here breaks the app at runtime.\n\n` +
94+
`${list}\n\n` +
95+
`Please fix the syntax and push again — this comment will update automatically.`;
96+
} else if (existing) {
97+
body = `${marker}\n### ✅ JSON is valid\n\nAll JSON files in \`Config\`, \`Tools\` and \`AddMSPApp\` parse correctly. Thanks for fixing it!`;
98+
} else {
99+
// Nothing was ever broken — stay quiet.
100+
return;
101+
}
102+
103+
if (existing) {
104+
await github.rest.issues.updateComment({
105+
...context.repo,
106+
comment_id: existing.id,
107+
body,
108+
});
109+
} else {
110+
await github.rest.issues.createComment({
111+
...context.repo,
112+
issue_number: context.issue.number,
113+
body,
114+
});
115+
}
116+
117+
- name: Fail if any JSON is invalid
118+
if: steps.validate.outputs.invalid_count != '0'
119+
run: |
120+
echo "::error::${{ steps.validate.outputs.invalid_count }} invalid JSON file(s). See annotations above."
121+
exit 1

0 commit comments

Comments
 (0)