Files
Juan Denis 727310a703 Run CI on pull requests into dev
CONTRIBUTING asks contributors to target dev, but every workflow only
triggered on PRs into main, so contributor PRs merged with no checks
and failures surfaced only after the push to dev.
2026-10-10 13:26:07 -04:00

171 lines
6.5 KiB
YAML

name: Test System Utilities
on:
push:
branches: [dev]
paths:
- 'backend/app/utils/system.py'
- 'backend/app/services/**'
- 'backend/tests/test_utils_system*.py'
- '.github/workflows/test-system-utils.yml'
pull_request:
branches: [main, dev]
paths:
- 'backend/app/utils/system.py'
- 'backend/app/services/**'
- 'backend/tests/test_utils_system*.py'
# The mocked `unit-tests` job that used to lead this file was removed: it ran
# `pytest tests/test_utils_system.py`, and Backend CI's
# `pytest tests -v` already collects that exact file — there is no pytest.ini,
# addopts, or collect_ignore narrowing it. What is left here is the part
# Backend CI genuinely cannot do: exercise the package-manager detection
# against real apt/dnf inside real distro images.
jobs:
# ──────────────────────────────────────────────────────────────────
# Job 1: Integration tests on real distros (no mocks)
# ──────────────────────────────────────────────────────────────────
integration-tests:
name: Integration (${{ matrix.distro }})
runs-on: ubuntu-latest
# A wedged apt mirror once held the ubuntu job "in progress" for 2 h, and
# Version Bump waits on every sibling — fail fast instead of blocking dev.
timeout-minutes: 15
strategy:
fail-fast: false
matrix:
include:
- distro: ubuntu
image: ubuntu:24.04
expected_manager: apt
- distro: debian
image: debian:12
expected_manager: apt
- distro: fedora
image: fedora:41
expected_manager: dnf
- distro: rocky
image: rockylinux:9
expected_manager: dnf
container:
image: ${{ matrix.image }}
env:
DEBIAN_FRONTEND: noninteractive
steps:
- uses: actions/checkout@v4
- name: Install Python & pytest
shell: bash
run: |
if command -v apt-get >/dev/null 2>&1; then
rm -rf /var/lib/apt/lists/*
apt-get -o Acquire::Retries=3 -o Acquire::http::Timeout=30 update
apt-get -o Acquire::Retries=3 -o Acquire::http::Timeout=30 -o Dpkg::Options::=--force-confdef -o Dpkg::Options::=--force-confold install -y python3 python3-pip
elif command -v dnf >/dev/null 2>&1; then
dnf install -y python3 python3-pip
elif command -v yum >/dev/null 2>&1; then
yum install -y python3 python3-pip
fi
pip3 install --break-system-packages pytest 2>/dev/null || pip3 install pytest
- name: Run integration smoke tests
working-directory: backend
env:
EXPECTED_MANAGER: ${{ matrix.expected_manager }}
run: python3 -m pytest tests/test_utils_system_integration.py -v
# ──────────────────────────────────────────────────────────────────
# Job 2: Audit — grep for raw subprocess patterns in services
# ──────────────────────────────────────────────────────────────────
audit-raw-patterns:
name: Audit Raw Subprocess Patterns
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.11'
# The ratchet (plan 75 §G1). The grep steps below check a hand-listed set
# of files for three known-bad patterns; this counts EVERY raw
# subprocess call site under backend/app and fails if the total grew.
# A grep cannot do this honestly — it also matches comments, docstrings
# and `except subprocess.SubprocessError` — so the census is AST-based.
- name: Raw subprocess census (ceiling may only go down)
working-directory: backend
run: python3 tests/raw_subprocess_census.py
- name: Check for raw 'sudo systemctl' calls in migrated services
run: |
MIGRATED_FILES=(
backend/app/services/firewall_service.py
backend/app/services/security_service.py
backend/app/services/python_service.py
backend/app/services/deployment_service.py
backend/app/services/ftp_service.py
backend/app/services/nginx_service.py
backend/app/services/php_service.py
backend/app/services/ssl_service.py
)
EXIT_CODE=0
echo "=== Checking for raw subprocess patterns ==="
echo ""
# Pattern 1: subprocess.run with 'sudo', 'systemctl'
echo "--- Pattern: 'sudo', 'systemctl' ---"
for f in "${MIGRATED_FILES[@]}"; do
if [ -f "$f" ]; then
MATCHES=$(grep -n "'sudo', 'systemctl'" "$f" || true)
if [ -n "$MATCHES" ]; then
echo "FAIL: $f"
echo "$MATCHES"
EXIT_CODE=1
fi
fi
done
# Pattern 2: subprocess.run with dpkg
echo ""
echo "--- Pattern: subprocess.run.*dpkg ---"
for f in "${MIGRATED_FILES[@]}"; do
if [ -f "$f" ]; then
MATCHES=$(grep -n "subprocess\.run.*dpkg" "$f" || true)
if [ -n "$MATCHES" ]; then
echo "FAIL: $f"
echo "$MATCHES"
EXIT_CODE=1
fi
fi
done
# Pattern 3: os.path.exists('/usr/bin/apt')
echo ""
echo "--- Pattern: os.path.exists('/usr/bin/apt') ---"
for f in "${MIGRATED_FILES[@]}"; do
if [ -f "$f" ]; then
MATCHES=$(grep -n "os\.path\.exists.*'/usr/bin/apt'" "$f" || true)
if [ -n "$MATCHES" ]; then
echo "FAIL: $f"
echo "$MATCHES"
EXIT_CODE=1
fi
fi
done
echo ""
if [ $EXIT_CODE -eq 0 ]; then
echo "All migrated files are clean."
else
echo "Raw subprocess patterns found — see failures above."
fi
exit $EXIT_CODE