Files
odysseus/.github/workflows/ci.yml
T
nopoz ed6cc88974 ci: harden existing workflows for the security gate (#3498)
Pin actions to commit SHAs, set persist-credentials: false on every
checkout, and scope token permissions to the jobs that use them. Suppress
the two findings that are safe by design: the description bot's
pull_request_target trigger (no fork code runs) and an intentional
word-split in the docker manifest step.

Clears actionlint and zizmor against dev so the blocking gate from #1314
can pass once both land.
2026-06-08 20:58:59 +02:00

95 lines
3.4 KiB
YAML

name: CI
on:
push:
branches: [main]
pull_request:
# Least privilege: none of the jobs write to the repo.
permissions:
contents: read
# Cancel superseded runs on the same ref to save Actions minutes.
concurrency:
group: ci-${{ github.ref }}
cancel-in-progress: true
jobs:
python-syntax:
name: Python syntax (compileall)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
with:
persist-credentials: false
- uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5
with:
python-version: "3.11"
# Byte-compile sources — catches syntax errors without installing deps.
- run: python -m compileall -q app.py core routes src services scripts tests
node-syntax:
name: JS syntax (node --check)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
with:
persist-credentials: false
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
with:
node-version: "20"
# Syntax-check our own JS (skip vendored libs in static/lib).
- name: node --check
run: |
shopt -s globstar nullglob
for f in static/app.js static/js/**/*.js; do
node --check "$f"
done
python-tests:
name: Python tests (pytest)
runs-on: ubuntu-latest
# Informational for now: the suite has known flaky / environment-dependent
# failures (test isolation + embedding-model assertions). Tracked under the
# ROADMAP "fresh install smoke tests" item; make this required once green.
continue-on-error: true
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
with:
fetch-depth: 0
persist-credentials: false
# Detect whether this PR only touches documentation files.
# If so, skip the expensive pytest run while still reporting a passing check.
- name: Check for docs-only changes
id: docs-check
run: |
if [ "${{ github.event_name }}" = "pull_request" ]; then
BASE="${{ github.event.pull_request.base.sha }}"
HEAD="${{ github.event.pull_request.head.sha }}"
else
BASE="${{ github.event.before }}"
HEAD="${{ github.sha }}"
fi
# List all changed files; if every file matches docs/markdown patterns, skip pytest.
changed=$(git diff --name-only "$BASE" "$HEAD" 2>/dev/null || git diff --name-only HEAD~1 HEAD)
non_docs=$(echo "$changed" | grep -Ev '^(docs/|.*\.md$|\.github/[^/]+\.md$)' || true)
if [ -z "$non_docs" ]; then
echo "docs_only=true" >> "$GITHUB_OUTPUT"
echo "Docs-only change detected — skipping pytest."
else
echo "docs_only=false" >> "$GITHUB_OUTPUT"
fi
- uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5
if: steps.docs-check.outputs.docs_only != 'true'
with:
python-version: "3.11"
cache: pip
- run: pip install -r requirements.txt
if: steps.docs-check.outputs.docs_only != 'true'
- run: mkdir -p data # sqlite DB lives at ./data/app.db
if: steps.docs-check.outputs.docs_only != 'true'
- run: python -m pytest -q
if: steps.docs-check.outputs.docs_only != 'true'