diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5d54642..269fb9c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -30,6 +30,15 @@ jobs: - run: npx nx run-many -t lint test build typecheck --exclude domscribe-test-fixtures + - name: Update coverage badge + if: github.ref == 'refs/heads/main' && github.event_name == 'push' + env: + GH_TOKEN: ${{ secrets.GIST_SECRET }} + run: | + npx nx run domscribe:coverage-badge + gh api --method PATCH "gists/${{ vars.COVERAGE_GIST_ID }}" \ + -f "files[domscribe-coverage.json][content]=$(cat coverage/shields.json)" + build-registry: runs-on: ubuntu-latest needs: checks diff --git a/README.md b/README.md index 90980a6..caf2b92 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@
-
+
diff --git a/package.json b/package.json
index 2904496..5a0436d 100644
--- a/package.json
+++ b/package.json
@@ -108,6 +108,13 @@
"command": "node scripts/sync-versions.mjs"
}
},
+ "coverage-badge": {
+ "executor": "nx:run-commands",
+ "dependsOn": ["^test"],
+ "options": {
+ "command": "node scripts/aggregate-coverage.mjs"
+ }
+ },
"local-registry": {
"executor": "@nx/js:verdaccio",
"options": {
diff --git a/packages/domscribe-core/vite.config.ts b/packages/domscribe-core/vite.config.ts
index 188efa4..e3ab15f 100644
--- a/packages/domscribe-core/vite.config.ts
+++ b/packages/domscribe-core/vite.config.ts
@@ -12,6 +12,7 @@ export default defineConfig({
coverage: {
enabled: true,
provider: 'v8',
+ reporter: ['text', 'json-summary'],
reportsDirectory: './test-output/vitest/coverage',
thresholds: {
lines: 0.8,
diff --git a/packages/domscribe-manifest/vite.config.ts b/packages/domscribe-manifest/vite.config.ts
index 7cda7a0..32f30b9 100644
--- a/packages/domscribe-manifest/vite.config.ts
+++ b/packages/domscribe-manifest/vite.config.ts
@@ -12,6 +12,7 @@ export default defineConfig({
coverage: {
enabled: true,
provider: 'v8',
+ reporter: ['text', 'json-summary'],
reportsDirectory: './test-output/vitest/coverage',
thresholds: {
lines: 0.8,
diff --git a/packages/domscribe-next/vite.config.ts b/packages/domscribe-next/vite.config.ts
index 574faab..90b7650 100644
--- a/packages/domscribe-next/vite.config.ts
+++ b/packages/domscribe-next/vite.config.ts
@@ -23,6 +23,7 @@ export default defineConfig({
coverage: {
enabled: true,
provider: 'v8',
+ reporter: ['text', 'json-summary'],
reportsDirectory: './test-output/vitest/coverage',
thresholds: {
lines: 0.8,
diff --git a/packages/domscribe-nuxt/vite.config.ts b/packages/domscribe-nuxt/vite.config.ts
index 91deaec..d92c94b 100644
--- a/packages/domscribe-nuxt/vite.config.ts
+++ b/packages/domscribe-nuxt/vite.config.ts
@@ -20,6 +20,7 @@ export default defineConfig({
coverage: {
enabled: true,
provider: 'v8',
+ reporter: ['text', 'json-summary'],
reportsDirectory: './test-output/vitest/coverage',
thresholds: {
lines: 0.8,
diff --git a/packages/domscribe-overlay/vite.config.ts b/packages/domscribe-overlay/vite.config.ts
index a97af07..9541cf0 100644
--- a/packages/domscribe-overlay/vite.config.ts
+++ b/packages/domscribe-overlay/vite.config.ts
@@ -21,6 +21,7 @@ export default defineConfig({
coverage: {
enabled: true,
provider: 'v8',
+ reporter: ['text', 'json-summary'],
reportsDirectory: './test-output/vitest/coverage',
thresholds: {
lines: 0,
diff --git a/packages/domscribe-react/vite.config.ts b/packages/domscribe-react/vite.config.ts
index 1a78cac..9ba1983 100644
--- a/packages/domscribe-react/vite.config.ts
+++ b/packages/domscribe-react/vite.config.ts
@@ -12,6 +12,7 @@ export default defineConfig({
coverage: {
enabled: true,
provider: 'v8',
+ reporter: ['text', 'json-summary'],
reportsDirectory: './test-output/vitest/coverage',
thresholds: {
lines: 0.8,
diff --git a/packages/domscribe-relay/vite.config.ts b/packages/domscribe-relay/vite.config.ts
index 67ca8f6..caf1641 100644
--- a/packages/domscribe-relay/vite.config.ts
+++ b/packages/domscribe-relay/vite.config.ts
@@ -12,6 +12,7 @@ export default defineConfig({
coverage: {
enabled: true,
provider: 'v8',
+ reporter: ['text', 'json-summary'],
reportsDirectory: './test-output/vitest/coverage',
include: ['src/**/*.ts'],
exclude: [
diff --git a/packages/domscribe-runtime/vite.config.ts b/packages/domscribe-runtime/vite.config.ts
index 46ebee5..d46aa7f 100644
--- a/packages/domscribe-runtime/vite.config.ts
+++ b/packages/domscribe-runtime/vite.config.ts
@@ -12,6 +12,7 @@ export default defineConfig({
coverage: {
enabled: true,
provider: 'v8',
+ reporter: ['text', 'json-summary'],
reportsDirectory: './test-output/vitest/coverage',
thresholds: {
lines: 0.8,
diff --git a/packages/domscribe-transform/vite.config.ts b/packages/domscribe-transform/vite.config.ts
index 0d39fac..0c1144f 100644
--- a/packages/domscribe-transform/vite.config.ts
+++ b/packages/domscribe-transform/vite.config.ts
@@ -12,6 +12,7 @@ export default defineConfig({
coverage: {
enabled: true,
provider: 'v8',
+ reporter: ['text', 'json-summary'],
reportsDirectory: './test-output/vitest/coverage',
thresholds: {
lines: 0.8,
diff --git a/packages/domscribe-vue/vite.config.ts b/packages/domscribe-vue/vite.config.ts
index 804a893..84b1904 100644
--- a/packages/domscribe-vue/vite.config.ts
+++ b/packages/domscribe-vue/vite.config.ts
@@ -12,6 +12,7 @@ export default defineConfig({
coverage: {
enabled: true,
provider: 'v8',
+ reporter: ['text', 'json-summary'],
reportsDirectory: './test-output/vitest/coverage',
thresholds: {
lines: 0.8,
diff --git a/scripts/aggregate-coverage.mjs b/scripts/aggregate-coverage.mjs
new file mode 100644
index 0000000..7deec60
--- /dev/null
+++ b/scripts/aggregate-coverage.mjs
@@ -0,0 +1,89 @@
+/**
+ * @module aggregate-coverage
+ *
+ * Reads coverage-summary.json from each package's test-output directory,
+ * computes a weighted-average line coverage percentage across all packages,
+ * and writes a shields.io-compatible JSON endpoint to coverage/shields.json.
+ */
+
+import {
+ readFileSync,
+ writeFileSync,
+ mkdirSync,
+ existsSync,
+ readdirSync,
+} from 'fs';
+import { resolve, dirname, join } from 'path';
+
+const ROOT = resolve(dirname(new URL(import.meta.url).pathname), '..');
+
+const summaryFiles = readdirSync(join(ROOT, 'packages'))
+ .filter((d) => d.startsWith('domscribe-'))
+ .map((d) =>
+ join('packages', d, 'test-output/vitest/coverage/coverage-summary.json'),
+ )
+ .filter((f) => existsSync(join(ROOT, f)));
+
+if (summaryFiles.length === 0) {
+ console.error(
+ 'No coverage-summary.json files found. Run tests with coverage first.',
+ );
+ process.exit(1);
+}
+
+let totalCovered = 0;
+let totalStatements = 0;
+
+for (const file of summaryFiles) {
+ const abs = resolve(ROOT, file);
+ const summary = JSON.parse(readFileSync(abs, 'utf8'));
+ const totals = summary.total;
+
+ if (!totals?.statements) {
+ console.warn(`Skipping ${file} — no total.statements`);
+ continue;
+ }
+
+ totalCovered += totals.statements.covered;
+ totalStatements += totals.statements.total;
+
+ const pkg = file.split('/')[1];
+ const pct = totals.statements.pct;
+ console.log(` ${pkg}: ${pct}%`);
+}
+
+if (totalStatements === 0) {
+ console.error('No statement data found across packages.');
+ process.exit(1);
+}
+
+const percentage = ((totalCovered / totalStatements) * 100).toFixed(1);
+
+const color =
+ percentage >= 90
+ ? 'brightgreen'
+ : percentage >= 80
+ ? 'green'
+ : percentage >= 70
+ ? 'yellowgreen'
+ : percentage >= 60
+ ? 'yellow'
+ : 'red';
+
+const badge = {
+ schemaVersion: 1,
+ label: 'coverage',
+ message: `${percentage}%`,
+ color,
+};
+
+const outDir = resolve(ROOT, 'coverage');
+if (!existsSync(outDir)) {
+ mkdirSync(outDir, { recursive: true });
+}
+
+const outPath = resolve(outDir, 'shields.json');
+writeFileSync(outPath, JSON.stringify(badge, null, 2) + '\n');
+
+console.log(`\nAggregate coverage: ${percentage}% (${summaryFiles.length} packages)`);
+console.log(`Badge JSON written to ${outPath}`);