Dependency Bundle Manager
In an enterprise scenario there can be 100s of applications using core infrastructure dependencies / modules.
Node.js applications typically manage their own dependencies, and use package.json
and package-lock.json
. Using global dependencies is uncommon. This means, rolling out an important non-breaking update / fix to all applications, needs rebuilding all applications with the updated dependencies.
Node.js runtime manages core modules
, and these are not deployed with the application, and act as global. Following this concept, and managing infrastructure dependencies outside the application as a global bundle is a possible solution for better management and control on the core infrastructure dependencies.
This can leverage mechanics used by Node.js require
for resolving a module. While trying to resolve a dependency, require
looks up for dependencies installed in the node_modules
folder under the immediate parent directory, and continues to iterate through the parent folders to check other node_modules
folders - stopping when either it finds the dependency, or it reaches the file-system root.
This process also involves looking up global folders starting with the one specified under NODE_PATH
environment variable.
require
's mechanics of resolving dependencies can be leveraged, and the infra dependencies as a bundle can be deployed under one the candidate folder outside the application directory. This then can enable separating deployment process and life cycle of the infra dependencies from the application's release and deployment process.
Here's how a managed deployment can look like:
... / path-of-app.... / app
| |
_ node_modules |
| |
- infra dependencies |
- node_modules
|
_ application dependencies
For all this to work effectively, dependencies should be compatible with one another. This means:
- avoid scenarios where multiple versions of a dependency gets included. i.e. all dependencies should expect the same version of a particular dependency. Ex: Dependencies A and B, should include the same semver of dependency C in their package.json to avoid multiple versions of C in the bundle.
- run test coverage for all infra dependencies while deployed as a bundle.
- follow semver rules of versioning while releasing a dependency bundle with new functionality, or with bug fixes. Breaking changes would require semver-major.
This is to manage infra dependency bundles.
- There can be a need for multiple bundles within an organization for supporting different flavors of applications, like UI+API, API only, Batch.
- Need for supporting multiple major versions of the same bundle.
- Ideally an organization should expect all applications depending on a specific major version to use the latest minor+patch version.
- While developing, developers may need to deploy multiple bundle + version combinations at any time to support multiple applications they may be running / developing at any time. This tries to leverage the
NODE_PATH
environment variable supported by therequire
module of Node.js. - CI jobs also need tools to deploy and manage multiple bundles, and allow selecting bundle name, version per job.
$ npm install -g https://github.com/sumeetkakkar/dbm.git
# $ npm install -g dbm
Add following in .profile, .bash_profile, .bashrc, .zshrc
export DBM_DIR="$HOME/.ndb"
[ -s "$DBM_DIR/bin/dbm.sh" ] && \. "$DBM_DIR/bin/dbm.sh" # This loads dbm
Individual commands support CLI options for accepting configurations needed by them.
.dbmrc
file is supported for setting configurations. File can be added under the project directory (or, one of it's parent). The file under the HOME
directory is used as default
.
Ex:
$ cat .dbmrc
store=git
repo=https://github.com/user_or_org
The environment variables with names prepended with dbm_config_
are also supported. These get higher precedence over configuration in ini
files
Ex: dbm_config_basedir
Bundles and the versions are managed in git repos (tested with github). Git tags
are leveraged to maintain versions. Github's release
creation process can be used for this, as this includes creation of git tag
.
package.json and package-lock.json are expected under the bundle. Installation takes care of running npm ci
to install the bundle.
- git
- npm
There are two supported schemes.
- Bundles can be managed as separate branches.
- The git
tag
used for managing bundle versions must contain the bundle name (branch name) and the bundle version. ex:mybundle/v1.2.3
. The parse logic is flexible and supports any scheme which makes sure that unique bundle name and bundle version are included in the gittag
.
Supported options:
repo
: ssh or http url of the git repository ex: [email protected]:user_or_org/bundles.git or https://github.com/user_or_org/bundles.gittag
: It can be used to specify the branch to use for installing files. If taglatest
is passed in, it will result in installing files from the branch matching the bundle name.
If no version is inputted, the highest version based on semver rules is installed.
- Each bundle has its own git repo.
- The git
tag
used for managing bundle versions should have the exact version string. ex:v1.2.3
or1.2.3
or1.2.3-beta.1
.
Supported options:
repo
: ssh or http url of the organization / user. Bundle repos are expected under this. ex: [email protected]:user_or_org or https://github.com/user_or_orgtag
: It can be used to specify the branch to use for installing files. If taglatest
is passed in, it will result in installing files from either oflatest
,master
,main
branches.
If no version is inputted, the highest version based on semver rules is installed.
FOR TESTING ONLY
The bundle versions published are saved in a local directory. Path of the local directory is accepted as parameter repo.
The directory structure is
- <bundle-name>
- metadata.json
- <bundle-version-1>
- metadata.json
- bundle.tgz
- <bundle-version-2>
- metadata.json
- bundle.tgz
...
- tar
- npm
View the available bundle versions
Installs the bundle version
Remove the installed bundle versions
List the installed bundle versions
Use the installed bundle version
Set installed bundle version as default version for the bundle
Create your own CLI with custom store (Store
) implementation, and invoke processCommand with process.args
.
Ex:
const { registerStore, processCommand } = require('dbm');
registerStore('customstore', require.resolve('path/to/customstore'), true);
processCommand(<cli-name>, <cli-version>, process.args);