From efc938e740e2906a0d69f3d56aec0cd76e316dc0 Mon Sep 17 00:00:00 2001 From: Michael St Clair Date: Sun, 26 Apr 2026 11:41:47 -0600 Subject: [PATCH 1/2] support monorepos --- README.md | 33 +++++++++++++++++++++++++++++++++ dist/index.js | 1 + src/main.ts | 1 + 3 files changed, 35 insertions(+) diff --git a/README.md b/README.md index ae76091..ccb0dc6 100644 --- a/README.md +++ b/README.md @@ -50,6 +50,8 @@ jobs: - `junit_folder`: (Optional) A folder containing JUnit XML files. Make sure the name of each file reflects the name of the test suite. In this way, different test suite runs can be grouped under the same test suite. +- `subdirectory`: (Optional) Path prefix for the project being measured. Set this when reporting coverage for a subproject in a monorepo (e.g. `app`, `packages/web`). Multiple jobs running for the same commit with different `subdirectory` values are stored as separate uploads and rolled up into a single consolidated coverage status (see [Monorepo support](#monorepo-support)). + - `devhub_api_key`: (Required) You will need to create an API key in the settings of your installed app and save it as a secret in GitHub Actions settings. @@ -63,6 +65,37 @@ jobs: - `percentage`: The coverage percentage. +## Monorepo support + +For monorepos with multiple projects under one repository, run the action once per project with a different `subdirectory:`. Each upload is stored separately, and the `coverbot` commit status reflects the **consolidated** total across every upload received for that sha. + +```yaml +jobs: + coverage-app: + steps: + - uses: devhub-tools/coverbot-action@v1 + with: + format: elixir + file: app/cover/excoveralls.json + subdirectory: app + domain: ${{ vars.DEVHUB_DOMAIN }} + devhub_api_key: ${{ secrets.DEVHUB_API_KEY }} + github_token: ${{ secrets.GITHUB_TOKEN }} + + coverage-web: + steps: + - uses: devhub-tools/coverbot-action@v1 + with: + format: lcov + file: web/coverage/lcov.info + subdirectory: web + domain: ${{ vars.DEVHUB_DOMAIN }} + devhub_api_key: ${{ secrets.DEVHUB_API_KEY }} + github_token: ${{ secrets.GITHUB_TOKEN }} +``` + +Each job posts to the same `coverbot` status; whichever finishes second sees both totals summed in the message (e.g. `30 lines covered out of 200 (15.00%)`). + ## Add the coverage badge to your README.md ```markdown diff --git a/dist/index.js b/dist/index.js index c504920..4845c89 100644 --- a/dist/index.js +++ b/dist/index.js @@ -36712,6 +36712,7 @@ function run() { relevant, percentage, files, + subdirectory: subdirectory || null, owner: repoOwner, repo, default_branch: (_a = github.context.payload.repository) === null || _a === void 0 ? void 0 : _a.default_branch, diff --git a/src/main.ts b/src/main.ts index 46f3500..1ee227d 100644 --- a/src/main.ts +++ b/src/main.ts @@ -33,6 +33,7 @@ async function run(): Promise { relevant, percentage, files, + subdirectory: subdirectory || null, owner: repoOwner, repo, default_branch: github.context.payload.repository?.default_branch, From d4a075fccbb3791fa0f6b44b1941cde8c2aa92ff Mon Sep 17 00:00:00 2001 From: Michael St Clair Date: Sun, 26 Apr 2026 11:54:20 -0600 Subject: [PATCH 2/2] add monorepo support --- dist/index.js | 2 +- src/main.ts | 2 +- src/post-coverage.ts | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/dist/index.js b/dist/index.js index 4845c89..59ee053 100644 --- a/dist/index.js +++ b/dist/index.js @@ -36749,7 +36749,7 @@ function run() { } octokit.rest.checks.update(Object.assign(Object.assign({}, github.context.repo), { check_run_id: checkRun.id, conclusion: res.result.state })); if (relevantForPatch && relevantForPatch > 0) { - octokit.rest.repos.createCommitStatus(Object.assign(Object.assign({}, github.context.repo), { sha: res.result.sha, state: Number(patchPercentage) >= Number(percentage) ? "success" : "failure", context: "coverbot (patch)", description: `${coveredForPatch} lines covered out of ${relevantForPatch} (${patchPercentage}%)`, target_url: `https://${domain}/coverbot/coverage/${res.result.id}` })); + octokit.rest.repos.createCommitStatus(Object.assign(Object.assign({}, github.context.repo), { sha: res.result.sha, state: Number(patchPercentage) >= Number(percentage) ? "success" : "failure", context: "coverbot (patch)", description: `${coveredForPatch} lines covered out of ${relevantForPatch} (${patchPercentage}%)`, target_url: `https://${domain}/coverbot/coverage/${res.result.repository_id}/${res.result.sha}` })); } } } diff --git a/src/main.ts b/src/main.ts index 1ee227d..f85da53 100644 --- a/src/main.ts +++ b/src/main.ts @@ -106,7 +106,7 @@ async function run(): Promise { state: Number(patchPercentage) >= Number(percentage) ? "success" : "failure", context: "coverbot (patch)", description: `${coveredForPatch} lines covered out of ${relevantForPatch} (${patchPercentage}%)`, - target_url: `https://${domain}/coverbot/coverage/${res.result.id}`, + target_url: `https://${domain}/coverbot/coverage/${res.result.repository_id}/${res.result.sha}`, }) } } diff --git a/src/post-coverage.ts b/src/post-coverage.ts index 2c97fe7..973e610 100644 --- a/src/post-coverage.ts +++ b/src/post-coverage.ts @@ -4,6 +4,7 @@ import { TypedResponse } from "@actions/http-client/lib/interfaces" type CoverageResponse = { id: string + repository_id: string sha: string state: "failure" | "success" message: string