Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 35 additions & 0 deletions .github/scripts/post-coverage.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/**
* Posts JMAP spec coverage report as a PR comment.
*
* @param {object} params
* @param {import('@actions/github/lib/utils').GitHub} params.github
* @param {import('@actions/github').context} params.context
*/
module.exports = async ({ github, context }) => {
const report = process.env.COVERAGE_REPORT;

if (!report) {
console.log('No coverage report found in COVERAGE_REPORT env var');
return;
}

const body = `## JMAP Spec Coverage

${report}
`;

const prNumber = context.payload.pull_request?.number;
if (!prNumber) {
console.log('Not a pull request context, skipping comment');
return;
}

await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: prNumber,
body
});

console.log('Coverage comment posted successfully');
};
44 changes: 44 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -129,3 +129,47 @@ jobs:
- name: Stop test server
if: always()
run: pnpm test:server:stop

spec-coverage:
name: Spec Coverage
runs-on: ubuntu-latest
if: github.event_name == 'pull_request'
permissions:
pull-requests: write
steps:
- uses: actions/checkout@v4
with:
submodules: true

- uses: pnpm/action-setup@v4
with:
version: 10.17.0

- uses: actions/setup-node@v4
with:
node-version: "24"
cache: "pnpm"

- name: Install dependencies
run: pnpm install --frozen-lockfile

- name: Run spec tests
run: pnpm test:spec

- name: Generate coverage report
id: coverage
run: |
{
echo 'report<<COVERAGE_EOF'
pnpm coverage:spec --markdown
echo 'COVERAGE_EOF'
} >> "$GITHUB_OUTPUT"

- name: Post coverage comment
uses: actions/github-script@v7
env:
COVERAGE_REPORT: ${{ steps.coverage.outputs.report }}
with:
script: |
const script = require('./.github/scripts/post-coverage.cjs')
await script({ github, context })
34 changes: 34 additions & 0 deletions scripts/spec-coverage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import {
interface CliArgs {
json: boolean
byType: boolean
markdown: boolean
help: boolean
}

Expand All @@ -29,6 +30,7 @@ function parseArgs(): CliArgs {
return {
json: args.includes('--json'),
byType: args.includes('--by-type'),
markdown: args.includes('--markdown'),
help: args.includes('--help') || args.includes('-h')
}
}
Expand All @@ -45,12 +47,14 @@ Usage:
Options:
--json Output as JSON
--by-type Group coverage by object type
--markdown Output as GitHub-flavored markdown table
--help, -h Show this help message

Examples:
pnpm coverage:spec # Show coverage table
pnpm coverage:spec --by-type # Show coverage grouped by type
pnpm coverage:spec --json # Output as JSON for CI/scripts
pnpm coverage:spec --markdown # Output markdown for PR comments
`)
}

Expand Down Expand Up @@ -145,6 +149,31 @@ function printJson() {
console.log(JSON.stringify(result, null, 2))
}

function printMarkdown() {
const byType = getCompletenessByType()
const completeness = getCompleteness()

// Header with overall progress
console.log(`**Overall: ${completeness.implemented}/${completeness.total} methods (${completeness.percentage.toFixed(1)}%)**`)
console.log('')

// Table for each object type
for (const [type, data] of Object.entries(byType).sort((a, b) => a[0].localeCompare(b[0]))) {
console.log(`### ${type} (${data.implemented}/${data.total})`)
console.log('')
console.log('| Method | Status |')
console.log('|--------|--------|')

for (const method of data.methods) {
const name = method.name.split('/')[1]
const status = method.implemented ? '✅' : '⬚'
console.log(`| ${name} | ${status} |`)
}

console.log('')
}
}

function main() {
const args = parseArgs()

Expand All @@ -158,6 +187,11 @@ function main() {
return
}

if (args.markdown) {
printMarkdown()
return
}

if (args.byType) {
printByType()
return
Expand Down