mirror of
https://github.com/navikt/aksel.git
synced 2026-10-12 16:52:03 +00:00
178 lines
4.7 KiB
TypeScript
178 lines
4.7 KiB
TypeScript
import { error as actionsError } from "@actions/core";
|
|
import { execSync } from "node:child_process";
|
|
import { readFileSync } from "node:fs";
|
|
import { join } from "node:path";
|
|
|
|
/**
|
|
* Packages released in lockstep must share one version, even when they are different dependency names.
|
|
*/
|
|
const PACKAGE_FAMILIES: {
|
|
name: string;
|
|
match: (dependency: string) => boolean;
|
|
}[] = [
|
|
{
|
|
name: "vitest",
|
|
match: (dependency) =>
|
|
dependency === "vitest" ||
|
|
(dependency.startsWith("@vitest/") &&
|
|
dependency !== "@vitest/eslint-plugin"),
|
|
},
|
|
{
|
|
name: "storybook",
|
|
match: (dependency) =>
|
|
dependency === "storybook" || dependency.startsWith("@storybook/"),
|
|
},
|
|
];
|
|
|
|
validateVersions();
|
|
|
|
function validateVersions() {
|
|
const workspaces = getYarnWorkspacesList();
|
|
|
|
if (workspaces.length === 0) {
|
|
exitWithError("No workspaces found");
|
|
}
|
|
|
|
const dependencies = new Map<string, string[]>();
|
|
|
|
for (const { location } of workspaces) {
|
|
const packageJson = JSON.parse(
|
|
readFileSync(join(location, "./package.json"), { encoding: "utf-8" }),
|
|
);
|
|
|
|
for (const localDependency of [
|
|
packageJson.dependencies,
|
|
packageJson.devDependencies,
|
|
]) {
|
|
if (!localDependency) {
|
|
continue;
|
|
}
|
|
|
|
for (const [dependency, version] of Object.entries(localDependency)) {
|
|
if (!dependencies.has(dependency)) {
|
|
dependencies.set(dependency, []);
|
|
}
|
|
dependencies.get(dependency)?.push(version as string);
|
|
}
|
|
}
|
|
}
|
|
|
|
const depsOutOfSync: { dependency: string; filteredVersions: string[] }[] =
|
|
[];
|
|
|
|
const checkVersions = (dependency: string, versions: string[]) => {
|
|
/**
|
|
* While we could resolve these cases using "semver" or other packages,
|
|
* we are going to keep it simple and just check "regular" cases.
|
|
*/
|
|
const filteredVersions = versions.filter(
|
|
(version) => version !== "*" && !version.includes(">="),
|
|
);
|
|
|
|
const versionsAreEqual = filteredVersions.every(
|
|
(version) => version === filteredVersions[0],
|
|
);
|
|
|
|
if (!versionsAreEqual) {
|
|
depsOutOfSync.push({
|
|
dependency,
|
|
/* To keep console simple, we hide duplicates */
|
|
filteredVersions: [...new Set(filteredVersions)],
|
|
});
|
|
}
|
|
};
|
|
|
|
for (const [dependency, versions] of dependencies) {
|
|
checkVersions(dependency, versions);
|
|
}
|
|
|
|
for (const family of PACKAGE_FAMILIES) {
|
|
const members = [...dependencies.keys()].filter(family.match);
|
|
|
|
checkVersions(
|
|
`${family.name} family (${members.join(", ")})`,
|
|
members.flatMap((member) => dependencies.get(member) ?? []),
|
|
);
|
|
}
|
|
|
|
if (depsOutOfSync.length > 0) {
|
|
console.info("");
|
|
|
|
if (process.env.CI) {
|
|
const joinedDependencies = depsOutOfSync
|
|
.map(
|
|
({ dependency, filteredVersions }) =>
|
|
`- ${dependency}: ${filteredVersions.join(", ")}`,
|
|
)
|
|
.join(" \n");
|
|
actionsError(
|
|
`Workspaces local dependency versions not synced across repository: \n${joinedDependencies} \nPlease make sure all workspaces have the same version for each dependency.`,
|
|
{
|
|
title: "Inconsistent dependency versions",
|
|
file: "yarn.lock",
|
|
startLine: 1,
|
|
},
|
|
);
|
|
} else {
|
|
console.error(
|
|
"Workspaces local dependency versions not synced across repository:",
|
|
);
|
|
for (const { dependency, filteredVersions } of depsOutOfSync) {
|
|
logDependency(dependency, filteredVersions);
|
|
}
|
|
console.error(
|
|
"Please make sure all workspaces have the same version for each dependency.\n",
|
|
);
|
|
}
|
|
|
|
process.exit(1);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get the list of workspaces in the repository by using the `yarn workspaces list` command
|
|
*/
|
|
function getYarnWorkspacesList(): { location: string; name: string }[] {
|
|
try {
|
|
const execCommand = execSync("yarn workspaces list --json", {
|
|
encoding: "utf-8",
|
|
});
|
|
|
|
const workspaces = execCommand
|
|
.split("\n")
|
|
.filter(Boolean)
|
|
.map((line) => {
|
|
const { location, name } = JSON.parse(line);
|
|
return { location, name };
|
|
});
|
|
|
|
return workspaces;
|
|
} catch {
|
|
exitWithError("Failed to get workspaces list");
|
|
}
|
|
return [];
|
|
}
|
|
|
|
/**
|
|
* Adds some colors to better highlight the warning
|
|
*/
|
|
function logDependency(dependency: string, filteredVersions: string[]) {
|
|
const colors = {
|
|
reset: "\x1b[0m",
|
|
red: "\x1b[31m",
|
|
yellow: "\x1b[33m",
|
|
};
|
|
|
|
const coloredDependency = `${colors.yellow}${dependency}${colors.reset}`;
|
|
const coloredVersions = `${colors.red}${filteredVersions.join(", ")}${
|
|
colors.reset
|
|
}`;
|
|
|
|
console.error(`- ${coloredDependency}: ${coloredVersions}`);
|
|
}
|
|
|
|
function exitWithError(message: string) {
|
|
console.error(message);
|
|
process.exit(1);
|
|
}
|