newrelic 7.0.2 → 7.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/bin/github.js ADDED
@@ -0,0 +1,144 @@
1
+ 'use strict'
2
+
3
+ const { Octokit } = require("@octokit/rest")
4
+
5
+ if (!process.env.GITHUB_TOKEN) {
6
+ console.log('GITHUB_TOKEN recommended to be set in ENV')
7
+ }
8
+
9
+ const octokit = new Octokit({
10
+ auth: process.env.GITHUB_TOKEN,
11
+ })
12
+
13
+ class Github {
14
+ constructor(repoOwner = 'newrelic', repository = 'node-newrelic') {
15
+ this.repoOwner = repoOwner
16
+ this.repository = repository
17
+ }
18
+
19
+ async getLatestRelease() {
20
+ const result = await octokit.repos.getLatestRelease({
21
+ owner: this.repoOwner,
22
+ repo: this.repository
23
+ })
24
+
25
+ return result.data
26
+ }
27
+
28
+ async createRelease(tag, name, body) {
29
+ const result = await octokit.repos.createRelease({
30
+ owner: this.repoOwner,
31
+ repo: this.repository,
32
+ tag_name: tag,
33
+ name: name,
34
+ body: body
35
+ })
36
+
37
+ return result.data
38
+ }
39
+
40
+ async getTagByName(name) {
41
+ const perPage = 100
42
+
43
+ let pageNum = 1
44
+
45
+ let result = null
46
+ do {
47
+ result = await octokit.repos.listTags({
48
+ owner: this.repoOwner,
49
+ repo: this.repository,
50
+ per_page: perPage,
51
+ page: pageNum
52
+ })
53
+
54
+ const found = result.data.find((tag) => {
55
+ return tag.name === name
56
+ })
57
+
58
+ if (found) {
59
+ return found
60
+ }
61
+
62
+ pageNum++
63
+ } while (result.data.length === perPage) // there *might* be more data
64
+
65
+ return null
66
+ }
67
+
68
+ async getCommit(sha) {
69
+ const result = await octokit.repos.getCommit({
70
+ owner: this.repoOwner,
71
+ repo: this.repository,
72
+ ref: sha
73
+ })
74
+
75
+ return result.data
76
+ }
77
+
78
+ async getMergedPullRequestsSince(date) {
79
+ const perPage = 50
80
+
81
+ const comparisonDate = new Date(date)
82
+
83
+ let pageNum = 1
84
+ const mergedPullRequests = []
85
+ let result = null
86
+ let hadData = false
87
+
88
+ do {
89
+ result = await octokit.pulls.list({
90
+ owner: this.repoOwner,
91
+ repo: this.repository,
92
+ state: 'closed',
93
+ sort: 'updated',
94
+ direction: 'desc',
95
+ per_page: perPage,
96
+ page: pageNum
97
+ })
98
+
99
+ const mergedPrs = result.data.filter((pr) => {
100
+ return pr.merged_at && new Date(pr.merged_at) > comparisonDate
101
+ })
102
+
103
+ mergedPullRequests.push(...mergedPrs)
104
+ // Since we are going by 'updated' on query but merged on filter,
105
+ // there's a chance some boundaries are off. While in super extreme
106
+ // cases we could still miss some it is unlikely given we are grabbing
107
+ // large pages.
108
+ hadData = mergedPrs.length > 0
109
+
110
+ pageNum++
111
+ } while (result.data.length === perPage && hadData) // might be more in next batch
112
+
113
+ return mergedPullRequests
114
+ }
115
+
116
+ async getLatestWorkflowRun(nameOrId, branch) {
117
+ // Appears to return in latest order.
118
+ const runs = await octokit.actions.listWorkflowRuns({
119
+ owner: this.repoOwner,
120
+ repo: this.repository,
121
+ workflow_id: nameOrId,
122
+ branch: branch,
123
+ per_page: 5
124
+ })
125
+
126
+ return runs.data.workflow_runs[0]
127
+ }
128
+
129
+ async createPR(options) {
130
+ const {head, base, title, body, draft} = options
131
+
132
+ await octokit.pulls.create({
133
+ owner: this.repoOwner,
134
+ repo: this.repository,
135
+ head: head,
136
+ base: base,
137
+ title: title,
138
+ body: body,
139
+ draft: draft
140
+ })
141
+ }
142
+ }
143
+
144
+ module.exports = Github
@@ -0,0 +1,31 @@
1
+ 'use strict'
2
+
3
+ const { exec } = require('child_process')
4
+
5
+ async function version(typeOrVersion, shouldCommitAndTag) {
6
+ let command = `npm version ${typeOrVersion}`
7
+
8
+ command += shouldCommitAndTag ? '' : ' --no-git-tag-version'
9
+
10
+ await execAsPromise(command)
11
+ }
12
+
13
+ function execAsPromise(command) {
14
+ const promise = new Promise((resolve, reject) => {
15
+ console.log(`Executing: '${command}'`)
16
+
17
+ exec(command, (err, stdout) => {
18
+ if (err) {
19
+ return reject(err)
20
+ }
21
+
22
+ resolve(stdout)
23
+ })
24
+ })
25
+
26
+ return promise
27
+ }
28
+
29
+ module.exports = {
30
+ version
31
+ }
@@ -0,0 +1,359 @@
1
+ 'use strict'
2
+
3
+ const fs = require('fs')
4
+
5
+ const {program, Option} = require('commander')
6
+
7
+ const Github = require('./github')
8
+ const git = require('./git-commands')
9
+ const npm = require('./npm-commands')
10
+
11
+ const FILE_NAME = 'NEWS.md'
12
+ const PROPOSED_NOTES_HEADER = 'Proposed Release Notes'
13
+
14
+ const FORCE_RUN_DEAFULT_REMOTE = 'origin'
15
+
16
+ // Add command line options
17
+ program.addOption(
18
+ new Option('--release-type <releaseType>', 'release type')
19
+ .choices(['patch', 'minor', 'major'])
20
+ .makeOptionMandatory()
21
+ )
22
+ program.option('--major-release', 'create a major release. (release-type option must be set to \'major\')')
23
+ program.option('--remote <remote>', 'remote to push branch to', 'origin')
24
+ program.option('--branch <branch>', 'branch to generate notes from', 'main')
25
+ program.option('--repo-owner <repoOwner>', 'repository owner', 'newrelic')
26
+ program.option('--dry-run', 'generate notes without creating a branch or PR')
27
+ program.option('--no-pr', 'generate notes and branch but do not create PR')
28
+ program.option('-f --force', 'bypass validation')
29
+
30
+ function stopOnError(err) {
31
+ if (err) {
32
+ console.error(err)
33
+ }
34
+
35
+ console.log('Halting execution with exit code: 1')
36
+ process.exit(1)
37
+ }
38
+
39
+ function logStep(step) {
40
+ console.log(`\n ----- [Step]: ${step} -----\n`)
41
+ }
42
+
43
+ async function prepareReleaseNotes() {
44
+ // Parse commandline options inputs
45
+ program.parse()
46
+
47
+ const options = program.opts()
48
+
49
+ console.log('Script running with following options: ', JSON.stringify(options))
50
+
51
+ logStep('Validation')
52
+
53
+ if (options.force) {
54
+ console.log('--force set. Skipping validation logic')
55
+ }
56
+
57
+ const startingBranch = options.branch.replace('refs/heads/', '')
58
+
59
+ const isValid = options.force || (
60
+ await validateReleaseType(options.releaseType, options.majorRelease) &&
61
+ await validateRemote(options.remote) &&
62
+ await validateLocalChanges() &&
63
+ await validateCurrentBranch(startingBranch)
64
+ )
65
+
66
+ if (!isValid) {
67
+ console.log('Invalid configuration. Halting script.')
68
+ stopOnError()
69
+ }
70
+
71
+ const remote = options.remote || FORCE_RUN_DEAFULT_REMOTE
72
+ console.log('Using remote: ', remote)
73
+
74
+ try {
75
+ logStep('Increment Version')
76
+
77
+ await npm.version(options.releaseType, false)
78
+
79
+ const packagePath = '../package.json'
80
+ console.log('Extracting new version from package.json here: ', )
81
+ const packageInfo = require(packagePath)
82
+
83
+ const version = `v${packageInfo.version}`
84
+ console.log('New version is: ', version)
85
+
86
+ logStep('Branch Creation')
87
+
88
+ const newBranchName = `release/${version}`
89
+
90
+ if (options.dryRun) {
91
+ console.log('Dry run indicated (--dry-run), not creating branch.')
92
+ } else {
93
+ console.log('Creating and checking out new branch: ', newBranchName)
94
+ await git.checkoutNewBranch(newBranchName)
95
+ }
96
+
97
+ logStep('Commit Package Files')
98
+
99
+ if (options.dryRun) {
100
+ console.log('Dry run indicated (--dry-run), not committing package files.')
101
+ } else {
102
+ console.log('Adding and committing package files.')
103
+ await git.addAllFiles()
104
+ await git.commit(`Setting version to ${version}.`)
105
+ }
106
+
107
+ logStep('Create Release Notes')
108
+
109
+ const releaseData = await generateReleaseNotes()
110
+ await updateReleaseNotesFile(FILE_NAME, version, releaseData.notes)
111
+
112
+ if (options.dryRun) {
113
+ console.log('\nDry run indicated (--dry-run), skipping remaining steps.')
114
+ return
115
+ }
116
+
117
+ logStep('Commit Release Notes')
118
+
119
+ console.log('Adding and committing release notes.')
120
+ await git.addAllFiles()
121
+ await git.commit('Adds auto-generated release notes.')
122
+
123
+ logStep('Push Branch')
124
+
125
+ console.log('Pushing branch to remote: ', remote)
126
+ await git.pushToRemote(remote, newBranchName)
127
+
128
+ logStep('Create Pull Request')
129
+ if (!options.pr) {
130
+ console.log('No PR creation indicated (--no-pr), skipping remaining steps.')
131
+ return
132
+ }
133
+
134
+ if (!process.env.GITHUB_TOKEN) {
135
+ console.log('GITHUB_TOKEN required to create a pull request (PR)')
136
+ stopOnError()
137
+ }
138
+
139
+ console.log('Creating draft PR with new release notes for repo owner: ', options.repoOwner)
140
+ const remoteApi = new Github(options.repoOwner)
141
+ const title = 'Updates release notes for next release'
142
+ const body = getFormattedPrBody(releaseData)
143
+ const prOptions = {
144
+ head: newBranchName,
145
+ base: 'main',
146
+ title,
147
+ body,
148
+ draft: true
149
+ }
150
+
151
+ await remoteApi.createPR(prOptions)
152
+
153
+ console.log('*** Full Run Successful ***')
154
+ } catch (err) {
155
+ stopOnError(err)
156
+ }
157
+ }
158
+
159
+ async function validateReleaseType(releaseType, majorFlag) {
160
+ if (releaseType === 'major' && !majorFlag) {
161
+ console.log('WARNING: you must set the \'-m\' flag to create a major release.')
162
+ return false
163
+ }
164
+
165
+ if (majorFlag && releaseType !== 'major') {
166
+ console.log(`WARNING: ignoring \'-m, --major-release\' option as release type set to ${options.rel}.`)
167
+ return false
168
+ }
169
+
170
+ return true
171
+ }
172
+
173
+ async function validateRemote(remote) {
174
+ try {
175
+ const remotes = await git.getPushRemotes()
176
+
177
+ if (!remote) {
178
+ console.log('No remote configured. Please execute with --remote.')
179
+ console.log('Available remotes are: ', remotes)
180
+ return false
181
+ }
182
+
183
+ if (!remotes[remote]) {
184
+ console.log(`Configured remote (${remote}) not found in ${JSON.stringify(remotes)}`)
185
+ return false
186
+ }
187
+
188
+ return true
189
+ } catch (err) {
190
+ console.error(err)
191
+ return false
192
+ }
193
+ }
194
+
195
+ async function validateLocalChanges() {
196
+ try {
197
+ const localChanges = await git.getLocalChanges()
198
+ if (localChanges.length > 0) {
199
+ console.log('Local changes detected: ', localChanges)
200
+ console.log('Please commit to a feature branch or stash changes and then try again.')
201
+ return false
202
+ }
203
+
204
+ return true
205
+ } catch (err) {
206
+ console.error(err)
207
+ return false
208
+ }
209
+ }
210
+
211
+ async function validateCurrentBranch(branch) {
212
+ try {
213
+ const currentBranch = await git.getCurrentBranch()
214
+
215
+ if (branch != currentBranch) {
216
+ console.log(
217
+ 'Current checked-out branch (%s) does not match expected (%s)',
218
+ currentBranch,
219
+ branch
220
+ )
221
+ return false
222
+ }
223
+
224
+ return true
225
+ } catch (err) {
226
+ console.error(err)
227
+ return false
228
+ }
229
+ }
230
+
231
+ async function generateReleaseNotes() {
232
+ const github = new Github()
233
+ const latestRelease = await github.getLatestRelease()
234
+ console.log(`The latest release is: ${latestRelease.name} published: ${latestRelease.published_at}`)
235
+ console.log(`Tag: ${latestRelease.tag_name}, Target: ${latestRelease.target_commitish}`)
236
+
237
+ const tag = await github.getTagByName(latestRelease.tag_name)
238
+ console.log('The tag commit sha is: ', tag.commit.sha)
239
+
240
+ const commit = await github.getCommit(tag.commit.sha)
241
+ const commitDate = commit.commit.committer.date
242
+
243
+ console.log(`Finding merged pull requests since: ${commitDate}`)
244
+
245
+ const mergedPullRequests = await github.getMergedPullRequestsSince(commitDate)
246
+ console.log(`Found ${mergedPullRequests.length}`)
247
+
248
+ const releaseNoteData = mergedPullRequests.map((pr) => {
249
+ const parts = pr.body.split(/(?:^|\n)##\s*/g)
250
+
251
+ // If only has one part, not in appropriate format.
252
+ if (parts.length === 1) {
253
+ return {
254
+ notes: generateUnformattedNotes(pr.body),
255
+ url: pr.html_url
256
+ }
257
+ }
258
+
259
+ const {1: proposedReleaseNotes} = parts
260
+
261
+ const titleRemoved = proposedReleaseNotes.replace(PROPOSED_NOTES_HEADER, '')
262
+ return {
263
+ notes: titleRemoved,
264
+ url: pr.html_url
265
+ }
266
+ })
267
+
268
+ const finalData = releaseNoteData.reduce((result, currentValue) => {
269
+ const trimmedNotes = currentValue.notes.trim()
270
+ if (trimmedNotes) { // avoid adding lines for empty notes
271
+ result.notes += '\n\n' + trimmedNotes
272
+ }
273
+ result.links += `\n* PR: ${currentValue.url}`
274
+ return result
275
+ }, {
276
+ notes: '',
277
+ links: ''
278
+ })
279
+
280
+ return finalData
281
+ }
282
+
283
+ function generateUnformattedNotes(originalNotes) {
284
+ let unformattedNotes = originalNotes
285
+
286
+ // Drop extra snyk details and just keep high-level summary.
287
+ if (originalNotes.indexOf('snyk:metadata') >= 0) {
288
+ const snykParts = originalNotes.split('<hr/>')
289
+ const {0: snykDescription} = snykParts
290
+
291
+ unformattedNotes = snykDescription.trim()
292
+ }
293
+
294
+ const needsReviewNotes = [
295
+ '--- NOTES NEEDS REVIEW ---',
296
+ unformattedNotes,
297
+ '--------------------------'
298
+ ].join('\n')
299
+
300
+ return needsReviewNotes
301
+ }
302
+
303
+ function updateReleaseNotesFile(file, version, newNotes) {
304
+ const promise = new Promise((resolve, reject) => {
305
+ fs.readFile(file, 'utf8', function (err, data) {
306
+ if (err) {
307
+ return reject(err)
308
+ }
309
+
310
+ if (data.startsWith(`### ${version}`)) {
311
+ const errMessage = [
312
+ `${file} already contains '${version}'`,
313
+ `Delete existing ${version} release notes (if desired) and run again`
314
+ ].join('\n')
315
+
316
+ return reject(new Error(errMessage))
317
+ }
318
+
319
+ // toISOString() will always return UTC time
320
+ const todayFormatted = new Date().toISOString().split('T')[0]
321
+ const newVersionHeader = `### ${version} (${todayFormatted})`
322
+
323
+ const newContent = [
324
+ newVersionHeader,
325
+ newNotes,
326
+ '\n\n',
327
+ data
328
+ ].join('')
329
+
330
+ fs.writeFile(file, newContent, 'utf8', function (err) {
331
+ if (err) {
332
+ return reject(err)
333
+ }
334
+
335
+ console.log(`Added new release notes to ${file} under ${newVersionHeader}`)
336
+
337
+ resolve()
338
+ })
339
+ })
340
+ })
341
+
342
+ return promise
343
+ }
344
+
345
+ function getFormattedPrBody(data) {
346
+ const body = [
347
+ '## Proposed Release Notes',
348
+ data.notes,
349
+ '## Links',
350
+ data.links,
351
+ '',
352
+ '## Details',
353
+ ''
354
+ ].join('\n')
355
+
356
+ return body
357
+ }
358
+
359
+ prepareReleaseNotes()
@@ -9,9 +9,6 @@ VERSIONED_MODE="${VERSIONED_MODE:---major}"
9
9
  if [[ $TRAVIS_BRANCH == `git describe --tags --always HEAD` ]]; then
10
10
  VERSIONED_MODE=--minor
11
11
  fi
12
- # if [[ $TRAVIS_BRANCH == "master" ]]; then
13
- # VERSIONED_MODE=--minor
14
- # fi
15
12
 
16
13
  set -f
17
14
  directories=()
@@ -24,9 +21,4 @@ fi
24
21
 
25
22
  export AGENT_PATH=`pwd`
26
23
 
27
- # Don't run the aws-sdk tests if we don't have the keys set
28
- if [[ -z "$AWS_ACCESS_KEY_ID" ]]; then
29
- time ./node_modules/.bin/versioned-tests $VERSIONED_MODE -i 2 -s aws-sdk ${directories[@]}
30
- else
31
- time ./node_modules/.bin/versioned-tests $VERSIONED_MODE -i 2 ${directories[@]}
32
- fi
24
+ time ./node_modules/.bin/versioned-tests $VERSIONED_MODE -i 2 ${directories[@]}
package/index.js CHANGED
@@ -51,13 +51,13 @@ function initialize() {
51
51
  // TODO: Update this check when Node v10 is deprecated.
52
52
  if (psemver.satisfies('<10.0.0')) {
53
53
  message = 'New Relic for Node.js requires a version of Node equal to or\n' +
54
- 'greater than 8.0.0. Not starting!'
54
+ 'greater than 10.0.0. Not starting!'
55
55
 
56
56
  logger.error(message)
57
57
  throw new Error(message)
58
58
 
59
59
  // TODO: Update this check when Node v16 support is added
60
- } else if (!psemver.satisfies(pkgJSON.engines.node) || psemver.satisfies('>=15.0.0')) {
60
+ } else if (!psemver.satisfies(pkgJSON.engines.node) || psemver.satisfies('>=15.0.0')) {
61
61
  logger.warn(
62
62
  'New Relic for Node.js %s has not been tested on Node.js %s. Please ' +
63
63
  'update the agent or downgrade your version of Node.js',
@@ -76,8 +76,10 @@ function initialize() {
76
76
  // just pipes to stdout.
77
77
  logger = require('./lib/logger')
78
78
 
79
- if (!config || !config.agent_enabled) {
80
- logger.info('Module not enabled in configuration; not starting.')
79
+ if (!config) {
80
+ logger.info('No configuration detected. Not starting.')
81
+ } else if (!config.agent_enabled) {
82
+ logger.info('Module disabled in configuration. Not starting.')
81
83
  } else {
82
84
  agent = createAgent(config)
83
85
  addStartupSupportabilities(agent)
package/lib/agent.js CHANGED
@@ -293,8 +293,11 @@ Agent.prototype.forceHarvestAll = function forceHarvestAll(callback) {
293
293
  promises.push(metricPromise)
294
294
 
295
295
  // TODO: plumb config through to aggregators so they can do their own checking.
296
- if (agent.config.distributed_tracing.enabled &&
297
- agent.config.span_events.enabled) {
296
+ if (
297
+ agent.config.distributed_tracing.enabled &&
298
+ agent.config.span_events.enabled &&
299
+ !agent.spanEventAggregator.isStream // Not valid to send on streaming aggregator
300
+ ) {
298
301
  const spanPromise = new Promise((resolve) => {
299
302
  agent.spanEventAggregator.once(
300
303
  'finished span_event_data data send.',
@@ -455,6 +458,10 @@ Agent.prototype.onConnect = function onConnect() {
455
458
  this.spanEventAggregator.reconfigure(this.config)
456
459
  this.transactionEventAggregator.reconfigure(this.config)
457
460
  this.customEventAggregator.reconfigure(this.config)
461
+
462
+ if (this.config.certificates && this.config.certificates.length > 0) {
463
+ this.metrics.getOrCreateMetric(NAMES.FEATURES.CERTIFICATES).incrementCallCount()
464
+ }
458
465
  }
459
466
 
460
467
  /**