Skip to content

Commit

Permalink
fix(version): keep operator in workspace: protocol, fixes #198
Browse files Browse the repository at this point in the history
- fixes #198, operator inside a semver should be kept, ie: `workspace:>=1.2.0`
- also add a lot more helpful info in `git-dry-run` mode about the tarball temp location (to potentially inspect its content) and also display all dependencies/devDependencies for easier debugging of what is about to be published (we'll display them only in dry-run mode)
  • Loading branch information
ghiscoding committed Jun 6, 2022
1 parent bffcb33 commit 1794ccd
Show file tree
Hide file tree
Showing 16 changed files with 192 additions and 64 deletions.
6 changes: 4 additions & 2 deletions .vscode/launch.json
Expand Up @@ -34,7 +34,8 @@
],
"args": [
"packages/cli/src/cli.ts",
"version"
"version",
"--git-dry-run"
],
"cwd": "${workspaceRoot}",
"console": "integratedTerminal",
Expand All @@ -57,7 +58,8 @@
"args": [
"packages/cli/src/cli.ts",
"publish",
"from-package"
"from-package",
"--git-dry-run"
],
"cwd": "${workspaceRoot}",
"console": "integratedTerminal",
Expand Down
3 changes: 2 additions & 1 deletion package.json
Expand Up @@ -11,8 +11,9 @@
"dist-info-cmd": "node ./packages/cli/dist/cli.js info",
"dist-init-cmd": "node ./packages/cli/dist/cli.js init --independent --exact --use-workspaces",
"dist-roll-version": "node ./packages/cli/dist/cli.js version",
"dist-roll-version-dry-run": "node ./packages/cli/dist/cli.js version --git-dry-run",
"dist-roll-publish": "node ./packages/cli/dist/cli.js publish from-package",
"dist-roll-version-dry-run": "node ./packages/cli/dist/cli.js version --git-dry-run",
"dist-roll-publish-dry-run": "node ./packages/cli/dist/cli.js publish from-package --git-dry-run",
"dist-exec-win": "node ./packages/cli/dist/cli.js exec --scope {@lerna-lite/cli,@lerna-lite/core} -- echo hello from package: %LERNA_PACKAGE_NAME%",
"dist-exec-unix": "node ./packages/cli/dist/cli.js exec -- echo hello from package: ${LERNA_PACKAGE_NAME}",
"dist-pack-tarball": "node ./packages/cli/dist/cli.js run pack-tarball",
Expand Down
62 changes: 55 additions & 7 deletions packages/core/src/__tests__/package.spec.ts
Expand Up @@ -322,8 +322,8 @@ describe('Package', () => {

const resolved: NpaResolveResult = npa.resolve("a", "^1.0.0", ".");
resolved.explicitWorkspace = true;
resolved.type = undefined;
resolved.registry = undefined;
resolved.type = undefined as any;
resolved.registry = undefined as any;
resolved.gitCommittish = '1.2.3';
resolved.hosted = { committish: '', domain: 'localhost', noGitPlus: false, noCommittish: false, saveSpec: true } as any;

Expand All @@ -344,8 +344,8 @@ describe('Package', () => {

const resolved: NpaResolveResult = npa.resolve("a", "^1.0.0", ".");
resolved.explicitWorkspace = true;
resolved.type = undefined;
resolved.registry = undefined;
resolved.type = undefined as any;
resolved.registry = undefined as any;
resolved.gitRange = '1.2.3';
resolved.hosted = { committish: '', domain: 'localhost', noGitPlus: false, noCommittish: false, saveSpec: true } as any;

Expand All @@ -360,7 +360,7 @@ describe('Package', () => {
const pkg = factory({
dependencies: {
a: "workspace:^1.0.0",
b: "workspace:^1.0.0",
b: "workspace:>=1.0.0",
c: "workspace:./foo",
d: "file:./foo",
e: "^1.0.0",
Expand All @@ -375,8 +375,8 @@ describe('Package', () => {
expect(pkg.toJSON()).toMatchInlineSnapshot(`
Object {
"dependencies": Object {
"a": "workspace:^2.0.0",
"b": "workspace:^1.0.0",
"a": "workspace:2.0.0",
"b": "workspace:>=1.0.0",
"c": "workspace:./foo",
"d": "file:./foo",
"e": "^1.0.0",
Expand Down Expand Up @@ -456,6 +456,54 @@ describe('Package', () => {
}
`);
});

it("works with workspace fixed version input target `workspace:X.Y.Z` and will keep same output target", () => {
const pkg = factory({
dependencies: {
a: "workspace:1.0.0",
b: "workspace:^1.0.0",
},
});

const resolved: NpaResolveResult = npa.resolve("a", "^1.0.0", ".");
resolved.explicitWorkspace = true;
resolved.workspaceTarget = 'workspace:1.0.0';

pkg.updateLocalDependency(resolved, "2.0.0", "^");

expect(pkg.toJSON()).toMatchInlineSnapshot(`
Object {
"dependencies": Object {
"a": "workspace:2.0.0",
"b": "workspace:^1.0.0",
},
}
`);
});

it("works with operator symbols like >= and workspace input target `workspace:>=X.Y.Z` and will be bumped", () => {
const pkg = factory({
dependencies: {
a: "workspace:>=1.2.0",
b: "workspace:^1.0.0",
},
});

const resolved: NpaResolveResult = npa.resolve("a", "^1.0.0", ".");
resolved.explicitWorkspace = true;
resolved.workspaceTarget = 'workspace:>=1.2.0';

pkg.updateLocalDependency(resolved, "2.0.0", "^");

expect(pkg.toJSON()).toMatchInlineSnapshot(`
Object {
"dependencies": Object {
"a": "workspace:>=2.0.0",
"b": "workspace:^1.0.0",
},
}
`);
});
});

describe('Publish with `workspace:` protocol', () => {
Expand Down
5 changes: 3 additions & 2 deletions packages/core/src/models/interfaces.ts
Expand Up @@ -37,7 +37,8 @@ export interface DescribeRefOptions {
/* When annotated release tags are missing */
export interface DescribeRefFallbackResult {
isDirty: boolean;
refCount: string;
lastTagName?: string;
refCount: number | string;
sha: string;
}

Expand All @@ -46,7 +47,7 @@ export interface DescribeRefDetailedResult {
lastTagName: string;
lastVersion: string;
isDirty: boolean;
refCount: string;
refCount: number | string;
sha: string;
}

Expand Down
Expand Up @@ -137,6 +137,8 @@ describe("PackageGraph", () => {
dependencies: {
"pkg-1": "workspace:^",
"pkg-2": "workspace:~",
"pkg-3": "workspace:^1.0.0",
"pkg-4": "workspace:>=1.0.0",
},
},
"/test/pkg-5"
Expand All @@ -156,6 +158,8 @@ describe("PackageGraph", () => {
expect(pkg5.localDependencies.has('pkg-2')).toBe(true);
expect(pkg5.localDependencies.get('pkg-1').workspaceTarget).toBe('workspace:^');
expect(pkg5.localDependencies.get('pkg-2').workspaceTarget).toBe('workspace:~');
expect(pkg5.localDependencies.get('pkg-3').workspaceTarget).toBe('workspace:^1.0.0');
expect(pkg5.localDependencies.get('pkg-4').workspaceTarget).toBe('workspace:>=1.0.0');
});
});

Expand Down
13 changes: 11 additions & 2 deletions packages/core/src/package.ts
Expand Up @@ -252,8 +252,8 @@ export class Package {
* @param {Object} resolved npa metadata
* @param {String} depVersion semver
* @param {String} savePrefix npm_config_save_prefix
* @param {Boolean} workspaceStrictMatch - are we using strict match with `workspace:` protocol
* @param {String} updatedByCommand - which command called this update?
* @param {Boolean} [workspaceStrictMatch] - are we using `workspace:` protocol strict match?
* @param {String} [updatedByCommand] - which command called this update?
*/
updateLocalDependency(resolved: NpaResolveResult, depVersion: string, savePrefix: string, workspaceStrictMatch = true, updatedByCommand?: CommandType) {
const depName = resolved.name as string;
Expand All @@ -278,6 +278,15 @@ export class Package {
// when using explicit `workspace:` protocol
if (resolved.explicitWorkspace) {
const workspaceTarget = resolved?.workspaceTarget ?? '';
const [_, _wsTxt, operatorPrefix, rangePrefix] = workspaceTarget.match(/^(workspace:)?([\<\>\=]{0,2})?([*|^|~])?(.*)$/) as RegExpMatchArray;

if (operatorPrefix) {
// with workspace it might include an operator, if so use it like "workspace:>=1.2.3"
depCollection[depName] = `${operatorPrefix}${depVersion}`;
} else if (workspaceStrictMatch) {
// with workspace in strict mode we might have empty range prefix like "workspace:1.2.3"
depCollection[depName] = `${rangePrefix || ''}${depVersion}`;
}

if (updatedByCommand === 'publish') {
// when publishing, workspace protocol will be transformed to semver range
Expand Down
12 changes: 6 additions & 6 deletions packages/core/src/utils/check-working-tree.ts
@@ -1,16 +1,16 @@
import { collectUncommitted } from './collect-uncommitted';
import { collectUncommitted, UncommittedConfig } from './collect-uncommitted';
import { describeRef } from './describe-ref';
import { ValidationError } from '../validation-error';

export function checkWorkingTree({ cwd } = {} as any, gitDryRun = false) {
export function checkWorkingTree({ cwd } = {} as UncommittedConfig, gitDryRun = false) {
let chain: Promise<any> = Promise.resolve();

chain = chain.then(() => describeRef({ cwd }, undefined, gitDryRun));

// wrap each test separately to allow all applicable errors to be reported
const tests = [
// prevent duplicate versioning
chain.then(throwIfReleased as any),
chain.then(throwIfReleased),
// prevent publish of uncommitted changes
chain.then(mkThrowIfUncommitted({ cwd }, gitDryRun) as any),
];
Expand All @@ -19,7 +19,7 @@ export function checkWorkingTree({ cwd } = {} as any, gitDryRun = false) {
return chain.then((result) => Promise.all(tests).then(() => result));
}

export function throwIfReleased({ refCount }) {
export function throwIfReleased({ refCount }: { refCount: number | string }) {
if (refCount === '0') {
throw new ValidationError(
'ERELEASED',
Expand All @@ -31,10 +31,10 @@ export function throwIfReleased({ refCount }) {
const EUNCOMMIT_MSG =
'Working tree has uncommitted changes, please commit or remove the following changes before continuing:\n';

export function mkThrowIfUncommitted(options: any = {}, gitDryRun = false) {
export function mkThrowIfUncommitted(options: Partial<UncommittedConfig> = {}, gitDryRun = false) {
return function ({ isDirty }) {
if (isDirty) {
return collectUncommitted(options, gitDryRun).then((uncommitted) => {
return collectUncommitted(options as UncommittedConfig, gitDryRun).then((uncommitted) => {
throw new ValidationError('EUNCOMMIT', `${EUNCOMMIT_MSG}${uncommitted.join('\n')}`);
});
}
Expand Down
11 changes: 2 additions & 9 deletions packages/core/src/utils/collect-uncommitted.ts
Expand Up @@ -3,26 +3,19 @@ import npmlog from 'npmlog';

import { exec, execSync } from '../child-process';

interface UncommittedConfig {
export interface UncommittedConfig {
cwd: string;
log?: typeof npmlog;
}

const maybeColorize = (colorize: (color?: string) => string) => (s?: string) => (s !== ' ' ? colorize(s) : s);
const cRed = maybeColorize(chalk.red);
const cGreen = maybeColorize(chalk.green);

const colorizeStats = (stats: string) => stats.replace(/^([^U]| )([A-Z]| )/gm, replaceStatus).replace(/^\?{2}|U{2}/gm, cRed('$&'));
const replaceStatus = (_, maybeGreen?: string, maybeRed?: string) => `${cGreen(maybeGreen)}${cRed(maybeRed)}`;

const colorizeStats = (stats: string) =>
stats.replace(/^([^U]| )([A-Z]| )/gm, replaceStatus).replace(/^\?{2}|U{2}/gm, cRed('$&'));

const splitOnNewLine = (str: string) => str.split('\n');

const filterEmpty = (lines: string[]) => lines.filter((line) => line.length);

const o = (l: any, r: any) => (x) => l(r(x));

const transformOutput = o(filterEmpty, o(splitOnNewLine, colorizeStats));

/**
Expand Down
8 changes: 4 additions & 4 deletions packages/core/src/utils/describe-ref.ts
@@ -1,7 +1,7 @@

import log from 'npmlog';

import { DescribeRefOptions } from '../models';
import { DescribeRefDetailedResult, DescribeRefFallbackResult, DescribeRefOptions } from '../models';
import { exec, execSync } from '../child-process';

/**
Expand Down Expand Up @@ -39,7 +39,7 @@ function getArgs(options: DescribeRefOptions, includeMergedTags?: boolean) {
* @param {boolean} [includeMergedTags]
* @returns {Promise<DescribeRefFallbackResult|DescribeRefDetailedResult>}
*/
function describeRef(options: DescribeRefOptions = {}, includeMergedTags?: boolean, gitDryRun = false) {
function describeRef(options: DescribeRefOptions = {}, includeMergedTags?: boolean, gitDryRun = false): Promise<DescribeRefFallbackResult | DescribeRefDetailedResult> {
const promise = exec('git', getArgs(options, includeMergedTags), options, gitDryRun);

return promise.then(({ stdout } = { stdout: '' }) => {
Expand All @@ -56,7 +56,7 @@ function describeRef(options: DescribeRefOptions = {}, includeMergedTags?: boole
* @param {DescribeRefOptions} [options]
* @param {boolean} [includeMergedTags]
*/
function describeRefSync(options: any = {}, includeMergedTags, gitDryRun = false) {
function describeRefSync(options: DescribeRefOptions = {}, includeMergedTags?: boolean, gitDryRun = false) {
const stdout = execSync('git', getArgs(options, includeMergedTags), options, gitDryRun);
const result = parse(stdout, options.cwd);

Expand All @@ -72,7 +72,7 @@ function describeRefSync(options: any = {}, includeMergedTags, gitDryRun = false
* @param {string} [cwd] Defaults to `process.cwd()`
* @returns {DescribeRefFallbackResult|DescribeRefDetailedResult}
*/
function parse(stdout, cwd) {
function parse(stdout: string, cwd?: string): DescribeRefFallbackResult | DescribeRefDetailedResult {
const minimalShaRegex = /^([0-9a-f]{7,40})(-dirty)?$/;
// when git describe fails to locate tags, it returns only the minimal sha
if (minimalShaRegex.test(stdout)) {
Expand Down
Expand Up @@ -2,6 +2,7 @@
"name": "package-6",
"version": "1.0.0",
"dependencies": {
"package-1": "workspace:>=1.0.0",
"tiny-tarball": "^1.0.0"
}
}
58 changes: 58 additions & 0 deletions packages/publish/src/__tests__/log-packed.spec.ts
@@ -0,0 +1,58 @@
import npmlog from 'npmlog';

import { logPacked } from '../lib';

describe('log-packed', () => {
const pkg = {
name: '@lerna-lite/core',
version: '1.3.0',
dependencies: {
'tiny-tarball': '^1.0.0',
},
devDependencies: {
'eslint': '^8.16.0',
},
location: '',
manifestLocation: '',
packed: {
id: '@lerna-lite/core@1.4.1',
name: '@lerna-lite/core',
filename: 'lerna-lite.tar.tgz',
files: [{ 'package-1': 'location1' }],
version: '1.4.1',
size: 84219,
shasum: 'ABC123',
integrity: 'ABC123',
unpackedSize: 340009,
entryCount: 14,
bundled: [{
name: 'test'
}]
},
}

it('should display dry-run details', () => {
const logSpy = jest.spyOn(npmlog, 'notice');

logPacked(pkg, true);

expect(logSpy).toHaveBeenCalledWith('=== Bundled Dependencies ===', '');
expect(logSpy).toHaveBeenCalledWith('=== Tarball Details ===', '');
expect(logSpy).toHaveBeenCalledWith('', expect.stringContaining('size: '));
expect(logSpy).toHaveBeenCalledWith('', expect.stringContaining('package size: '));
expect(logSpy).toHaveBeenCalledWith('', expect.stringContaining('unpacked size: '));
expect(logSpy).toHaveBeenCalledWith('', expect.stringContaining('shasum: '));
expect(logSpy).toHaveBeenCalledWith('', expect.stringContaining('integrity: '));
expect(logSpy).toHaveBeenCalledWith('', expect.stringContaining('bundled deps: '));
expect(logSpy).toHaveBeenCalledWith('', expect.stringContaining('bundled files: '));
expect(logSpy).toHaveBeenCalledWith('', expect.stringContaining('own files: '));
expect(logSpy).toHaveBeenCalledWith('', expect.stringContaining('total files: '));
expect(logSpy).toHaveBeenCalledWith('', '--- dry-run details ---');
expect(logSpy).toHaveBeenCalledWith('', 'package name: @lerna-lite/core');
expect(logSpy).toHaveBeenCalledWith('dependencies:', '');
expect(logSpy).toHaveBeenCalledWith('', 'tiny-tarball | ^1.0.0');
expect(logSpy).toHaveBeenCalledWith('devDependencies:', '');
expect(logSpy).toHaveBeenCalledWith('', 'eslint | ^8.16.0');
expect(logSpy).toHaveBeenCalledWith('', '');
});
});

0 comments on commit 1794ccd

Please sign in to comment.