release: pyproc-worker 0.1.1 (PyPI) #290
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: CI | |
| on: | |
| push: | |
| branches: [ main, master ] | |
| pull_request: | |
| branches: [ "**" ] | |
| permissions: | |
| contents: write | |
| jobs: | |
| go: | |
| name: Go ${{ matrix.task }} | |
| runs-on: ubuntu-latest | |
| strategy: | |
| matrix: | |
| task: [test, lint, build] | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| - name: Setup Go | |
| uses: actions/setup-go@v5 | |
| with: | |
| go-version-file: go.mod | |
| cache: true | |
| # Test task | |
| - name: Setup Python for tests | |
| if: matrix.task == 'test' | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: '3.10' | |
| - name: Install Python worker package | |
| if: matrix.task == 'test' | |
| run: | | |
| cd worker/python | |
| pip install -e . | |
| cd ../.. | |
| - name: Test with coverage | |
| if: matrix.task == 'test' | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| pkgs=$(go list ./... | grep -v '/bench') | |
| if [ -n "$pkgs" ]; then | |
| go test -timeout 5m -v -race -coverprofile=coverage.out -covermode=atomic $pkgs | |
| fi | |
| - name: Check Go coverage is 100% | |
| if: matrix.task == 'test' | |
| continue-on-error: true | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| if [ -f coverage.out ]; then | |
| coverage=$(go tool cover -func=coverage.out | grep total | awk '{print $3}' | sed 's/%//') | |
| echo "Total coverage: ${coverage}%" | |
| if (( $(echo "$coverage < 100" | bc -l) )); then | |
| echo "⚠️ Coverage ${coverage}% is below 100% (currently non-blocking)" | |
| echo "Goal: Achieve 100% coverage before re-enabling strict enforcement" | |
| else | |
| echo "✅ Coverage is 100%" | |
| fi | |
| fi | |
| - name: Upload coverage to Codecov | |
| if: matrix.task == 'test' && github.event_name == 'push' | |
| uses: codecov/codecov-action@v5 | |
| with: | |
| token: ${{ secrets.CODECOV_TOKEN }} | |
| files: ./coverage.out | |
| fail_ci_if_error: false | |
| - name: Update coverage report | |
| if: matrix.task == 'test' && github.event_name == 'push' | |
| uses: ncruces/go-coverage-report@v0 | |
| with: | |
| report: true | |
| chart: true | |
| amend: true | |
| continue-on-error: true | |
| # Lint task | |
| - name: Go fmt check | |
| if: matrix.task == 'lint' | |
| run: | | |
| fmt_out=$(gofmt -l . || true) | |
| if [ -n "$fmt_out" ]; then | |
| echo "Run 'go fmt' on the following files:" >&2 | |
| echo "$fmt_out" >&2 | |
| exit 1 | |
| fi | |
| - name: golangci-lint | |
| if: matrix.task == 'lint' | |
| uses: golangci/golangci-lint-action@v6 | |
| with: | |
| version: latest | |
| args: --timeout=5m | |
| # Build task | |
| - name: Go vet | |
| if: matrix.task == 'build' | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| pkgs=$(go list ./... | grep -v '/bench') | |
| if [ -n "$pkgs" ]; then | |
| echo "$pkgs" | xargs -r -n1 go vet | |
| fi | |
| - name: Build all packages | |
| if: matrix.task == 'build' | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| pkgs=$(go list ./... | grep -v '/bench') | |
| if [ -n "$pkgs" ]; then | |
| echo "$pkgs" | xargs -r -n1 go build | |
| fi | |
| - name: Build examples | |
| if: matrix.task == 'build' | |
| run: go build ./examples/basic | |
| python: | |
| name: Python lint and tests (uv) | |
| runs-on: ubuntu-latest | |
| defaults: | |
| run: | |
| working-directory: worker/python | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| - name: Setup uv and Python | |
| uses: astral-sh/setup-uv@v4 | |
| - name: Sync dependencies | |
| run: uv sync --all-extras --dev | |
| - name: Ruff check | |
| run: uv run ruff check . | |
| - name: Ruff format check | |
| run: uv run ruff format --check . | |
| - name: ty check | |
| run: uv run ty check . | |
| - name: Pytest with coverage | |
| run: uv run pytest -q --cov=pyproc_worker --cov-report=term-missing --cov-report=xml | |
| - name: Check Python coverage is 100% | |
| continue-on-error: true | |
| run: | | |
| coverage=$(uv run coverage report --precision=2 | grep TOTAL | awk '{print $4}' | sed 's/%//') | |
| echo "Total coverage: ${coverage}%" | |
| if (( $(echo "$coverage < 100" | bc -l) )); then | |
| echo "⚠️ Coverage ${coverage}% is below 100% (currently non-blocking)" | |
| echo "Goal: Achieve 100% coverage before re-enabling strict enforcement" | |
| else | |
| echo "✅ Coverage is 100%" | |
| fi | |
| benchmark: | |
| name: Performance benchmarks and gates | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| - name: Setup Go | |
| uses: actions/setup-go@v5 | |
| with: | |
| go-version-file: go.mod | |
| cache: true | |
| - name: Setup Python with uv | |
| uses: astral-sh/setup-uv@v4 | |
| - name: Install Python dependencies | |
| run: | | |
| cd worker/python | |
| uv sync --all-extras | |
| - name: Run benchmarks | |
| run: | | |
| cd bench | |
| go test -bench=BenchmarkLatencyPercentiles -benchmem -benchtime=10s -timeout=5m | tee benchmark_results.txt || true | |
| # Extract and check performance metrics | |
| if grep -q "p50:" benchmark_results.txt; then | |
| echo "=== Performance Results ===" | |
| grep -E "p50:|p95:|p99:" benchmark_results.txt | |
| # Extract p50 value (in microseconds) | |
| p50=$(grep "p50:" benchmark_results.txt | sed -E 's/.*p50: ([0-9.]+).*/\1/') | |
| p99=$(grep "p99:" benchmark_results.txt | sed -E 's/.*p99: ([0-9.]+).*/\1/') | |
| echo "" | |
| echo "Checking performance gates..." | |
| # Check p50 < 100µs | |
| if [ ! -z "$p50" ]; then | |
| if (( $(echo "$p50 > 100" | bc -l) )); then | |
| echo "⚠️ p50 latency ${p50}µs exceeds target of 100µs (non-blocking)" | |
| else | |
| echo "✅ p50 latency ${p50}µs meets target (<100µs)" | |
| fi | |
| fi | |
| # Check p99 < 500µs | |
| if [ ! -z "$p99" ]; then | |
| if (( $(echo "$p99 > 500" | bc -l) )); then | |
| echo "⚠️ p99 latency ${p99}µs exceeds target of 500µs (non-blocking)" | |
| else | |
| echo "✅ p99 latency ${p99}µs meets target (<500µs)" | |
| fi | |
| fi | |
| else | |
| echo "No latency metrics found in benchmark results" | |
| fi | |
| - name: Upload benchmark results | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: benchmark-results | |
| path: bench/benchmark_results.txt | |
| pr-language: | |
| name: PR language check (English) | |
| if: github.event_name == 'pull_request' | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Check PR title and body are in English | |
| uses: actions/github-script@v7 | |
| with: | |
| script: | | |
| const title = context.payload.pull_request.title; | |
| const body = context.payload.pull_request.body || ''; | |
| // Detect non-ASCII heavy content (CJK, Cyrillic, etc.) | |
| const nonAsciiRatio = (str) => { | |
| if (!str || str.length === 0) return 0; | |
| const nonAscii = str.match(/[^\x00-\x7F]/g) || []; | |
| return nonAscii.length / str.length; | |
| }; | |
| const titleRatio = nonAsciiRatio(title); | |
| const bodyRatio = nonAsciiRatio(body); | |
| console.log(`Title non-ASCII ratio: ${(titleRatio * 100).toFixed(1)}%`); | |
| console.log(`Body non-ASCII ratio: ${(bodyRatio * 100).toFixed(1)}%`); | |
| const threshold = 0.3; | |
| const issues = []; | |
| if (titleRatio > threshold) { | |
| issues.push(`PR title contains ${(titleRatio * 100).toFixed(0)}% non-ASCII characters. Please use English.`); | |
| } | |
| if (body.length > 0 && bodyRatio > threshold) { | |
| issues.push(`PR body contains ${(bodyRatio * 100).toFixed(0)}% non-ASCII characters. Please use English.`); | |
| } | |
| if (issues.length > 0) { | |
| core.setFailed(issues.join('\n')); | |
| } else { | |
| console.log('PR title and body are in English.'); | |
| } | |
| links: | |
| name: README and docs link check | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| - name: Link Checker | |
| uses: lycheeverse/lychee-action@v2 | |
| with: | |
| args: --config .lychee.toml README.md "docs/**/*.md" | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| sbom: | |
| name: SBOM generation | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| - name: Install syft | |
| uses: anchore/sbom-action/download-syft@v0 | |
| - name: Generate Go SBOM | |
| run: syft dir:. --exclude ./worker/python -o cyclonedx-json=sbom-go.cdx.json | |
| - name: Generate Python SBOM | |
| run: syft dir:worker/python -o cyclonedx-json=sbom-python.cdx.json | |
| - name: Upload SBOMs | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: sbom | |
| path: | | |
| sbom-go.cdx.json | |
| sbom-python.cdx.json | |
| k8s-manifests: | |
| name: K8s manifest validation | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| - name: Install Helm | |
| uses: azure/setup-helm@v4 | |
| - name: Install kustomize | |
| uses: imranismail/setup-kustomize@v2 | |
| - name: Helm lint | |
| run: helm lint charts/pyproc/ | |
| - name: Helm template | |
| run: helm template test charts/pyproc/ | |
| - name: Kustomize build - dev | |
| run: kustomize build deploy/kustomize/overlays/dev | |
| - name: Kustomize build - staging | |
| run: kustomize build deploy/kustomize/overlays/staging | |
| - name: Kustomize build - production | |
| run: kustomize build deploy/kustomize/overlays/production | |