Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ci: Add Node.js 20.x to CI and remove 14.x #1603

Merged
merged 6 commits into from
Jul 24, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 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 @@
try {
return stringify(pair)
} catch (err) {
logger.debug(err, 'Unabled to stringify package version')
logger.debug(err, 'Unable to stringify package version')

Check warning on line 264 in lib/environment.js

View check run for this annotation

Codecov / codecov/patch

lib/environment.js#L264

Added line #L264 was not covered by tests
return '<unknown>'
}
})
Expand Down Expand Up @@ -291,6 +292,19 @@
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
Loading