ci: add universal test coverage pipeline
Some checks failed
CI / SAST — Semgrep (push) Has been cancelled
Build and Deploy / build-push (push) Has been cancelled
Build and Deploy / deploy (push) Has been cancelled
CI / Push to Registry (push) Has been cancelled
CI / Code Quality & Linting (push) Has been cancelled
CI / Unit Tests (push) Has been cancelled
CI / Dependency Vulnerability Scan (push) Has been cancelled
CI / Build Docker Image (push) Has been cancelled
CI / Trivy Scan (push) Has been cancelled
CI / Report Results (push) Has been cancelled
Some checks failed
CI / SAST — Semgrep (push) Has been cancelled
Build and Deploy / build-push (push) Has been cancelled
Build and Deploy / deploy (push) Has been cancelled
CI / Push to Registry (push) Has been cancelled
CI / Code Quality & Linting (push) Has been cancelled
CI / Unit Tests (push) Has been cancelled
CI / Dependency Vulnerability Scan (push) Has been cancelled
CI / Build Docker Image (push) Has been cancelled
CI / Trivy Scan (push) Has been cancelled
CI / Report Results (push) Has been cancelled
This commit is contained in:
236
.gitea/workflows/ci.yml
Normal file
236
.gitea/workflows/ci.yml
Normal file
@@ -0,0 +1,236 @@
|
|||||||
|
# Universal CI Pipeline — ollama-mcp
|
||||||
|
# Based on: moserja/projectTracker/.gitea/workflows/ci-cd.yml
|
||||||
|
# Plan: https://outline.themosers.club/doc/universal-test-coverage-plan-nHgsMTqBgk
|
||||||
|
|
||||||
|
name: CI
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [main, develop]
|
||||||
|
pull_request:
|
||||||
|
branches: [main]
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
deploy_images:
|
||||||
|
description: 'Build and push Docker image'
|
||||||
|
default: 'false'
|
||||||
|
type: boolean
|
||||||
|
|
||||||
|
env:
|
||||||
|
REGISTRY: ${{ secrets.REGISTRY_URL }}
|
||||||
|
REGISTRY_USERNAME: ${{ secrets.REGISTRY_USERNAME }}
|
||||||
|
REGISTRY_PASSWORD: ${{ secrets.REGISTRY_PASSWORD }}
|
||||||
|
IMAGE_PREFIX: ollama-mcp
|
||||||
|
PYTHON_VERSION: '3.11'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
# ── Stage 1: Code Quality ────────────────────────────────────────────────────
|
||||||
|
code-quality:
|
||||||
|
name: Code Quality & Linting
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- uses: actions/setup-python@v5
|
||||||
|
with:
|
||||||
|
python-version: ${{ env.PYTHON_VERSION }}
|
||||||
|
- name: Lint (ruff + mypy)
|
||||||
|
run: |
|
||||||
|
pip install ruff mypy
|
||||||
|
ruff check src/ --output-format json > ruff-results.json || ruff check src/
|
||||||
|
mypy src/ --ignore-missing-imports || true
|
||||||
|
- uses: actions/upload-artifact@v4
|
||||||
|
if: always()
|
||||||
|
with:
|
||||||
|
name: lint-results
|
||||||
|
path: ruff-results.json
|
||||||
|
retention-days: 30
|
||||||
|
|
||||||
|
# ── Stage 2: Unit Tests ──────────────────────────────────────────────────────
|
||||||
|
unit-tests:
|
||||||
|
name: Unit Tests
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- uses: actions/setup-python@v5
|
||||||
|
with:
|
||||||
|
python-version: ${{ env.PYTHON_VERSION }}
|
||||||
|
- name: Run tests
|
||||||
|
run: |
|
||||||
|
pip install -r requirements.txt
|
||||||
|
pip install pytest pytest-cov pytest-asyncio
|
||||||
|
pytest --cov=src --cov-report=xml --cov-fail-under=70 \
|
||||||
|
--junitxml=test-results/junit.xml -v || echo "No tests or tests failed"
|
||||||
|
- uses: actions/upload-artifact@v4
|
||||||
|
if: always()
|
||||||
|
with:
|
||||||
|
name: test-results
|
||||||
|
path: test-results/
|
||||||
|
retention-days: 30
|
||||||
|
|
||||||
|
# ── Stage 3: Dependency Scan ─────────────────────────────────────────────────
|
||||||
|
dependency-scan:
|
||||||
|
name: Dependency Vulnerability Scan
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- uses: actions/setup-python@v5
|
||||||
|
with:
|
||||||
|
python-version: ${{ env.PYTHON_VERSION }}
|
||||||
|
- name: pip-audit
|
||||||
|
continue-on-error: true
|
||||||
|
run: |
|
||||||
|
pip install pip-audit
|
||||||
|
pip-audit -r requirements.txt --format json > pip-audit.json || true
|
||||||
|
pip-audit -r requirements.txt
|
||||||
|
- uses: actions/upload-artifact@v4
|
||||||
|
if: always()
|
||||||
|
with:
|
||||||
|
name: dependency-scan-results
|
||||||
|
path: pip-audit.json
|
||||||
|
retention-days: 30
|
||||||
|
|
||||||
|
# ── Stage 4: SAST ────────────────────────────────────────────────────────────
|
||||||
|
sast:
|
||||||
|
name: SAST — Semgrep
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: Run Semgrep
|
||||||
|
uses: semgrep/semgrep-action@v1
|
||||||
|
with:
|
||||||
|
config: >-
|
||||||
|
p/python
|
||||||
|
p/docker
|
||||||
|
p/secrets
|
||||||
|
p/owasp-top-ten
|
||||||
|
env:
|
||||||
|
SEMGREP_APP_TOKEN: ''
|
||||||
|
|
||||||
|
# ── Stage 5: Build Docker Image ───────────────────────────────────────────────
|
||||||
|
build-image:
|
||||||
|
name: Build Docker Image
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: [code-quality, unit-tests]
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- uses: docker/setup-buildx-action@v3
|
||||||
|
- name: Generate tags
|
||||||
|
id: meta
|
||||||
|
run: |
|
||||||
|
echo "short_sha=$(echo ${{ github.sha }} | cut -c1-7)" >> $GITHUB_OUTPUT
|
||||||
|
echo "branch=$(echo ${GITHUB_REF#refs/heads/} | sed 's/\//-/g')" >> $GITHUB_OUTPUT
|
||||||
|
- uses: docker/build-push-action@v5
|
||||||
|
with:
|
||||||
|
context: .
|
||||||
|
push: false
|
||||||
|
tags: |
|
||||||
|
${{ env.IMAGE_PREFIX }}/server:${{ steps.meta.outputs.short_sha }}
|
||||||
|
${{ env.IMAGE_PREFIX }}/server:${{ steps.meta.outputs.branch }}
|
||||||
|
${{ env.IMAGE_PREFIX }}/server:latest
|
||||||
|
outputs: type=docker,dest=/tmp/ollama-mcp.tar
|
||||||
|
- uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: docker-image
|
||||||
|
path: /tmp/ollama-mcp.tar
|
||||||
|
retention-days: 1
|
||||||
|
|
||||||
|
# ── Stage 6: Container Scan ───────────────────────────────────────────────────
|
||||||
|
container-scan:
|
||||||
|
name: Trivy Scan
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: build-image
|
||||||
|
steps:
|
||||||
|
- uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
name: docker-image
|
||||||
|
path: /tmp
|
||||||
|
- run: docker load --input /tmp/ollama-mcp.tar
|
||||||
|
- uses: aquasecurity/trivy-action@master
|
||||||
|
with:
|
||||||
|
image-ref: ${{ env.IMAGE_PREFIX }}/server:latest
|
||||||
|
format: sarif
|
||||||
|
output: trivy.sarif
|
||||||
|
severity: CRITICAL,HIGH
|
||||||
|
exit-code: '1'
|
||||||
|
- uses: aquasecurity/trivy-action@master
|
||||||
|
if: always()
|
||||||
|
with:
|
||||||
|
image-ref: ${{ env.IMAGE_PREFIX }}/server:latest
|
||||||
|
format: json
|
||||||
|
output: trivy.json
|
||||||
|
severity: CRITICAL,HIGH
|
||||||
|
exit-code: '0'
|
||||||
|
- uses: actions/upload-artifact@v4
|
||||||
|
if: always()
|
||||||
|
with:
|
||||||
|
name: trivy-results
|
||||||
|
path: trivy.*
|
||||||
|
retention-days: 30
|
||||||
|
|
||||||
|
# ── Stage 7: Results Reporting ────────────────────────────────────────────────
|
||||||
|
report:
|
||||||
|
name: Report Results
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: [code-quality, unit-tests, dependency-scan, sast, container-scan]
|
||||||
|
if: always()
|
||||||
|
steps:
|
||||||
|
- name: Build summary
|
||||||
|
id: summary
|
||||||
|
run: |
|
||||||
|
icon() { [ "$1" = "success" ] && echo "✅" || ([ "$1" = "skipped" ] && echo "⏭️" || echo "❌"); }
|
||||||
|
BODY="## CI Results — \`$(echo ${{ github.sha }} | cut -c1-7)\`
|
||||||
|
|
||||||
|
| Stage | Status |
|
||||||
|
|-------|--------|
|
||||||
|
| Code Quality | $(icon ${{ needs.code-quality.result }}) ${{ needs.code-quality.result }} |
|
||||||
|
| Unit Tests | $(icon ${{ needs.unit-tests.result }}) ${{ needs.unit-tests.result }} |
|
||||||
|
| Dependency Scan | $(icon ${{ needs.dependency-scan.result }}) ${{ needs.dependency-scan.result }} |
|
||||||
|
| SAST (Semgrep) | $(icon ${{ needs.sast.result }}) ${{ needs.sast.result }} |
|
||||||
|
| Container Scan (Trivy) | $(icon ${{ needs.container-scan.result }}) ${{ needs.container-scan.result }} |
|
||||||
|
|
||||||
|
[View artifacts →](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})"
|
||||||
|
echo "body<<EOF" >> $GITHUB_OUTPUT
|
||||||
|
echo "$BODY" >> $GITHUB_OUTPUT
|
||||||
|
echo "EOF" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
- name: Post PR comment
|
||||||
|
if: github.event_name == 'pull_request'
|
||||||
|
run: |
|
||||||
|
curl -s -X POST \
|
||||||
|
"${{ github.api_url }}/repos/${{ github.repository }}/issues/${{ github.event.pull_request.number }}/comments" \
|
||||||
|
-H "Authorization: token ${{ secrets.GITEA_TOKEN }}" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d "$(jq -n --arg body "${{ steps.summary.outputs.body }}" '{"body": $body}')"
|
||||||
|
|
||||||
|
- name: Notify Mattermost
|
||||||
|
run: |
|
||||||
|
OVERALL="${{ needs.container-scan.result }}"
|
||||||
|
[ "${{ needs.unit-tests.result }}" = "failure" ] && OVERALL="failure"
|
||||||
|
COLOR="good"; EMOJI="✅"
|
||||||
|
[ "$OVERALL" = "failure" ] && COLOR="danger" && EMOJI="❌"
|
||||||
|
curl -s -X POST "${{ secrets.MATTERMOST_WEBHOOK_URL }}" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d "{\"attachments\":[{\"color\":\"$COLOR\",\"title\":\"$EMOJI ollama-mcp CI — $(echo ${{ github.sha }} | cut -c1-7)\",\"text\":\"Branch: \`${{ github.ref_name }}\` | [View run](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})\"}]}"
|
||||||
|
|
||||||
|
# ── Push to Registry (main only) ─────────────────────────────────────────────
|
||||||
|
push-image:
|
||||||
|
name: Push to Registry
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: container-scan
|
||||||
|
if: |
|
||||||
|
(github.ref == 'refs/heads/main' && github.event_name == 'push') ||
|
||||||
|
(github.event_name == 'workflow_dispatch' && inputs.deploy_images == true)
|
||||||
|
steps:
|
||||||
|
- uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
name: docker-image
|
||||||
|
path: /tmp
|
||||||
|
- run: docker load --input /tmp/ollama-mcp.tar
|
||||||
|
- run: echo "${{ env.REGISTRY_PASSWORD }}" | docker login ${{ env.REGISTRY }} -u ${{ env.REGISTRY_USERNAME }} --password-stdin
|
||||||
|
- name: Tag and push
|
||||||
|
run: |
|
||||||
|
SHORT_SHA=$(echo ${{ github.sha }} | cut -c1-7)
|
||||||
|
docker tag ${{ env.IMAGE_PREFIX }}/server:latest ${{ env.REGISTRY }}/${{ env.IMAGE_PREFIX }}/server:latest
|
||||||
|
docker tag ${{ env.IMAGE_PREFIX }}/server:latest ${{ env.REGISTRY }}/${{ env.IMAGE_PREFIX }}/server:$SHORT_SHA
|
||||||
|
docker push ${{ env.REGISTRY }}/${{ env.IMAGE_PREFIX }}/server:latest
|
||||||
|
docker push ${{ env.REGISTRY }}/${{ env.IMAGE_PREFIX }}/server:$SHORT_SHA
|
||||||
Reference in New Issue
Block a user