Skip to content

Commit cd3b9f5

Browse files
👷 do not rely on monitors for deployment gate (#3973)
1 parent dd89e23 commit cd3b9f5

File tree

8 files changed

+461
-104
lines changed

8 files changed

+461
-104
lines changed

‎.gitlab/deploy-auto.yml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ stages:
1717
- VERSION=$(node -p -e "require('./lerna.json').version")
1818
- yarn
1919
- yarn build:bundle
20-
- node ./scripts/deploy/deploy-prod-dc.ts v${VERSION%%.*} $UPLOAD_PATH --check-monitors
20+
- node ./scripts/deploy/deploy-prod-dc.ts v${VERSION%%.*} $UPLOAD_PATH --check-telemetry-errors
2121

2222
step-1_deploy-prod-minor-dcs:
2323
when: manual

‎.gitlab/deploy-manual.yml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ stages:
1919
- VERSION=$(node -p -e "require('./lerna.json').version")
2020
- yarn
2121
- yarn build:bundle
22-
- node ./scripts/deploy/deploy-prod-dc.ts v${VERSION%%.*} $UPLOAD_PATH --no-check-monitors
22+
- node ./scripts/deploy/deploy-prod-dc.ts v${VERSION%%.*} $UPLOAD_PATH --no-check-telemetry-errors
2323

2424
step-1_deploy-prod-minor-dcs:
2525
extends:

‎scripts/deploy/check-monitors.ts‎

Lines changed: 0 additions & 62 deletions
This file was deleted.
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
import assert from 'node:assert/strict'
2+
import path from 'node:path'
3+
import { beforeEach, before, describe, it, mock, type Mock } from 'node:test'
4+
import { browserSdkVersion } from '../lib/browserSdkVersion.ts'
5+
import type { CommandDetail } from './lib/testHelpers.ts'
6+
import { mockModule, mockCommandImplementation } from './lib/testHelpers.ts'
7+
8+
const currentBrowserSdkVersionMajor = browserSdkVersion.split('.')[0]
9+
10+
describe('deploy-prod-dc', () => {
11+
const commandMock = mock.fn()
12+
const checkTelemetryErrorsMock: Mock<(datacenters: string[], version: string) => Promise<void>> = mock.fn()
13+
14+
let commands: CommandDetail[]
15+
let checkTelemetryErrorsCalls: Array<{ version: string; datacenters: string[] }>
16+
17+
before(async () => {
18+
await mockModule(path.resolve(import.meta.dirname, '../lib/command.ts'), { command: commandMock })
19+
await mockModule(path.resolve(import.meta.dirname, '../lib/executionUtils.ts'), {
20+
timeout: () => Promise.resolve(),
21+
})
22+
await mockModule(path.resolve(import.meta.dirname, './lib/checkTelemetryErrors.ts'), {
23+
checkTelemetryErrors: checkTelemetryErrorsMock,
24+
})
25+
})
26+
27+
beforeEach(() => {
28+
commands = mockCommandImplementation(commandMock)
29+
checkTelemetryErrorsCalls = []
30+
checkTelemetryErrorsMock.mock.mockImplementation((datacenters: string[], version: string) => {
31+
checkTelemetryErrorsCalls.push({ version, datacenters })
32+
return Promise.resolve()
33+
})
34+
})
35+
36+
it('should deploy a given datacenter', async () => {
37+
await runScript('./deploy-prod-dc.ts', 'v6', 'us1')
38+
39+
// Should not call checkTelemetryErrors by default (no flag)
40+
assert.strictEqual(checkTelemetryErrorsCalls.length, 0)
41+
42+
assert.deepEqual(commands, [
43+
{ command: 'node ./scripts/deploy/deploy.ts prod v6 us1' },
44+
{ command: 'node ./scripts/deploy/upload-source-maps.ts v6 us1' },
45+
])
46+
})
47+
48+
it('should deploy a given datacenter with check telemetry errors', async () => {
49+
await runScript('./deploy-prod-dc.ts', 'v6', 'us1', '--check-telemetry-errors')
50+
51+
// Should call checkTelemetryErrors 31 times: 1 initial + 30 during gating
52+
assert.strictEqual(checkTelemetryErrorsCalls.length, 31)
53+
assert.deepEqual(checkTelemetryErrorsCalls[0], {
54+
version: `${currentBrowserSdkVersionMajor}.*`,
55+
datacenters: ['us1'],
56+
}) // Initial check
57+
assert.deepEqual(checkTelemetryErrorsCalls[30], { version: browserSdkVersion, datacenters: ['us1'] }) // Last gating check
58+
59+
assert.deepEqual(commands, [
60+
{ command: 'node ./scripts/deploy/deploy.ts prod v6 us1' },
61+
{ command: 'node ./scripts/deploy/upload-source-maps.ts v6 us1' },
62+
])
63+
})
64+
65+
it('should only check telemetry errors before deploying if the upload path is root', async () => {
66+
await runScript('./deploy-prod-dc.ts', 'v6', 'root', '--check-telemetry-errors')
67+
68+
// Should only call checkTelemetryErrors once (no gating for root)
69+
assert.strictEqual(checkTelemetryErrorsCalls.length, 1)
70+
assert.deepEqual(checkTelemetryErrorsCalls[0], {
71+
version: `${currentBrowserSdkVersionMajor}.*`,
72+
datacenters: ['root'],
73+
})
74+
75+
assert.deepEqual(commands, [
76+
{ command: 'node ./scripts/deploy/deploy.ts prod v6 root' },
77+
{ command: 'node ./scripts/deploy/upload-source-maps.ts v6 root' },
78+
])
79+
})
80+
81+
it('should deploy all minor datacenters', async () => {
82+
await runScript('./deploy-prod-dc.ts', 'v6', 'minor-dcs', '--no-check-telemetry-errors')
83+
84+
// Should not call checkTelemetryErrors when --no-check-telemetry-errors is used
85+
assert.strictEqual(checkTelemetryErrorsCalls.length, 0)
86+
87+
assert.deepEqual(commands, [
88+
{ command: 'node ./scripts/deploy/deploy.ts prod v6 us3,us5,ap1,ap2,prtest00' },
89+
{ command: 'node ./scripts/deploy/upload-source-maps.ts v6 us3,us5,ap1,ap2,prtest00' },
90+
])
91+
})
92+
})
93+
94+
async function runScript(scriptPath: string, ...args: string[]): Promise<void> {
95+
const { main } = (await import(scriptPath)) as { main: (...args: string[]) => Promise<void> }
96+
97+
return main(...args)
98+
}

‎scripts/deploy/deploy-prod-dc.ts‎

Lines changed: 35 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ import { parseArgs } from 'node:util'
22
import { printLog, runMain, timeout } from '../lib/executionUtils.ts'
33
import { command } from '../lib/command.ts'
44
import { siteByDatacenter } from '../lib/datacenter.ts'
5+
import { browserSdkVersion } from '../lib/browserSdkVersion.ts'
6+
import { checkTelemetryErrors } from './lib/checkTelemetryErrors.ts'
57

68
/**
79
* Orchestrate the deployments of the artifacts for specific DCs
@@ -21,43 +23,51 @@ function getAllMinorDcs(): string[] {
2123
return Object.keys(siteByDatacenter).filter((dc) => !MAJOR_DCS.includes(dc))
2224
}
2325

24-
const {
25-
values: { 'check-monitors': checkMonitors },
26-
positionals,
27-
} = parseArgs({
28-
allowPositionals: true,
29-
allowNegative: true,
30-
options: {
31-
'check-monitors': {
32-
type: 'boolean',
26+
if (!process.env.NODE_TEST_CONTEXT) {
27+
runMain(() => main(...process.argv.slice(2)))
28+
}
29+
30+
export async function main(...args: string[]): Promise<void> {
31+
const {
32+
values: { 'check-telemetry-errors': shouldCheckTelemetryErrors },
33+
positionals,
34+
} = parseArgs({
35+
args,
36+
allowPositionals: true,
37+
allowNegative: true,
38+
options: {
39+
'check-telemetry-errors': {
40+
type: 'boolean',
41+
default: false,
42+
},
3343
},
34-
},
35-
})
44+
})
3645

37-
const version = positionals[0]
38-
const uploadPath = positionals[1] === 'minor-dcs' ? getAllMinorDcs().join(',') : positionals[1]
46+
const version = positionals[0]
47+
const uploadPath = positionals[1] === 'minor-dcs' ? getAllMinorDcs().join(',') : positionals[1]
3948

40-
if (!uploadPath) {
41-
throw new Error('UPLOAD_PATH argument is required')
42-
}
49+
if (!uploadPath) {
50+
throw new Error('UPLOAD_PATH argument is required')
51+
}
4352

44-
runMain(async () => {
45-
if (checkMonitors) {
46-
command`node ./scripts/deploy/check-monitors.ts ${uploadPath}`.withLogs().run()
53+
if (shouldCheckTelemetryErrors) {
54+
// Make sure system is in a good state before deploying
55+
const currentBrowserSdkVersionMajor = browserSdkVersion.split('.')[0]
56+
await checkTelemetryErrors(uploadPath.split(','), `${currentBrowserSdkVersionMajor}.*`)
4757
}
4858

4959
command`node ./scripts/deploy/deploy.ts prod ${version} ${uploadPath}`.withLogs().run()
5060
command`node ./scripts/deploy/upload-source-maps.ts ${version} ${uploadPath}`.withLogs().run()
5161

52-
if (checkMonitors && uploadPath !== 'root') {
53-
await gateMonitors(uploadPath)
62+
if (shouldCheckTelemetryErrors && uploadPath !== 'root') {
63+
await gateTelemetryErrors(uploadPath)
5464
}
55-
})
65+
}
5666

57-
async function gateMonitors(uploadPath: string): Promise<void> {
58-
printLog(`Check monitors for ${uploadPath} during ${GATE_DURATION / ONE_MINUTE_IN_SECOND} minutes`)
67+
async function gateTelemetryErrors(uploadPath: string): Promise<void> {
68+
printLog(`Check telemetry errors for ${uploadPath} during ${GATE_DURATION / ONE_MINUTE_IN_SECOND} minutes`)
5969
for (let i = 0; i < GATE_DURATION; i += GATE_INTERVAL) {
60-
command`node ./scripts/deploy/check-monitors.ts ${uploadPath}`.run()
70+
await checkTelemetryErrors(uploadPath.split(','), browserSdkVersion)
6171
process.stdout.write('.') // progress indicator
6272
await timeout(GATE_INTERVAL * 1000)
6373
}

0 commit comments

Comments
 (0)