-
Notifications
You must be signed in to change notification settings - Fork 0
364 lines (311 loc) · 12 KB
/
Copy pathci.yml
File metadata and controls
364 lines (311 loc) · 12 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
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
name: CI — Lint & Quality
on:
pull_request:
branches: [main]
push:
branches: [main]
permissions:
contents: read
jobs:
shellcheck:
name: ShellCheck — Bash Scripts
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- name: Install ShellCheck
run: sudo apt-get install -y shellcheck
- name: Find and check shell scripts
run: |
echo "=== Checking shell scripts ==="
find . -name '*.sh' -type f | while read -r script; do
echo "Checking: $script"
shellcheck -x -S warning "$script" || true
done
- name: Strict check (deploy scripts)
run: |
echo "=== Strict check on deploy scripts ==="
ERRORS=0
while IFS= read -r script; do
echo "Checking: $script"
if ! shellcheck -x -S error "$script"; then
ERRORS=$((ERRORS + 1))
fi
done < <(find tools/deploy -name '*.sh' -type f)
while IFS= read -r script; do
echo "Checking: $script"
if ! shellcheck -x -S error "$script"; then
ERRORS=$((ERRORS + 1))
fi
done < <(find tools -maxdepth 1 -name '*.sh' -type f)
if [ "$ERRORS" -gt 0 ]; then
echo "❌ $ERRORS script(s) failed strict check"
exit 1
fi
markdown-lint:
name: Markdown Lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
# No continue-on-error: every markdown file in the repo now lints clean,
# so this is a real gate. It previously could not fail, which is why the
# docs accumulated broken links, mislabelled fences and bad tables.
- uses: DavidAnson/markdownlint-cli2-action@v24
with:
globs: '**/*.md'
config: '.markdownlint.yml'
secrets-scan:
name: Secrets & Credential Scan
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Check for potential secrets
run: |
echo "=== Scanning for potential credential leaks ==="
FOUND=0
# Check for common secret patterns (excluding docs with <PLACEHOLDER> format)
echo "--- Private keys ---"
if grep -rn "PRIVATE KEY" --include='*.sh' --include='*.js' --include='*.env' --include='*.toml' . 2>/dev/null | grep -v 'example\|PLACEHOLDER\|<.*>'; then
echo "⚠️ Potential private key found"
FOUND=1
fi
echo "--- API tokens/passwords in code ---"
if grep -rn "Bearer [A-Za-z0-9_-]\{20,\}" --include='*.sh' --include='*.js' . 2>/dev/null | grep -v 'example\|PLACEHOLDER\|<.*>\|Authorization.*\$'; then
echo "⚠️ Potential API token found"
FOUND=1
fi
echo "--- .env files (should be gitignored) ---"
if find . -name 'vars.env' -o -name '.env' | grep -v example | grep -v node_modules; then
echo "⚠️ .env file found in repo!"
FOUND=1
fi
echo "--- Hardcoded passwords ---"
if grep -rn "password\s*=\s*['\"][^<]" --include='*.sh' --include='*.js' --include='*.toml' . 2>/dev/null | grep -vi 'example\|placeholder\|<.*>\|PLACEHOLDER\|todo\|changeme'; then
echo "⚠️ Potential hardcoded password"
FOUND=1
fi
if [ "$FOUND" -eq 0 ]; then
echo "✅ No secrets detected"
else
echo ""
echo "⚠️ Review the findings above. False positives are possible."
echo "If these are intentional (e.g., documented placeholders), they're safe."
fi
worker-syntax:
name: Worker.js Syntax & Type Check
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/setup-node@v6
with:
node-version: '20'
- name: Install dependencies
run: npm ci --registry https://registry.npmjs.org
- name: TypeScript type-check
run: npx tsc --noEmit
- name: Build worker
run: npm run build
- name: Check worker.js syntax
run: node --check tools/smart-sub/worker.js
- name: Basic structure validation
run: |
echo "=== Worker.js Structure Check ==="
# Check that critical sections exist
echo "--- Server definitions ---"
grep -c "tag:" tools/smart-sub/worker.js | xargs -I{} echo "Servers defined: {}"
echo "--- Generator functions ---"
grep -c "function generate" tools/smart-sub/worker.js | xargs -I{} echo "Generator functions: {}"
echo "--- Config builder (v4.1.0+) ---"
if grep -q "buildConfig" tools/smart-sub/worker.js; then
echo "✅ buildConfig(env) found"
else
echo "❌ buildConfig(env) missing!"
exit 1
fi
echo "--- Health endpoint ---"
if grep -q "/health" tools/smart-sub/worker.js; then
echo "✅ Health endpoint found"
else
echo "❌ Health endpoint missing!"
exit 1
fi
openapi-lint:
name: OpenAPI Spec Lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/setup-node@v6
with:
node-version: '20'
- run: npm ci
- run: npx @redocly/cli lint docs/openapi.yaml
validate-config:
name: Config Schema Validation
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/setup-node@v6
with:
node-version: '20'
- run: npm ci
- run: npm run validate:config
docs-freshness:
name: Generated Docs Freshness
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/setup-node@v6
with:
node-version: '20'
- run: npm ci
- run: npm run docs:generate
- name: Check docs are up to date
run: |
git diff --exit-code -- '*.md' || (echo "Docs are stale. Run: npm run docs:generate" && exit 1)
doc-sync:
name: Doc Sync — Version & Count Check
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- name: Extract worker.js source of truth
id: worker
run: |
WORKER="tools/smart-sub/worker.js"
VERSION=$(grep -m1 'worker_version:' "$WORKER" | grep -oP '\d+\.\d+\.\d+')
SHORT_VERSION="v$(echo "$VERSION" | cut -d. -f1,2)"
LINES=$(wc -l < "$WORKER" | tr -d ' ')
GENERATORS=$(grep -c '^function generate' "$WORKER")
echo "version=$VERSION" >> $GITHUB_OUTPUT
echo "short_version=$SHORT_VERSION" >> $GITHUB_OUTPUT
echo "lines=$LINES" >> $GITHUB_OUTPUT
echo "generators=$GENERATORS" >> $GITHUB_OUTPUT
- name: Check version consistency across docs
env:
SHORT_VERSION: ${{ steps.worker.outputs.short_version }}
run: |
ERRORS=0
DOC_FILES=("README.md" "architecture.md" "strategy-roadmap.md" "docs/api.md" ".claude/CLAUDE.md")
for f in "${DOC_FILES[@]}"; do
if [ ! -f "$f" ]; then continue; fi
STALE=$(grep -inP '(smart.sub|worker|subscription)\s+v\d+\.\d+' "$f" \
| grep -ivP "${SHORT_VERSION}" \
| grep -ivP 'changelog|history|was |before |old |previous' || true)
if [ -n "$STALE" ]; then
echo "❌ $f has stale version references:"
echo "$STALE" | head -3
ERRORS=$((ERRORS + 1))
fi
done
if [ "$ERRORS" -gt 0 ]; then
echo "::error::$ERRORS file(s) have stale version references."
exit 1
fi
- name: Check backup-sub is in sync
run: |
if diff -q tools/smart-sub/worker.js tools/backup-sub/core-worker.js > /dev/null 2>&1; then
echo "✅ backup-sub in sync"
else
echo "::error::backup-sub/core-worker.js is out of sync. Run: npm run build"
exit 1
fi
bash-syntax:
name: Bash Syntax Check
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- name: Check all shell scripts parse correctly
run: |
echo "=== Bash syntax check ==="
ERRORS=0
# Note: bash -n can false-positive on scripts with embedded Python/heredocs
# We check with bash -n first, then verify real errors vs embedded-language false positives
while IFS= read -r script; do
if ! bash -n "$script" 2>/dev/null; then
# Check if the "error" is from embedded python/heredoc (known pattern)
if grep -q 'python3 -c' "$script" 2>/dev/null; then
echo "⚠️ $script — skipped (contains embedded Python)"
else
echo "❌ Syntax error: $script"
bash -n "$script" 2>&1 || true
ERRORS=$((ERRORS + 1))
fi
else
echo "✅ $script"
fi
done < <(find . -name '*.sh' -type f)
if [ "$ERRORS" -gt 0 ]; then
echo "❌ $ERRORS script(s) have syntax errors"
exit 1
fi
echo "✅ All scripts passed"
editorconfig:
name: EditorConfig Check
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- name: Check final newlines
run: |
echo "=== Checking files end with newline ==="
ERRORS=0
find . \( -name '*.md' -o -name '*.sh' -o -name '*.js' -o -name '*.toml' -o -name '*.yml' \) \
-not -path './.git/*' -not -path './node_modules/*' -type f | while read -r file; do
if [ -s "$file" ] && [ "$(tail -c 1 "$file" | wc -l)" -eq 0 ]; then
echo "⚠️ Missing final newline: $file"
fi
done
ansible_lint:
name: Ansible Lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- name: Install Ansible and ansible-lint
run: pip install ansible ansible-lint
- name: Install Ansible collections
run: ansible-galaxy collection install -r infra/ansible/requirements.yml
# Run from infra/ansible so ansible.cfg (roles_path = roles) is loaded.
# From the repo root, ansible-lint resolves roles against
# infra/ansible/playbooks/roles and fails with "role not found".
- name: Run ansible-lint
working-directory: infra/ansible
run: ansible-lint .
onboarding_demo:
name: Onboarding Demo
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/setup-node@v6
with:
node-version: "20"
cache: npm
- run: npm ci
# The demo is the project's front door: a stranger should be able to clone
# and see real output with no VPS, no domain and no credentials. If this
# breaks, onboarding is broken — so it fails the build rather than warning.
- name: npm run demo
run: npm run demo
- name: Assert the demo actually generated configs
run: |
set -euo pipefail
out="$(npm run demo --silent)"
echo "$out"
count="$(printf '%s' "$out" | sed -n 's/.*[^0-9]\([0-9]\{2,\}\) configs generated.*/\1/p' | head -1)"
if [ -z "${count:-}" ] || [ "$count" -lt 50 ]; then
echo "::error::demo generated ${count:-0} configs (expected >= 50)"
exit 1
fi
echo "demo generated $count configs"
packer_validate:
name: Packer Validate
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: hashicorp/setup-packer@main
- name: Packer init (ubuntu-base)
run: packer init infra/packer/ubuntu-base.pkr.hcl
- name: Validate ubuntu-base (syntax-only — shared config, no build block)
run: packer validate -syntax-only infra/packer/ubuntu-base.pkr.hcl
- name: Packer init (hetzner)
run: packer init infra/packer/hetzner.pkr.hcl
- name: Validate hetzner
run: packer validate -syntax-only infra/packer/hetzner.pkr.hcl