diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml index 7b484e0..44347e8 100644 --- a/.gitea/workflows/ci.yml +++ b/.gitea/workflows/ci.yml @@ -1,6 +1,6 @@ --- -# ci-sync: 2026-05-30T01:31:23Z -# Homelab CI — Python lane (git-ci-01) + secret scan (git-ci-02) +# Homelab CI — Python lane (ubuntu-latest docker image) + secret scan (git-ci-02) +# POLICY: lint and tests are HARD gates. Never add `|| true` to them. # Skip: @skipci in branch name or commit message name: CI @@ -32,24 +32,25 @@ jobs: python-ci: needs: skip-ci-check if: needs.skip-ci-check.outputs.should-skip != '1' - runs-on: [homelab, self-hosted, linux] - container: - # node image: actions/checkout@v4 needs Node; install python3 in-job - image: node:20-bookworm + # ubuntu-latest → docker://docker.gitea.com/runner-images:ubuntu-latest + # (avoids host/container nesting quirks that broke node:20-bookworm python-ci) + runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: "3.12" + - name: Install Python tooling run: | - apt-get update -qq - DEBIAN_FRONTEND=noninteractive apt-get install -y -qq python3 python3-pip python3-venv - python3 -m pip install --upgrade pip --break-system-packages - if [ -f requirements.txt ]; then pip install -r requirements.txt --break-system-packages; fi - if [ -f requirements-dev.txt ]; then pip install -r requirements-dev.txt --break-system-packages; fi - pip install bandit pip-audit ruff --break-system-packages + python -m pip install --upgrade pip + if [ -f requirements.txt ]; then pip install -r requirements.txt; fi + pip install -r requirements-dev.txt bandit pip-audit ruff - name: Ruff lint - run: python3 -m ruff check . + run: ruff check server.py slack_export.py tests scripts - name: Bandit (advisory) run: bandit -r . -q || true @@ -60,13 +61,7 @@ jobs: - name: Pytest env: PYTHONPATH: . - run: | - if [ -d tests ] || ls test_*.py *_test.py 2>/dev/null; then - pip install -r requirements-dev.txt --break-system-packages - python3 -m pytest -q --tb=short - else - echo "No tests found — skip" - fi + run: python -m pytest -q --tb=short secret-scan: needs: skip-ci-check diff --git a/scripts/auto-cherry-pick.py b/scripts/auto-cherry-pick.py index 1d011e2..aee0845 100755 --- a/scripts/auto-cherry-pick.py +++ b/scripts/auto-cherry-pick.py @@ -355,10 +355,6 @@ def main() -> int: if not arg.startswith("-") and Path(arg).expanduser().is_dir(): export_path = Path(arg).expanduser().resolve() - sys.path.insert(0, str(REPO_ROOT)) - from slack_export import SlackExport - - export = SlackExport(export_path) manifest: dict = {"channels": {}, "users": {}} if MANIFEST_PATH.is_file(): manifest = json.loads(MANIFEST_PATH.read_text(encoding="utf-8")) @@ -377,6 +373,10 @@ def main() -> int: ) return 0 + sys.path.insert(0, str(REPO_ROOT)) + from slack_export import SlackExport + + export = SlackExport(export_path) channels = manifest.setdefault("channels", {}) stats: dict[str, int] = {} diff --git a/tests/conftest.py b/tests/conftest.py index 902e28f..cefc0a3 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -69,16 +69,18 @@ def _free_port() -> int: return s.getsockname()[1] -def _wait_health(url: str, timeout: float = 30.0) -> None: +def _wait_health(url: str, timeout: float = 45.0) -> None: deadline = time.time() + timeout + last_err: Exception | None = None while time.time() < deadline: try: with urllib.request.urlopen(url, timeout=1) as resp: if resp.status == 200: return - except (urllib.error.URLError, TimeoutError): + except (urllib.error.URLError, TimeoutError) as exc: + last_err = exc time.sleep(0.15) - raise RuntimeError(f"Server did not start: {url}") + raise RuntimeError(f"Server did not start: {url} ({last_err!r})") @pytest.fixture(scope="session")