Skip to content

ci

ci #112

Workflow file for this run

name: ci
env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
permissions:
contents: read
on:
pull_request:
push:
branches:
- master
tags:
- "v*"
schedule:
- cron: "0 3 * * 1"
workflow_dispatch:
jobs:
resolve-nexttrace-release:
runs-on: ubuntu-latest
outputs:
tag: ${{ steps.resolve.outputs.tag }}
steps:
- name: Resolve latest NTrace-core release
id: resolve
env:
GITHUB_TOKEN: ${{ github.token }}
run: |
tag="$(curl -fsSL \
-H "Authorization: Bearer ${GITHUB_TOKEN}" \
-H "Accept: application/vnd.github+json" \
-H "X-GitHub-Api-Version: 2022-11-28" \
https://api.github.com/repos/nxtrace/NTrace-core/releases/latest | python3 -c 'import json, sys; print(json.load(sys.stdin)["tag_name"])')"
echo "tag=${tag}" >> "$GITHUB_OUTPUT"
python-check:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
python-version: ["3.10", "3.12"]
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: ${{ matrix.python-version }}
cache: pip
- name: Install dependencies
run: python -m pip install -r requirements.txt
- name: Run unit tests
run: python -m unittest discover -s tests -p "test_*.py"
- name: Compile Python modules
run: python -m py_compile app.py nexttrace_mtr.py
frontend-check:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Set up Node
uses: actions/setup-node@v6
with:
node-version: "20"
- name: Check browser scripts
run: |
node --check assets/js/main.js
node --check assets/js/mtr-agg.js
node --check assets/js/settingsmenu.js
node --check assets/js/ui-state.js
node --check tests/browser-harness.cjs
node --check tests/browser-controls.check.cjs
- name: Run frontend tests
run: |
node --test tests/*.test.cjs
node tests/browser-controls.check.cjs
security-check:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: "3.12"
cache: pip
- name: Install dependencies
run: python -m pip install -r requirements.txt pip-audit
- name: Audit Python dependencies
run: pip-audit -r requirements.txt
- name: Verify baseline hardening
run: |
python - <<'PY'
from pathlib import Path
required_snippets = {
"app.py": [
"NTWA_SECRET_KEY",
"NTWA_TRUSTED_HOSTS",
"SESSION_COOKIE_HTTPONLY",
"/healthz",
],
"Dockerfile": [
"HEALTHCHECK",
],
"requirements.txt": [
"gunicorn",
],
"nginx.conf": [
"Content-Security-Policy",
"X-Content-Type-Options",
"Referrer-Policy",
"X-Frame-Options",
"server_tokens off",
],
".github/workflows/docker-image.yml": [
"pull_request:",
"schedule:",
"docker-smoke",
],
}
missing = []
for path, snippets in required_snippets.items():
content = Path(path).read_text()
for snippet in snippets:
if snippet not in content:
missing.append(f"{path} missing {snippet!r}")
if missing:
raise SystemExit("\n".join(missing))
PY
docker-smoke:
runs-on: ubuntu-latest
needs:
- resolve-nexttrace-release
- python-check
- frontend-check
- security-check
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Build smoke image
run: docker build --build-arg NEXTTRACE_RELEASE_TAG="${{ needs.resolve-nexttrace-release.outputs.tag }}" -t nexttraceweb:smoke .
- name: Start container
run: |
cid="$(docker run -d -p 30080:30080 nexttraceweb:smoke)"
echo "CONTAINER_ID=${cid}" >> "$GITHUB_ENV"
- name: Wait for healthz
run: |
for _ in $(seq 1 60); do
if curl -fsS http://127.0.0.1:30080/healthz >/dev/null; then
exit 0
fi
sleep 2
done
docker logs "$CONTAINER_ID"
exit 1
- name: Verify container exits when gunicorn dies
run: |
gunicorn_pid="$(docker exec "$CONTAINER_ID" pgrep -f 'gunicorn.*app:app' | head -n1)"
docker exec "$CONTAINER_ID" kill -TERM "$gunicorn_pid"
timeout 20s docker wait "$CONTAINER_ID"
- name: Dump logs on failure
if: failure()
run: docker logs "$CONTAINER_ID"
- name: Cleanup container
if: always()
run: docker rm -f "$CONTAINER_ID" >/dev/null 2>&1 || true
publish:
runs-on: ubuntu-latest
needs:
- resolve-nexttrace-release
- python-check
- frontend-check
- security-check
- docker-smoke
if: github.event_name == 'push' && (github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/tags/v'))
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Log in to DockerHub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Compute image tags
id: meta
run: |
short_sha="${GITHUB_SHA::7}"
tags="tsosc/nexttraceweb:sha-${short_sha}"
if [[ "${GITHUB_REF}" == "refs/heads/master" ]]; then
tags="${tags},tsosc/nexttraceweb:latest"
fi
if [[ "${GITHUB_REF}" == refs/tags/* ]]; then
tags="${tags},tsosc/nexttraceweb:${GITHUB_REF_NAME}"
fi
echo "tags=${tags}" >> "$GITHUB_OUTPUT"
- name: Build and push image
uses: docker/build-push-action@v6
with:
context: .
platforms: linux/amd64,linux/arm64
push: true
tags: ${{ steps.meta.outputs.tags }}
build-args: |
NEXTTRACE_RELEASE_TAG=${{ needs.resolve-nexttrace-release.outputs.tag }}
cache-from: type=gha
cache-to: type=gha,mode=max