Skip to content

Commit 5aefd5a

Browse files
committed
fix: simplify codecov calls
1 parent 066e12b commit 5aefd5a

3 files changed

Lines changed: 155 additions & 103 deletions

File tree

.github/workflows/release.yml

Lines changed: 26 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -62,84 +62,29 @@ jobs:
6262
6363
- name: Upload coverage to Codecov
6464
if: always()
65-
uses: codecov/codecov-action@v5
66-
with:
67-
token: ${{ secrets.CODECOV_TOKEN }}
68-
flags: core
69-
files: tests/Flowthru.Tests/**/coverage.cobertura.xml
70-
fail_ci_if_error: false
71-
72-
- name: Upload coverage (funit)
73-
if: always()
74-
uses: codecov/codecov-action@v5
75-
with:
76-
token: ${{ secrets.CODECOV_TOKEN }}
77-
flags: funit
78-
files: tests/Flowthru.FUnit.Tests/**/coverage.cobertura.xml
79-
fail_ci_if_error: false
80-
81-
- name: Upload coverage (examples)
82-
if: always()
83-
uses: codecov/codecov-action@v5
84-
with:
85-
token: ${{ secrets.CODECOV_TOKEN }}
86-
flags: examples
87-
files: tests/Flowthru.Tests.Examples/**/coverage.cobertura.xml
88-
fail_ci_if_error: false
89-
90-
- name: Upload coverage (templates)
91-
if: always()
92-
uses: codecov/codecov-action@v5
93-
with:
94-
token: ${{ secrets.CODECOV_TOKEN }}
95-
flags: templates
96-
files: tests/Flowthru.Tests.Templates/**/coverage.cobertura.xml
97-
fail_ci_if_error: false
98-
99-
- name: Upload coverage (spark)
100-
if: always()
101-
uses: codecov/codecov-action@v5
102-
with:
103-
token: ${{ secrets.CODECOV_TOKEN }}
104-
flags: spark
105-
files: tests/Flowthru.Tests.Spark/**/coverage.cobertura.xml
106-
fail_ci_if_error: false
107-
108-
- name: Upload coverage (ext-efcore)
109-
if: always()
110-
uses: codecov/codecov-action@v5
111-
with:
112-
token: ${{ secrets.CODECOV_TOKEN }}
113-
flags: ext-efcore
114-
files: tests/Flowthru.Extensions.EFCore.Tests/**/coverage.cobertura.xml
115-
fail_ci_if_error: false
116-
117-
- name: Upload coverage (ext-gql)
118-
if: always()
119-
uses: codecov/codecov-action@v5
120-
with:
121-
token: ${{ secrets.CODECOV_TOKEN }}
122-
flags: ext-gql
123-
files: tests/Flowthru.Extensions.GQL.Tests/**/coverage.cobertura.xml
124-
fail_ci_if_error: false
125-
126-
- name: Upload coverage (ext-python)
127-
if: always()
128-
uses: codecov/codecov-action@v5
129-
with:
130-
token: ${{ secrets.CODECOV_TOKEN }}
131-
flags: ext-python
132-
files: tests/Flowthru.Extensions.Python.Tests/**/coverage.cobertura.xml
133-
fail_ci_if_error: false
134-
135-
- name: Upload coverage (ext-spark)
136-
if: always()
137-
uses: codecov/codecov-action@v5
138-
with:
139-
token: ${{ secrets.CODECOV_TOKEN }}
140-
flags: ext-spark
141-
files: tests/Flowthru.Extensions.Spark.Tests/**/coverage.cobertura.xml
142-
fail_ci_if_error: false
65+
env:
66+
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
67+
run: |
68+
pip install codecov-cli --quiet
69+
# For each test project root, find all coverage files and upload them
70+
# together under one flag named after the project directory.
71+
while IFS= read -r -d '' coverage_file; do
72+
dir=$(dirname "$coverage_file")
73+
# Walk up until we find a directory containing a *.csproj or project.json
74+
while [[ "$dir" != "." && "$dir" != "/" ]]; do
75+
if compgen -G "${dir}/*.csproj" > /dev/null 2>&1 || [[ -f "${dir}/project.json" ]]; then
76+
break
77+
fi
78+
dir=$(dirname "$dir")
79+
done
80+
flag=$(basename "$dir")
81+
echo "Uploading $coverage_file as flag: $flag"
82+
codecovcli upload-coverage \
83+
--flag "$flag" \
84+
--file "$coverage_file" \
85+
--handle-no-reports-found \
86+
|| true
87+
done < <(find tests/ -name "coverage.cobertura.xml" -print0)
14388
14489
- name: Configure Git
14590
run: |
@@ -161,6 +106,9 @@ jobs:
161106
echo "tag=${BASE_TAG}" >> "$GITHUB_OUTPUT"
162107
echo "Base tag for release: ${BASE_TAG}"
163108
109+
- name: Sync Codecov flags
110+
run: node scripts/sync-codecov-flags.mjs
111+
164112
- name: Run NX Release (version + changelog + commit + tag)
165113
id: release
166114
env:

codecov.yml

