From 4ab5ab0153b303680ca0dad5fcf7ab0989bd11b9 Mon Sep 17 00:00:00 2001 From: Andriy Oblivantsev Date: Wed, 8 Jul 2026 13:54:06 +0100 Subject: [PATCH] ci: verify, Trivy scan, and semantic release pipeline Gitea Actions: compile check, virtual-drone smoke flight validated with DuckDB, read-only SQL gate tests, Trivy vuln and misconfig scans. Pushes to main cut a semantic version from the conventional-commit type and publish a release with a fleet release manifest and source bundle. --- .github/workflows/release.yaml | 192 +++++++++++++++++++++++++++++++++ README.md | 4 + 2 files changed, 196 insertions(+) create mode 100644 .github/workflows/release.yaml diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml new file mode 100644 index 0000000..e4d1da0 --- /dev/null +++ b/.github/workflows/release.yaml @@ -0,0 +1,192 @@ +# Swarm House - Gitea Actions +# verify: compile check, virtual-drone smoke flight, SQL gate tests (every push/PR) +# scan: Trivy vulnerability + misconfiguration scan +# release: semantic version bump (conventional commits), tag, Gitea release +# with a fleet release manifest + source bundle + +name: CI & Release + +on: + push: + branches: [main] + paths-ignore: + - 'docs/**' + - 'README.md' + - 'AGENTS.md' + pull_request: + branches: [main] + +jobs: + verify: + name: Verify simulator + runs-on: ubuntu-latest + container: python:3.12-bookworm + steps: + - name: Checkout + run: | + git clone --depth=50 https://${{ gitea.actor }}:${{ gitea.token }}@git.produktor.io/${{ gitea.repository }}.git . + git checkout ${{ gitea.sha }} + + - name: Install dependencies + run: pip install --quiet -r simulator/requirements.txt + + - name: Compile check + run: python -m compileall -q simulator/virtual_drone simulator/monitoring simulator/explorer + + - name: Smoke flight + working-directory: simulator + run: | + DRONE_ID=ci-01 FLIGHT_ID=ci-smoke DURATION_S=10 SPEEDUP=10 DATA_DIR=./data \ + python -m virtual_drone.main + python - <<'EOF' + import duckdb + rows = duckdb.sql(""" + SELECT count(*) FROM read_parquet( + 'data/dataset=telemetry/**/*.parquet', + hive_partitioning=true, union_by_name=true) + """).fetchone()[0] + assert rows > 0, "smoke flight produced no telemetry" + print(f"smoke flight OK: {rows} telemetry rows") + EOF + + - name: SQL gate tests + working-directory: simulator + run: | + python - <<'EOF' + import sys + sys.path.insert(0, "explorer") + from server import gate + + assert gate("SELECT 1") is None + assert gate("WITH t AS (SELECT 1) SELECT * FROM t") is None + assert gate("DESCRIBE telemetry") is None + assert gate("DROP TABLE telemetry") is not None + assert gate("SELECT 1; SELECT 2") is not None + assert gate("INSTALL httpfs") is not None + assert gate("SET memory_limit='1GB'") is not None + assert gate("/* sneaky */ COPY t TO 'x'") is not None + assert gate("") is not None + print("SQL gate OK: reads pass, writes and config are rejected") + EOF + + scan: + name: Trivy scan + runs-on: ubuntu-latest + container: python:3.12-bookworm + needs: verify + steps: + - name: Checkout + run: | + git clone --depth=1 https://${{ gitea.actor }}:${{ gitea.token }}@git.produktor.io/${{ gitea.repository }}.git . + git checkout ${{ gitea.sha }} + + - name: Install Trivy + run: | + TRIVY_VER="0.58.2" + curl -fsSL "https://github.com/aquasecurity/trivy/releases/download/v${TRIVY_VER}/trivy_${TRIVY_VER}_Linux-64bit.tar.gz" \ + | tar -xz -C /usr/local/bin trivy + trivy --version + + - name: Dependency vulnerabilities + run: trivy fs --scanners vuln --severity HIGH,CRITICAL --exit-code 1 . + + - name: Dockerfile and Compose misconfigurations + run: trivy config --severity HIGH,CRITICAL --exit-code 0 . + + release: + name: Semantic Release + runs-on: ubuntu-latest + container: python:3.12-bookworm + needs: [verify, scan] + if: gitea.event_name == 'push' + permissions: + contents: write + steps: + - name: Checkout (full history for tags) + run: | + git clone https://${{ gitea.actor }}:${{ gitea.token }}@git.produktor.io/${{ gitea.repository }}.git . + git checkout ${{ gitea.sha }} + git fetch --tags + + - name: Determine version bump (conventional commits) + id: version + run: | + LATEST_TAG=$(git tag -l 'v*' --sort=-v:refname | head -1) + [ -z "$LATEST_TAG" ] && LATEST_TAG="v0.0.0" + echo "Latest tag: $LATEST_TAG" + + VER="${LATEST_TAG#v}" + MAJOR=$(echo "$VER" | cut -d. -f1) + MINOR=$(echo "$VER" | cut -d. -f2) + PATCH=$(echo "$VER" | cut -d. -f3) + + MSG=$(git log -1 --format='%s%n%b' ${{ gitea.sha }}) + echo "Commit: $(git log -1 --format='%s' ${{ gitea.sha }})" + + if echo "$MSG" | grep -qE '^[a-z]+(\(.+\))?!:|BREAKING CHANGE'; then + MAJOR=$((MAJOR + 1)); MINOR=0; PATCH=0; BUMP="major" + elif echo "$MSG" | grep -qE '^feat(\(.+\))?:'; then + MINOR=$((MINOR + 1)); PATCH=0; BUMP="minor" + else + PATCH=$((PATCH + 1)); BUMP="patch" + fi + + NEW_TAG="v${MAJOR}.${MINOR}.${PATCH}" + echo "Bump: $BUMP -> $NEW_TAG" + echo "new_tag=${NEW_TAG}" >> "$GITHUB_OUTPUT" + echo "bump_type=${BUMP}" >> "$GITHUB_OUTPUT" + + - name: Build fleet release manifest and source bundle + run: | + TAG="${{ steps.version.outputs.new_tag }}" + mkdir -p .tmp + tar --exclude='.git' --exclude='.tmp' --exclude='simulator/data' \ + --exclude='simulator/.venv' --exclude='prototype/node_modules' \ + -czf ".tmp/swarm-house-${TAG}.tar.gz" . + + cat > .tmp/manifest.yaml <