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(release): add fallback for currentVersionResolver in the version step #21155

Merged
Merged
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
12 changes: 12 additions & 0 deletions docs/generated/cli/release.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,12 @@ nx release [specifier]

#### Options

##### first-release

Type: `boolean`

Indicates that this is the first release for the selected release group. If the current version cannot be determined as usual, the version on disk will be used as a fallback. This is useful when using git or the registry to determine the current version of packages, since those sources are only available after the first release.

##### help

Type: `boolean`
Expand Down Expand Up @@ -107,6 +113,12 @@ nx release version [specifier]

#### Options

##### first-release

Type: `boolean`

Indicates that this is the first release for the selected release group. If the current version cannot be determined as usual, the version on disk will be used as a fallback. This is useful when using git or the registry to determine the current version of packages, since those sources are only available after the first release.

##### git-commit

Type: `boolean`
Expand Down
12 changes: 12 additions & 0 deletions docs/generated/packages/nx/documents/release.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,12 @@ nx release [specifier]

#### Options

##### first-release

Type: `boolean`

Indicates that this is the first release for the selected release group. If the current version cannot be determined as usual, the version on disk will be used as a fallback. This is useful when using git or the registry to determine the current version of packages, since those sources are only available after the first release.

##### help

Type: `boolean`
Expand Down Expand Up @@ -107,6 +113,12 @@ nx release version [specifier]

#### Options

##### first-release

Type: `boolean`

Indicates that this is the first release for the selected release group. If the current version cannot be determined as usual, the version on disk will be used as a fallback. This is useful when using git or the registry to determine the current version of packages, since those sources are only available after the first release.

##### git-commit

Type: `boolean`
Expand Down
142 changes: 142 additions & 0 deletions e2e/release/src/release.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1054,5 +1054,147 @@ ${JSON.stringify(
new RegExp(`Successfully ran target nx-release-publish for`, 'g')
).length
).toEqual(2);

// change the releaseTagPattern to something that doesn't exist in order to test fallbackCurrentVersionResolver
updateJson<NxJsonConfiguration>('nx.json', (nxJson) => {
nxJson.release = {
groups: {
group1: {
projects: [pkg1, pkg2, pkg3],
releaseTagPattern: '>{version}',
},
},
git: {
commit: false,
tag: false,
},
version: {
generatorOptions: {
currentVersionResolver: 'git-tag',
},
},
};
return nxJson;
});

const releaseOutput4a = runCLI(`release patch --skip-publish`, {
silenceError: true,
});

expect(releaseOutput4a).toMatchInlineSnapshot(`

> NX Running release version for project: {project-name}

{project-name} 🔍 Reading data for package "@proj/{project-name}" from {project-name}/package.json

> NX No git tags matching pattern ">{version}" for project "{project-name}" were found. You will need to create an initial matching tag to use as a base for determining the next version. Alternatively, you can set the "version.generatorOptions.fallbackCurrentVersionResolver" to "disk" in order to fallback to the version on disk when no matching git tags are found.


`);

const releaseOutput4b = runCLI(
`release patch --skip-publish --first-release`,
{
silenceError: true,
}
);

expect(releaseOutput4b).toMatch(
`📄 Unable to resolve the current version from git tag using pattern ">{version}". Falling back to the version on disk of 1400.1.0`
);
expect(
releaseOutput4b.match(
new RegExp(
`📄 Using the current version 1400\\.1\\.0 already resolved from disk fallback\\.`,
'g'
)
).length
).toEqual(2);

updateJson<NxJsonConfiguration>('nx.json', (nxJson) => {
nxJson.release.version.generatorOptions.fallbackCurrentVersionResolver =
'disk';
return nxJson;
});

const releaseOutput5 = runCLI(`release patch --skip-publish`);

expect(releaseOutput5).toMatch(
`📄 Unable to resolve the current version from git tag using pattern ">{version}". Falling back to the version on disk of 1400.1.1`
);
expect(
releaseOutput5.match(
new RegExp(
`📄 Using the current version 1400\\.1\\.1 already resolved from disk fallback\\.`,
'g'
)
).length
).toEqual(2);

updateJson<NxJsonConfiguration>('nx.json', (nxJson) => {
nxJson.release.version.generatorOptions.currentVersionResolver =
'registry';
nxJson.release.version.generatorOptions.currentVersionResolverMetadata = {
tag: 'other',
};
delete nxJson.release.version.generatorOptions
.fallbackCurrentVersionResolver;
return nxJson;
});

const releaseOutput6a = runCLI(`release patch --skip-publish`, {
silenceError: true,
});

