Support extending ESM based configurations (#3037)
Co-authored-by: Matt Travi <programmer@travi.org>
This commit is contained in:
parent
250e7ae2c5
commit
6900865324
@ -1,6 +1,5 @@
|
|||||||
import { dirname, resolve } from "node:path";
|
import { dirname, extname } from "node:path";
|
||||||
import { fileURLToPath } from "node:url";
|
import { fileURLToPath } from "node:url";
|
||||||
import { createRequire } from "node:module";
|
|
||||||
|
|
||||||
import { castArray, isNil, isPlainObject, isString, pickBy } from "lodash-es";
|
import { castArray, isNil, isPlainObject, isString, pickBy } from "lodash-es";
|
||||||
import { readPackageUp } from "read-pkg-up";
|
import { readPackageUp } from "read-pkg-up";
|
||||||
@ -14,7 +13,6 @@ import { parseConfig, validatePlugin } from "./plugins/utils.js";
|
|||||||
|
|
||||||
const debug = debugConfig("semantic-release:config");
|
const debug = debugConfig("semantic-release:config");
|
||||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||||
const require = createRequire(import.meta.url);
|
|
||||||
|
|
||||||
const CONFIG_NAME = "release";
|
const CONFIG_NAME = "release";
|
||||||
|
|
||||||
@ -35,7 +33,17 @@ export default async (context, cliOptions) => {
|
|||||||
options = {
|
options = {
|
||||||
...(await castArray(extendPaths).reduce(async (eventualResult, extendPath) => {
|
...(await castArray(extendPaths).reduce(async (eventualResult, extendPath) => {
|
||||||
const result = await eventualResult;
|
const result = await eventualResult;
|
||||||
const extendsOptions = require(resolveFrom.silent(__dirname, extendPath) || resolveFrom(cwd, extendPath));
|
const resolvedPath = resolveFrom.silent(__dirname, extendPath) || resolveFrom(cwd, extendPath);
|
||||||
|
const importAssertions =
|
||||||
|
extname(resolvedPath) === ".json"
|
||||||
|
? {
|
||||||
|
assert: {
|
||||||
|
type: "json",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
: undefined;
|
||||||
|
|
||||||
|
const { default: extendsOptions } = await import(resolvedPath, importAssertions);
|
||||||
|
|
||||||
// For each plugin defined in a shareable config, save in `pluginsPath` the extendable config path,
|
// For each plugin defined in a shareable config, save in `pluginsPath` the extendable config path,
|
||||||
// so those plugin will be loaded relative to the config file
|
// so those plugin will be loaded relative to the config file
|
||||||
|
@ -389,6 +389,92 @@ test.serial('Read configuration from an array of paths in "extends"', async (t)
|
|||||||
t.deepEqual(result, { options: expectedOptions, plugins: pluginsConfig });
|
t.deepEqual(result, { options: expectedOptions, plugins: pluginsConfig });
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test.serial('Read configuration from an array of CJS files in "extends"', async (t) => {
|
||||||
|
// Create a git repository, set the current working directory at the root of the repo
|
||||||
|
const { cwd } = await gitRepo();
|
||||||
|
const pkgOptions = { extends: ["./shareable1.cjs", "./shareable2.cjs"] };
|
||||||
|
const options1 = {
|
||||||
|
verifyRelease: "verifyRelease1",
|
||||||
|
analyzeCommits: { path: "analyzeCommits1", param: "analyzeCommits_param1" },
|
||||||
|
branches: ["test_branch"],
|
||||||
|
repositoryUrl: "https://host.null/owner/module.git",
|
||||||
|
};
|
||||||
|
const options2 = {
|
||||||
|
verifyRelease: "verifyRelease2",
|
||||||
|
generateNotes: "generateNotes2",
|
||||||
|
analyzeCommits: { path: "analyzeCommits2", param: "analyzeCommits_param2" },
|
||||||
|
branches: ["test_branch"],
|
||||||
|
tagFormat: `v\${version}`,
|
||||||
|
plugins: false,
|
||||||
|
};
|
||||||
|
// Create package.json and shareable.json in repository root
|
||||||
|
await outputJson(path.resolve(cwd, "package.json"), { release: pkgOptions });
|
||||||
|
await writeFile(path.resolve(cwd, "shareable1.cjs"), `module.exports = ${JSON.stringify(options1)}`);
|
||||||
|
await writeFile(path.resolve(cwd, "shareable2.cjs"), `module.exports = ${JSON.stringify(options2)}`);
|
||||||
|
const expectedOptions = { ...options1, ...options2, branches: ["test_branch"] };
|
||||||
|
// Verify the plugins module is called with the plugin options from shareable1.mjs and shareable2.mjs
|
||||||
|
td.when(
|
||||||
|
plugins(
|
||||||
|
{ options: expectedOptions, cwd },
|
||||||
|
{
|
||||||
|
verifyRelease1: "./shareable1.cjs",
|
||||||
|
verifyRelease2: "./shareable2.cjs",
|
||||||
|
generateNotes2: "./shareable2.cjs",
|
||||||
|
analyzeCommits1: "./shareable1.cjs",
|
||||||
|
analyzeCommits2: "./shareable2.cjs",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
).thenResolve(pluginsConfig);
|
||||||
|
|
||||||
|
const result = await t.context.getConfig({ cwd });
|
||||||
|
|
||||||
|
// Verify the options contains the plugin config from shareable1.json and shareable2.json
|
||||||
|
t.deepEqual(result, { options: expectedOptions, plugins: pluginsConfig });
|
||||||
|
});
|
||||||
|
|
||||||
|
test.serial('Read configuration from an array of ESM files in "extends"', async (t) => {
|
||||||
|
// Create a git repository, set the current working directory at the root of the repo
|
||||||
|
const { cwd } = await gitRepo();
|
||||||
|
const pkgOptions = { extends: ["./shareable1.mjs", "./shareable2.mjs"] };
|
||||||
|
const options1 = {
|
||||||
|
verifyRelease: "verifyRelease1",
|
||||||
|
analyzeCommits: { path: "analyzeCommits1", param: "analyzeCommits_param1" },
|
||||||
|
branches: ["test_branch"],
|
||||||
|
repositoryUrl: "https://host.null/owner/module.git",
|
||||||
|
};
|
||||||
|
const options2 = {
|
||||||
|
verifyRelease: "verifyRelease2",
|
||||||
|
generateNotes: "generateNotes2",
|
||||||
|
analyzeCommits: { path: "analyzeCommits2", param: "analyzeCommits_param2" },
|
||||||
|
branches: ["test_branch"],
|
||||||
|
tagFormat: `v\${version}`,
|
||||||
|
plugins: false,
|
||||||
|
};
|
||||||
|
// Create package.json and shareable.json in repository root
|
||||||
|
await outputJson(path.resolve(cwd, "package.json"), { release: pkgOptions });
|
||||||
|
await writeFile(path.resolve(cwd, "shareable1.mjs"), `export default ${JSON.stringify(options1)}`);
|
||||||
|
await writeFile(path.resolve(cwd, "shareable2.mjs"), `export default ${JSON.stringify(options2)}`);
|
||||||
|
const expectedOptions = { ...options1, ...options2, branches: ["test_branch"] };
|
||||||
|
// Verify the plugins module is called with the plugin options from shareable1.mjs and shareable2.mjs
|
||||||
|
td.when(
|
||||||
|
plugins(
|
||||||
|
{ options: expectedOptions, cwd },
|
||||||
|
{
|
||||||
|
verifyRelease1: "./shareable1.mjs",
|
||||||
|
verifyRelease2: "./shareable2.mjs",
|
||||||
|
generateNotes2: "./shareable2.mjs",
|
||||||
|
analyzeCommits1: "./shareable1.mjs",
|
||||||
|
analyzeCommits2: "./shareable2.mjs",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
).thenResolve(pluginsConfig);
|
||||||
|
|
||||||
|
const result = await t.context.getConfig({ cwd });
|
||||||
|
|
||||||
|
// Verify the options contains the plugin config from shareable1.json and shareable2.json
|
||||||
|
t.deepEqual(result, { options: expectedOptions, plugins: pluginsConfig });
|
||||||
|
});
|
||||||
|
|
||||||
test.serial('Prioritize configuration from config file over "extends"', async (t) => {
|
test.serial('Prioritize configuration from config file over "extends"', async (t) => {
|
||||||
// Create a git repository, set the current working directory at the root of the repo
|
// Create a git repository, set the current working directory at the root of the repo
|
||||||
const { cwd } = await gitRepo();
|
const { cwd } = await gitRepo();
|
||||||
|
Loading…
x
Reference in New Issue
Block a user