Skip to content

Commit 4f34cf2

Browse files
committed
Implement basic CI version capper
1 parent 0b396b6 commit 4f34cf2

File tree

4 files changed

+334
-2
lines changed

4 files changed

+334
-2
lines changed
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
{
2+
"pinned": [
3+
"ENTER_PACKAGE_NAME_HERE"
4+
],
5+
"latests": {
6+
"aerospike": "6.0.2",
7+
"amqp10": "3.6.0",
8+
"amqplib": "0.10.5",
9+
"apollo-server-core": "3.13.0",
10+
"@apollo/server": "4.11.3",
11+
"@apollo/gateway": "2.10.0",
12+
"avsc": "5.7.7",
13+
"@smithy/smithy-client": "4.1.6",
14+
"@aws-sdk/smithy-client": "3.374.0",
15+
"aws-sdk": "2.1692.0",
16+
"@azure/functions": "4.6.1",
17+
"bluebird": "3.7.2",
18+
"body-parser": "1.20.3",
19+
"bunyan": "1.8.15",
20+
"cassandra-driver": "4.8.0",
21+
"connect": "3.7.0",
22+
"cookie-parser": "1.4.7",
23+
"cookie": "1.0.2",
24+
"couchbase": "4.4.5",
25+
"@cucumber/cucumber": "11.2.0",
26+
"cypress": "14.1.0",
27+
"@elastic/transport": "8.9.4",
28+
"@elastic/elasticsearch": "8.17.1",
29+
"elasticsearch": "16.7.3",
30+
"express-mongo-sanitize": "2.2.0",
31+
"express-session": "1.18.1",
32+
"express": "4.21.2",
33+
"fastify": "5.2.1",
34+
"find-my-way": "9.2.0",
35+
"fs": "0.0.1-security",
36+
"generic-pool": "3.9.0",
37+
"@google-cloud/pubsub": "4.10.0",
38+
"@graphql-tools/executor": "1.4.2",
39+
"graphql": "16.10.0",
40+
"@grpc/grpc-js": "1.12.6",
41+
"handlebars": "4.7.8",
42+
"@hapi/hapi": "21.3.12",
43+
"hapi": "18.1.0",
44+
"ioredis": "5.5.0",
45+
"jest-environment-node": "29.7.0",
46+
"jest-environment-jsdom": "29.7.0",
47+
"@jest/core": "29.7.0",
48+
"@jest/test-sequencer": "29.7.0",
49+
"@jest/reporters": "29.7.0",
50+
"jest-circus": "29.7.0",
51+
"@jest/transform": "29.7.0",
52+
"jest-config": "29.7.0",
53+
"jest-runtime": "29.7.0",
54+
"jest-worker": "29.7.0",
55+
"kafkajs": "2.2.4",
56+
"knex": "3.1.0",
57+
"koa": "2.16.0",
58+
"@koa/router": "13.1.0",
59+
"koa-router": "13.0.1",
60+
"@langchain/core": "0.3.41",
61+
"@langchain/openai": "0.4.4",
62+
"ldapjs": "3.0.7",
63+
"limitd-client": "2.14.1",
64+
"lodash": "4.17.21",
65+
"mariadb": "3.4.0",
66+
"memcached": "2.2.2",
67+
"microgateway-core": "3.3.5",
68+
"moleculer": "0.14.35",
69+
"mongodb-core": "3.2.7",
70+
"mongodb": "6.14.0",
71+
"mongoose": "8.11.0",
72+
"mquery": "5.0.0",
73+
"multer": "1.4.5-lts.1",
74+
"mysql": "2.18.1",
75+
"mysql2": "3.12.0",
76+
"next": "15.2.0",
77+
"node-serialize": "0.0.4",
78+
"nyc": "17.1.0",
79+
"openai": "4.86.1",
80+
"@opensearch-project/opensearch": "3.4.0",
81+
"oracledb": "6.8.0",
82+
"paperplane": "3.1.2",
83+
"passport-http": "0.3.0",
84+
"passport-local": "1.0.0",
85+
"passport": "0.7.0",
86+
"pg": "8.13.3",
87+
"pino": "9.6.0",
88+
"pino-pretty": "13.0.0",
89+
"@playwright/test": "1.50.1",
90+
"playwright": "1.50.1",
91+
"promise-js": "0.0.7",
92+
"promise": "8.3.0",
93+
"protobufjs": "7.4.0",
94+
"pug": "3.0.3",
95+
"q": "1.5.1",
96+
"@node-redis/client": "1.0.6",
97+
"@redis/client": "1.6.0",
98+
"redis": "4.7.0",
99+
"restify": "11.1.0",
100+
"rhea": "3.0.3",
101+
"router": "1.3.8",
102+
"selenium-webdriver": "4.29.0",
103+
"sequelize": "6.37.5",
104+
"sharedb": "5.1.1",
105+
"tedious": "18.6.1",
106+
"undici": "7.4.0",
107+
"vitest": "3.0.7",
108+
"@vitest/runner": "3.0.7",
109+
"when": "3.7.8",
110+
"winston": "3.17.0"
111+
}
112+
}

scripts/helpers/versioning.js

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
const fs = require('fs')
2+
const path = require('path')
3+
const childProcess = require('child_process')
4+
const proxyquire = require('proxyquire')
5+
6+
const versionLists = {}
7+
const names = []
8+
9+
const filter = process.env.hasOwnProperty('PLUGINS') && process.env.PLUGINS.split('|')
10+
11+
fs.readdirSync(path.join(__dirname, '../../packages/datadog-instrumentations/src'))
12+
.filter(file => file.endsWith('js'))
13+
.forEach(file => {
14+
file = file.replace('.js', '')
15+
16+
if (!filter || filter.includes(file)) {
17+
names.push(file)
18+
}
19+
})
20+
21+
async function getVersionList (name) {
22+
if (versionLists[name]) {
23+
return versionLists[name]
24+
}
25+
const list = await npmView(`${name} versions`)
26+
versionLists[name] = list
27+
return list
28+
}
29+
30+
function npmView (input) {
31+
return new Promise((resolve, reject) => {
32+
childProcess.exec(`npm view ${input} --json`, (err, stdout) => {
33+
if (err) {
34+
reject(err)
35+
return
36+
}
37+
resolve(JSON.parse(stdout.toString('utf8')))
38+
})
39+
})
40+
}
41+
42+
function loadInstFile (file, instrumentations) {
43+
const instrument = {
44+
addHook (instrumentation) {
45+
instrumentations.push(instrumentation)
46+
}
47+
}
48+
49+
const instPath = path.join(__dirname, `../../packages/datadog-instrumentations/src/${file}`)
50+
51+
proxyquire.noPreserveCache()(instPath, {
52+
'./helpers/instrument': instrument,
53+
'../helpers/instrument': instrument
54+
})
55+
}
56+
57+
function getInternals () {
58+
return names.map(key => {
59+
const instrumentations = []
60+
const name = key
61+
62+
try {
63+
loadInstFile(`${name}/server.js`, instrumentations)
64+
loadInstFile(`${name}/client.js`, instrumentations)
65+
} catch (e) {
66+
loadInstFile(`${name}.js`, instrumentations)
67+
}
68+
69+
return instrumentations
70+
}).reduce((prev, next) => prev.concat(next), [])
71+
}
72+
73+
module.exports = {
74+
getVersionList,
75+
npmView,
76+
loadInstFile,
77+
getInternals
78+
}

scripts/install_plugin_modules.js

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ const exec = require('./helpers/exec')
99
const childProcess = require('child_process')
1010
const externals = require('../packages/dd-trace/test/plugins/externals')
1111
const { getInstrumentation } = require('../packages/dd-trace/test/setup/helpers/load-inst')
12+
const latests = require('../packages/datadog-instrumentations/src/helpers/latests.json')
1213

1314
const requirePackageJsonPath = require.resolve('../packages/dd-trace/src/require-package-json')
1415

@@ -106,9 +107,21 @@ function assertFolder (name, version) {
106107
}
107108

