diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml index b847bc2d3e..5227ce2b49 100644 --- a/.github/workflows/benchmark.yml +++ b/.github/workflows/benchmark.yml @@ -17,10 +17,13 @@ jobs: NPM_CONFIG_UNSAFE_PERM: true steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: + cache: 'npm' + cache-dependency-path: | + package-lock.json node-version: ${{ matrix.node_version }} - run: npm install -g npm@latest diff --git a/.github/workflows/docs.yaml b/.github/workflows/docs.yaml index 9c5e8037c7..3c519e632e 100644 --- a/.github/workflows/docs.yaml +++ b/.github/workflows/docs.yaml @@ -13,6 +13,9 @@ jobs: - uses: actions/setup-node@v4 with: + cache: 'npm' + cache-dependency-path: | + package-lock.json node-version: '18' - name: Install and Build 🔧 diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index eac7627c9b..132fb5e606 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -12,12 +12,15 @@ jobs: runs-on: ubuntu-latest steps: + - name: Checkout + uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: + cache: 'npm' + cache-dependency-path: | + package-lock.json node-version: '16' - - uses: actions/checkout@v4 - - name: Lint changelog file uses: avto-dev/markdown-lint@v1 with: diff --git a/.github/workflows/peer-api.yml b/.github/workflows/peer-api.yml index bfcd7ab152..f7b84a8bf0 100644 --- a/.github/workflows/peer-api.yml +++ b/.github/workflows/peer-api.yml @@ -20,7 +20,7 @@ jobs: run: npm install -g lerna@6.6.2 - name: Install semver - run: npm install semver + run: npm install -g semver - name: Check API dependency semantics working-directory: packages diff --git a/.github/workflows/unit-test.yml b/.github/workflows/unit-test.yml index ffebc82c29..281d1f4d29 100644 --- a/.github/workflows/unit-test.yml +++ b/.github/workflows/unit-test.yml @@ -15,6 +15,7 @@ jobs: - "14" - "16" - "18" + - "20" runs-on: ubuntu-latest env: NPM_CONFIG_UNSAFE_PERM: true @@ -24,6 +25,9 @@ jobs: - uses: actions/setup-node@v4 with: + cache: 'npm' + cache-dependency-path: | + package-lock.json node-version: ${{ matrix.node_version }} - run: npm install -g npm@latest @@ -42,7 +46,7 @@ jobs: - name: Unit tests run: | # TODO(legendecas): webpack https://stackoverflow.com/questions/69692842/error-message-error0308010cdigital-envelope-routinesunsupported - if [ "${{ matrix.node_version }}" = "18" ]; then + if [ "${{ matrix.node_version }}" = "18" ] || [ "${{ matrix.node_version }}" == "20" ]; then export NODE_OPTIONS=--openssl-legacy-provider fi npm run test @@ -59,6 +63,9 @@ jobs: - uses: actions/setup-node@v4 with: + cache: 'npm' + cache-dependency-path: | + package-lock.json node-version: '18' - run: npm install -g npm@latest @@ -85,6 +92,9 @@ jobs: - uses: actions/setup-node@v4 with: + cache: 'npm' + cache-dependency-path: | + package-lock.json node-version: 16 - name: Bootstrap @@ -106,6 +116,9 @@ jobs: uses: actions/checkout@v4.0.0 - uses: actions/setup-node@v4 with: + cache: 'npm' + cache-dependency-path: | + package-lock.json node-version: 16 - name: Bootstrap @@ -133,6 +146,9 @@ jobs: - uses: actions/setup-node@v4 with: + cache: 'npm' + cache-dependency-path: | + package-lock.json node-version: ${{ matrix.node_version }} - name: Build diff --git a/.github/workflows/w3c-integration-test.yml b/.github/workflows/w3c-integration-test.yml index 1df205b05d..723314cb05 100644 --- a/.github/workflows/w3c-integration-test.yml +++ b/.github/workflows/w3c-integration-test.yml @@ -16,6 +16,9 @@ jobs: - uses: actions/setup-node@v4 with: + cache: 'npm' + cache-dependency-path: | + package-lock.json node-version: '16' - name: Install and Bootstrap 🔧 diff --git a/.npmrc b/.npmrc index b15c08c1bd..776cfbfbf2 100644 --- a/.npmrc +++ b/.npmrc @@ -1,2 +1 @@ -//registry.npmjs.org/:_authToken=${NPM_TOKEN} lockfile-version=2 diff --git a/CHANGELOG.md b/CHANGELOG.md index 336e211d6f..22ce796f14 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ For experimental package changes, see the [experimental CHANGELOG](experimental/ * chore: type reference on zone.js [#4257](https://github.com/open-telemetry/opentelemetry-js/pull/4257) @legendecas * chore: no need for 'packages' in lerna.json [#4264](https://github.com/open-telemetry/opentelemetry-js/pull/4264) @trentm +* test: add node 20 to test matrix [#4336](https://github.com/open-telemetry/opentelemetry-js/pull/4336) @dyladan ### :bug: (Bug Fix) @@ -365,16 +366,8 @@ There are no changes between 1.0.0 and the previous 0.33.0 version. * fix(sdk-web): parse url with relative url string [#2972](https://github.com/open-telemetry/opentelemetry-js/pull/2972) @legendecas -### :books: (Refine Doc) - -### :house: (Internal) - ## 1.2.0 -### :boom: Breaking Change - -### :rocket: (Enhancement) - ### :bug: (Bug Fix) * fix: sanitize attributes inputs [#2881](https://github.com/open-telemetry/opentelemetry-js/pull/2881) @legendecas @@ -2290,7 +2283,9 @@ Released 2020-03-19 Released 2020-03-16 -### This is a first official beta release, which provides almost fully complete metrics, tracing, and context propagation functionality but makes no promises around breaking changes +### First official beta release + +* provides almost fully complete metrics, tracing, and context propagation functionality but makes **no promises** around breaking changes ### :boom: Breaking Change diff --git a/README.md b/README.md index f20b3e8950..a2c0387fdc 100644 --- a/README.md +++ b/README.md @@ -111,6 +111,7 @@ If you are a library author looking to build OpenTelemetry into your library, pl | Platform Version | Supported | |---------------------|-----------------------------------------------| +| Node.JS `v20` | :heavy_check_mark: | | Node.JS `v18` | :heavy_check_mark: | | Node.JS `v16` | :heavy_check_mark: | | Node.JS `v14` | :heavy_check_mark: | diff --git a/experimental/CHANGELOG.md b/experimental/CHANGELOG.md index d462a066fe..1bcefed6d3 100644 --- a/experimental/CHANGELOG.md +++ b/experimental/CHANGELOG.md @@ -15,10 +15,7 @@ All notable changes to experimental packages in this project will be documented * fix(sdk-logs): avoid map attribute set when count limit exceeded * fix(instrumentation-fetch): only access navigator if it is defined [#4063](https://github.com/open-telemetry/opentelemetry-js/pull/4063) * allows for experimental usage of this instrumentation with non-browser runtimes - -### :books: (Refine Doc) - -### :house: (Internal) +* fix(instrumentation-http): memory leak when responses are not resumed ## 0.45.1 @@ -185,8 +182,6 @@ All notable changes to experimental packages in this project will be documented * doc(instrumentation): add limitiations section to readme [#3786](https://github.com/open-telemetry/opentelemetry-js/pull/3786) @flarna -### :house: (Internal) - ## 0.38.0 ### :boom: Breaking Change @@ -403,10 +398,6 @@ All notable changes to experimental packages in this project will be documented * fix(histogram): fix maximum when only values < -1 are provided [#3086](https://github.com/open-telemetry/opentelemetry-js/pull/3086) @pichlermarc * fix(instrumentation-grpc): always set grpc semcov status code attribute with numeric value [#3076](https://github.com/open-telemetry/opentelemetry-js/pull/3076) @blumamir -### :books: (Refine Doc) - -### :house: (Internal) - ## 0.30.0 ### :boom: Breaking Change @@ -481,10 +472,6 @@ All notable changes to experimental packages in this project will be documented * fix(metrics): specification compliant default metric unit [#2983](https://github.com/open-telemetry/opentelemetry-js/pull/2983) @andyfleming * fix(opentelemetry-instrumentation): use all provided patches for the same file [#2963](https://github.com/open-telemetry/opentelemetry-js/pull/2963) @Ugzuzg -### :books: (Refine Doc) - -### :house: (Internal) - ## 0.28.0 ### :boom: Breaking Change diff --git a/experimental/packages/opentelemetry-exporter-prometheus/test/PrometheusExporter.test.ts b/experimental/packages/opentelemetry-exporter-prometheus/test/PrometheusExporter.test.ts index b4c6f0ade0..1030203a2c 100644 --- a/experimental/packages/opentelemetry-exporter-prometheus/test/PrometheusExporter.test.ts +++ b/experimental/packages/opentelemetry-exporter-prometheus/test/PrometheusExporter.test.ts @@ -360,8 +360,8 @@ describe('PrometheusExporter', () => { .get('http://localhost:9464/metrics', res => { errorHandler(done)(new Error('unreachable')); }) - .on('error', err => { - assert(`${err}`.match('ECONNREFUSED')); + .on('error', (err: any) => { + assert.equal(err.code, 'ECONNREFUSED'); done(); }); }); diff --git a/experimental/packages/opentelemetry-instrumentation-http/src/http.ts b/experimental/packages/opentelemetry-instrumentation-http/src/http.ts index 9422bbc9ef..81e08ac860 100644 --- a/experimental/packages/opentelemetry-instrumentation-http/src/http.ts +++ b/experimental/packages/opentelemetry-instrumentation-http/src/http.ts @@ -330,6 +330,9 @@ export class HttpInstrumentation extends InstrumentationBase { 'response', (response: http.IncomingMessage & { aborted?: boolean }) => { this._diag.debug('outgoingRequest on response()'); + if (request.listenerCount('response') <= 1) { + response.resume(); + } const responseAttributes = utils.getOutgoingRequestAttributesOnResponse(response); span.setAttributes(responseAttributes); diff --git a/experimental/packages/opentelemetry-instrumentation-http/test/utils/rawRequest.ts b/experimental/packages/opentelemetry-instrumentation-http/test/utils/rawRequest.ts index c2fefa284a..dc00fabfad 100644 --- a/experimental/packages/opentelemetry-instrumentation-http/test/utils/rawRequest.ts +++ b/experimental/packages/opentelemetry-instrumentation-http/test/utils/rawRequest.ts @@ -22,7 +22,7 @@ export async function sendRequestTwice( port: number ): Promise { return new Promise((resolve, reject) => { - const request = 'GET /raw HTTP/1.1\n\n'; + const request = `GET /raw HTTP/1.1\r\nHost: ${host}:${port}\r\n\r\n`; const socket = net.createConnection({ host, port }, () => { socket.write(`${request}${request}`, err => { if (err) reject(err); diff --git a/experimental/packages/opentelemetry-instrumentation/src/index.ts b/experimental/packages/opentelemetry-instrumentation/src/index.ts index 8420c6968e..0185bfc79d 100644 --- a/experimental/packages/opentelemetry-instrumentation/src/index.ts +++ b/experimental/packages/opentelemetry-instrumentation/src/index.ts @@ -15,7 +15,9 @@ */ export * from './autoLoader'; -export * from './platform/index'; +export { InstrumentationBase } from './platform/index'; +export { InstrumentationNodeModuleDefinition } from './instrumentationNodeModuleDefinition'; +export { InstrumentationNodeModuleFile } from './instrumentationNodeModuleFile'; export * from './types'; export * from './types_internal'; export * from './utils'; diff --git a/experimental/packages/opentelemetry-instrumentation/src/instrumentation.ts b/experimental/packages/opentelemetry-instrumentation/src/instrumentation.ts index 4b729fd439..4552f6dfab 100644 --- a/experimental/packages/opentelemetry-instrumentation/src/instrumentation.ts +++ b/experimental/packages/opentelemetry-instrumentation/src/instrumentation.ts @@ -25,16 +25,19 @@ import { TracerProvider, } from '@opentelemetry/api'; import * as shimmer from 'shimmer'; -import { InstrumentationModuleDefinition } from './platform/node'; -import * as types from './types'; +import { + InstrumentationModuleDefinition, + Instrumentation, + InstrumentationConfig, +} from './types'; /** * Base abstract internal class for instrumenting node and web plugins */ export abstract class InstrumentationAbstract - implements types.Instrumentation + implements Instrumentation { - protected _config: types.InstrumentationConfig; + protected _config: InstrumentationConfig; private _tracer: Tracer; private _meter: Meter; @@ -43,7 +46,7 @@ export abstract class InstrumentationAbstract constructor( public readonly instrumentationName: string, public readonly instrumentationVersion: string, - config: types.InstrumentationConfig = {} + config: InstrumentationConfig = {} ) { this._config = { enabled: true, @@ -95,7 +98,7 @@ export abstract class InstrumentationAbstract } /* Returns InstrumentationConfig */ - public getConfig(): types.InstrumentationConfig { + public getConfig(): InstrumentationConfig { return this._config; } @@ -103,7 +106,7 @@ export abstract class InstrumentationAbstract * Sets InstrumentationConfig to this plugin * @param InstrumentationConfig */ - public setConfig(config: types.InstrumentationConfig = {}): void { + public setConfig(config: InstrumentationConfig = {}): void { this._config = Object.assign({}, config); } diff --git a/experimental/packages/opentelemetry-instrumentation/src/platform/node/instrumentationNodeModuleDefinition.ts b/experimental/packages/opentelemetry-instrumentation/src/instrumentationNodeModuleDefinition.ts similarity index 100% rename from experimental/packages/opentelemetry-instrumentation/src/platform/node/instrumentationNodeModuleDefinition.ts rename to experimental/packages/opentelemetry-instrumentation/src/instrumentationNodeModuleDefinition.ts diff --git a/experimental/packages/opentelemetry-instrumentation/src/platform/node/instrumentationNodeModuleFile.ts b/experimental/packages/opentelemetry-instrumentation/src/instrumentationNodeModuleFile.ts similarity index 100% rename from experimental/packages/opentelemetry-instrumentation/src/platform/node/instrumentationNodeModuleFile.ts rename to experimental/packages/opentelemetry-instrumentation/src/instrumentationNodeModuleFile.ts diff --git a/experimental/packages/opentelemetry-instrumentation/src/platform/browser/index.ts b/experimental/packages/opentelemetry-instrumentation/src/platform/browser/index.ts index 24c76056a1..0b238b42b8 100644 --- a/experimental/packages/opentelemetry-instrumentation/src/platform/browser/index.ts +++ b/experimental/packages/opentelemetry-instrumentation/src/platform/browser/index.ts @@ -14,4 +14,4 @@ * limitations under the License. */ -export * from './instrumentation'; +export { InstrumentationBase } from './instrumentation'; diff --git a/experimental/packages/opentelemetry-instrumentation/src/platform/index.ts b/experimental/packages/opentelemetry-instrumentation/src/platform/index.ts index cdaf8858ce..81d3096252 100644 --- a/experimental/packages/opentelemetry-instrumentation/src/platform/index.ts +++ b/experimental/packages/opentelemetry-instrumentation/src/platform/index.ts @@ -14,4 +14,4 @@ * limitations under the License. */ -export * from './node'; +export { InstrumentationBase } from './node'; diff --git a/experimental/packages/opentelemetry-instrumentation/src/platform/node/index.ts b/experimental/packages/opentelemetry-instrumentation/src/platform/node/index.ts index 842797c341..1e81931b2a 100644 --- a/experimental/packages/opentelemetry-instrumentation/src/platform/node/index.ts +++ b/experimental/packages/opentelemetry-instrumentation/src/platform/node/index.ts @@ -13,7 +13,4 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -export * from './instrumentation'; -export * from './instrumentationNodeModuleDefinition'; -export * from './instrumentationNodeModuleFile'; -export * from './types'; +export { InstrumentationBase } from './instrumentation'; diff --git a/experimental/packages/opentelemetry-instrumentation/src/platform/node/instrumentation.ts b/experimental/packages/opentelemetry-instrumentation/src/platform/node/instrumentation.ts index 03d8f6ba37..a4c11498c9 100644 --- a/experimental/packages/opentelemetry-instrumentation/src/platform/node/instrumentation.ts +++ b/experimental/packages/opentelemetry-instrumentation/src/platform/node/instrumentation.ts @@ -26,7 +26,7 @@ import { } from './RequireInTheMiddleSingleton'; import type { HookFn } from 'import-in-the-middle'; import * as ImportInTheMiddle from 'import-in-the-middle'; -import { InstrumentationModuleDefinition } from './types'; +import { InstrumentationModuleDefinition } from '../../types'; import { diag } from '@opentelemetry/api'; import type { OnRequireFn } from 'require-in-the-middle'; import { Hook } from 'require-in-the-middle'; diff --git a/experimental/packages/opentelemetry-instrumentation/src/platform/node/types.ts b/experimental/packages/opentelemetry-instrumentation/src/platform/node/types.ts deleted file mode 100644 index 5cdfbc84c0..0000000000 --- a/experimental/packages/opentelemetry-instrumentation/src/platform/node/types.ts +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -export interface InstrumentationModuleFile { - /** Name of file to be patched with relative path */ - name: string; - - moduleExports?: T; - - /** Supported version this file */ - supportedVersions: string[]; - - /** Method to patch the instrumentation */ - patch(moduleExports: T, moduleVersion?: string): T; - - /** Method to patch the instrumentation */ - - /** Method to unpatch the instrumentation */ - unpatch(moduleExports?: T, moduleVersion?: string): void; -} - -export interface InstrumentationModuleDefinition { - /** Module name or path */ - name: string; - - moduleExports?: T; - - /** Instrumented module version */ - moduleVersion?: string; - - /** Supported version of module */ - supportedVersions: string[]; - - /** Module internal files to be patched */ - // eslint-disable-next-line @typescript-eslint/no-explicit-any - files: InstrumentationModuleFile[]; - - /** If set to true, the includePrerelease check will be included when calling semver.satisfies */ - includePrerelease?: boolean; - - /** Method to patch the instrumentation */ - patch?: (moduleExports: T, moduleVersion?: string) => T; - - /** Method to unpatch the instrumentation */ - unpatch?: (moduleExports: T, moduleVersion?: string) => void; -} diff --git a/experimental/packages/opentelemetry-instrumentation/src/types.ts b/experimental/packages/opentelemetry-instrumentation/src/types.ts index 837f096792..760e31165f 100644 --- a/experimental/packages/opentelemetry-instrumentation/src/types.ts +++ b/experimental/packages/opentelemetry-instrumentation/src/types.ts @@ -77,3 +77,47 @@ export interface ShimWrapped extends Function { // eslint-disable-next-line @typescript-eslint/ban-types __original: Function; } + +export interface InstrumentationModuleFile { + /** Name of file to be patched with relative path */ + name: string; + + moduleExports?: T; + + /** Supported version this file */ + supportedVersions: string[]; + + /** Method to patch the instrumentation */ + patch(moduleExports: T, moduleVersion?: string): T; + + /** Method to patch the instrumentation */ + + /** Method to unpatch the instrumentation */ + unpatch(moduleExports?: T, moduleVersion?: string): void; +} + +export interface InstrumentationModuleDefinition { + /** Module name or path */ + name: string; + + moduleExports?: T; + + /** Instrumented module version */ + moduleVersion?: string; + + /** Supported version of module */ + supportedVersions: string[]; + + /** Module internal files to be patched */ + // eslint-disable-next-line @typescript-eslint/no-explicit-any + files: InstrumentationModuleFile[]; + + /** If set to true, the includePrerelease check will be included when calling semver.satisfies */ + includePrerelease?: boolean; + + /** Method to patch the instrumentation */ + patch?: (moduleExports: T, moduleVersion?: string) => T; + + /** Method to unpatch the instrumentation */ + unpatch?: (moduleExports: T, moduleVersion?: string) => void; +} diff --git a/package.json b/package.json index 4ea5fa489a..a2d3828583 100644 --- a/package.json +++ b/package.json @@ -37,11 +37,11 @@ "comment_prepare_1": "echo scripts in this section automatically prepare releases. Intended for use by maintainers only.", "comment_prepare_2": "echo experimental preparation scripts only prepare experimental packages", - "prepare_release:experimental:patch": "npm run _check:no_changes && npm run _backup:package-json && npm run _lerna:remove_api && npm run _lerna:remove_stable && npm run _lerna:version_patch && npm run _restore:package-json", - "prepare_release:experimental:minor": "npm run _check:no_changes && npm run _backup:package-json && npm run _lerna:remove_api && npm run _lerna:remove_stable && npm run _lerna:version_minor && npm run _restore:package-json", + "prepare_release:experimental:patch": "npm run _check:no_changes && npm run _backup:package-json && npm run _lerna:remove_api && npm run _lerna:remove_stable && npm run _lerna:version_patch && npm run _restore:package-json && npm run _changelog:prepare_experimental", + "prepare_release:experimental:minor": "npm run _check:no_changes && npm run _backup:package-json && npm run _lerna:remove_api && npm run _lerna:remove_stable && npm run _lerna:version_minor && npm run _restore:package-json && npm run _changelog:prepare_experimental", "comment_prepare_3": "echo sdk preparation scripts prepare all stable and experimental packages", - "prepare_release:sdk:patch": "npm run _check:no_changes && npm run _backup:package-json && npm run _lerna:remove_api && npm run _lerna:version_patch && npm run _restore:package-json", - "prepare_release:sdk:minor": "npm run _check:no_changes && npm run _backup:package-json && npm run _lerna:remove_api && npm run _lerna:version_minor && npm run _restore:package-json", + "prepare_release:sdk:patch": "npm run _check:no_changes && npm run _backup:package-json && npm run _lerna:remove_api && npm run _lerna:version_patch && npm run _restore:package-json && npm run _changelog:prepare_experimental && npm run _changelog:prepare_stable", + "prepare_release:sdk:minor": "npm run _check:no_changes && npm run _backup:package-json && npm run _lerna:remove_api && npm run _lerna:version_minor && npm run _restore:package-json && npm run _changelog:prepare_experimental && npm run _changelog:prepare_stable", "release:publish": "lerna publish from-package --no-push --no-private --no-git-tag-version --no-verify-access", "comment_internal": "echo scripts below this line are for internal use", @@ -51,7 +51,9 @@ "_lerna:remove_api": "node -e 'var fs=require(\"fs\");var p=require(\"./package.json\");p.workspaces=p.workspaces.filter(p=>p!==\"api\");fs.writeFileSync(\"package.json\",JSON.stringify(p,null,2))'", "_lerna:remove_stable": "node -e 'var fs=require(\"fs\");var p=require(\"./package.json\");p.workspaces=p.workspaces.filter(p=>p!==\"packages/*\");fs.writeFileSync(\"package.json\",JSON.stringify(p,null,2))'", "_lerna:version_patch": "npx lerna version patch --exact --no-git-tag-version --no-push --yes", - "_lerna:version_minor": "npx lerna version minor --exact --no-git-tag-version --no-push --yes" + "_lerna:version_minor": "npx lerna version minor --exact --no-git-tag-version --no-push --yes", + "_changelog:prepare_experimental": "node scripts/update-changelog.js ./experimental/CHANGELOG.md ./experimental/packages/", + "_changelog:prepare_stable": "node scripts/update-changelog.js ./CHANGELOG.md ./packages/" }, "repository": "open-telemetry/opentelemetry-js", "keywords": [ diff --git a/scripts/update-changelog.js b/scripts/update-changelog.js new file mode 100644 index 0000000000..f131afc0ce --- /dev/null +++ b/scripts/update-changelog.js @@ -0,0 +1,65 @@ +/** + * This script updates changelogs after lerna has updated versions in the respective areas (packages/*, experimental/packages/*) + * - removes all empty subsections (bugs, enhancements, etc.) in the changelog. + * - replaces the "Unreleased"-header with the version from the first non-private package in the directory (versions are expected to be uniform across a changelog) + * - adds a new "Unreleased"-header with empty subsections at the top + * + * Usage (from project root): + * - node scripts/update-changelog.js [PATH TO CHANGELOG] [DIRECTORY CONTAINING ASSOCIATED PACKAGES] + * Examples: + * - node scripts/update-changelog.js ./CHANGELOG.md ./packages + * - node scripts/update-changelog.js ./experimental/CHANGELOG.md ./experimental/packages + */ + +const fs = require('fs'); +const path = require("path"); + +const EMPTY_UNRELEASED_SECTION = `## Unreleased + +### :boom: Breaking Change + +### :rocket: (Enhancement) + +### :bug: (Bug Fix) + +### :books: (Refine Doc) + +### :house: (Internal) + +` + +function findFirstPackageVersion(basePath){ + const packageDirs = fs.readdirSync(basePath); + for(const packageDir of packageDirs){ + const packageJsonPath = path.join(basePath, packageDir, 'package.json'); + try { + const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8')); + + if(packageJson.private === true || packageJson.private === 'true'){ + console.log('Skipping version from private package at', packageJsonPath); + continue; + } + + if(packageJson.version != null){ + return packageJson.version; + } + + console.log('Version in', packageJsonPath, 'was null or undefined, skipping'); + } catch (err) { + console.log('Could not get package JSON', packageJsonPath, err); + } + } + throw new Error('Unable to extract version from packages in ' + basePath); +} + +// no special handling for bad args as this is only intended for use via predefined npm scripts. +const changelogPath = path.resolve(process.argv[2]); +const version = findFirstPackageVersion(path.resolve(process.argv[3])); + +const changelog = fs.readFileSync(changelogPath, 'utf8').toString() + // replace all empty sections + .replace(new RegExp('^###.*\n*(?=^##)', 'gm'), '') + // replace unreleased header with new unreleased section and a version header for the former unreleased section + .replace(RegExp('## Unreleased'), EMPTY_UNRELEASED_SECTION + '## ' + version); + +fs.writeFileSync(changelogPath, changelog);