hackathon-ENGIN/.gitea/workflows/ci.yaml
Jakub Famulski 2 5ebfdc389a
All checks were successful
ENGIN CI / Build, test and smoke (push) Successful in 56s
prod: run container with restricted privileges
2026-08-25 13:16:23 +02:00

134 lines
4.1 KiB
YAML

name: ENGIN CI
on:
push:
branches:
- main
- prod-hardening
pull_request:
branches:
- main
jobs:
production-check:
name: Build, test and smoke
runs-on: engin-ci
timeout-minutes: 20
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Clean previous CI resources
run: |
docker rm -f engin-ci-app 2>/dev/null || true
docker image rm -f engin-console:ci 2>/dev/null || true
docker image rm -f engin-console:test 2>/dev/null || true
- name: Build test image
run: docker build --target test --tag engin-console:test .
- name: Run Ruff
run: |
docker run --rm \
--entrypoint sh \
engin-console:test \
-c "python -m pip install --quiet \
--disable-pip-version-check \
--root-user-action=ignore \
ruff==0.16.4 &&
ruff check --no-cache \
app.py engin tests final_pipeline.py ml_polish_benchmark.py scripts"
- name: Check installed dependencies
run: |
docker run --rm \
--entrypoint python \
engin-console:test \
-m pip check
- name: Run test suite
run: |
docker run --rm \
--entrypoint python \
engin-console:test \
-m unittest discover -v
- name: Verify frozen model artifact
run: |
docker run --rm \
--entrypoint python \
engin-console:test \
-m scripts.build_model_artifact --verify-only
- name: Validate Python syntax
run: |
docker run --rm \
--entrypoint python \
engin-console:test \
-m compileall -q app.py engin tests final_pipeline.py scripts
- name: Build production image
run: docker build --target runtime --tag engin-console:ci .
- name: Assert production image contract
run: |
docker run --rm \
--entrypoint sh \
engin-console:ci \
-c "test \"\$(id -u)\" = 10001 &&
test \"\$(id -g)\" = 10001 &&
test ! -w /app &&
test ! -e val.csv &&
test ! -e train.csv &&
test ! -e final_pipeline.py &&
test ! -e severity_benchmark.py &&
test -s artifacts/engin-2026.08.25-1/model.pkl &&
test -s artifacts/engin-2026.08.25-1/manifest.json"
- name: Start application
run: |
docker run -d \
--name engin-ci-app \
--read-only \
--tmpfs /tmp:rw,noexec,nosuid,size=64m \
--cap-drop ALL \
--security-opt no-new-privileges:true \
--pids-limit 256 \
--memory 1g \
--cpus 2 \
engin-console:ci
- name: Verify runtime isolation
run: |
test "$(docker inspect --format '{{.HostConfig.ReadonlyRootfs}}' engin-ci-app)" = true
test "$(docker inspect --format '{{.HostConfig.PidsLimit}}' engin-ci-app)" = 256
test "$(docker inspect --format '{{.HostConfig.Memory}}' engin-ci-app)" = 1073741824
test "$(docker inspect --format '{{.HostConfig.NanoCpus}}' engin-ci-app)" = 2000000000
- name: Verify Streamlit health
run: |
set -eu
attempt=1
while [ "$attempt" -le 30 ]; do
if docker exec engin-ci-app python -c \
"import urllib.request; urllib.request.urlopen('http://127.0.0.1:8501/_stcore/health', timeout=3).read()"; then
exit 0
fi
sleep 2
attempt=$((attempt + 1))
done
docker logs engin-ci-app
exit 1
- name: Cleanup
if: always()
run: |
docker logs engin-ci-app 2>/dev/null || true
docker rm -f engin-ci-app 2>/dev/null || true
docker image rm -f engin-console:ci 2>/dev/null || true
docker image rm -f engin-console:test 2>/dev/null || true