108109
async function assertPackage (name, version, dependencyVersionRange, external) {
109-
const dependencies = { [name]: dependencyVersionRange }
110+
// Apply version cap from latests.json if available
111+
// TODO: pinned versions?
112+
let cappedVersionRange = dependencyVersionRange
113+
if (latests.latests[name]) {
114+
const latestVersion = latests.latests[name]
115+
// If the range would allow versions beyond what we've tested, cap it
116+
if (semver.validRange(dependencyVersionRange) &&
117+
!semver.subset(`<=${latestVersion}`, dependencyVersionRange)) {
118+
cappedVersionRange = `${dependencyVersionRange} <=${latestVersion}`
119+
}
120+
}
121+
122+
const dependencies = { [name]: cappedVersionRange }
110123
if (deps[name]) {
111-
await addDependencies(dependencies, name, dependencyVersionRange)
124+
await addDependencies(dependencies, name, cappedVersionRange)
112125
}
113126
const pkg = {
114127
name: [name, sha1(name).substr(0, 8), sha1(version)].filter(val => val).join('-'),

scripts/outdated.js

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
/* eslint-disable no-console */
2+
const {
3+
getInternals,
4+
npmView
5+
} = require('./helpers/versioning')
6+
const path = require('path')
7+
const fs = require('fs')
8+
9+
const latestsPath = path.join(
10+
__dirname,
11+
'..',
12+
'packages',
13+
'datadog-instrumentations',
14+
'src',
15+
'helpers',
16+
'latests.json'
17+
)
18+
19+
// Get internal package names from existing getInternals helper
20+
const internalsNames = Array.from(new Set(getInternals().map(n => n.name)))
21+
.filter(x => typeof x === 'string' && x !== 'child_process' && !x.startsWith('node:'))
22+
23+
// Initial structure with placeholder for pinned packages
24+
const initialStructure = {
25+
pinned: ['ENTER_PACKAGE_NAME_HERE'],
26+
latests: {}
27+
}
28+
29+
/**
30+
* Updates latests.json with the current latest versions from npm
31+
*/
32+
async function fix () {
33+
console.log('Starting fix operation...')
34+
console.log(`Found ${internalsNames.length} packages to process`)
35+
36+
let outputData = initialStructure
37+
if (fs.existsSync(latestsPath)) {
38+
console.log('Found existing latests.json, loading it...')
39+
outputData = require(latestsPath)
40+
}
41+
42+
const latests = {}
43+
let processed = 0
44+
const total = internalsNames.length
45+
46+
for (const name of internalsNames) {
47+
processed++
48+
process.stdout.write(`Processing package ${processed}/${total}: ${name}...`)
49+
50+
try {
51+
const distTags = await npmView(name + ' dist-tags')
52+
const latest = distTags.latest
53+
if (latest) {
54+
latests[name] = latest
55+
process.stdout.write(` found version ${latest}\n`)
56+
} else {
57+
process.stdout.write(' WARNING: no version found\n')
58+
console.log(`Warning: Could not fetch latest version for "${name}"`)
59+
}
60+
} catch (error) {
61+
process.stdout.write(' ERROR\n')
62+
console.error(`Error fetching version for "${name}":`, error.message)
63+
}
64+
}
65+
66+
outputData.latests = latests
67+
console.log('\nWriting updated versions to latests.json...')
68+
fs.writeFileSync(latestsPath, JSON.stringify(outputData, null, 2))
69+
console.log('Successfully updated latests.json')
70+
console.log(`Processed ${total} packages`)
71+
}
72+
73+
/**
74+
* Checks if latests.json matches current npm versions
75+
*/
76+
async function check () {
77+
console.log('Starting version check...')
78+
79+
if (!fs.existsSync(latestsPath)) {
80+
console.log('latests.json does not exist. Run with "fix" to create it.')
81+
process.exitCode = 1
82+
return
83+
}
84+
85+
const currentData = require(latestsPath)
86+
console.log(`Found ${internalsNames.length} packages to check`)
87+
88+
let processed = 0
89+
let mismatches = 0
90+
const total = internalsNames.length
91+
92+
for (const name of internalsNames) {
93+
processed++
94+
process.stdout.write(`Checking package ${processed}/${total}: ${name}...`)
95+
96+
const latest = currentData.latests[name]
97+
if (!latest) {
98+
process.stdout.write(' MISSING\n')
99+
console.log(`No latest version found for "${name}"`)
100+
process.exitCode = 1
101+
continue
102+
}
103+
104+
try {
105+
const distTags = await npmView(name + ' dist-tags')
106+
const npmLatest = distTags.latest
107+
if (npmLatest !== latest) {
108+
process.stdout.write(' MISMATCH\n')
109+
console.log(`"latests.json: is not up to date for "${name}": expected "${npmLatest}", got "${latest}"`)
110+
process.exitCode = 1
111+
mismatches++
112+
} else {
113+
process.stdout.write(' OK\n')
114+
}
115+
} catch (error) {
116+
process.stdout.write(' ERROR\n')
117+
console.error(`Error checking version for "${name}":`, error.message)
118+
}
119+
}
120+
121+
console.log('\nCheck completed:')
122+
console.log(`- Total packages checked: ${total}`)
123+
console.log(`- Version mismatches found: ${mismatches}`)
124+
if (mismatches > 0) {
125+
console.log('Run with "fix" to update versions')
126+
}
127+
}
128+
if (process.argv.includes('fix')) fix()
129+
else check()

0 commit comments

Comments
 (0)