diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9438bf0d..7013f86f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -7,8 +7,41 @@ on: branches: [main] jobs: + # Detect which paths changed so downstream jobs can skip when only + # docs/markdown files were modified. Saves ~15 min of runner time per + # docs-only PR. + changes: + name: Detect changes + runs-on: [self-hosted, macos, arm64] + outputs: + platform: ${{ steps.filter.outputs.platform }} + canvas: ${{ steps.filter.outputs.canvas }} + python: ${{ steps.filter.outputs.python }} + scripts: ${{ steps.filter.outputs.scripts }} + steps: + - uses: actions/checkout@v4 + - uses: dorny/paths-filter@v3 + id: filter + with: + filters: | + platform: + - 'platform/**' + - '.github/workflows/ci.yml' + canvas: + - 'canvas/**' + - '.github/workflows/ci.yml' + python: + - 'workspace-template/**' + - '.github/workflows/ci.yml' + scripts: + - 'tests/e2e/**' + - 'scripts/**' + - '.github/workflows/ci.yml' + platform-build: name: Platform (Go) + needs: changes + if: needs.changes.outputs.platform == 'true' runs-on: [self-hosted, macos, arm64] defaults: run: @@ -43,6 +76,8 @@ jobs: canvas-build: name: Canvas (Next.js) + needs: changes + if: needs.changes.outputs.canvas == 'true' runs-on: [self-hosted, macos, arm64] defaults: run: @@ -67,6 +102,8 @@ jobs: shellcheck: name: Shellcheck (E2E scripts) + needs: changes + if: needs.changes.outputs.scripts == 'true' runs-on: [self-hosted, macos, arm64] steps: - uses: actions/checkout@v4 @@ -84,7 +121,8 @@ jobs: canvas-deploy-reminder: name: Canvas Deploy Reminder runs-on: [self-hosted, macos, arm64] - needs: canvas-build + needs: [changes, canvas-build] + if: needs.changes.outputs.canvas == 'true' # Only fires on direct pushes to main (i.e. after a PR merges). # PRs get canvas-build CI but no reminder — no deployment happens on PRs. if: github.event_name == 'push' && github.ref == 'refs/heads/main' @@ -128,6 +166,8 @@ jobs: python-lint: name: Python Lint & Test + needs: changes + if: needs.changes.outputs.python == 'true' runs-on: [self-hosted, macos, arm64] defaults: run: diff --git a/.github/workflows/e2e-api.yml b/.github/workflows/e2e-api.yml index ed29a00d..8468ebaa 100644 --- a/.github/workflows/e2e-api.yml +++ b/.github/workflows/e2e-api.yml @@ -15,8 +15,16 @@ name: E2E API Smoke Test on: push: branches: [main] + paths: + - 'platform/**' + - 'tests/e2e/**' + - '.github/workflows/e2e-api.yml' pull_request: branches: [main] + paths: + - 'platform/**' + - 'tests/e2e/**' + - '.github/workflows/e2e-api.yml' # Workflow-level concurrency: new runs queue rather than cancel. # `cancel-in-progress: false` is load-bearing — without it GitHub would still diff --git a/CLAUDE.md b/CLAUDE.md index aedab50f..2db4a209 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -266,12 +266,27 @@ All five E2E scripts share `tests/e2e/_lib.sh` + `tests/e2e/_extract_token.py` h The MCP server now lives at **github.com/Molecule-AI/molecule-mcp-server** and is published as `@molecule-ai/mcp-server` on npm. Install: `npx @molecule-ai/mcp-server`. 87 tools for managing Molecule AI from any MCP client. Configured in `.mcp.json`. Env: `MOLECULE_URL` (default http://localhost:8080). ### CI Pipeline -GitHub Actions (`.github/workflows/ci.yml`) runs on push to main and PRs: +GitHub Actions (`.github/workflows/ci.yml`) runs on push to main and PRs. +**Path-filtered:** each job only runs when its relevant files change (via +`dorny/paths-filter`). Docs-only PRs (`docs/**`, `*.md`) skip all jobs, +saving ~15 min of runner time. The path filters are: + +| Job | Triggers on | +|-----|-------------| +| **platform-build** | `platform/**` | +| **canvas-build** | `canvas/**` | +| **python-lint** | `workspace-template/**` | +| **shellcheck** | `tests/e2e/**`, `scripts/**` | +| **e2e-api** | `platform/**`, `tests/e2e/**` | + +All jobs also trigger on `.github/workflows/ci.yml` changes (self-test). + +Job details: - **platform-build**: Go build, vet, `go test -race` with coverage profiling (25% baseline threshold; `setup-go` uses module cache) - **canvas-build**: npm build, `vitest run` (no `--passWithNoTests` -- tests must exist and pass) - **python-lint**: `pytest --cov=. --cov-report=term-missing` (workspace-template tests; SDK + MCP now in standalone repos) -- **e2e-api** (added 2026-04-13): spins up Postgres + Redis service containers, runs platform migrations via `docker exec`, then executes `tests/e2e/test_api.sh` against a locally-built binary (62/62 must pass) -- **shellcheck** (added 2026-04-13): lints every `tests/e2e/*.sh` via the shellcheck marketplace action +- **e2e-api** (`.github/workflows/e2e-api.yml`): spins up Postgres + Redis service containers, runs platform migrations via `docker exec`, then executes `tests/e2e/test_api.sh` against a locally-built binary (62/62 must pass) +- **shellcheck**: lints every `tests/e2e/*.sh` via shellcheck on the self-hosted runner - **publish-platform-image** (`.github/workflows/publish-platform-image.yml`): on push to main touching `platform/**`, builds `platform/Dockerfile` (clones templates + plugins from GitHub via `manifest.json` at build time) and pushes to `ghcr.io/molecule-ai/platform:latest` + `:sha-`. Tenant image uses `platform/Dockerfile.tenant` (combined Go + Canvas). Manual re-trigger via `workflow_dispatch`. **Standalone repo CI** — all 33 plugin + template repos call reusable workflows from `Molecule-AI/molecule-ci`: