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

feat: implement independent package versioning #755

Open
wants to merge 10 commits into
base: master
Choose a base branch
from
Open
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
43 changes: 35 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ yarn add @lit-protocol/lit-node-client

# Packages

📝 If you're looking to use the Lit SDK, you're probably all set with just the lit-node-client <link>. <br/>Get started with interacting with Lit network!
📝 If you're looking to use the Lit SDK, you're probably all set with just the lit-node-client. <br/>Get started with interacting with Lit network!

<!-- autogen:package:start -->

Expand All @@ -55,7 +55,7 @@ yarn add @lit-protocol/lit-node-client
| [@lit-protocol/lit-node-client-nodejs](https://github.com/LIT-Protocol/js-sdk/tree/master/packages/lit-node-client-nodejs) | ![lit-node-client-nodejs](https://img.shields.io/badge/-nodejs-2E8B57 'lit-node-client-nodejs') | <a target="_blank" href="https://www.npmjs.com/package/@lit-protocol/lit-node-client-nodejs"><img src="https://img.shields.io/npm/v/@lit-protocol/lit-node-client-nodejs"/></a> |
| [@lit-protocol/lit-node-client](https://github.com/LIT-Protocol/js-sdk/tree/master/packages/lit-node-client) | ![lit-node-client](https://img.shields.io/badge/-universal-8A6496 'lit-node-client') | <a target="_blank" href="https://www.npmjs.com/package/@lit-protocol/lit-node-client"><img src="https://img.shields.io/npm/v/@lit-protocol/lit-node-client"/></a> |

If you're a tech-savvy user and wish to utilize only specific submodules that our main module relies upon, you can find individual packages listed below. This way, you can import only the necessary packages that cater to your specific use case::
If you're a tech-savvy user and wish to utilize only specific submodules that our main module relies upon, you can find individual packages listed below. This way, you can import only the necessary packages that cater to your specific use case:

| Package | Category | Download |
| -------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
Expand Down Expand Up @@ -214,23 +214,50 @@ Having done this setup, this is what the development cycle looks like moving for

If changes are made to `packages/wasm` see [here](./packages/wasm/README.md) for info on building from source.

## Package Versioning

Starting from version 7.x.x, this monorepo uses independent versioning for packages. This means:

- Each package maintains its own version number
- Only modified packages have their versions incremented during releases
- Internal package dependencies use semver ranges (e.g., ^1.2.3) instead of exact versions
- Package versions are no longer synchronized across all packages

This change improves developer experience by:

- Avoiding unnecessary version bumps for unchanged packages
- Making package version history more meaningful
- Allowing packages to evolve at their own pace
- Providing clearer dependency relationships between packages

### Breaking Changes

If you're upgrading from a previous version, note that this is a breaking change in how versions are managed. You may need to update your dependency management practices to:

- Use appropriate semver ranges for internal dependencies
- Track individual package versions instead of a single SDK version
- Handle potentially different versions of related packages

## Publishing

You must have at least nodejs v18 to do this.

1. Install the latest packages with `yarn install`

2. Run `yarn bump` to bump the version
2. Build all the packages with `yarn build`

3. Build all the packages with `yarn build`
3. Run the unit tests with `yarn test:unit` & e2e node tests `yarn test:local` locally &
ensure that they pass

4. Run the unit tests with `yarn test:unit` & e2e node tests `yarn test:local` locally & ensure that they pass
4. Update the docs with `yarn gen:docs --push`

5. Update the docs with `yarn gen:docs --push`
5. Publish packages with `yarn publish:packages`. This will:

6. Finally, publish with `yarn publish:packages`
- Prompt for version increments for modified packages
- Update internal dependencies automatically
- Publish only the packages that have changed

7. Commit these changes "Published version X.X.X"
6. Commit these changes with a message describing the published updates

## Testing

Expand Down
2 changes: 1 addition & 1 deletion lerna.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@
"$schema": "node_modules/lerna/schemas/lerna-schema.json",
"useNx": true,
"useWorkspaces": true,
"version": "7.0.4"
"version": "independent"
}
27 changes: 27 additions & 0 deletions local-tests/tests/version-check.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import fs from 'fs';
import path from 'path';

describe('Version Check', () => {
it('checks @lit-protocol/uint8arrays version is 8.0.0', () => {
const distPkgPath = path.join(
__dirname,
'../../dist/packages/uint8arrays/package.json'
);
const distPkg = JSON.parse(fs.readFileSync(distPkgPath, 'utf8'));
expect(distPkg.version).toBe('8.0.0');
});

it('verifies other packages maintain their versions', () => {
const uint8arraysPath = path.join(
__dirname,
'../../dist/packages/uint8arrays/package.json'
);
const uint8arraysPkg = JSON.parse(fs.readFileSync(uint8arraysPath, 'utf8'));

// Check that dependencies maintain their original versions
expect(uint8arraysPkg.dependencies['@lit-protocol/constants']).toBe(
'7.0.4'
);
expect(uint8arraysPkg.dependencies['@lit-protocol/types']).toBe('7.0.4');
});
});
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
"reset": "rm -rf ./dist/packages && yarn reset:dev",
"build": "yarn build:packages",
"build:dev": "yarn tools --remove-local-dev && rm -rf ./dist && yarn tools check --no-empty-directories=true && yarn tools fixTsConfig && yarn tools --match-versions && yarn nx run-many --target=build && yarn tools --setup-local-dev && yarn build:verify",
"build:packages": "yarn tools --remove-local-dev && rm -rf ./dist && yarn tools check --no-empty-directories=true && yarn tools fixTsConfig && yarn tools --match-versions && yarn nx run-many --target=build && yarn tools --setup-local-dev && yarn gen:readme && yarn build:verify && yarn nx format:write --all",
"build:packages": "yarn tools --remove-local-dev && rm -rf ./dist && yarn tools check --no-empty-directories=true && yarn tools fixTsConfig && yarn nx run-many --target=build && yarn tools --setup-local-dev && yarn gen:readme && yarn build:verify && yarn nx format:write --all",
"build:target": "yarn node tools/scripts/build.mjs",
"build:setupLocalDev": "yarn tools --setup-local-dev",
"build:verify": "yarn tools --verify",
Expand All @@ -20,7 +20,7 @@
"test:unit": "nx run-many --target=test",
"test:unit:watch": "nx run-many --target=test --watch",
"test:unit:bun": "bun ./tools/scripts/unit-test-with-bun.mjs",
"publish:packages": "yarn node ./tools/scripts/pub.mjs --prod",
"publish:packages": "yarn node ./tools/scripts/pub.mjs --independent",
"publish:beta": "yarn node ./tools/scripts/pub.mjs --tag beta",
"publish:staging": "yarn node ./tools/scripts/pub.mjs --tag staging",
"gen:docs": "node ./tools/scripts/gen-doc.mjs",
Expand Down
4 changes: 1 addition & 3 deletions packages/uint8arrays/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,5 @@
"tags": [
"universal"
],
"version": "7.0.4",
"main": "./dist/src/index.js",
"typings": "./dist/src/index.d.ts"
"version": "8.0.0"
}
2 changes: 0 additions & 2 deletions tools/scripts/build.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,6 @@ const build = async (name) => {
await runCommand(`rm ${packageDistPath}`);
}

greenLog(`Matching packages/${name}/project.json versions to lerna.json...`);

greenLog('Building Tsc...');
await runCommand(`yarn nx run ${name}:_buildTsc`);

Expand Down
47 changes: 6 additions & 41 deletions tools/scripts/pub.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -45,24 +45,16 @@ if (OPTION) {
}
}

// read lerna.json version
const lerna = await readJsonFile('lerna.json');
const lernaVersion = lerna.version;

let dirs = await listDirsRecursive('dist/packages', false);

console.log('Ready to publish the following packages:');

let publishVersion = null;

await asyncForEach(dirs, async (dir) => {
// read the package.json file
const pkg = await readJsonFile(`${dir}/package.json`);

greenLog(`${pkg.name} => ${pkg.version}`);

publishVersion = pkg.version;

// remove peer dependencies
delete pkg.peerDependencies;

Expand Down Expand Up @@ -107,41 +99,14 @@ await question('Are you sure you want to publish to? (y/n)', {
// read the package.json file
const pkg = await readJsonFile(`${dir}/package.json`);

// also read the individual package.json and update the version
try {
const pkg2 = await readJsonFile(
`${dir.replace('dist/', '')}/package.json`
);
// Keep the original package version for publishing
const originalVersion = pkg.version;

if (OPTION === '--tag' && (VALUE === 'dev' || VALUE === 'test')) {
pkg2.version = publishVersion;
} else {
pkg2.version = lernaVersion;
}

// write the package.json file
await writeJsonFile(`${dir.replace('dist/', '')}/package.json`, pkg2);
} catch (e) {
const path = `${dir.replace('dist/', '')}/package.json`;

// swallow error if it's not a vanilla package
if (!path.includes('vanilla')) {
yellowLog(`No such file or directory: ${path}`);
}
}

// update version
if (OPTION === '--tag' && (VALUE === 'dev' || VALUE === 'test')) {
pkg.version = publishVersion;
} else {
pkg.version = lernaVersion;
}

// write the package.json file
// write the package.json file back
await writeJsonFile(`${dir}/package.json`, pkg);

if (OPTION === '--tag') {
greenLog(`Publishing ${dir} with tag ${VALUE}`);
greenLog(`Publishing ${dir} with version ${originalVersion} and tag ${VALUE}`);

spawnCommand(
'npm',
Expand All @@ -153,13 +118,14 @@ await question('Are you sure you want to publish to? (y/n)', {
logExit: false,
exitCallback: () => {
counter++;
// console.log(`${dir} published with tag ${VALUE}`)
},
}
);
}

if (OPTION === '--prod') {
greenLog(`Publishing ${dir} with version ${originalVersion}`);

spawnCommand(
'npm',
['publish', '--access', 'public'],
Expand All @@ -170,7 +136,6 @@ await question('Are you sure you want to publish to? (y/n)', {
logExit: false,
exitCallback: () => {
counter++;
// console.log(`${dir} published with tag ${VALUE}`)
},
}
);
Expand Down
63 changes: 15 additions & 48 deletions tools/scripts/tools.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -635,22 +635,8 @@ async function setupLocalDevFunc() {
}

async function matchVersionsFunc() {
// async foreach packages
const packageList = await listDirsRecursive('./packages', false);

// get lerna version
const lernaJson = await readJsonFile(`lerna.json`);

await asyncForEach(packageList, async (pkg) => {
const packageJson = await readJsonFile(`${pkg}/package.json`);
packageJson.version = lernaJson.version;

greenLog(
`Updating ${pkg}/package.json version ${packageJson.version} => ${lernaJson.version}...`
);
await writeJsonFile(`${pkg}/package.json`, packageJson);
});

// This function has been deprecated as we now use independent versioning
greenLog('matchVersionsFunc is deprecated - packages now use independent versioning');
exit();
}

Expand All @@ -664,60 +650,41 @@ async function validateDependencyVersions() {
}
);

const packageTotal = packageList.length;
let packagePasses = 0;
let hasErrors = false;

await asyncForEach(packageList, async (pkg, i) => {
await asyncForEach(packageList, async (pkg) => {
const packageJson = await readJsonFile(pkg);
const pkgVersion = packageJson.version;

const dependencies = packageJson?.dependencies ?? {};

let total = 0;
let passes = 0;
let fails = 0;

// search for dependencies that start with @lit-protocol
// Validate semver ranges for internal dependencies
for (const [key, value] of Object.entries(dependencies)) {
if (key.includes(PREFIX) && !ignoreList.includes(key)) {
total++;
if (value !== pkgVersion) {
fails++;
if (!value.match(/^\^|~|>=|>|<=|<|\d+\.\d+\.\d+$/)) {
redLog(`❌ ${pkg} has invalid semver range for ${key}: ${value}`);
hasErrors = true;
} else {
passes++;
greenLog(`✅ ${pkg} has valid semver range for ${key}: ${value}`);
}
}
}

if (fails > 0) {
redLog(
`❌ ${pkg} has ${fails} dependencies with versions that do not match.`
);
} else {
greenLog(
`✅ ${i + 1} ${pkg} contains all dependencies with matching versions.`
);
packagePasses++;
}
});

// log that to make sure the builds works, make sure we have tested it
if (packagePasses >= packageTotal) {
greenLog(
`
if (!hasErrors) {
greenLog(`
❗️ Before publishing, make sure you have tested the build!
- yarn test:unit | run unit tests
- yarn test:local | run e2e tests on nodejs
`,
true
);
`, true);

console.log(`
Note: for e2e nodejs test, you can use the following options:
-------------------------------------------------------------
--filter flag to filter tests (eg. yarn test:local --filter=Encryption)
`);
} else {
process.exit(1);
}

process.exit(0);
}

Expand Down
Loading