Skip to content

Latest commit

 

History

History
323 lines (199 loc) · 20.3 KB

CONTRIBUTING.md

File metadata and controls

323 lines (199 loc) · 20.3 KB

Contributing

Requirements

Please install nvm to manage multiple Node.js versions. The current development version is defined in our .nvmrc.

Python3 (< 3.11) is required, otherwise the db2 package won't build, see nodejs/node-gyp#2219.

brew install jq for OSX (for other systems please look up here) is required to run npm run audit or lerna audit run.

Note: You might need to install libpq-dev/postgresql-devel or a similar package before running npm install because pg-native depends on it. (@instana/collector and friends do not depend on pg-native but our test suite depends on it.)

After cloning the repository, run npm install in the root of the repository.

Ensure that your IDE is set up to utilize ESLint and Prettier, with automatic code formatting enabled.

Troubleshooting pg_config: command not found: The tests in this package depend on (among others) pg-native and that in turn depends on the native add-on libpq. That add-on might try to call pg_config during npm install. If npm install terminates with pg_config: command not found, install the PostgreSQL package for your system (e.g. brew install postgresql or similar). If you do not want to run any tests, you can also omit this step and install dependencies with npm install --production instead.

Install the aws-cli to publish AWS layers from local.

Upgrade npm for Node.js v14

If you are using Node.js v14 and would like to take advantage of the "overrides" feature in package.json, you need to upgrade npm manually since Node.js v14 ships with npm v6, and overrides were introduced in npm v8.3. Upgrade npm manually to the latest version using the following command:

npm install -g npm@latest

Executing Tests Locally

Some of the tests require infrastructure components (databases etc.) to run locally. The easiest way to run all required components locally is to use Docker and on top of this Docker Compose. Start the script node bin/start-test-containers.js to run all containers (not recommended).

Instead:

node bin/start-test-containers.js --mongo --redis

It's not recommended to run all tests using bin/run-tests.sh - it takes too long. Take a look at the root package.json and run a specific test group or run e.g. bin/run-collector-tests.sh with the mocha .only attribute.

If you want to see the Node.js collector's debug output while running the tests, make sure the environment variable WITH_STDOUT is set to a non-empty string.

Adding Tests

ES6

We have added a CI build to test our instrumentations against ES module apps. See #672

Not all of the current test apps have an app.mjs variant, because the effort is high. If you are adding a new test, please consider to also generate an app.mjs file for it. If you are modifying an existing application under test, and it has an accompanying .mjs file, you need to update that as well (by regenerating it).

You can use the script bin/convert-test-app-to-es6.sh for both purposes, it generates an equivalent app.mjs file from a given app.js file. For example, run bin/convert-test-app-to-es6.sh packages/collector/test/tracing/database/ioredis/app.js to regenerate the ES6 variant of the ioredis test application, when you have made changes to packages/collector/test/tracing/database/ioredis/app.js.

After regenerating an existing app.mjs file you should check the diff for it and potentially revert any changes that are artifacts of the conversion process, not related to your original changes in app.js.

Setting RUN_ESM=true locally will run use the ESM app instead of the CJS app when executing the tests.

Executing code coverage tool

If you are actively developing a feature and you would like to know which lines and files you have already covered in your tests, you can run:

npm run coverage --npm_command="test:ci:opentelemetry-exporter"

At the end of the execution it will open the coverage report in the browser and you can navigate through the result.

On Tekton we run npm run coverage-ci once per week to generate a full coverage report.

How to Contribute

This is an open source project, and we appreciate your help!

Each source file must include this license header:

/*
 * (c) Copyright IBM Corp. 2024
 */

Furthermore you must include a sign-off statement in the commit message.

Signed-off-by: John Doe john.doe@example.com

Thank you for your interest in the Instana Node.js project!

Commit messages

We are adhering to the Conventional Commits standard, which defines specific commit message types. The types we are using are: chore, build, docs, feat, fix, refactor, ci, or test.

