@uipath/solution-tool 1.203.0-preview.212 → 1.204.0-preview.211
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/README.md +2 -2
- package/dist/THIRD-PARTY-NOTICES.md +4 -4
- package/dist/deploy-delete-service-gg00vadc.js +22 -0
- package/dist/deploy-status-service-cqfrvxpz.js +256 -0
- package/dist/deploy.d.ts +11 -4
- package/dist/deploy.js +19 -7
- package/dist/deployment-search-j34w33b2.js +49 -0
- package/dist/{embedded-file-reader-xxqe8vs8.js → embedded-file-reader-rf2g1kfj.js} +2 -2
- package/dist/feed-resolver-31gcvnnk.js +27 -0
- package/dist/feed-scope-failure-fwtdvvrh.js +18 -0
- package/dist/feed-session-sxga31vn.js +17 -0
- package/dist/index.js +27 -18
- package/dist/init.js +10 -7
- package/dist/{list-xjdnbyka.js → list-q4dzkv45.js} +9 -6
- package/dist/pack.js +14 -11
- package/dist/{packager-tool-7qmx64kz.js → packager-tool-00vh1ktg.js} +1022 -1342
- package/dist/packager-tool-1phh2rfx.js +37 -0
- package/dist/{packager-tool-vas0xg5h.js → packager-tool-2cntqvdv.js} +5 -3
- package/dist/{packager-tool-1haahq17.js → packager-tool-3enm2h28.js} +5 -5
- package/dist/{packager-tool-2syrt51a.js → packager-tool-3fv6zc86.js} +1062 -825
- package/dist/{packager-tool-grtmkd54.js → packager-tool-3m36mxn7.js} +16 -74
- package/dist/packager-tool-40vcj4wv.js +172 -0
- package/dist/packager-tool-482eg22x.js +322 -0
- package/dist/{packager-tool-k4mskzww.js → packager-tool-5dzjsw5q.js} +3 -3
- package/dist/{packager-tool-0f0wt9vh.js → packager-tool-5fjp60dk.js} +3071 -2794
- package/dist/{packager-tool-bkwnetqn.js → packager-tool-83rzavkx.js} +383 -88
- package/dist/{packager-tool-f423v35w.js → packager-tool-9hj0cfd0.js} +756 -359
- package/dist/{packager-tool-7znqtw3f.js → packager-tool-9vvdgbay.js} +8 -6
- package/dist/packager-tool-bqre28m1.js +43 -0
- package/dist/packager-tool-f5gq77m5.js +28 -0
- package/dist/{packager-tool-nzghfa48.js → packager-tool-fv7xyzg2.js} +550 -2
- package/dist/{packager-tool-3jrq7smt.js → packager-tool-gb35b4qs.js} +206 -289
- package/dist/{packager-tool-4kdt8mys.js → packager-tool-j30xck4w.js} +1008 -647
- package/dist/packager-tool-ks8spnq8.js +166 -0
- package/dist/packager-tool-q3tgpemc.js +113 -0
- package/dist/packager-tool-qdtjywsk.js +173 -0
- package/dist/{packager-tool-2p5yh1q9.js → packager-tool-qgaqqq41.js} +320 -151
- package/dist/packager-tool-qm24rbae.js +5889 -0
- package/dist/{packager-tool-resolver-fmjr60x8.js → packager-tool-resolver-2cbxyq6k.js} +6 -5
- package/dist/{packager-tool-znakt6yw.js → packager-tool-vh4ng74p.js} +8 -6
- package/dist/packager-tool-vmsxq1pc.js +104 -0
- package/dist/packager-tool-zf3929se.js +237 -0
- package/dist/packager-tool-zns6n8zp.js +837 -0
- package/dist/packager-tool.js +2 -2
- package/dist/prepare-solution-resources-p3gzdecy.js +25 -0
- package/dist/{project-contributions-cvttys34.js → project-contributions-txe8v6sm.js} +12 -9
- package/dist/publish.js +12 -7
- package/dist/resource.js +9 -6
- package/dist/services/activation.d.ts +17 -2
- package/dist/services/deploy-activate-service.d.ts +10 -0
- package/dist/services/deploy-delete-service.d.ts +82 -0
- package/dist/services/deploy-list-service.d.ts +15 -45
- package/dist/services/deploy-run-service.d.ts +36 -23
- package/dist/services/deploy-upgrade-service.d.ts +198 -39
- package/dist/services/deployment-feed-locator.d.ts +50 -12
- package/dist/services/deployment-search-client.d.ts +55 -0
- package/dist/services/deployment-search.d.ts +87 -34
- package/dist/services/deployment-state.d.ts +119 -0
- package/dist/services/feed-resolver.d.ts +31 -0
- package/dist/services/feed-session.d.ts +70 -0
- package/dist/services/folder-helper.d.ts +4 -27
- package/dist/services/http-status.d.ts +9 -0
- package/dist/services/incomplete-feed-sweep.d.ts +77 -0
- package/dist/services/legacy-deployment-search-v3.d.ts +23 -0
- package/dist/services/packages-list-service.d.ts +5 -0
- package/dist/services/pipeline-deploy-poll.d.ts +69 -0
- package/dist/services/publish-service.d.ts +14 -16
- package/dist/services/search-failure.d.ts +27 -0
- package/dist/templates/AGENTS.md +60 -20
- package/dist/tool.js +27 -18
- package/dist/utils/deployment-errors.d.ts +57 -0
- package/package.json +2 -2
- package/dist/packager-tool-jz2wbfjz.js +0 -2275
- package/dist/packager-tool-wtpe9285.js +0 -51960
- package/dist/prepare-solution-resources-wds3r8bq.js +0 -22
- package/dist/services/personal-workspace-deploy.d.ts +0 -49
|
@@ -1,37 +1,81 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
2
|
+
appendDeploymentStateInstructions,
|
|
3
|
+
deploymentNextSteps,
|
|
4
|
+
explainRefusal,
|
|
5
|
+
needsPendingResources,
|
|
6
|
+
readPendingResourcesAsync
|
|
7
|
+
} from "./packager-tool-ks8spnq8.js";
|
|
8
|
+
import {
|
|
9
|
+
INCOMPLETE_SWEEP_ADVICE,
|
|
10
|
+
findDeploymentAcrossFeeds,
|
|
11
|
+
isNameOrFeedRefusal,
|
|
12
|
+
reportIncompleteSweepAsync,
|
|
13
|
+
retryAdvice
|
|
14
|
+
} from "./packager-tool-40vcj4wv.js";
|
|
15
|
+
import {
|
|
16
|
+
DeploymentSearchNeedsUserSessionError,
|
|
17
|
+
MAX_LISTED_DEPLOYMENTS,
|
|
18
|
+
blocksRedeploy,
|
|
19
|
+
deploymentToTerminalPipelineResult,
|
|
20
|
+
findDeploymentBestEffort,
|
|
21
|
+
findDeploymentByName,
|
|
22
|
+
findDeploymentsByName,
|
|
23
|
+
findDeploymentsByPackageName,
|
|
24
|
+
formatExistingDeployments,
|
|
25
|
+
interpretFailedDeployPoll,
|
|
26
|
+
isAuthRefusal,
|
|
27
|
+
isCompletedUninstall,
|
|
28
|
+
isPipelineDeploymentNotFoundError,
|
|
29
|
+
mapOperationStatusToPipelineStatus,
|
|
30
|
+
newestDeploymentRow,
|
|
31
|
+
resolveInstalledFolder,
|
|
32
|
+
searchDeploymentsPage
|
|
33
|
+
} from "./packager-tool-482eg22x.js";
|
|
34
|
+
import {
|
|
35
|
+
Configuration as Configuration2,
|
|
36
|
+
DeploymentOperation,
|
|
37
|
+
DeploymentStatus,
|
|
38
|
+
PipelinesApi
|
|
39
|
+
} from "./packager-tool-qgaqqq41.js";
|
|
40
|
+
import {
|
|
41
|
+
describeFeedResolutionFailureAsync,
|
|
5
42
|
feedScopeInitOverride,
|
|
43
|
+
feedSelectorFlag,
|
|
6
44
|
folderKeyInitOverride,
|
|
7
|
-
listSelectablePublishLocations,
|
|
8
45
|
resolveFeedScope,
|
|
9
46
|
resolvePersonalWorkspace
|
|
10
|
-
} from "./packager-tool-
|
|
47
|
+
} from "./packager-tool-qdtjywsk.js";
|
|
11
48
|
import {
|
|
12
|
-
Configuration
|
|
13
|
-
DeploymentOperationStatus,
|
|
49
|
+
Configuration,
|
|
14
50
|
DeploymentsApi,
|
|
15
51
|
SearchApi,
|
|
16
52
|
SolutionDeploymentAction,
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
53
|
+
WorkflowAction
|
|
54
|
+
} from "./packager-tool-00vh1ktg.js";
|
|
55
|
+
import {
|
|
56
|
+
classifyFeedSession,
|
|
57
|
+
feedReadRefusalForApplication,
|
|
58
|
+
feedSessionRefusal
|
|
59
|
+
} from "./packager-tool-bqre28m1.js";
|
|
60
|
+
import {
|
|
61
|
+
getSolutionAuthContext
|
|
62
|
+
} from "./packager-tool-f5gq77m5.js";
|
|
20
63
|
import {
|
|
21
|
-
getSolutionAuthContext,
|
|
22
64
|
resolveFolder
|
|
23
|
-
} from "./packager-tool-
|
|
65
|
+
} from "./packager-tool-3fv6zc86.js";
|
|
24
66
|
import {
|
|
25
67
|
DEFAULT_PAGE_SIZE,
|
|
26
68
|
PollOutcome,
|
|
27
69
|
catchError,
|
|
28
70
|
extractErrorDetails,
|
|
29
|
-
getFileSystem,
|
|
30
71
|
logger,
|
|
31
72
|
mapPollFailure,
|
|
32
73
|
pollUntil,
|
|
33
74
|
processContext
|
|
34
|
-
} from "./packager-tool-
|
|
75
|
+
} from "./packager-tool-5fjp60dk.js";
|
|
76
|
+
import {
|
|
77
|
+
getFileSystem
|
|
78
|
+
} from "./packager-tool-zf3929se.js";
|
|
35
79
|
import {
|
|
36
80
|
__commonJS,
|
|
37
81
|
__require
|
|
@@ -7087,6 +7131,10 @@ function formatDetailedValidationError(error) {
|
|
|
7087
7131
|
const prefix = resource ? `${resource}: ` : "";
|
|
7088
7132
|
return `${formatErrorCode(error.errorCode)}${prefix}${error.errorText}`;
|
|
7089
7133
|
}
|
|
7134
|
+
function formatDetailedConflictError(error) {
|
|
7135
|
+
const actions = error.conflictFixingActions?.length ? ` (fix: ${error.conflictFixingActions.join(", ")})` : "";
|
|
7136
|
+
return `${formatDetailedValidationError(error)}${actions}`;
|
|
7137
|
+
}
|
|
7090
7138
|
function formatDetailedScheduleError(error) {
|
|
7091
7139
|
return `${formatErrorCode(error.errorCode)}${error.errorText}`;
|
|
7092
7140
|
}
|
|
@@ -7123,13 +7171,24 @@ function formatDeploymentIdentifiers(pipelineDeploymentId, status) {
|
|
|
7123
7171
|
return `Operation: ${identifiers.join(", ")}`;
|
|
7124
7172
|
}
|
|
7125
7173
|
function formatDeploymentFailureDetails(pipelineDeploymentId, status) {
|
|
7174
|
+
return [
|
|
7175
|
+
...formatPipelineErrors(status),
|
|
7176
|
+
formatDeploymentIdentifiers(pipelineDeploymentId, status)
|
|
7177
|
+
].join(`
|
|
7178
|
+
`);
|
|
7179
|
+
}
|
|
7180
|
+
function formatPipelineErrors(status) {
|
|
7126
7181
|
const errorParts = [];
|
|
7127
7182
|
const validationErrors = status.validationResult?.validationErrors;
|
|
7128
7183
|
if (validationErrors?.length) {
|
|
7129
7184
|
errorParts.push(`Validation: ${validationErrors.map(formatDetailedValidationError).join("; ")}`);
|
|
7130
7185
|
}
|
|
7186
|
+
const conflictErrors = status.validationResult?.conflictErrors;
|
|
7187
|
+
if (conflictErrors?.length) {
|
|
7188
|
+
errorParts.push(`Conflicts: ${conflictErrors.map(formatDetailedConflictError).join("; ")}`);
|
|
7189
|
+
}
|
|
7131
7190
|
if (status.conflictFixingErrors?.length) {
|
|
7132
|
-
errorParts.push(`
|
|
7191
|
+
errorParts.push(`Conflict fixing: ${status.conflictFixingErrors.map(formatDetailedValidationError).join("; ")}`);
|
|
7133
7192
|
}
|
|
7134
7193
|
if (status.deploymentScheduleErrors?.length) {
|
|
7135
7194
|
errorParts.push(`Schedule: ${status.deploymentScheduleErrors.map(formatDetailedScheduleError).join("; ")}`);
|
|
@@ -7142,215 +7201,26 @@ function formatDeploymentFailureDetails(pipelineDeploymentId, status) {
|
|
|
7142
7201
|
if (serviceErrors?.length) {
|
|
7143
7202
|
errorParts.push(`Services: ${serviceErrors.join("; ")}`);
|
|
7144
7203
|
}
|
|
7145
|
-
errorParts
|
|
7146
|
-
return errorParts.join(`
|
|
7147
|
-
`);
|
|
7148
|
-
}
|
|
7149
|
-
|
|
7150
|
-
// src/services/deployment-search.ts
|
|
7151
|
-
async function findDeploymentByName(auth, deploymentName, scope) {
|
|
7152
|
-
const api = new SearchApi(new Configuration2({
|
|
7153
|
-
basePath: auth.basePath,
|
|
7154
|
-
accessToken: auth.accessToken
|
|
7155
|
-
}));
|
|
7156
|
-
const take = 50;
|
|
7157
|
-
for (let skip = 0;; ) {
|
|
7158
|
-
const result = await api.searchSearchDeployments22({
|
|
7159
|
-
deploymentsSearchRequest2: {
|
|
7160
|
-
searchTerm: deploymentName,
|
|
7161
|
-
take,
|
|
7162
|
-
skip,
|
|
7163
|
-
operationStatuses: [],
|
|
7164
|
-
activationStatuses: []
|
|
7165
|
-
}
|
|
7166
|
-
}, scope);
|
|
7167
|
-
const values = result.values ?? [];
|
|
7168
|
-
const match = values.find((deployment) => deployment.name === deploymentName);
|
|
7169
|
-
if (match) {
|
|
7170
|
-
return match;
|
|
7171
|
-
}
|
|
7172
|
-
if (values.length < take) {
|
|
7173
|
-
return;
|
|
7174
|
-
}
|
|
7175
|
-
skip += values.length;
|
|
7176
|
-
}
|
|
7177
|
-
}
|
|
7178
|
-
async function findDeploymentsByPackageName(auth, packageName, options = {}) {
|
|
7179
|
-
const api = new SearchApi(new Configuration2({
|
|
7180
|
-
basePath: auth.basePath,
|
|
7181
|
-
accessToken: auth.accessToken
|
|
7182
|
-
}));
|
|
7183
|
-
const take = 50;
|
|
7184
|
-
const maxPages = 20;
|
|
7185
|
-
const matches = [];
|
|
7186
|
-
for (let page = 0, skip = 0;page < maxPages; page++) {
|
|
7187
|
-
const result = await api.searchSearchDeployments22({
|
|
7188
|
-
deploymentsSearchRequest2: {
|
|
7189
|
-
searchTerm: packageName,
|
|
7190
|
-
packageName,
|
|
7191
|
-
take,
|
|
7192
|
-
skip,
|
|
7193
|
-
operationStatuses: [],
|
|
7194
|
-
activationStatuses: []
|
|
7195
|
-
}
|
|
7196
|
-
}, options.scope);
|
|
7197
|
-
const values = result.values ?? [];
|
|
7198
|
-
for (const deployment of values) {
|
|
7199
|
-
if (deployment.packageName === packageName && (options.folderPath === undefined || deployment.folderPath === options.folderPath)) {
|
|
7200
|
-
matches.push(deployment);
|
|
7201
|
-
}
|
|
7202
|
-
}
|
|
7203
|
-
if (values.length < take) {
|
|
7204
|
-
break;
|
|
7205
|
-
}
|
|
7206
|
-
skip += values.length;
|
|
7207
|
-
}
|
|
7208
|
-
return matches;
|
|
7209
|
-
}
|
|
7210
|
-
function blocksRedeploy(deployment) {
|
|
7211
|
-
if (deployment.operationStatus === "InProgress") {
|
|
7212
|
-
return true;
|
|
7213
|
-
}
|
|
7214
|
-
if (deployment.operationStatus === "Successful") {
|
|
7215
|
-
return deployment.operation !== "Uninstall";
|
|
7216
|
-
}
|
|
7217
|
-
if (deployment.operationStatus === "Draft" || deployment.operationStatus === "Failed" || deployment.operationStatus === "FailedRollback") {
|
|
7218
|
-
return deployment.operation !== undefined && deployment.operation !== null && deployment.operation !== "Install";
|
|
7219
|
-
}
|
|
7220
|
-
return false;
|
|
7204
|
+
return errorParts;
|
|
7221
7205
|
}
|
|
7222
|
-
|
|
7223
|
-
|
|
7224
|
-
const described = deployments.slice(0, MAX_LISTED_DEPLOYMENTS).map((deployment) => {
|
|
7225
|
-
const resolved = deployment.installedRootFolderKey ? folderByKey?.get(deployment.installedRootFolderKey) : undefined;
|
|
7226
|
-
const folder = resolved ?? deployment.folderPath ?? "an unknown folder";
|
|
7227
|
-
const current = deployment.currentPackageVersion ?? deployment.packageVersion ?? "an unknown version";
|
|
7228
|
-
const target = deployment.targetPackageVersion ?? deployment.newPackageVersionAvailable;
|
|
7229
|
-
const version = target && target !== current ? `${current} → ${target}` : current;
|
|
7230
|
-
const state = [
|
|
7231
|
-
deployment.operation,
|
|
7232
|
-
deployment.operationStatus,
|
|
7233
|
-
deployment.activationStatus
|
|
7234
|
-
].filter((part) => part !== undefined && part !== null).join("/");
|
|
7235
|
-
return `'${deployment.name}' in ${folder} (version ${version}${state ? `, ${state}` : ""})`;
|
|
7236
|
-
}).join("; ");
|
|
7237
|
-
const remaining = deployments.length - MAX_LISTED_DEPLOYMENTS;
|
|
7238
|
-
return remaining > 0 ? `${described}; and ${remaining} more (see: uip solution deploy list)` : described;
|
|
7206
|
+
function hasServerErrorCode(details, code) {
|
|
7207
|
+
return new RegExp(`"errorCode"\\s*:\\s*"${code}"`).test(details);
|
|
7239
7208
|
}
|
|
7240
|
-
|
|
7241
|
-
|
|
7242
|
-
if (lookupError) {
|
|
7243
|
-
return;
|
|
7244
|
-
}
|
|
7245
|
-
return deployment;
|
|
7209
|
+
function isBareResponse(details) {
|
|
7210
|
+
return !/^\s*[{[]/.test(details);
|
|
7246
7211
|
}
|
|
7247
|
-
|
|
7248
|
-
|
|
7249
|
-
|
|
7250
|
-
}
|
|
7251
|
-
return `${instructions}
|
|
7252
|
-
Deployment '${deployment.name}' exists but is still in Draft state (OperationStatus: ${deployment.operationStatus}, ActivationStatus: ${deployment.activationStatus}). Draft deployments are created by deploy runs that did not complete successfully and cannot be activated. Fix the deploy run validation errors, rerun 'uip solution deploy run', then activate after 'uip solution deploy list' shows the deployment is no longer Draft.`;
|
|
7212
|
+
var DRAFT_VERSION_MISMATCH_CODE = "DraftDeploymentHasDifferentPackageVersion";
|
|
7213
|
+
function leavesPendingDraft(status) {
|
|
7214
|
+
return status === "ValidationFailed" || status === "ConflictFixingError" || status === "DeploymentScheduleError";
|
|
7253
7215
|
}
|
|
7254
|
-
|
|
7255
|
-
|
|
7256
|
-
|
|
7257
|
-
}
|
|
7258
|
-
const response = error.response;
|
|
7259
|
-
if (!response || typeof response !== "object") {
|
|
7260
|
-
return false;
|
|
7261
|
-
}
|
|
7262
|
-
const status = response.status;
|
|
7263
|
-
if (status !== 404) {
|
|
7264
|
-
return false;
|
|
7265
|
-
}
|
|
7266
|
-
const [bodyError, body] = await catchError(response.clone().text());
|
|
7267
|
-
if (bodyError || !body) {
|
|
7268
|
-
return false;
|
|
7269
|
-
}
|
|
7270
|
-
return body.includes("PipelineDeploymentNotFound");
|
|
7216
|
+
function installLandedDespiteStatus(status) {
|
|
7217
|
+
const record = status.deploymentResult?.status;
|
|
7218
|
+
return status.status !== "DeploymentSucceeded" && (record === "SuccessfulInstall" || record === "SuccessfulActivate");
|
|
7271
7219
|
}
|
|
7272
|
-
function
|
|
7273
|
-
|
|
7274
|
-
|
|
7275
|
-
}
|
|
7276
|
-
if (deployment.operationStatus === "Failed" || deployment.operationStatus === "FailedRollback") {
|
|
7277
|
-
return {
|
|
7278
|
-
kind: "DeployFailed",
|
|
7279
|
-
operationStatus: deployment.operationStatus
|
|
7280
|
-
};
|
|
7281
|
-
}
|
|
7282
|
-
if (deployment.operationStatus !== "Successful") {
|
|
7283
|
-
return;
|
|
7284
|
-
}
|
|
7285
|
-
switch (deployment.activationStatus) {
|
|
7286
|
-
case "Active":
|
|
7287
|
-
return { kind: "Active", instanceId: deployment.instanceId };
|
|
7288
|
-
case "NeedsSetupToActivate":
|
|
7289
|
-
return { kind: "NeedsSetupToActivate" };
|
|
7290
|
-
case "ReadyToActivate":
|
|
7291
|
-
case "Inactive":
|
|
7292
|
-
return { kind: "ReadyToActivate" };
|
|
7293
|
-
case "ActivationFailed":
|
|
7294
|
-
return { kind: "ActivationFailed" };
|
|
7295
|
-
default:
|
|
7296
|
-
return;
|
|
7297
|
-
}
|
|
7298
|
-
}
|
|
7299
|
-
function isSettledDeployOperationStatus(operationStatus) {
|
|
7300
|
-
return operationStatus === "Successful" || operationStatus === "Failed" || operationStatus === "FailedRollback";
|
|
7301
|
-
}
|
|
7302
|
-
function mapOperationStatusToPipelineStatus(operationStatus) {
|
|
7303
|
-
switch (operationStatus) {
|
|
7304
|
-
case "Successful":
|
|
7305
|
-
return "DeploymentSucceeded";
|
|
7306
|
-
case "Failed":
|
|
7307
|
-
case "FailedRollback":
|
|
7308
|
-
case "Draft":
|
|
7309
|
-
return "DeploymentFailed";
|
|
7310
|
-
default:
|
|
7311
|
-
return;
|
|
7312
|
-
}
|
|
7313
|
-
}
|
|
7314
|
-
function deploymentToTerminalPipelineResult(deployment) {
|
|
7315
|
-
const status = mapOperationStatusToPipelineStatus(deployment.operationStatus);
|
|
7316
|
-
if (!status) {
|
|
7317
|
-
return;
|
|
7318
|
-
}
|
|
7319
|
-
return {
|
|
7320
|
-
status,
|
|
7321
|
-
validationResult: { validationErrors: [], conflictErrors: [] },
|
|
7322
|
-
deploymentKey: deployment.installDeploymentKey ?? deployment.key,
|
|
7323
|
-
configurationKey: deployment.configurationKey,
|
|
7324
|
-
instanceId: deployment.instanceId
|
|
7325
|
-
};
|
|
7326
|
-
}
|
|
7327
|
-
async function resolveInstalledFolder(deployment, requested, resolveFolderByKey) {
|
|
7328
|
-
const fallback = {
|
|
7329
|
-
folderName: requested.folderName,
|
|
7330
|
-
folderPath: requested.folderPath,
|
|
7331
|
-
requestedMatched: true
|
|
7332
|
-
};
|
|
7333
|
-
const pathFor = (name, path) => path ?? (requested.parentFolderPath ? `${requested.parentFolderPath}/${name}` : name);
|
|
7334
|
-
const folderKey = deployment?.installedRootFolderKey;
|
|
7335
|
-
if (folderKey && resolveFolderByKey) {
|
|
7336
|
-
const [lookupError, folder] = await catchError(resolveFolderByKey(folderKey));
|
|
7337
|
-
if (!lookupError && folder?.name) {
|
|
7338
|
-
return {
|
|
7339
|
-
folderName: folder.name,
|
|
7340
|
-
folderPath: pathFor(folder.name, folder.path ?? deployment?.folderPath ?? undefined),
|
|
7341
|
-
requestedMatched: folder.name === requested.folderName
|
|
7342
|
-
};
|
|
7343
|
-
}
|
|
7344
|
-
}
|
|
7345
|
-
const resolvedName = deployment?.installedRootFolderName;
|
|
7346
|
-
if (resolvedName) {
|
|
7347
|
-
return {
|
|
7348
|
-
folderName: resolvedName,
|
|
7349
|
-
folderPath: pathFor(resolvedName, deployment?.folderPath ?? undefined),
|
|
7350
|
-
requestedMatched: resolvedName === requested.folderName
|
|
7351
|
-
};
|
|
7352
|
-
}
|
|
7353
|
-
return fallback;
|
|
7220
|
+
function explainPipelineStatusAfterInstall(status) {
|
|
7221
|
+
const lines = formatPipelineErrors(status);
|
|
7222
|
+
const errors = lines.length > 0 ? ` ${lines.join(" ")}` : "";
|
|
7223
|
+
return `The deploy pipeline ended at ${status.status} although the install landed (the deployment record reports ${status.deploymentResult?.status}): the errors it attached come from the activation validation the pipeline runs after the install, not from the install.${errors}`;
|
|
7354
7224
|
}
|
|
7355
7225
|
|
|
7356
7226
|
// src/services/activation.ts
|
|
@@ -7359,19 +7229,22 @@ var TERMINAL_STATUSES = new Set([
|
|
|
7359
7229
|
"FailedActivate"
|
|
7360
7230
|
]);
|
|
7361
7231
|
async function activateDeployment(auth, deploymentName, options) {
|
|
7362
|
-
const api = new PipelinesApi(new
|
|
7232
|
+
const api = new PipelinesApi(new Configuration2({
|
|
7363
7233
|
basePath: auth.basePath,
|
|
7364
7234
|
accessToken: auth.accessToken
|
|
7365
7235
|
}));
|
|
7366
7236
|
const [stepsError, steps] = await catchError(api.pipelinesGetPreActivateSteps({ deploymentName }, options.scope));
|
|
7367
7237
|
if (stepsError) {
|
|
7368
|
-
const { message, details } = await extractErrorDetails(stepsError);
|
|
7238
|
+
const { message, details, errorCode, retry, context } = await extractErrorDetails(stepsError);
|
|
7369
7239
|
const deployment = await findDeploymentBestEffort(auth, deploymentName, options.scope);
|
|
7370
7240
|
return {
|
|
7371
7241
|
kind: "RequestFailed",
|
|
7372
7242
|
phase: "preActivateSteps",
|
|
7373
7243
|
message,
|
|
7374
7244
|
details,
|
|
7245
|
+
errorCode,
|
|
7246
|
+
retry,
|
|
7247
|
+
httpStatus: context?.httpStatus,
|
|
7375
7248
|
deployment
|
|
7376
7249
|
};
|
|
7377
7250
|
}
|
|
@@ -7383,13 +7256,16 @@ async function activateDeployment(auth, deploymentName, options) {
|
|
|
7383
7256
|
}
|
|
7384
7257
|
const [activateError, activateResult] = await catchError(api.pipelinesActivate({ deploymentName }, options.scope));
|
|
7385
7258
|
if (activateError) {
|
|
7386
|
-
const { message, details } = await extractErrorDetails(activateError);
|
|
7259
|
+
const { message, details, errorCode, retry, context } = await extractErrorDetails(activateError);
|
|
7387
7260
|
const deployment = await findDeploymentBestEffort(auth, deploymentName, options.scope);
|
|
7388
7261
|
return {
|
|
7389
7262
|
kind: "RequestFailed",
|
|
7390
7263
|
phase: "activate",
|
|
7391
7264
|
message,
|
|
7392
7265
|
details,
|
|
7266
|
+
errorCode,
|
|
7267
|
+
retry,
|
|
7268
|
+
httpStatus: context?.httpStatus,
|
|
7393
7269
|
deployment
|
|
7394
7270
|
};
|
|
7395
7271
|
}
|
|
@@ -7427,44 +7303,6 @@ async function activateDeployment(auth, deploymentName, options) {
|
|
|
7427
7303
|
};
|
|
7428
7304
|
}
|
|
7429
7305
|
|
|
7430
|
-
// src/services/deployment-feed-locator.ts
|
|
7431
|
-
async function nonTenantFeedKeysAsync(options) {
|
|
7432
|
-
const [listError, locations] = await catchError(listSelectablePublishLocations({
|
|
7433
|
-
tenant: options.tenant,
|
|
7434
|
-
loginValidity: options.loginValidity,
|
|
7435
|
-
envFilePath: options.envFilePath
|
|
7436
|
-
}));
|
|
7437
|
-
if (!listError) {
|
|
7438
|
-
const rank = (location) => location.type === "PersonalWorkspace" ? 0 : 1;
|
|
7439
|
-
return [...locations].filter((location) => location.type !== "Tenant" && location.key).sort((a, b) => rank(a) - rank(b)).map((location) => location.key);
|
|
7440
|
-
}
|
|
7441
|
-
const [, personalWorkspace] = await catchError(resolvePersonalWorkspace({
|
|
7442
|
-
tenant: options.tenant,
|
|
7443
|
-
loginValidity: options.loginValidity,
|
|
7444
|
-
envFilePath: options.envFilePath
|
|
7445
|
-
}));
|
|
7446
|
-
return personalWorkspace?.key ? [personalWorkspace.key] : [];
|
|
7447
|
-
}
|
|
7448
|
-
async function findDeploymentAcrossFeeds(auth, deploymentName, options) {
|
|
7449
|
-
const tenantMatch = await findDeploymentByName(auth, deploymentName);
|
|
7450
|
-
if (tenantMatch) {
|
|
7451
|
-
return { deployment: tenantMatch };
|
|
7452
|
-
}
|
|
7453
|
-
const feedKeys = await nonTenantFeedKeysAsync(options);
|
|
7454
|
-
for (const folderKey of feedKeys) {
|
|
7455
|
-
const scope = folderKeyInitOverride(folderKey);
|
|
7456
|
-
const [, deployment] = await catchError(findDeploymentByName(auth, deploymentName, scope));
|
|
7457
|
-
if (deployment) {
|
|
7458
|
-
return { deployment, scope };
|
|
7459
|
-
}
|
|
7460
|
-
}
|
|
7461
|
-
return;
|
|
7462
|
-
}
|
|
7463
|
-
async function findDeploymentAcrossFeedsBestEffort(auth, deploymentName, options) {
|
|
7464
|
-
const [, location] = await catchError(findDeploymentAcrossFeeds(auth, deploymentName, options));
|
|
7465
|
-
return location ?? undefined;
|
|
7466
|
-
}
|
|
7467
|
-
|
|
7468
7306
|
// src/services/deploy-activate-service.ts
|
|
7469
7307
|
var fail = (reason, message, instructions, exitCode = 1) => ({
|
|
7470
7308
|
ok: false,
|
|
@@ -7474,6 +7312,22 @@ var fail = (reason, message, instructions, exitCode = 1) => ({
|
|
|
7474
7312
|
exitCode
|
|
7475
7313
|
});
|
|
7476
7314
|
var AUTH_INSTRUCTIONS = "Run 'uip login' to refresh credentials, then retry activation. If the deployment is in a different tenant, switch with 'uip login tenant set <tenant>'.";
|
|
7315
|
+
async function incompleteSweepRefusalAsync(deploymentName, skipped, result) {
|
|
7316
|
+
const details = `Details: ${result.details}`;
|
|
7317
|
+
if (!isNameOrFeedRefusal(result.errorCode, result.httpStatus)) {
|
|
7318
|
+
return {
|
|
7319
|
+
...fail("request_failed", result.message, `The activation request failed. ${retryAdvice(result.errorCode, result.retry, "Orchestrator")} ${details}`),
|
|
7320
|
+
errorCode: result.errorCode,
|
|
7321
|
+
retry: result.retry
|
|
7322
|
+
};
|
|
7323
|
+
}
|
|
7324
|
+
const report = await reportIncompleteSweepAsync(deploymentName, skipped);
|
|
7325
|
+
return {
|
|
7326
|
+
...fail("request_failed", report.message, `${INCOMPLETE_SWEEP_ADVICE} Without its feed the activation went to the tenant feed, which refused it. ${report.nextStep} ${details} (feed search: ${report.details})`),
|
|
7327
|
+
errorCode: report.errorCode,
|
|
7328
|
+
retry: report.retry
|
|
7329
|
+
};
|
|
7330
|
+
}
|
|
7477
7331
|
async function activateDeploymentAsync(deploymentName, options = {}) {
|
|
7478
7332
|
const [authError, auth] = await catchError(getSolutionAuthContext({
|
|
7479
7333
|
tenant: options.tenant,
|
|
@@ -7483,11 +7337,12 @@ async function activateDeploymentAsync(deploymentName, options = {}) {
|
|
|
7483
7337
|
if (authError) {
|
|
7484
7338
|
return fail("auth_failed", authError.message, AUTH_INSTRUCTIONS);
|
|
7485
7339
|
}
|
|
7486
|
-
const
|
|
7340
|
+
const sweep = await findDeploymentAcrossFeeds(auth, deploymentName, {
|
|
7487
7341
|
tenant: options.tenant,
|
|
7488
7342
|
loginValidity: options.loginValidity,
|
|
7489
7343
|
envFilePath: options.envFilePath
|
|
7490
7344
|
});
|
|
7345
|
+
const location = sweep.location;
|
|
7491
7346
|
const result = await activateDeployment(auth, deploymentName, {
|
|
7492
7347
|
timeoutSeconds: options.timeout ?? 360,
|
|
7493
7348
|
pollIntervalMs: options.pollInterval ?? 5000,
|
|
@@ -7495,8 +7350,20 @@ async function activateDeploymentAsync(deploymentName, options = {}) {
|
|
|
7495
7350
|
scope: location?.scope
|
|
7496
7351
|
});
|
|
7497
7352
|
if (result.kind === "RequestFailed") {
|
|
7498
|
-
const
|
|
7499
|
-
|
|
7353
|
+
const details = `Details: ${result.details}`;
|
|
7354
|
+
const known = result.deployment ?? location?.deployment;
|
|
7355
|
+
if (!known && !sweep.complete) {
|
|
7356
|
+
return incompleteSweepRefusalAsync(deploymentName, sweep.skipped, result);
|
|
7357
|
+
}
|
|
7358
|
+
const pendingResources = known && needsPendingResources(known) ? await readPendingResourcesAsync(new DeploymentsApi(new Configuration({
|
|
7359
|
+
basePath: auth.basePath,
|
|
7360
|
+
accessToken: auth.accessToken
|
|
7361
|
+
})), known.key, location?.scope) : undefined;
|
|
7362
|
+
const feedSelector = feedSelectorFlag(location?.feed);
|
|
7363
|
+
const explanation = known ? explainRefusal(known, pendingResources, feedSelector) : undefined;
|
|
7364
|
+
const instructions = explanation ? [...explanation, details].join(`
|
|
7365
|
+
`) : known ? `Deployment '${known.name}' exists (Operation: ${known.operation}, OperationStatus: ${known.operationStatus}, ActivationStatus: ${known.activationStatus}) but the service refused to activate it. Check it with: uip solution deploy status ${known.key}${feedSelector}. ${details}` : result.phase === "preActivateSteps" ? `Check deployment name. Deploy first with: uip solution deploy run. ${details}` : `Check deployment name matches an existing deployment. ${details}`;
|
|
7366
|
+
return fail("request_failed", result.message, instructions);
|
|
7500
7367
|
}
|
|
7501
7368
|
if (result.kind === "NoInstance") {
|
|
7502
7369
|
return {
|
|
@@ -7570,13 +7437,51 @@ async function resolveFolderInfo(options) {
|
|
|
7570
7437
|
return { key: resolved.key, path: resolved.path };
|
|
7571
7438
|
}
|
|
7572
7439
|
|
|
7440
|
+
// src/services/search-failure.ts
|
|
7441
|
+
async function describeSearchFailureAsync(error, what, session, { userSessionsOnly }) {
|
|
7442
|
+
const details = await extractErrorDetails(error);
|
|
7443
|
+
if (userSessionsOnly && details.context?.httpStatus === 401 && classifyFeedSession(session) === "application") {
|
|
7444
|
+
return {
|
|
7445
|
+
message: `Failed to list ${what}: Solution Search accepts user sessions only, and this session is an external app.`,
|
|
7446
|
+
instructions: `Run it with a user session ('uip login'). A client-credentials login cannot list solution ${what}.`,
|
|
7447
|
+
errorCode: "permission_denied",
|
|
7448
|
+
retry: "RetryWillNotFix",
|
|
7449
|
+
context: details.context
|
|
7450
|
+
};
|
|
7451
|
+
}
|
|
7452
|
+
return {
|
|
7453
|
+
message: `Failed to list ${what}: ${details.message}`,
|
|
7454
|
+
instructions: searchFailureInstructions(details.errorCode, details.retry),
|
|
7455
|
+
errorCode: details.errorCode,
|
|
7456
|
+
retry: details.retry,
|
|
7457
|
+
context: details.context
|
|
7458
|
+
};
|
|
7459
|
+
}
|
|
7460
|
+
function searchFailureInstructions(errorCode, retry) {
|
|
7461
|
+
if (errorCode === "authentication_required") {
|
|
7462
|
+
return "Run 'uip login' to refresh the session, then run the command again.";
|
|
7463
|
+
}
|
|
7464
|
+
if (errorCode === "permission_denied") {
|
|
7465
|
+
return "Use an account that can read Automation Solutions in this tenant.";
|
|
7466
|
+
}
|
|
7467
|
+
if (retry === "RetryLater") {
|
|
7468
|
+
return "Wait a moment, then run the command again.";
|
|
7469
|
+
}
|
|
7470
|
+
return "Check the options and the session ('uip login status --detailed'), then run the command again.";
|
|
7471
|
+
}
|
|
7472
|
+
|
|
7573
7473
|
// src/services/deploy-list-service.ts
|
|
7574
|
-
var fail2 = (reason, message, instructions,
|
|
7474
|
+
var fail2 = (reason, message, instructions, extras = {}) => ({
|
|
7575
7475
|
ok: false,
|
|
7576
7476
|
reason,
|
|
7577
7477
|
message,
|
|
7578
7478
|
instructions,
|
|
7579
|
-
exitCode
|
|
7479
|
+
exitCode: 1,
|
|
7480
|
+
...extras
|
|
7481
|
+
});
|
|
7482
|
+
var refuseFeedRead = (refusal) => fail2("feed_requires_user_session", refusal.message, refusal.instructions, {
|
|
7483
|
+
errorCode: "permission_denied",
|
|
7484
|
+
retry: "RetryWillNotFix"
|
|
7580
7485
|
});
|
|
7581
7486
|
async function listDeploymentsAsync(options = {}) {
|
|
7582
7487
|
const limit = options.limit ?? DEFAULT_PAGE_SIZE;
|
|
@@ -7589,6 +7494,12 @@ async function listDeploymentsAsync(options = {}) {
|
|
|
7589
7494
|
if (authError) {
|
|
7590
7495
|
return fail2("auth_failed", authError.message, authError.message);
|
|
7591
7496
|
}
|
|
7497
|
+
if (options.personalWorkspace) {
|
|
7498
|
+
const refusal = feedReadRefusalForApplication(auth, "Listing the Personal Workspace's deployments");
|
|
7499
|
+
if (refusal) {
|
|
7500
|
+
return refuseFeedRead(refusal);
|
|
7501
|
+
}
|
|
7502
|
+
}
|
|
7592
7503
|
const [folderError, folderPathFilter] = await catchError(resolveParentFolder({
|
|
7593
7504
|
folderPath: options.folderPath,
|
|
7594
7505
|
folderKey: options.folderKey,
|
|
@@ -7607,30 +7518,34 @@ async function listDeploymentsAsync(options = {}) {
|
|
|
7607
7518
|
envFilePath: options.envFilePath
|
|
7608
7519
|
}));
|
|
7609
7520
|
if (scopeError) {
|
|
7610
|
-
|
|
7611
|
-
|
|
7612
|
-
|
|
7613
|
-
|
|
7614
|
-
|
|
7615
|
-
|
|
7616
|
-
|
|
7617
|
-
|
|
7618
|
-
|
|
7619
|
-
|
|
7620
|
-
|
|
7621
|
-
|
|
7622
|
-
|
|
7623
|
-
|
|
7624
|
-
|
|
7521
|
+
const { message, instructions, ...extras } = await describeFeedResolutionFailureAsync(scopeError, options, auth);
|
|
7522
|
+
return fail2("feed_resolution_failed", message, instructions, extras);
|
|
7523
|
+
}
|
|
7524
|
+
if (scope.kind !== "tenant") {
|
|
7525
|
+
const refusal = feedReadRefusalForApplication(auth, `Listing the deployments of the feed '${scope.name ?? scope.folderKey}'`);
|
|
7526
|
+
if (refusal) {
|
|
7527
|
+
return refuseFeedRead(refusal);
|
|
7528
|
+
}
|
|
7529
|
+
}
|
|
7530
|
+
const [listError, result] = await catchError(searchDeploymentsPage(auth, {
|
|
7531
|
+
take: limit,
|
|
7532
|
+
skip: offset,
|
|
7533
|
+
orderByColumn: options.sortBy ?? "startTime",
|
|
7534
|
+
orderByDirection: options.sortOrder ?? "Descending",
|
|
7535
|
+
operationStatuses: options.operationStatuses ?? [],
|
|
7536
|
+
activationStatuses: options.activationStatuses ?? [],
|
|
7537
|
+
...folderPathFilter ? { targetFolderName: folderPathFilter } : {}
|
|
7625
7538
|
}, feedScopeInitOverride(scope)));
|
|
7626
|
-
if (listError) {
|
|
7627
|
-
|
|
7628
|
-
return fail2("list_request_failed", message, `Failed to list deployments. Details: ${details}`);
|
|
7539
|
+
if (listError instanceof DeploymentSearchNeedsUserSessionError) {
|
|
7540
|
+
return fail2("list_request_failed", `Failed to list deployments: ${listError.message}`, "Run it with a user session ('uip login'). A client-credentials login cannot list deployments on this server.", { errorCode: "permission_denied", retry: "RetryWillNotFix" });
|
|
7629
7541
|
}
|
|
7630
|
-
|
|
7631
|
-
|
|
7632
|
-
|
|
7542
|
+
if (listError) {
|
|
7543
|
+
const { message, instructions, ...extras } = await describeSearchFailureAsync(listError, "deployments", auth, {
|
|
7544
|
+
userSessionsOnly: false
|
|
7545
|
+
});
|
|
7546
|
+
return fail2("list_request_failed", message, instructions, extras);
|
|
7633
7547
|
}
|
|
7548
|
+
const values = result.values ?? [];
|
|
7634
7549
|
const deployments = values.map((d) => ({
|
|
7635
7550
|
key: d.key,
|
|
7636
7551
|
installDeploymentKey: d.installDeploymentKey,
|
|
@@ -7720,7 +7635,7 @@ async function describeValidationFailureAsync(deploymentsApi, deploymentKey, dep
|
|
|
7720
7635
|
return messages.length > 0 ? messages.join("; ") : NO_SERVER_REASON;
|
|
7721
7636
|
}
|
|
7722
7637
|
async function describeDeploymentFailureAsync(auth, deployment, deploymentAction, scope, signal) {
|
|
7723
|
-
const deploymentsApi = new DeploymentsApi(new
|
|
7638
|
+
const deploymentsApi = new DeploymentsApi(new Configuration({
|
|
7724
7639
|
basePath: auth.basePath,
|
|
7725
7640
|
accessToken: auth.accessToken
|
|
7726
7641
|
}));
|
|
@@ -7734,79 +7649,96 @@ async function describeDeploymentFailureAsync(auth, deployment, deploymentAction
|
|
|
7734
7649
|
return NO_SERVER_REASON;
|
|
7735
7650
|
}
|
|
7736
7651
|
|
|
7737
|
-
// src/services/
|
|
7738
|
-
|
|
7739
|
-
|
|
7740
|
-
|
|
7741
|
-
|
|
7742
|
-
|
|
7743
|
-
|
|
7744
|
-
|
|
7745
|
-
|
|
7746
|
-
|
|
7747
|
-
|
|
7748
|
-
|
|
7749
|
-
|
|
7750
|
-
|
|
7751
|
-
if (pkg || values.length < take) {
|
|
7752
|
-
break;
|
|
7753
|
-
}
|
|
7754
|
-
skip += values.length;
|
|
7755
|
-
}
|
|
7756
|
-
if (!pkg) {
|
|
7757
|
-
throw new Error(`Package '${params.packageName}' was not found in the target feed. Publish it there first with: uip solution publish <package> --feed <name-or-key>.`);
|
|
7758
|
-
}
|
|
7759
|
-
let packageVersionKey = pkg.packageVersionKey;
|
|
7760
|
-
if (params.packageVersion && params.packageVersion !== pkg.latestVersion) {
|
|
7761
|
-
packageVersionKey = await resolvePackageVersionKey(auth, pkg, params.packageVersion, target.feedFolderKey);
|
|
7762
|
-
}
|
|
7763
|
-
await deploymentsAutoInstall({ basePath: auth.basePath, accessToken: auth.accessToken }, {
|
|
7764
|
-
deploymentName: params.deploymentName,
|
|
7765
|
-
packageVersionKey,
|
|
7766
|
-
newDeploymentKey: crypto.randomUUID(),
|
|
7767
|
-
forceActivate: !params.skipActivate,
|
|
7768
|
-
installationFolderKey: target.locationFolderKey,
|
|
7769
|
-
pathFullyQualifiedName: target.locationFQN,
|
|
7770
|
-
packageAndDeploy: false,
|
|
7771
|
-
solutionRootFolderName: params.solutionRootFolderName,
|
|
7772
|
-
pauseBeforeRun: false,
|
|
7773
|
-
runtimeSetup: { requireServerlessTemplate: true }
|
|
7774
|
-
}, target.feedFolderKey);
|
|
7652
|
+
// src/services/pipeline-deploy-poll.ts
|
|
7653
|
+
var PIPELINE_TERMINAL_STATUSES = new Set([
|
|
7654
|
+
"DeploymentSucceeded",
|
|
7655
|
+
"DeploymentFailed",
|
|
7656
|
+
"ValidationFailed",
|
|
7657
|
+
"ConflictFixingError",
|
|
7658
|
+
"DeploymentScheduleError"
|
|
7659
|
+
]);
|
|
7660
|
+
var NO_ERRORS = {
|
|
7661
|
+
validationResult: { validationErrors: [], conflictErrors: [] },
|
|
7662
|
+
conflictFixingErrors: [],
|
|
7663
|
+
deploymentScheduleErrors: []
|
|
7664
|
+
};
|
|
7665
|
+
function stillInFlight(deploymentKey) {
|
|
7775
7666
|
return {
|
|
7776
|
-
|
|
7777
|
-
|
|
7778
|
-
|
|
7779
|
-
|
|
7667
|
+
status: "DeploymentInProgress",
|
|
7668
|
+
deploymentKey: deploymentKey ?? "",
|
|
7669
|
+
configurationKey: "",
|
|
7670
|
+
...NO_ERRORS
|
|
7780
7671
|
};
|
|
7781
7672
|
}
|
|
7782
|
-
async function
|
|
7783
|
-
const
|
|
7784
|
-
|
|
7785
|
-
|
|
7786
|
-
|
|
7787
|
-
|
|
7788
|
-
|
|
7789
|
-
|
|
7790
|
-
|
|
7791
|
-
|
|
7792
|
-
if (match) {
|
|
7793
|
-
return match.key;
|
|
7794
|
-
}
|
|
7795
|
-
if (page.values.length < take) {
|
|
7796
|
-
throw new Error(`Version '${version}' of package '${pkg.name}' was not found in the target feed (latest is '${pkg.latestVersion}'). List versions with: uip solution packages list, or publish that version to the feed first.`);
|
|
7797
|
-
}
|
|
7798
|
-
skip += page.values.length;
|
|
7673
|
+
async function readOutcomeByKeyAsync(api, deploymentKey) {
|
|
7674
|
+
const [error, info] = await catchError(() => api.pipelinesGetDeploymentByKey({ key: deploymentKey }));
|
|
7675
|
+
if (error || !info) {
|
|
7676
|
+
return;
|
|
7677
|
+
}
|
|
7678
|
+
if (info.deploymentKey !== deploymentKey) {
|
|
7679
|
+
return;
|
|
7680
|
+
}
|
|
7681
|
+
if (info.operation === "Uninstall" || info.operationStatus === "Draft") {
|
|
7682
|
+
return stillInFlight(deploymentKey);
|
|
7799
7683
|
}
|
|
7684
|
+
const status = mapOperationStatusToPipelineStatus(info.operationStatus);
|
|
7685
|
+
if (!status) {
|
|
7686
|
+
return stillInFlight(deploymentKey);
|
|
7687
|
+
}
|
|
7688
|
+
return {
|
|
7689
|
+
status,
|
|
7690
|
+
deploymentKey: info.deploymentKey,
|
|
7691
|
+
configurationKey: "",
|
|
7692
|
+
...NO_ERRORS,
|
|
7693
|
+
synthesizedOperation: info.operation
|
|
7694
|
+
};
|
|
7695
|
+
}
|
|
7696
|
+
async function pollPipelineDeploymentAsync(api, auth, options) {
|
|
7697
|
+
let observedDeploymentKey;
|
|
7698
|
+
return pollUntil({
|
|
7699
|
+
fn: async () => {
|
|
7700
|
+
try {
|
|
7701
|
+
const status = await api.pipelinesGetPipelineDeploymentStatus({ pipelineDeploymentId: options.pipelineDeploymentId }, options.scope);
|
|
7702
|
+
if (status.deploymentKey) {
|
|
7703
|
+
observedDeploymentKey = status.deploymentKey;
|
|
7704
|
+
}
|
|
7705
|
+
return status;
|
|
7706
|
+
} catch (err) {
|
|
7707
|
+
if (!await isPipelineDeploymentNotFoundError(err)) {
|
|
7708
|
+
throw err;
|
|
7709
|
+
}
|
|
7710
|
+
if (!observedDeploymentKey) {
|
|
7711
|
+
return stillInFlight(undefined);
|
|
7712
|
+
}
|
|
7713
|
+
const byKey = await readOutcomeByKeyAsync(api, observedDeploymentKey);
|
|
7714
|
+
if (byKey) {
|
|
7715
|
+
return byKey;
|
|
7716
|
+
}
|
|
7717
|
+
const deployment = await findDeploymentBestEffort(auth, options.deploymentName, options.scope, { deploymentKey: observedDeploymentKey });
|
|
7718
|
+
if (!deployment || deployment.operationStatus === "Draft") {
|
|
7719
|
+
return stillInFlight(observedDeploymentKey);
|
|
7720
|
+
}
|
|
7721
|
+
const synthesized = deploymentToTerminalPipelineResult(deployment);
|
|
7722
|
+
if (!synthesized) {
|
|
7723
|
+
return stillInFlight(observedDeploymentKey);
|
|
7724
|
+
}
|
|
7725
|
+
return {
|
|
7726
|
+
...synthesized,
|
|
7727
|
+
synthesizedOperation: deployment.operation
|
|
7728
|
+
};
|
|
7729
|
+
}
|
|
7730
|
+
},
|
|
7731
|
+
until: (result) => PIPELINE_TERMINAL_STATUSES.has(result.status),
|
|
7732
|
+
getStatus: (result) => result.status,
|
|
7733
|
+
label: `deployment ${options.pipelineDeploymentId}`,
|
|
7734
|
+
logPrefix: options.logPrefix,
|
|
7735
|
+
timeoutMs: options.timeoutMs,
|
|
7736
|
+
intervalMs: options.intervalMs,
|
|
7737
|
+
signal: options.signal
|
|
7738
|
+
});
|
|
7800
7739
|
}
|
|
7801
7740
|
|
|
7802
7741
|
// src/services/deploy-run-service.ts
|
|
7803
|
-
var TERMINAL_STATUSES2 = new Set([
|
|
7804
|
-
"DeploymentSucceeded",
|
|
7805
|
-
"DeploymentFailed",
|
|
7806
|
-
"ValidationFailed",
|
|
7807
|
-
"ConflictFixingError",
|
|
7808
|
-
"DeploymentScheduleError"
|
|
7809
|
-
]);
|
|
7810
7742
|
var PROCESS_RESOURCE_MAPPING_ERROR = /(?:AgentProcessResource|FlowProcessResource)\s*->\s*ReleaseDto/;
|
|
7811
7743
|
var PROCESS_KEY_MAPPING_ERROR = /Destination Member:\s*ProcessKey|Invalid argument 'Key'/;
|
|
7812
7744
|
function buildDeploymentFailureInstructions(instructions, pipelineDeploymentId) {
|
|
@@ -7865,7 +7797,13 @@ async function deployRunAsync(options) {
|
|
|
7865
7797
|
}
|
|
7866
7798
|
let feedScope = { kind: "tenant" };
|
|
7867
7799
|
if (options.personalWorkspace || options.feed !== undefined) {
|
|
7868
|
-
|
|
7800
|
+
if (options.personalWorkspace) {
|
|
7801
|
+
const refusal = refuseFeedForSession(auth, "Deploying into a Personal Workspace");
|
|
7802
|
+
if (refusal) {
|
|
7803
|
+
return refusal;
|
|
7804
|
+
}
|
|
7805
|
+
}
|
|
7806
|
+
const [scopeError, scope2] = await catchError(resolveFeedScope({
|
|
7869
7807
|
personalWorkspace: options.personalWorkspace,
|
|
7870
7808
|
feed: options.feed,
|
|
7871
7809
|
tenant: options.tenant,
|
|
@@ -7875,27 +7813,47 @@ async function deployRunAsync(options) {
|
|
|
7875
7813
|
if (scopeError) {
|
|
7876
7814
|
return fail3(options.feed !== undefined ? "feed_resolution_failed" : "personal_workspace_deploy_failed", scopeError.message, "List the feeds you can deploy from with: uip solution feeds list.");
|
|
7877
7815
|
}
|
|
7878
|
-
feedScope =
|
|
7816
|
+
feedScope = scope2;
|
|
7817
|
+
if (feedScope.kind !== "tenant") {
|
|
7818
|
+
const refusal = refuseFeedForSession(auth, `Deploying into the feed '${feedScope.name ?? feedScope.folderKey}'`);
|
|
7819
|
+
if (refusal) {
|
|
7820
|
+
return refusal;
|
|
7821
|
+
}
|
|
7822
|
+
}
|
|
7879
7823
|
}
|
|
7880
|
-
const blocked = await guardExistingDeployment(auth, options, feedScope);
|
|
7824
|
+
const blocked = adviseForApplication(auth, await guardExistingDeployment(auth, options, feedScope)) ?? adviseForApplication(auth, await guardPendingDraft(auth, options, feedScope));
|
|
7881
7825
|
if (blocked) {
|
|
7882
7826
|
return blocked;
|
|
7883
7827
|
}
|
|
7828
|
+
let parentFolderPath;
|
|
7884
7829
|
if (feedScope.kind !== "tenant") {
|
|
7885
|
-
|
|
7886
|
-
|
|
7887
|
-
|
|
7888
|
-
|
|
7889
|
-
|
|
7890
|
-
|
|
7891
|
-
|
|
7892
|
-
|
|
7893
|
-
|
|
7894
|
-
|
|
7895
|
-
|
|
7896
|
-
|
|
7830
|
+
const [locationError, location] = await catchError(resolveFeedDeployLocation(feedScope, options));
|
|
7831
|
+
if (locationError) {
|
|
7832
|
+
return fail3("location_outside_feed", locationError.message, "Pass a --parent-folder-path / --parent-folder-key inside the feed folder, or omit it to deploy into the feed folder itself.");
|
|
7833
|
+
}
|
|
7834
|
+
parentFolderPath = location.folderFQN || undefined;
|
|
7835
|
+
} else {
|
|
7836
|
+
const [folderError, resolved] = await catchError(resolveParentFolder({
|
|
7837
|
+
folderPath: options.parentFolderPath,
|
|
7838
|
+
folderKey: options.parentFolderKey,
|
|
7839
|
+
personalWorkspace: options.personalWorkspace,
|
|
7840
|
+
tenant: options.tenant,
|
|
7841
|
+
loginValidity: options.loginValidity,
|
|
7842
|
+
envFilePath: options.envFilePath
|
|
7843
|
+
}));
|
|
7844
|
+
if (folderError) {
|
|
7845
|
+
return fail3("folder_resolution_failed", folderError.message, "Provide a valid --parent-folder-path, --parent-folder-key, or --personal-workspace.");
|
|
7846
|
+
}
|
|
7847
|
+
parentFolderPath = resolved;
|
|
7897
7848
|
}
|
|
7898
|
-
const
|
|
7849
|
+
const scope = feedScopeInitOverride(feedScope);
|
|
7850
|
+
const feedFields = feedScope.kind === "tenant" ? { feedKind: feedScope.kind } : {
|
|
7851
|
+
feedKind: feedScope.kind,
|
|
7852
|
+
deploymentName: options.deploymentName,
|
|
7853
|
+
packageName: options.packageName,
|
|
7854
|
+
packageVersion: options.packageVersion
|
|
7855
|
+
};
|
|
7856
|
+
const configuration = new Configuration2({
|
|
7899
7857
|
basePath: auth.basePath,
|
|
7900
7858
|
accessToken: auth.accessToken
|
|
7901
7859
|
});
|
|
@@ -7924,43 +7882,33 @@ async function deployRunAsync(options) {
|
|
|
7924
7882
|
solutionRootFolderName: options.folderName,
|
|
7925
7883
|
folderFullyQualifiedName: parentFolderPath,
|
|
7926
7884
|
body: configBody
|
|
7927
|
-
}));
|
|
7928
|
-
if (installError) {
|
|
7929
|
-
|
|
7930
|
-
|
|
7885
|
+
}, scope));
|
|
7886
|
+
if (installError || !installResult?.pipelineDeploymentId) {
|
|
7887
|
+
if (!installError) {
|
|
7888
|
+
return fail3(installFailureReason(feedScope), "The install request returned no pipeline deployment.", installFailureHint(feedScope, options));
|
|
7889
|
+
}
|
|
7890
|
+
const detail = await extractErrorDetails(installError);
|
|
7891
|
+
if (hasServerErrorCode(detail.details, DRAFT_VERSION_MISMATCH_CODE)) {
|
|
7892
|
+
return fail3("pending_draft", detail.message, `${pendingDraftHint(feedScope, options)} Details: ${detail.details}`, { errorCode: detail.errorCode, retry: detail.retry });
|
|
7893
|
+
}
|
|
7894
|
+
return fail3(installFailureReason(feedScope), detail.message, `${installFailureHint(feedScope, options)} Details: ${detail.details}`, { errorCode: detail.errorCode, retry: detail.retry });
|
|
7931
7895
|
}
|
|
7932
7896
|
const pipelineDeploymentId = installResult.pipelineDeploymentId;
|
|
7933
|
-
const pollResult = await
|
|
7934
|
-
|
|
7935
|
-
|
|
7936
|
-
|
|
7937
|
-
pipelineDeploymentId
|
|
7938
|
-
});
|
|
7939
|
-
} catch (err) {
|
|
7940
|
-
if (!await isPipelineDeploymentNotFoundError(err)) {
|
|
7941
|
-
throw err;
|
|
7942
|
-
}
|
|
7943
|
-
const deployment = await findDeploymentBestEffort(auth, options.deploymentName);
|
|
7944
|
-
const synthesized = deployment ? deploymentToTerminalPipelineResult(deployment) : undefined;
|
|
7945
|
-
if (!synthesized) {
|
|
7946
|
-
throw err;
|
|
7947
|
-
}
|
|
7948
|
-
return synthesized;
|
|
7949
|
-
}
|
|
7950
|
-
},
|
|
7951
|
-
until: (result) => TERMINAL_STATUSES2.has(result.status),
|
|
7952
|
-
getStatus: (result) => result.status,
|
|
7953
|
-
label: `deployment ${pipelineDeploymentId}`,
|
|
7954
|
-
logPrefix: "deploy",
|
|
7897
|
+
const pollResult = await pollPipelineDeploymentAsync(api, auth, {
|
|
7898
|
+
pipelineDeploymentId,
|
|
7899
|
+
deploymentName: options.deploymentName,
|
|
7900
|
+
scope,
|
|
7955
7901
|
timeoutMs: (options.timeout ?? 360) * 1000,
|
|
7956
7902
|
intervalMs: options.pollInterval ?? 5000,
|
|
7957
|
-
signal: options.signal
|
|
7903
|
+
signal: options.signal,
|
|
7904
|
+
logPrefix: "deploy"
|
|
7958
7905
|
});
|
|
7959
7906
|
const folderPath = parentFolderPath ? `${parentFolderPath}/${options.folderName}` : options.folderName;
|
|
7960
7907
|
if (pollResult.outcome !== PollOutcome.Completed || !pollResult.data) {
|
|
7961
|
-
const
|
|
7908
|
+
const recoveryKey = pollResult.data?.deploymentKey || undefined;
|
|
7909
|
+
const recoveryDeployment = recoveryKey ? await findDeploymentBestEffort(auth, options.deploymentName, scope, { deploymentKey: recoveryKey }) : undefined;
|
|
7962
7910
|
const recovery = interpretFailedDeployPoll(recoveryDeployment);
|
|
7963
|
-
|
|
7911
|
+
const recoveredSuccess = async () => {
|
|
7964
7912
|
const resolvedFolder2 = await resolveInstalledFolder(recoveryDeployment, {
|
|
7965
7913
|
folderName: options.folderName,
|
|
7966
7914
|
folderPath,
|
|
@@ -7968,45 +7916,64 @@ async function deployRunAsync(options) {
|
|
|
7968
7916
|
}, makeFolderKeyResolver(options));
|
|
7969
7917
|
return {
|
|
7970
7918
|
ok: true,
|
|
7919
|
+
...feedFields,
|
|
7971
7920
|
status: "DeploymentSucceeded",
|
|
7972
|
-
deploymentKey: recoveryDeployment?.
|
|
7921
|
+
deploymentKey: recoveryDeployment?.key,
|
|
7973
7922
|
pipelineDeploymentId,
|
|
7974
|
-
instanceId:
|
|
7923
|
+
instanceId: recoveryDeployment?.instanceId == null ? undefined : recoveryDeployment.instanceId,
|
|
7975
7924
|
folderName: resolvedFolder2.folderName,
|
|
7976
7925
|
folderPath: resolvedFolder2.folderPath,
|
|
7977
|
-
...resolvedFolder2.requestedMatched ? {} : { requestedFolderName: options.folderName }
|
|
7926
|
+
...resolvedFolder2.requestedMatched ? {} : { requestedFolderName: options.folderName }
|
|
7927
|
+
};
|
|
7928
|
+
};
|
|
7929
|
+
if (recovery?.kind === "Active") {
|
|
7930
|
+
return {
|
|
7931
|
+
...await recoveredSuccess(),
|
|
7978
7932
|
activationStatus: "SuccessfulActivate"
|
|
7979
7933
|
};
|
|
7980
7934
|
}
|
|
7981
|
-
if (recovery?.kind === "NeedsSetupToActivate") {
|
|
7982
|
-
return
|
|
7983
|
-
|
|
7984
|
-
|
|
7985
|
-
|
|
7935
|
+
if (recoveryDeployment && (recovery?.kind === "NeedsSetupToActivate" || recovery?.kind === "ReadyToActivate")) {
|
|
7936
|
+
return {
|
|
7937
|
+
...await recoveredSuccess(),
|
|
7938
|
+
...await describeInstalledNotLiveAsync(auth, recoveryDeployment, scope, feedScope)
|
|
7939
|
+
};
|
|
7986
7940
|
}
|
|
7987
7941
|
if (recovery?.kind === "ActivationFailed") {
|
|
7988
7942
|
return fail3("activation_failed_post_deploy", `Deployment '${options.deploymentName}' completed but activation failed.`, `Inspect activation details in Orchestrator, then retry with: uip solution deploy activate ${options.deploymentName}.`, { pipelineDeploymentId });
|
|
7989
7943
|
}
|
|
7990
7944
|
if (recovery?.kind === "DeployFailed") {
|
|
7991
|
-
|
|
7945
|
+
const reason2 = recoveryDeployment ? await describeDeploymentFailureAsync(auth, recoveryDeployment, SolutionDeploymentAction.Install, scope, options.signal) : NO_SERVER_REASON;
|
|
7946
|
+
return fail3("deploy_failed", `Deployment '${options.deploymentName}' failed (OperationStatus: ${recovery.operationStatus}): ${reason2}`, `${inspectHint(pipelineDeploymentId, feedScope)} Fix the underlying issue and retry: uip solution deploy run.`, { pipelineDeploymentId });
|
|
7992
7947
|
}
|
|
7993
7948
|
const { reason, message, exitCode, errorCode, retry } = mapPollFailure(pollResult, "Deployment");
|
|
7994
|
-
return fail3(reason, message, `Deployment may still be in progress.
|
|
7949
|
+
return fail3(reason, message, `Deployment may still be in progress. ${followHint(pipelineDeploymentId, feedScope)}`, { exitCode, pipelineDeploymentId, errorCode, retry });
|
|
7995
7950
|
}
|
|
7996
7951
|
const finalStatus = pollResult.data;
|
|
7997
7952
|
if (!finalStatus) {
|
|
7998
|
-
return fail3("no_final_status", "Deployment did not return a final status.",
|
|
7999
|
-
}
|
|
8000
|
-
|
|
8001
|
-
|
|
8002
|
-
|
|
8003
|
-
|
|
7953
|
+
return fail3("no_final_status", "Deployment did not return a final status.", followHint(pipelineDeploymentId, feedScope), { pipelineDeploymentId });
|
|
7954
|
+
}
|
|
7955
|
+
const landedDespiteStatus = installLandedDespiteStatus(finalStatus);
|
|
7956
|
+
if (finalStatus.status !== "DeploymentSucceeded" && !landedDespiteStatus) {
|
|
7957
|
+
const classification = finalStatus.status === "ValidationFailed" || finalStatus.status === "ConflictFixingError" ? {
|
|
7958
|
+
errorCode: "invalid_argument",
|
|
7959
|
+
retry: "RetryWillNotFix"
|
|
7960
|
+
} : {};
|
|
7961
|
+
const fromPipeline = formatDeploymentFailureDetails(pipelineDeploymentId, finalStatus);
|
|
7962
|
+
const details = finalStatus.synthesizedOperation !== undefined && finalStatus.deploymentKey ? await describeSynthesizedFailureAsync(auth, finalStatus.deploymentKey, scope, options.signal, fromPipeline) : fromPipeline;
|
|
7963
|
+
const draftNote = leavesPendingDraft(finalStatus.status) ? `
|
|
7964
|
+
Nothing was installed. The rejected attempt is kept as a pending draft${finalStatus.deploymentKey ? ` (${finalStatus.deploymentKey})` : ""}: fix the configuration and re-run this command, and the server re-validates that draft with the new file (settings and resource links the file does not name keep the values the draft was rejected with).${finalStatus.deploymentKey ? ` When this was an update of an existing deployment, you can discard the draft and upgrade the live version instead: uip solution deploy upgrade ${finalStatus.deploymentKey} --discard-draft --version ${options.packageVersion}` : ""}` : "";
|
|
7965
|
+
return fail3("deploy_failed", `Deployment failed with status: ${finalStatus.status}`, `${buildDeploymentFailureInstructions(details, pipelineDeploymentId)}${draftNote}`, { pipelineDeploymentId, ...classification });
|
|
7966
|
+
}
|
|
7967
|
+
const resolvedFolder = await resolveInstalledFolder(await findDeploymentBestEffort(auth, options.deploymentName, scope, {
|
|
7968
|
+
deploymentKey: finalStatus.deploymentKey || undefined
|
|
7969
|
+
}), {
|
|
8004
7970
|
folderName: options.folderName,
|
|
8005
7971
|
folderPath,
|
|
8006
7972
|
parentFolderPath
|
|
8007
7973
|
}, makeFolderKeyResolver(options));
|
|
8008
7974
|
const baseSuccess = {
|
|
8009
7975
|
ok: true,
|
|
7976
|
+
...feedFields,
|
|
8010
7977
|
status: finalStatus.status,
|
|
8011
7978
|
deploymentKey: finalStatus.deploymentKey,
|
|
8012
7979
|
pipelineDeploymentId,
|
|
@@ -8015,21 +7982,44 @@ async function deployRunAsync(options) {
|
|
|
8015
7982
|
folderPath: resolvedFolder.folderPath,
|
|
8016
7983
|
...resolvedFolder.requestedMatched ? {} : { requestedFolderName: options.folderName }
|
|
8017
7984
|
};
|
|
7985
|
+
if (landedDespiteStatus) {
|
|
7986
|
+
const described = await describeInstalledRunAsync(auth, options.deploymentName, finalStatus, scope, feedScope);
|
|
7987
|
+
return {
|
|
7988
|
+
...baseSuccess,
|
|
7989
|
+
...described,
|
|
7990
|
+
nextSteps: [
|
|
7991
|
+
explainPipelineStatusAfterInstall(finalStatus),
|
|
7992
|
+
...described.nextSteps ?? []
|
|
7993
|
+
]
|
|
7994
|
+
};
|
|
7995
|
+
}
|
|
8018
7996
|
if (options.skipActivate) {
|
|
8019
7997
|
return { ...baseSuccess, activationStatus: "Skipped" };
|
|
8020
7998
|
}
|
|
8021
7999
|
const activationResult = await activateDeployment(auth, options.deploymentName, {
|
|
8022
8000
|
timeoutSeconds: options.timeout ?? 360,
|
|
8023
8001
|
pollIntervalMs: options.pollInterval ?? 5000,
|
|
8024
|
-
signal: options.signal
|
|
8002
|
+
signal: options.signal,
|
|
8003
|
+
scope
|
|
8025
8004
|
});
|
|
8026
8005
|
if (activationResult.kind === "RequestFailed") {
|
|
8006
|
+
const refused = activationResult.deployment;
|
|
8007
|
+
if (refused && refused.key === finalStatus.deploymentKey && refused.operationStatus === "Successful" && refused.activationStatus === "NeedsSetupToActivate") {
|
|
8008
|
+
return {
|
|
8009
|
+
...baseSuccess,
|
|
8010
|
+
...await describeInstalledNotLiveAsync(auth, refused, scope, feedScope)
|
|
8011
|
+
};
|
|
8012
|
+
}
|
|
8027
8013
|
const fallback = activationResult.phase === "preActivateSteps" ? `Deployment '${options.deploymentName}' was deployed successfully but pre-activate steps lookup failed. Retry activation with: uip solution deploy activate ${options.deploymentName}. Details: ${activationResult.details}` : `Deployment '${options.deploymentName}' was deployed successfully but activation failed. Retry activation with: uip solution deploy activate ${options.deploymentName}. Details: ${activationResult.details}`;
|
|
8028
|
-
|
|
8014
|
+
const pendingResources = refused && needsPendingResources(refused) ? await readPendingResourcesAsync(new DeploymentsApi(new Configuration({
|
|
8015
|
+
basePath: auth.basePath,
|
|
8016
|
+
accessToken: auth.accessToken
|
|
8017
|
+
})), refused.key, scope) : undefined;
|
|
8018
|
+
return fail3("activation_request_failed", activationResult.message, appendDeploymentStateInstructions(fallback, refused, pendingResources, feedSelectorFlag(feedScope)), { pipelineDeploymentId });
|
|
8029
8019
|
}
|
|
8030
8020
|
if (activationResult.kind === "PollFailed") {
|
|
8031
8021
|
const { message, exitCode, errorCode, retry } = mapPollFailure(activationResult.poll, "Activation");
|
|
8032
|
-
return fail3("activation_poll_failed", message, `Deployment '${options.deploymentName}' was deployed successfully but activation polling did not complete. Activation may still be in progress; check with:
|
|
8022
|
+
return fail3("activation_poll_failed", message, `Deployment '${options.deploymentName}' was deployed successfully but activation polling did not complete. Activation may still be in progress; check with: ${listCommand(feedScope)}.`, { exitCode, pipelineDeploymentId, errorCode, retry });
|
|
8033
8023
|
}
|
|
8034
8024
|
if (activationResult.kind === "ActivationFailed") {
|
|
8035
8025
|
return fail3("activation_failed", `Activation failed with status: ${activationResult.status}`, activationResult.errorMessage, { pipelineDeploymentId });
|
|
@@ -8039,22 +8029,115 @@ async function deployRunAsync(options) {
|
|
|
8039
8029
|
const finalInstanceId = activationInstanceId == null ? baseSuccess.instanceId : activationInstanceId;
|
|
8040
8030
|
return { ...baseSuccess, activationStatus, instanceId: finalInstanceId };
|
|
8041
8031
|
}
|
|
8042
|
-
|
|
8043
|
-
|
|
8044
|
-
|
|
8045
|
-
|
|
8032
|
+
async function describeInstalledRunAsync(auth, deploymentName, finalStatus, scope, feedScope) {
|
|
8033
|
+
const key = finalStatus.deploymentKey || undefined;
|
|
8034
|
+
const row = key ? await findDeploymentBestEffort(auth, deploymentName, scope, {
|
|
8035
|
+
deploymentKey: key
|
|
8036
|
+
}) : undefined;
|
|
8037
|
+
if (row) {
|
|
8038
|
+
return describeInstalledNotLiveAsync(auth, row, scope, feedScope);
|
|
8039
|
+
}
|
|
8040
|
+
const record = finalStatus.deploymentResult;
|
|
8041
|
+
if (record?.status === "SuccessfulActivate") {
|
|
8042
|
+
return { activationStatus: "SuccessfulActivate" };
|
|
8043
|
+
}
|
|
8044
|
+
const actions = record?.actions ?? [];
|
|
8045
|
+
const activationStatus = actions.includes("SetupActivation") ? "NeedsSetupToActivate" : actions.includes("Activate") ? "ReadyToActivate" : "Inactive";
|
|
8046
|
+
return describeInstalledNotLiveAsync(auth, {
|
|
8047
|
+
name: deploymentName,
|
|
8048
|
+
key,
|
|
8049
|
+
operation: record?.operation ?? finalStatus.synthesizedOperation,
|
|
8050
|
+
operationStatus: "Successful",
|
|
8051
|
+
activationStatus,
|
|
8052
|
+
packageVersion: record?.packageVersion,
|
|
8053
|
+
currentPackageVersion: record?.packageVersion
|
|
8054
|
+
}, scope, feedScope);
|
|
8055
|
+
}
|
|
8056
|
+
async function describeInstalledNotLiveAsync(auth, deployment, scope, feedScope) {
|
|
8057
|
+
const pendingResources = needsPendingResources(deployment) && deployment.key ? await readPendingResourcesAsync(new DeploymentsApi(new Configuration({
|
|
8058
|
+
basePath: auth.basePath,
|
|
8059
|
+
accessToken: auth.accessToken
|
|
8060
|
+
})), deployment.key, scope) : undefined;
|
|
8061
|
+
const nextSteps = deploymentNextSteps(deployment, pendingResources, feedSelectorFlag(feedScope));
|
|
8062
|
+
return {
|
|
8063
|
+
activationStatus: deployment.activationStatus ?? "Inactive",
|
|
8064
|
+
...pendingResources ? { pendingResources } : {},
|
|
8065
|
+
...nextSteps && nextSteps.length > 0 ? { nextSteps } : {}
|
|
8066
|
+
};
|
|
8067
|
+
}
|
|
8068
|
+
function inspectHint(pipelineDeploymentId, scope) {
|
|
8069
|
+
if (scope.kind === "tenant") {
|
|
8070
|
+
return `Inspect failure details with: uip solution deploy status ${pipelineDeploymentId}.`;
|
|
8046
8071
|
}
|
|
8047
|
-
|
|
8048
|
-
|
|
8072
|
+
return `Inspect it with: ${listCommand(scope)}.`;
|
|
8073
|
+
}
|
|
8074
|
+
function followHint(pipelineDeploymentId, scope) {
|
|
8075
|
+
if (scope.kind === "tenant") {
|
|
8076
|
+
return `Check status with: uip solution deploy status ${pipelineDeploymentId}.`;
|
|
8049
8077
|
}
|
|
8050
|
-
|
|
8051
|
-
|
|
8052
|
-
|
|
8053
|
-
|
|
8054
|
-
|
|
8055
|
-
}
|
|
8078
|
+
return `Check it with: ${listCommand(scope)}.`;
|
|
8079
|
+
}
|
|
8080
|
+
function listCommand(scope) {
|
|
8081
|
+
if (scope.kind === "personal") {
|
|
8082
|
+
return "uip solution deploy list --personal-workspace";
|
|
8056
8083
|
}
|
|
8057
|
-
|
|
8084
|
+
if (scope.kind === "folder") {
|
|
8085
|
+
return `uip solution deploy list --feed ${scope.folderKey ?? "<name-or-key>"}`;
|
|
8086
|
+
}
|
|
8087
|
+
return "uip solution deploy list";
|
|
8088
|
+
}
|
|
8089
|
+
function adviseForApplication(auth, failure) {
|
|
8090
|
+
if (!failure || classifyFeedSession(auth) !== "application") {
|
|
8091
|
+
return failure;
|
|
8092
|
+
}
|
|
8093
|
+
logger.warn(`${failure.message} Deploying anyway: under an external app (client-credentials) login this check only warns, so a pipeline keeps the outcome it had before the check could run. ${failure.instructions}`);
|
|
8094
|
+
return;
|
|
8095
|
+
}
|
|
8096
|
+
function refuseFeedForSession(session, action) {
|
|
8097
|
+
const verdict = classifyFeedSession(session);
|
|
8098
|
+
if (verdict === "user") {
|
|
8099
|
+
return;
|
|
8100
|
+
}
|
|
8101
|
+
const { message, instructions } = feedSessionRefusal(verdict, action, "deploy the package to the tenant feed");
|
|
8102
|
+
return fail3("feed_requires_user_session", message, instructions, {
|
|
8103
|
+
errorCode: "permission_denied",
|
|
8104
|
+
retry: "RetryWillNotFix"
|
|
8105
|
+
});
|
|
8106
|
+
}
|
|
8107
|
+
async function describeSynthesizedFailureAsync(auth, deploymentKey, scope, signal, fromPipeline) {
|
|
8108
|
+
const deploymentsApi = new DeploymentsApi(new Configuration({
|
|
8109
|
+
basePath: auth.basePath,
|
|
8110
|
+
accessToken: auth.accessToken
|
|
8111
|
+
}));
|
|
8112
|
+
const reason = await describeValidationFailureAsync(deploymentsApi, deploymentKey, SolutionDeploymentAction.Install, scope, signal);
|
|
8113
|
+
return reason === NO_SERVER_REASON ? fromPipeline : `Validation: ${reason}
|
|
8114
|
+
${fromPipeline}`;
|
|
8115
|
+
}
|
|
8116
|
+
function pendingDraftHint(scope, options) {
|
|
8117
|
+
const cause = `'${options.deploymentName}' has a pending draft for another version: an earlier attempt that never completed, usually one rejected by validation. To retry that draft with a corrected configuration, re-run this command with the draft's --package-version (the message names it).`;
|
|
8118
|
+
if (scope.kind === "folder") {
|
|
8119
|
+
return `${cause} To discard it and upgrade the live deployment to ${options.packageVersion} instead: uip solution deploy upgrade <deployment-key> --discard-draft --version ${options.packageVersion} (Key from ${listCommand(scope)}).`;
|
|
8120
|
+
}
|
|
8121
|
+
const workspace = scope.kind === "personal" ? " --personal-workspace" : "";
|
|
8122
|
+
return `${cause} To discard it and upgrade the live deployment to ${options.packageVersion} instead: uip solution deploy upgrade --name ${options.deploymentName} --package-name ${options.packageName}${workspace} --discard-draft --version ${options.packageVersion} (under an external app, pass the deployment's key in place of --name and --package-name).`;
|
|
8123
|
+
}
|
|
8124
|
+
function installFailureReason(scope) {
|
|
8125
|
+
if (scope.kind === "personal") {
|
|
8126
|
+
return "personal_workspace_deploy_failed";
|
|
8127
|
+
}
|
|
8128
|
+
if (scope.kind === "folder") {
|
|
8129
|
+
return "feed_deploy_failed";
|
|
8130
|
+
}
|
|
8131
|
+
return "install_request_failed";
|
|
8132
|
+
}
|
|
8133
|
+
function installFailureHint(scope, options) {
|
|
8134
|
+
if (scope.kind === "personal") {
|
|
8135
|
+
return "Ensure the package was published to your Personal Workspace first: uip solution publish <package> --personal-workspace.";
|
|
8136
|
+
}
|
|
8137
|
+
if (scope.kind === "folder") {
|
|
8138
|
+
return `Ensure the package was published to the feed first: uip solution publish <package> --feed ${options.feed ?? "<name-or-key>"}.`;
|
|
8139
|
+
}
|
|
8140
|
+
return "Check package name/version with: uip solution packages list.";
|
|
8058
8141
|
}
|
|
8059
8142
|
function scopeLabel(scope) {
|
|
8060
8143
|
if (scope.kind === "personal") {
|
|
@@ -8084,7 +8167,7 @@ function buildDeploymentExistsInstructions(options) {
|
|
|
8084
8167
|
}
|
|
8085
8168
|
}
|
|
8086
8169
|
parts.push("To see every deployment and its state, run: uip solution deploy list.");
|
|
8087
|
-
parts.push(scope.kind === "tenant" ? "To create a second, separate deployment anyway, re-run this command with --yes." : "To create a second, separate deployment anyway, re-run with --yes — Orchestrator may still refuse it while the existing deployment is running or in a failed state.");
|
|
8170
|
+
parts.push(scope.kind === "tenant" ? "To create a second, separate deployment anyway, re-run this command with --yes and a different --name: re-using an existing deployment's name updates that deployment in place instead." : "To create a second, separate deployment anyway, re-run with --yes and a different --name (re-using an existing deployment's name updates that deployment in place) — Orchestrator may still refuse it while the existing deployment is running or in a failed state.");
|
|
8088
8171
|
if (existing.length === 0) {
|
|
8089
8172
|
parts.unshift(`A deployment of '${packageName}' already exists in ${scopeLabel(scope)}.`);
|
|
8090
8173
|
} else if (options.includeInventory) {
|
|
@@ -8107,6 +8190,31 @@ async function resolveDeploymentFolders(deployments, options) {
|
|
|
8107
8190
|
}));
|
|
8108
8191
|
return resolved;
|
|
8109
8192
|
}
|
|
8193
|
+
async function guardPendingDraft(auth, options, scope) {
|
|
8194
|
+
const [lookupError, rows] = await catchError(findDeploymentsByName(auth, options.deploymentName, feedScopeInitOverride(scope)));
|
|
8195
|
+
if (lookupError instanceof DeploymentSearchNeedsUserSessionError || lookupError && classifyFeedSession(auth) === "application" && isAuthRefusal(lookupError)) {
|
|
8196
|
+
return;
|
|
8197
|
+
}
|
|
8198
|
+
if (lookupError) {
|
|
8199
|
+
logger.warn(`Skipped the pending-draft check for '${options.deploymentName}': the deployment lookup failed (${lookupError.message}). Deploying anyway — if this deployment has a pending draft for another version, the server may install that draft's version instead of ${options.packageVersion}. Check it with 'uip solution deploy list'.`);
|
|
8200
|
+
return;
|
|
8201
|
+
}
|
|
8202
|
+
const draft = newestDeploymentRow(rows);
|
|
8203
|
+
if (draft?.operationStatus !== "Draft") {
|
|
8204
|
+
return;
|
|
8205
|
+
}
|
|
8206
|
+
const draftVersion = draft.targetPackageVersion ?? draft.packageVersion;
|
|
8207
|
+
if (draft.operation === "ActivationSetup") {
|
|
8208
|
+
return fail3("pending_draft", `Deployment '${options.deploymentName}' has an unfinished Setup activation (${draft.key}): the Orchestrator "Setup activation" wizard was started on version ${draftVersion ?? "<version>"} and not completed.`, `While that draft exists the server would run it in place of this deploy, or refuse a different version. Finish the setup in Orchestrator (Solutions, the deployment, Setup activation); or discard it — in Orchestrator (Delete draft), or with uip solution deploy upgrade ${draft.key} --discard-draft${draft.currentPackageVersion ? ` --version ${draft.currentPackageVersion}` : ""} — then re-run this command.`, { errorCode: "invalid_argument", retry: "RetryWillNotFix" });
|
|
8209
|
+
}
|
|
8210
|
+
if (!draftVersion || draftVersion.toLowerCase() === options.packageVersion.toLowerCase()) {
|
|
8211
|
+
return;
|
|
8212
|
+
}
|
|
8213
|
+
const liveVersion = draft.currentPackageVersion;
|
|
8214
|
+
const discard = draft.operation === "Install" || !liveVersion ? "delete it in Orchestrator (Delete draft)" : `discard it — in Orchestrator (Delete draft), or with uip solution deploy upgrade ${draft.key} --discard-draft --version ${liveVersion}, which also re-applies the live version unchanged —`;
|
|
8215
|
+
return fail3("pending_draft", `Deployment '${options.deploymentName}' has a pending draft (${draft.key}) for version ${draftVersion}: an earlier attempt that never completed, usually one rejected by validation, or one still running.`, `The server reuses that draft for the name, so this request would not deploy ${options.packageVersion}: asking for the live version installs the draft's ${draftVersion} instead, and any other version is refused. To retry the draft with a corrected configuration, re-run with --package-version ${draftVersion}.
|
|
8216
|
+
To deploy ${options.packageVersion} instead, ${discard} then re-run this command.`, { errorCode: "invalid_argument", retry: "RetryWillNotFix" });
|
|
8217
|
+
}
|
|
8110
8218
|
async function guardExistingDeployment(auth, options, scope) {
|
|
8111
8219
|
if (options.yes) {
|
|
8112
8220
|
return;
|
|
@@ -8114,8 +8222,12 @@ async function guardExistingDeployment(auth, options, scope) {
|
|
|
8114
8222
|
const [lookupError, deployments] = await catchError(findDeploymentsByPackageName(auth, options.packageName, {
|
|
8115
8223
|
scope: feedScopeInitOverride(scope)
|
|
8116
8224
|
}));
|
|
8225
|
+
if (lookupError instanceof DeploymentSearchNeedsUserSessionError) {
|
|
8226
|
+
logger.warn(`Skipped the already-deployed check for '${options.packageName}': this server's deployment search reads user sessions only, and this is an application (client-credentials) login. Deploying anyway — if a deployment already exists this creates a second one in its own folder. Check what is already deployed in the Orchestrator Solutions UI, or sign in with 'uip login' and re-run.`);
|
|
8227
|
+
return;
|
|
8228
|
+
}
|
|
8117
8229
|
if (lookupError) {
|
|
8118
|
-
logger.warn(`Skipped the already-deployed check for '${options.packageName}': the deployment lookup failed (${lookupError.message}). Deploying anyway — if a deployment already exists this creates a second one in its own folder.
|
|
8230
|
+
logger.warn(`Skipped the already-deployed check for '${options.packageName}': the deployment lookup failed (${lookupError.message}). Deploying anyway — if a deployment already exists this creates a second one in its own folder. 'uip solution deploy list' reads the same search, so it may fail the same way. Check what is already deployed in the Orchestrator Solutions UI, or re-run once the lookup answers.`);
|
|
8119
8231
|
return;
|
|
8120
8232
|
}
|
|
8121
8233
|
const existing = deployments.filter(blocksRedeploy);
|
|
@@ -8129,85 +8241,6 @@ async function guardExistingDeployment(auth, options, scope) {
|
|
|
8129
8241
|
requestedDeploymentName: options.deploymentName
|
|
8130
8242
|
}));
|
|
8131
8243
|
}
|
|
8132
|
-
async function deployRunViaFeedAsync(auth, scope, options) {
|
|
8133
|
-
const [locationError, location] = await catchError(resolveFeedDeployLocation(scope, options));
|
|
8134
|
-
if (locationError) {
|
|
8135
|
-
return fail3("location_outside_feed", locationError.message, "Pass a --parent-folder-path / --parent-folder-key inside the feed folder, or omit it to deploy into the feed folder itself.");
|
|
8136
|
-
}
|
|
8137
|
-
const isPersonal = scope.kind === "personal";
|
|
8138
|
-
const [deployError, result] = await catchError(deployToFeed(auth, {
|
|
8139
|
-
feedFolderKey: scope.folderKey,
|
|
8140
|
-
locationFolderKey: location.folderKey,
|
|
8141
|
-
locationFQN: location.folderFQN
|
|
8142
|
-
}, {
|
|
8143
|
-
packageName: options.packageName,
|
|
8144
|
-
packageVersion: options.packageVersion,
|
|
8145
|
-
deploymentName: options.deploymentName,
|
|
8146
|
-
solutionRootFolderName: options.folderName,
|
|
8147
|
-
skipActivate: options.skipActivate
|
|
8148
|
-
}));
|
|
8149
|
-
if (deployError) {
|
|
8150
|
-
const { message, details, context, parsedErrors } = await extractErrorDetails(deployError);
|
|
8151
|
-
if (isExistingDeploymentConflict(context?.httpStatus, message, details, parsedErrors)) {
|
|
8152
|
-
const [, blockers] = await catchError(findDeploymentsByPackageName(auth, options.packageName, {
|
|
8153
|
-
scope: feedScopeInitOverride(scope)
|
|
8154
|
-
}));
|
|
8155
|
-
return fail3("deployment_exists", message, buildDeploymentExistsInstructions({
|
|
8156
|
-
packageName: options.packageName,
|
|
8157
|
-
scope,
|
|
8158
|
-
existing: blockers ?? [],
|
|
8159
|
-
requestedDeploymentName: options.deploymentName,
|
|
8160
|
-
includeInventory: true
|
|
8161
|
-
}));
|
|
8162
|
-
}
|
|
8163
|
-
const publishHint = isPersonal ? "uip solution publish <package> --personal-workspace" : `uip solution publish <package> --feed ${options.feed ?? "<name-or-key>"}`;
|
|
8164
|
-
return fail3(isPersonal ? "personal_workspace_deploy_failed" : "feed_deploy_failed", message, `Ensure the package was published to the target feed first: ${publishHint}. Details: ${details}`);
|
|
8165
|
-
}
|
|
8166
|
-
const target = isPersonal ? "your Personal Workspace" : `feed '${scope.name ?? location.folderFQN}'`;
|
|
8167
|
-
const started = {
|
|
8168
|
-
ok: true,
|
|
8169
|
-
status: "DeploymentStarted",
|
|
8170
|
-
folderName: options.folderName,
|
|
8171
|
-
folderPath: `${result.folderFQN}/${options.folderName}`,
|
|
8172
|
-
activationStatus: options.skipActivate ? "Skipped" : "Auto",
|
|
8173
|
-
deploymentName: result.deploymentName,
|
|
8174
|
-
packageName: result.packageName,
|
|
8175
|
-
packageVersion: result.packageVersion,
|
|
8176
|
-
feedKind: scope.kind,
|
|
8177
|
-
nextSteps: options.skipActivate ? `Deployment created in ${target}. Activate it with: uip solution deploy activate ${result.deploymentName}. Check status with: uip solution deploy list.` : `Deployment created in ${target} and will auto-activate. Check status with: uip solution deploy list.`
|
|
8178
|
-
};
|
|
8179
|
-
if (!options.wait) {
|
|
8180
|
-
return started;
|
|
8181
|
-
}
|
|
8182
|
-
return awaitFeedDeployAsync(auth, scope, options, started, result.deploymentName);
|
|
8183
|
-
}
|
|
8184
|
-
async function awaitFeedDeployAsync(auth, scope, options, started, deploymentName) {
|
|
8185
|
-
const scopeOverride = feedScopeInitOverride(scope);
|
|
8186
|
-
const pollResult = await pollUntil({
|
|
8187
|
-
fn: () => findDeploymentByName(auth, deploymentName, scopeOverride),
|
|
8188
|
-
until: (deployment2) => isSettledDeployOperationStatus(deployment2?.operationStatus),
|
|
8189
|
-
getStatus: (deployment2) => deployment2?.operationStatus ?? "Unknown",
|
|
8190
|
-
label: `deployment '${deploymentName}'`,
|
|
8191
|
-
logPrefix: "deploy",
|
|
8192
|
-
timeoutMs: (options.timeout ?? 360) * 1000,
|
|
8193
|
-
intervalMs: options.pollInterval ?? 5000,
|
|
8194
|
-
signal: options.signal
|
|
8195
|
-
});
|
|
8196
|
-
if (pollResult.outcome !== PollOutcome.Completed || !pollResult.data) {
|
|
8197
|
-
const { reason, message, exitCode, errorCode, retry } = mapPollFailure(pollResult, "Deployment");
|
|
8198
|
-
return fail3(reason, message, appendDraftDeploymentInstructions(`Deployment '${deploymentName}' may still be in progress. Check it with: uip solution deploy list.`, pollResult.data), { exitCode, errorCode, retry });
|
|
8199
|
-
}
|
|
8200
|
-
const deployment = pollResult.data;
|
|
8201
|
-
if (deployment.operationStatus !== "Successful") {
|
|
8202
|
-
const reason = await describeDeploymentFailureAsync(auth, deployment, SolutionDeploymentAction.Install, scopeOverride, options.signal);
|
|
8203
|
-
return fail3("deploy_failed", `Deployment '${deploymentName}' failed (OperationStatus: ${deployment.operationStatus}): ${reason}`, `Inspect the deployment with: uip solution deploy list, fix the underlying issue, then deploy again.`);
|
|
8204
|
-
}
|
|
8205
|
-
return {
|
|
8206
|
-
...started,
|
|
8207
|
-
status: "DeploymentSucceeded",
|
|
8208
|
-
nextSteps: options.skipActivate ? started.nextSteps : undefined
|
|
8209
|
-
};
|
|
8210
|
-
}
|
|
8211
8244
|
async function resolveFeedDeployLocation(scope, options) {
|
|
8212
8245
|
const feedFolder = {
|
|
8213
8246
|
folderKey: scope.folderKey,
|
|
@@ -8234,7 +8267,7 @@ async function resolveFeedDeployLocation(scope, options) {
|
|
|
8234
8267
|
}
|
|
8235
8268
|
|
|
8236
8269
|
// src/services/deploy-uninstall-service.ts
|
|
8237
|
-
var
|
|
8270
|
+
var TERMINAL_STATUSES2 = new Set([
|
|
8238
8271
|
"SuccessfulUninstall",
|
|
8239
8272
|
"FailedUninstall"
|
|
8240
8273
|
]);
|
|
@@ -8274,10 +8307,11 @@ function buildDeploymentStateInstructions(deployment, details) {
|
|
|
8274
8307
|
if (deployment.actions?.includes("Uninstall")) {
|
|
8275
8308
|
return `Deployment '${deployment.name}' lists uninstall as available but Orchestrator rejected the request ${status}. Run 'uip solution deploy list' to recheck its state, then retry. If it stays stuck, uninstall it from the Orchestrator Solutions UI. Details: ${details}`;
|
|
8276
8309
|
}
|
|
8310
|
+
const removalAdvice = deployment.actions?.includes(WorkflowAction.Delete) ? `Delete it with: uip solution deploy delete '${deployment.name}' --yes.` : "Remove it from the Orchestrator Solutions UI.";
|
|
8277
8311
|
if (deployment.activationStatus === "Inactive") {
|
|
8278
|
-
return `Deployment '${deployment.name}' cannot be uninstalled in its current state ${status}. Available actions: ${availableActions}. Superseded or inactive deployments
|
|
8312
|
+
return `Deployment '${deployment.name}' cannot be uninstalled in its current state ${status}. Available actions: ${availableActions}. Superseded or inactive deployments are never uninstallable. ${removalAdvice} Details: ${details}`;
|
|
8279
8313
|
}
|
|
8280
|
-
return `Deployment '${deployment.name}' cannot be uninstalled in its current state ${status}. Available actions: ${availableActions}. Run 'uip solution deploy list' to recheck its state
|
|
8314
|
+
return `Deployment '${deployment.name}' cannot be uninstalled in its current state ${status}. Available actions: ${availableActions}. Run 'uip solution deploy list' to recheck its state. ${removalAdvice} Details: ${details}`;
|
|
8281
8315
|
}
|
|
8282
8316
|
async function waitForOperationToFinishAsync(auth, deploymentName, opts) {
|
|
8283
8317
|
let last;
|
|
@@ -8297,9 +8331,10 @@ async function waitForOperationToFinishAsync(auth, deploymentName, opts) {
|
|
|
8297
8331
|
return { outcome: poll.outcome, state: last, error: poll.error };
|
|
8298
8332
|
}
|
|
8299
8333
|
async function requestUninstallAsync(api, auth, deploymentName, ctx) {
|
|
8300
|
-
const { options,
|
|
8334
|
+
const { options, sweep, intervalMs, remainingMs } = ctx;
|
|
8335
|
+
const { location } = sweep;
|
|
8336
|
+
const sweepIncomplete = !location && !sweep.complete;
|
|
8301
8337
|
let state = location?.deployment;
|
|
8302
|
-
let stateUnknown = ctx.lookupError !== undefined;
|
|
8303
8338
|
let waited = false;
|
|
8304
8339
|
let rechecked = false;
|
|
8305
8340
|
for (;; ) {
|
|
@@ -8307,8 +8342,11 @@ async function requestUninstallAsync(api, auth, deploymentName, ctx) {
|
|
|
8307
8342
|
if (!uninstallError) {
|
|
8308
8343
|
return { ok: true, result };
|
|
8309
8344
|
}
|
|
8310
|
-
const { message, details, errorCode, retry } = await extractErrorDetails(uninstallError);
|
|
8345
|
+
const { message, details, errorCode, retry, context } = await extractErrorDetails(uninstallError);
|
|
8311
8346
|
if (errorCode !== "invalid_argument") {
|
|
8347
|
+
if (errorCode === "not_found" && sweepIncomplete) {
|
|
8348
|
+
return incompleteSweepFailureAsync(deploymentName, sweep, details);
|
|
8349
|
+
}
|
|
8312
8350
|
return fail4("uninstall_request_failed", message, requestErrorInstructions(deploymentName, errorCode, details), { errorCode, retry });
|
|
8313
8351
|
}
|
|
8314
8352
|
if (!rechecked && !waited && state?.operationStatus !== "InProgress") {
|
|
@@ -8316,7 +8354,6 @@ async function requestUninstallAsync(api, auth, deploymentName, ctx) {
|
|
|
8316
8354
|
const [readError, fresh] = await catchError(findDeploymentByName(auth, deploymentName, location?.scope));
|
|
8317
8355
|
if (!readError) {
|
|
8318
8356
|
state = fresh;
|
|
8319
|
-
stateUnknown = false;
|
|
8320
8357
|
}
|
|
8321
8358
|
}
|
|
8322
8359
|
if (!waited && state?.operationStatus === "InProgress") {
|
|
@@ -8341,23 +8378,25 @@ async function requestUninstallAsync(api, auth, deploymentName, ctx) {
|
|
|
8341
8378
|
return fail4(reason, pollMessage, `The deployment may still be installing. Check it with: uip solution deploy list, then retry the uninstall.`, { exitCode, errorCode: pollErrorCode, retry: pollRetry });
|
|
8342
8379
|
}
|
|
8343
8380
|
state = wait.state;
|
|
8344
|
-
stateUnknown = false;
|
|
8345
8381
|
continue;
|
|
8346
8382
|
}
|
|
8347
|
-
|
|
8348
|
-
|
|
8349
|
-
|
|
8350
|
-
|
|
8351
|
-
|
|
8352
|
-
} else {
|
|
8353
|
-
instructions = `No deployment named '${deploymentName}' was found in any feed you can reach (tenant, Personal Workspace, or a folder feed). Check the name with 'uip solution deploy list'. Details: ${details}`;
|
|
8383
|
+
if (sweepIncomplete && (!state || isCompletedUninstall(state))) {
|
|
8384
|
+
if (isNameOrFeedRefusal(errorCode, context?.httpStatus)) {
|
|
8385
|
+
return incompleteSweepFailureAsync(deploymentName, sweep, details);
|
|
8386
|
+
}
|
|
8387
|
+
return fail4("uninstall_request_failed", message, requestErrorInstructions(deploymentName, errorCode, details), { errorCode, retry });
|
|
8354
8388
|
}
|
|
8389
|
+
const instructions = state ? buildDeploymentStateInstructions(state, details) : `No deployment named '${deploymentName}' was found in any feed you can reach (tenant, Personal Workspace, or a folder feed). Check the name with 'uip solution deploy list'. Details: ${details}`;
|
|
8355
8390
|
return fail4("uninstall_request_failed", message, instructions, {
|
|
8356
8391
|
errorCode,
|
|
8357
8392
|
retry
|
|
8358
8393
|
});
|
|
8359
8394
|
}
|
|
8360
8395
|
}
|
|
8396
|
+
async function incompleteSweepFailureAsync(deploymentName, sweep, refusalDetails) {
|
|
8397
|
+
const report = await reportIncompleteSweepAsync(deploymentName, sweep.skipped);
|
|
8398
|
+
return fail4("uninstall_request_failed", report.message, `${INCOMPLETE_SWEEP_ADVICE} Without its feed the uninstall request went to the tenant feed, which refused it. ${report.nextStep} Details: ${refusalDetails} (feed search: ${report.details})`, { errorCode: report.errorCode, retry: report.retry });
|
|
8399
|
+
}
|
|
8361
8400
|
async function uninstallDeploymentAsync(deploymentName, options = {}) {
|
|
8362
8401
|
const [authError, auth] = await catchError(getSolutionAuthContext({
|
|
8363
8402
|
tenant: options.tenant,
|
|
@@ -8367,18 +8406,18 @@ async function uninstallDeploymentAsync(deploymentName, options = {}) {
|
|
|
8367
8406
|
if (authError) {
|
|
8368
8407
|
return fail4("auth_failed", authError.message, authError.message);
|
|
8369
8408
|
}
|
|
8370
|
-
const api = new PipelinesApi(new
|
|
8409
|
+
const api = new PipelinesApi(new Configuration2({
|
|
8371
8410
|
basePath: auth.basePath,
|
|
8372
8411
|
accessToken: auth.accessToken
|
|
8373
8412
|
}));
|
|
8374
8413
|
const deadline = Date.now() + (options.timeout ?? 360) * 1000;
|
|
8375
8414
|
const intervalMs = options.pollInterval ?? 5000;
|
|
8376
8415
|
const remainingMs = () => Math.max(deadline - Date.now(), 1);
|
|
8377
|
-
const
|
|
8416
|
+
const sweep = await findDeploymentAcrossFeeds(auth, deploymentName, options);
|
|
8417
|
+
const location = sweep.location;
|
|
8378
8418
|
const [requestError, requested] = await catchError(requestUninstallAsync(api, auth, deploymentName, {
|
|
8379
8419
|
options,
|
|
8380
|
-
|
|
8381
|
-
lookupError,
|
|
8420
|
+
sweep,
|
|
8382
8421
|
intervalMs,
|
|
8383
8422
|
remainingMs
|
|
8384
8423
|
}));
|
|
@@ -8407,7 +8446,7 @@ async function uninstallDeploymentAsync(deploymentName, options = {}) {
|
|
|
8407
8446
|
}
|
|
8408
8447
|
const pollResult = await pollUntil({
|
|
8409
8448
|
fn: () => api.pipelinesGetDeploymentInstanceStatus({ instanceId }, location?.scope),
|
|
8410
|
-
until: (result) =>
|
|
8449
|
+
until: (result) => TERMINAL_STATUSES2.has(result.status),
|
|
8411
8450
|
getStatus: (result) => result.status,
|
|
8412
8451
|
label: `uninstall ${deploymentName}`,
|
|
8413
8452
|
logPrefix: "uninstall",
|
|
@@ -8426,7 +8465,7 @@ async function uninstallDeploymentAsync(deploymentName, options = {}) {
|
|
|
8426
8465
|
const finalStatus = pollResult.data;
|
|
8427
8466
|
if (finalStatus.status !== "SuccessfulUninstall") {
|
|
8428
8467
|
const errors2 = coerceWorkflowErrorMessages(finalStatus.errorMessage).map((e) => e.errorMessage).join(", ");
|
|
8429
|
-
return fail4("uninstall_failed", `Uninstall failed with status: ${finalStatus.status}`, errors2 ||
|
|
8468
|
+
return fail4("uninstall_failed", `Uninstall failed with status: ${finalStatus.status}`, errors2 || `Orchestrator ended the uninstall in a FailedUninstall state without an error detail. Run 'uip solution deploy list' to recheck the deployment's state and retry. If it stays in a failed state and that listing shows Delete among its actions, remove the record with: uip solution deploy delete '${deploymentName}' --yes. Remove it from the Orchestrator Solutions UI when it does not.`);
|
|
8430
8469
|
}
|
|
8431
8470
|
return {
|
|
8432
8471
|
ok: true,
|
|
@@ -8437,17 +8476,34 @@ async function uninstallDeploymentAsync(deploymentName, options = {}) {
|
|
|
8437
8476
|
}
|
|
8438
8477
|
|
|
8439
8478
|
// src/services/deploy-upgrade-service.ts
|
|
8440
|
-
var fail5 = (reason, message, instructions,
|
|
8479
|
+
var fail5 = (reason, message, instructions, extras = {}) => ({
|
|
8441
8480
|
ok: false,
|
|
8442
8481
|
reason,
|
|
8443
8482
|
message,
|
|
8444
8483
|
instructions,
|
|
8445
|
-
exitCode
|
|
8484
|
+
exitCode: extras.exitCode ?? 1,
|
|
8485
|
+
errorCode: extras.errorCode,
|
|
8486
|
+
retry: extras.retry
|
|
8446
8487
|
});
|
|
8447
8488
|
var DEFAULT_TIMEOUT_SECONDS = 300;
|
|
8448
8489
|
var DEFAULT_POLL_INTERVAL_MS = 5000;
|
|
8449
8490
|
var AUTH_INSTRUCTIONS2 = "Run 'uip login' to refresh credentials, then retry. If the deployment is in a different tenant, switch with 'uip login tenant set <tenant>'.";
|
|
8450
|
-
|
|
8491
|
+
var PACKAGE_INSTRUCTIONS = "Confirm the version is published to the tenant (or Personal Workspace) feed: uip solution packages list.";
|
|
8492
|
+
var TARGET_INSTRUCTIONS = "Address the deployment either by key — uip solution deploy upgrade <deployment-key> — or by name with its package: uip solution deploy upgrade --name <deployment-name> --package-name <package>. 'uip solution deploy list' reports all three.";
|
|
8493
|
+
var DEPLOYMENT_NOT_FOUND_INSTRUCTIONS = "Check the key with 'uip solution deploy list'. A server without the Pipelines deployment lookup answers 404 to every key for an external app; on one of those, address the deployment with --name and --package-name instead.";
|
|
8494
|
+
var LOOKUP_FAILED_INSTRUCTIONS = "The deployment lookup failed before any upgrade was requested. Retry, or address the deployment with --name and --package-name.";
|
|
8495
|
+
var INVALID_TARGET = {
|
|
8496
|
+
errorCode: "invalid_argument",
|
|
8497
|
+
retry: "RetryWillNotFix"
|
|
8498
|
+
};
|
|
8499
|
+
var GUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
8500
|
+
var UPGRADE_REFUSED_INSTRUCTIONS = "The server refused to start the upgrade. The deployment name may not exist, or may belong to a different package — check both with 'uip solution deploy list'. Another operation may still be running on it (wait, then retry), or it may be in a state the platform does not offer upgrades for.";
|
|
8501
|
+
async function upgradeDeploymentAsync(first, second) {
|
|
8502
|
+
const options = typeof first === "string" ? { ...second, deploymentKey: first } : first;
|
|
8503
|
+
const targetError = validateTarget(options);
|
|
8504
|
+
if (targetError) {
|
|
8505
|
+
return targetError;
|
|
8506
|
+
}
|
|
8451
8507
|
const [authError, auth] = await catchError(getSolutionAuthContext({
|
|
8452
8508
|
tenant: options.tenant,
|
|
8453
8509
|
loginValidity: options.loginValidity,
|
|
@@ -8456,140 +8512,442 @@ async function upgradeDeploymentAsync(deploymentKey, options = {}) {
|
|
|
8456
8512
|
if (authError) {
|
|
8457
8513
|
return fail5("auth_failed", authError.message, AUTH_INSTRUCTIONS2);
|
|
8458
8514
|
}
|
|
8459
|
-
const
|
|
8515
|
+
const pipelinesApi = new PipelinesApi(new Configuration2({
|
|
8460
8516
|
basePath: auth.basePath,
|
|
8461
8517
|
accessToken: auth.accessToken
|
|
8462
|
-
});
|
|
8463
|
-
const
|
|
8464
|
-
|
|
8465
|
-
|
|
8466
|
-
|
|
8467
|
-
|
|
8468
|
-
|
|
8469
|
-
|
|
8470
|
-
}));
|
|
8471
|
-
if (
|
|
8472
|
-
|
|
8518
|
+
}));
|
|
8519
|
+
const target = options.deploymentKey ? await resolveTargetByKeyAsync(pipelinesApi, auth, options, options.deploymentKey) : await resolveTargetByNameAsync(pipelinesApi, auth, options);
|
|
8520
|
+
if (!target.ok) {
|
|
8521
|
+
return target;
|
|
8522
|
+
}
|
|
8523
|
+
const { deploymentName, packageName, scope } = target;
|
|
8524
|
+
let targetVersion = options.version;
|
|
8525
|
+
if (targetVersion === undefined) {
|
|
8526
|
+
const [versionError, latest] = await catchError(pipelinesApi.pipelinesGetPackageVersion({ packageName, packageVersion: null }, scope));
|
|
8527
|
+
if (versionError || !latest) {
|
|
8528
|
+
const detail = versionError && await extractErrorDetails(versionError);
|
|
8529
|
+
if (!detail || detail.context?.httpStatus === 404) {
|
|
8530
|
+
return fail5("package_not_found", detail?.details ? `Package '${packageName}' was not found in the feed: ${detail.details}` : `Package '${packageName}' was not found in the feed.`, PACKAGE_INSTRUCTIONS, { errorCode: "not_found", retry: "RetryWillNotFix" });
|
|
8531
|
+
}
|
|
8532
|
+
return fail5("lookup_failed", detail.details ? `${detail.message}: ${detail.details}` : detail.message, `The version lookup for '${packageName}' failed before any upgrade was requested. Retry, or pass --version explicitly to skip the lookup.`, { errorCode: detail.errorCode, retry: detail.retry });
|
|
8473
8533
|
}
|
|
8474
|
-
|
|
8475
|
-
}
|
|
8476
|
-
const [
|
|
8477
|
-
|
|
8478
|
-
|
|
8479
|
-
|
|
8480
|
-
|
|
8481
|
-
|
|
8482
|
-
|
|
8483
|
-
|
|
8484
|
-
|
|
8485
|
-
|
|
8486
|
-
|
|
8487
|
-
|
|
8488
|
-
take,
|
|
8489
|
-
skip
|
|
8490
|
-
}
|
|
8491
|
-
}, feedScope));
|
|
8492
|
-
if (searchError) {
|
|
8493
|
-
const { message, details } = await extractErrorDetails(searchError);
|
|
8494
|
-
return fail5("package_not_found", details ? `${message}: ${details}` : message, "Confirm the package is published to the tenant (or Personal Workspace) feed.");
|
|
8495
|
-
}
|
|
8496
|
-
const values = packages.values ?? [];
|
|
8497
|
-
pkg = values.find((p) => p.name === deployment.packageName);
|
|
8498
|
-
if (pkg || values.length < take) {
|
|
8499
|
-
break;
|
|
8500
|
-
}
|
|
8501
|
-
skip += values.length;
|
|
8502
|
-
}
|
|
8503
|
-
if (!pkg) {
|
|
8504
|
-
return fail5("package_not_found", `Package '${deployment.packageName}' was not found in the feed.`, "Confirm the package is published to the tenant (or Personal Workspace) feed.");
|
|
8505
|
-
}
|
|
8506
|
-
if (options.version && options.version !== pkg.latestVersion) {
|
|
8507
|
-
return fail5("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.");
|
|
8508
|
-
}
|
|
8509
|
-
const currentVersion = deployment.currentPackageVersion ?? deployment.packageVersion;
|
|
8510
|
-
if (currentVersion === pkg.latestVersion) {
|
|
8511
|
-
return fail5("no_upgrade_available", `Deployment '${deployment.name}' is already at the latest published version (${pkg.latestVersion}).`, "Publish a newer version of the package, then retry.");
|
|
8512
|
-
}
|
|
8513
|
-
const [upgradeError, draft] = await catchError(deploymentsApi.deploymentsUpgrade({
|
|
8514
|
-
deploymentKey,
|
|
8515
|
-
packageVersionKey: pkg.packageVersionKey,
|
|
8516
|
-
deploymentUpgradeRequest: {
|
|
8517
|
-
packageVersionKey: pkg.packageVersionKey
|
|
8518
|
-
}
|
|
8519
|
-
}, upgradeScope));
|
|
8520
|
-
if (upgradeError || !draft) {
|
|
8521
|
-
let message = "The upgrade request returned no deployment.";
|
|
8522
|
-
if (upgradeError) {
|
|
8523
|
-
const detail = await extractErrorDetails(upgradeError);
|
|
8534
|
+
targetVersion = latest.packageVersion;
|
|
8535
|
+
}
|
|
8536
|
+
const [installError, installResult] = await catchError(pipelinesApi.pipelinesInstall({
|
|
8537
|
+
deploymentName,
|
|
8538
|
+
packageName,
|
|
8539
|
+
packageVersion: targetVersion,
|
|
8540
|
+
solutionRootFolderName: deploymentName
|
|
8541
|
+
}, scope));
|
|
8542
|
+
if (installError || !installResult?.pipelineDeploymentId) {
|
|
8543
|
+
let message = "The upgrade request returned no pipeline deployment.";
|
|
8544
|
+
let classification = {};
|
|
8545
|
+
let instructions = UPGRADE_REFUSED_INSTRUCTIONS;
|
|
8546
|
+
if (installError) {
|
|
8547
|
+
const detail = await extractErrorDetails(installError);
|
|
8524
8548
|
message = detail.details ? `${detail.message}: ${detail.details}` : detail.message;
|
|
8549
|
+
classification = {
|
|
8550
|
+
errorCode: detail.errorCode,
|
|
8551
|
+
retry: detail.retry
|
|
8552
|
+
};
|
|
8553
|
+
if (detail.context?.httpStatus === 404) {
|
|
8554
|
+
instructions = `The server knows no such version of '${packageName}' in this feed. ${PACKAGE_INSTRUCTIONS} 'uip solution deploy list' shows the deployment's package and current version.`;
|
|
8555
|
+
}
|
|
8556
|
+
if (hasServerErrorCode(detail.details, DRAFT_VERSION_MISMATCH_CODE)) {
|
|
8557
|
+
return fail5("pending_draft", message, `'${deploymentName}' has a pending draft for another version: an earlier attempt that never completed, usually one rejected by validation, and the server will not upgrade past it. Discard it and upgrade the live version: ${discardRetryCommand(options, target)}${options.deploymentKey ? "" : `
|
|
8558
|
+
Under an external app the name form cannot find the draft; pass the deployment's key instead (InstallDeploymentKey from the original deploy, or Key from 'uip solution deploy list').`}`, INVALID_TARGET);
|
|
8559
|
+
}
|
|
8525
8560
|
}
|
|
8526
|
-
return fail5("request_failed", message,
|
|
8527
|
-
}
|
|
8528
|
-
const [runError] = await catchError(deploymentsApi.deploymentsRun({ deploymentKey: draft.key }, upgradeScope));
|
|
8529
|
-
if (runError) {
|
|
8530
|
-
const { message, details } = await extractErrorDetails(runError);
|
|
8531
|
-
return fail5("install_failed", details ? `${message}: ${details}` : message, `The upgrade was queued as deployment '${draft.key}' but installing it failed. Run 'uip solution deploy list' to inspect its state.`);
|
|
8561
|
+
return fail5("request_failed", message, instructions, classification);
|
|
8532
8562
|
}
|
|
8563
|
+
const pipelineDeploymentId = installResult.pipelineDeploymentId;
|
|
8533
8564
|
const base = {
|
|
8534
|
-
deploymentName
|
|
8535
|
-
|
|
8536
|
-
|
|
8537
|
-
|
|
8565
|
+
deploymentName,
|
|
8566
|
+
toVersion: targetVersion,
|
|
8567
|
+
pipelineDeploymentId,
|
|
8568
|
+
...target.fromVersion !== undefined && {
|
|
8569
|
+
fromVersion: target.fromVersion
|
|
8570
|
+
},
|
|
8571
|
+
...target.installDeploymentKey !== undefined && {
|
|
8572
|
+
installDeploymentKey: target.installDeploymentKey
|
|
8573
|
+
},
|
|
8574
|
+
...target.discardedDraftKey !== undefined && {
|
|
8575
|
+
discardedDraftKey: target.discardedDraftKey
|
|
8576
|
+
}
|
|
8538
8577
|
};
|
|
8539
8578
|
if (options.noWait) {
|
|
8540
|
-
return { ok: true, ...base, status:
|
|
8579
|
+
return { ok: true, ...base, status: "Accepted" };
|
|
8541
8580
|
}
|
|
8542
8581
|
const timeoutSeconds = Number.isFinite(options.timeout) ? options.timeout : DEFAULT_TIMEOUT_SECONDS;
|
|
8543
|
-
const poll = await
|
|
8582
|
+
const poll = await pollPipelineDeploymentAsync(pipelinesApi, auth, {
|
|
8583
|
+
pipelineDeploymentId,
|
|
8584
|
+
deploymentName,
|
|
8585
|
+
scope,
|
|
8544
8586
|
timeoutMs: timeoutSeconds * 1000,
|
|
8545
|
-
intervalMs: Number.isFinite(options.pollInterval) ? options.pollInterval : DEFAULT_POLL_INTERVAL_MS
|
|
8587
|
+
intervalMs: Number.isFinite(options.pollInterval) ? options.pollInterval : DEFAULT_POLL_INTERVAL_MS,
|
|
8588
|
+
signal: options.signal,
|
|
8589
|
+
logPrefix: "deploy upgrade"
|
|
8546
8590
|
});
|
|
8547
|
-
if (
|
|
8591
|
+
if (poll.outcome !== PollOutcome.Completed) {
|
|
8592
|
+
const failure = mapPollFailure(poll, `Upgrade ${pipelineDeploymentId}`);
|
|
8548
8593
|
const isTimeout = poll.outcome === PollOutcome.Timeout;
|
|
8549
|
-
|
|
8594
|
+
const lastStatus = poll.data?.status ?? "unknown";
|
|
8595
|
+
return fail5(isTimeout ? "install_timeout" : failure.reason, isTimeout ? `The upgrade of '${deploymentName}' to ${targetVersion} did not finish within ${timeoutSeconds}s (last status: ${lastStatus}).` : `${failure.message} while upgrading '${deploymentName}' to ${targetVersion} (last status: ${lastStatus}).`, isTimeout ? `It may still be running. ${trackingHint(pipelineDeploymentId, target.listFlag)} Or pass a longer --timeout.` : `The upgrade may still be running. ${trackingHint(pipelineDeploymentId, target.listFlag)}`, {
|
|
8596
|
+
exitCode: failure.exitCode,
|
|
8597
|
+
errorCode: failure.errorCode,
|
|
8598
|
+
retry: failure.retry
|
|
8599
|
+
});
|
|
8550
8600
|
}
|
|
8551
|
-
const
|
|
8552
|
-
if (
|
|
8553
|
-
return fail5("
|
|
8601
|
+
const finalStatus = poll.data;
|
|
8602
|
+
if (!finalStatus) {
|
|
8603
|
+
return fail5("deploy_failed", `The upgrade of '${deploymentName}' returned no final status.`, trackingHint(pipelineDeploymentId, target.listFlag));
|
|
8604
|
+
}
|
|
8605
|
+
const landedDespiteStatus = installLandedDespiteStatus(finalStatus);
|
|
8606
|
+
if (finalStatus.status !== "DeploymentSucceeded" && !landedDespiteStatus) {
|
|
8607
|
+
return fail5("deploy_failed", `The upgrade of '${deploymentName}' to ${targetVersion} ended in ${finalStatus.status}.`, `${await describeUpgradeFailureAsync(auth.basePath, auth.accessToken, pipelineDeploymentId, finalStatus, scope, options.signal)}
|
|
8608
|
+
${leavesPendingDraft(finalStatus.status) ? `Nothing was installed; the deployment stays on the version it was already running. The rejected attempt is kept as a pending draft${finalStatus.deploymentKey ? ` (${finalStatus.deploymentKey})` : ""}, which a plain retry would run into, so once the cause is fixed discard it and retry: ${discardRetryCommand(options, target, finalStatus.deploymentKey || undefined)}` : "Fix the reported problem, then retry the upgrade. The deployment stays on the version it was already running."}`, classifyTerminalFailure(finalStatus.status));
|
|
8554
8609
|
}
|
|
8555
|
-
|
|
8610
|
+
const described = await describeUpgradedDeploymentAsync(auth, deploymentName, finalStatus, scope, target.listFlag);
|
|
8611
|
+
return {
|
|
8612
|
+
ok: true,
|
|
8613
|
+
...base,
|
|
8614
|
+
deploymentKey: finalStatus.deploymentKey,
|
|
8615
|
+
operation: finalStatus.deploymentResult?.operation ?? finalStatus.synthesizedOperation,
|
|
8616
|
+
status: finalStatus.status,
|
|
8617
|
+
...finalStatus.deploymentResult?.status && {
|
|
8618
|
+
deploymentStatus: finalStatus.deploymentResult.status,
|
|
8619
|
+
actions: finalStatus.deploymentResult.actions ?? []
|
|
8620
|
+
},
|
|
8621
|
+
...described,
|
|
8622
|
+
...landedDespiteStatus && {
|
|
8623
|
+
nextSteps: [
|
|
8624
|
+
explainPipelineStatusAfterInstall(finalStatus),
|
|
8625
|
+
...described.nextSteps ?? []
|
|
8626
|
+
]
|
|
8627
|
+
}
|
|
8628
|
+
};
|
|
8556
8629
|
}
|
|
8557
|
-
|
|
8558
|
-
|
|
8559
|
-
|
|
8560
|
-
|
|
8561
|
-
|
|
8562
|
-
|
|
8563
|
-
|
|
8564
|
-
|
|
8565
|
-
|
|
8566
|
-
|
|
8567
|
-
|
|
8568
|
-
|
|
8569
|
-
|
|
8570
|
-
|
|
8571
|
-
|
|
8572
|
-
|
|
8573
|
-
|
|
8574
|
-
|
|
8575
|
-
|
|
8576
|
-
|
|
8630
|
+
async function describeUpgradedDeploymentAsync(auth, deploymentName, finalStatus, scope, listFlag) {
|
|
8631
|
+
const record = finalStatus.deploymentResult;
|
|
8632
|
+
const actions = record?.actions ?? [];
|
|
8633
|
+
if (record?.status === "SuccessfulActivate") {
|
|
8634
|
+
return { activationStatus: "Active" };
|
|
8635
|
+
}
|
|
8636
|
+
const [, rows] = await catchError(findDeploymentsByName(auth, deploymentName, scope));
|
|
8637
|
+
const row = rows?.find((candidate) => candidate.key === finalStatus.deploymentKey);
|
|
8638
|
+
const activationStatus = row?.activationStatus ?? (actions.includes("SetupActivation") ? "NeedsSetupToActivate" : actions.includes("Activate") ? "ReadyToActivate" : undefined);
|
|
8639
|
+
if (!activationStatus) {
|
|
8640
|
+
return {};
|
|
8641
|
+
}
|
|
8642
|
+
const state = {
|
|
8643
|
+
name: deploymentName,
|
|
8644
|
+
operation: row?.operation ?? record?.operation ?? finalStatus.synthesizedOperation,
|
|
8645
|
+
operationStatus: row?.operationStatus ?? "Successful",
|
|
8646
|
+
activationStatus,
|
|
8647
|
+
packageVersion: row?.packageVersion ?? record?.packageVersion,
|
|
8648
|
+
currentPackageVersion: row?.currentPackageVersion ?? record?.packageVersion
|
|
8649
|
+
};
|
|
8650
|
+
const pendingResources = needsPendingResources(state) && finalStatus.deploymentKey ? await readPendingResourcesAsync(new DeploymentsApi(new Configuration({
|
|
8651
|
+
basePath: auth.basePath,
|
|
8652
|
+
accessToken: auth.accessToken
|
|
8653
|
+
})), finalStatus.deploymentKey, scope) : undefined;
|
|
8654
|
+
const nextSteps = deploymentNextSteps(state, pendingResources, listFlag ? ` ${listFlag}` : "");
|
|
8655
|
+
return {
|
|
8656
|
+
activationStatus,
|
|
8657
|
+
...pendingResources ? { pendingResources } : {},
|
|
8658
|
+
...nextSteps && nextSteps.length > 0 ? { nextSteps } : {}
|
|
8659
|
+
};
|
|
8660
|
+
}
|
|
8661
|
+
function trackingHint(pipelineDeploymentId, listFlag) {
|
|
8662
|
+
return listFlag ? `Track it with: uip solution deploy list ${listFlag} (deploy status reads the tenant feed and cannot see this run).` : `Track it with: uip solution deploy status ${pipelineDeploymentId}, or uip solution deploy list.`;
|
|
8663
|
+
}
|
|
8664
|
+
function discardRetryCommand(options, target, draftKey) {
|
|
8665
|
+
const version = options.version ? ` --version ${options.version}` : "";
|
|
8666
|
+
const key = draftKey ?? options.deploymentKey;
|
|
8667
|
+
if (key) {
|
|
8668
|
+
return `uip solution deploy upgrade ${key} --discard-draft${version}`;
|
|
8669
|
+
}
|
|
8670
|
+
const workspace = options.personalWorkspace ? " --personal-workspace" : "";
|
|
8671
|
+
return `uip solution deploy upgrade --name ${target.deploymentName} --package-name ${target.packageName}${workspace} --discard-draft${version}`;
|
|
8672
|
+
}
|
|
8673
|
+
function validateTarget(options) {
|
|
8674
|
+
const byKey = options.deploymentKey !== undefined;
|
|
8675
|
+
const byName = options.deploymentName !== undefined || options.packageName !== undefined;
|
|
8676
|
+
if (options.version !== undefined && options.version.trim() === "") {
|
|
8677
|
+
return fail5("invalid_target", "--version was given but is empty.", "Pass the version to upgrade to (uip solution packages list shows the published ones), or omit --version to upgrade to the newest.", INVALID_TARGET);
|
|
8678
|
+
}
|
|
8679
|
+
if (byKey && byName) {
|
|
8680
|
+
return fail5("invalid_target", "Pass either a deployment key or --name with --package-name, not both.", TARGET_INSTRUCTIONS, INVALID_TARGET);
|
|
8681
|
+
}
|
|
8682
|
+
if (byKey) {
|
|
8683
|
+
return GUID_PATTERN.test(options.deploymentKey) ? undefined : fail5("invalid_target", `'${options.deploymentKey}' is not a deployment key.`, `A deployment key is a GUID, reported as Key by 'uip solution deploy list'. To address the deployment by name instead: ${TARGET_INSTRUCTIONS}`, INVALID_TARGET);
|
|
8684
|
+
}
|
|
8685
|
+
if (!options.deploymentName || !options.packageName) {
|
|
8686
|
+
return fail5("invalid_target", "--name and --package-name go together: the Pipelines install route needs both.", TARGET_INSTRUCTIONS, INVALID_TARGET);
|
|
8687
|
+
}
|
|
8688
|
+
return;
|
|
8689
|
+
}
|
|
8690
|
+
async function resolveTargetByNameAsync(pipelinesApi, auth, options) {
|
|
8691
|
+
const deploymentName = options.deploymentName;
|
|
8692
|
+
let scope;
|
|
8693
|
+
if (options.personalWorkspace) {
|
|
8694
|
+
const verdict = classifyFeedSession(auth);
|
|
8695
|
+
if (verdict !== "user") {
|
|
8696
|
+
const { message, instructions } = feedSessionRefusal(verdict, `Upgrading the Personal Workspace deployment '${deploymentName}'`, "address the deployment by key");
|
|
8697
|
+
return fail5("feed_requires_user_session", message, instructions, {
|
|
8698
|
+
errorCode: "permission_denied",
|
|
8699
|
+
retry: "RetryWillNotFix"
|
|
8700
|
+
});
|
|
8701
|
+
}
|
|
8702
|
+
const [pwError, pw] = await catchError(resolvePersonalWorkspace({
|
|
8703
|
+
tenant: options.tenant,
|
|
8704
|
+
loginValidity: options.loginValidity,
|
|
8705
|
+
envFilePath: options.envFilePath
|
|
8706
|
+
}));
|
|
8707
|
+
if (pwError || !pw) {
|
|
8708
|
+
return fail5("personal_workspace_failed", pwError?.message ?? "Personal Workspace could not be resolved.", "Ensure your user has a Personal Workspace, then retry.");
|
|
8709
|
+
}
|
|
8710
|
+
scope = folderKeyInitOverride(pw.key);
|
|
8711
|
+
}
|
|
8712
|
+
const [existsError] = await catchError(pipelinesApi.pipelinesGetPreActivateSteps({ deploymentName }, scope));
|
|
8713
|
+
if (existsError) {
|
|
8714
|
+
const detail = await extractErrorDetails(existsError);
|
|
8715
|
+
const where = scope ? "your Personal Workspace feed" : `the tenant feed of '${auth.tenantName}'`;
|
|
8716
|
+
if (isDeploymentNotFound(detail)) {
|
|
8717
|
+
return fail5("deployment_not_found", `Deployment '${deploymentName}' was not found in ${where}. 'deploy upgrade' never creates a deployment.`, `Check the name with 'uip solution deploy list'${scope ? "" : " (add --personal-workspace for a Personal Workspace deployment)"}. For a first deployment use 'uip solution deploy run'.`, { errorCode: "not_found", retry: "RetryWillNotFix" });
|
|
8718
|
+
}
|
|
8719
|
+
return fail5("lookup_failed", detail.details ? `${detail.message}: ${detail.details}` : detail.message, `The existence check for '${deploymentName}' failed before any upgrade was requested. Retry, or address the deployment by key: uip solution deploy upgrade <deployment-key>.`, { errorCode: detail.errorCode, retry: detail.retry });
|
|
8720
|
+
}
|
|
8721
|
+
const [rowsError, rows] = await catchError(findDeploymentsByName(auth, deploymentName, scope));
|
|
8722
|
+
if (rowsError) {
|
|
8723
|
+
const unreadable = rowsError instanceof DeploymentSearchNeedsUserSessionError || classifyFeedSession(auth) === "application" && isAuthRefusal(rowsError);
|
|
8724
|
+
if (!unreadable) {
|
|
8725
|
+
const detail = await extractErrorDetails(rowsError);
|
|
8726
|
+
return fail5("lookup_failed", `The status check for '${deploymentName}' failed: ${detail.message}`, "The deployment exists, but whether it is still installed could not be confirmed, and upgrading an uninstalled name would deploy the package afresh under it. Retry, or address the deployment by key: uip solution deploy upgrade <deployment-key>.", { errorCode: detail.errorCode, retry: detail.retry });
|
|
8727
|
+
}
|
|
8728
|
+
}
|
|
8729
|
+
if (isCompletedUninstall(rows ? newestDeploymentRow(rows) : undefined)) {
|
|
8730
|
+
return fail5("deployment_uninstalled", `Deployment '${deploymentName}' has been uninstalled, so there is nothing to upgrade.`, "The install route would create a new deployment under that name rather than upgrade this one. Deploy the package afresh with 'uip solution deploy run', or pick a live deployment from 'uip solution deploy list'.", INVALID_TARGET);
|
|
8731
|
+
}
|
|
8732
|
+
const resolved = {
|
|
8733
|
+
deploymentName,
|
|
8734
|
+
packageName: options.packageName,
|
|
8735
|
+
listFlag: scope ? "--personal-workspace" : undefined
|
|
8736
|
+
};
|
|
8737
|
+
let discardedDraftKey;
|
|
8738
|
+
const newest = rows ? newestDeploymentRow(rows) : undefined;
|
|
8739
|
+
if (newest?.operationStatus === "Draft") {
|
|
8740
|
+
const draft = {
|
|
8741
|
+
key: newest.key,
|
|
8742
|
+
firstInstall: newest.operation === "Install",
|
|
8743
|
+
activationSetup: newest.operation === "ActivationSetup",
|
|
8744
|
+
targetVersion: newest.targetPackageVersion ?? newest.packageVersion,
|
|
8745
|
+
liveVersion: newest.currentPackageVersion
|
|
8746
|
+
};
|
|
8747
|
+
const refusal = refusePendingDraft(options, resolved, draft);
|
|
8748
|
+
if (refusal) {
|
|
8749
|
+
return refusal;
|
|
8750
|
+
}
|
|
8751
|
+
const discarded = await discardDraftAsync(pipelinesApi, deploymentName, draft.key, scope);
|
|
8752
|
+
if (discarded) {
|
|
8753
|
+
return discarded;
|
|
8754
|
+
}
|
|
8755
|
+
discardedDraftKey = draft.key;
|
|
8577
8756
|
}
|
|
8578
8757
|
return {
|
|
8579
|
-
ok:
|
|
8580
|
-
|
|
8581
|
-
|
|
8582
|
-
|
|
8758
|
+
ok: true,
|
|
8759
|
+
...resolved,
|
|
8760
|
+
scope,
|
|
8761
|
+
discardedDraftKey
|
|
8583
8762
|
};
|
|
8584
8763
|
}
|
|
8764
|
+
function isDeploymentNotFound(detail) {
|
|
8765
|
+
const status = detail.context?.httpStatus;
|
|
8766
|
+
if (status === 404) {
|
|
8767
|
+
return true;
|
|
8768
|
+
}
|
|
8769
|
+
return status === 400 && (/"errorCode"\s*:\s*"4004"/.test(detail.details) || (detail.parsedErrors?.deploymentName ?? []).some((message) => /not found/i.test(message)));
|
|
8770
|
+
}
|
|
8771
|
+
async function resolveTargetByKeyAsync(pipelinesApi, auth, options, deploymentKey) {
|
|
8772
|
+
const [lookupError, info] = await catchError(pipelinesApi.pipelinesGetDeploymentByKey({ key: deploymentKey }));
|
|
8773
|
+
if (lookupError || !info) {
|
|
8774
|
+
const detail = lookupError ? await extractErrorDetails(lookupError) : undefined;
|
|
8775
|
+
if (detail?.context?.httpStatus === 404 && classifyFeedSession(auth) === "user") {
|
|
8776
|
+
return resolveTargetByKeyViaSolutionsAsync(pipelinesApi, auth, options, deploymentKey);
|
|
8777
|
+
}
|
|
8778
|
+
if (!detail || detail.context?.httpStatus === 404) {
|
|
8779
|
+
return fail5("deployment_not_found", `Deployment '${deploymentKey}' was not found in tenant '${auth.tenantName}'.`, DEPLOYMENT_NOT_FOUND_INSTRUCTIONS, { errorCode: "not_found", retry: "RetryWillNotFix" });
|
|
8780
|
+
}
|
|
8781
|
+
return fail5("lookup_failed", detail.details ? `${detail.message}: ${detail.details}` : detail.message, LOOKUP_FAILED_INSTRUCTIONS, { errorCode: detail.errorCode, retry: detail.retry });
|
|
8782
|
+
}
|
|
8783
|
+
if (info.status === DeploymentStatus.SuccessfulUninstall) {
|
|
8784
|
+
return fail5("deployment_uninstalled", `Deployment '${info.deploymentName}' (${deploymentKey}) has been uninstalled, so there is nothing to upgrade.`, "The install route would create a new deployment under that name rather than upgrade this one. Deploy the package afresh with 'uip solution deploy run', or pick a live deployment from 'uip solution deploy list'.", INVALID_TARGET);
|
|
8785
|
+
}
|
|
8786
|
+
let scope;
|
|
8787
|
+
if (info.locationKey) {
|
|
8788
|
+
const verdict = classifyFeedSession(auth);
|
|
8789
|
+
if (verdict !== "user") {
|
|
8790
|
+
const { message, instructions } = feedSessionRefusal(verdict, `Upgrading deployment '${info.deploymentName}', which is in a Personal Workspace or folder feed (folder key ${info.locationKey}),`, "run the upgrade from the Orchestrator UI");
|
|
8791
|
+
return fail5("feed_requires_user_session", message, instructions, {
|
|
8792
|
+
errorCode: "permission_denied",
|
|
8793
|
+
retry: "RetryWillNotFix"
|
|
8794
|
+
});
|
|
8795
|
+
}
|
|
8796
|
+
scope = folderKeyInitOverride(info.locationKey);
|
|
8797
|
+
} else if (options.personalWorkspace) {
|
|
8798
|
+
return fail5("invalid_target", `Deployment '${info.deploymentName}' (${deploymentKey}) is on the tenant feed, not in a Personal Workspace.`, "Drop --personal-workspace: with a deployment key the feed is read from the deployment itself.", INVALID_TARGET);
|
|
8799
|
+
}
|
|
8800
|
+
const listFlag = info.locationKey ? `--feed ${info.locationKey}` : undefined;
|
|
8801
|
+
let live = info;
|
|
8802
|
+
let discardedDraftKey;
|
|
8803
|
+
if (info.status === DeploymentStatus.Draft) {
|
|
8804
|
+
const draft = {
|
|
8805
|
+
key: info.deploymentKey,
|
|
8806
|
+
firstInstall: info.operation === DeploymentOperation.Install,
|
|
8807
|
+
activationSetup: info.operation === DeploymentOperation.ActivationSetup,
|
|
8808
|
+
targetVersion: info.targetPackageVersion ?? info.packageVersion,
|
|
8809
|
+
liveVersion: info.packageVersion
|
|
8810
|
+
};
|
|
8811
|
+
const refusal = refusePendingDraft(options, {
|
|
8812
|
+
deploymentName: info.deploymentName,
|
|
8813
|
+
packageName: info.packageName,
|
|
8814
|
+
listFlag
|
|
8815
|
+
}, draft);
|
|
8816
|
+
if (refusal) {
|
|
8817
|
+
return refusal;
|
|
8818
|
+
}
|
|
8819
|
+
const discarded = await discardDraftAsync(pipelinesApi, info.deploymentName, draft.key, scope);
|
|
8820
|
+
if (discarded) {
|
|
8821
|
+
return discarded;
|
|
8822
|
+
}
|
|
8823
|
+
const [relookupError, current] = await catchError(pipelinesApi.pipelinesGetDeploymentByKey({
|
|
8824
|
+
key: info.installDeploymentKey
|
|
8825
|
+
}));
|
|
8826
|
+
if (relookupError || !current) {
|
|
8827
|
+
const detail = relookupError ? await extractErrorDetails(relookupError) : undefined;
|
|
8828
|
+
return fail5("lookup_failed", `The pending draft ${draft.key} of '${info.deploymentName}' was discarded, but the live deployment could not be looked up again${detail ? `: ${detail.message}` : "."}`, `Nothing was upgraded. Retry the upgrade: uip solution deploy upgrade ${info.installDeploymentKey}${options.version ? ` --version ${options.version}` : ""}`, detail ? { errorCode: detail.errorCode, retry: detail.retry } : {});
|
|
8829
|
+
}
|
|
8830
|
+
if (current.status === DeploymentStatus.Draft || current.status === DeploymentStatus.SuccessfulUninstall) {
|
|
8831
|
+
return fail5("pending_draft", `The pending draft ${draft.key} of '${info.deploymentName}' was discarded, but the deployment's newest generation is now ${current.status === DeploymentStatus.Draft ? `another draft (${current.deploymentKey})` : "an uninstall"}.`, "Nothing was upgraded. Another operation on this deployment ran in between; check it with 'uip solution deploy list', then retry.", INVALID_TARGET);
|
|
8832
|
+
}
|
|
8833
|
+
live = current;
|
|
8834
|
+
discardedDraftKey = draft.key;
|
|
8835
|
+
}
|
|
8836
|
+
return {
|
|
8837
|
+
ok: true,
|
|
8838
|
+
deploymentName: live.deploymentName,
|
|
8839
|
+
packageName: live.packageName,
|
|
8840
|
+
scope,
|
|
8841
|
+
listFlag,
|
|
8842
|
+
fromVersion: live.packageVersion ?? undefined,
|
|
8843
|
+
installDeploymentKey: live.installDeploymentKey,
|
|
8844
|
+
discardedDraftKey
|
|
8845
|
+
};
|
|
8846
|
+
}
|
|
8847
|
+
async function resolveTargetByKeyViaSolutionsAsync(pipelinesApi, auth, options, deploymentKey) {
|
|
8848
|
+
let scope;
|
|
8849
|
+
if (options.personalWorkspace) {
|
|
8850
|
+
const [pwError, pw] = await catchError(resolvePersonalWorkspace({
|
|
8851
|
+
tenant: options.tenant,
|
|
8852
|
+
loginValidity: options.loginValidity,
|
|
8853
|
+
envFilePath: options.envFilePath
|
|
8854
|
+
}));
|
|
8855
|
+
if (pwError || !pw) {
|
|
8856
|
+
return fail5("personal_workspace_failed", pwError?.message ?? "Personal Workspace could not be resolved.", "Ensure your user has a Personal Workspace, then retry.");
|
|
8857
|
+
}
|
|
8858
|
+
scope = folderKeyInitOverride(pw.key);
|
|
8859
|
+
}
|
|
8860
|
+
const deploymentsApi = new DeploymentsApi(new Configuration({
|
|
8861
|
+
basePath: auth.basePath,
|
|
8862
|
+
accessToken: auth.accessToken
|
|
8863
|
+
}));
|
|
8864
|
+
const [readError, row] = await catchError(deploymentsApi.deploymentsGetByKey({ deploymentKey }, scope));
|
|
8865
|
+
if (readError || !row) {
|
|
8866
|
+
const detail = readError ? await extractErrorDetails(readError) : undefined;
|
|
8867
|
+
if (!detail || detail.context?.httpStatus === 404) {
|
|
8868
|
+
return fail5("deployment_not_found", `Deployment '${deploymentKey}' was not found in ${scope ? "your Personal Workspace feed" : `the tenant feed of '${auth.tenantName}'`}.`, `Check the key with 'uip solution deploy list'. Neither the Pipelines deployment lookup nor the Solutions read found it; the latter reads a key on the tenant feed, or with --personal-workspace in your workspace, so address a folder-feed deployment with --name and --package-name instead.`, { errorCode: "not_found", retry: "RetryWillNotFix" });
|
|
8869
|
+
}
|
|
8870
|
+
return fail5("lookup_failed", detail.details ? `${detail.message}: ${detail.details}` : detail.message, LOOKUP_FAILED_INSTRUCTIONS, { errorCode: detail.errorCode, retry: detail.retry });
|
|
8871
|
+
}
|
|
8872
|
+
if (row.status === "SuccessfulUninstall") {
|
|
8873
|
+
return fail5("deployment_uninstalled", `Deployment '${row.name}' (${deploymentKey}) has been uninstalled, so there is nothing to upgrade.`, "The install route would create a new deployment under that name rather than upgrade this one. Deploy the package afresh with 'uip solution deploy run', or pick a live deployment from 'uip solution deploy list'.", INVALID_TARGET);
|
|
8874
|
+
}
|
|
8875
|
+
const target = await resolveTargetByNameAsync(pipelinesApi, auth, {
|
|
8876
|
+
...options,
|
|
8877
|
+
deploymentName: row.name,
|
|
8878
|
+
packageName: row.packageName
|
|
8879
|
+
});
|
|
8880
|
+
if (!target.ok) {
|
|
8881
|
+
return target;
|
|
8882
|
+
}
|
|
8883
|
+
return { ...target, installDeploymentKey: row.installDeploymentKey };
|
|
8884
|
+
}
|
|
8885
|
+
function refusePendingDraft(options, target, draft) {
|
|
8886
|
+
const targetVersion = draft.targetVersion ?? "<version>";
|
|
8887
|
+
const feed = target.listFlag ? ` ${target.listFlag}` : "";
|
|
8888
|
+
const retryDraft = `uip solution deploy run --name ${target.deploymentName} --package-name ${target.packageName} --package-version ${targetVersion} --config-file <fixed-config>${feed} --yes`;
|
|
8889
|
+
if (draft.firstInstall) {
|
|
8890
|
+
return fail5("pending_draft", `Deployment '${target.deploymentName}' was never installed: its first install (${draft.key}) is still a pending draft, so there is nothing to upgrade.`, `The draft is an attempt that never completed, usually one rejected by validation, or one still running. To retry it with a corrected configuration: ${retryDraft} (the server re-validates the draft with the new file). To abandon it, delete it in Orchestrator.`, INVALID_TARGET);
|
|
8891
|
+
}
|
|
8892
|
+
if (options.discardDraft) {
|
|
8893
|
+
return;
|
|
8894
|
+
}
|
|
8895
|
+
if (draft.activationSetup) {
|
|
8896
|
+
return fail5("pending_draft", `Deployment '${target.deploymentName}' has an unfinished Setup activation (${draft.key}): the Orchestrator "Setup activation" wizard was started on version ${targetVersion} and not completed.`, `While that draft exists the server would run it in place of this upgrade, or refuse a different version. Finish the setup in Orchestrator (Solutions, the deployment, Setup activation), then retry; or discard it and upgrade the live version: ${discardRetryCommand(options, target, draft.key)}`, INVALID_TARGET);
|
|
8897
|
+
}
|
|
8898
|
+
const live = draft.liveVersion ? ` ${draft.liveVersion}` : "";
|
|
8899
|
+
return fail5("pending_draft", `Deployment '${target.deploymentName}' has a pending draft (${draft.key}) for version ${targetVersion}: an earlier attempt that never completed, usually one rejected by validation, or one still running.`, `The server would run that draft, with the configuration it was built with, in place of this upgrade, or refuse a different version. To discard it and upgrade the live version${live}: ${discardRetryCommand(options, target, draft.key)}
|
|
8900
|
+
To retry the draft itself with a corrected configuration instead: ${retryDraft}`, INVALID_TARGET);
|
|
8901
|
+
}
|
|
8902
|
+
async function discardDraftAsync(pipelinesApi, deploymentName, draftKey, scope) {
|
|
8903
|
+
const [error] = await catchError(pipelinesApi.pipelinesDeleteDeploymentByKey({ deploymentKey: draftKey }, scope));
|
|
8904
|
+
if (!error) {
|
|
8905
|
+
return;
|
|
8906
|
+
}
|
|
8907
|
+
const detail = await extractErrorDetails(error);
|
|
8908
|
+
const status = detail.context?.httpStatus;
|
|
8909
|
+
const hasBody = !isBareResponse(detail.details);
|
|
8910
|
+
if (hasServerErrorCode(detail.details, "DeploymentNotFound") || status === 404 && hasBody) {
|
|
8911
|
+
return;
|
|
8912
|
+
}
|
|
8913
|
+
const message = `The pending draft ${draftKey} of '${deploymentName}' could not be discarded: ${hasBody ? `${detail.message}: ${detail.details}` : detail.message}`;
|
|
8914
|
+
if (status === 405 || status === 404 && !hasBody) {
|
|
8915
|
+
return fail5("discard_draft_failed", message, "This server cannot discard drafts through the Pipelines API yet. Nothing was upgraded. Delete the draft in Orchestrator, then retry the upgrade.", {
|
|
8916
|
+
errorCode: status === 405 ? "method_not_allowed" : "not_found",
|
|
8917
|
+
retry: "RetryWillNotFix"
|
|
8918
|
+
});
|
|
8919
|
+
}
|
|
8920
|
+
if (hasServerErrorCode(detail.details, "DeploymentAlreadyInProgress")) {
|
|
8921
|
+
return fail5("discard_draft_failed", message, "The draft is still being validated. Nothing was upgraded. Wait for its run to finish (uip solution deploy list shows it), then retry.", { errorCode: "invalid_argument", retry: "RetryLater" });
|
|
8922
|
+
}
|
|
8923
|
+
if (hasServerErrorCode(detail.details, "DeploymentInvalidState")) {
|
|
8924
|
+
return fail5("discard_draft_failed", message, "The draft is no longer one that can be discarded: it moved on (it was promoted, or it is a rollback). Nothing was upgraded. Check the deployment with 'uip solution deploy list', then retry.", INVALID_TARGET);
|
|
8925
|
+
}
|
|
8926
|
+
return fail5("discard_draft_failed", message, "Nothing was upgraded. Retry, or delete the draft in Orchestrator and then retry the upgrade.", { errorCode: detail.errorCode, retry: detail.retry });
|
|
8927
|
+
}
|
|
8928
|
+
function classifyTerminalFailure(status) {
|
|
8929
|
+
return status === "ValidationFailed" || status === "ConflictFixingError" ? { errorCode: "invalid_argument", retry: "RetryWillNotFix" } : {};
|
|
8930
|
+
}
|
|
8931
|
+
async function describeUpgradeFailureAsync(basePath, accessToken, pipelineDeploymentId, finalStatus, scope, signal) {
|
|
8932
|
+
const fromPipeline = formatDeploymentFailureDetails(pipelineDeploymentId, finalStatus);
|
|
8933
|
+
const explained = finalStatus.validationResult?.validationErrors?.length || finalStatus.validationResult?.conflictErrors?.length || finalStatus.conflictFixingErrors?.length || finalStatus.deploymentScheduleErrors?.length || finalStatus.deploymentResult?.errorMessage;
|
|
8934
|
+
if (explained || !finalStatus.deploymentKey) {
|
|
8935
|
+
return fromPipeline;
|
|
8936
|
+
}
|
|
8937
|
+
const deploymentsApi = new DeploymentsApi(new Configuration({ basePath, accessToken }));
|
|
8938
|
+
const reason = await describeValidationFailureAsync(deploymentsApi, finalStatus.deploymentKey, SolutionDeploymentAction.Upgrade, scope, signal);
|
|
8939
|
+
return reason === NO_SERVER_REASON ? fromPipeline : `Validation: ${reason}
|
|
8940
|
+
${fromPipeline}`;
|
|
8941
|
+
}
|
|
8585
8942
|
|
|
8586
8943
|
// src/services/packages-list-service.ts
|
|
8587
|
-
var fail6 = (reason, message, instructions,
|
|
8944
|
+
var fail6 = (reason, message, instructions, extras = {}) => ({
|
|
8588
8945
|
ok: false,
|
|
8589
8946
|
reason,
|
|
8590
8947
|
message,
|
|
8591
8948
|
instructions,
|
|
8592
|
-
exitCode
|
|
8949
|
+
exitCode: 1,
|
|
8950
|
+
...extras
|
|
8593
8951
|
});
|
|
8594
8952
|
async function listPackagesAsync(options = {}) {
|
|
8595
8953
|
const limit = options.limit ?? DEFAULT_PAGE_SIZE;
|
|
@@ -8610,9 +8968,10 @@ async function listPackagesAsync(options = {}) {
|
|
|
8610
8968
|
envFilePath: options.envFilePath
|
|
8611
8969
|
}));
|
|
8612
8970
|
if (scopeError) {
|
|
8613
|
-
|
|
8971
|
+
const { message, instructions, ...extras } = await describeFeedResolutionFailureAsync(scopeError, options, auth);
|
|
8972
|
+
return fail6("feed_resolution_failed", message, instructions, extras);
|
|
8614
8973
|
}
|
|
8615
|
-
const searchApi = new SearchApi(new
|
|
8974
|
+
const searchApi = new SearchApi(new Configuration({
|
|
8616
8975
|
basePath: auth.basePath,
|
|
8617
8976
|
accessToken: auth.accessToken
|
|
8618
8977
|
}));
|
|
@@ -8626,8 +8985,10 @@ async function listPackagesAsync(options = {}) {
|
|
|
8626
8985
|
}
|
|
8627
8986
|
}, feedScopeInitOverride(scope)));
|
|
8628
8987
|
if (listError) {
|
|
8629
|
-
const { message,
|
|
8630
|
-
|
|
8988
|
+
const { message, instructions, ...extras } = await describeSearchFailureAsync(listError, "packages", auth, {
|
|
8989
|
+
userSessionsOnly: true
|
|
8990
|
+
});
|
|
8991
|
+
return fail6("list_request_failed", message, instructions, extras);
|
|
8631
8992
|
}
|
|
8632
8993
|
const packages = result.values ?? [];
|
|
8633
8994
|
return {
|
|
@@ -8641,4 +9002,4 @@ async function listPackagesAsync(options = {}) {
|
|
|
8641
9002
|
|
|
8642
9003
|
export { extractDeploymentErrors, formatDeploymentErrorsAsText, activateDeploymentAsync, listDeploymentsAsync, deployRunAsync, uninstallDeploymentAsync, upgradeDeploymentAsync, listPackagesAsync };
|
|
8643
9004
|
|
|
8644
|
-
//# debugId=
|
|
9005
|
+
//# debugId=3F2F38B7889B837064756E2164756E21
|