-
Notifications
You must be signed in to change notification settings - Fork 2
248 lines (223 loc) · 11.3 KB
/
Copy pathci-quality-gates.yml
File metadata and controls
248 lines (223 loc) · 11.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
name: Quality
on:
workflow_call:
inputs:
should_skip:
description: "Whether to skip the workflow"
required: false
type: string
default: "false"
# Path-based change detection inputs for selective execution
webapp_changed:
description: "Whether webapp files changed (or should run for other reasons)"
required: false
type: string
default: "true"
application_server_changed:
description: "Whether application-server files changed (or should run for other reasons)"
required: false
type: string
default: "true"
jobs:
quality-gates:
name: "${{ matrix.display-name }}"
runs-on: ubuntu-latest
if: inputs.should_skip != 'true'
timeout-minutes: ${{ matrix.check == 'database-validation' && 20 || 15 }}
strategy:
fail-fast: false
matrix:
include:
- check: application-server-quality
display-name: "App Server"
- check: webapp-quality
display-name: "Webapp"
- check: openapi-validation
display-name: "OpenAPI"
- check: database-validation
display-name: "Database"
steps:
- name: Determine if check should run
id: should_run
run: |
case "${{ matrix.check }}" in
"webapp-quality")
echo "run=${{ inputs.webapp_changed }}" >> $GITHUB_OUTPUT
;;
"application-server-quality"|"openapi-validation"|"database-validation")
echo "run=${{ inputs.application_server_changed }}" >> $GITHUB_OUTPUT
;;
*)
# Unknown check type - fail explicitly to catch missing case statements
echo "::error::Unknown check type '${{ matrix.check }}' in ci-quality-gates.yml matrix."
echo "::error::Please add a case for this check in the 'Determine if check should run' step."
echo "run=false" >> $GITHUB_OUTPUT
exit 1
;;
esac
- name: Skip notification
if: steps.should_run.outputs.run != 'true'
run: echo "⏭️ Skipping ${{ matrix.check }} - no relevant files changed"
- name: Checkout repository
if: steps.should_run.outputs.run == 'true'
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
with:
# Database checks need full history for git diff on schema/migrations
# Other quality checks only need HEAD for faster checkout
fetch-depth: ${{ matrix.check == 'database-validation' && 0 || 1 }}
- name: Setup pnpm + Node.js
if: steps.should_run.outputs.run == 'true'
uses: ./.github/actions/setup-pnpm-node
- name: Setup caches
if: steps.should_run.outputs.run == 'true'
uses: ./.github/actions/setup-caches
with:
cache-type: ${{ matrix.check }}
os: ${{ runner.os }}
- name: Application-server formatting + Pi extension typecheck + runner smoke
if: steps.should_run.outputs.run == 'true' && matrix.check == 'application-server-quality'
run: |
pnpm install --frozen-lockfile
ISSUES_FOUND=()
FMT_OK=true; TS_OK=true; RUNNER_OK=true
pnpm run format:java:check || { FMT_OK=false; ISSUES_FOUND+=("Java formatting failed. Run: pnpm run format:java"); }
pnpm --filter @hephaestus/agent-extensions run typecheck \
|| { TS_OK=false; ISSUES_FOUND+=("Pi extension TS failed typecheck against @earendil-works/pi-coding-agent. Run: pnpm --filter @hephaestus/agent-extensions run typecheck"); }
node --test server/src/test/resources/agent/pi-mentor-runner.spec.mjs \
|| { RUNNER_OK=false; ISSUES_FOUND+=("Pi mentor runner smoke test failed. Run: node --test server/src/test/resources/agent/pi-mentor-runner.spec.mjs"); }
if [ ${#ISSUES_FOUND[@]} -gt 0 ]; then
echo "### App Server Quality" >> $GITHUB_STEP_SUMMARY
echo "| Check | Status | Fix |" >> $GITHUB_STEP_SUMMARY
echo "|-------|--------|-----|" >> $GITHUB_STEP_SUMMARY
[[ "$FMT_OK" == "false" ]] && echo "| Java formatting | :x: Failed | \`pnpm run format:java\` |" >> $GITHUB_STEP_SUMMARY
[[ "$TS_OK" == "false" ]] && echo "| Pi extension TS | :x: Failed | \`pnpm --filter @hephaestus/agent-extensions run typecheck\` |" >> $GITHUB_STEP_SUMMARY
[[ "$RUNNER_OK" == "false" ]] && echo "| Pi mentor runner smoke | :x: Failed | \`node --test server/src/test/resources/agent/pi-mentor-runner.spec.mjs\` |" >> $GITHUB_STEP_SUMMARY
for issue in "${ISSUES_FOUND[@]}"; do echo "::error::$issue"; done
exit 1
fi
- name: Webapp quality
if: steps.should_run.outputs.run == 'true' && matrix.check == 'webapp-quality'
working-directory: ./webapp
run: |
cd ..
pnpm install --frozen-lockfile
cd webapp
ISSUES_FOUND=()
BIOME_OK=true
TYPES_OK=true
pnpm run check || { BIOME_OK=false; ISSUES_FOUND+=("Biome checks failed. Run: cd webapp && pnpm run check -- --write"); }
pnpm run typecheck || { TYPES_OK=false; ISSUES_FOUND+=("TypeScript errors found. Run: cd webapp && pnpm run typecheck"); }
if [ ${#ISSUES_FOUND[@]} -gt 0 ]; then
echo "### Webapp Quality" >> $GITHUB_STEP_SUMMARY
echo "| Check | Status | Fix |" >> $GITHUB_STEP_SUMMARY
echo "|-------|--------|-----|" >> $GITHUB_STEP_SUMMARY
[[ "$BIOME_OK" == "false" ]] && echo "| Biome (lint + format) | :x: Failed | \`cd webapp && pnpm run check -- --write\` |" >> $GITHUB_STEP_SUMMARY
[[ "$TYPES_OK" == "false" ]] && echo "| TypeScript | :x: Failed | \`cd webapp && pnpm run typecheck\` |" >> $GITHUB_STEP_SUMMARY
for issue in "${ISSUES_FOUND[@]}"; do echo "::error::$issue"; done
exit 1
fi
- name: OpenAPI validation
if: steps.should_run.outputs.run == 'true' && matrix.check == 'openapi-validation'
env:
DATABASE_URL: postgresql://fake:fake@localhost:5432/fake
MODEL_NAME: fake:model
run: |
pnpm install --frozen-lockfile
cd server
mvn compile -DskipTests --quiet -Dmaven.build.cache.enabled=true
cd ..
pnpm run generate:api
git add .
if ! git diff --cached --quiet; then
echo "::error::OpenAPI out of sync. Run: pnpm run generate:api"
echo "### OpenAPI Validation" >> $GITHUB_STEP_SUMMARY
echo "| Check | Status | Fix |" >> $GITHUB_STEP_SUMMARY
echo "|-------|--------|-----|" >> $GITHUB_STEP_SUMMARY
echo "| API specs sync | :x: Out of sync | \`pnpm run generate:api\` |" >> $GITHUB_STEP_SUMMARY
exit 1
fi
- name: Database validation (schema + ERD + models)
if: steps.should_run.outputs.run == 'true' && matrix.check == 'database-validation'
run: |
# Install dependencies (pnpm + platform binaries needed for models check)
pnpm install --frozen-lockfile
# Start a single shared Postgres instance for all DB checks
docker run -d --name postgres-db \
-e POSTGRES_DB=hephaestus -e POSTGRES_PASSWORD=root -e POSTGRES_USER=root \
-p 5432:5432 postgres:16
# Guarantee cleanup regardless of how the step exits
trap 'docker stop postgres-db 2>/dev/null; docker rm postgres-db 2>/dev/null' EXIT
until docker exec postgres-db pg_isready -U root -d hephaestus; do sleep 2; done
export SPRING_DATASOURCE_URL="jdbc:postgresql://localhost:5432/hephaestus"
export SPRING_DATASOURCE_USERNAME="root"
export SPRING_DATASOURCE_PASSWORD="root"
# Compile application-server once (shared across all checks)
if ! (cd server && mvn compile -DskipTests --quiet -Dmaven.build.cache.enabled=true); then
echo "::error::Maven compilation failed. This blocks all database validation checks. Fix compilation errors first."
echo "### Database Validation" >> $GITHUB_STEP_SUMMARY
echo "| Check | Status | Fix |" >> $GITHUB_STEP_SUMMARY
echo "|-------|--------|-----|" >> $GITHUB_STEP_SUMMARY
echo "| Maven compile | :x: Failed | Fix compilation errors in \`server\` |" >> $GITHUB_STEP_SUMMARY
exit 1
fi
ISSUES_FOUND=()
# Check 1: Schema drift
scripts/db-utils.sh draft-changelog || ISSUES_FOUND+=("Schema check script failed. Run: pnpm run db:draft-changelog")
if [ -f "server/src/main/resources/db/changelog_new.xml" ]; then
echo "::error::Schema drift detected. Run: pnpm run db:draft-changelog"
cat server/src/main/resources/db/changelog_new.xml
ISSUES_FOUND+=("Schema drift detected")
fi
# Check 2: ERD documentation
scripts/db-utils.sh generate-erd || ISSUES_FOUND+=("ERD generation failed. Run: pnpm run db:generate-erd-docs")
git add docs/contributor/erd/schema.mmd
if ! git diff --cached --quiet docs/contributor/erd/schema.mmd; then
echo "::error::ERD outdated. Run: pnpm run db:generate-erd-docs"
ISSUES_FOUND+=("ERD outdated")
fi
if [ ${#ISSUES_FOUND[@]} -gt 0 ]; then
echo "### Database Validation" >> $GITHUB_STEP_SUMMARY
echo "| Check | Status | Fix |" >> $GITHUB_STEP_SUMMARY
echo "|-------|--------|-----|" >> $GITHUB_STEP_SUMMARY
for issue in "${ISSUES_FOUND[@]}"; do
echo "::error::$issue"
# Extract fix command from "...Run: <command>" format, or show issue directly
if [[ "$issue" == *"Run:"* ]]; then
echo "| ${issue%%.*} | :x: Failed | \`${issue##*Run: }\` |" >> $GITHUB_STEP_SUMMARY
else
echo "| $issue | :x: Failed | See logs |" >> $GITHUB_STEP_SUMMARY
fi
done
exit 1
fi
# Needle built at runtime so this guard file doesn't self-match.
legacy-cleanup-guard:
name: "No legacy agent image"
if: inputs.should_skip != 'true'
runs-on: ubuntu-latest
timeout-minutes: 2
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
with:
fetch-depth: 1
- name: Assert no legacy agent-image references
run: |
set -euo pipefail
NEEDLE="agent-$(printf 'open\x63ode')"
if grep -rEnI "$NEEDLE" \
--exclude-dir=node_modules --exclude-dir=target --exclude-dir=.git \
. \
| grep -vE '^\./(MIGRATION\.md|docs/admin/agent-image-digests\.md):' ; then
echo "::error::Legacy agent image is removed in issue #1076; relocate any new references to MIGRATION.md."
exit 1
fi
- name: Assert no legacy agent-image-pin.env references
run: |
set -euo pipefail
if grep -rEnI 'docker/agent-image-pin(\.local)?\.env' \
--exclude-dir=node_modules --exclude-dir=target --exclude-dir=.git \
. \
| grep -vE '^\./(MIGRATION\.md|docs/admin/agent-image-digests\.md):' ; then
echo "::error::The legacy pin file was removed in v0.10.0; new references must live in MIGRATION.md only."
exit 1
fi