For any change that is customer-facing and should appear in the changelog file, we require the use of either 'fix' or 'feat' in the commit message. If the change is not customer-facing and is more related to internal tasks or housekeeping, it should use 'chore' for instance. Please use 'fix' if you need to deprecate a library.

When composing commit messages, it's important to use past tense. Additionally, if there is a corresponding ticket or issue associated with the change, please put the ticket link in the commit message and pull request.

For instance, you can refer to this example commit message: https://github.com/instana/nodejs/commit/bd3e7554fe21188c3ad10d442e4d72546d5c2267

Managing Dependencies In Packages

We are using npm workspaces and lerna v7. There is no need to install lerna globally. Please use npx lerna.

Development dependencies that are shared between multiple packages should be added to the root package.json file.

Production dependencies that are required by a package always need to be added to that particular package directly. Dependencies from the root package.json are never part of the individual packages when they are uploaded to the npm registry, hence they would also not be installed for projects that depend on any @instana package. Thus, the root package.json file only has devDependencies and no dependencies.

The following sections describe how to manage dependencies in practice.

Adding A Package Dependency

npm install -D ${dependency-name}: Adds a dev dependency to the root package.json file. npm install ${dependency-name} -w packages/collector: Adds a production dependency to the package @instana/collector. This is equivalent to cd packages/collector; npm install ${dependency-name}. npm install -D ${dependency-name} -w packages/collector: Adds a dev dependency to the package @instana/collector. This is equivalent to cd packages/collector; npm install -D ${dependency-name}.

Removing A Package Dependency

npm uninstall -D ${dependency-name}: Removes a dev dependency from the root package. npm uninstall ${dependency-name} -w packages/collector: Removes a production dependency from the package @instana/collector. This is equivalent to cd packages/collector; npm uninstall ${dependency-name}. npm uninstall -D ${dependency-name} -w packages/collector: Removes a dev dependency from the package @instana/collector. This is equivalent to cd packages/collector; npm uninstall -D ${dependency-name}.

Updating A Single Version In A package.json File

npm install ${dependency-name}@${version}: Updates a specific production dependency on the root. npm install -D ${dependency-name}@${version}: Updates a specific dev dependency on the root. npm install ${dependency-name}@${version} -w packages/collector: Updates a specific production dependency in the package @instana/collector. This is equivalent to cd packages/collector; npm install ${dependency-name}@${version}. npm install -D ${dependency-name}@${version} -w packages/collector: Updates a specific dev dependency in the package @instana/collector. This is equivalent to cd packages/collector; npm install -D ${dependency-name}@${version}.

Updating A Single Version In A Lockfile

npm update ${dependency-name}: Updates a specific production dependency on the root lock file. npm update -D ${dependency-name}: Updates a specific dev dependency on the root lock file. npm update ${dependency-name} -w packages/collector: Updates a specific production dependency of the package @instana/collector. This is equivalent to cd packages/collector; npm update ${dependency-name}. npm update -D ${dependency-name} -w packages/collector: Updates a specific dev dependency of the package @instana/collector. This is equivalent to cd packages/collector; npm update -D ${dependency-name}.

package-lock.json

There is only one single package-lock.json file when using npm workspaces.

We are currently using lockfileVersion 3.

If you need to recreate the package lock file, please us npm i --lockfile-version 2.

Refs: - https://docs.npmjs.com/cli/v9/configuring-npm/package-lock-json#lockfileversion - #710

Note: Packages published on the npm registry never contain package-lock.json files. So the versions pinned in our package-lock.json file are only relevant to determine the package versions that get installed when running npm install inside this repository (locally or on CI), they do not affect users installing @instana packages as a dependency.

Version Ranges vs. Pinning a Specific Version

When adding new production dependencies, there is always the choice to either:

  • add the dependency with a version range, e.g. list it as "package-name": "~x.y.z" or "package-name": "^x.y.z" in the package.json file, or
  • pin an exact version, that is, add it as "package-name": "x.y.z" to package.json.

