Skip to content

Commit

Permalink
ci: Add Node.js 20.x to CI and remove 14.x (#1603)
Browse files Browse the repository at this point in the history
  • Loading branch information
bizob2828 authored Jul 24, 2023
1 parent 9bf2a01 commit c4b008c
Show file tree
Hide file tree
Showing 19 changed files with 110 additions and 78 deletions.
23 changes: 8 additions & 15 deletions .github/workflows/ci-workflow.yml
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ jobs:

strategy:
matrix:
node-version: [14.x, 16.x, 18.x]
node-version: [16.x, 18.x, 20.x]

steps:
- uses: actions/checkout@v3
Expand All @@ -86,6 +86,7 @@ jobs:
name: unit-tests-${{ matrix.node-version }}
path: ./coverage/unit/lcov.info
- name: Run ESM Unit Tests
if: matrix.node-version != '20.x'
run: npm run unit:esm
- name: Archive ESM Unit Test Coverage
uses: actions/upload-artifact@v3
Expand All @@ -100,7 +101,7 @@ jobs:

strategy:
matrix:
node-version: [14.x, 16.x, 18.x]
node-version: [16.x, 18.x, 20.x]

steps:
- uses: actions/checkout@v3
Expand All @@ -127,7 +128,7 @@ jobs:

strategy:
matrix:
node-version: [14.x, 16.x, 18.x]
node-version: [16.x, 18.x, 20.x]

steps:
- uses: actions/checkout@v3
Expand All @@ -139,16 +140,8 @@ jobs:
run: npm ci
- name: Run Docker Services
run: npm run services
- name: Run Versioned Tests (npm v6 / Node 12/14)
if: ${{ matrix.node-version == '14.x' }}
run: TEST_CHILD_TIMEOUT=600000 npm run versioned:npm6
env:
VERSIONED_MODE: ${{ github.ref == 'refs/heads/main' && '--minor' || '--major' }}
JOBS: 4 # 2 per CPU seems to be the sweet spot in GHA (July 2022)
C8_REPORTER: lcovonly
- name: Run Versioned Tests (npm v7 / Node 16+)
if: ${{ matrix.node-version != '14.x' }}
run: TEST_CHILD_TIMEOUT=600000 npm run versioned:npm7
- name: Run Versioned Tests
run: TEST_CHILD_TIMEOUT=600000 npm run versioned
env:
VERSIONED_MODE: ${{ github.ref == 'refs/heads/main' && '--minor' || '--major' }}
JOBS: 4 # 2 per CPU seems to be the sweet spot in GHA (July 2022)
Expand All @@ -165,7 +158,7 @@ jobs:

strategy:
matrix:
node-version: [14.x, 16.x, 18.x]
node-version: [16.x, 18.x, 20.x]

steps:
- uses: actions/checkout@v3
Expand Down Expand Up @@ -194,4 +187,4 @@ jobs:
with:
token: ${{ secrets.CODECOV_TOKEN }}
directory: versioned-tests-${{ matrix.node-version }}
flags: versioned-tests-${{ matrix.node-version }}
flags: versioned-tests-${{ matrix.node-version }}
2 changes: 1 addition & 1 deletion .github/workflows/versioned-coverage.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ jobs:

strategy:
matrix:
node-version: [16.x, 18.x]
node-version: [16.x, 18.x, 20.x]

steps:
- uses: actions/checkout@v3
Expand Down
4 changes: 2 additions & 2 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,8 @@ function initialize() {
throw new Error(message)
}

// TODO: Update this check when Node v20 support is added
if (psemver.satisfies('>=19.0.0')) {
// TODO: Update this check when Node v22 support is added
if (psemver.satisfies('>=21.0.0')) {
logger.warn(
'New Relic for Node.js %s has not been tested on Node.js %s. Please ' +
'update the agent or downgrade your version of Node.js',
Expand Down
16 changes: 15 additions & 1 deletion lib/environment.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ const logger = require('./logger').child({ component: 'environment' })
const stringify = require('json-stringify-safe')
const asyncEachLimit = require('./util/async-each-limit')
const DISPATCHER_VERSION = 'Dispatcher Version'
const semver = require('semver')

// As of 1.7.0 you can no longer dynamically link v8
// https://github.com/nodejs/io.js/commit/d726a177ed
Expand Down Expand Up @@ -260,7 +261,7 @@ function flattenVersions(packages) {
try {
return stringify(pair)
} catch (err) {
logger.debug(err, 'Unabled to stringify package version')
logger.debug(err, 'Unable to stringify package version')
return '<unknown>'
}
})
Expand Down Expand Up @@ -291,6 +292,19 @@ function remapConfigSettings() {
addSetting(remapping[key], value)
}
})

maybeAddMissingProcessVars()
}
}

