# CI for shopdb-flask. # # The Gitea runner executes jobs on the host (act host executor), so # actions/setup-python cannot provision interpreters (no prebuilt binaries # for the host distro). The backend job uses the system python3 in a venv # instead. setup-node works because node is resolved differently. # # Jobs run on push and pull_request: # backend - pytest (tests use in-memory SQLite via TestingConfig, so no # database service is needed). # naming - the CONTRIBUTING.md naming/style gate. # frontend - Vue build. # migrations-mysql - proves the REAL multi-site deploy path: a fresh # `flask db upgrade` + per-plugin install on utf8mb4 MySQL # from empty, idempotent on a second run. The pytest suite # only exercises SQLite create_all(), so without this a # regression in the Alembic chain on MySQL would ship # undetected. Needs a runner that supports service # containers; if yours does not, run these steps against a # host MySQL instead. name: CI on: push: pull_request: # Lets a run be retried from the Actions page without inventing a commit. # Gitea's API exposes no rerun endpoint, so without this the only way to # re-test a runner-side change is to push something. workflow_dispatch: jobs: backend: runs-on: ubuntu-latest steps: - name: Check out uses: actions/checkout@v4 - name: Set up Python # Pin the interpreter instead of inheriting the runner image's default. # requirements.txt is hash-pinned and was compiled on 3.14; on an older # Python, alembic additionally requires tomli, which has no hash in the # lock file and pip then refuses the whole install. uses: actions/setup-python@v5 with: python-version: '3.14' - name: Install dependencies run: | python3 --version python3 -m venv .venv .venv/bin/pip install --upgrade pip .venv/bin/pip install -r requirements.txt # pytest lives in requirements-dev.txt, not requirements.txt. Without # this the test step dies with "No module named pytest". .venv/bin/pip install -r requirements-dev.txt - name: Run backend tests run: .venv/bin/python -m pytest naming: runs-on: ubuntu-latest steps: - name: Check out uses: actions/checkout@v4 - name: Run naming and style check run: bash scripts/check-naming-and-style.sh frontend: runs-on: ubuntu-latest steps: - name: Check out uses: actions/checkout@v4 - name: Set up Node uses: actions/setup-node@v4 with: node-version: '20' - name: Install and build frontend run: | npm ci npm run build working-directory: frontend migrations-mysql: runs-on: ubuntu-latest services: mysql: image: mysql:8.0 env: MYSQL_ROOT_PASSWORD: root MYSQL_DATABASE: shopdb_ci ports: - 3306:3306 options: >- --health-cmd="mysqladmin ping -h localhost -uroot -proot" --health-interval=5s --health-timeout=5s --health-retries=20 env: # 'mysql' is the service name, not 127.0.0.1. Under a container-mode # runner the job is itself a container on the services network, so the # service is reached by name. Published ports land on the runner host, # not on the job's localhost the way they do on GitHub-hosted runners. DATABASE_URL: mysql+pymysql://root:root@mysql:3306/shopdb_ci?charset=utf8mb4 SECRET_KEY: ci-secret JWT_SECRET_KEY: ci-jwt-secret steps: - name: Check out uses: actions/checkout@v4 - name: Set up Python # Pin the interpreter instead of inheriting the runner image's default. # requirements.txt is hash-pinned and was compiled on 3.14; on an older # Python, alembic additionally requires tomli, which has no hash in the # lock file and pip then refuses the whole install. uses: actions/setup-python@v5 with: python-version: '3.14' - name: Install dependencies run: | python3 -m venv .venv .venv/bin/pip install --upgrade pip .venv/bin/pip install -r requirements.txt - name: Drop the git-crypt .env # .env is committed git-crypt encrypted. CI has no key, so the checked # out file is the raw encrypted blob and python-dotenv dies reading it # as UTF-8. This job supplies DATABASE_URL, SECRET_KEY and # JWT_SECRET_KEY through env: already, so the file is not needed. run: rm -f .env - name: Force utf8mb4 on the CI database # Uses pymysql, already a runtime dependency, rather than the mysql CLI, # which is not present in every runner image. run: | .venv/bin/python - <<'PY' import pymysql c = pymysql.connect(host="mysql", port=3306, user="root", password="root") with c.cursor() as cur: cur.execute("ALTER DATABASE shopdb_ci CHARACTER SET utf8mb4 " "COLLATE utf8mb4_unicode_ci") c.close() PY - name: Fresh core upgrade from empty run: .venv/bin/flask db upgrade - name: Install every bundled plugin (runs its chain) run: | for p in computers employees geenforce knowledgebase machines \ measuringtools network notifications printers slides usb warranty; do .venv/bin/flask plugin install "$p" done - name: Assert schema built + utf8mb4, and a second upgrade is a no-op run: | .venv/bin/python - <<'PY' from shopdb import create_app from shopdb.extensions import db from sqlalchemy import text app = create_app() with app.app_context(): insp = db.inspect(db.engine) tables = insp.get_table_names() assert len(tables) >= 70, f'only {len(tables)} tables built' row = db.session.execute(text( "SELECT default_character_set_name FROM information_schema.schemata " "WHERE schema_name = 'shopdb_ci'")).first() assert row[0] == 'utf8mb4', f'charset is {row[0]}, not utf8mb4' print(f'OK: {len(tables)} tables, charset {row[0]}') PY - name: Second core upgrade must be a clean no-op run: .venv/bin/flask db upgrade