Since we maintain packages that are used as libraries by others (in contrast to maintaining an application that we run ourselves), we should usually allow a version range of type ^ for all dependencies, unless there are very specific reasons not to. A ^ version range of ^1.2.3 matches all versions of >= 1.2.3 and < 2.0.0, that is, it allows newer patch and minor versions, but no newer major version. This allows users of @instana/collector and other @instana/... packages to update any transitive dependency these packages have on their own, without depending on IBM to release a new version of these packages. This is particularly relevant when transitive dependencies get flagged by vulnerability scanners. Nevertheless, we also always update depdencies when they get flagged by npm audit in a timely manner.

Possible reasons to pin an exact version of a dependency:

  • We are using internals of the library (monkey-patching functions, depending on internal APIs etc.)
  • The library had a history of introducing breaking changes in a semver-minor or semver-patch update. That is, we don't trust the maintainers to handle semver correctly.

These rules apply first and foremost to production dependencies (that is, dependencies and optionalDependencies, not as strictly for devDependencies). Still, for devDependencies, we usually should use ^ version ranges as well.

Adding A New Package

When adding a new package to this monorepo, there are a few things to consider.

Example PR to add a new package: #932

Naming

Please choose a strong and meaninful name for the package. Discuss the name within the team.

Set Publishing Access For The New Package

After the package has been published to the npm registry for the first time, visit https://www.npmjs.com/package/@instana/${package-name}/access and set publishing access to "Require two-factor authentication or automation tokens". (The default setting is "Two-factor authentication is not required".)

Release Process

Publishing A New Release