/**
* As of Node 19 DTrace and ETW are no longer bundled
* see: https://nodejs.org/en/blog/announcements/v19-release-announce#dtrace/systemtap/etw-support
*/
function maybeAddMissingProcessVars() {
if (semver.gte(process.version, '19.0.0')) {
addSetting(remapping.node_use_dtrace, 'no')
addSetting(remapping.node_use_etw, 'no')
}
}

Expand Down
12 changes: 5 additions & 7 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@
],
"homepage": "https://github.com/newrelic/node-newrelic",
"engines": {
"node": ">=14",
"node": ">=16",
"npm": ">=6.0.0"
},
"directories": {
Expand Down Expand Up @@ -168,13 +168,11 @@
"versioned-tests": "./bin/run-versioned-tests.sh",
"update-changelog-version": "node ./bin/update-changelog-version",
"checkout-external-versioned": "node ./test/versioned-external/checkout-external-tests.js",
"versioned": "npm run versioned:npm7",
"versioned:major": "VERSIONED_MODE=--major npm run versioned:npm7",
"versioned:npm6": "npm run checkout-external-versioned && npm run prepare-test && time ./bin/run-versioned-tests.sh",
"versioned:npm7": "npm run checkout-external-versioned && npm run prepare-test && NPM7=1 time ./bin/run-versioned-tests.sh",
"versioned:async-local": "NEW_RELIC_FEATURE_FLAG_ASYNC_LOCAL_CONTEXT=1 npm run versioned:npm7",
"versioned:major": "VERSIONED_MODE=--major npm run versioned",
"versioned": "npm run checkout-external-versioned && npm run prepare-test && NPM7=1 time ./bin/run-versioned-tests.sh",
"versioned:async-local": "NEW_RELIC_FEATURE_FLAG_ASYNC_LOCAL_CONTEXT=1 npm run versioned",
"versioned:async-local:major": "NEW_RELIC_FEATURE_FLAG_ASYNC_LOCAL_CONTEXT=1 npm run versioned:major",
"versioned:security": "NEW_RELIC_SECURITY_AGENT_ENABLED=true npm run versioned:npm7",
"versioned:security": "NEW_RELIC_SECURITY_AGENT_ENABLED=true npm run versioned",
"versioned:security:major": "NEW_RELIC_SECURITY_AGENT_ENABLED=true npm run versioned:major",
"prepare": "husky install"
},
Expand Down
41 changes: 28 additions & 13 deletions test/integration/distributed-tracing/dt.tap.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,6 @@ const helper = require('../../lib/agent_helper')
const tap = require('tap')
const url = require('url')

const START_PORT = 10000
const MIDDLE_PORT = 10001
const END_PORT = 10002
const ACCOUNT_ID = '1337'
const APP_ID = '7331'
const EXPECTED_DT_METRICS = ['DurationByCaller', 'TransportDuration']
Expand Down Expand Up @@ -54,9 +51,14 @@ tap.test('distributed tracing full integration', (t) => {
}
}

// eslint-disable-next-line prefer-const
let MIDDLE_PORT
// eslint-disable-next-line prefer-const
let END_PORT

// Naming is how the requests will flow through the system, to test that all
// metrics are generated as expected as well as the dirac events.
const start = generateServer(http, api, START_PORT, started, (req, res) => {
const start = generateServer(http, api, started, (req, res) => {
const tx = agent.tracer.getTransaction()
tx.nameState.appendPath('foobar')
http.get(generateUrl(MIDDLE_PORT, 'start/middle'), (externRes) => {
Expand All @@ -69,7 +71,9 @@ tap.test('distributed tracing full integration', (t) => {
})
})

const middle = generateServer(http, api, MIDDLE_PORT, started, (req, res) => {
const START_PORT = start.address().port

const middle = generateServer(http, api, started, (req, res) => {
t.ok(req.headers.newrelic, 'middle received newrelic from start')

const tx = agent.tracer.getTransaction()
Expand All @@ -84,11 +88,15 @@ tap.test('distributed tracing full integration', (t) => {
})
})

const end = generateServer(http, api, END_PORT, started, (req, res) => {
MIDDLE_PORT = middle.address().port

const end = generateServer(http, api, started, (req, res) => {
t.ok(req.headers.newrelic, 'end received newrelic from middle')
res.end()
})

END_PORT = end.address().port

t.teardown(() => {
start.close()
middle.close()
Expand Down Expand Up @@ -147,7 +155,7 @@ tap.test('distributed tracing full integration', (t) => {
t.equal(scopedKeys.length, 1, 'middle should only be the inbound and outbound request.')
t.same(
scopedKeys,
['External/localhost:10002/http'],
[`External/localhost:${END_PORT}/http`],
'should have expected scoped metric name'
)

Expand Down Expand Up @@ -186,7 +194,7 @@ tap.test('distributed tracing full integration', (t) => {
t.equal(scopedKeys.length, 1, 'start should only be the inbound and outbound request.')
t.same(
scopedKeys,
['External/localhost:10001/http'],
[`External/localhost:${MIDDLE_PORT}/http`],
'should have expected scoped metric name'
)

Expand Down Expand Up @@ -242,6 +250,9 @@ tap.test('distributed tracing', (t) => {
let start = null
let middle = null
let end = null
let START_PORT
let MIDDLE_PORT
let END_PORT

t.autoend()

Expand Down Expand Up @@ -271,15 +282,19 @@ tap.test('distributed tracing', (t) => {
})
}

start = generateServer(http, api, START_PORT, cb, (req, res) => {
start = generateServer(http, api, cb, (req, res) => {
return getNextUrl('start/middle', 'start', MIDDLE_PORT, req, res)
})
middle = generateServer(http, api, MIDDLE_PORT, cb, (req, res) => {

START_PORT = start.address().port
middle = generateServer(http, api, cb, (req, res) => {
return getNextUrl('middle/end', 'middle', END_PORT, req, res)
})
end = generateServer(http, api, END_PORT, cb, (req, res) => {
MIDDLE_PORT = middle.address().port
end = generateServer(http, api, cb, (req, res) => {
return createResponse(req, res, {}, 'end')
})
END_PORT = end.address().port
})

t.afterEach(async () => {
Expand Down Expand Up @@ -322,14 +337,14 @@ tap.test('distributed tracing', (t) => {
})
})

function generateServer(http, api, port, started, responseHandler) {
function generateServer(http, api, started, responseHandler) {
const server = http.createServer((req, res) => {
const tx = api.agent.getTransaction()
tx.nameState.appendPath(req.url)
req.resume()
responseHandler(req, res)
})
server.listen(port, () => started())
server.listen(() => started())
return server
}

Expand Down
12 changes: 8 additions & 4 deletions test/integration/logger.tap.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ const tap = require('tap')
const rimraf = require('rimraf')
const util = require('util')
const exec = util.promisify(require('child_process').exec)
const { isSupportedVersion } = require('../lib/agent_helper')

const DIRNAME = 'XXXNOCONFTEST'

Expand Down Expand Up @@ -56,10 +57,13 @@ tap.test('logger', function (t) {
tap.test('Logger output', (t) => {
t.autoend()

const execArgs = [
{ opt: '-r', arg: '../../../index.js' },
{ opt: '--experimental-loader', arg: '../../../esm-loader.mjs' }
]
const execArgs = [{ opt: '-r', arg: '../../../index.js' }]

// TODO: add back to array when we fix ESM loader
if (!isSupportedVersion('v19.0.0')) {
execArgs.push({ opt: '--experimental-loader', arg: '../../../esm-loader.mjs' })
}

for (const pair of execArgs) {
const { opt, arg } = pair
t.test(`Check for ${opt} in logger output at debug level`, async (t) => {
Expand Down
9 changes: 9 additions & 0 deletions test/lib/agent_helper.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ const Transaction = require('../../lib/transaction')
const symbols = require('../../lib/symbols')
const http = require('http')
const https = require('https')
const semver = require('semver')

const KEYPATH = path.join(__dirname, 'test-key.key')
const CERTPATH = path.join(__dirname, 'self-signed-test-certificate.crt')
Expand Down Expand Up @@ -686,5 +687,13 @@ const helper = (module.exports = {
: original[symbols.original]
}
return original
},
/**
* Util that checks if current node version is supported
* @param {string} version semver version string
* @returns {boolean} if version is supported
*/
isSupportedVersion(version) {
return semver.gt(process.version, version)
}
})
2 changes: 1 addition & 1 deletion test/unit/collector/remote-method.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -254,7 +254,7 @@ tap.test('when the connection fails', (t) => {
method.invoke({ message: 'none' }, {}, (error) => {
t.ok(error)
// regex for either ipv4 or ipv6 localhost
t.match(error.message, /connect ECONNREFUSED (127\.0\.0\.1|::1):8765/)
t.equal(error.code, 'ECONNREFUSED')

t.end()
})
Expand Down
4 changes: 2 additions & 2 deletions test/unit/config/config-formatters.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ tap.test('config formatters', (t) => {
const val = 'invalid'
t.notOk(formatters.object(val, loggerMock))
t.equal(loggerMock.error.args[0][0], 'New Relic configurator could not deserialize object:')
t.match(loggerMock.error.args[1][0], /SyntaxError: Unexpected token i in JSON at position/)
t.match(loggerMock.error.args[1][0], /SyntaxError: Unexpected token/)
t.end()
})
})
Expand All @@ -132,7 +132,7 @@ tap.test('config formatters', (t) => {
loggerMock.error.args[0][0],
'New Relic configurator could not deserialize object list:'
)
t.match(loggerMock.error.args[1][0], /SyntaxError: Unexpected token i in JSON at position/)
t.match(loggerMock.error.args[1][0], /SyntaxError: Unexpected token/)
t.end()
})
})
Expand Down
6 changes: 5 additions & 1 deletion test/unit/environment.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ const path = require('path')
const fs = require('fs/promises')
const spawn = require('child_process').spawn
const environment = require('../../lib/environment')
const { isSupportedVersion } = require('../lib/agent_helper')

function find(settings, name) {
const items = settings.filter(function (candidate) {
Expand Down Expand Up @@ -136,7 +137,8 @@ tap.test('the environment scraper', (t) => {
t.end()
})

t.test('without process.config', (t) => {
// TODO: remove tests when we drop support for node 18
t.test('without process.config', { skip: isSupportedVersion('v19.0.0') }, (t) => {
let conf = null

t.before(() => {
Expand Down Expand Up @@ -216,8 +218,10 @@ tap.test('the environment scraper', (t) => {
t.end()
})

// TODO: remove this test when we drop support for node 18
t.test(
'should resolve refresh where deps and deps of deps are symlinked to each other',
{ skip: isSupportedVersion('v19.0.0') },
async (t) => {
process.config.variables.node_prefix = path.join(__dirname, '../lib/example-deps')
const data = await environment.getJSON()
Expand Down
Loading

0 comments on commit c4b008c

Please sign in to comment.