Lines changed: 23 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
codecov:
22
require_ci_to_pass: true
3-
43
coverage:
54
status:
65
project:
@@ -10,37 +9,36 @@ coverage:
109
patch:
1110
default:
1211
target: 80%
13-
12+
comment:
13+
layout: 'reach,diff,flags,components'
14+
behavior: default
15+
require_changes: false
1416
flags:
15-
core:
16-
paths: []
17+
Flowthru.Analyzers.Tests:
1718
carryforward: true
18-
funit:
19-
paths: []
19+
Flowthru.Extensions.EFCore.Tests:
2020
carryforward: true
21-
examples:
22-
paths: []
21+
Flowthru.Extensions.GQL.Tests:
2322
carryforward: true
24-
templates:
25-
paths: []
23+
Flowthru.Extensions.Python.Tests:
2624
carryforward: true
27-
spark:
28-
paths: []
25+
Flowthru.Extensions.Spark.Tests:
2926
carryforward: true
30-
ext-efcore:
31-
paths: []
27+
Flowthru.FUnit.Tests:
3228
carryforward: true
33-
ext-gql:
34-
paths: []
29+
Flowthru.Tests:
3530
carryforward: true
36-
ext-python:
37-
paths: []
31+
Flowthru.Tests.Examples:
3832
carryforward: true
39-
ext-spark:
40-
paths: []
33+
Flowthru.Tests.Spark:
34+
carryforward: true
35+
Flowthru.Tests.Templates:
36+
carryforward: true
37+
KedroIrisFUnit:
38+
carryforward: true
39+
KedroSpaceflights.Custom:
40+
carryforward: true
41+
KedroSpaceflightsFUnit:
42+
carryforward: true
43+
SpaceflightsDistributed:
4144
carryforward: true
42-
43-
comment:
44-
layout: "reach,diff,flags,components"
45-
behavior: default
46-
require_changes: false

scripts/sync-codecov-flags.mjs

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
#!/usr/bin/env node
2+
/**
3+
* Synchronizes the `flags` section of codecov.yml with the set of NX
4+
* projects that have a `test` target.
5+
*
6+
* Flag naming convention: basename of the project's root directory.
7+
* e.g. tests/Flowthru.Tests → Flowthru.Tests
8+
*
9+
* Intended to be run before `scripts/release.mjs` so the updated
10+
* codecov.yml is included in the release commit.
11+
*
12+
* Usage:
13+
* node scripts/sync-codecov-flags.mjs # update in place + git add
14+
* node scripts/sync-codecov-flags.mjs --dry-run # preview without side effects
15+
*/
16+
17+
import { execSync } from 'node:child_process';
18+
import { readFileSync, writeFileSync } from 'node:fs';
19+
import { basename, resolve, dirname } from 'node:path';
20+
import { fileURLToPath } from 'node:url';
21+
import yaml from 'js-yaml';
22+
23+
const __dirname = dirname(fileURLToPath(import.meta.url));
24+
const ROOT = resolve(__dirname, '..');
25+
const DRY_RUN = process.argv.includes('--dry-run');
26+
const CODECOV_YML = resolve(ROOT, 'codecov.yml');
27+
28+
// ── 1. Discover all projects with a test target ───────────────────────────────
29+
30+
const projectsJson = execSync('pnpm nx show projects --with-target=test --json', {
31+
cwd: ROOT,
32+
encoding: 'utf8',
33+
});
34+
const projectNames = JSON.parse(projectsJson);
35+
36+
// ── 2. Map each project to a flag name (basename of root directory) ───────────
37+
//
38+
// Projects whose root is outside the repo's source tree (e.g. the NX
39+
// github-actions project rooted at .github/) cannot produce .NET coverage
40+
// files and are excluded.
41+
42+
const EXCLUDED_ROOTS = ['.github'];
43+
44+
const flagNames = [];
45+
46+
for (const name of projectNames) {
47+
let projectJson;
48+
try {
49+
projectJson = JSON.parse(
50+
execSync(`pnpm nx show project "${name}" --json`, {
51+
cwd: ROOT,
52+
encoding: 'utf8',
53+
stdio: ['pipe', 'pipe', 'ignore'],
54+
})
55+
);
56+
} catch {
57+
console.warn(`Warning: could not inspect project "${name}" — skipping.`);
58+
continue;
59+
}
60+
61+
const root = projectJson.root;
62+
if (!root) {
63+
console.warn(`Warning: project "${name}" has no root — skipping.`);
64+
continue;
65+
}
66+
67+
if (EXCLUDED_ROOTS.includes(root)) {
68+
continue;
69+
}
70+
71+
flagNames.push(basename(root));
72+
}
73+
74+
flagNames.sort((a, b) => a.localeCompare(b));
75+
76+
console.log(`Derived ${flagNames.length} flags:`);
77+
for (const flag of flagNames) {
78+
console.log(` ${flag}`);
79+
}
80+
81+
// ── 3. Update flags in codecov.yml ───────────────────────────────────────────
82+
83+
const raw = readFileSync(CODECOV_YML, 'utf8');
84+
const doc = yaml.load(raw);
85+
86+
const newFlags = Object.fromEntries(
87+
flagNames.map(f => [f, { carryforward: true }])
88+
);
89+
90+
doc.flags = newFlags;
91+
92+
const updated = yaml.dump(doc, { lineWidth: -1 });
93+
94+
if (updated === raw) {
95+
console.log('\ncodecov.yml flags already up to date — no changes needed.');
96+
process.exit(0);
97+
}
98+
99+
if (!DRY_RUN) {
100+
writeFileSync(CODECOV_YML, updated, 'utf8');
101+
execSync(`git add "${CODECOV_YML}"`);
102+
console.log('\n✓ Updated codecov.yml and staged for commit.');
103+
} else {
104+
console.log('\n[dry-run] Would write the following to codecov.yml:');
105+
console.log(updated);
106+
}

0 commit comments

Comments
 (0)