Skip to content

fix(v0.1.10): security hardening — close the six surviving 2026-06 review findings #88

fix(v0.1.10): security hardening — close the six surviving 2026-06 review findings

fix(v0.1.10): security hardening — close the six surviving 2026-06 review findings #88

Workflow file for this run

name: CI
# Runs on every pull request and every push to main.
# Gates: the full unit + offline-e2e pytest suite, plus ruff lint.
#
# Network-gated e2e tests (live APIs, credentials) live in e2e.yml on a
# manual trigger. Tagged releases are handled by publish.yml.
on:
pull_request:
push:
branches: [main]
permissions:
contents: read
concurrency:
group: ci-${{ github.ref }}
cancel-in-progress: true
jobs:
test:
name: pytest (py${{ matrix.python-version }})
runs-on: ubuntu-latest
timeout-minutes: 10
strategy:
# Keep one failure from hiding the other version's result.
fail-fast: false
matrix:
python-version: ["3.12", "3.13"]
steps:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
- name: Install uv
uses: astral-sh/setup-uv@37802adc94f370d6bfd71619e3f0bf239e1f3b78 # v7.6.0
with:
enable-cache: true
- name: Set up Python ${{ matrix.python-version }}
run: uv python install ${{ matrix.python-version }}
- name: Sync project (including dagster + ask extras)
run: uv sync --all-extras --python ${{ matrix.python-version }}
- name: Run pytest
# Default addopts already excludes the `e2e` marker. The
# `offline_e2e` marker (csv-import template full-pipeline test)
# is included so CI gates on real integration.
#
# --cov gates on the coverage floor in pyproject.toml
# ([tool.coverage.report].fail_under). Pinned at 60% for v0.1.2
# (baseline ~65%, ~5% drift headroom); raise over time as
# coverage improves. CI fails if coverage drops below the floor.
run: uv run pytest -q --cov=src/tycoon --cov-report=term --cov-report=xml
- name: Upload coverage report
if: matrix.python-version == '3.12'
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: coverage-xml
path: coverage.xml
if-no-files-found: ignore
lint:
name: ruff
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
- name: Install uv
uses: astral-sh/setup-uv@37802adc94f370d6bfd71619e3f0bf239e1f3b78 # v7.6.0
with:
enable-cache: true
- name: Ruff lint
# Use uvx so we don't need to add ruff to project deps.
run: uvx ruff check src tests
secret-scan:
# Scans the full git history for committed secrets. Runs the
# checksum-verified gitleaks binary directly instead of the marketplace
# action, so the scan itself doesn't widen the supply chain.
name: gitleaks
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
# Full history so gitleaks scans every commit, not just HEAD.
fetch-depth: 0
- name: Run gitleaks
env:
GITLEAKS_VERSION: 8.30.1
GITLEAKS_SHA256: 551f6fc83ea457d62a0d98237cbad105af8d557003051f41f3e7ca7b3f2470eb
run: |
curl -sSfL -o /tmp/gitleaks.tar.gz \
"https://github.com/gitleaks/gitleaks/releases/download/v${GITLEAKS_VERSION}/gitleaks_${GITLEAKS_VERSION}_linux_x64.tar.gz"
echo "${GITLEAKS_SHA256} /tmp/gitleaks.tar.gz" | sha256sum --check
tar -xzf /tmp/gitleaks.tar.gz -C /tmp gitleaks
/tmp/gitleaks git --no-banner --redact --verbose .
build:
# Verifies the wheel + sdist build cleanly with current pins, and
# that the built wheel installs into a fresh venv with a working
# `tycoon` entry point. Catches packaging regressions (manifest
# typos, broken extras, console-script wiring drift) before they
# reach a tagged release.
name: build + install smoke
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
- name: Install uv
uses: astral-sh/setup-uv@37802adc94f370d6bfd71619e3f0bf239e1f3b78 # v7.6.0
with:
enable-cache: true
- name: Build wheel + sdist
run: uv build
- name: Upload built artifacts
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: dist
path: dist/
if-no-files-found: error
- name: Install built wheel into a fresh venv
# Use a separate venv so we exercise the published wheel rather
# than the editable install. Picks the wheel by glob so the
# version doesn't have to be hard-coded.
run: |
uv venv /tmp/wheel-smoke --python 3.12
/tmp/wheel-smoke/bin/python -m ensurepip
/tmp/wheel-smoke/bin/python -m pip install dist/database_tycoon-*.whl
- name: Smoke-check tycoon entry point
run: |
/tmp/wheel-smoke/bin/tycoon --version
/tmp/wheel-smoke/bin/tycoon -h | head -20
docs:
# Builds the MkDocs site in --strict mode so broken internal links,
# missing nav targets, and deprecated config keys block the merge
# rather than landing silently.
name: docs (mkdocs --strict)
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
- name: Install uv
uses: astral-sh/setup-uv@37802adc94f370d6bfd71619e3f0bf239e1f3b78 # v7.6.0
with:
enable-cache: true
- name: Sync docs extra
run: uv sync --extra docs
- name: Build docs (strict)
run: uv run mkdocs build --strict
template-smoke:
# Initializes a fresh project from each built-in template and runs
# `tycoon doctor` against it, asserting clean exit. Catches
# template-side regressions (missing files, bad placeholder
# substitution, profile alignment errors) without spinning up the
# full e2e ingestion suite.
name: template smoke (${{ matrix.template }})
runs-on: ubuntu-latest
timeout-minutes: 5
strategy:
fail-fast: false
matrix:
template: [csv-import, nyc-transit]
steps:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
- name: Install uv
uses: astral-sh/setup-uv@37802adc94f370d6bfd71619e3f0bf239e1f3b78 # v7.6.0
with:
enable-cache: true
- name: Sync project (default extras)
run: uv sync
- name: Init project from template
run: |
mkdir -p /tmp/smoke-${{ matrix.template }}
cd /tmp/smoke-${{ matrix.template }}
uv run --project ${{ github.workspace }} tycoon init \
--template ${{ matrix.template }}
- name: tycoon doctor (no errors)
run: |
cd /tmp/smoke-${{ matrix.template }}
uv run --project ${{ github.workspace }} tycoon doctor 2>&1 | tee /tmp/doctor.log
# Doctor always exits 0 today; assert no ERROR lines made it
# into the output as a stronger gate.
if grep -q "^ERROR" /tmp/doctor.log; then
echo "::error::tycoon doctor reported errors against template ${{ matrix.template }}"
exit 1
fi