expect(releaseOutput6a).toMatchInlineSnapshot(`

> NX Running release version for project: {project-name}

{project-name} 🔍 Reading data for package "@proj/{project-name}" from {project-name}/package.json

> NX Unable to resolve the current version from the registry ${e2eRegistryUrl}. Please ensure that the package exists in the registry in order to use the "registry" currentVersionResolver. Alternatively, you can set the "version.generatorOptions.fallbackCurrentVersionResolver" to "disk" in order to fallback to the version on disk when the registry lookup fails.

- Resolving the current version for tag "other" on ${e2eRegistryUrl}

`);

const releaseOutput6b = runCLI(
`release patch --skip-publish --first-release`,
{
silenceError: true,
}
);

expect(releaseOutput6b).toMatch(
`📄 Unable to resolve the current version from the registry ${e2eRegistryUrl}. Falling back to the version on disk of 1400.1.2`
);
expect(
releaseOutput6b.match(
new RegExp(
`📄 Using the current version 1400\\.1\\.2 already resolved from disk fallback\\.`,
'g'
)
).length
).toEqual(2);

updateJson<NxJsonConfiguration>('nx.json', (nxJson) => {
nxJson.release.version.generatorOptions.fallbackCurrentVersionResolver =
'disk';
return nxJson;
});

const releaseOutput7 = runCLI(`release patch --skip-publish --verbose`);

expect(releaseOutput7).toMatch(
`📄 Unable to resolve the current version from the registry ${e2eRegistryUrl}. Falling back to the version on disk of 1400.1.3`
);
expect(
releaseOutput7.match(
new RegExp(
`📄 Using the current version 1400\\.1\\.3 already resolved from disk fallback\\.`,
'g'
)
).length
).toEqual(2);
}, 500000);
});
104 changes: 73 additions & 31 deletions packages/js/src/generators/release-version/release-version.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,11 @@ export async function releaseVersionGenerator(
options.specifier = options.specifier.replace(/^v/, '');
}

if (options.firstRelease) {
// always use disk as a fallback for the first release
options.fallbackCurrentVersionResolver = 'disk';
}

const projects = options.projects;

const createResolvePackageRoot =
Expand Down Expand Up @@ -75,6 +80,7 @@ export async function releaseVersionGenerator(
}

let currentVersion: string;
let currentVersionResolvedFromFallback: boolean = false;

// only used for options.currentVersionResolver === 'git-tag', but
// must be declared here in order to reuse it for additional projects
Expand Down Expand Up @@ -146,31 +152,53 @@ To fix this you will either need to add a package.json file at that location, or
color.spinnerColor as typeof colors[number]['spinnerColor'];
spinner.start();

