Skip to content

Commit 921cb06

Browse files
committed
ci(coverage): bound grouped upload flags
Multi-cell integrations kept their full normalized name, so Codecov silently dropped flags longer than 45 characters.
1 parent ed4f30c commit 921cb06

2 files changed

Lines changed: 60 additions & 6 deletions

File tree

scripts/group-coverage.mjs

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { createHash } from 'node:crypto'
12
import { copyFileSync, mkdirSync, readdirSync, rmSync, writeFileSync } from 'node:fs'
23
import { join } from 'node:path'
34
import { argv } from 'node:process'
@@ -40,8 +41,28 @@ const VERSION_RE = /^(?:gte|gt|lte|lt|eq)?\.?\d+(?:\.\d+)*(?:\.(?:and|or|gte|gt|
4041
// of at most this many libraries, named for their members so the flag still points at a library.
4142
const MAX_LIBS_PER_BUCKET = 3
4243
// Codecov validates flags against `^[\w\.\-]{1,45}$` and silently drops any that fail. `+` is out, so
43-
// members join with `_`; a name longer than this falls back to a numbered bucket that stays valid.
44+
// members join with `_`; invalid or overlong names receive a deterministic hash suffix.
4445
const MAX_FLAG_LENGTH = 45
46+
const VALID_FLAG_RE = /^[\w.-]{1,45}$/
47+
const HASH_LENGTH = 8
48+
const INSTRUMENTATIONS_PREFIX = 'instrumentations-instrumentation-'
49+
50+
/**
51+
* Keep readable Codecov flags where possible and deterministically bound every other name.
52+
*
53+
* @param {string} flag
54+
* @returns {string}
55+
*/
56+
function codecovFlag (flag) {
57+
const readable = flag.startsWith(INSTRUMENTATIONS_PREFIX)
58+
? `instr-${flag.slice(INSTRUMENTATIONS_PREFIX.length)}`
59+
: flag
60+
if (VALID_FLAG_RE.test(readable)) return readable
61+
62+
const hash = createHash('sha256').update(flag).digest('hex').slice(0, HASH_LENGTH)
63+
const sanitized = readable.replaceAll(/[^\w.-]/g, '_')
64+
return `${sanitized.slice(0, MAX_FLAG_LENGTH - HASH_LENGTH - 1)}-${hash}`
65+
}
4566

4667
/**
4768
* @param {string} token
@@ -132,7 +153,7 @@ function planGroups (cellsByIntegration) {
132153

133154
for (const [integration, cells] of cellsByIntegration) {
134155
if (cells.length > 1) {
135-
groups.set(integration, [integration])
156+
groups.set(codecovFlag(integration), [integration])
136157
continue
137158
}
138159
const area = integration.split('-')[0]
@@ -148,16 +169,14 @@ function planGroups (cellsByIntegration) {
148169
integrations.sort()
149170
if (integrations.length <= 2) {
150171
for (const integration of integrations) {
151-
groups.set(integration, [integration])
172+
groups.set(codecovFlag(integration), [integration])
152173
}
153174
continue
154175
}
155-
let bucket = 0
156176
for (let i = 0; i < integrations.length; i += MAX_LIBS_PER_BUCKET) {
157177
const chunk = integrations.slice(i, i + MAX_LIBS_PER_BUCKET)
158178
const named = `${area}-${chunk.map(integration => integration.slice(area.length + 1)).join('_')}`
159-
groups.set(named.length <= MAX_FLAG_LENGTH ? named : `${area}-bucket-${bucket}`, chunk)
160-
bucket++
179+
groups.set(codecovFlag(named), chunk)
161180
}
162181
}
163182

scripts/group-coverage.spec.mjs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,41 @@ describe('group-coverage', () => {
7777
assert.deepEqual([...groups], [['apm-integrations-next', ['apm-integrations-next']]])
7878
})
7979

80+
it('shortens the duplicated instrumentations prefix', () => {
81+
const integration = 'instrumentations-instrumentation-confluentinc-kafka-javascript'
82+
const groups = planGroups(new Map([[integration, ['a', 'b']]]))
83+
assert.deepEqual([...groups], [['instr-confluentinc-kafka-javascript', [integration]]])
84+
})
85+
86+
it('distinguishes overlong integration flags that share the accepted prefix', () => {
87+
const accepted = `area-${'a'.repeat(40)}`
88+
const longPrefix = `area-${'a'.repeat(40)}`
89+
const groups = planGroups(new Map([
90+
[accepted, ['a', 'b']],
91+
[`${longPrefix}b`, ['c', 'd']],
92+
[`${longPrefix}c`, ['e', 'f']],
93+
]))
94+
const flags = [...groups.keys()]
95+
assert.equal(flags[0], accepted)
96+
assert.equal(flags[1].length, 45)
97+
assert.equal(flags[2].length, 45)
98+
assert.notEqual(flags[1], flags[2])
99+
for (const flag of flags) {
100+
assert.match(flag, /^[\w.-]{1,45}$/)
101+
}
102+
})
103+
104+
it('distinguishes sanitized flags from existing valid names', () => {
105+
const groups = planGroups(new Map([
106+
['area-library+variant', ['a', 'b']],
107+
['area-library_variant', ['c', 'd']],
108+
]))
109+
const flags = [...groups.keys()]
110+
assert.match(flags[0], /^[\w.-]{1,45}$/)
111+
assert.equal(flags[1], 'area-library_variant')
112+
assert.notEqual(flags[0], flags[1])
113+
})
114+
80115
it('leaves a small singleton tail (<=2) standalone', () => {
81116
const groups = planGroups(new Map([
82117
['serverless-lambda', ['a']],

0 commit comments

Comments
 (0)