mirror of
https://github.com/goauthentik/authentik.git
synced 2026-06-17 19:09:11 +03:00
6fb4bb543a
Migrate package management from npm + Corepack to pnpm across the root,
web, and website workspaces:
- Swap npm/Corepack tooling for pnpm: drop package-lock.json files and the
bespoke Corepack bootstrap scripts (setup-corepack.mjs, utils/corepack.mjs,
lint-lockfile.mjs); add pnpm-lock.yaml + pnpm-workspace.yaml per workspace.
- CI uses the official pnpm/action-setup + actions/setup-node; pin the pnpm
store dir via PNPM_HOME so setup-node's `cache: pnpm` post-step succeeds.
- Docker sources pnpm from the official ghcr.io/pnpm/pnpm image via a
${BUILDPLATFORM}-pinned stage; the website docs build does a hoisted root
install so @goauthentik/docusaurus-config resolves its own deps.
- Gate the web install on the `node` dep so runtime-only jobs don't invoke
pnpm; scope the from-stable env setup so the new tooling doesn't run against
the stable checkout's npm packageManager field.
- Resolve @goauthentik/api (client-ts) from its TypeScript source instead of a
tsc-built dist, so it no longer depends on an install-time prepare having run
(the storybook build's environment never built it); sfe's rollup gains .ts
resolution to match.
- Netlify builds with pnpm; encode pnpm's supply-chain controls
(onlyBuiltDependencies/allowBuilds, minimumReleaseAge) in the workspace.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
160 lines
4.4 KiB
JavaScript
160 lines
4.4 KiB
JavaScript
/**
|
|
* Utility functions for working with npm packages and versions.
|
|
*
|
|
* @import { ExecOptions } from "node:child_process"
|
|
*/
|
|
|
|
import * as fs from "node:fs/promises";
|
|
import { dirname, join } from "node:path";
|
|
|
|
import { $ } from "./commands.mjs";
|
|
|
|
/**
|
|
* Find the nearest directory containing both package.json and pnpm-lock.yaml,
|
|
* starting from the given directory and walking upward.
|
|
*
|
|
* @param {string} start The directory to start searching from.
|
|
* @returns {Promise<{ packageJSONPath: string, packageLockPath: string }>}
|
|
* @throws {Error} If no co-located package.json and pnpm-lock.yaml are found.
|
|
*/
|
|
export async function findNPMPackage(start) {
|
|
let currentDir = start;
|
|
|
|
while (currentDir !== dirname(currentDir)) {
|
|
const packageJSONPath = join(currentDir, "package.json");
|
|
const packageLockPath = join(currentDir, "pnpm-lock.yaml");
|
|
|
|
try {
|
|
await Promise.all([fs.access(packageJSONPath), fs.access(packageLockPath)]);
|
|
return {
|
|
packageJSONPath,
|
|
packageLockPath,
|
|
};
|
|
} catch {
|
|
// Continue searching up the directory tree
|
|
}
|
|
|
|
currentDir = dirname(currentDir);
|
|
}
|
|
|
|
throw new Error(`No co-located package.json and pnpm-lock.yaml found above ${start}`);
|
|
}
|
|
|
|
/**
|
|
* @typedef {object} PackageJSON
|
|
* @property {string} name
|
|
* @property {string} version
|
|
* @property {Record<string, string>} [dependencies]
|
|
* @property {Record<string, string>} [devDependencies]
|
|
* @property {Record<string, string>} [peerDependencies]
|
|
* @property {Record<string, string>} [optionalDependencies]
|
|
* @property {Record<string, string>} [peerDependenciesMeta]
|
|
* @property {Record<string, string>} [engines]
|
|
* @property {Record<string, string>} [devEngines]
|
|
* @property {string} [packageManager]
|
|
*/
|
|
|
|
/**
|
|
* @param {string} jsonPath
|
|
* @returns {Promise<PackageJSON>}
|
|
*/
|
|
export function loadJSON(jsonPath) {
|
|
return fs
|
|
.readFile(jsonPath, "utf-8")
|
|
.then(JSON.parse)
|
|
.catch((cause) => {
|
|
throw new Error(`Failed to load JSON file at ${jsonPath}`, { cause });
|
|
});
|
|
}
|
|
|
|
const PackageJSONComparisionFields = /** @type {const} */ ([
|
|
"name",
|
|
"dependencies",
|
|
"devDependencies",
|
|
"optionalDependencies",
|
|
"peerDependencies",
|
|
"peerDependenciesMeta",
|
|
]);
|
|
|
|
/**
|
|
* @typedef {typeof PackageJSONComparisionFields[number]} PackageJSONComparisionField
|
|
*/
|
|
|
|
/**
|
|
* Extracts only the dependency fields from a package.json object for comparison purposes.
|
|
*
|
|
* @param {PackageJSON} data
|
|
* @returns {Pick<PackageJSON, PackageJSONComparisionField>}
|
|
*/
|
|
export function pluckDependencyFields(data) {
|
|
/**
|
|
* @type {Record<string, unknown>}
|
|
*/
|
|
const result = {};
|
|
|
|
for (const field of PackageJSONComparisionFields) {
|
|
if (data[field]) {
|
|
result[field] = data[field];
|
|
}
|
|
}
|
|
|
|
return /** @type {Pick<PackageJSON, PackageJSONComparisionField>} */ (result);
|
|
}
|
|
|
|
//#region Versioning
|
|
|
|
/**
|
|
* Compares two semantic version strings (e.g., "14.17.0").
|
|
*
|
|
* @param {string} a The first version string.
|
|
* @param {string} b The second version string.
|
|
* @returns {number}
|
|
*/
|
|
export function compareVersions(a, b) {
|
|
const pa = a.split(".").map(Number);
|
|
const pb = b.split(".").map(Number);
|
|
for (let i = 0; i < 3; i++) {
|
|
if (pa[i] > pb[i]) return 1;
|
|
if (pa[i] < pb[i]) return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Runs a Node.js command and returns its stdout output as a string.
|
|
*
|
|
* @param {TemplateStringsArray} strings
|
|
* @param {...unknown} expressions
|
|
* @returns {(options?: ExecOptions) => Promise<string>}
|
|
*/
|
|
export const node = $.bind("node");
|
|
|
|
/**
|
|
* Runs a pnpm command and returns its stdout output as a string.
|
|
*
|
|
* @param {TemplateStringsArray} strings
|
|
* @param {...unknown} expressions
|
|
* @returns {(options?: ExecOptions) => Promise<string>}
|
|
*/
|
|
export const pnpm = $.bind("pnpm");
|
|
|
|
/**
|
|
* Parses a version range string, stripping any leading >= and normalizing to three parts.
|
|
* @param {string} range
|
|
* @returns {{ operator: ">=" | "=", version: string }}
|
|
*/
|
|
export function parseRange(range) {
|
|
const hasGte = range.startsWith(">=");
|
|
const raw = hasGte ? range.slice(2) : range;
|
|
const parts = raw.split(".").map(Number);
|
|
|
|
while (parts.length < 3) parts.push(0);
|
|
|
|
return {
|
|
operator: hasGte ? ">=" : "=",
|
|
version: parts.join("."),
|
|
};
|
|
}
|
|
|
|
//#endregion
|