To make a release, you first need to ensure that the released version will either be a semver minor or patch release, so that automatic updates are working for our users. Major releases require a different workflow, see below. A major release would occur when the list of commits since the last release contains breaking changes, that is, commits with a commit comment with a footer starting with "BREAKING CHANGE:" (see https://www.conventionalcommits.org/en/v1.0.0/#specification for details).

The process to publish a new minor or patch release (that is, new versions of all packages) is as follows:

The Github action will try to publish new versions for all packages from the most recent commit of the main branch.

Parameters for the release Github action:

  • "Use lerna publish from-package": Instead of executing lerna publish, the action will execute lerna publish from-package. See below for the use case for this parameter. If in doubt, leave it unchanged (that is, false).
  • "Dry Run": With this option set to true, the Github action will not actually create a release but only apply all changes (package.json and package-lock.json files, CHANGELOG files). The action log will show the resulting diff at the end. The changes will not be committed. This can be used to preview the changes the release action would apply. If this option is true, the option "Use lerna publish from-package" has no effect.

Lerna will determine if this is going to be a minor or patch version from the commit comments of all commits since the last release. It will also automatically update the CHANGELOG.md files in the root of the repository and also in all individual packages, based on those commits.

If you want to locally review the changes that lerna is about to apply (for example the CHANGELOG files), you can execute lerna version --no-git-tag-version --no-push --conventional-commits locally beforehand, inspect the diff, and discard the changes afterwards. You can also use the dry run option (see above).

For each release, we also publishing a new Lambda layer, a Fargate Docker image layer and a Google Cloud Run image layer. This happens automatically via CI.

The Github action will send a Slack notification to the channel of team Node.js. Additionally, you can enable notifications for actions for your account at https://github.com/settings/notifications (in the section titled "Actions"). But enabling that might also get you notifications for other repositories you contribute to.

Separate lerna version And lerna publish

It is possible to execute the git parts of a release (version bump and tagging) and the npm part (publishing to the npm registry) separately. This can be used to rectify a situation when the lerna publish step of the Github release action did not go through successfully and has only completed the git actions but not the npm publish, or only published a subset of the packages successfully.

  • Run lerna version to bump all versions and create a git tag.
    • You can skip this step if it has already happened for the release you want to publish. If not, the step is mandatory.
    • Lerna will determine if this is going to be a major, minor or patch version from the commit comments of all commits since the last release. It will also update all relevant changelog files.
    • Lerna will push the commit and the tag created by lerna version to GitHub. Check that this has happened. Also check that the version numbers in package-lock.json have been updated as well.
  • Run the Github action release-on-demand with the parameter Use lerna publish from-package set to true.

This will execute lerna publish from-package. This can also be used if the previous execution of the release Github action went through for a subset of packages but not for others. Lerna will automatically figure out for which packages the latest version is not present in the registry and only publish those.

Rate Limited OTP

If publishing the packages fails with an error like this:

lerna ERR! E429 Could not authenticate ${npm-user-name}: rate limited otp

you will need to wait five minutes before trying again. In case some packages have already been published and others have not, use the option "Use lerna publish from-package" as explained in the previous section.

Publishing A Pre-Release

To publish a pre-release for the next major version, execute:

NPM_CONFIG_OTP={your token} lerna publish --dist-tag next --preid rc

This assumes that you are on a branch where the versions in all package.json files have already been bumped to x.0.0 with x being the next major version.

Publishing without Pre-Releases

To publish the next major version, execute:

NPM_CONFIG_OTP={TOKEN} lerna publish --no-verify-access --force-publish

Publishing A Major Release When A Pre-Release Exists

Doing a stable release after a prerelease requires you to use:

NPM_CONFIG_OTP={TOKEN} lerna publish --force-publish --conventional-graduate

NOTE: With each prerelease the changelogs are getting updated. It might be wishful to delete the changelog before doing a final major release.

Adding support for a new Node.js version

Example PR Node v18 #529

Native dependencies (prebuilds)

We deliver prebuilds of native dependencies for every supported Node version in our releases.

You can find the ABI (NODE_MODULE_VERSION) here. They removed the ABI versions from this page. You can read it from this link for now.

shared-metrics package

Please run these commands to generate the prebuilds for event-loop-stats and gcstats:

cd native-dep-packs
./rebuild-precompiled-addons.sh
BUILD_FOR_MACOS=true ./rebuild-precompiled-addons.sh

autoprofile package

Please read packages/autoprofile/CONTRIBUTING.md.

Support for Cloud Services

Publishing local layer to AWS

To publish local layer to AWS with the AWS CLI, the CLI needs to have access to the IAM credentials.

Steps to follow

  • Create an IAM user if you don't already have one with the required rights.
  • Create credentials file on the following folder location, ~/.aws
  • Add below informations to the newly created credentials file. The access key and secret key can be obtained from the IAM user account.
[default]
aws_access_key_id = <add your access key>
aws_secret_access_key = <add your secret key>

More information can be found here.

  • Run these commands to publish local layer to AWS:
cd packages/aws-lambda/layer/bin/
REGIONS=<region> SKIP_DOCKER_IMAGE=true BUILD_LAYER_WITH=local LAYER_NAME=experimental-instana-nodejs-with-extension ./publish-layer.sh

ESM Support

We have added the ESM support for all Node.js versions, Since version 20.6, ESM loaders are off-thread, loaded separately, a shift from previous setups where the Instana collector was loaded within the loader, leading to a disruption in existing implementation. To resolve this, we've replaced the deprecated --experimental-loader with --import, facilitating the loading of the collector in the main thread. However, note that --import is only compatible with Node.js v18.19 and later, necessitating the maintenance of both styles for different Node.js versions.

Efforts are ongoing to integrate native ESM support, detailed in ref INSTA-807.

Use the following command to enable experimental ESM support:

  • For Node.js versions greater than or equal to 18.19:
node --import  /path/to/instana/node_modules/@instana/collector/esm-register.mjs entry-point
  • For Node.js versions less than 18.19:
node --experimental-loader /path/to/instana/node_modules/@instana/collector/esm-loader.mjs entry-point