@uipath/solution-tool 1.201.0-preview.133 → 1.202.0-preview.134
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/deploy.d.ts +6 -1
- package/dist/deploy.js +15 -16
- package/dist/{index-2fj08e7n.js → index-gpd94m28.js} +27 -27
- package/dist/{index-w03qc9m8.js → index-sby22td6.js} +4 -4
- package/dist/index.js +16 -19
- package/dist/init.js +10 -13
- package/dist/{js-yaml-4ypbq2tt.js → js-yaml-b2jq67kn.js} +16 -16
- package/dist/{list-skbehtq2.js → list-3mrc9s3f.js} +7 -11
- package/dist/models/pack-command-types.d.ts +8 -0
- package/dist/pack.js +9 -12
- package/dist/{packager-tool-krd5v2r5.js → packager-tool-0qva0ebq.js} +129 -53
- package/dist/{packager-tool-0v6na3yp.js → packager-tool-1de529jm.js} +9 -7
- package/dist/{packager-tool-9vehmnke.js → packager-tool-3hbsdwjm.js} +6 -661
- package/dist/{packager-tool-fzwxq48d.js → packager-tool-3y92zbg2.js} +3 -3
- package/dist/{packager-tool-f7kj5fpa.js → packager-tool-5pfxgc8z.js} +19 -11
- package/dist/{packager-tool-9w3k4e77.js → packager-tool-b16qemg2.js} +494 -198
- package/dist/{packager-tool-zbp5p149.js → packager-tool-bahvrjbv.js} +592 -264
- package/dist/{packager-tool-129wn232.js → packager-tool-dfrk01gn.js} +198 -2
- package/dist/{packager-tool-67ssxgph.js → packager-tool-fh8pb2th.js} +3 -5
- package/dist/{packager-tool-c29pg93e.js → packager-tool-hc6ybtdw.js} +5 -5
- package/dist/{packager-tool-bcpknnr8.js → packager-tool-hgx66d1p.js} +1656 -558
- package/dist/{packager-tool-2yz7m5ny.js → packager-tool-nq2nq71y.js} +21282 -20197
- package/dist/{packager-tool-wjhn59ft.js → packager-tool-t89r1edr.js} +596 -180
- package/dist/{packager-tool-vpr77gre.js → packager-tool-x3ehbhsn.js} +3 -2
- package/dist/{packager-tool-yktm4v4r.js → packager-tool-xntjj1bt.js} +1 -1
- package/dist/packager-tool.d.ts +4 -3
- package/dist/packager-tool.js +4 -4
- package/dist/publish.js +8 -11
- package/dist/resource.js +7 -10
- package/dist/services/activation.d.ts +9 -1
- package/dist/services/deploy-run-service.d.ts +25 -1
- package/dist/services/deploy-uninstall-service.d.ts +20 -1
- package/dist/services/deploy-upgrade-service.d.ts +94 -0
- package/dist/services/deployment-feed-locator.d.ts +36 -0
- package/dist/services/deployment-search.d.ts +88 -7
- package/dist/services/pack-command-service.d.ts +15 -0
- package/dist/services/package-metadata-rewrite.d.ts +9 -5
- package/dist/services/publish-service.d.ts +11 -9
- package/dist/services/solution-path.d.ts +34 -0
- package/dist/templates/AGENTS.md +183 -16
- package/dist/tool.js +18 -21
- package/package.json +2 -2
- package/dist/browser-strategy-yccf8mtd.js +0 -99
- package/dist/node-strategy-12qfy0nv.js +0 -348
- package/dist/packager-tool-5arsyj36.js +0 -11
- package/dist/packager-tool-7eva0peq.js +0 -265
- package/dist/packager-tool-9qecd4wb.js +0 -14
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
getStudioWebAuthWithOrganizationId,
|
|
3
3
|
useStudioWebAuth
|
|
4
|
-
} from "./packager-tool-
|
|
4
|
+
} from "./packager-tool-xntjj1bt.js";
|
|
5
5
|
import {
|
|
6
6
|
activateDeploymentAsync,
|
|
7
7
|
deployRunAsync,
|
|
@@ -9,53 +9,54 @@ import {
|
|
|
9
9
|
formatDeploymentErrorsAsText,
|
|
10
10
|
listDeploymentsAsync,
|
|
11
11
|
listPackagesAsync,
|
|
12
|
-
uninstallDeploymentAsync
|
|
13
|
-
|
|
12
|
+
uninstallDeploymentAsync,
|
|
13
|
+
upgradeDeploymentAsync
|
|
14
|
+
} from "./packager-tool-t89r1edr.js";
|
|
14
15
|
import {
|
|
15
16
|
SolutionInitError,
|
|
16
17
|
solutionInitAsync
|
|
17
|
-
} from "./packager-tool-
|
|
18
|
+
} from "./packager-tool-fh8pb2th.js";
|
|
18
19
|
import {
|
|
19
20
|
GOVERNANCE_OPTION_INSTRUCTIONS,
|
|
20
21
|
PackCommandService,
|
|
22
|
+
SOLUTION_PATH_INSTRUCTIONS,
|
|
21
23
|
SolutionPackOptions,
|
|
22
24
|
applyAuthConnection,
|
|
25
|
+
checkSolutionPathAsync,
|
|
23
26
|
createSolutionPackager,
|
|
24
27
|
ensurePackagerTools,
|
|
25
28
|
packSolutionAsync,
|
|
26
29
|
readSolutionFileAsUint8Array,
|
|
27
30
|
resolveSdkLogLevel,
|
|
28
31
|
validateGovernanceOptions
|
|
29
|
-
} from "./packager-tool-
|
|
32
|
+
} from "./packager-tool-nq2nq71y.js";
|
|
30
33
|
import {
|
|
31
34
|
publishSolutionAsync
|
|
32
|
-
} from "./packager-tool-
|
|
35
|
+
} from "./packager-tool-5pfxgc8z.js";
|
|
33
36
|
import {
|
|
34
37
|
Configuration,
|
|
35
38
|
PipelinesApi,
|
|
36
39
|
feedResolutionFailureInstructions,
|
|
37
40
|
feedScopeInitOverride,
|
|
38
|
-
folderKeyInitOverride,
|
|
39
41
|
listSelectablePublishLocations,
|
|
40
|
-
resolveFeedScope
|
|
41
|
-
|
|
42
|
-
} from "./packager-tool-c29pg93e.js";
|
|
42
|
+
resolveFeedScope
|
|
43
|
+
} from "./packager-tool-hc6ybtdw.js";
|
|
43
44
|
import {
|
|
44
45
|
Configuration as Configuration2,
|
|
45
46
|
DeploymentActivationStatus,
|
|
46
47
|
DeploymentOperationStatus,
|
|
47
|
-
DeploymentsApi,
|
|
48
48
|
PackagesApi,
|
|
49
49
|
SearchApi,
|
|
50
|
+
StudioWebIncompatibleProjectError,
|
|
50
51
|
bundleSolution,
|
|
51
52
|
getProjectDebugStatus,
|
|
52
53
|
importSolution,
|
|
53
54
|
isDebugProvisioningSucceeded,
|
|
54
55
|
isDebugProvisioningTerminal,
|
|
56
|
+
overwriteSolution,
|
|
55
57
|
searchPackageVersions,
|
|
56
|
-
solutionExistsOnStudioWeb
|
|
57
|
-
|
|
58
|
-
} from "./packager-tool-9w3k4e77.js";
|
|
58
|
+
solutionExistsOnStudioWeb
|
|
59
|
+
} from "./packager-tool-b16qemg2.js";
|
|
59
60
|
import {
|
|
60
61
|
Tokens,
|
|
61
62
|
applyEntryPointEnhancementAsync,
|
|
@@ -63,6 +64,7 @@ import {
|
|
|
63
64
|
createLocalResource,
|
|
64
65
|
createProjectInSolutionBuilder,
|
|
65
66
|
createResourceBuilderServices,
|
|
67
|
+
createResourceKindMetadataResolver,
|
|
66
68
|
findMatchingLocalResource,
|
|
67
69
|
findProjectFile,
|
|
68
70
|
findResourceByKeyInRcs,
|
|
@@ -76,13 +78,13 @@ import {
|
|
|
76
78
|
syncAndLog,
|
|
77
79
|
syncResourcesFromBindings,
|
|
78
80
|
writeSolutionFile
|
|
79
|
-
} from "./packager-tool-
|
|
81
|
+
} from "./packager-tool-0qva0ebq.js";
|
|
80
82
|
import {
|
|
81
83
|
ToolResult
|
|
82
|
-
} from "./packager-tool-
|
|
84
|
+
} from "./packager-tool-x3ehbhsn.js";
|
|
83
85
|
import {
|
|
84
86
|
getSolutionAuthContext
|
|
85
|
-
} from "./packager-tool-
|
|
87
|
+
} from "./packager-tool-3y92zbg2.js";
|
|
86
88
|
import {
|
|
87
89
|
DEFAULT_PAGE_SIZE,
|
|
88
90
|
Option,
|
|
@@ -90,11 +92,14 @@ import {
|
|
|
90
92
|
Pagination,
|
|
91
93
|
PollOutcome,
|
|
92
94
|
RESULTS,
|
|
95
|
+
carriedInstructions,
|
|
93
96
|
catchError,
|
|
94
97
|
createHiddenDeprecatedTenantOption,
|
|
95
98
|
extractErrorDetails,
|
|
96
99
|
extractErrorMessage,
|
|
100
|
+
getFileSystem,
|
|
97
101
|
getLoginStatusAsync,
|
|
102
|
+
isGuid,
|
|
98
103
|
isPreviewBuild,
|
|
99
104
|
logger,
|
|
100
105
|
mapPackageMetadataOptions,
|
|
@@ -102,7 +107,6 @@ import {
|
|
|
102
107
|
parseLimit,
|
|
103
108
|
parseOffset,
|
|
104
109
|
pollUntil,
|
|
105
|
-
previewOnly,
|
|
106
110
|
processContext,
|
|
107
111
|
registerPackageMetadataOptions,
|
|
108
112
|
requireConfirmation,
|
|
@@ -113,21 +117,20 @@ import {
|
|
|
113
117
|
unsupportedSolutionProjectType,
|
|
114
118
|
updateUipxSolutionId,
|
|
115
119
|
withCompleter
|
|
116
|
-
} from "./packager-tool-
|
|
117
|
-
import {
|
|
118
|
-
getFileSystem
|
|
119
|
-
} from "./packager-tool-7eva0peq.js";
|
|
120
|
+
} from "./packager-tool-hgx66d1p.js";
|
|
120
121
|
import {
|
|
122
|
+
strFromU8,
|
|
123
|
+
unzip,
|
|
121
124
|
unzipSync
|
|
122
|
-
} from "./packager-tool-
|
|
125
|
+
} from "./packager-tool-dfrk01gn.js";
|
|
123
126
|
import {
|
|
124
127
|
__require
|
|
125
|
-
} from "./packager-tool-
|
|
128
|
+
} from "./packager-tool-1de529jm.js";
|
|
126
129
|
// package.json
|
|
127
130
|
var package_default = {
|
|
128
131
|
name: "@uipath/solution-tool",
|
|
129
132
|
license: "MIT",
|
|
130
|
-
version: "1.
|
|
133
|
+
version: "1.202.0-preview.134",
|
|
131
134
|
description: "Create, pack, publish, and deploy UiPath Automation Solutions.",
|
|
132
135
|
repository: {
|
|
133
136
|
type: "git",
|
|
@@ -188,7 +191,6 @@ var package_default = {
|
|
|
188
191
|
"@uipath/filesystem": "workspace:*",
|
|
189
192
|
"@uipath/orchestrator-sdk": "workspace:*",
|
|
190
193
|
"@uipath/packager-tool-connector": "workspace:*",
|
|
191
|
-
"@uipath/packager-tool-webapp": "workspace:*",
|
|
192
194
|
"@uipath/pipelines-sdk": "workspace:*",
|
|
193
195
|
"@uipath/project-executor": "workspace:*",
|
|
194
196
|
"@uipath/resource-builder-sdk": "^2025.11.0-alpha4202-3459",
|
|
@@ -211,13 +213,12 @@ class CleanupCommandService {
|
|
|
211
213
|
this.fs = getFileSystem();
|
|
212
214
|
}
|
|
213
215
|
async execute(solutionPath, options, context) {
|
|
214
|
-
const
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
const msg = statError?.message ?? `Solution path does not exist: ${absoluteSolutionPath}`;
|
|
218
|
-
this.reportFailureAndExit(context, msg, "Ensure the solution path exists and points to a solution directory (containing .uipx) or a .uis file.");
|
|
216
|
+
const check = await checkSolutionPathAsync(this.fs, solutionPath);
|
|
217
|
+
if (!check.ok) {
|
|
218
|
+
this.reportFailureAndExit(context, check.message, SOLUTION_PATH_INSTRUCTIONS, check.statError, check.errorCode);
|
|
219
219
|
return;
|
|
220
220
|
}
|
|
221
|
+
const { absoluteSolutionPath } = check;
|
|
221
222
|
logger.info(options.dryRun ? `
|
|
222
223
|
Detecting unused items (dry-run)...
|
|
223
224
|
` : `
|
|
@@ -229,11 +230,11 @@ Removing unused items...
|
|
|
229
230
|
const [error, result] = await catchError(this.executeAsync(solutionPath, options));
|
|
230
231
|
if (error) {
|
|
231
232
|
const msg = error instanceof Error ? error.message : String(error);
|
|
232
|
-
this.reportFailureAndExit(context, msg, "Check that the solution can be loaded, the required packager tools are available, and the current login has access to the tenant.", error);
|
|
233
|
+
this.reportFailureAndExit(context, msg, carriedInstructions(error) ?? "Check that the solution can be loaded, the required packager tools are available, and the current login has access to the tenant.", error);
|
|
233
234
|
return;
|
|
234
235
|
}
|
|
235
236
|
if (result.errorCode !== "SUCCESS" /* Success */) {
|
|
236
|
-
this.reportFailureAndExit(context, `Error code: ${result.errorCode} : ${result.message}`, result.message ?? "");
|
|
237
|
+
this.reportFailureAndExit(context, `Error code: ${result.errorCode} : ${result.message}`, result.instructions ?? result.message ?? "");
|
|
237
238
|
return;
|
|
238
239
|
}
|
|
239
240
|
const changedFilesByProject = result.details?.changedFilesByProject ?? {};
|
|
@@ -248,11 +249,12 @@ Removing unused items...
|
|
|
248
249
|
}
|
|
249
250
|
});
|
|
250
251
|
}
|
|
251
|
-
reportFailureAndExit(context, message, instructions, error) {
|
|
252
|
+
reportFailureAndExit(context, message, instructions, error, errorCode) {
|
|
252
253
|
OutputFormatter.error({
|
|
253
254
|
Result: RESULTS.Failure,
|
|
254
255
|
Message: message,
|
|
255
|
-
Instructions: instructions
|
|
256
|
+
Instructions: instructions,
|
|
257
|
+
ErrorCode: errorCode
|
|
256
258
|
});
|
|
257
259
|
if (error instanceof Error && error.stack) {
|
|
258
260
|
logger.error("Stack trace:");
|
|
@@ -261,22 +263,14 @@ Removing unused items...
|
|
|
261
263
|
context.exit(1);
|
|
262
264
|
}
|
|
263
265
|
async executeAsync(solutionPath, options) {
|
|
264
|
-
const absoluteSolutionPath = this.fs.path.resolve(solutionPath);
|
|
265
266
|
if (options.loginValidity !== undefined && Number.isNaN(options.loginValidity)) {
|
|
266
267
|
return new ToolResult("INTERNAL_ERROR" /* InternalError */, `Invalid --login-validity value: ${options.loginValidity}`, []);
|
|
267
268
|
}
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
const [statError, stats] = await catchError(this.fs.stat(absoluteSolutionPath));
|
|
272
|
-
if (statError || !stats) {
|
|
273
|
-
return new ToolResult("INTERNAL_ERROR" /* InternalError */, statError?.message ?? `Cannot access solution path: ${absoluteSolutionPath}`, []);
|
|
274
|
-
}
|
|
275
|
-
const isDirectory = stats.isDirectory();
|
|
276
|
-
const isUisFile = stats.isFile() && absoluteSolutionPath.endsWith(".uis");
|
|
277
|
-
if (!isDirectory && !isUisFile) {
|
|
278
|
-
return new ToolResult("INTERNAL_ERROR" /* InternalError */, `Solution path must be a directory or a .uis file: ${absoluteSolutionPath}`, []);
|
|
269
|
+
const check = await checkSolutionPathAsync(this.fs, solutionPath);
|
|
270
|
+
if (!check.ok) {
|
|
271
|
+
return new ToolResult("INTERNAL_ERROR" /* InternalError */, check.message, []);
|
|
279
272
|
}
|
|
273
|
+
const { absoluteSolutionPath, isUisFile } = check;
|
|
280
274
|
if (isUisFile && !options.dryRun) {
|
|
281
275
|
return new ToolResult("INTERNAL_ERROR" /* InternalError */, `In-place cleanup of a .uis file is not supported: changes would be written to a temporary extraction and then discarded, leaving '${absoluteSolutionPath}' unchanged. Re-run with --dry-run to only detect unused items, or point at an unpacked solution directory to modify it in place.`, []);
|
|
282
276
|
}
|
|
@@ -294,7 +288,7 @@ Removing unused items...
|
|
|
294
288
|
return packager.cleanupSolutionAsync(cleanupOptions);
|
|
295
289
|
});
|
|
296
290
|
if (error) {
|
|
297
|
-
return new ToolResult("INTERNAL_ERROR" /* InternalError */, error instanceof Error ? error.message : String(error), []);
|
|
291
|
+
return new ToolResult("INTERNAL_ERROR" /* InternalError */, error instanceof Error ? error.message : String(error), [], carriedInstructions(error));
|
|
298
292
|
}
|
|
299
293
|
return result;
|
|
300
294
|
}
|
|
@@ -310,7 +304,8 @@ Removing unused items...
|
|
|
310
304
|
skipImports: options.skipImports ?? false
|
|
311
305
|
};
|
|
312
306
|
cleanupOptions.logLevel = resolveSdkLogLevel();
|
|
313
|
-
cleanupOptions.
|
|
307
|
+
cleanupOptions.nuGetSourcesConfigPath = options.nugetSourcesConfigPath;
|
|
308
|
+
cleanupOptions.excludeConfiguredSources = options.excludeConfiguredSources ?? false;
|
|
314
309
|
cleanupOptions.feedFolder = options.feedFolder;
|
|
315
310
|
await applyAuthConnection(cleanupOptions, options.loginValidity);
|
|
316
311
|
return cleanupOptions;
|
|
@@ -357,11 +352,13 @@ var SOLUTION_CLEANUP_EXAMPLES = [
|
|
|
357
352
|
}
|
|
358
353
|
];
|
|
359
354
|
var registerCleanupCommand = (program) => {
|
|
360
|
-
program.command("cleanup").description("Remove unused items from every project in a UiPath solution. Accepts a solution directory (containing .uipx) or a .uis file. For a directory the project sources are modified in place. Pass --dry-run to only detect unused items.").argument("<solutionPath>", "Path to a solution directory (containing .uipx) or a .uis file").option("--dry-run", "Detect unused items but do not modify any project files.").option("--skip-imports", "Do not clean up now-unused references to removed items in the project's files.").option("--feed-folder <name>", "Resolve project dependencies from this Orchestrator folder's library feed instead of every tenant feed. Accepts a folder name or fully qualified path.").option("--login-validity <minutes>", "Minimum minutes before token expiration to trigger a refresh (default: 10)", (value) => Number.parseInt(value, 10), 10).examples(SOLUTION_CLEANUP_EXAMPLES).trackedAction(processContext, async (solutionPath, options) => {
|
|
355
|
+
program.command("cleanup").description("Remove unused items from every project in a UiPath solution. Accepts a solution directory (containing .uipx) or a .uis file. For a directory the project sources are modified in place. Pass --dry-run to only detect unused items.").argument("<solutionPath>", "Path to a solution directory (containing .uipx) or a .uis file").option("--dry-run", "Detect unused items but do not modify any project files.").option("--skip-imports", "Do not clean up now-unused references to removed items in the project's files.").option("--nuget-sources-config-path <path>", "Path to NuGet sources configuration file").option("--exclude-configured-sources", "Resolve project dependencies only from the tenant feeds and any --nuget-sources-config-path. Drops the built-in UiPath and nuget.org feeds, so a dependency that is not on those feeds fails to restore.").option("--feed-folder <name>", "Resolve project dependencies from this Orchestrator folder's library feed instead of every tenant feed. Accepts a folder name or fully qualified path.").option("--login-validity <minutes>", "Minimum minutes before token expiration to trigger a refresh (default: 10)", (value) => Number.parseInt(value, 10), 10).examples(SOLUTION_CLEANUP_EXAMPLES).trackedAction(processContext, async (solutionPath, options) => {
|
|
361
356
|
const service = new CleanupCommandService;
|
|
362
357
|
const cleanupOptions = {
|
|
363
358
|
dryRun: options.dryRun,
|
|
364
359
|
skipImports: options.skipImports,
|
|
360
|
+
nugetSourcesConfigPath: options.nugetSourcesConfigPath,
|
|
361
|
+
excludeConfiguredSources: options.excludeConfiguredSources,
|
|
365
362
|
feedFolder: options.feedFolder,
|
|
366
363
|
loginValidity: options.loginValidity
|
|
367
364
|
};
|
|
@@ -1079,9 +1076,7 @@ var SOLUTION_DEPLOY_LIST_EXAMPLES = [
|
|
|
1079
1076
|
];
|
|
1080
1077
|
var registerDeployListCommand = (program) => {
|
|
1081
1078
|
const command = program.command("list").description("List solution deployments. Shows deployment name, status, package version (current/target), pending operation, and folder path. --operation-status/--activation-status filter server-side; --folder-path filters after fetching.").addOption(createHiddenDeprecatedTenantOption("-t, --tenant <tenant-name>")).option("--folder-path <path>", "Filter by parent folder path (e.g., 'Shared')").option("--folder-key <key>", "Filter by parent folder key (GUID). Alternative to --folder-path.").option("-l, --limit <number>", "Max deployments to fetch. Note: --folder-path filtering happens after fetching, so increase this value if results seem incomplete.", parseLimit, DEFAULT_PAGE_SIZE).option("--offset <number>", "Number of deployments to skip (applied before --folder-path/--folder-key filtering)", parseOffset, 0).option("--sort-by <column>", "Column to sort by (default: startTime)", "startTime").option("--sort-order <direction>", "Sort direction: Ascending or Descending (default: Descending)", "Descending").option("--operation-status <statuses>", `Filter by deployment operation status, server-side (comma-separated). One or more of: ${OPERATION_STATUS_VALUES.join(", ")}.`).option("--activation-status <statuses>", `Filter by deployment activation status, server-side (comma-separated). One or more of: ${ACTIVATION_STATUS_VALUES.join(", ")}.`).option("--login-validity <minutes>", "Minimum minutes before token expiration to trigger a refresh (default: 10)", parseInt, 10);
|
|
1082
|
-
|
|
1083
|
-
command.addOption(new Option("--feed <name-or-key>", "Only list deployments from this feed (name or folder key, from 'solution feeds list'). Mutually exclusive with --personal-workspace.").conflicts("personalWorkspace")).option("--personal-workspace", "Only list deployments from the current user's Personal Workspace feed.");
|
|
1084
|
-
}
|
|
1079
|
+
command.addOption(new Option("--feed <name-or-key>", "Only list deployments from this feed (name or folder key, from 'solution feeds list'). Mutually exclusive with --personal-workspace.").conflicts("personalWorkspace")).option("--personal-workspace", "Only list deployments from the current user's Personal Workspace feed.");
|
|
1085
1080
|
command.examples(SOLUTION_DEPLOY_LIST_EXAMPLES).trackedAction(processContext, async (options) => {
|
|
1086
1081
|
const operationStatus = parseStatusFilter(options.operationStatus, OPERATION_STATUS_VALUES, "--operation-status");
|
|
1087
1082
|
if (!operationStatus.ok) {
|
|
@@ -1236,6 +1231,38 @@ var SOLUTION_DEPLOY_RUN_EXAMPLES = [
|
|
|
1236
1231
|
NextSteps: "Deployment created in your Personal Workspace and will auto-activate. Check status with: uip solution deploy list."
|
|
1237
1232
|
}
|
|
1238
1233
|
}
|
|
1234
|
+
},
|
|
1235
|
+
{
|
|
1236
|
+
Description: "Deploy into the Personal Workspace and wait for the install to finish before returning",
|
|
1237
|
+
Command: "uip solution deploy run --name my-deployment --package-name my-package --package-version 1.0.0 --folder-name MySolution --personal-workspace --wait",
|
|
1238
|
+
Output: {
|
|
1239
|
+
Code: "SolutionDeployRun",
|
|
1240
|
+
Data: {
|
|
1241
|
+
Status: "DeploymentSucceeded",
|
|
1242
|
+
DeploymentName: "my-deployment",
|
|
1243
|
+
PackageName: "my-package",
|
|
1244
|
+
PackageVersion: "1.0.0",
|
|
1245
|
+
FolderName: "MySolution",
|
|
1246
|
+
FolderPath: "alice@uipath.com/MySolution",
|
|
1247
|
+
ActivationStatus: "Auto"
|
|
1248
|
+
}
|
|
1249
|
+
}
|
|
1250
|
+
},
|
|
1251
|
+
{
|
|
1252
|
+
Description: "Deploy a second, separate copy of a package that is already deployed (each copy gets its own Orchestrator folder)",
|
|
1253
|
+
Command: 'uip solution deploy run --name my-deployment-2 --package-name my-package --package-version 1.0.0 --folder-name MySolutionCopy --parent-folder-path "Shared" --yes',
|
|
1254
|
+
Output: {
|
|
1255
|
+
Code: "SolutionDeployRun",
|
|
1256
|
+
Data: {
|
|
1257
|
+
Status: "DeploymentSucceeded",
|
|
1258
|
+
DeploymentKey: "a1b2c3d4-0000-0000-0000-000000000002",
|
|
1259
|
+
PipelineDeploymentId: "b2c3d4e5-0000-0000-0000-000000000002",
|
|
1260
|
+
InstanceId: "c3d4e5f6-0000-0000-0000-000000000002",
|
|
1261
|
+
FolderName: "MySolutionCopy",
|
|
1262
|
+
FolderPath: "Shared/MySolutionCopy",
|
|
1263
|
+
ActivationStatus: "SuccessfulActivate"
|
|
1264
|
+
}
|
|
1265
|
+
}
|
|
1239
1266
|
}
|
|
1240
1267
|
];
|
|
1241
1268
|
var SOLUTION_DEPLOY_RUN_FEED_EXAMPLE = {
|
|
@@ -1256,14 +1283,12 @@ var SOLUTION_DEPLOY_RUN_FEED_EXAMPLE = {
|
|
|
1256
1283
|
}
|
|
1257
1284
|
};
|
|
1258
1285
|
var registerDeployRunCommand = (program) => {
|
|
1259
|
-
const command = program.command("run").description("Deploy a solution package to Orchestrator. Creates a folder, provisions resources, and (by default) activates the solution. Pass --skip-activate to leave the deployment in 'Inactive (Ready to activate)' state and activate later with 'deploy activate'. To customize resource settings before deploying, use 'config get' to fetch the default config, 'config set' / 'config link' to edit it, then pass it here with --config-file.").requiredOption("-n, --name <deployment-name>", "Name for the deployment").addOption(withCompleter(new Option("--package-name <name>", "Solution package name to deploy. Use 'solution packages list' to find available names.").makeOptionMandatory(), packageNameCompleter)).addOption(withCompleter(new Option("--package-version <version>", "Solution package version to deploy. Use 'solution packages list' to find available versions.").makeOptionMandatory(), packageVersionCompleter)).requiredOption("--folder-name <name>", "Name for the new Orchestrator folder created for this deployment (created under --parent-folder-path if specified)").addOption(new Option("--parent-folder-path <path>", "Parent folder path (e.g., 'Shared'). Solution folder is created under this.").conflicts("parentFolderKey")).option("--parent-folder-key <key>", "Parent folder key (GUID). Alternative to --parent-folder-path.").addOption(new Option("--personal-workspace", "Deploy into the current user's Personal Workspace. Resolves the workspace name from Orchestrator automatically. Mutually exclusive with --parent-folder-path and --parent-folder-key.").conflicts(["parentFolderPath", "parentFolderKey"])).option("--config-file <path>", "JSON or YAML configuration file. Generate with: uip solution deploy config get <name> -d config.json, then customize with 'config set' or 'config link'.").addOption(createHiddenDeprecatedTenantOption("-t, --tenant <tenant-name>")).option("--timeout <seconds>", "Timeout in seconds applied per polling phase — deploy and (when not skipped) activate (default: 360 each)", (v) => parseInt(v, 10), 360).option("--poll-interval <ms>", "Milliseconds between status polls during the deploy and activate phases (default: 5000)", (v) => parseInt(v, 10), 5000).option("--login-validity <minutes>", "Minimum minutes before token expiration to trigger a refresh (default: 10)", parseInt, 10).option("--skip-activate", "Skip auto-activation; leave the deployment in 'Inactive (Ready to activate)' state. Activate later with 'uip solution deploy activate <name>'.");
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
}
|
|
1263
|
-
command.examples(isPreviewBuild() ? [
|
|
1286
|
+
const command = program.command("run").description("Deploy a solution package to Orchestrator. Creates a folder, provisions resources, and (by default) activates the solution. This always creates a NEW deployment in a NEW folder — it never updates an existing one — so if the package is already deployed the command stops and points you at the upgrade path; pass --yes to deploy a second, separate copy anyway. Pass --skip-activate to leave the deployment in 'Inactive (Ready to activate)' state and activate later with 'deploy activate'. To customize resource settings before deploying, use 'config get' to fetch the default config, 'config set' / 'config link' to edit it, then pass it here with --config-file.").requiredOption("-n, --name <deployment-name>", "Name for the deployment").addOption(withCompleter(new Option("--package-name <name>", "Solution package name to deploy. Use 'solution packages list' to find available names.").makeOptionMandatory(), packageNameCompleter)).addOption(withCompleter(new Option("--package-version <version>", "Solution package version to deploy. Use 'solution packages list' to find available versions.").makeOptionMandatory(), packageVersionCompleter)).requiredOption("--folder-name <name>", "Name for the new Orchestrator folder created for this deployment (created under --parent-folder-path if specified)").addOption(new Option("--parent-folder-path <path>", "Parent folder path (e.g., 'Shared'). Solution folder is created under this.").conflicts("parentFolderKey")).option("--parent-folder-key <key>", "Parent folder key (GUID). Alternative to --parent-folder-path.").addOption(new Option("--personal-workspace", "Deploy into the current user's Personal Workspace. Resolves the workspace name from Orchestrator automatically. Mutually exclusive with --parent-folder-path and --parent-folder-key.").conflicts(["parentFolderPath", "parentFolderKey"])).option("--config-file <path>", "JSON or YAML configuration file. Generate with: uip solution deploy config get <name> -d config.json, then customize with 'config set' or 'config link'.").addOption(createHiddenDeprecatedTenantOption("-t, --tenant <tenant-name>")).option("--timeout <seconds>", "Timeout in seconds applied per polling phase — deploy and (when not skipped) activate (default: 360 each)", (v) => parseInt(v, 10), 360).option("--poll-interval <ms>", "Milliseconds between status polls during the deploy and activate phases (default: 5000)", (v) => parseInt(v, 10), 5000).option("--login-validity <minutes>", "Minimum minutes before token expiration to trigger a refresh (default: 10)", parseInt, 10).option("--wait", "Wait for a --personal-workspace / --feed deploy to finish instead of returning as soon as it starts. Bounded by --timeout. The tenant path always waits, so this changes nothing there.").option("--skip-activate", "Skip auto-activation; leave the deployment in 'Inactive (Ready to activate)' state. Activate later with 'uip solution deploy activate <name>'.").option("-y, --yes", "Deploy even when this package already has a deployment. Without it, deploy run looks the package up and stops if it is already deployed. That check is best-effort, not a guarantee: it reads the Solutions search, which needs a user session, so an application (client-credentials) login deploys without it, and a lookup failure only warns.");
|
|
1287
|
+
command.addOption(new Option("--feed <name-or-key>", "Deploy a package from a specific feed (name or folder key, from 'solution feeds list') instead of the tenant feed. Mutually exclusive with --personal-workspace.").conflicts("personalWorkspace"));
|
|
1288
|
+
command.examples([
|
|
1264
1289
|
...SOLUTION_DEPLOY_RUN_EXAMPLES,
|
|
1265
1290
|
SOLUTION_DEPLOY_RUN_FEED_EXAMPLE
|
|
1266
|
-
]
|
|
1291
|
+
]).trackedAction(processContext, async (options) => {
|
|
1267
1292
|
const result = await deployRunAsync({
|
|
1268
1293
|
tenant: options.tenant,
|
|
1269
1294
|
deploymentName: options.name,
|
|
@@ -1275,17 +1300,21 @@ var registerDeployRunCommand = (program) => {
|
|
|
1275
1300
|
personalWorkspace: options.personalWorkspace,
|
|
1276
1301
|
feed: options.feed,
|
|
1277
1302
|
configFile: options.configFile,
|
|
1303
|
+
wait: options.wait,
|
|
1278
1304
|
timeout: options.timeout,
|
|
1279
1305
|
pollInterval: options.pollInterval,
|
|
1280
1306
|
loginValidity: options.loginValidity,
|
|
1281
1307
|
skipActivate: options.skipActivate,
|
|
1308
|
+
yes: options.yes,
|
|
1282
1309
|
signal: processContext.pollSignal
|
|
1283
1310
|
});
|
|
1284
1311
|
if (!result.ok) {
|
|
1285
1312
|
OutputFormatter.error({
|
|
1286
1313
|
Result: RESULTS.Failure,
|
|
1287
1314
|
Message: result.message,
|
|
1288
|
-
Instructions: result.instructions
|
|
1315
|
+
Instructions: result.instructions,
|
|
1316
|
+
ErrorCode: result.errorCode,
|
|
1317
|
+
Retry: result.retry
|
|
1289
1318
|
});
|
|
1290
1319
|
processContext.exit(result.exitCode);
|
|
1291
1320
|
return;
|
|
@@ -1433,7 +1462,9 @@ var registerDeployUninstallCommand = (program) => {
|
|
|
1433
1462
|
OutputFormatter.error({
|
|
1434
1463
|
Result: RESULTS.Failure,
|
|
1435
1464
|
Message: result.message,
|
|
1436
|
-
Instructions: result.instructions
|
|
1465
|
+
Instructions: result.instructions,
|
|
1466
|
+
ErrorCode: result.errorCode,
|
|
1467
|
+
Retry: result.retry
|
|
1437
1468
|
});
|
|
1438
1469
|
processContext.exit(result.exitCode);
|
|
1439
1470
|
return;
|
|
@@ -1473,97 +1504,6 @@ var registerDeployUninstallCommand = (program) => {
|
|
|
1473
1504
|
});
|
|
1474
1505
|
};
|
|
1475
1506
|
|
|
1476
|
-
// src/services/deploy-upgrade-service.ts
|
|
1477
|
-
var fail = (reason, message, instructions, exitCode = 1) => ({
|
|
1478
|
-
ok: false,
|
|
1479
|
-
reason,
|
|
1480
|
-
message,
|
|
1481
|
-
instructions,
|
|
1482
|
-
exitCode
|
|
1483
|
-
});
|
|
1484
|
-
var AUTH_INSTRUCTIONS = "Run 'uip login' to refresh credentials, then retry. If the deployment is in a different tenant, switch with 'uip login tenant set <tenant>'.";
|
|
1485
|
-
async function upgradeDeploymentAsync(deploymentKey, options = {}) {
|
|
1486
|
-
const [authError, auth] = await catchError(getSolutionAuthContext({
|
|
1487
|
-
tenant: options.tenant,
|
|
1488
|
-
loginValidity: options.loginValidity,
|
|
1489
|
-
envFilePath: options.envFilePath
|
|
1490
|
-
}));
|
|
1491
|
-
if (authError) {
|
|
1492
|
-
return fail("auth_failed", authError.message, AUTH_INSTRUCTIONS);
|
|
1493
|
-
}
|
|
1494
|
-
const config = new Configuration2({
|
|
1495
|
-
basePath: auth.basePath,
|
|
1496
|
-
accessToken: auth.accessToken
|
|
1497
|
-
});
|
|
1498
|
-
const deploymentsApi = new DeploymentsApi(config);
|
|
1499
|
-
const searchApi = new SearchApi(config);
|
|
1500
|
-
let feedScope;
|
|
1501
|
-
if (options.personalWorkspace) {
|
|
1502
|
-
const [pwError, pw] = await catchError(resolvePersonalWorkspace({
|
|
1503
|
-
tenant: options.tenant,
|
|
1504
|
-
loginValidity: options.loginValidity
|
|
1505
|
-
}));
|
|
1506
|
-
if (pwError || !pw) {
|
|
1507
|
-
return fail("personal_workspace_failed", pwError?.message ?? "Personal Workspace could not be resolved.", "Ensure your user has a Personal Workspace, then retry.");
|
|
1508
|
-
}
|
|
1509
|
-
feedScope = folderKeyInitOverride(pw.key);
|
|
1510
|
-
}
|
|
1511
|
-
const [getError, deployment] = await catchError(deploymentsApi.deploymentsGetByKey({ deploymentKey }, feedScope));
|
|
1512
|
-
if (getError || !deployment) {
|
|
1513
|
-
const detail = getError && await extractErrorDetails(getError);
|
|
1514
|
-
return fail("deployment_not_found", detail ? detail.details ? `${detail.message}: ${detail.details}` : detail.message : `Deployment '${deploymentKey}' was not found.`, "Run 'uip solution deploy list' to find the deployment key. Add '--personal-workspace' if it lives in your Personal Workspace.");
|
|
1515
|
-
}
|
|
1516
|
-
const upgradeScope = feedScope;
|
|
1517
|
-
const take = 50;
|
|
1518
|
-
let pkg;
|
|
1519
|
-
for (let skip = 0;; ) {
|
|
1520
|
-
const [searchError, packages] = await catchError(searchApi.searchSearchPackages({
|
|
1521
|
-
searchFilter: {
|
|
1522
|
-
searchTerm: deployment.packageName,
|
|
1523
|
-
take,
|
|
1524
|
-
skip
|
|
1525
|
-
}
|
|
1526
|
-
}, feedScope));
|
|
1527
|
-
if (searchError) {
|
|
1528
|
-
const { message, details } = await extractErrorDetails(searchError);
|
|
1529
|
-
return fail("package_not_found", details ? `${message}: ${details}` : message, "Confirm the package is published to the tenant (or Personal Workspace) feed.");
|
|
1530
|
-
}
|
|
1531
|
-
const values = packages.values ?? [];
|
|
1532
|
-
pkg = values.find((p) => p.name === deployment.packageName);
|
|
1533
|
-
if (pkg || values.length < take) {
|
|
1534
|
-
break;
|
|
1535
|
-
}
|
|
1536
|
-
skip += values.length;
|
|
1537
|
-
}
|
|
1538
|
-
if (!pkg) {
|
|
1539
|
-
return fail("package_not_found", `Package '${deployment.packageName}' was not found in the feed.`, "Confirm the package is published to the tenant (or Personal Workspace) feed.");
|
|
1540
|
-
}
|
|
1541
|
-
if (options.version && options.version !== pkg.latestVersion) {
|
|
1542
|
-
return fail("unsupported_version", `Upgrade supports only the latest published version ('${pkg.latestVersion}'); requested '${options.version}'.`, "Publish the desired version as the latest, or omit '--version' to upgrade to the newest.");
|
|
1543
|
-
}
|
|
1544
|
-
const currentVersion = deployment.currentPackageVersion ?? deployment.packageVersion;
|
|
1545
|
-
if (currentVersion === pkg.latestVersion) {
|
|
1546
|
-
return fail("no_upgrade_available", `Deployment '${deployment.name}' is already at the latest published version (${pkg.latestVersion}).`, "Publish a newer version of the package, then retry.");
|
|
1547
|
-
}
|
|
1548
|
-
const [upgradeError] = await catchError(deploymentsApi.deploymentsUpgrade({
|
|
1549
|
-
deploymentKey,
|
|
1550
|
-
packageVersionKey: pkg.packageVersionKey,
|
|
1551
|
-
deploymentUpgradeRequest: {
|
|
1552
|
-
packageVersionKey: pkg.packageVersionKey
|
|
1553
|
-
}
|
|
1554
|
-
}, upgradeScope));
|
|
1555
|
-
if (upgradeError) {
|
|
1556
|
-
const { message, details } = await extractErrorDetails(upgradeError);
|
|
1557
|
-
return fail("request_failed", details ? `${message}: ${details}` : message, "The upgrade could not be started. Check the deployment key and that a newer version is published.");
|
|
1558
|
-
}
|
|
1559
|
-
return {
|
|
1560
|
-
ok: true,
|
|
1561
|
-
deploymentName: deployment.name,
|
|
1562
|
-
fromVersion: currentVersion,
|
|
1563
|
-
toVersion: pkg.latestVersion
|
|
1564
|
-
};
|
|
1565
|
-
}
|
|
1566
|
-
|
|
1567
1507
|
// src/commands/deploy-upgrade.ts
|
|
1568
1508
|
var SOLUTION_DEPLOY_UPGRADE_EXAMPLES = [
|
|
1569
1509
|
{
|
|
@@ -1572,21 +1512,39 @@ var SOLUTION_DEPLOY_UPGRADE_EXAMPLES = [
|
|
|
1572
1512
|
Output: {
|
|
1573
1513
|
Code: "SolutionDeployUpgrade",
|
|
1574
1514
|
Data: {
|
|
1575
|
-
Status: "
|
|
1515
|
+
Status: "Successful",
|
|
1516
|
+
DeploymentName: "billing-solution",
|
|
1517
|
+
FromVersion: "1.0.0",
|
|
1518
|
+
ToVersion: "2.0.0",
|
|
1519
|
+
VersionChangeKey: "b2c3d4e5-0000-0000-0000-000000000002"
|
|
1520
|
+
}
|
|
1521
|
+
}
|
|
1522
|
+
},
|
|
1523
|
+
{
|
|
1524
|
+
Description: "Start the upgrade without waiting for it to finish",
|
|
1525
|
+
Command: "uip solution deploy upgrade a1b2c3d4-0000-0000-0000-000000000001 --no-wait",
|
|
1526
|
+
Output: {
|
|
1527
|
+
Code: "SolutionDeployUpgrade",
|
|
1528
|
+
Data: {
|
|
1529
|
+
Status: "Draft",
|
|
1576
1530
|
DeploymentName: "billing-solution",
|
|
1577
1531
|
FromVersion: "1.0.0",
|
|
1578
|
-
ToVersion: "2.0.0"
|
|
1532
|
+
ToVersion: "2.0.0",
|
|
1533
|
+
VersionChangeKey: "b2c3d4e5-0000-0000-0000-000000000002"
|
|
1579
1534
|
}
|
|
1580
1535
|
}
|
|
1581
1536
|
}
|
|
1582
1537
|
];
|
|
1583
1538
|
var registerDeployUpgradeCommand = (program) => {
|
|
1584
|
-
program.command("upgrade").description("Upgrade an existing deployment to a newer package version in place — the CLI equivalent of the Orchestrator UI 'Upgrade' button. Use this when 'deploy run' is blocked because a deployment already exists.
|
|
1539
|
+
program.command("upgrade").description("Upgrade an existing deployment to a newer package version in place — the CLI equivalent of the Orchestrator UI 'Upgrade' button. Use this when 'deploy run' is blocked because a deployment already exists. The deployment's existing configuration is preserved, so values already set on it (for example a credential asset's secret) are not reset to the package defaults. Waits for the upgrade to finish unless --no-wait is passed.").argument("<deployment-key>", "Key of the deployment to upgrade. Use 'solution deploy list' to find it.").addOption(createHiddenDeprecatedTenantOption("-t, --tenant <tenant-name>")).option("--version <version>", "Target package version (default: the newest available). Only the latest published version is supported today.").option("--personal-workspace", "Upgrade a deployment in your Personal Workspace feed instead of the tenant.").option("--login-validity <minutes>", "Minimum minutes before token expiration to trigger a refresh (default: 10)", parseInt, 10).option("--no-wait", "Return as soon as the upgrade is accepted instead of waiting for it to finish.").option("--timeout <seconds>", "Seconds to wait for the upgrade to finish (default: 300)", (v) => parseInt(v, 10), 300).option("--poll-interval <ms>", "Milliseconds between status polls (default: 5000)", (v) => parseInt(v, 10), 5000).examples(SOLUTION_DEPLOY_UPGRADE_EXAMPLES).trackedAction(processContext, async (deploymentKey, options) => {
|
|
1585
1540
|
const result = await upgradeDeploymentAsync(deploymentKey, {
|
|
1586
1541
|
tenant: options.tenant,
|
|
1587
1542
|
version: options.version,
|
|
1588
1543
|
personalWorkspace: options.personalWorkspace,
|
|
1589
|
-
loginValidity: options.loginValidity
|
|
1544
|
+
loginValidity: options.loginValidity,
|
|
1545
|
+
noWait: options.wait === false,
|
|
1546
|
+
timeout: options.timeout,
|
|
1547
|
+
pollInterval: options.pollInterval
|
|
1590
1548
|
});
|
|
1591
1549
|
if (!result.ok) {
|
|
1592
1550
|
OutputFormatter.error({
|
|
@@ -1601,10 +1559,11 @@ var registerDeployUpgradeCommand = (program) => {
|
|
|
1601
1559
|
Result: RESULTS.Success,
|
|
1602
1560
|
Code: "SolutionDeployUpgrade",
|
|
1603
1561
|
Data: {
|
|
1604
|
-
Status:
|
|
1562
|
+
Status: result.status,
|
|
1605
1563
|
DeploymentName: result.deploymentName,
|
|
1606
1564
|
FromVersion: result.fromVersion,
|
|
1607
|
-
ToVersion: result.toVersion
|
|
1565
|
+
ToVersion: result.toVersion,
|
|
1566
|
+
VersionChangeKey: result.versionChangeKey
|
|
1608
1567
|
}
|
|
1609
1568
|
});
|
|
1610
1569
|
});
|
|
@@ -1619,9 +1578,57 @@ var registerDeployCommand = (program) => {
|
|
|
1619
1578
|
registerDeployActivateCommand(deploy);
|
|
1620
1579
|
registerDeployUninstallCommand(deploy);
|
|
1621
1580
|
registerDeployListCommand(deploy);
|
|
1622
|
-
|
|
1581
|
+
registerDeployUpgradeCommand(deploy);
|
|
1623
1582
|
};
|
|
1624
1583
|
|
|
1584
|
+
// src/services/agents-briefing.ts
|
|
1585
|
+
import { fileURLToPath } from "node:url";
|
|
1586
|
+
var BRIEFING_FILENAMES = ["AGENTS.md", "CLAUDE.md"];
|
|
1587
|
+
var CLI_VERSION_PLACEHOLDER = "{{cli_version}}";
|
|
1588
|
+
function agentsTemplateCandidateUrls() {
|
|
1589
|
+
return [
|
|
1590
|
+
new URL("./templates/AGENTS.md", import.meta.url),
|
|
1591
|
+
new URL("../../templates/AGENTS.md", import.meta.url)
|
|
1592
|
+
];
|
|
1593
|
+
}
|
|
1594
|
+
async function renderBriefingTemplateAsync() {
|
|
1595
|
+
const fs = getFileSystem();
|
|
1596
|
+
const candidateUrls = agentsTemplateCandidateUrls();
|
|
1597
|
+
let templateContent;
|
|
1598
|
+
for (const candidateUrl of candidateUrls) {
|
|
1599
|
+
const candidatePath = fileURLToPath(candidateUrl);
|
|
1600
|
+
const [, content] = await catchError(fs.readFile(candidatePath, "utf-8"));
|
|
1601
|
+
if (content !== undefined && content !== null) {
|
|
1602
|
+
templateContent = content;
|
|
1603
|
+
break;
|
|
1604
|
+
}
|
|
1605
|
+
}
|
|
1606
|
+
if (templateContent === undefined) {
|
|
1607
|
+
throw new Error(`AGENTS.md template asset not found in package. Tried: ${candidateUrls.map((u) => fileURLToPath(u)).join(", ")}`);
|
|
1608
|
+
}
|
|
1609
|
+
return templateContent.replaceAll(CLI_VERSION_PLACEHOLDER, package_default.version);
|
|
1610
|
+
}
|
|
1611
|
+
async function writeMissingBriefingFilesAsync(solutionDir) {
|
|
1612
|
+
const fs = getFileSystem();
|
|
1613
|
+
const targets = BRIEFING_FILENAMES.map((name) => fs.path.join(solutionDir, name));
|
|
1614
|
+
const presence = await Promise.all(targets.map((path) => fs.exists(path)));
|
|
1615
|
+
const missing = targets.filter((_, index) => !presence[index]);
|
|
1616
|
+
if (missing.length === 0) {
|
|
1617
|
+
return [];
|
|
1618
|
+
}
|
|
1619
|
+
const content = await renderBriefingTemplateAsync();
|
|
1620
|
+
const written = [];
|
|
1621
|
+
for (const path of missing) {
|
|
1622
|
+
const [error] = await catchError(fs.writeFile(path, content));
|
|
1623
|
+
if (error) {
|
|
1624
|
+
logger.warn(`Could not write ${path}: ${error.message}`);
|
|
1625
|
+
continue;
|
|
1626
|
+
}
|
|
1627
|
+
written.push(path);
|
|
1628
|
+
}
|
|
1629
|
+
return written;
|
|
1630
|
+
}
|
|
1631
|
+
|
|
1625
1632
|
// src/services/archive-extract.ts
|
|
1626
1633
|
function resolveZipEntryPath(fs, destinationDir, entryName) {
|
|
1627
1634
|
if (entryName.includes("\x00")) {
|
|
@@ -1660,6 +1667,36 @@ async function extractArchive(archivePath, destinationDir) {
|
|
|
1660
1667
|
}
|
|
1661
1668
|
await extractZipDataToDir(fs, data, destinationDir);
|
|
1662
1669
|
}
|
|
1670
|
+
var SOLUTION_STORAGE_ENTRY = "SolutionStorage.json";
|
|
1671
|
+
function isRootUipxEntry(name) {
|
|
1672
|
+
return !name.includes("/") && name.endsWith(".uipx");
|
|
1673
|
+
}
|
|
1674
|
+
function readSolutionIdField(bytes) {
|
|
1675
|
+
if (!bytes) {
|
|
1676
|
+
return;
|
|
1677
|
+
}
|
|
1678
|
+
const [parseError, parsed] = catchError(() => JSON.parse(strFromU8(bytes)));
|
|
1679
|
+
if (parseError || !parsed) {
|
|
1680
|
+
return;
|
|
1681
|
+
}
|
|
1682
|
+
const id = parsed.SolutionId;
|
|
1683
|
+
return typeof id === "string" && isGuid(id) ? id : undefined;
|
|
1684
|
+
}
|
|
1685
|
+
function readBundledSolutionId(archive) {
|
|
1686
|
+
const [unzipError, entries] = catchError(() => unzipSync(archive, {
|
|
1687
|
+
filter: (file) => file.name === SOLUTION_STORAGE_ENTRY || isRootUipxEntry(file.name)
|
|
1688
|
+
}));
|
|
1689
|
+
if (unzipError || !entries) {
|
|
1690
|
+
logger.debug(`Could not inspect the solution archive for its SolutionId: ${unzipError?.message ?? "no entries"}`);
|
|
1691
|
+
return;
|
|
1692
|
+
}
|
|
1693
|
+
const rootUipxName = Object.keys(entries).find(isRootUipxEntry);
|
|
1694
|
+
const solutionId = readSolutionIdField(entries[SOLUTION_STORAGE_ENTRY]) ?? readSolutionIdField(rootUipxName ? entries[rootUipxName] : undefined);
|
|
1695
|
+
if (!solutionId) {
|
|
1696
|
+
logger.debug("No SolutionId found in the solution archive.");
|
|
1697
|
+
}
|
|
1698
|
+
return solutionId;
|
|
1699
|
+
}
|
|
1663
1700
|
|
|
1664
1701
|
// src/commands/download.ts
|
|
1665
1702
|
var SOLUTION_DOWNLOAD_EXAMPLES = [
|
|
@@ -1672,13 +1709,17 @@ var SOLUTION_DOWNLOAD_EXAMPLES = [
|
|
|
1672
1709
|
Status: "Solution exported successfully",
|
|
1673
1710
|
SolutionId: "a1b2c3d4-0000-0000-0000-000000000001",
|
|
1674
1711
|
Output: "./downloads/a1b2c3d4-0000-0000-0000-000000000001.uis",
|
|
1675
|
-
ExtractedTo: "./downloads/a1b2c3d4-0000-0000-0000-000000000001"
|
|
1712
|
+
ExtractedTo: "./downloads/a1b2c3d4-0000-0000-0000-000000000001",
|
|
1713
|
+
BriefingFiles: [
|
|
1714
|
+
"./downloads/a1b2c3d4-0000-0000-0000-000000000001/AGENTS.md",
|
|
1715
|
+
"./downloads/a1b2c3d4-0000-0000-0000-000000000001/CLAUDE.md"
|
|
1716
|
+
]
|
|
1676
1717
|
}
|
|
1677
1718
|
}
|
|
1678
1719
|
}
|
|
1679
1720
|
];
|
|
1680
1721
|
function registerDownloadCommand(program) {
|
|
1681
|
-
const download = program.command("download").description("Download/export a solution from Studio Web").argument("<solution-id>", "Solution UUID to export").option("-d, --destination <dir>", "Destination directory for the downloaded file", ".").option("-n, --name <name>", "Output filename (without extension). Existing files are overwritten; --extract uses this name for the directory").option("--extract", "Extract the downloaded .uis zip into a directory", false).examples(SOLUTION_DOWNLOAD_EXAMPLES);
|
|
1722
|
+
const download = program.command("download").description("Download/export a solution from Studio Web").argument("<solution-id>", "Solution UUID to export").option("-d, --destination <dir>", "Destination directory for the downloaded file", ".").option("-n, --name <name>", "Output filename (without extension). Existing files are overwritten; --extract uses this name for the directory").option("--extract", "Extract the downloaded .uis zip into a directory, and add AGENTS.md / CLAUDE.md there if the archive did not already carry them", false).examples(SOLUTION_DOWNLOAD_EXAMPLES);
|
|
1682
1723
|
useStudioWebAuth(download, { requireOrganizationId: true }).trackedAction(processContext, async (solutionId, options, command) => {
|
|
1683
1724
|
const [error] = await catchError(downloadSolution(solutionId, options, command));
|
|
1684
1725
|
if (error) {
|
|
@@ -1714,9 +1755,11 @@ async function downloadSolution(solutionId, options, command) {
|
|
|
1714
1755
|
await fs.writeFile(outputPath, new Uint8Array(arrayBuffer));
|
|
1715
1756
|
logger.info(`Downloaded solution to ${outputPath}`);
|
|
1716
1757
|
const extractedTo = options.extract ? fs.path.join(outputDir, filename.replace(/\.uis$/, "")) : undefined;
|
|
1758
|
+
let briefingFiles = [];
|
|
1717
1759
|
if (extractedTo) {
|
|
1718
1760
|
await extractArchive(outputPath, extractedTo);
|
|
1719
1761
|
logger.info(`Extracted to ${extractedTo}`);
|
|
1762
|
+
briefingFiles = await writeBriefingFiles(extractedTo);
|
|
1720
1763
|
}
|
|
1721
1764
|
OutputFormatter.success({
|
|
1722
1765
|
Result: RESULTS.Success,
|
|
@@ -1725,10 +1768,22 @@ async function downloadSolution(solutionId, options, command) {
|
|
|
1725
1768
|
Status: "Solution exported successfully",
|
|
1726
1769
|
SolutionId: solutionId,
|
|
1727
1770
|
Output: outputPath,
|
|
1728
|
-
...extractedTo ? { ExtractedTo: extractedTo } : {}
|
|
1771
|
+
...extractedTo ? { ExtractedTo: extractedTo } : {},
|
|
1772
|
+
...briefingFiles.length > 0 ? { BriefingFiles: briefingFiles } : {}
|
|
1729
1773
|
}
|
|
1730
1774
|
});
|
|
1731
1775
|
}
|
|
1776
|
+
async function writeBriefingFiles(solutionDir) {
|
|
1777
|
+
const [error, written] = await catchError(writeMissingBriefingFilesAsync(solutionDir));
|
|
1778
|
+
if (error) {
|
|
1779
|
+
logger.warn(`Extracted the solution but could not write its AGENTS.md / CLAUDE.md: ${error.message}`);
|
|
1780
|
+
return [];
|
|
1781
|
+
}
|
|
1782
|
+
for (const path of written) {
|
|
1783
|
+
logger.info(`Wrote agent instructions to ${path}`);
|
|
1784
|
+
}
|
|
1785
|
+
return written;
|
|
1786
|
+
}
|
|
1732
1787
|
function getOutputFilename(name) {
|
|
1733
1788
|
validatePlainFilename(name);
|
|
1734
1789
|
return name.endsWith(".uis") ? name : `${name}.uis`;
|
|
@@ -1803,18 +1858,10 @@ var registerFeedsCommand = (program) => {
|
|
|
1803
1858
|
};
|
|
1804
1859
|
|
|
1805
1860
|
// src/commands/new.ts
|
|
1806
|
-
import { fileURLToPath } from "node:url";
|
|
1807
1861
|
var SOLUTION_INIT_STAGE_MESSAGES = {
|
|
1808
1862
|
directory: "Failed to create solution directory",
|
|
1809
1863
|
briefing: "Failed to write AI-agent instruction files"
|
|
1810
1864
|
};
|
|
1811
|
-
var CLI_VERSION_PLACEHOLDER = "{{cli_version}}";
|
|
1812
|
-
function agentsTemplateCandidateUrls() {
|
|
1813
|
-
return [
|
|
1814
|
-
new URL("./templates/AGENTS.md", import.meta.url),
|
|
1815
|
-
new URL("../../templates/AGENTS.md", import.meta.url)
|
|
1816
|
-
];
|
|
1817
|
-
}
|
|
1818
1865
|
var SOLUTION_NEW_EXAMPLES = [
|
|
1819
1866
|
{
|
|
1820
1867
|
Description: "Initialize a new empty UiPath solution",
|
|
@@ -1829,26 +1876,9 @@ var SOLUTION_NEW_EXAMPLES = [
|
|
|
1829
1876
|
}
|
|
1830
1877
|
}
|
|
1831
1878
|
];
|
|
1832
|
-
async function renderBriefingTemplate() {
|
|
1833
|
-
const fs = getFileSystem();
|
|
1834
|
-
const candidateUrls = agentsTemplateCandidateUrls();
|
|
1835
|
-
let templateContent;
|
|
1836
|
-
for (const candidateUrl of candidateUrls) {
|
|
1837
|
-
const candidatePath = fileURLToPath(candidateUrl);
|
|
1838
|
-
const [, content] = await catchError(fs.readFile(candidatePath, "utf-8"));
|
|
1839
|
-
if (content !== undefined && content !== null) {
|
|
1840
|
-
templateContent = content;
|
|
1841
|
-
break;
|
|
1842
|
-
}
|
|
1843
|
-
}
|
|
1844
|
-
if (templateContent === undefined) {
|
|
1845
|
-
throw new Error(`AGENTS.md template asset not found in package. Tried: ${candidateUrls.map((u) => fileURLToPath(u)).join(", ")}`);
|
|
1846
|
-
}
|
|
1847
|
-
return templateContent.replaceAll(CLI_VERSION_PLACEHOLDER, package_default.version);
|
|
1848
|
-
}
|
|
1849
1879
|
var registerNewCommand = (program) => {
|
|
1850
1880
|
program.command("init").description("Initialize a new empty UiPath solution. Creates a directory named after the solution containing a .uipx manifest file with an empty Projects array, plus AGENTS.md and CLAUDE.md briefing files that orient AI coding agents to the solution. Use 'solution projects add' to add projects afterward.").argument("<solutionName>", "Solution name or path. Creates a directory with this name containing a .uipx manifest file.").examples(SOLUTION_NEW_EXAMPLES).trackedAction(processContext, async (solutionName) => {
|
|
1851
|
-
const [briefingError, briefingContent] = await catchError(
|
|
1881
|
+
const [briefingError, briefingContent] = await catchError(renderBriefingTemplateAsync());
|
|
1852
1882
|
if (briefingError) {
|
|
1853
1883
|
OutputFormatter.error({
|
|
1854
1884
|
Result: RESULTS.Failure,
|
|
@@ -1963,7 +1993,7 @@ var SOLUTION_PACK_EXAMPLES = [
|
|
|
1963
1993
|
}
|
|
1964
1994
|
];
|
|
1965
1995
|
var registerPackCommand = (program) => {
|
|
1966
|
-
const command = program.command("pack").description("Pack a UiPath solution into a deployable .zip package. Accepts a solution directory (containing .uipx) or a .uis file. Output is used with 'solution publish' to upload to UiPath. Pass --dry-run to check that the solution packs without producing a package.").argument("<solutionPath>", "Path to a solution directory (containing .uipx) or a .uis file").argument("[output-path]", "Output directory where the packed solution will be saved. Required unless --dry-run is set.").option("-n, --name <name>", "Package name (default: solution folder name or .uis file name)").option("-v, --version <version>", "Package version (default: 1.0.0)", "1.0.0").option("--nuget-sources-config-path <path>", "Path to NuGet sources configuration file").option("--feed-folder <name>", "Resolve project dependencies from this Orchestrator folder's library feed instead of every tenant feed. Accepts a folder name or fully qualified path.").option("--signing-certificate-path <path>", "Path to .pfx certificate file used to sign each packed project .nupkg").option("--signing-certificate-password <password>", "Certificate password. Use env.ENV_NAME to read from an environment variable.").option("--signing-timestamp-server <url>", "Timestamp server URL").option("--skip-analyze", "Skip the workflow analyzer for every RPA project in the solution. Compilation and validation still run.").addOption(new Option("--governance-file-path <path>", "Analyze every RPA project against this local governance policy file, instead of the analyzer rules shipped with the WorkflowCompiler.").conflicts("automationOpsProfile")).addOption(new Option("--automation-ops-profile <profile>", "Analyze every RPA project against the governance policy published in AutomationOps for the signed-in tenant. The value is the product whose policy is downloaded, e.g. StudioWeb, Development, Business. Mutually exclusive with --governance-file-path.").conflicts("governanceFilePath"));
|
|
1996
|
+
const command = program.command("pack").description("Pack a UiPath solution into a deployable .zip package. Accepts a solution directory (containing .uipx) or a .uis file. Output is used with 'solution publish' to upload to UiPath. Pass --dry-run to check that the solution packs without producing a package.").argument("<solutionPath>", "Path to a solution directory (containing .uipx) or a .uis file").argument("[output-path]", "Output directory where the packed solution will be saved. Required unless --dry-run is set.").option("-n, --name <name>", "Package name (default: solution folder name or .uis file name)").option("-v, --version <version>", "Package version (default: 1.0.0)", "1.0.0").option("--nuget-sources-config-path <path>", "Path to NuGet sources configuration file").option("--exclude-configured-sources", "Resolve project dependencies only from the tenant feeds and any --nuget-sources-config-path. Drops the built-in UiPath and nuget.org feeds, so a dependency that is not on those feeds fails to restore.").option("--feed-folder <name>", "Resolve project dependencies from this Orchestrator folder's library feed instead of every tenant feed. Accepts a folder name or fully qualified path.").option("--signing-certificate-path <path>", "Path to .pfx certificate file used to sign each packed project .nupkg").option("--signing-certificate-password <password>", "Certificate password. Use env.ENV_NAME to read from an environment variable.").option("--signing-timestamp-server <url>", "Timestamp server URL").option("--skip-analyze", "Skip the workflow analyzer for every RPA project in the solution. Compilation and validation still run.").addOption(new Option("--governance-file-path <path>", "Analyze every RPA project against this local governance policy file, instead of the analyzer rules shipped with the WorkflowCompiler.").conflicts("automationOpsProfile")).addOption(new Option("--automation-ops-profile <profile>", "Analyze every RPA project against the governance policy published in AutomationOps for the signed-in tenant. The value is the product whose policy is downloaded, e.g. StudioWeb, Development, Business. Mutually exclusive with --governance-file-path.").conflicts("governanceFilePath"));
|
|
1967
1997
|
registerPackageMetadataOptions(command).option("--login-validity <minutes>", "Minimum minutes before token expiration to trigger a refresh (default: 10)", parseInt, 10).option("--dry-run", "Run the full pack pipeline against a temporary directory and discard the output. Catches pack-time failures; does not inspect the produced package contents.").examples(SOLUTION_PACK_EXAMPLES).trackedAction(processContext, async (solutionPath, outputPath, options) => {
|
|
1968
1998
|
if (!options.dryRun && outputPath === undefined) {
|
|
1969
1999
|
OutputFormatter.error({
|
|
@@ -1995,6 +2025,7 @@ var registerPackCommand = (program) => {
|
|
|
1995
2025
|
loginValidity: options.loginValidity,
|
|
1996
2026
|
dryRun: options.dryRun,
|
|
1997
2027
|
nugetSourcesConfigPath: options.nugetSourcesConfigPath,
|
|
2028
|
+
excludeConfiguredSources: options.excludeConfiguredSources,
|
|
1998
2029
|
feedFolder: options.feedFolder,
|
|
1999
2030
|
signingCertificatePath: options.signingCertificatePath,
|
|
2000
2031
|
signingCertificatePassword: resolveEnvReference(options.signingCertificatePassword),
|
|
@@ -2040,10 +2071,7 @@ function packageDeleteFailureInstructions(scope, packageName) {
|
|
|
2040
2071
|
if (scope.kind === "folder") {
|
|
2041
2072
|
return `List packages in that feed with: uip solution packages list --feed <name-or-key> --name ${packageName}.`;
|
|
2042
2073
|
}
|
|
2043
|
-
|
|
2044
|
-
return `${base} If the package was published to a Personal Workspace or folder feed, add --personal-workspace or --feed.`;
|
|
2045
|
-
}
|
|
2046
|
-
return `${base} If the package was published to a Personal Workspace, add --personal-workspace.`;
|
|
2074
|
+
return `${base} If the package was published to a Personal Workspace or folder feed, add --personal-workspace or --feed.`;
|
|
2047
2075
|
}
|
|
2048
2076
|
async function resolvePackageVersionKey(auth, scope, packageName, packageVersion) {
|
|
2049
2077
|
const config = new Configuration2({
|
|
@@ -2112,9 +2140,7 @@ async function deleteScopedPackageVersion(auth, scope, packageName, packageVersi
|
|
|
2112
2140
|
}
|
|
2113
2141
|
var registerPackagesDeleteCommand = (program) => {
|
|
2114
2142
|
const command = program.command("delete").description("Delete a specific version of a solution package. " + "Use 'solution packages list' to find available package names and versions.").argument("<package-name>", "The name of the package").argument("<package-version>", "The version to delete (e.g. 1.0.0)").addOption(createHiddenDeprecatedTenantOption("-t, --tenant <tenant-name>")).option("--login-validity <minutes>", "Minimum minutes before token expiration to trigger a refresh (default: 10)", parseInt, 10).option("-y, --yes", "Confirm this irreversible operation (required; the CLI never prompts)").option("--personal-workspace", "Delete the package version from the current user's Personal Workspace feed.");
|
|
2115
|
-
|
|
2116
|
-
command.addOption(new Option("--feed <name-or-key>", "Delete the package version from this feed (name or folder key, from 'solution feeds list'). Mutually exclusive with --personal-workspace.").conflicts("personalWorkspace"));
|
|
2117
|
-
}
|
|
2143
|
+
command.addOption(new Option("--feed <name-or-key>", "Delete the package version from this feed (name or folder key, from 'solution feeds list'). Mutually exclusive with --personal-workspace.").conflicts("personalWorkspace"));
|
|
2118
2144
|
command.examples(SOLUTION_PACKAGES_DELETE_EXAMPLES).trackedAction(processContext, async (packageName, packageVersion, options) => {
|
|
2119
2145
|
if (!requireConfirmation(options, `delete package '${packageName}@${packageVersion}'`)) {
|
|
2120
2146
|
return;
|
|
@@ -2376,9 +2402,7 @@ var SOLUTION_PACKAGES_LIST_EXAMPLES = [
|
|
|
2376
2402
|
];
|
|
2377
2403
|
var registerPackagesListCommand = (program) => {
|
|
2378
2404
|
const command = program.command("list").description("List solution packages published to UiPath. Returns package name, version, and publish date. Use the package name and version from this output when running 'solution deploy run'.").addOption(createHiddenDeprecatedTenantOption("-t, --tenant <tenant-name>")).option("-l, --limit <number>", "Number of packages to return", parseLimit, DEFAULT_PAGE_SIZE).option("--offset <number>", "Number of packages to skip", parseOffset, 0).option("--sort-by <column>", "Column to sort by (default: publishDate)", "publishDate").option("--sort-order <direction>", "Sort direction: Ascending or Descending (default: Descending)", "Descending").option("--name <pattern>", "Filter packages by name (server-side substring match)").option("--login-validity <minutes>", "Minimum minutes before token expiration to trigger a refresh (default: 10)", parseInt, 10).option("--personal-workspace", "Only list packages in the current user's Personal Workspace feed.");
|
|
2379
|
-
|
|
2380
|
-
command.addOption(new Option("--feed <name-or-key>", "Only list packages in this feed (name or folder key, from 'solution feeds list'). Mutually exclusive with --personal-workspace.").conflicts("personalWorkspace"));
|
|
2381
|
-
}
|
|
2405
|
+
command.addOption(new Option("--feed <name-or-key>", "Only list packages in this feed (name or folder key, from 'solution feeds list'). Mutually exclusive with --personal-workspace.").conflicts("personalWorkspace"));
|
|
2382
2406
|
command.examples(SOLUTION_PACKAGES_LIST_EXAMPLES).trackedAction(processContext, async (options) => {
|
|
2383
2407
|
const result = await listPackagesAsync({
|
|
2384
2408
|
tenant: options.tenant,
|
|
@@ -3022,6 +3046,14 @@ var registerProjectPublishCommand = (project) => {
|
|
|
3022
3046
|
processContext.exit(1);
|
|
3023
3047
|
return;
|
|
3024
3048
|
}
|
|
3049
|
+
trackShipSucceeded({
|
|
3050
|
+
ship_kind: "publish",
|
|
3051
|
+
target: "solution_package_feed",
|
|
3052
|
+
project_type: "solution_project",
|
|
3053
|
+
command_name: "uip.solution.projects.publish",
|
|
3054
|
+
package_name: options.packageName,
|
|
3055
|
+
package_version: options.packageVersion
|
|
3056
|
+
});
|
|
3025
3057
|
OutputFormatter.success({
|
|
3026
3058
|
Result: RESULTS.Success,
|
|
3027
3059
|
Code: "SolutionProjectPublish",
|
|
@@ -3396,22 +3428,46 @@ function readProjectTypesFromUipx(content) {
|
|
|
3396
3428
|
return typeof project.Type === "string" ? project.Type : undefined;
|
|
3397
3429
|
}).filter((projectType) => projectType !== undefined);
|
|
3398
3430
|
}
|
|
3431
|
+
var MAX_INSPECTABLE_PACKAGE_BYTES = 256 * 1024 * 1024;
|
|
3432
|
+
function isProjectManifestEntry(name) {
|
|
3433
|
+
return name.endsWith(".uipx");
|
|
3434
|
+
}
|
|
3435
|
+
function unzipProjectManifests(archive) {
|
|
3436
|
+
return new Promise((resolve, reject) => {
|
|
3437
|
+
unzip(archive, { filter: (file) => isProjectManifestEntry(file.name) }, (error, entries) => {
|
|
3438
|
+
if (error) {
|
|
3439
|
+
reject(error);
|
|
3440
|
+
return;
|
|
3441
|
+
}
|
|
3442
|
+
resolve(entries);
|
|
3443
|
+
});
|
|
3444
|
+
});
|
|
3445
|
+
}
|
|
3399
3446
|
async function inspectSolutionPackageProjectTypes(packagePath) {
|
|
3400
3447
|
const fs = getFileSystem();
|
|
3448
|
+
const [statError, stats] = await catchError(fs.stat(packagePath));
|
|
3449
|
+
if (statError || !stats) {
|
|
3450
|
+
logger.debug(`[uip.ship.succeeded] Could not stat solution package for project type telemetry: ${statError?.message ?? "no stats"}`);
|
|
3451
|
+
return { projectType: "unknown" };
|
|
3452
|
+
}
|
|
3453
|
+
if (stats.size > MAX_INSPECTABLE_PACKAGE_BYTES) {
|
|
3454
|
+
logger.debug(`[uip.ship.succeeded] Solution package is ${stats.size} bytes, over the ${MAX_INSPECTABLE_PACKAGE_BYTES}-byte inspection cap; skipping project type telemetry.`);
|
|
3455
|
+
return { projectType: "unknown" };
|
|
3456
|
+
}
|
|
3401
3457
|
const [readError, archive] = await catchError(fs.readFile(packagePath));
|
|
3402
3458
|
if (readError || !archive) {
|
|
3403
3459
|
logger.debug(`[uip.ship.succeeded] Could not read solution package for project type telemetry: ${readError?.message ?? "empty archive"}`);
|
|
3404
3460
|
return { projectType: "unknown" };
|
|
3405
3461
|
}
|
|
3406
|
-
const [unzipError, entries] = await catchError(
|
|
3407
|
-
if (unzipError) {
|
|
3408
|
-
logger.debug(`[uip.ship.succeeded] Could not inspect solution package zip: ${unzipError
|
|
3462
|
+
const [unzipError, entries] = await catchError(unzipProjectManifests(new Uint8Array(archive)));
|
|
3463
|
+
if (unzipError || !entries) {
|
|
3464
|
+
logger.debug(`[uip.ship.succeeded] Could not inspect solution package zip: ${unzipError?.message ?? "no entries"}`);
|
|
3409
3465
|
return { projectType: "unknown" };
|
|
3410
3466
|
}
|
|
3411
3467
|
const decoder = new TextDecoder;
|
|
3412
3468
|
const projectTypes = [];
|
|
3413
3469
|
for (const [entryName, entryBytes] of Object.entries(entries)) {
|
|
3414
|
-
if (!entryName
|
|
3470
|
+
if (!isProjectManifestEntry(entryName)) {
|
|
3415
3471
|
continue;
|
|
3416
3472
|
}
|
|
3417
3473
|
const [parseError, parsedTypes] = await catchError(Promise.resolve().then(() => readProjectTypesFromUipx(decoder.decode(entryBytes))));
|
|
@@ -3500,6 +3556,7 @@ var NOT_A_ZIP_INSTRUCTIONS = "Ensure file is a valid .zip solution package from:
|
|
|
3500
3556
|
var UPLOAD_GENERIC_INSTRUCTIONS = "The upload did not complete. Check the error message; if it persists, retry with UIP_LOG_LEVEL=debug and report the output.";
|
|
3501
3557
|
var UPLOAD_REJECTED_INSTRUCTIONS = "The solution feed rejected the package contents; re-packing the same source will reproduce it. Fix the project named in the error detail, or report the error code with your package name.";
|
|
3502
3558
|
var NOT_PACKED_INSTRUCTIONS = "Pack the solution into a .zip first with 'uip solution pack <solution-path> <output-path>', then publish that .zip.";
|
|
3559
|
+
var PROJECT_PACKAGE_INSTRUCTIONS = "A .nupkg is a single project's package (e.g. from 'uip maestro flow pack') and the solution feed does not accept it. Pack the enclosing solution instead: 'uip solution pack <solution-path> <output-dir>', then publish the .zip it writes.";
|
|
3503
3560
|
var suggestedPackOutput = (src) => `${src.replace(/\.(uis|uipx)$/i, "").replace(/[\\/]+$/, "")}.zip`;
|
|
3504
3561
|
var POLL_PENDING_INSTRUCTIONS = "Package may still be pending. Check package state with: uip solution packages list.";
|
|
3505
3562
|
var PUBLISH_INSTRUCTIONS = {
|
|
@@ -3507,6 +3564,7 @@ var PUBLISH_INSTRUCTIONS = {
|
|
|
3507
3564
|
file_not_found: "Ensure the file path is correct. Build a package first with: uip solution pack",
|
|
3508
3565
|
not_a_zip: NOT_A_ZIP_INSTRUCTIONS,
|
|
3509
3566
|
not_packed: NOT_PACKED_INSTRUCTIONS,
|
|
3567
|
+
project_package: PROJECT_PACKAGE_INSTRUCTIONS,
|
|
3510
3568
|
file_read_failed: "",
|
|
3511
3569
|
metadata_rewrite_failed: METADATA_REWRITE_INSTRUCTIONS,
|
|
3512
3570
|
personal_workspace_resolution_failed: "Verify the current user has a Personal Workspace configured in Orchestrator, then retry. To publish to the tenant feed instead, omit --personal-workspace.",
|
|
@@ -3525,10 +3583,8 @@ var EXIT_CODES_BY_REASON = {
|
|
|
3525
3583
|
};
|
|
3526
3584
|
var registerPublishCommand = (program) => {
|
|
3527
3585
|
const command = program.command("publish").description("Publish a solution package (.zip) to the UiPath solution feed. The .zip must be produced by 'solution pack'. By default the package lands in the tenant feed; use --personal-workspace to target the current user's Personal Workspace feed. After publishing, use 'solution deploy run' to deploy it.").addOption(createHiddenDeprecatedTenantOption("-t, --tenant <tenant-name>")).argument("<packagePath>", "Path to a .zip package file produced by 'solution pack'.").option("--personal-workspace", "Upload the package into the current user's Personal Workspace feed instead of the tenant feed. Resolves the workspace folder key from Orchestrator automatically.").option("--package-name <name>", "Publish under this package name instead of the one the .zip was packed with. Rewrites the archive's solutionMetadata.json in memory (with a fresh package version key); the file on disk is left untouched. The bundled project packages keep their own names and versions.").option("--package-version <version>", "Publish under this package version instead of the one the .zip was packed with. Use it to clear a 'version already exists' rejection when you have the packed .zip but not the solution source. Same in-memory rewrite as --package-name.").option("--wait", "Wait until the published package is Ready or Active").option("--timeout <seconds>", "Timeout in seconds for package state polling (default: 360)", (v) => parseInt(v, 10), 360).option("--poll-interval <ms>", "Milliseconds between package state polls (default: 5000)", (v) => parseInt(v, 10), 5000).option("--login-validity <minutes>", "Minimum minutes before token expiration to trigger a refresh (default: 10)", parseInt, 10);
|
|
3528
|
-
|
|
3529
|
-
|
|
3530
|
-
}
|
|
3531
|
-
command.examples(isPreviewBuild() ? [...SOLUTION_PUBLISH_EXAMPLES, SOLUTION_PUBLISH_FEED_EXAMPLE] : SOLUTION_PUBLISH_EXAMPLES).trackedAction(processContext, async (packagePath, options) => {
|
|
3586
|
+
command.addOption(new Option("--feed <name-or-key>", "Publish into a feed by its name or folder key (from 'solution feeds list') instead of the tenant feed. Mutually exclusive with --personal-workspace.").conflicts("personalWorkspace"));
|
|
3587
|
+
command.examples([...SOLUTION_PUBLISH_EXAMPLES, SOLUTION_PUBLISH_FEED_EXAMPLE]).trackedAction(processContext, async (packagePath, options) => {
|
|
3532
3588
|
const result = await publishSolutionAsync(packagePath, {
|
|
3533
3589
|
tenant: options.tenant,
|
|
3534
3590
|
personalWorkspace: options.personalWorkspace,
|
|
@@ -4193,6 +4249,21 @@ var RESOURCE_GET_EXAMPLES = [
|
|
|
4193
4249
|
}
|
|
4194
4250
|
}
|
|
4195
4251
|
},
|
|
4252
|
+
{
|
|
4253
|
+
Description: "When the linked live resource has drifted from the local declaration (matchingOverwriteConfiguration: false), the live spec is returned as liveSpec next to the declared spec",
|
|
4254
|
+
Command: "uip solution resources get 0f8a1c9e-2b47-4d0a-9c31-5e2f6d8a7b10",
|
|
4255
|
+
Output: {
|
|
4256
|
+
Code: "ResourceConfiguration",
|
|
4257
|
+
Data: {
|
|
4258
|
+
key: "0f8a1c9e-2b47-4d0a-9c31-5e2f6d8a7b10",
|
|
4259
|
+
spec: { name: "Approval App", version: "7.0.0" },
|
|
4260
|
+
liveSpec: { name: "Approval App", version: "12.0.0" },
|
|
4261
|
+
resourceOverwrite: {
|
|
4262
|
+
matchingOverwriteConfiguration: false
|
|
4263
|
+
}
|
|
4264
|
+
}
|
|
4265
|
+
}
|
|
4266
|
+
},
|
|
4196
4267
|
{
|
|
4197
4268
|
Description: "Get the configuration plus all dependency configurations",
|
|
4198
4269
|
Command: "uip solution resources get fb39bc38-255e-4fea-93c6-7c7eb44d766f --solution-folder ./my-solution --include-dependencies",
|
|
@@ -4278,7 +4349,10 @@ var registerResourceGetCommand = (resource) => {
|
|
|
4278
4349
|
const includeDeps = options.includeDependencies === true;
|
|
4279
4350
|
const [fetchError, data] = await catchError(includeDeps ? builder.getAllConfigurationsAsync(resourceKey) : builder.getConfigurationAsync(resourceKey));
|
|
4280
4351
|
if (!fetchError) {
|
|
4281
|
-
await refreshArtefactSpecAsync(builder, data, includeDeps);
|
|
4352
|
+
const resource2 = await refreshArtefactSpecAsync(builder, data, includeDeps);
|
|
4353
|
+
if (!includeDeps) {
|
|
4354
|
+
await attachLiveSpecOnMismatchAsync(services, resource2, data);
|
|
4355
|
+
}
|
|
4282
4356
|
await builder.disposeAsync();
|
|
4283
4357
|
OutputFormatter.success({
|
|
4284
4358
|
Result: RESULTS.Success,
|
|
@@ -4352,22 +4426,40 @@ function mapDefinitionToConfiguration(def) {
|
|
|
4352
4426
|
isOverridable: def.isOverridable
|
|
4353
4427
|
};
|
|
4354
4428
|
}
|
|
4429
|
+
async function attachLiveSpecOnMismatchAsync(services, resource, config) {
|
|
4430
|
+
const overwriteData = config.resourceOverwrite;
|
|
4431
|
+
const overwrite = overwriteData?.resourceOverwrite?.overwrite;
|
|
4432
|
+
if (overwriteData?.matchingOverwriteConfiguration !== false || !overwrite || !resource) {
|
|
4433
|
+
return;
|
|
4434
|
+
}
|
|
4435
|
+
const importer = services.container.resolve(Tokens.IImportResourceHelper);
|
|
4436
|
+
const [exportError, exported] = await catchError(importer.exportResourceOverwriteAsync(resource, overwrite));
|
|
4437
|
+
if (exportError || !exported?.spec) {
|
|
4438
|
+
return;
|
|
4439
|
+
}
|
|
4440
|
+
config.liveSpec = exported.spec;
|
|
4441
|
+
}
|
|
4355
4442
|
async function refreshArtefactSpecAsync(builder, data, includeDeps) {
|
|
4356
4443
|
const [loadError, solution] = await catchError(builder.context.loadAsync());
|
|
4357
4444
|
if (loadError || !solution) {
|
|
4358
4445
|
return;
|
|
4359
4446
|
}
|
|
4447
|
+
let requestedResource;
|
|
4360
4448
|
const configs = includeDeps ? data.resources ?? [] : [data];
|
|
4361
4449
|
for (const config of configs) {
|
|
4362
4450
|
if (!config?.key) {
|
|
4363
4451
|
continue;
|
|
4364
4452
|
}
|
|
4365
4453
|
const resource = solution.resources.find((r) => r.key === config.key);
|
|
4454
|
+
if (!includeDeps) {
|
|
4455
|
+
requestedResource = resource;
|
|
4456
|
+
}
|
|
4366
4457
|
if (!resource?.projectKey) {
|
|
4367
4458
|
continue;
|
|
4368
4459
|
}
|
|
4369
4460
|
config.spec = await applyEntryPointEnhancementAsync(builder.context, resource, config.spec ?? {});
|
|
4370
4461
|
}
|
|
4462
|
+
return requestedResource;
|
|
4371
4463
|
}
|
|
4372
4464
|
async function findRemoteReferenceByKey(services, resourceKey) {
|
|
4373
4465
|
const rcs = services.container.resolve(Tokens.IResourceCatalogClientProxy);
|
|
@@ -4396,9 +4488,130 @@ async function findRemoteReferenceByKey(services, resourceKey) {
|
|
|
4396
4488
|
return null;
|
|
4397
4489
|
}
|
|
4398
4490
|
|
|
4491
|
+
// src/services/resource-version-lookup.ts
|
|
4492
|
+
var VERSION_PROPERTY = "version";
|
|
4493
|
+
var LOOKUP_CONCURRENCY = 5;
|
|
4494
|
+
async function resolveResourceVersionsAsync(services, builder, targets) {
|
|
4495
|
+
const versions = new Map;
|
|
4496
|
+
if (targets.length === 0) {
|
|
4497
|
+
return versions;
|
|
4498
|
+
}
|
|
4499
|
+
const resolveKind = await createResourceKindMetadataResolver(services);
|
|
4500
|
+
const versioned = [];
|
|
4501
|
+
for (const target of targets) {
|
|
4502
|
+
const metadata = await resolveKind(target.kind, target.type);
|
|
4503
|
+
if (metadata?.specProperties.has(VERSION_PROPERTY)) {
|
|
4504
|
+
versioned.push(target);
|
|
4505
|
+
}
|
|
4506
|
+
}
|
|
4507
|
+
if (versioned.length === 0) {
|
|
4508
|
+
return versions;
|
|
4509
|
+
}
|
|
4510
|
+
const importer = services.container.resolve(Tokens.IImportResourceHelper);
|
|
4511
|
+
for (let i = 0;i < versioned.length; i += LOOKUP_CONCURRENCY) {
|
|
4512
|
+
const batch = versioned.slice(i, i + LOOKUP_CONCURRENCY);
|
|
4513
|
+
const resolved = await Promise.all(batch.map((target) => target.source === "Local" ? readLocalVersionAsync(builder, target) : readRemoteVersionAsync(importer, target)));
|
|
4514
|
+
for (const [id, version] of resolved) {
|
|
4515
|
+
if (version !== undefined) {
|
|
4516
|
+
versions.set(id, version);
|
|
4517
|
+
}
|
|
4518
|
+
}
|
|
4519
|
+
}
|
|
4520
|
+
return versions;
|
|
4521
|
+
}
|
|
4522
|
+
async function readLocalVersionAsync(builder, target) {
|
|
4523
|
+
const [error, configuration] = await catchError(builder.getConfigurationAsync(target.key));
|
|
4524
|
+
if (error || !configuration) {
|
|
4525
|
+
warnLookupFailed(target, error);
|
|
4526
|
+
return [target.id, undefined];
|
|
4527
|
+
}
|
|
4528
|
+
return [target.id, readVersion(configuration.spec)];
|
|
4529
|
+
}
|
|
4530
|
+
async function readRemoteVersionAsync(importer, target) {
|
|
4531
|
+
const reference = {
|
|
4532
|
+
kind: target.kind,
|
|
4533
|
+
type: target.type,
|
|
4534
|
+
key: target.key,
|
|
4535
|
+
name: target.name,
|
|
4536
|
+
folder: target.folderFullyQualifiedName ? {
|
|
4537
|
+
fullyQualifiedName: target.folderFullyQualifiedName,
|
|
4538
|
+
folderKey: target.folderKey
|
|
4539
|
+
} : undefined
|
|
4540
|
+
};
|
|
4541
|
+
const [error, payload] = await catchError(importer.exportResourceAsync(reference));
|
|
4542
|
+
if (error || !payload) {
|
|
4543
|
+
warnLookupFailed(target, error);
|
|
4544
|
+
return [target.id, undefined];
|
|
4545
|
+
}
|
|
4546
|
+
const definitions = payload.resourceDefinitions ?? [];
|
|
4547
|
+
const primary = definitions.find((d) => d.key === target.key);
|
|
4548
|
+
if (!primary) {
|
|
4549
|
+
logger.warn(`Export for ${target.kind} '${target.name ?? target.key}' returned no definition for that key; version omitted.`);
|
|
4550
|
+
return [target.id, undefined];
|
|
4551
|
+
}
|
|
4552
|
+
return [target.id, readVersion(primary.spec)];
|
|
4553
|
+
}
|
|
4554
|
+
function readVersion(spec) {
|
|
4555
|
+
if (!spec) {
|
|
4556
|
+
return;
|
|
4557
|
+
}
|
|
4558
|
+
let value = spec[VERSION_PROPERTY];
|
|
4559
|
+
if (value === undefined) {
|
|
4560
|
+
for (const [key, candidate] of Object.entries(spec)) {
|
|
4561
|
+
if (key.toLowerCase() === VERSION_PROPERTY) {
|
|
4562
|
+
value = candidate;
|
|
4563
|
+
break;
|
|
4564
|
+
}
|
|
4565
|
+
}
|
|
4566
|
+
}
|
|
4567
|
+
if (value === undefined || value === null || value === "") {
|
|
4568
|
+
return;
|
|
4569
|
+
}
|
|
4570
|
+
return typeof value === "string" ? value : String(value);
|
|
4571
|
+
}
|
|
4572
|
+
function warnLookupFailed(target, error) {
|
|
4573
|
+
logger.warn(`Failed to read the version of ${target.kind} '${target.name ?? target.key}': ${error?.message ?? "no configuration returned"}`);
|
|
4574
|
+
}
|
|
4575
|
+
|
|
4399
4576
|
// src/commands/resource-list.ts
|
|
4577
|
+
var RESOURCE_LIST_EXAMPLES = [
|
|
4578
|
+
{
|
|
4579
|
+
Description: "List every resource the solution holds and every one available in Orchestrator",
|
|
4580
|
+
Command: "uip solution resources list",
|
|
4581
|
+
Output: {
|
|
4582
|
+
Code: "ResourceList",
|
|
4583
|
+
Data: [
|
|
4584
|
+
{
|
|
4585
|
+
Source: "Remote",
|
|
4586
|
+
Key: "0c2b7f04-1d3e-4a55-9f77-2b1a6d9e4c81",
|
|
4587
|
+
Name: "InvoiceQueue",
|
|
4588
|
+
Kind: "queue",
|
|
4589
|
+
Folder: "Shared"
|
|
4590
|
+
}
|
|
4591
|
+
]
|
|
4592
|
+
}
|
|
4593
|
+
},
|
|
4594
|
+
{
|
|
4595
|
+
Description: "Show the version deployed per folder for classic Apps. --include-version reads the live `version` spec property, which is what Orchestrator's Automations > Apps page shows; it costs one extra call per resource, so it is off by default",
|
|
4596
|
+
Command: "uip solution resources list --kind app --source remote --include-version",
|
|
4597
|
+
Output: {
|
|
4598
|
+
Code: "ResourceList",
|
|
4599
|
+
Data: [
|
|
4600
|
+
{
|
|
4601
|
+
Source: "Remote",
|
|
4602
|
+
Key: "a9c9d05f-6d85-41cd-8a54-a479d494bedc",
|
|
4603
|
+
Name: "Validation App",
|
|
4604
|
+
Kind: "app",
|
|
4605
|
+
Folder: "Shared/Validation",
|
|
4606
|
+
FolderKey: "0932a1f4-5cd1-44d4-9f02-cdc92244a6d2",
|
|
4607
|
+
Version: "12.0.0"
|
|
4608
|
+
}
|
|
4609
|
+
]
|
|
4610
|
+
}
|
|
4611
|
+
}
|
|
4612
|
+
];
|
|
4400
4613
|
var registerResourceListCommand = (resource) => {
|
|
4401
|
-
resource.command("list").description("List resources in the solution and/or available in Orchestrator. " + "Shows local solution resources, remote Orchestrator resources, or both.").option("--solution-folder <path>", "Path to the solution root folder (defaults to current directory)").option("--kind <kind>", "Filter by resource kind (e.g. Queue, Asset, Bucket, Process, Connection, App, Index, Trigger, Entity, ChoiceSet). Any kind RCS indexes is accepted.").option("--search <term>", "Filter resources by name").option("--source <source>", "Resource source: all, local, or remote (default: all)", "all").option("--login-validity <minutes>", "Minimum minutes before token expiration to trigger a refresh (default: 10)", parseInt, 10).trackedAction(processContext, async (options) => {
|
|
4614
|
+
resource.command("list").description("List resources in the solution and/or available in Orchestrator. " + "Shows local solution resources, remote Orchestrator resources, or both.").option("--solution-folder <path>", "Path to the solution root folder (defaults to current directory)").option("--kind <kind>", "Filter by resource kind (e.g. Queue, Asset, Bucket, Process, Connection, App, Index, Trigger, Entity, ChoiceSet). Any kind RCS indexes is accepted.").option("--search <term>", "Filter resources by name").option("--source <source>", "Resource source: all, local, or remote (default: all)", "all").option("--include-version", "Also report each resource's version, for the kinds that carry one (app, appVersion, package, process). " + "Remote rows report the version live in the folder, local rows the version the solution pins. " + "Costs one extra call per resource, so it is off by default.").option("--login-validity <minutes>", "Minimum minutes before token expiration to trigger a refresh (default: 10)", parseInt, 10).examples(RESOURCE_LIST_EXAMPLES).trackedAction(processContext, async (options) => {
|
|
4402
4615
|
const solutionPath = options.solutionFolder ?? ".";
|
|
4403
4616
|
const fs = getFileSystem();
|
|
4404
4617
|
const resolvedPath = fs.path.resolve(fs.env.cwd(), solutionPath);
|
|
@@ -4455,7 +4668,7 @@ var registerResourceListCommand = (resource) => {
|
|
|
4455
4668
|
processContext.exit(1);
|
|
4456
4669
|
return;
|
|
4457
4670
|
}
|
|
4458
|
-
const [listError, data] = await catchError(listResources(builder, services, source, normalizedKind, options.search));
|
|
4671
|
+
const [listError, data] = await catchError(listResources(builder, services, source, normalizedKind, options.search, options.includeVersion === true));
|
|
4459
4672
|
await builder.disposeAsync();
|
|
4460
4673
|
if (listError) {
|
|
4461
4674
|
OutputFormatter.error({
|
|
@@ -4473,7 +4686,14 @@ var registerResourceListCommand = (resource) => {
|
|
|
4473
4686
|
});
|
|
4474
4687
|
});
|
|
4475
4688
|
};
|
|
4476
|
-
async function listResources(builder, services, source, kind, search) {
|
|
4689
|
+
async function listResources(builder, services, source, kind, search, includeVersion = false) {
|
|
4690
|
+
const entries = await collectResources(builder, services, source, kind, search);
|
|
4691
|
+
if (includeVersion) {
|
|
4692
|
+
await annotateVersions(services, builder, entries);
|
|
4693
|
+
}
|
|
4694
|
+
return entries;
|
|
4695
|
+
}
|
|
4696
|
+
async function collectResources(builder, services, source, kind, search) {
|
|
4477
4697
|
const includeLocal = source === "local" || source === "all";
|
|
4478
4698
|
const includeRemote = source === "remote" || source === "all";
|
|
4479
4699
|
if (kind) {
|
|
@@ -4491,6 +4711,40 @@ async function listResources(builder, services, source, kind, search) {
|
|
|
4491
4711
|
}
|
|
4492
4712
|
return all;
|
|
4493
4713
|
}
|
|
4714
|
+
function versionLookupId(entry) {
|
|
4715
|
+
return `${entry.Source}|${entry.Key}|${entry.Folder ?? ""}`;
|
|
4716
|
+
}
|
|
4717
|
+
async function annotateVersions(services, builder, entries) {
|
|
4718
|
+
const targets = new Map;
|
|
4719
|
+
for (const entry of entries) {
|
|
4720
|
+
if (entry.Key === "") {
|
|
4721
|
+
continue;
|
|
4722
|
+
}
|
|
4723
|
+
const id = versionLookupId(entry);
|
|
4724
|
+
if (targets.has(id)) {
|
|
4725
|
+
continue;
|
|
4726
|
+
}
|
|
4727
|
+
targets.set(id, {
|
|
4728
|
+
id,
|
|
4729
|
+
key: entry.Key,
|
|
4730
|
+
kind: entry.Kind,
|
|
4731
|
+
type: entry.Type,
|
|
4732
|
+
name: entry.Name,
|
|
4733
|
+
source: entry.Source === "Local" ? "Local" : "Remote",
|
|
4734
|
+
folderFullyQualifiedName: entry.Folder,
|
|
4735
|
+
folderKey: entry.FolderKey
|
|
4736
|
+
});
|
|
4737
|
+
}
|
|
4738
|
+
const versions = await resolveResourceVersionsAsync(services, builder, [
|
|
4739
|
+
...targets.values()
|
|
4740
|
+
]);
|
|
4741
|
+
for (const entry of entries) {
|
|
4742
|
+
const version = versions.get(versionLookupId(entry));
|
|
4743
|
+
if (version !== undefined) {
|
|
4744
|
+
entry.Version = version;
|
|
4745
|
+
}
|
|
4746
|
+
}
|
|
4747
|
+
}
|
|
4494
4748
|
async function listAllRemoteResources(services, search) {
|
|
4495
4749
|
const rcs = services.container.resolve(Tokens.IResourceCatalogClientProxy);
|
|
4496
4750
|
const entries = [];
|
|
@@ -4582,6 +4836,7 @@ var RESOURCE_REFRESH_EXAMPLES = [
|
|
|
4582
4836
|
Created: 2,
|
|
4583
4837
|
Imported: 1,
|
|
4584
4838
|
Skipped: 5,
|
|
4839
|
+
Pruned: 0,
|
|
4585
4840
|
Warnings: [],
|
|
4586
4841
|
Status: "Synced 3 resources (5 already in solution)"
|
|
4587
4842
|
}
|
|
@@ -4595,7 +4850,7 @@ var FAILURE_MESSAGE = {
|
|
|
4595
4850
|
sync_failed: "Failed to refresh resources"
|
|
4596
4851
|
};
|
|
4597
4852
|
var registerResourceRefreshSubcommand = (resource) => {
|
|
4598
|
-
resource.command("refresh").description("Re-scan all projects in the solution and sync resource declarations from their bindings_v2.json files. " + "Creates new resources for bindings not yet in the solution, imports from Orchestrator when a matching resource exists. " + "Run this after editing project bindings to update the solution resource inventory.").option("--solution-folder <path>", "Path to the solution root folder (defaults to current directory)").option("--login-validity <minutes>", "Minimum minutes before token expiration to trigger a refresh (default: 10)", "10").examples(RESOURCE_REFRESH_EXAMPLES).trackedAction(processContext, async (options) => {
|
|
4853
|
+
resource.command("refresh").description("Re-scan all projects in the solution and sync resource declarations from their bindings_v2.json files. " + "Creates new resources for bindings not yet in the solution, imports from Orchestrator when a matching resource exists, " + "and removes duplicate copies of the solution's own projects that earlier runs imported from a deployed folder. " + "Run this after editing project bindings to update the solution resource inventory.").option("--solution-folder <path>", "Path to the solution root folder (defaults to current directory)").option("--login-validity <minutes>", "Minimum minutes before token expiration to trigger a refresh (default: 10)", "10").examples(RESOURCE_REFRESH_EXAMPLES).trackedAction(processContext, async (options) => {
|
|
4599
4854
|
const loginValidityMinutes = options.loginValidity ? parseInt(options.loginValidity, 10) : undefined;
|
|
4600
4855
|
const result = await resourceRefreshAsync(options.solutionFolder ?? ".", { loginValidity: loginValidityMinutes });
|
|
4601
4856
|
if (!result.ok) {
|
|
@@ -4607,6 +4862,7 @@ var registerResourceRefreshSubcommand = (resource) => {
|
|
|
4607
4862
|
processContext.exit(1);
|
|
4608
4863
|
return;
|
|
4609
4864
|
}
|
|
4865
|
+
const prunedNote = result.pruned > 0 ? `, ${result.pruned} duplicates removed` : "";
|
|
4610
4866
|
OutputFormatter.success({
|
|
4611
4867
|
Result: RESULTS.Success,
|
|
4612
4868
|
Code: "ResourceRefresh",
|
|
@@ -4614,8 +4870,9 @@ var registerResourceRefreshSubcommand = (resource) => {
|
|
|
4614
4870
|
Created: result.created,
|
|
4615
4871
|
Imported: result.imported,
|
|
4616
4872
|
Skipped: result.skipped,
|
|
4873
|
+
Pruned: result.pruned,
|
|
4617
4874
|
Warnings: result.warnings,
|
|
4618
|
-
Status: `Synced ${result.created + result.imported} resources (${result.skipped} already in solution)`
|
|
4875
|
+
Status: `Synced ${result.created + result.imported} resources (${result.skipped} already in solution${prunedNote})`
|
|
4619
4876
|
}
|
|
4620
4877
|
});
|
|
4621
4878
|
});
|
|
@@ -4768,23 +5025,22 @@ class RestoreCommandService {
|
|
|
4768
5025
|
this.fs = getFileSystem();
|
|
4769
5026
|
}
|
|
4770
5027
|
async execute(solutionPath, options, context) {
|
|
4771
|
-
const
|
|
4772
|
-
|
|
4773
|
-
if (statError || !stats) {
|
|
4774
|
-
const msg = statError?.message ?? `Solution path does not exist: ${absoluteSolutionPath}`;
|
|
5028
|
+
const check = await checkSolutionPathAsync(this.fs, solutionPath);
|
|
5029
|
+
if (!check.ok) {
|
|
4775
5030
|
OutputFormatter.error({
|
|
4776
5031
|
Result: RESULTS.Failure,
|
|
4777
|
-
Message:
|
|
4778
|
-
Instructions:
|
|
5032
|
+
Message: check.message,
|
|
5033
|
+
Instructions: SOLUTION_PATH_INSTRUCTIONS,
|
|
5034
|
+
ErrorCode: check.errorCode
|
|
4779
5035
|
});
|
|
4780
|
-
if (statError?.stack) {
|
|
5036
|
+
if (check.statError?.stack) {
|
|
4781
5037
|
logger.error("Stack trace:");
|
|
4782
|
-
logger.error(statError.stack);
|
|
5038
|
+
logger.error(check.statError.stack);
|
|
4783
5039
|
}
|
|
4784
5040
|
context.exit(1);
|
|
4785
5041
|
return;
|
|
4786
5042
|
}
|
|
4787
|
-
const isUisFile =
|
|
5043
|
+
const { absoluteSolutionPath, isUisFile } = check;
|
|
4788
5044
|
const solutionName = isUisFile ? this.fs.path.basename(solutionPath, ".uis") : this.fs.path.basename(solutionPath);
|
|
4789
5045
|
logger.info(`
|
|
4790
5046
|
Restoring solution dependencies...
|
|
@@ -4798,7 +5054,7 @@ Restoring solution dependencies...
|
|
|
4798
5054
|
OutputFormatter.error({
|
|
4799
5055
|
Result: RESULTS.Failure,
|
|
4800
5056
|
Message: msg,
|
|
4801
|
-
Instructions: "Check that you are logged in (`uip auth login`) and that the solution path is accessible, then rerun: uip solution restore <solutionPath>"
|
|
5057
|
+
Instructions: carriedInstructions(error) ?? "Check that you are logged in (`uip auth login`) and that the solution path is accessible, then rerun: uip solution restore <solutionPath>"
|
|
4802
5058
|
});
|
|
4803
5059
|
if (error instanceof Error && error.stack) {
|
|
4804
5060
|
logger.error("Stack trace:");
|
|
@@ -4826,9 +5082,14 @@ Restoring solution dependencies...
|
|
|
4826
5082
|
});
|
|
4827
5083
|
}
|
|
4828
5084
|
async executeAsync(solutionPath, options) {
|
|
4829
|
-
const
|
|
4830
|
-
|
|
4831
|
-
|
|
5085
|
+
const check = await checkSolutionPathAsync(this.fs, solutionPath);
|
|
5086
|
+
if (!check.ok) {
|
|
5087
|
+
if (check.statError) {
|
|
5088
|
+
throw check.statError;
|
|
5089
|
+
}
|
|
5090
|
+
return new ToolResult("INTERNAL_ERROR" /* InternalError */, check.message, []);
|
|
5091
|
+
}
|
|
5092
|
+
const { absoluteSolutionPath, isUisFile } = check;
|
|
4832
5093
|
const solutionName = isUisFile ? this.fs.path.basename(solutionPath, ".uis") : this.fs.path.basename(solutionPath);
|
|
4833
5094
|
const packager = await this.ensurePackagerAsync();
|
|
4834
5095
|
let inputPath;
|
|
@@ -4852,7 +5113,7 @@ Restoring solution dependencies...
|
|
|
4852
5113
|
const restoreOptions = new SolutionPackOptions;
|
|
4853
5114
|
restoreOptions.inputPath = inputPath;
|
|
4854
5115
|
restoreOptions.configuration = "Release" /* Release */;
|
|
4855
|
-
restoreOptions.excludeConfiguredSources = false;
|
|
5116
|
+
restoreOptions.excludeConfiguredSources = options.excludeConfiguredSources ?? false;
|
|
4856
5117
|
restoreOptions.package = {
|
|
4857
5118
|
id: solutionName,
|
|
4858
5119
|
version: "1.0.0",
|
|
@@ -4882,11 +5143,12 @@ var SOLUTION_RESTORE_EXAMPLES = [
|
|
|
4882
5143
|
}
|
|
4883
5144
|
];
|
|
4884
5145
|
var registerRestoreCommand = (program) => {
|
|
4885
|
-
program.command("restore").description("Restore NuGet dependencies for every project in a UiPath solution using the authenticated session. Accepts a solution directory (containing .uipx) or a .uis file. Resolves dependencies in place and does not produce a package — run it before 'uip solution pack'.").argument("<solutionPath>", "Path to a solution directory (containing .uipx) or a .uis file").option("--nuget-sources-config-path <path>", "Path to NuGet sources configuration file").option("--feed-folder <name>", "Resolve project dependencies from this Orchestrator folder's library feed instead of every tenant feed. Accepts a folder name or fully qualified path.").option("--login-validity <minutes>", "Minimum minutes before token expiration to trigger a refresh (default: 10)", parseInt, 10).examples(SOLUTION_RESTORE_EXAMPLES).trackedAction(processContext, async (solutionPath, options) => {
|
|
5146
|
+
program.command("restore").description("Restore NuGet dependencies for every project in a UiPath solution using the authenticated session. Accepts a solution directory (containing .uipx) or a .uis file. Resolves dependencies in place and does not produce a package — run it before 'uip solution pack'.").argument("<solutionPath>", "Path to a solution directory (containing .uipx) or a .uis file").option("--nuget-sources-config-path <path>", "Path to NuGet sources configuration file").option("--exclude-configured-sources", "Resolve project dependencies only from the tenant feeds and any --nuget-sources-config-path. Drops the built-in UiPath and nuget.org feeds, so a dependency that is not on those feeds fails to restore.").option("--feed-folder <name>", "Resolve project dependencies from this Orchestrator folder's library feed instead of every tenant feed. Accepts a folder name or fully qualified path.").option("--login-validity <minutes>", "Minimum minutes before token expiration to trigger a refresh (default: 10)", parseInt, 10).examples(SOLUTION_RESTORE_EXAMPLES).trackedAction(processContext, async (solutionPath, options) => {
|
|
4886
5147
|
const service = new RestoreCommandService;
|
|
4887
5148
|
await service.execute(solutionPath, {
|
|
4888
5149
|
loginValidity: options.loginValidity,
|
|
4889
5150
|
nugetSourcesConfigPath: options.nugetSourcesConfigPath,
|
|
5151
|
+
excludeConfiguredSources: options.excludeConfiguredSources,
|
|
4890
5152
|
feedFolder: options.feedFolder
|
|
4891
5153
|
}, processContext);
|
|
4892
5154
|
});
|
|
@@ -4975,7 +5237,7 @@ class ExecutorsFactoryRepository {
|
|
|
4975
5237
|
getProjectExecutorFactory(projectType) {
|
|
4976
5238
|
const factory = this.factoryMap.get(projectType);
|
|
4977
5239
|
if (!factory) {
|
|
4978
|
-
throw new Error(`
|
|
5240
|
+
throw new Error(`Cannot run project type '${projectType}': no runner is installed for it. ` + `Project types that can be run: ${this.registeredTypes.join(", ") || "none"}.`);
|
|
4979
5241
|
}
|
|
4980
5242
|
return factory;
|
|
4981
5243
|
}
|
|
@@ -5046,7 +5308,7 @@ class ProjectRunner {
|
|
|
5046
5308
|
const { projectType, name } = options.project;
|
|
5047
5309
|
if (!this.canRun(projectType)) {
|
|
5048
5310
|
const known = this.runnableTypes.join(", ") || "none";
|
|
5049
|
-
return RunResult.failed("UnsupportedProjectType" /* UnsupportedProjectType */, `
|
|
5311
|
+
return RunResult.failed("UnsupportedProjectType" /* UnsupportedProjectType */, `Cannot run project '${name}' (project type '${projectType}'): ` + `no runner is installed for it. Project types that can be run: ${known}.`);
|
|
5050
5312
|
}
|
|
5051
5313
|
let executor;
|
|
5052
5314
|
try {
|
|
@@ -5093,7 +5355,7 @@ async function ensureExecutorTools(projectTypes) {
|
|
|
5093
5355
|
if (toolVerb) {
|
|
5094
5356
|
neededTools.add(toolVerb);
|
|
5095
5357
|
} else {
|
|
5096
|
-
logger.warn(`No
|
|
5358
|
+
logger.warn(`No installable tool runs project type '${type}'. Run may fail.`);
|
|
5097
5359
|
}
|
|
5098
5360
|
}
|
|
5099
5361
|
for (const toolVerb of neededTools) {
|
|
@@ -5155,11 +5417,11 @@ async function provisionSolutionForDebugAsync(options, deps = {}) {
|
|
|
5155
5417
|
logger.info("Resolving the personal workspace to deploy into...");
|
|
5156
5418
|
const [locationsError, locations] = await catchError(listLocations(authOptions));
|
|
5157
5419
|
if (locationsError) {
|
|
5158
|
-
return
|
|
5420
|
+
return fail(`Could not resolve a personal workspace to deploy into: ${locationsError.message}`);
|
|
5159
5421
|
}
|
|
5160
5422
|
const personal = locations.find((l) => l.isMyPersonalWorkspace);
|
|
5161
5423
|
if (!personal) {
|
|
5162
|
-
return
|
|
5424
|
+
return fail("This account has no personal workspace, so there is nowhere to deploy the solution for a debug run. " + "Ask an administrator to enable a personal workspace, or run with --no-provision-resources — " + "the run then starts without a deployment, and cross-project calls will not resolve.");
|
|
5163
5425
|
}
|
|
5164
5426
|
const version = options.version ?? nextDebugVersion();
|
|
5165
5427
|
const fs = getFileSystem();
|
|
@@ -5173,13 +5435,13 @@ async function provisionSolutionForDebugAsync(options, deps = {}) {
|
|
|
5173
5435
|
outputPath
|
|
5174
5436
|
});
|
|
5175
5437
|
if (!uploaded.ok) {
|
|
5176
|
-
return
|
|
5438
|
+
return fail(uploaded.message);
|
|
5177
5439
|
}
|
|
5178
5440
|
const packageVersionKey = uploaded.packageVersionKey;
|
|
5179
5441
|
logger.info("Provisioning the solution's resources for the run...");
|
|
5180
5442
|
const [builderError, builder] = await catchError(getBuilder(options.solutionDir));
|
|
5181
5443
|
if (builderError) {
|
|
5182
|
-
return
|
|
5444
|
+
return fail(`Could not read the solution: ${builderError.message}`);
|
|
5183
5445
|
}
|
|
5184
5446
|
const sessionId = newSessionId();
|
|
5185
5447
|
const [debugError, debug] = await catchError(builder.debugProjectAsync(personal.key, options.solutionName, sessionId, packageVersionKey, true));
|
|
@@ -5190,11 +5452,11 @@ async function provisionSolutionForDebugAsync(options, deps = {}) {
|
|
|
5190
5452
|
}
|
|
5191
5453
|
await builder.disposeAsync().catch(() => {});
|
|
5192
5454
|
if (debugError) {
|
|
5193
|
-
return
|
|
5455
|
+
return fail(`Provisioning the solution failed: ${debugError.message}`);
|
|
5194
5456
|
}
|
|
5195
5457
|
if (debug.complete === false) {
|
|
5196
5458
|
if (!solutionKey) {
|
|
5197
|
-
return
|
|
5459
|
+
return fail("The cloud is still provisioning the solution, but its key could not be read back from the .uipx, so the provisioning cannot be waited on. Retry the run.");
|
|
5198
5460
|
}
|
|
5199
5461
|
return waitForProvisioningAsync({
|
|
5200
5462
|
solutionKey,
|
|
@@ -5212,7 +5474,7 @@ async function provisionSolutionForDebugAsync(options, deps = {}) {
|
|
|
5212
5474
|
packageVersionKey
|
|
5213
5475
|
};
|
|
5214
5476
|
}
|
|
5215
|
-
function
|
|
5477
|
+
function fail(message) {
|
|
5216
5478
|
return { ok: false, message };
|
|
5217
5479
|
}
|
|
5218
5480
|
async function packAndUploadAsync(args) {
|
|
@@ -5285,13 +5547,13 @@ async function waitForProvisioningAsync(args) {
|
|
|
5285
5547
|
signal: processContext.pollSignal
|
|
5286
5548
|
});
|
|
5287
5549
|
if (poll.outcome !== PollOutcome.Completed || !poll.data) {
|
|
5288
|
-
return
|
|
5550
|
+
return fail(describeIncompleteWait(poll, timeoutMs));
|
|
5289
5551
|
}
|
|
5290
5552
|
const info = poll.data;
|
|
5291
5553
|
if (!isDebugProvisioningSucceeded(info.status)) {
|
|
5292
5554
|
const details = (info.errors ?? []).map((error) => error.resource ? `${error.resource.kind ?? "resource"} '${error.resource.name ?? error.resource.key ?? ""}': ${error.message}` : error.message).join(`
|
|
5293
5555
|
`);
|
|
5294
|
-
return
|
|
5556
|
+
return fail(`Provisioning the solution failed (${info.status}).` + (details ? `
|
|
5295
5557
|
${details}` : ""));
|
|
5296
5558
|
}
|
|
5297
5559
|
logger.info("Solution provisioned.");
|
|
@@ -5337,7 +5599,7 @@ async function runSolutionAsync(options, deps, cancellationToken) {
|
|
|
5337
5599
|
}) : Promise.resolve(undefined)
|
|
5338
5600
|
]);
|
|
5339
5601
|
if (executorOutcome.status === "rejected") {
|
|
5340
|
-
return RunResult.failed("PrepareFailed" /* PrepareFailed */, `Could not load the
|
|
5602
|
+
return RunResult.failed("PrepareFailed" /* PrepareFailed */, `Could not load the runner for project type '${selected.projectType}': ${describeError(executorOutcome.reason)}`);
|
|
5341
5603
|
}
|
|
5342
5604
|
if (provisionOutcome.status === "rejected") {
|
|
5343
5605
|
return RunResult.failed("PrepareFailed" /* PrepareFailed */, `Could not deploy the solution before the run: ${describeError(provisionOutcome.reason)}`);
|
|
@@ -5401,7 +5663,9 @@ async function resolveConnection(folderPath, options, deps) {
|
|
|
5401
5663
|
};
|
|
5402
5664
|
}
|
|
5403
5665
|
function describeError(reason) {
|
|
5404
|
-
|
|
5666
|
+
const message = reason instanceof Error ? reason.message : String(reason);
|
|
5667
|
+
const instructions = carriedInstructions(reason);
|
|
5668
|
+
return instructions ? `${message} ${instructions}` : message;
|
|
5405
5669
|
}
|
|
5406
5670
|
function selectProject(runnable, requested) {
|
|
5407
5671
|
if (requested) {
|
|
@@ -5519,32 +5783,53 @@ function describeJson(value) {
|
|
|
5519
5783
|
// src/commands/upload.ts
|
|
5520
5784
|
var SOLUTION_UPLOAD_EXAMPLES = [
|
|
5521
5785
|
{
|
|
5522
|
-
Description: "Upload a solution directory
|
|
5786
|
+
Description: "Upload a solution directory. Studio Web has no solution with its SolutionId, so it is imported as new and the local .uipx is updated with the assigned id (reported under Data.LocalSolutionIdUpdated).",
|
|
5523
5787
|
Command: "uip solution upload ./my-solution",
|
|
5524
5788
|
Output: {
|
|
5525
5789
|
Code: "SolutionUpload",
|
|
5526
5790
|
Data: {
|
|
5527
5791
|
Status: "Uploaded successfully",
|
|
5792
|
+
Action: "Imported",
|
|
5528
5793
|
SolutionId: "a1b2c3d4-0000-0000-0000-000000000001",
|
|
5529
|
-
DesignerUrl: "https://cloud.uipath.com/myorg/
|
|
5794
|
+
DesignerUrl: "https://cloud.uipath.com/myorg/studio_/designer/p1?solutionId=a1b2c3d4-0000-0000-0000-000000000001",
|
|
5795
|
+
LocalSolutionIdUpdated: {
|
|
5796
|
+
Path: "/workspace/my-solution/my-solution.uipx",
|
|
5797
|
+
From: "00000000-0000-0000-0000-0000000000ff",
|
|
5798
|
+
To: "a1b2c3d4-0000-0000-0000-000000000001"
|
|
5799
|
+
}
|
|
5530
5800
|
}
|
|
5531
5801
|
}
|
|
5532
5802
|
},
|
|
5533
5803
|
{
|
|
5534
|
-
Description: "
|
|
5535
|
-
Command: "uip solution upload ./my-solution
|
|
5804
|
+
Description: "Same command when the solution already exists on Studio Web: it is overwritten in place, with the replaced contents recorded first as a restorable version.",
|
|
5805
|
+
Command: "uip solution upload ./my-solution",
|
|
5536
5806
|
Output: {
|
|
5537
5807
|
Code: "SolutionUpload",
|
|
5538
5808
|
Data: {
|
|
5539
5809
|
Status: "Uploaded successfully",
|
|
5810
|
+
Action: "Overwritten",
|
|
5540
5811
|
SolutionId: "a1b2c3d4-0000-0000-0000-000000000001",
|
|
5541
|
-
DesignerUrl: "https://cloud.uipath.com/myorg/
|
|
5812
|
+
DesignerUrl: "https://cloud.uipath.com/myorg/studio_/designer/p1?solutionId=a1b2c3d4-0000-0000-0000-000000000001",
|
|
5813
|
+
SnapshotCreated: true
|
|
5814
|
+
}
|
|
5815
|
+
}
|
|
5816
|
+
},
|
|
5817
|
+
{
|
|
5818
|
+
Description: "Overwrite without recording a pre-overwrite version. Changes made in Studio Web since the last upload are lost; local files are unaffected.",
|
|
5819
|
+
Command: "uip solution upload ./my-solution --no-snapshot",
|
|
5820
|
+
Output: {
|
|
5821
|
+
Code: "SolutionUpload",
|
|
5822
|
+
Data: {
|
|
5823
|
+
Status: "Uploaded successfully",
|
|
5824
|
+
Action: "Overwritten",
|
|
5825
|
+
SolutionId: "a1b2c3d4-0000-0000-0000-000000000001",
|
|
5826
|
+
DesignerUrl: "https://cloud.uipath.com/myorg/studio_/designer/p1?solutionId=a1b2c3d4-0000-0000-0000-000000000001"
|
|
5542
5827
|
}
|
|
5543
5828
|
}
|
|
5544
5829
|
}
|
|
5545
5830
|
];
|
|
5546
5831
|
var registerUploadCommand = (program) => {
|
|
5547
|
-
program.command("upload").description("Upload a solution to UiPath Studio Web for browser-based editing. Accepts a solution directory, .uipx file, or .uis file.
|
|
5832
|
+
program.command("upload").description("Upload a solution to UiPath Studio Web for browser-based editing. Accepts a solution directory, .uipx file, or .uis file. If Studio Web has no solution with the bundled SolutionId, the solution is imported as new and the local .uipx is updated with the assigned id; otherwise it is overwritten in place, with the replaced contents recorded first as a restorable version (skip with --no-snapshot). The output field Data.Action says which happened: 'Imported' or 'Overwritten'.").argument("<solutionPath>", "Path to a solution directory (containing .uipx), a .uipx file, or a .uis file.").option("--force", "Skip the existence check and overwrite the Studio Web solution named by the bundled SolutionId directly. Not normally needed — an upload overwrites an existing cloud solution on its own. Fails if that solution does not exist. No effect when no SolutionId is bundled (a .uis archive without one): the upload imports as new.").option("--no-snapshot", "Do not record a restorable pre-overwrite version of the cloud solution before replacing its contents. Changes made in Studio Web since the last upload are lost; local files are unaffected. No effect when the upload imports a new solution.").option("--login-validity <minutes>", "Minimum minutes before token expiration to trigger a refresh (default: 10)", parseInt, 10).examples(SOLUTION_UPLOAD_EXAMPLES).trackedAction(processContext, async (solutionPath, options) => {
|
|
5548
5833
|
const fs = getFileSystem();
|
|
5549
5834
|
const [loginError, loginStatus] = await catchError(getLoginStatusAsync({
|
|
5550
5835
|
ensureTokenValidityMinutes: options.loginValidity
|
|
@@ -5604,6 +5889,16 @@ var registerUploadCommand = (program) => {
|
|
|
5604
5889
|
if (cleanupError) {
|
|
5605
5890
|
logger.debug(`Failed to clean up temp dir: ${cleanupError.message}`);
|
|
5606
5891
|
}
|
|
5892
|
+
if (bundleError instanceof StudioWebIncompatibleProjectError) {
|
|
5893
|
+
OutputFormatter.error({
|
|
5894
|
+
Result: RESULTS.Failure,
|
|
5895
|
+
ErrorCode: "invalid_argument",
|
|
5896
|
+
Message: bundleError.message,
|
|
5897
|
+
Instructions: "Convert the listed project(s) to a project.uiproj manifest before uploading: scaffold the accepted layout with 'uip api-workflow init <name>' inside the solution and move Workflow.json (plus entry-points.json and bindings_v2.json) into it, then remove the project.json-based entry from the .uipx. Note that 'uip solution pack' and 'uip solution publish' accept project.json — only Studio Web upload requires project.uiproj."
|
|
5898
|
+
});
|
|
5899
|
+
processContext.exit(1);
|
|
5900
|
+
return;
|
|
5901
|
+
}
|
|
5607
5902
|
outputError(bundleError.message);
|
|
5608
5903
|
return;
|
|
5609
5904
|
}
|
|
@@ -5622,12 +5917,22 @@ var registerUploadCommand = (program) => {
|
|
|
5622
5917
|
outputError(`Failed to read file: ${uisPath}${readError ? ` — ${readError.message}` : ""}`);
|
|
5623
5918
|
return;
|
|
5624
5919
|
}
|
|
5920
|
+
const uisFileName = fs.path.basename(uisPath);
|
|
5921
|
+
if (!solutionId) {
|
|
5922
|
+
solutionId = readBundledSolutionId(fileBuffer);
|
|
5923
|
+
if (solutionId) {
|
|
5924
|
+
logger.info(`Read SolutionId ${solutionId} from ${uisFileName}`);
|
|
5925
|
+
}
|
|
5926
|
+
}
|
|
5625
5927
|
const studioWebConfig = {
|
|
5626
5928
|
baseUrl,
|
|
5627
5929
|
authToken: accessToken,
|
|
5628
5930
|
tenantId
|
|
5629
5931
|
};
|
|
5630
|
-
|
|
5932
|
+
let overwriteExisting = false;
|
|
5933
|
+
if (solutionId && options.force) {
|
|
5934
|
+
overwriteExisting = true;
|
|
5935
|
+
} else if (solutionId) {
|
|
5631
5936
|
const [probeError, exists] = await catchError(solutionExistsOnStudioWeb(studioWebConfig, organizationName, solutionId));
|
|
5632
5937
|
if (probeError) {
|
|
5633
5938
|
const errorMessage = await extractErrorMessage(probeError);
|
|
@@ -5639,18 +5944,23 @@ var registerUploadCommand = (program) => {
|
|
|
5639
5944
|
processContext.exit(1);
|
|
5640
5945
|
return;
|
|
5641
5946
|
}
|
|
5642
|
-
|
|
5947
|
+
overwriteExisting = exists;
|
|
5948
|
+
}
|
|
5949
|
+
const createSnapshot = overwriteExisting && options.snapshot !== false;
|
|
5950
|
+
logger.info(overwriteExisting ? `Overwriting existing Studio Web solution ${solutionId}...` : "Importing as a new Studio Web solution...");
|
|
5951
|
+
const [uploadError, result] = await catchError(overwriteExisting && solutionId ? overwriteSolution(studioWebConfig, organizationName, fileBuffer, uisFileName, solutionId, createSnapshot) : importSolution(studioWebConfig, organizationName, fileBuffer, uisFileName));
|
|
5952
|
+
if (uploadError) {
|
|
5953
|
+
if (overwriteExisting && isSolutionNotFoundError(uploadError)) {
|
|
5643
5954
|
OutputFormatter.error({
|
|
5644
5955
|
Result: RESULTS.Failure,
|
|
5645
|
-
|
|
5646
|
-
|
|
5956
|
+
ErrorCode: "not_found",
|
|
5957
|
+
Message: `Studio Web has no solution with SolutionId ${solutionId}, so there is nothing to overwrite. It may have been deleted, or it may belong to a tenant or user you no longer have access to.`,
|
|
5958
|
+
Instructions: `The SolutionId in ${uipxPath ?? uisPath} is stale. Re-run the upload${options.force ? " without --force" : ""} — the existence check will see the id is gone and import as a new Studio Web solution${uipxPath ? ", updating the local .uipx with the new id" : ""}. Nothing was uploaded.`,
|
|
5959
|
+
Retry: "RetryWillNotFix"
|
|
5647
5960
|
});
|
|
5648
5961
|
processContext.exit(1);
|
|
5649
5962
|
return;
|
|
5650
5963
|
}
|
|
5651
|
-
}
|
|
5652
|
-
const [uploadError, result] = await catchError(options.force ? uploadOrOverwriteSolution(studioWebConfig, organizationName, fileBuffer, solutionId) : importSolution(studioWebConfig, organizationName, fileBuffer, fs.path.basename(uisPath)));
|
|
5653
|
-
if (uploadError) {
|
|
5654
5964
|
const errorMessage = await extractErrorMessage(uploadError);
|
|
5655
5965
|
OutputFormatter.error({
|
|
5656
5966
|
Result: RESULTS.Failure,
|
|
@@ -5660,20 +5970,25 @@ var registerUploadCommand = (program) => {
|
|
|
5660
5970
|
processContext.exit(1);
|
|
5661
5971
|
return;
|
|
5662
5972
|
}
|
|
5663
|
-
const returnedSolutionId =
|
|
5664
|
-
if (returnedSolutionId && uipxPath) {
|
|
5665
|
-
await updateUipxSolutionId(fs, uipxPath, returnedSolutionId);
|
|
5666
|
-
}
|
|
5973
|
+
const { returnedSolutionId, rewroteLocalSolutionId } = await updateLocalSolutionId(fs, result, solutionId, uipxPath);
|
|
5667
5974
|
const projectId = result.projects?.[0]?.id;
|
|
5668
|
-
const
|
|
5669
|
-
const designerUrl = projectId && designerSolutionId ? `${baseUrl}/${organizationName}/studio_/designer/${projectId}?solutionId=${designerSolutionId}` : undefined;
|
|
5975
|
+
const designerUrl = projectId && returnedSolutionId ? `${baseUrl}/${encodeURIComponent(organizationName)}/studio_/designer/${encodeURIComponent(projectId)}?solutionId=${encodeURIComponent(returnedSolutionId)}` : undefined;
|
|
5670
5976
|
OutputFormatter.success({
|
|
5671
5977
|
Result: RESULTS.Success,
|
|
5672
5978
|
Code: "SolutionUpload",
|
|
5673
5979
|
Data: {
|
|
5674
5980
|
Status: "Uploaded successfully",
|
|
5981
|
+
Action: overwriteExisting ? "Overwritten" : "Imported",
|
|
5675
5982
|
SolutionId: returnedSolutionId,
|
|
5676
5983
|
DesignerUrl: designerUrl,
|
|
5984
|
+
...createSnapshot ? { SnapshotCreated: true } : {},
|
|
5985
|
+
...rewroteLocalSolutionId ? {
|
|
5986
|
+
LocalSolutionIdUpdated: {
|
|
5987
|
+
Path: uipxPath,
|
|
5988
|
+
From: solutionId,
|
|
5989
|
+
To: returnedSolutionId
|
|
5990
|
+
}
|
|
5991
|
+
} : {},
|
|
5677
5992
|
Response: result
|
|
5678
5993
|
}
|
|
5679
5994
|
});
|
|
@@ -5696,6 +6011,19 @@ var registerUploadCommand = (program) => {
|
|
|
5696
6011
|
}
|
|
5697
6012
|
});
|
|
5698
6013
|
};
|
|
6014
|
+
async function updateLocalSolutionId(fs, result, solutionId, uipxPath) {
|
|
6015
|
+
const returnedSolutionId = result.id ?? result.solutionId ?? solutionId;
|
|
6016
|
+
const shouldUpdateUipx = uipxPath !== undefined && returnedSolutionId !== undefined && returnedSolutionId !== solutionId;
|
|
6017
|
+
const rewroteLocalSolutionId = shouldUpdateUipx && solutionId !== undefined;
|
|
6018
|
+
if (shouldUpdateUipx && returnedSolutionId && uipxPath) {
|
|
6019
|
+
await updateUipxSolutionId(fs, uipxPath, returnedSolutionId);
|
|
6020
|
+
logger.info(solutionId ? `Studio Web assigned SolutionId ${returnedSolutionId} — the local .uipx now points at it (was ${solutionId}).` : `Studio Web assigned SolutionId ${returnedSolutionId} — the local .uipx now points at it.`);
|
|
6021
|
+
}
|
|
6022
|
+
return { returnedSolutionId, rewroteLocalSolutionId };
|
|
6023
|
+
}
|
|
6024
|
+
function isSolutionNotFoundError(error) {
|
|
6025
|
+
return error instanceof Error && "status" in error && error.status === 404;
|
|
6026
|
+
}
|
|
5699
6027
|
function outputError(message) {
|
|
5700
6028
|
OutputFormatter.error({
|
|
5701
6029
|
Result: RESULTS.Failure,
|
|
@@ -5727,12 +6055,12 @@ var registerCommands = async (program) => {
|
|
|
5727
6055
|
registerResourceCommand(program);
|
|
5728
6056
|
registerDeleteCommand(program);
|
|
5729
6057
|
if (isPreviewBuild()) {
|
|
5730
|
-
const { registerListCommand } = await import("./list-
|
|
6058
|
+
const { registerListCommand } = await import("./list-3mrc9s3f.js");
|
|
5731
6059
|
registerListCommand(program);
|
|
5732
6060
|
}
|
|
5733
|
-
|
|
6061
|
+
registerFeedsCommand(program);
|
|
5734
6062
|
};
|
|
5735
6063
|
|
|
5736
6064
|
export { metadata, registerCommands };
|
|
5737
6065
|
|
|
5738
|
-
//# debugId=
|
|
6066
|
+
//# debugId=58375CF8CAC8E11064756E2164756E21
|