// Must be non-blocking async to allow spinner to render
currentVersion = await new Promise<string>((resolve, reject) => {
exec(
`npm view ${packageName} version --registry=${registry} --tag=${tag}`,
(error, stdout, stderr) => {
if (error) {
return reject(error);
try {
// Must be non-blocking async to allow spinner to render
currentVersion = await new Promise<string>((resolve, reject) => {
exec(
`npm view ${packageName} version --registry=${registry} --tag=${tag}`,
(error, stdout, stderr) => {
if (error) {
return reject(error);
}
if (stderr) {
return reject(stderr);
}
return resolve(stdout.trim());
}
if (stderr) {
return reject(stderr);
}
return resolve(stdout.trim());
}
);
});
);
});

spinner.stop();
spinner.stop();

log(
`📄 Resolved the current version as ${currentVersion} for tag "${tag}" from registry ${registry}`
);
log(
`📄 Resolved the current version as ${currentVersion} for tag "${tag}" from registry ${registry}`
);
} catch (e) {
spinner.stop();

if (options.fallbackCurrentVersionResolver === 'disk') {
log(
`📄 Unable to resolve the current version from the registry ${registry}. Falling back to the version on disk of ${currentVersionFromDisk}`
);
currentVersion = currentVersionFromDisk;
currentVersionResolvedFromFallback = true;
} else {
throw new Error(
`Unable to resolve the current version from the registry ${registry}. Please ensure that the package exists in the registry in order to use the "registry" currentVersionResolver. Alternatively, you can set the "version.generatorOptions.fallbackCurrentVersionResolver" to "disk" in order to fallback to the version on disk when the registry lookup fails.`
);
}
}
} else {
log(
`📄 Using the current version ${currentVersion} already resolved from the registry ${registry}`
);
if (currentVersionResolvedFromFallback) {
log(
`📄 Using the current version ${currentVersion} already resolved from disk fallback.`
);
} else {
log(
`📄 Using the current version ${currentVersion} already resolved from the registry ${registry}`
);
}
}
break;
}
Expand All @@ -194,19 +222,33 @@ To fix this you will either need to add a package.json file at that location, or
}
);
if (!latestMatchingGitTag) {
throw new Error(
`No git tags matching pattern "${releaseTagPattern}" for project "${project.name}" were found. You will need to create an initial matching tag to use as a base for determining the next version.`
if (options.fallbackCurrentVersionResolver === 'disk') {
log(
`📄 Unable to resolve the current version from git tag using pattern "${releaseTagPattern}". Falling back to the version on disk of ${currentVersionFromDisk}`
);
currentVersion = currentVersionFromDisk;
currentVersionResolvedFromFallback = true;
} else {
throw new Error(
`No git tags matching pattern "${releaseTagPattern}" for project "${project.name}" were found. You will need to create an initial matching tag to use as a base for determining the next version. Alternatively, you can set the "version.generatorOptions.fallbackCurrentVersionResolver" to "disk" in order to fallback to the version on disk when no matching git tags are found.`
);
}
} else {
currentVersion = latestMatchingGitTag.extractedVersion;
log(
`📄 Resolved the current version as ${currentVersion} from git tag "${latestMatchingGitTag.tag}".`
);
}

currentVersion = latestMatchingGitTag.extractedVersion;
log(
`📄 Resolved the current version as ${currentVersion} from git tag "${latestMatchingGitTag.tag}".`
);
} else {
log(
`📄 Using the current version ${currentVersion} already resolved from git tag "${latestMatchingGitTag.tag}".`
);
if (currentVersionResolvedFromFallback) {
log(
`📄 Using the current version ${currentVersion} already resolved from disk fallback.`
);
} else {
log(
`📄 Using the current version ${currentVersion} already resolved from git tag "${latestMatchingGitTag.tag}".`
);
}
}
break;
}
Expand Down
12 changes: 12 additions & 0 deletions packages/nx/src/command-line/release/command-object.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ export type VersionOptions = NxReleaseArgs &
specifier?: string;
preid?: string;
stageChanges?: boolean;
firstRelease?: boolean;
};

export type ChangelogOptions = NxReleaseArgs &
Expand All @@ -55,6 +56,7 @@ export type PublishOptions = NxReleaseArgs &
export type ReleaseOptions = NxReleaseArgs & {
yes?: boolean;
skipPublish?: boolean;
firstRelease?: boolean;
};

export const yargsReleaseCommand: CommandModule<
Expand Down Expand Up @@ -145,6 +147,11 @@ const releaseCommand: CommandModule<NxReleaseArgs, ReleaseOptions> = {
description:
'Skip publishing by automatically answering no to the confirmation prompt for publishing',
})
.option('first-release', {
type: 'boolean',
description:
'Indicates that this is the first release for the selected release group. If the current version cannot be determined as usual, the version on disk will be used as a fallback. This is useful when using git or the registry to determine the current version of packages, since those sources are only available after the first release.',
})
.check((argv) => {
if (argv.yes !== undefined && argv.skipPublish !== undefined) {
throw new Error(
Expand Down Expand Up @@ -188,6 +195,11 @@ const versionCommand: CommandModule<NxReleaseArgs, VersionOptions> = {
describe:
'Whether or not to stage the changes made by this command. Useful when combining this command with changelog generation.',
})
.option('first-release', {
type: 'boolean',
description:
'Indicates that this is the first release for the selected release group. If the current version cannot be determined as usual, the version on disk will be used as a fallback. This is useful when using git or the registry to determine the current version of packages, since those sources are only available after the first release.',
})
),
handler: (args) =>
import('./version')
Expand Down
3 changes: 3 additions & 0 deletions packages/nx/src/command-line/release/version.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ export interface ReleaseVersionGeneratorSchema {
packageRoot?: string;
currentVersionResolver?: 'registry' | 'disk' | 'git-tag';
currentVersionResolverMetadata?: Record<string, unknown>;
fallbackCurrentVersionResolver?: 'disk';
firstRelease?: boolean;
}

export interface NxReleaseVersionResult {
Expand Down Expand Up @@ -370,6 +372,7 @@ async function runVersionOnProjects(
projects: projectNames.map((p) => projectGraph.nodes[p]),
projectGraph,
releaseGroup,
firstRelease: args.firstRelease ?? false,
};

// Apply generator defaults from schema.json file etc
Expand Down