Skip to content

Commit 4efb078

Browse files
dbejarano820claude
andcommitted
feat: add CI/CD pipeline with shellcheck and JSON validation
GitHub Actions workflow for automated validation: - shellcheck on all bash scripts - JSON syntax validation for data files - Schema checks for concept-tree and quiz-bank - Concept coverage comparison Closes DOJ-2447 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent b96a272 commit 4efb078

File tree

2 files changed

+140
-0
lines changed

2 files changed

+140
-0
lines changed

.github/workflows/validate.yml

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
name: Validate
2+
3+
on:
4+
push:
5+
branches: [main]
6+
pull_request:
7+
branches: [main]
8+
9+
jobs:
10+
shellcheck:
11+
name: Shellcheck
12+
runs-on: ubuntu-latest
13+
steps:
14+
- uses: actions/checkout@v4
15+
16+
- name: Install shellcheck
17+
run: sudo apt-get update && sudo apt-get install -y shellcheck
18+
19+
- name: Run shellcheck on scripts
20+
run: |
21+
shellcheck scripts/*.sh
22+
23+
- name: Run shellcheck on lib scripts (if any)
24+
run: |
25+
if ls scripts/lib/*.sh 2>/dev/null | grep -q .; then
26+
shellcheck scripts/lib/*.sh
27+
else
28+
echo "No scripts/lib/*.sh files found, skipping."
29+
fi
30+
31+
json-validation:
32+
name: JSON Validation
33+
runs-on: ubuntu-latest
34+
steps:
35+
- uses: actions/checkout@v4
36+
37+
- name: Validate data JSON files
38+
run: |
39+
for f in data/*.json; do
40+
echo "Validating $f"
41+
jq . "$f" > /dev/null
42+
done
43+
44+
- name: Validate plugin manifest files
45+
run: |
46+
for f in .claude-plugin/plugin.json .claude-plugin/marketplace.json hooks/hooks.json; do
47+
echo "Validating $f"
48+
jq . "$f" > /dev/null
49+
done
50+
51+
schema-validation:
52+
name: Schema Validation
53+
runs-on: ubuntu-latest
54+
steps:
55+
- uses: actions/checkout@v4
56+
57+
- name: Verify concept-tree.json has .categories array
58+
run: |
59+
result=$(jq '.categories | type' data/concept-tree.json)
60+
if [ "$result" != '"array"' ]; then
61+
echo "ERROR: concept-tree.json .categories is not an array (got $result)"
62+
exit 1
63+
fi
64+
echo "concept-tree.json .categories is a valid array"
65+
66+
- name: Verify quiz-bank.json is a non-empty object
67+
run: |
68+
type=$(jq 'type' data/quiz-bank.json)
69+
if [ "$type" != '"object"' ]; then
70+
echo "ERROR: quiz-bank.json is not an object (got $type)"
71+
exit 1
72+
fi
73+
keys=$(jq 'keys | length' data/quiz-bank.json)
74+
if [ "$keys" -eq 0 ]; then
75+
echo "ERROR: quiz-bank.json is an empty object"
76+
exit 1
77+
fi
78+
echo "quiz-bank.json is a valid non-empty object with $keys top-level keys"
79+
80+
- name: Verify plugin.json has required fields
81+
run: |
82+
name=$(jq -r '.name // empty' .claude-plugin/plugin.json)
83+
version=$(jq -r '.version // empty' .claude-plugin/plugin.json)
84+
if [ -z "$name" ]; then
85+
echo "ERROR: plugin.json is missing required field: name"
86+
exit 1
87+
fi
88+
if [ -z "$version" ]; then
89+
echo "ERROR: plugin.json is missing required field: version"
90+
exit 1
91+
fi
92+
echo "plugin.json has name='$name' and version='$version'"
93+
94+
concept-coverage:
95+
name: Concept Coverage Check
96+
runs-on: ubuntu-latest
97+
steps:
98+
- uses: actions/checkout@v4
99+
100+
- name: Compare quiz-bank keys vs concept-tree concept keys
101+
run: |
102+
echo "--- Quiz bank top-level keys ---"
103+
quiz_keys=$(jq -r 'keys[]' data/quiz-bank.json | sort)
104+
echo "$quiz_keys"
105+
106+
echo ""
107+
echo "--- Concept tree concept IDs ---"
108+
concept_keys=$(jq -r '.categories[].concepts[].id' data/concept-tree.json 2>/dev/null | sort || \
109+
jq -r '.categories[] | .concepts // [] | .[].id' data/concept-tree.json 2>/dev/null | sort || \
110+
echo "")
111+
echo "$concept_keys"
112+
113+
echo ""
114+
echo "--- Concepts in quiz-bank but not in concept-tree ---"
115+
only_in_quiz=$(comm -23 <(echo "$quiz_keys") <(echo "$concept_keys"))
116+
if [ -n "$only_in_quiz" ]; then
117+
echo "WARNING: The following quiz-bank keys have no matching concept in concept-tree:"
118+
echo "$only_in_quiz"
119+
else
120+
echo "None"
121+
fi
122+
123+
echo ""
124+
echo "--- Concepts in concept-tree but not in quiz-bank ---"
125+
only_in_tree=$(comm -13 <(echo "$quiz_keys") <(echo "$concept_keys"))
126+
if [ -n "$only_in_tree" ]; then
127+
echo "WARNING: The following concept-tree concepts have no quiz-bank coverage:"
128+
echo "$only_in_tree"
129+
else
130+
echo "None"
131+
fi
132+
133+
echo ""
134+
echo "Concept coverage check complete (warnings do not fail CI)."

.shellcheckrc

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# CodeSensei shell script conventions
2+
# Allow sourcing from dynamic paths
3+
external-sources=true
4+
# Common patterns we use
5+
disable=SC2034 # Unused variables (used by sourcing scripts)
6+
disable=SC2155 # Declare and assign separately (too noisy for our style)

0 commit comments

Comments
 (0)