-
-
Notifications
You must be signed in to change notification settings - Fork 0
137 lines (133 loc) · 6.27 KB
/
Copy pathci.yml
File metadata and controls
137 lines (133 loc) · 6.27 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
name: CI
# Runs the project's existing self-contained test suites — no live server,
# no API keys, no network calls to an LLM. Compatible with both GitHub
# Actions and Gitea Actions (same workflow syntax).
on:
push:
branches: [main]
pull_request:
jobs:
backend:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
# actions/setup-python only ships prebuilt interpreters for GitHub's
# hosted Ubuntu images; in a minimal Gitea job container it fails with
# "version '3.11' ... was not found for this operating system". Use the
# platform's own Python instead: already present on GitHub runners,
# apt-installed in the container otherwise. Both paths give Python 3.11.
- name: Set up Python
run: |
if ! command -v python3 >/dev/null 2>&1; then
apt-get update && apt-get install -y --no-install-recommends python3 python3-pip
fi
# node:*-bookworm ships python3 but no pip module — ensure pip too.
if ! python3 -m pip --version >/dev/null 2>&1; then
if command -v apt-get >/dev/null 2>&1; then
apt-get update && apt-get install -y --no-install-recommends python3-pip
else
python3 -m ensurepip --upgrade
fi
fi
python3 --version && python3 -m pip --version
- name: Install dependencies
# Debian's system Python is PEP 668 "externally managed"; the flag is
# safe in this ephemeral CI env and is supported by modern pip on both.
run: python3 -m pip install --break-system-packages -r requirements.txt
- name: Audit Python deps
# pip-audit catches CVEs in pinned transitive deps. The default
# pip-audit behavior (no flags) creates an ephemeral venv via
# ensurepip to resolve each requirement — and that fails on minimal
# Gitea job containers that don't have python3-venv / ensurepip.
# We avoid that by:
# 1) generating a *pinned* requirements file with pip-compile
# (from pip-tools; one extra dep, but deterministic & fast)
# 2) installing requirements into the current env so pip-audit
# can read installed versions directly
# 3) running pip-audit with --disable-pip --no-deps so it scans
# the current env's installed packages instead of spinning up
# another venv
# --strict requires pinned (==) versions; --disable-pip requires
# either --no-deps or --require-hashes; both are satisfied here.
run: |
set -e
python3 -m pip install --break-system-packages 'pip-audit==2.10.1' 'pip-tools==7.5.3'
python3 -m pip install --break-system-packages -r requirements.txt
# Pin transitive deps for audit. --quiet keeps the log readable.
python3 -m piptools compile --quiet --output-file=requirements-pinned.txt --no-header --strip-extras requirements.txt
python3 -m pip_audit -r requirements-pinned.txt --strict --disable-pip --no-deps
- run: python3 -m py_compile server.py translator.py cache.py
- run: python3 test_translation.py
- run: python3 test_hardening.py
frontend:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: "20"
- run: node -c static/translator.js
- run: node -c static/loader.js
- run: npm install
- run: npm audit --omit=dev
- run: npm test
# Smoke test: build the image, run it, hit /ping, expect 200.
# Catches Dockerfile regressions (syntax, missing deps, broken entrypoint,
# USER appuser) that pure Python tests can't. Skips when Docker isn't
# available — some Gitea job containers ship without dind, and we'd
# rather get a green run with a "skipped" note than a hard fail that
# blocks legitimate PRs. The pure-Python backend suite above already
# exercises the application code on a mock LLM, so losing this smoke
# step in those environments doesn't drop coverage to zero.
docker-smoke:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Detect Docker
id: docker
run: |
if command -v docker >/dev/null 2>&1; then
echo "available=true" >> "$GITHUB_OUTPUT"
docker --version
else
echo "available=false" >> "$GITHUB_OUTPUT"
echo "::notice::docker not installed in runner; skipping docker-smoke job"
fi
- name: Build image
if: steps.docker.outputs.available == 'true'
run: docker build -t bt-audit:ci .
- name: Run container
if: steps.docker.outputs.available == 'true'
run: |
docker run -d --name bt-smoke -p 8390:8390 bt-audit:ci
# Wait for /ping to come up (the image has start-period=10s in its
# HEALTHCHECK, but we don't want to depend on Docker's healthcheck
# state for the smoke test — poll the endpoint directly).
for i in $(seq 1 30); do
if curl -sf http://127.0.0.1:8390/ping >/dev/null 2>&1; then
echo "ping ok on try $i"
break
fi
sleep 1
done
# The actual assertion: /ping returns 200 with {"status":"ok"}.
curl -sf http://127.0.0.1:8390/ping | grep -q '"status":"ok"'
- name: Verify non-root API process
# The /ping endpoint serves from gunicorn. The `appuser` should own
# the gunicorn process; the entrypoint (and nginx if it were started)
# can stay root.
if: steps.docker.outputs.available == 'true'
run: |
# BusyBox `ps` (Alpine) has no -p flag — list user+args and grep the
# gunicorn master instead. [g]unicorn avoids matching the grep itself.
USER=$(docker exec bt-smoke ps -o user,args | grep '[g]unicorn' | head -1 | awk '{print $1}')
if [ "$USER" = "appuser" ]; then
echo "gunicorn runs as appuser (unprivileged) — OK"
else
echo "gunicorn runs as '$USER', expected appuser"
docker exec bt-smoke ps -o user,args | head -10
exit 1
fi
- name: Cleanup
if: always() && steps.docker.outputs.available == 'true'
run: docker rm -f bt-smoke 2>/dev/null || true