diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml new file mode 100644 index 000000000..c4843d511 --- /dev/null +++ b/.github/workflows/coverage.yml @@ -0,0 +1,65 @@ +name: PR Diff Coverage + +on: + pull_request: + branches: [main] + +concurrency: + group: coverage-${{ github.ref }} + cancel-in-progress: true + +jobs: + diff-coverage: + name: Diff Coverage (80% on changed code) + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - uses: pnpm/action-setup@v4 + - uses: actions/setup-node@v4 + with: + node-version: 20 + cache: pnpm + + - uses: actions/setup-python@v5 + with: + python-version: "3.x" + + - name: Install diff-cover + run: pip install diff-cover + + - name: Install tmux + run: sudo apt-get update && sudo apt-get install -y tmux + + - name: Start tmux server + run: tmux start-server + + - run: pnpm install --frozen-lockfile + - run: pnpm -r --filter '!@composio/ao-web' build + + - name: Core tests with coverage + run: pnpm --filter @composio/ao-core exec vitest run --coverage + + - name: CLI tests with coverage + run: pnpm --filter @composio/ao-cli exec vitest run --coverage + + - name: Web tests with coverage + run: pnpm --filter @composio/ao-web exec vitest run --coverage + + - name: Prepare lcov reports + run: | + mkdir -p combined-coverage + # Prefix source paths so they match git diff (repo-root-relative) + sed 's|^SF:|SF:packages/core/|' packages/core/coverage/lcov.info > combined-coverage/core.info + sed 's|^SF:|SF:packages/cli/|' packages/cli/coverage/lcov.info > combined-coverage/cli.info + sed 's|^SF:|SF:packages/web/|' packages/web/coverage/lcov.info > combined-coverage/web.info + cat combined-coverage/core.info combined-coverage/cli.info combined-coverage/web.info > combined-coverage/lcov.info + + - name: Check diff coverage (80% on changed lines) + run: | + diff-cover combined-coverage/lcov.info \ + --compare-branch=origin/${{ github.base_ref }} \ + --fail-under=80 \ + --diff-range-notation='...' diff --git a/packages/cli/vitest.config.ts b/packages/cli/vitest.config.ts index e717490f6..8379aa72b 100644 --- a/packages/cli/vitest.config.ts +++ b/packages/cli/vitest.config.ts @@ -11,5 +11,9 @@ export default defineConfig({ maxThreads: 8, }, }, + coverage: { + provider: "v8", + reporter: ["lcov"], + }, }, }); diff --git a/packages/core/vitest.config.ts b/packages/core/vitest.config.ts index 32cbe7b9b..4d31bb5dd 100644 --- a/packages/core/vitest.config.ts +++ b/packages/core/vitest.config.ts @@ -16,5 +16,11 @@ export default defineConfig({ ), "@composio/ao-plugin-scm-github": resolve(__dirname, "../plugins/scm-github/src/index.ts"), }, + coverage: { + provider: "v8", + reporter: ["lcov"], + include: ["src/**/*.ts"], + exclude: ["src/__tests__/**", "src/index.ts", "src/recovery/index.ts"], + }, }, }); diff --git a/packages/web/package.json b/packages/web/package.json index 02ce3eb0f..962df764c 100644 --- a/packages/web/package.json +++ b/packages/web/package.json @@ -62,6 +62,7 @@ "tailwindcss": "^4.0.0", "tsx": "^4.19.0", "typescript": "^5.7.0", + "@vitest/coverage-v8": "^2.1.0", "vitest": "^2.1.0" } } diff --git a/packages/web/vitest.config.ts b/packages/web/vitest.config.ts index f3cbb2328..d0f48db74 100644 --- a/packages/web/vitest.config.ts +++ b/packages/web/vitest.config.ts @@ -9,6 +9,11 @@ export default defineConfig({ environment: "jsdom", setupFiles: ["./src/__tests__/setup.ts"], include: ["src/**/*.test.{ts,tsx}", "server/**/*.test.ts"], + coverage: { + provider: "v8", + reporter: ["lcov"], + include: ["src/**/*.{ts,tsx}", "server/**/*.ts"], + }, }, resolve: { alias: { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b54aa87ff..d77082c42 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -649,6 +649,9 @@ importers: '@vitejs/plugin-react': specifier: ^4.3.0 version: 4.7.0(vite@5.4.21(@types/node@25.2.3)(lightningcss@1.30.2)) + '@vitest/coverage-v8': + specifier: ^2.1.0 + version: 2.1.9(vitest@2.1.9(@types/node@25.2.3)(jsdom@25.0.1)(lightningcss@1.30.2)) concurrently: specifier: ^9.2.1 version: 9.2.1 @@ -803,6 +806,9 @@ packages: resolution: {integrity: sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==} engines: {node: '>=6.9.0'} + '@bcoe/v8-coverage@0.2.3': + resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==} + '@bcoe/v8-coverage@1.0.2': resolution: {integrity: sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA==} engines: {node: '>=18'} @@ -2001,6 +2007,15 @@ packages: peerDependencies: vite: ^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 + '@vitest/coverage-v8@2.1.9': + resolution: {integrity: sha512-Z2cOr0ksM00MpEfyVE8KXIYPEcBFxdbLSs56L8PO0QQMxt/6bDj45uQfxoc96v05KW3clk7vvgP0qfDit9DmfQ==} + peerDependencies: + '@vitest/browser': 2.1.9 + vitest: 2.1.9 + peerDependenciesMeta: + '@vitest/browser': + optional: true + '@vitest/coverage-v8@3.2.4': resolution: {integrity: sha512-EyF9SXU6kS5Ku/U82E259WSnvg6c8KTjppUncuNdm5QHpe17mwREHnjDzozC8x9MZ0xfBUFSaLkRv4TMA75ALQ==} peerDependencies: @@ -4278,6 +4293,8 @@ snapshots: '@babel/helper-string-parser': 7.27.1 '@babel/helper-validator-identifier': 7.28.5 + '@bcoe/v8-coverage@0.2.3': {} + '@bcoe/v8-coverage@1.0.2': {} '@cfworker/json-schema@4.1.1': {} @@ -5385,6 +5402,24 @@ snapshots: transitivePeerDependencies: - supports-color + '@vitest/coverage-v8@2.1.9(vitest@2.1.9(@types/node@25.2.3)(jsdom@25.0.1)(lightningcss@1.30.2))': + dependencies: + '@ampproject/remapping': 2.3.0 + '@bcoe/v8-coverage': 0.2.3 + debug: 4.4.3 + istanbul-lib-coverage: 3.2.2 + istanbul-lib-report: 3.0.1 + istanbul-lib-source-maps: 5.0.6 + istanbul-reports: 3.2.0 + magic-string: 0.30.21 + magicast: 0.3.5 + std-env: 3.10.0 + test-exclude: 7.0.2 + tinyrainbow: 1.2.0 + vitest: 2.1.9(@types/node@25.2.3)(jsdom@25.0.1)(lightningcss@1.30.2) + transitivePeerDependencies: + - supports-color + '@vitest/coverage-v8@3.2.4(vitest@3.2.4(@types/node@25.2.3)(jiti@2.6.1)(jsdom@25.0.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2))': dependencies: '@ampproject/remapping': 2.3.0