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

fix: include unchanged dependents in release validation #173

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
230 changes: 226 additions & 4 deletions src/release-specification.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -602,7 +602,7 @@ Your release spec could not be processed due to the following issues:
});
});

it('throws if there are any packages in the release with a major version bump using the word "major", but any of their dependents defined as "peerDependencies" are not listed in the release', async () => {
it('throws if there are any packages in the release with a major version bump using the word "major", but any of their dependents defined as "peerDependencies" have changes since their latest release and are not listed in the release', async () => {
await withSandbox(async (sandbox) => {
const project = buildMockProject({
workspacePackages: {
Expand Down Expand Up @@ -657,7 +657,62 @@ ${releaseSpecificationPath}
});
});

it('throws if there are any packages in the release with a major version bump using a literal version, but any of their dependents defined as "peerDependencies" are not listed in the release', async () => {
it('throws if there are any packages in the release with a major version bump using the word "major", but any of their dependents defined as "peerDependencies" are not listed in the release, even if they have no changes', async () => {
await withSandbox(async (sandbox) => {
const project = buildMockProject({
workspacePackages: {
a: buildMockPackage('a', {
hasChangesSinceLatestRelease: true,
}),
b: buildMockPackage('b', {
hasChangesSinceLatestRelease: false,
validatedManifest: {
peerDependencies: {
a: '1.0.0',
},
},
}),
},
});
const releaseSpecificationPath = path.join(
sandbox.directoryPath,
'release-spec',
);
await fs.promises.writeFile(
releaseSpecificationPath,
YAML.stringify({
packages: {
a: 'major',
},
}),
);

await expect(
validateReleaseSpecification(project, releaseSpecificationPath),
).rejects.toThrow(
`
Your release spec could not be processed due to the following issues:

* The following dependents of package 'a', which is being released with a major version bump, are missing from the release spec.

- b

Consider including them in the release spec so that they are compatible with the new 'a' version.

If you are ABSOLUTELY SURE these packages are safe to omit, however, and want to postpone the release of a package, then list it with a directive of "intentionally-skip". For example:

packages:
b: intentionally-skip

The release spec file has been retained for you to edit again and make the necessary fixes. Once you've done this, re-run this tool.

${releaseSpecificationPath}
`.trim(),
);
});
});

it('throws if there are any packages in the release with a major version bump using a literal version, but any of their dependents defined as "peerDependencies" have changes since their latest release and are not listed in the release', async () => {
await withSandbox(async (sandbox) => {
const project = buildMockProject({
workspacePackages: {
Expand Down Expand Up @@ -712,7 +767,62 @@ ${releaseSpecificationPath}
});
});

it('throws if there are any packages in the release with a major version bump using the word "major", but their dependents via "peerDependencies" have their version specified as null in the release spec', async () => {
it('throws if there are any packages in the release with a major version bump using a literal version, but any of their dependents defined as "peerDependencies" are not listed in the release, even if they have no changes', async () => {
await withSandbox(async (sandbox) => {
const project = buildMockProject({
workspacePackages: {
a: buildMockPackage('a', '2.1.4', {
hasChangesSinceLatestRelease: true,
}),
b: buildMockPackage('b', {
hasChangesSinceLatestRelease: true,
validatedManifest: {
peerDependencies: {
a: '2.1.4',
},
},
}),
},
});
const releaseSpecificationPath = path.join(
sandbox.directoryPath,
'release-spec',
);
await fs.promises.writeFile(
releaseSpecificationPath,
YAML.stringify({
packages: {
a: '3.0.0',
},
}),
);

await expect(
validateReleaseSpecification(project, releaseSpecificationPath),
).rejects.toThrow(
`
Your release spec could not be processed due to the following issues:

* The following dependents of package 'a', which is being released with a major version bump, are missing from the release spec.

- b

Consider including them in the release spec so that they are compatible with the new 'a' version.

If you are ABSOLUTELY SURE these packages are safe to omit, however, and want to postpone the release of a package, then list it with a directive of "intentionally-skip". For example:

packages:
b: intentionally-skip

The release spec file has been retained for you to edit again and make the necessary fixes. Once you've done this, re-run this tool.

${releaseSpecificationPath}
`.trim(),
);
});
});

it('throws if there are any packages in the release with a major version bump using the word "major", but their dependents via "peerDependencies" have changes since their latest release and have their version specified as null in the release spec', async () => {
await withSandbox(async (sandbox) => {
const project = buildMockProject({
workspacePackages: {
Expand Down Expand Up @@ -768,7 +878,63 @@ ${releaseSpecificationPath}
});
});

it('throws if there are any packages in the release with a major version bump using a literal version, but their dependents via "peerDependencies" have their version specified as null in the release spec', async () => {
it('throws if there are any packages in the release with a major version bump using the word "major", but their dependents via "peerDependencies" have their version specified as null in the release spec, even if they have no changes', async () => {
await withSandbox(async (sandbox) => {
const project = buildMockProject({
workspacePackages: {
a: buildMockPackage('a', {
hasChangesSinceLatestRelease: true,
}),
b: buildMockPackage('b', {
hasChangesSinceLatestRelease: false,
validatedManifest: {
peerDependencies: {
a: '1.0.0',
},
},
}),
},
});
const releaseSpecificationPath = path.join(
sandbox.directoryPath,
'release-spec',
);
await fs.promises.writeFile(
releaseSpecificationPath,
YAML.stringify({
packages: {
a: 'major',
b: null,
},
}),
);

await expect(
validateReleaseSpecification(project, releaseSpecificationPath),
).rejects.toThrow(
`
Your release spec could not be processed due to the following issues:

* The following dependents of package 'a', which is being released with a major version bump, are missing from the release spec.

- b

Consider including them in the release spec so that they are compatible with the new 'a' version.

If you are ABSOLUTELY SURE these packages are safe to omit, however, and want to postpone the release of a package, then list it with a directive of "intentionally-skip". For example:

packages:
b: intentionally-skip

The release spec file has been retained for you to edit again and make the necessary fixes. Once you've done this, re-run this tool.

${releaseSpecificationPath}
`.trim(),
);
});
});

it('throws if there are any packages in the release with a major version bump using a literal version, but their dependents via "peerDependencies" have changes since their latest release and have their version specified as null in the release spec', async () => {
await withSandbox(async (sandbox) => {
const project = buildMockProject({
workspacePackages: {
Expand Down Expand Up @@ -818,6 +984,62 @@ Your release spec could not be processed due to the following issues:

The release spec file has been retained for you to edit again and make the necessary fixes. Once you've done this, re-run this tool.

${releaseSpecificationPath}
`.trim(),
);
});
});

it('throws if there are any packages in the release with a major version bump using a literal version, but their dependents via "peerDependencies" have their version specified as null in the release spec, even if they have no changes', async () => {
await withSandbox(async (sandbox) => {
const project = buildMockProject({
workspacePackages: {
a: buildMockPackage('a', '2.1.4', {
hasChangesSinceLatestRelease: true,
}),
b: buildMockPackage('b', {
hasChangesSinceLatestRelease: false,
validatedManifest: {
peerDependencies: {
a: '2.1.4',
},
},
}),
},
});
const releaseSpecificationPath = path.join(
sandbox.directoryPath,
'release-spec',
);
await fs.promises.writeFile(
releaseSpecificationPath,
YAML.stringify({
packages: {
a: '3.0.0',
b: null,
},
}),
);

await expect(
validateReleaseSpecification(project, releaseSpecificationPath),
).rejects.toThrow(
`
Your release spec could not be processed due to the following issues:

* The following dependents of package 'a', which is being released with a major version bump, are missing from the release spec.

- b

Consider including them in the release spec so that they are compatible with the new 'a' version.

If you are ABSOLUTELY SURE these packages are safe to omit, however, and want to postpone the release of a package, then list it with a directive of "intentionally-skip". For example:

packages:
b: intentionally-skip

The release spec file has been retained for you to edit again and make the necessary fixes. Once you've done this, re-run this tool.

${releaseSpecificationPath}
`.trim(),
);
Expand Down
34 changes: 23 additions & 11 deletions src/release-specification.ts
Original file line number Diff line number Diff line change
Expand Up @@ -168,17 +168,15 @@ export async function waitForUserToEditReleaseSpecification(
}

/**
* Finds all workspace packages that depend on the given package and have changes since their latest release.
* Finds all workspace packages that depend on the given package.
*
* @param project - The project containing workspace packages.
* @param packageName - The name of the package to find dependents for.
* @param unvalidatedReleaseSpecificationPackages - The packages in the release specification.
* @returns An array of package names that depend on the given package and are missing from the release spec.
* @returns An array of package names that depend on the given package.
*/
export function findMissingUnreleasedDependents(
export function findAllWorkspacePackagesThatDependOnPackage(
project: Project,
packageName: string,
unvalidatedReleaseSpecificationPackages: Record<string, string | null>,
): string[] {
const dependentNames = Object.keys(project.workspacePackages).filter(
(possibleDependentName) => {
Expand All @@ -189,14 +187,28 @@ export function findMissingUnreleasedDependents(
},
);

const changedDependentNames = dependentNames.filter(
(possibleDependentName) => {
return project.workspacePackages[possibleDependentName]
.hasChangesSinceLatestRelease;
},
return dependentNames;
}

/**
* Finds all workspace packages that depend on the given package.
*
* @param project - The project containing workspace packages.
* @param packageName - The name of the package to find dependents for.
* @param unvalidatedReleaseSpecificationPackages - The packages in the release specification.
* @returns An array of package names that depend on the given package and are missing from the release spec.
*/
export function findMissingUnreleasedDependents(
project: Project,
packageName: string,
unvalidatedReleaseSpecificationPackages: Record<string, string | null>,
): string[] {
const dependentNames = findAllWorkspacePackagesThatDependOnPackage(
project,
packageName,
);

return changedDependentNames.filter((dependentName) => {
return dependentNames.filter((dependentName) => {
return !unvalidatedReleaseSpecificationPackages[dependentName];
});
}
Expand Down
21 changes: 18 additions & 3 deletions src/ui.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
} from './project.js';
import { Package } from './package.js';
import {
findAllWorkspacePackagesThatDependOnPackage,
findMissingUnreleasedDependenciesForRelease,
findMissingUnreleasedDependentsForBreakingChanges,
IncrementableVersionParts,
Expand Down Expand Up @@ -130,15 +131,29 @@ function createApp({
app.use(express.static(UI_BUILD_DIR));
app.use(express.json());

app.get('/api/packages', (_req, res) => {
app.get('/api/packages', (req, res) => {
const { majorBumps } = req.query;

const majorBumpsArray =
typeof majorBumps === 'string'
? majorBumps.split(',').filter(Boolean)
: (req.query.majorBumps as string[] | undefined) || [];

const requiredDependents = new Set(
majorBumpsArray.flatMap((majorBump) =>
findAllWorkspacePackagesThatDependOnPackage(project, majorBump),
),
);

const pkgs = Object.values(project.workspacePackages).filter(
(pkg) => pkg.hasChangesSinceLatestRelease,
(pkg) =>
pkg.hasChangesSinceLatestRelease ||
requiredDependents.has(pkg.validatedManifest.name),
);

const packages = pkgs.map((pkg) => ({
name: pkg.validatedManifest.name,
version: pkg.validatedManifest.version.version,
location: pkg.directoryPath,
}));

res.json(packages);
Expand Down
Loading
Loading