Fix Rust SDK Tutorial Issues #3029
Workflow file for this run
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 ] | |
| pull_request: | |
| branches: [ main ] | |
| schedule: | |
| - cron: "0 6 * * *" # Daily at 06:00 UTC | |
| concurrency: | |
| group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} | |
| cancel-in-progress: true | |
| permissions: | |
| contents: read | |
| jobs: | |
| # ── Path detection — determines which jobs to run ───────────────────── | |
| changes: | |
| runs-on: ubuntu-latest | |
| outputs: | |
| python: ${{ steps.filter.outputs.python }} | |
| dotnet: ${{ steps.filter.outputs.dotnet }} | |
| typescript: ${{ steps.filter.outputs.typescript }} | |
| integrations: ${{ steps.filter.outputs.integrations }} | |
| rust: ${{ steps.filter.outputs.rust }} | |
| go: ${{ steps.filter.outputs.go }} | |
| workflows: ${{ steps.filter.outputs.workflows }} | |
| docs-only: ${{ steps.filter.outputs.docs-only }} | |
| docker: ${{ steps.filter.outputs.docker }} | |
| changed-py-pkgs: ${{ steps.py-pkgs.outputs.list }} | |
| steps: | |
| - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 | |
| - uses: dorny/paths-filter@fbd0ab8f3e69293af611ebaee6363fc25e6d187d # v4.0.1 | |
| id: filter | |
| with: | |
| filters: | | |
| python: | |
| - 'agent-governance-python/agent-os/**' | |
| - 'agent-governance-python/agent-mesh/**' | |
| - 'agent-governance-python/agent-hypervisor/**' | |
| - 'agent-governance-python/agent-sre/**' | |
| - 'agent-governance-python/agent-compliance/**' | |
| - 'agent-governance-python/agent-runtime/**' | |
| - 'agent-governance-python/agent-lightning/**' | |
| - 'agent-governance-python/agent-primitives/**' | |
| - 'agent-governance-python/agent-mcp-governance/**' | |
| - 'agent-governance-python/agent-marketplace/**' | |
| - 'agent-governance-python/agent-rag-governance/**' | |
| - 'scripts/**' | |
| - 'agent-governance-python/requirements/**' | |
| dotnet: | |
| - 'agent-governance-dotnet/**' | |
| typescript: | |
| - 'agent-governance-typescript/**' | |
| - 'agent-governance-python/agent-os/extensions/**' | |
| - 'agent-governance-python/agentmesh-integrations/mastra-agentmesh/**' | |
| - 'agent-governance-python/agentmesh-integrations/copilot-governance/**' | |
| integrations: | |
| - 'agent-governance-python/agentmesh-integrations/**' | |
| workflows: | |
| - '.github/workflows/**' | |
| rust: | |
| - 'agent-governance-rust/**' | |
| go: | |
| - 'agent-governance-golang/**' | |
| docs-only: | |
| - '**/*.md' | |
| - 'agent-governance-python/notebooks/**' | |
| - 'docs/**' | |
| docker: | |
| - 'Dockerfile' | |
| - 'docker-compose*.yml' | |
| - 'scripts/docker/**' | |
| - '.gitattributes' | |
| - 'agent-governance-python/**/pyproject.toml' | |
| - 'agent-governance-python/agent-hypervisor/examples/dashboard/requirements.txt' | |
| - '.github/workflows/ci.yml' | |
| pkg-agent-os: | |
| - 'agent-governance-python/agent-os/**' | |
| pkg-agent-mesh: | |
| - 'agent-governance-python/agent-mesh/**' | |
| pkg-agent-hypervisor: | |
| - 'agent-governance-python/agent-hypervisor/**' | |
| pkg-agent-sre: | |
| - 'agent-governance-python/agent-sre/**' | |
| pkg-agent-compliance: | |
| - 'agent-governance-python/agent-compliance/**' | |
| pkg-agent-runtime: | |
| - 'agent-governance-python/agent-runtime/**' | |
| pkg-agent-lightning: | |
| - 'agent-governance-python/agent-lightning/**' | |
| - name: Compose changed-py-pkgs JSON list | |
| id: py-pkgs | |
| env: | |
| AO: ${{ steps.filter.outputs.pkg-agent-os }} | |
| AM: ${{ steps.filter.outputs.pkg-agent-mesh }} | |
| AH: ${{ steps.filter.outputs.pkg-agent-hypervisor }} | |
| AS: ${{ steps.filter.outputs.pkg-agent-sre }} | |
| AC: ${{ steps.filter.outputs.pkg-agent-compliance }} | |
| AR: ${{ steps.filter.outputs.pkg-agent-runtime }} | |
| AL: ${{ steps.filter.outputs.pkg-agent-lightning }} | |
| run: | | |
| set -euo pipefail | |
| pkgs=() | |
| [ "$AO" = "true" ] && pkgs+=('"agent-os"') | |
| [ "$AM" = "true" ] && pkgs+=('"agent-mesh"') | |
| [ "$AH" = "true" ] && pkgs+=('"agent-hypervisor"') | |
| [ "$AS" = "true" ] && pkgs+=('"agent-sre"') | |
| [ "$AC" = "true" ] && pkgs+=('"agent-compliance"') | |
| [ "$AR" = "true" ] && pkgs+=('"agent-runtime"') | |
| [ "$AL" = "true" ] && pkgs+=('"agent-lightning"') | |
| ifs=$IFS; IFS=,; list="[${pkgs[*]:-}]"; IFS=$ifs | |
| echo "list=$list" >> "$GITHUB_OUTPUT" | |
| echo "Changed Python packages: $list" | |
| # ── Python lint + test (only when Python files change) ──────────────── | |
| lint: | |
| needs: changes | |
| if: needs.changes.outputs.python == 'true' || github.event_name == 'schedule' | |
| runs-on: ubuntu-latest | |
| strategy: | |
| matrix: | |
| package: | |
| [ | |
| agent-os, | |
| agent-mesh, | |
| agent-hypervisor, | |
| agent-sre, | |
| agent-compliance, | |
| agent-runtime, | |
| agent-lightning, | |
| agent-rag-governance, | |
| ] | |
| steps: | |
| - name: Determine if package changed | |
| id: gate | |
| env: | |
| CHANGED: ${{ needs.changes.outputs.changed-py-pkgs }} | |
| PKG: ${{ matrix.package }} | |
| EVENT: ${{ github.event_name }} | |
| run: | | |
| set -euo pipefail | |
| if [ "$EVENT" = "schedule" ] || [ "$EVENT" = "push" ]; then | |
| echo "run=true" >> "$GITHUB_OUTPUT" | |
| elif printf '%s' "$CHANGED" | grep -q "\"$PKG\""; then | |
| echo "run=true" >> "$GITHUB_OUTPUT" | |
| else | |
| echo "run=false" >> "$GITHUB_OUTPUT" | |
| echo "::notice::Package $PKG unchanged on this PR — skipping lint." | |
| fi | |
| - if: steps.gate.outputs.run == 'true' | |
| uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 | |
| - if: steps.gate.outputs.run == 'true' | |
| uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 | |
| with: | |
| python-version: "3.11" | |
| - if: steps.gate.outputs.run == 'true' | |
| name: Install ruff | |
| run: pip install --require-hashes --no-cache-dir -r agent-governance-python/requirements/ci-lint.txt | |
| - if: steps.gate.outputs.run == 'true' | |
| name: Lint ${{ matrix.package }} | |
| working-directory: agent-governance-python/${{ matrix.package }} | |
| run: ruff check src/ --select E,F,W --ignore E501 | |
| # ── Python test (only when Python files change) ─────────────────────── | |
| test: | |
| needs: changes | |
| # Always run so matrix check names are reported to branch protection. | |
| # Steps are skipped when no Python files changed. | |
| runs-on: ubuntu-latest | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| package: | |
| [ | |
| agent-os, | |
| agent-mesh, | |
| agent-hypervisor, | |
| agent-sre, | |
| agent-compliance, | |
| agent-runtime, | |
| agent-lightning, | |
| agent-rag-governance, | |
| ] | |
| python-version: [ "3.10", "3.11", "3.12", "3.13" ] | |
| exclude: | |
| - package: agent-os | |
| python-version: "3.10" | |
| - package: agent-mesh | |
| python-version: "3.10" | |
| - package: agent-hypervisor | |
| python-version: "3.10" | |
| - package: agent-compliance | |
| python-version: "3.10" | |
| - package: agent-runtime | |
| python-version: "3.10" | |
| - package: agent-lightning | |
| python-version: "3.10" | |
| env: | |
| RUN_TESTS: ${{ needs.changes.outputs.python == 'true' || github.event_name == 'schedule' }} | |
| steps: | |
| - name: Skip (no Python changes) | |
| if: env.RUN_TESTS != 'true' | |
| run: echo "No Python changes — skipping tests" | |
| - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 | |
| if: env.RUN_TESTS == 'true' | |
| - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 | |
| if: env.RUN_TESTS == 'true' | |
| with: | |
| python-version: ${{ matrix.python-version }} | |
| - name: Install local sibling dependencies | |
| if: env.RUN_TESTS == 'true' | |
| run: | | |
| # agent-os depends on agent_primitives (local, not on PyPI at >=3.x) | |
| # and agentmesh (test_cmd_sign.py imports agentmesh.marketplace) | |
| if [ "${{ matrix.package }}" = "agent-os" ]; then | |
| pip install --no-cache-dir -e agent-governance-python/agent-primitives | |
| pip install --no-cache-dir -e agent-governance-python/agent-mesh | |
| fi | |
| - name: Install ${{ matrix.package }} | |
| if: env.RUN_TESTS == 'true' | |
| working-directory: agent-governance-python/${{ matrix.package }} | |
| run: | | |
| pip install --no-cache-dir -e ".[dev]" 2>/dev/null || pip install --no-cache-dir -e ".[test]" 2>/dev/null || pip install --no-cache-dir -e . # Install local package (Scorecard: pinned via pyproject.toml) | |
| pip install --no-cache-dir pytest==8.4.1 pytest-asyncio==0.26.0 2>/dev/null || true | |
| - name: Test ${{ matrix.package }} | |
| if: env.RUN_TESTS == 'true' | |
| working-directory: agent-governance-python/${{ matrix.package }} | |
| run: pytest tests/ -q --tb=short | |
| # ── PyPI package build (only when Python files change) ──────────────── | |
| build-pypi: | |
| needs: changes | |
| if: needs.changes.outputs.python == 'true' || github.event_name == 'schedule' | |
| runs-on: ubuntu-latest | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| package: | |
| [ | |
| agent-os, | |
| agent-mesh, | |
| agent-hypervisor, | |
| agent-sre, | |
| agent-compliance, | |
| agent-runtime, | |
| agent-lightning, | |
| agent-rag-governance, | |
| ] | |
| steps: | |
| - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 | |
| - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 | |
| with: | |
| python-version: "3.11" | |
| - name: Install build tools | |
| run: pip install --no-cache-dir build==1.2.2 setuptools==75.8.0 | |
| - name: Build ${{ matrix.package }} | |
| working-directory: agent-governance-python/${{ matrix.package }} | |
| run: python -m build | |
| - name: Verify wheel | |
| working-directory: agent-governance-python/${{ matrix.package }} | |
| run: ls -la dist/*.whl | |
| # ── Python dependency safety (only when Python files change) ────────── | |
| security: | |
| needs: changes | |
| if: needs.changes.outputs.python == 'true' || github.event_name == 'schedule' | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 | |
| - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 | |
| with: | |
| python-version: "3.11" | |
| - name: Install safety | |
| run: | | |
| pip install --no-cache-dir safety==3.2.1 | |
| - name: Check dependencies | |
| env: | |
| GIT_TERMINAL_PROMPT: "0" | |
| run: | | |
| for pkg in agent-os agent-mesh agent-hypervisor agent-sre agent-compliance agent-runtime agent-lightning; do | |
| echo "=== $pkg ===" | |
| cd agent-governance-python/$pkg | |
| pip install --no-cache-dir -e . 2>/dev/null || true # Install local package (Scorecard: pinned via pyproject.toml) | |
| cd ../.. | |
| done | |
| safety check 2>/dev/null || echo "Safety check completed with warnings" | |
| # ── .NET build + test (only when C# files change) ──────────────────── | |
| test-dotnet: | |
| needs: changes | |
| if: needs.changes.outputs.dotnet == 'true' || github.event_name == 'schedule' | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 | |
| - uses: actions/setup-dotnet@c2fa09f4bde5ebb9d1777cf28262a3eb3db3ced7 # v5.2.0 | |
| with: | |
| dotnet-version: "8.0.x" | |
| - name: Build .NET SDK | |
| working-directory: agent-governance-dotnet | |
| run: dotnet build --configuration Release --verbosity quiet | |
| - name: Test .NET SDK | |
| working-directory: agent-governance-dotnet | |
| run: dotnet test --configuration Release --verbosity normal --no-build | |
| - name: Pack NuGet | |
| working-directory: agent-governance-dotnet | |
| run: dotnet pack src/AgentGovernance/AgentGovernance.csproj --configuration | |
| Release --no-build --output ./nupkg | |
| - name: Verify NuGet package | |
| working-directory: agent-governance-dotnet | |
| run: ls -la ./nupkg/*.nupkg | |
| - name: BinSkim — binary security analysis | |
| working-directory: agent-governance-dotnet | |
| run: | | |
| dotnet tool install --global Microsoft.CodeAnalysis.BinSkim --version 4.* 2>/dev/null || true | |
| BinSkim analyze "src/AgentGovernance/bin/Release/net8.0/*.dll" \ | |
| --output binskim-results.sarif \ | |
| --verbose 2>/dev/null || echo "BinSkim completed with warnings" | |
| - name: Upload BinSkim SARIF | |
| if: always() | |
| uses: github/codeql-action/upload-sarif@e46ed2cbd01164d986452f91f178727624ae40d7 # v4.35.3 | |
| with: | |
| sarif_file: agent-governance-dotnet/binskim-results.sarif | |
| category: binskim | |
| continue-on-error: true | |
| # ── Integration tests (only when integration packages change) ──────── | |
| test-integrations: | |
| needs: changes | |
| if: needs.changes.outputs.integrations == 'true' || needs.changes.outputs.python | |
| == 'true' || github.event_name == 'schedule' | |
| runs-on: ubuntu-latest | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| - package: a2a-protocol | |
| import-module: a2a_agentmesh | |
| - package: crewai-agentmesh | |
| import-module: crewai_agentmesh | |
| - package: flowise-agentmesh | |
| import-module: flowise_agentmesh | |
| - package: haystack-agentmesh | |
| import-module: haystack_agentmesh | |
| - package: langchain-agentmesh | |
| import-module: langchain_agentmesh | |
| - package: langflow-agentmesh | |
| import-module: langflow_agentmesh | |
| - package: langgraph-trust | |
| import-module: langgraph_trust | |
| - package: llamaindex-agentmesh | |
| import-module: llama_index.agent.agentmesh | |
| - package: mcp-trust-proxy | |
| import-module: mcp_trust_proxy | |
| - package: nostr-wot | |
| import-module: agentmesh_nostr_wot | |
| - package: openai-agents-agentmesh | |
| import-module: openai_agents_agentmesh | |
| - package: openai-agents-trust | |
| import-module: openai_agents_trust | |
| - package: pydantic-ai-governance | |
| import-module: pydantic_ai_governance | |
| steps: | |
| - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 | |
| - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 | |
| with: | |
| python-version: "3.11" | |
| - name: Install ${{ matrix.package }} | |
| working-directory: agent-governance-python/agentmesh-integrations/${{ matrix.package }} | |
| run: | | |
| pip install --no-cache-dir -e ".[dev]" 2>/dev/null || pip install --no-cache-dir -e ".[test]" 2>/dev/null || pip install --no-cache-dir -e . # Install local package (Scorecard: pinned via pyproject.toml) | |
| pip install --no-cache-dir pytest==8.4.1 pytest-asyncio==0.26.0 2>/dev/null || true | |
| - name: Validate Python syntax | |
| working-directory: agent-governance-python/agentmesh-integrations/${{ matrix.package }} | |
| run: | | |
| python -c " | |
| import ast, glob, sys | |
| errors = 0 | |
| for f in glob.glob('**/*.py', recursive=True): | |
| try: | |
| with open(f) as fh: | |
| ast.parse(fh.read(), f) | |
| except SyntaxError as e: | |
| print(f'FAIL {f}: {e}') | |
| errors += 1 | |
| if errors: | |
| sys.exit(1) | |
| print('All Python files parse successfully') | |
| " | |
| - name: Smoke test — import ${{ matrix.import-module }} | |
| run: python -c "import ${{ matrix.import-module }}" | |
| continue-on-error: true | |
| - name: Run tests | |
| working-directory: agent-governance-python/agentmesh-integrations/${{ matrix.package }} | |
| run: | | |
| if [ -d tests ]; then | |
| pytest tests/ -q --tb=short | |
| else | |
| echo "No tests/ directory — smoke import passed" | |
| fi | |
| # ── Dependency confusion scan (always runs — security gate) ────────── | |
| dep-confusion-scan: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 | |
| - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 | |
| with: | |
| python-version: "3.11" | |
| - name: Dependency confusion scan | |
| run: python3 scripts/check_dependency_confusion.py --strict | |
| # ── Notebook pip-install audit (always runs — security gate) ───────── | |
| notebook-pip-audit: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 | |
| - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 | |
| with: | |
| python-version: "3.11" | |
| - name: Notebook pip-install audit | |
| run: | | |
| python3 -c " | |
| import json, glob, sys, re | |
| REGISTERED = { | |
| 'agent-os-kernel','agentmesh-platform','agent-hypervisor', | |
| 'agentmesh-runtime','agent-sre','agent-governance-toolkit', | |
| 'agentmesh-lightning','agentmesh-marketplace', | |
| 'pydantic','pyyaml','cryptography','pynacl','click','rich', | |
| 'httpx','aiohttp','fastapi','uvicorn','structlog','numpy', | |
| 'scipy','openai','anthropic','langchain','crewai', | |
| 'streamlit','plotly','pandas','networkx','aioredis', | |
| 'langchain-openai','langchain-core','python-dotenv', | |
| 'agent-primitives','agentmesh-memory','emk', | |
| } | |
| bad = [] | |
| for nb in glob.glob('**/*.ipynb', recursive=True): | |
| if 'node_modules' in nb or '.ipynb_checkpoints' in nb: | |
| continue | |
| try: | |
| with open(nb, encoding='utf-8') as fh: | |
| cells = json.load(fh)['cells'] | |
| except Exception: | |
| continue | |
| for c in cells: | |
| for line in c.get('source', []): | |
| if 'pip install' in line and not line.strip().startswith('#') and not line.strip().startswith('>'): | |
| pkgs = re.findall(r'(?:pip install\s+)(.+)', line) | |
| if pkgs: | |
| for p in pkgs[0].split(): | |
| name = re.sub(r'[^a-zA-Z0-9._-]', '', re.sub(r'\[.*\]', '', p)) | |
| if (name and not name.startswith('-') and not name.startswith('.') | |
| and not name.startswith('http') and name not in REGISTERED | |
| and not name.startswith('--')): | |
| bad.append(f'{nb}: {name}') | |
| if bad: | |
| print('UNREGISTERED PACKAGES IN NOTEBOOKS:') | |
| for b in bad: | |
| print(f' {b}') | |
| sys.exit(1) | |
| print(f'OK: All notebook pip install packages are registered') | |
| " | |
| # ── Markdown link check (checks all .md files in the repo) | |
| # Approach adapted from gaurav-nelson/github-action-markdown-link-check | |
| markdown-link-check: | |
| needs: changes | |
| if: github.event_name == 'schedule' || needs.changes.outputs['docs-only'] == 'true' | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 | |
| with: | |
| fetch-depth: 0 | |
| - name: Markdown link check | |
| uses: gaurav-nelson/github-action-markdown-link-check@v1 | |
| with: | |
| use-quiet-mode: 'yes' | |
| use-verbose-mode: 'no' | |
| config-file: '.github/linters/markdown-link-check.json' | |
| # Configure it to check all .md files in the repo | |
| check-modified-files-only: 'no' | |
| base-branch: main | |
| # ── Workflow security audit (only when workflows change) ───────────── | |
| workflow-security: | |
| needs: changes | |
| if: needs.changes.outputs.workflows == 'true' || github.event_name == 'schedule' | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 | |
| - name: Audit pull_request_target workflows | |
| run: | | |
| echo "=== Checking pull_request_target safety ===" | |
| UNSAFE=0 | |
| for f in .github/workflows/*.yml; do | |
| if grep -q 'pull_request_target' "$f"; then | |
| # Only flag if actions/checkout has ref: pointing to head (unsafe) | |
| # Uses awk to check checkout blocks specifically, not unrelated lines | |
| if awk '/actions\/checkout/{found=1} found && /ref:.*head\.(ref|sha)/{print; exit 1}' "$f" 2>/dev/null; then | |
| echo "OK: $f (pull_request_target, base-only checkout)" | |
| else | |
| echo "UNSAFE: $f checks out PR head in pull_request_target context" | |
| UNSAFE=1 | |
| fi | |
| fi | |
| done | |
| if [ $UNSAFE -eq 1 ]; then exit 1; fi | |
| # ── TypeScript integration tests (only when TS files change) ───────── | |
| test-integrations-ts: | |
| needs: changes | |
| if: needs.changes.outputs.typescript == 'true' || github.event_name == | |
| 'schedule' | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 | |
| - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 | |
| with: | |
| node-version: "20" | |
| - name: Install mastra-agentmesh | |
| working-directory: agent-governance-python/agentmesh-integrations/mastra-agentmesh | |
| run: npm ci 2>/dev/null || npm install --ignore-scripts | |
| - name: Lint mastra-agentmesh | |
| working-directory: agent-governance-python/agentmesh-integrations/mastra-agentmesh | |
| run: npm run lint 2>/dev/null || true | |
| - name: Test mastra-agentmesh | |
| working-directory: agent-governance-python/agentmesh-integrations/mastra-agentmesh | |
| run: npm test | |
| - name: Install copilot-governance | |
| working-directory: agent-governance-python/agentmesh-integrations/copilot-governance | |
| run: npm ci 2>/dev/null || npm install --ignore-scripts | |
| - name: Lint copilot-governance | |
| working-directory: agent-governance-python/agentmesh-integrations/copilot-governance | |
| run: npm run lint 2>/dev/null || true | |
| - name: Test copilot-governance | |
| working-directory: agent-governance-python/agentmesh-integrations/copilot-governance | |
| run: npm test | |
| # ── npm package build + test (only when TS files change) ────────────── | |
| build-npm: | |
| needs: changes | |
| if: needs.changes.outputs.typescript == 'true' || github.event_name == | |
| 'schedule' | |
| runs-on: ubuntu-latest | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| - name: agentmesh-mcp-proxy | |
| path: agent-governance-python/agent-mesh/packages/mcp-proxy | |
| - name: agent-governance-sdk | |
| path: agent-governance-typescript | |
| - name: agentmesh-api | |
| path: agent-governance-python/agent-mesh/services/api | |
| - name: agent-os-copilot-extension | |
| path: agent-governance-python/agent-os/extensions/copilot | |
| - name: agentos-mcp-server | |
| path: agent-governance-python/agent-os/extensions/mcp-server | |
| steps: | |
| - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 | |
| - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 | |
| with: | |
| node-version: "20" | |
| - name: Install dependencies | |
| working-directory: ${{ matrix.path }} | |
| run: npm ci --legacy-peer-deps 2>/dev/null || npm install --legacy-peer-deps | |
| --ignore-scripts | |
| - name: Build ${{ matrix.name }} | |
| working-directory: ${{ matrix.path }} | |
| run: npm run build | |
| - name: Test ${{ matrix.name }} | |
| working-directory: ${{ matrix.path }} | |
| run: npm test 2>/dev/null || echo "No tests configured" | |
| # ── Rust build + test (only when Rust files change) ────────────────── | |
| build-rust: | |
| needs: changes | |
| if: needs.changes.outputs.rust == 'true' || github.event_name == 'schedule' | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 | |
| - uses: dtolnay/rust-toolchain@29eef336d9b2848a0b548edc03f92a220660cdb8 # stable | |
| - name: Build | |
| working-directory: agent-governance-rust | |
| run: cargo build --release --workspace | |
| - name: Test | |
| working-directory: agent-governance-rust | |
| run: cargo test --release --workspace | |
| # ── Go build + test (only when Go files change) ───────────────────── | |
| build-go: | |
| needs: changes | |
| if: needs.changes.outputs.go == 'true' || github.event_name == 'schedule' | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 | |
| - uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0 | |
| with: | |
| go-version: "1.22" | |
| - name: Build | |
| working-directory: agent-governance-golang | |
| run: go build ./... | |
| - name: Test | |
| working-directory: agent-governance-golang | |
| run: go test ./... | |
| - name: Vet | |
| working-directory: agent-governance-golang | |
| run: go vet ./... | |
| # ── Docker integrated test (matches the documented contributor flow) ── | |
| # Runs the exact two commands from CONTRIBUTING.md "Docker Quickstart" so | |
| # contributor-facing breakage is caught before merge to main. Catches | |
| # shim/canonical drift, Dockerfile/compose drift, line-ending regressions, | |
| # and any bug that only surfaces in the integrated install shape. | |
| docker-compose-test: | |
| needs: changes | |
| if: | | |
| needs.changes.outputs.docker == 'true' || | |
| needs.changes.outputs.python == 'true' || | |
| github.event_name == 'schedule' | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 20 | |
| permissions: | |
| contents: read | |
| steps: | |
| - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 | |
| - name: Diagnostic — Docker / Compose / Buildx versions | |
| run: | | |
| docker version | |
| docker compose version | |
| docker buildx version | |
| - name: Set up Docker Buildx | |
| uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0 | |
| - name: Run documented contributor flow | |
| run: | | |
| # Compute and validate host UID/GID so the test container can write | |
| # to the bind-mounted workspace. Validate they are pure positive | |
| # integers before exporting (defense-in-depth: the values come from | |
| # `id` on the runner, but we still refuse to forward anything that | |
| # is not a non-negative integer to docker compose). | |
| HOST_UID="$(id -u)" | |
| HOST_GID="$(id -g)" | |
| if ! [[ "${HOST_UID}" =~ ^[0-9]+$ ]] || ! [[ "${HOST_GID}" =~ ^[0-9]+$ ]]; then | |
| echo "::error::Refusing to run: id -u/-g returned non-numeric value (uid='${HOST_UID}' gid='${HOST_GID}')" | |
| exit 1 | |
| fi | |
| export HOST_UID HOST_GID | |
| echo "Running compose with HOST_UID=${HOST_UID} HOST_GID=${HOST_GID}" | |
| docker compose up --build dev -d | |
| docker compose run --rm test | |
| - name: Collect dev container logs | |
| if: always() | |
| run: | | |
| # Capture and redact obvious secret-shaped tokens before the log is | |
| # uploaded as a build artifact. The dev container does not receive | |
| # GitHub secrets, but we still scrub conservatively so that any | |
| # accidental leakage from a future change does not surface here. | |
| docker compose logs dev > dev.log.raw 2>&1 || true | |
| sed -E \ | |
| -e 's/(gh[pousr]_[A-Za-z0-9]{20,})/[REDACTED-GH-TOKEN]/g' \ | |
| -e 's/(sk-[A-Za-z0-9_-]{20,})/[REDACTED-API-KEY]/g' \ | |
| -e 's/(AKIA[A-Z0-9]{16})/[REDACTED-AWS-ACCESS-KEY]/g' \ | |
| -e 's/((password|secret|token|api_?key)[[:space:]]*[:=][[:space:]]*)[^[:space:]"]+/\1[REDACTED]/Ig' \ | |
| dev.log.raw > dev.log | |
| rm -f dev.log.raw | |
| - name: Upload artifacts on failure | |
| if: failure() | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 | |
| with: | |
| name: docker-compose-test-logs | |
| path: dev.log | |
| retention-days: 7 | |
| - name: Tear down | |
| if: always() | |
| run: docker compose down -v | |
| # ── CI Gate — required status check that handles skipped jobs ──────── | |
| # When path-filters skip jobs (e.g. docs-only PRs skip tests), those | |
| # jobs report "skipped" which doesn't satisfy required status checks. | |
| # This gate job always runs, checks that no jobs FAILED, and reports | |
| # success. Configure this as the single required status check. | |
| ci-complete: | |
| if: always() | |
| needs: | |
| [ | |
| changes, | |
| lint, | |
| test, | |
| build-pypi, | |
| security, | |
| test-dotnet, | |
| test-integrations, | |
| dep-confusion-scan, | |
| notebook-pip-audit, | |
| workflow-security, | |
| test-integrations-ts, | |
| build-npm, | |
| build-rust, | |
| build-go, | |
| docker-compose-test, | |
| markdown-link-check, | |
| ] | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 | |
| - name: Check job results | |
| env: | |
| NEEDS: ${{ toJSON(needs) }} | |
| run: | | |
| echo "Job results: $NEEDS" | |
| set +e | |
| failed=$(printf '%s' "$NEEDS" | python3 scripts/ci_complete_check.py) | |
| status=$? | |
| set -e | |
| if [ "$status" -eq 1 ]; then | |
| echo "::error::Failed jobs: $failed" | |
| { | |
| echo "## ci-complete: failures" | |
| echo "" | |
| echo "Failed jobs:" | |
| echo "$failed" | tr ',' '\n' | sed 's/^/- /' | |
| } >> "$GITHUB_STEP_SUMMARY" | |
| exit 1 | |
| elif [ "$status" -ne 0 ]; then | |
| echo "::error::Unable to parse ci-complete dependency results" | |
| exit "$status" | |
| fi | |
| # AC3.4 — docker-compose-test must not be silently skipped on relevant PRs | |
| docker_changed='${{ needs.changes.outputs.docker }}' | |
| python_changed='${{ needs.changes.outputs.python }}' | |
| docker_test_result='${{ needs.docker-compose-test.result }}' | |
| event_name='${{ github.event_name }}' | |
| if [ "$docker_changed" = "true" ] || [ "$python_changed" = "true" ] || [ "$event_name" = "schedule" ]; then | |
| if [ "$docker_test_result" = "skipped" ]; then | |
| echo "::error::docker-compose-test was skipped on a PR that touched docker- or python-relevant paths." | |
| echo "::error::This violates AC3.4 — the gate must run when changes.outputs.docker or python is true." | |
| exit 1 | |
| fi | |
| fi | |
| echo "All required jobs succeeded." | |