Files
awg-dash/.gitea/workflows/ci.yml
T
embeddevio 88bdaf7d3a
ci / lint (push) Successful in 46s
contract-openapi / openapi-contract (push) Failing after 35s
contract-openapi / ts-client-is-current (push) Failing after 32s
ci / unit (push) Successful in 56s
ci / security (push) Successful in 1m13s
ci / build (push) Has been skipped
ci / image-scan (push) Has been skipped
ci / push (push) Has been skipped
ci: trivy ставим из релизного tarball (install.sh + v0.57.1 → 404), версия 0.76.1
Refs #17
2026-10-28 22:21:04 +03:00

320 lines
11 KiB
YAML

name: ci
# CI-пайплайн с гейтами (issue #17, Refs #15)
# lint -> unit (coverage >= 70%) -> security (bandit/pip-audit) -> build -> image-scan (trivy) -> push staging-<sha>
# Гейты (роняют pipeline):
# * unit — покрытие < 70 % (--cov-fail-under=70)
# * security — bandit HIGH, pip-audit: любая известная уязвимость
# * image-scan — trivy: HIGH/CRITICAL в образе
# * build стартует только после зелёных lint+unit+security; push — только после зелёного image-scan.
on:
push:
branches: ["feat/**", "fix/**", "chore/**", "ci/**", "main"]
pull_request:
branches: [main]
workflow_dispatch:
env:
PYTHON_VERSION: "3.12"
REGISTRY: 192.168.1.111:3000
IMAGE_NAME: ${{ github.repository }}
jobs:
# ─────────────────────────────── 1. LINT ────────────────────────────────
lint:
name: lint
runs-on: ubuntu-latest
outputs:
has_dockerfile: ${{ steps.dockerfile.outputs.has_dockerfile }}
steps:
- name: checkout
id: checkout
uses: actions/checkout@v4
continue-on-error: true
# Дефект Gitea git-http: upload-pack отдаёт пустой pack (Content-Length: 90),
# git clone/fetch падает "early EOF / invalid index-pack output".
# Обход: эндпоинт archive/<sha>.tar.gz работает — забираем исходники им.
# Когда git-http починят, шаг просто перестанет срабатывать (outcome != failure).
- name: "checkout fallback (archive tarball)"
if: steps.checkout.outcome == 'failure'
run: |
set -euo pipefail
curl -fsSL -o /tmp/src.tar.gz "http://192.168.1.111:3000/${IMAGE_NAME}/archive/${GITHUB_SHA}.tar.gz"
tar -xzf /tmp/src.tar.gz --strip-components=1 -C .
echo "sources: archive fallback, $(ls -1 | wc -l) entries"
- name: "python 3.12"
uses: actions/setup-python@v5
with:
python-version: ${{ env.PYTHON_VERSION }}
- name: install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements-dev.txt -r requirements-contract.txt
- name: ruff check
run: ruff check backend
- name: ruff format --check
run: ruff format --check backend
- name: "mypy (strict)"
run: mypy backend
- name: detect deploy/Dockerfile
id: dockerfile
run: |
if [ -f deploy/Dockerfile ]; then
echo "has_dockerfile=true" >> "$GITHUB_OUTPUT"
echo "deploy/Dockerfile найден — jobs build/image-scan/push активны"
else
echo "has_dockerfile=false" >> "$GITHUB_OUTPUT"
echo "::warning::deploy/Dockerfile отсутствует — build/image-scan/push пропущены (ждут merge #18 / PR #49)"
fi
# ─────────────────── 2. UNIT + ГЕЙТ ПОКРЫТИЯ >= 70 % ───────────────────
unit:
name: unit
needs: lint
runs-on: ubuntu-latest
steps:
- name: checkout
id: checkout
uses: actions/checkout@v4
continue-on-error: true
- name: "checkout fallback (archive tarball)"
if: steps.checkout.outcome == 'failure'
run: |
set -euo pipefail
curl -fsSL -o /tmp/src.tar.gz "http://192.168.1.111:3000/${IMAGE_NAME}/archive/${GITHUB_SHA}.tar.gz"
tar -xzf /tmp/src.tar.gz --strip-components=1 -C .
- name: "python 3.12"
uses: actions/setup-python@v5
with:
python-version: ${{ env.PYTHON_VERSION }}
- name: install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements-dev.txt -r requirements-contract.txt
- name: "pytest + coverage gate (fail below 70%)"
run: |
python -m pytest \
--cov=awg_agent --cov=awg_api \
--cov-report=term-missing \
--cov-report=xml:coverage.xml \
--cov-report=html:htmlcov \
--cov-fail-under=70
- name: coverage summary
if: always()
run: python -m coverage report --sort=cover
- name: upload coverage artifacts
if: always()
uses: actions/upload-artifact@v3
with:
name: coverage
path: |
coverage.xml
htmlcov/
retention-days: 14
# ───────────── 3. SECURITY: bandit (HIGH) + pip-audit ──────────────────
security:
name: security
needs: lint
runs-on: ubuntu-latest
steps:
- name: checkout
id: checkout
uses: actions/checkout@v4
continue-on-error: true
- name: "checkout fallback (archive tarball)"
if: steps.checkout.outcome == 'failure'
run: |
set -euo pipefail
curl -fsSL -o /tmp/src.tar.gz "http://192.168.1.111:3000/${IMAGE_NAME}/archive/${GITHUB_SHA}.tar.gz"
tar -xzf /tmp/src.tar.gz --strip-components=1 -C .
- name: "python 3.12"
uses: actions/setup-python@v5
with:
python-version: ${{ env.PYTHON_VERSION }}
- name: install scanners
run: |
python -m pip install --upgrade pip
pip install "bandit[toml]>=1.7" "pip-audit>=2.7"
pip install -r requirements.txt -r requirements-contract.txt
- name: "bandit -lll (HIGH severity gate)"
run: |
set -o pipefail
bandit -lll -r backend 2>&1 | tee bandit-report.txt
bandit -lll -f json -o bandit-report.json -r backend || true
echo "bandit: HIGH/CRITICAL не найдены"
- name: "pip-audit (known vulnerabilities gate)"
run: |
pip-audit -r requirements.txt -r requirements-contract.txt --progress-spinner off
- name: pip-audit json report
if: always()
run: pip-audit -r requirements.txt -r requirements-contract.txt --progress-spinner off --format json -o pip-audit-report.json || true
- name: upload security artifacts
if: always()
uses: actions/upload-artifact@v3
with:
name: security-reports
path: |
bandit-report.txt
bandit-report.json
pip-audit-report.json
retention-days: 14
# ───────────────── 4. BUILD: docker build (нужен #18) ──────────────────
build:
name: build
needs: [lint, unit, security]
if: needs.lint.outputs.has_dockerfile == 'true'
runs-on: ubuntu-latest
steps:
- name: checkout
id: checkout
uses: actions/checkout@v4
continue-on-error: true
- name: "checkout fallback (archive tarball)"
if: steps.checkout.outcome == 'failure'
run: |
set -euo pipefail
curl -fsSL -o /tmp/src.tar.gz "http://192.168.1.111:3000/${IMAGE_NAME}/archive/${GITHUB_SHA}.tar.gz"
tar -xzf /tmp/src.tar.gz --strip-components=1 -C .
- name: "guard: deploy/Dockerfile"
run: |
if [ ! -f deploy/Dockerfile ]; then
echo "::error::deploy/Dockerfile отсутствует: сборка образа ждёт merge #18 (PR #49)."
echo "Пайплайн целиком проверен на ветке feat/17-verify (она же + deploy/Dockerfile)."
exit 1
fi
- name: image meta
id: meta
run: |
SHA_SHORT="${GITHUB_SHA::8}"
IMAGE="${REGISTRY}/${IMAGE_NAME}:staging-${SHA_SHORT}"
echo "sha_short=${SHA_SHORT}" >> "$GITHUB_OUTPUT"
echo "image=${IMAGE}" >> "$GITHUB_OUTPUT"
echo "IMAGE=${IMAGE}" >> "$GITHUB_ENV"
echo "image: ${IMAGE}"
- name: docker build
run: |
set -o pipefail
docker build --pull -f deploy/Dockerfile -t "$IMAGE" . 2>&1 | tee build.log
- name: image digest
run: |
docker image inspect "$IMAGE" --format 'id={{.Id}} size={{.Size}}'
docker image inspect "$IMAGE" --format '{{.Id}}' > image-id.txt
- name: upload build artifacts
if: always()
uses: actions/upload-artifact@v3
with:
name: "build-${{ github.sha }}"
path: |
build.log
image-id.txt
retention-days: 14
# ──────── 5. IMAGE-SCAN: trivy, гейт HIGH/CRITICAL ────────────────────
image-scan:
name: image-scan
needs: [lint, build]
if: needs.lint.outputs.has_dockerfile == 'true'
runs-on: ubuntu-latest
env:
TRIVY_VERSION: "0.76.1"
TRIVY_SEVERITY: "HIGH,CRITICAL"
steps:
- name: install trivy
run: |
set -euo pipefail
curl -fsSL -o /tmp/trivy.tar.gz \
"https://github.com/aquasecurity/trivy/releases/download/v${TRIVY_VERSION}/trivy_${TRIVY_VERSION}_Linux-64bit.tar.gz"
tar -xzf /tmp/trivy.tar.gz -C /usr/local/bin trivy
chmod +x /usr/local/bin/trivy
trivy --version
- name: "trivy image (HIGH/CRITICAL -> fail)"
run: |
set -o pipefail
IMAGE="${REGISTRY}/${IMAGE_NAME}:staging-${GITHUB_SHA::8}"
echo "scan: ${IMAGE}"
trivy image \
--image-src docker \
--scanners vuln \
--severity "$TRIVY_SEVERITY" \
--ignore-unfixed \
--exit-code 1 \
--format table \
"$IMAGE" 2>&1 | tee trivy-report.txt
- name: trivy json report
if: always()
run: |
IMAGE="${REGISTRY}/${IMAGE_NAME}:staging-${GITHUB_SHA::8}"
trivy image --image-src docker --scanners vuln --severity "$TRIVY_SEVERITY" \
--ignore-unfixed --format json -o trivy-report.json "$IMAGE" || true
- name: upload trivy report
if: always()
uses: actions/upload-artifact@v3
with:
name: trivy-report
path: |
trivy-report.txt
trivy-report.json
retention-days: 14
# ─────── 6. PUSH: staging-<sha> в registry Gitea ──────────────────────
push:
name: push
needs: [lint, build, image-scan]
if: needs.lint.outputs.has_dockerfile == 'true' && github.event_name == 'push'
runs-on: ubuntu-latest
steps:
- name: docker login
run: |
set -o pipefail
echo "${{ secrets.GITHUB_TOKEN }}" \
| docker login "$REGISTRY" -u "${{ github.actor }}" --password-stdin
- name: "push staging-<sha>"
run: |
set -o pipefail
IMAGE="${REGISTRY}/${IMAGE_NAME}:staging-${GITHUB_SHA::8}"
docker push "$IMAGE" 2>&1 | tee push.log
echo "${IMAGE}" | tee pushed-image.txt
- name: upload push record
if: always()
uses: actions/upload-artifact@v3
with:
name: pushed-image
path: |
push.log
pushed-image.txt
retention-days: 14