nx
Version:
235 lines (234 loc) • 11.4 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.releasePublishCLIHandler = void 0;
exports.createAPI = createAPI;
const nx_json_1 = require("../../config/nx-json");
const tree_1 = require("../../generators/tree");
const native_1 = require("../../native");
const file_map_utils_1 = require("../../project-graph/file-map-utils");
const tasks_execution_hooks_1 = require("../../project-graph/plugins/tasks-execution-hooks");
const project_graph_1 = require("../../project-graph/project-graph");
const run_command_1 = require("../../tasks-runner/run-command");
const command_line_utils_1 = require("../../utils/command-line-utils");
const handle_errors_1 = require("../../utils/handle-errors");
const output_1 = require("../../utils/output");
const project_graph_utils_1 = require("../../utils/project-graph-utils");
const workspace_root_1 = require("../../utils/workspace-root");
const graph_1 = require("../graph/graph");
const config_1 = require("./config/config");
const deep_merge_json_1 = require("./config/deep-merge-json");
const print_config_1 = require("./utils/print-config");
const release_graph_1 = require("./utils/release-graph");
const releasePublishCLIHandler = (args) => (0, handle_errors_1.handleErrors)(args.verbose, async () => {
const publishProjectsResult = await createAPI({}, false)(args);
// If all projects are published successfully, return 0, otherwise return 1
return Object.values(publishProjectsResult).every((result) => result.code === 0)
? 0
: 1;
});
exports.releasePublishCLIHandler = releasePublishCLIHandler;
function createAPI(overrideReleaseConfig, ignoreNxJsonConfig) {
/**
* NOTE: This function is also exported for programmatic usage and forms part of the public API
* of Nx. We intentionally do not wrap the implementation with handleErrors because users need
* to have control over their own error handling when using the API.
*/
return async function releasePublish(args) {
/**
* When used via the CLI, the args object will contain a __overrides_unparsed__ property that is
* important for invoking the relevant executor behind the scenes.
*
* We intentionally do not include that in the function signature, however, so as not to cause
* confusing errors for programmatic consumers of this function.
*/
const _args = args;
const projectGraph = await (0, project_graph_1.createProjectGraphAsync)({ exitOnError: true });
const nxJson = (0, nx_json_1.readNxJson)();
const overriddenConfig = overrideReleaseConfig ?? {};
const userProvidedReleaseConfig = ignoreNxJsonConfig
? overriddenConfig
: (0, deep_merge_json_1.deepMergeJson)(nxJson.release ?? {}, overriddenConfig);
// Apply default configuration to any optional user configuration
const { error: configError, nxReleaseConfig } = await (0, config_1.createNxReleaseConfig)(projectGraph, await (0, file_map_utils_1.createProjectFileMapUsingProjectGraph)(projectGraph), userProvidedReleaseConfig);
if (configError) {
return await (0, config_1.handleNxReleaseConfigError)(configError);
}
// --print-config exits directly as it is not designed to be combined with any other programmatic operations
if (args.printConfig) {
return (0, print_config_1.printConfigAndExit)({
userProvidedReleaseConfig,
nxReleaseConfig,
isDebug: args.printConfig === 'debug',
});
}
// Use pre-built release graph if provided, otherwise create a new one
const releaseGraph = args.releaseGraph ||
(await (0, release_graph_1.createReleaseGraph)({
// Only build the tree if no existing graph, it's only needed for this
tree: new tree_1.FsTree(workspace_root_1.workspaceRoot, args.verbose),
projectGraph,
nxReleaseConfig,
filters: {
projects: _args.projects,
groups: _args.groups,
},
firstRelease: args.firstRelease,
verbose: args.verbose,
// Publish doesn't need to resolve current versions during graph construction
skipVersionResolution: true,
}));
// Display filter log if filters were applied
if (releaseGraph.filterLog &&
process.env.NX_RELEASE_INTERNAL_SUPPRESS_FILTER_LOG !== 'true') {
output_1.output.note(releaseGraph.filterLog);
}
/**
* If the user is filtering to a subset of projects or groups, we should not run the publish task
* for dependencies, because that could cause projects outset of the filtered set to be published.
*/
const shouldExcludeTaskDependencies = _args.projects?.length > 0 ||
_args.groups?.length > 0 ||
args.excludeTaskDependencies;
let overallPublishProjectsResult = {};
if (args.projects?.length) {
/**
* Run publishing for all remaining release groups and filtered projects within them
* in topological order
*/
for (const releaseGroupName of releaseGraph.sortedReleaseGroups) {
const releaseGroup = releaseGraph.releaseGroups.find((g) => g.name === releaseGroupName);
if (!releaseGroup) {
// Release group was filtered out, skip
continue;
}
const publishProjectsResult = await runPublishOnProjects(_args, projectGraph, nxJson, Array.from(releaseGraph.releaseGroupToFilteredProjects.get(releaseGroup)), {
excludeTaskDependencies: shouldExcludeTaskDependencies,
loadDotEnvFiles: process.env.NX_LOAD_DOT_ENV_FILES !== 'false',
});
overallPublishProjectsResult = {
...overallPublishProjectsResult,
...publishProjectsResult,
};
}
return overallPublishProjectsResult;
}
/**
* Run publishing for all remaining release groups
*/
for (const releaseGroupName of releaseGraph.sortedReleaseGroups) {
const releaseGroup = releaseGraph.releaseGroups.find((g) => g.name === releaseGroupName);
if (!releaseGroup) {
// Release group was filtered out, skip
continue;
}
const publishProjectsResult = await runPublishOnProjects(_args, projectGraph, nxJson, releaseGroup.projects, {
excludeTaskDependencies: shouldExcludeTaskDependencies,
loadDotEnvFiles: process.env.NX_LOAD_DOT_ENV_FILES !== 'false',
});
overallPublishProjectsResult = {
...overallPublishProjectsResult,
...publishProjectsResult,
};
}
return overallPublishProjectsResult;
};
}
async function runPublishOnProjects(args, projectGraph, nxJson, projectNames, extraOptions) {
const projectsToRun = projectNames.map((projectName) => projectGraph.nodes[projectName]);
const overrides = (0, command_line_utils_1.createOverrides)(args.__overrides_unparsed__);
if (args.registry) {
overrides.registry = args.registry;
}
if (args.tag) {
overrides.tag = args.tag;
}
if (args.otp) {
overrides.otp = args.otp;
}
if (args.access) {
overrides.access = args.access;
}
if (args.dryRun) {
overrides.dryRun = args.dryRun;
/**
* Ensure the env var is set too, so that any and all publish executors triggered
* indirectly via dependsOn can also pick up on the fact that this is a dry run.
*/
process.env.NX_DRY_RUN = 'true';
}
if (args.firstRelease) {
overrides.firstRelease = args.firstRelease;
}
/**
* If using the `nx release` command, or possibly via the programmatic API, versionData will be passed through from the version subcommand.
* Provide it automatically to the publish executor options with a clear namespace to avoid userland conflicts.
* It will be filtered out of the final terminal output lifecycle to avoid cluttering the terminal.
*/
if (args.versionData) {
overrides.nxReleaseVersionData = args.versionData;
}
const requiredTargetName = 'nx-release-publish';
if (args.graph) {
const file = (0, command_line_utils_1.readGraphFileFromGraphArg)(args);
const projectNamesWithTarget = projectsToRun
.map((t) => t.name)
.filter((projectName) => (0, project_graph_utils_1.projectHasTarget)(projectGraph.nodes[projectName], requiredTargetName));
await (0, graph_1.generateGraph)({
watch: true,
all: false,
open: true,
view: 'tasks',
targets: [requiredTargetName],
projects: projectNamesWithTarget,
file,
}, projectNamesWithTarget);
return {};
}
const projectsWithTarget = projectsToRun.filter((project) => (0, project_graph_utils_1.projectHasTarget)(project, requiredTargetName));
if (projectsWithTarget.length === 0) {
throw new Error(`Based on your config, the following projects were matched for publishing but do not have the "${requiredTargetName}" target specified:\n${[
...projectsToRun.map((p) => `- ${p.name}`),
'',
`This is usually caused by either`,
`- not having an appropriate plugin, such as "@nx/js" installed, which will add the appropriate "${requiredTargetName}" target for you automatically`,
`- having "private": true set in your package.json, which prevents the target from being created`,
].join('\n')}\n`);
}
const id = (0, native_1.hashArray)([...process.argv, Date.now().toString()]);
await (0, tasks_execution_hooks_1.runPreTasksExecution)({
id,
workspaceRoot: workspace_root_1.workspaceRoot,
nxJsonConfiguration: nxJson,
argv: process.argv,
});
const startTime = Date.now();
/**
* Run the relevant nx-release-publish executor on each of the selected projects.
* NOTE: Force TUI to be disabled for now.
*/
process.env.NX_TUI = 'false';
const { taskResults } = await (0, run_command_1.runCommandForTasks)(projectsWithTarget, projectGraph, { nxJson }, {
targets: [requiredTargetName],
outputStyle: 'static',
...args,
// It is possible for workspaces to have circular dependencies between packages and still release them to a registry
nxIgnoreCycles: true,
}, overrides, null, {}, extraOptions);
const endTime = Date.now();
const publishProjectsResult = {};
for (const taskData of Object.values(taskResults)) {
publishProjectsResult[taskData.task.target.project] = {
code: taskData.code,
};
}
await (0, tasks_execution_hooks_1.runPostTasksExecution)({
id,
taskResults,
workspaceRoot: workspace_root_1.workspaceRoot,
nxJsonConfiguration: nxJson,
argv: process.argv,
startTime,
endTime,
});
return publishProjectsResult;
}