@sfdxy/anypoint-connect 0.14.0 → 0.16.2
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 +39 -16
- package/dist/analysis/LogAnalyzer.d.ts +10 -4
- package/dist/analysis/LogAnalyzer.d.ts.map +1 -1
- package/dist/analysis/LogAnalyzer.js +29 -18
- package/dist/analysis/LogAnalyzer.js.map +1 -1
- package/dist/analysis/error-context.d.ts +2 -0
- package/dist/analysis/error-context.d.ts.map +1 -1
- package/dist/analysis/error-context.js +2 -1
- package/dist/analysis/error-context.js.map +1 -1
- package/dist/analysis/error-grouper.js +1 -1
- package/dist/analysis/error-grouper.js.map +1 -1
- package/dist/analysis/filter.d.ts +54 -0
- package/dist/analysis/filter.d.ts.map +1 -0
- package/dist/analysis/filter.js +100 -0
- package/dist/analysis/filter.js.map +1 -0
- package/dist/analysis/{utils.d.ts → normalize.d.ts} +1 -1
- package/dist/analysis/normalize.d.ts.map +1 -0
- package/dist/analysis/{utils.js → normalize.js} +2 -2
- package/dist/analysis/normalize.js.map +1 -0
- package/dist/analysis/parser.d.ts.map +1 -1
- package/dist/analysis/parser.js +45 -17
- package/dist/analysis/parser.js.map +1 -1
- package/dist/analysis/pattern-detector.js +1 -1
- package/dist/analysis/pattern-detector.js.map +1 -1
- package/dist/analysis/stats.d.ts +25 -0
- package/dist/analysis/stats.d.ts.map +1 -1
- package/dist/analysis/stats.js +35 -1
- package/dist/analysis/stats.js.map +1 -1
- package/dist/analysis/summary.d.ts +51 -0
- package/dist/analysis/summary.d.ts.map +1 -0
- package/dist/analysis/summary.js +47 -0
- package/dist/analysis/summary.js.map +1 -0
- package/dist/analysis/types.d.ts +14 -0
- package/dist/analysis/types.d.ts.map +1 -1
- package/dist/api/ApiManagerApi.d.ts +53 -0
- package/dist/api/ApiManagerApi.d.ts.map +1 -1
- package/dist/api/ApiManagerApi.js +53 -2
- package/dist/api/ApiManagerApi.js.map +1 -1
- package/dist/api/CloudHub2Api.d.ts +24 -4
- package/dist/api/CloudHub2Api.d.ts.map +1 -1
- package/dist/api/CloudHub2Api.js +10 -22
- package/dist/api/CloudHub2Api.js.map +1 -1
- package/dist/api/DesignCenterApi.d.ts +0 -20
- package/dist/api/DesignCenterApi.d.ts.map +1 -1
- package/dist/api/DesignCenterApi.js +0 -22
- package/dist/api/DesignCenterApi.js.map +1 -1
- package/dist/api/ExchangeApi.d.ts +15 -0
- package/dist/api/ExchangeApi.d.ts.map +1 -1
- package/dist/api/ExchangeApi.js +51 -0
- package/dist/api/ExchangeApi.js.map +1 -1
- package/dist/api/LogsApi.d.ts +50 -18
- package/dist/api/LogsApi.d.ts.map +1 -1
- package/dist/api/LogsApi.js +69 -98
- package/dist/api/LogsApi.js.map +1 -1
- package/dist/api/MonitoringApi.d.ts +137 -93
- package/dist/api/MonitoringApi.d.ts.map +1 -1
- package/dist/api/MonitoringApi.js +471 -204
- package/dist/api/MonitoringApi.js.map +1 -1
- package/dist/api/RuntimesApi.d.ts +56 -0
- package/dist/api/RuntimesApi.d.ts.map +1 -0
- package/dist/api/RuntimesApi.js +58 -0
- package/dist/api/RuntimesApi.js.map +1 -0
- package/dist/auth/FileStore.js +1 -1
- package/dist/auth/FileStore.js.map +1 -1
- package/dist/cli.js +4 -2
- package/dist/cli.js.map +1 -1
- package/dist/client/AnypointClient.d.ts +3 -1
- package/dist/client/AnypointClient.d.ts.map +1 -1
- package/dist/client/AnypointClient.js +5 -2
- package/dist/client/AnypointClient.js.map +1 -1
- package/dist/client/smoke-test.d.ts +111 -0
- package/dist/client/smoke-test.d.ts.map +1 -0
- package/dist/client/smoke-test.js +275 -0
- package/dist/client/smoke-test.js.map +1 -0
- package/dist/commands/api.d.ts +1 -1
- package/dist/commands/api.d.ts.map +1 -1
- package/dist/commands/api.js +175 -30
- package/dist/commands/api.js.map +1 -1
- package/dist/commands/app-change-cli.d.ts +52 -0
- package/dist/commands/app-change-cli.d.ts.map +1 -0
- package/dist/commands/app-change-cli.js +149 -0
- package/dist/commands/app-change-cli.js.map +1 -0
- package/dist/commands/apps.d.ts +1 -1
- package/dist/commands/apps.d.ts.map +1 -1
- package/dist/commands/apps.js +199 -18
- package/dist/commands/apps.js.map +1 -1
- package/dist/commands/auth.js +1 -1
- package/dist/commands/auth.js.map +1 -1
- package/dist/commands/compare.d.ts +8 -0
- package/dist/commands/compare.d.ts.map +1 -0
- package/dist/commands/compare.js +104 -0
- package/dist/commands/compare.js.map +1 -0
- package/dist/commands/config.js +1 -1
- package/dist/commands/config.js.map +1 -1
- package/dist/commands/deploy.d.ts +6 -1
- package/dist/commands/deploy.d.ts.map +1 -1
- package/dist/commands/deploy.js +89 -91
- package/dist/commands/deploy.js.map +1 -1
- package/dist/commands/design-center.d.ts +1 -1
- package/dist/commands/design-center.d.ts.map +1 -1
- package/dist/commands/design-center.js +36 -25
- package/dist/commands/design-center.js.map +1 -1
- package/dist/commands/index.d.ts +1 -0
- package/dist/commands/index.d.ts.map +1 -1
- package/dist/commands/index.js +1 -0
- package/dist/commands/index.js.map +1 -1
- package/dist/commands/logs.d.ts +3 -2
- package/dist/commands/logs.d.ts.map +1 -1
- package/dist/commands/logs.js +142 -17
- package/dist/commands/logs.js.map +1 -1
- package/dist/commands/monitor.d.ts +5 -6
- package/dist/commands/monitor.d.ts.map +1 -1
- package/dist/commands/monitor.js +210 -230
- package/dist/commands/monitor.js.map +1 -1
- package/dist/commands/runtimes.d.ts +7 -0
- package/dist/commands/runtimes.d.ts.map +1 -0
- package/dist/commands/runtimes.js +66 -0
- package/dist/commands/runtimes.js.map +1 -0
- package/dist/commands/shared.d.ts +2 -0
- package/dist/commands/shared.d.ts.map +1 -1
- package/dist/commands/shared.js +16 -1
- package/dist/commands/shared.js.map +1 -1
- package/dist/{utils/config.d.ts → config/profiles.d.ts} +9 -1
- package/dist/config/profiles.d.ts.map +1 -0
- package/dist/{utils/config.js → config/profiles.js} +1 -1
- package/dist/config/profiles.js.map +1 -0
- package/dist/index.d.ts +12 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +12 -2
- package/dist/index.js.map +1 -1
- package/dist/mcp/prompts.js +7 -7
- package/dist/mcp/prompts.js.map +1 -1
- package/dist/mcp/server.d.ts +14 -0
- package/dist/mcp/server.d.ts.map +1 -0
- package/dist/mcp/server.js +61 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/mcp/tools/analysis.d.ts.map +1 -1
- package/dist/mcp/tools/analysis.js +74 -46
- package/dist/mcp/tools/analysis.js.map +1 -1
- package/dist/mcp/tools/anypoint-mq.d.ts.map +1 -1
- package/dist/mcp/tools/anypoint-mq.js +25 -54
- package/dist/mcp/tools/anypoint-mq.js.map +1 -1
- package/dist/mcp/tools/api-manager.d.ts +2 -1
- package/dist/mcp/tools/api-manager.d.ts.map +1 -1
- package/dist/mcp/tools/api-manager.js +154 -89
- package/dist/mcp/tools/api-manager.js.map +1 -1
- package/dist/mcp/tools/app-inputs.d.ts +92 -0
- package/dist/mcp/tools/app-inputs.d.ts.map +1 -0
- package/dist/mcp/tools/app-inputs.js +142 -0
- package/dist/mcp/tools/app-inputs.js.map +1 -0
- package/dist/mcp/tools/apps-deploy.d.ts +13 -0
- package/dist/mcp/tools/apps-deploy.d.ts.map +1 -0
- package/dist/mcp/tools/apps-deploy.js +358 -0
- package/dist/mcp/tools/apps-deploy.js.map +1 -0
- package/dist/mcp/tools/apps-lifecycle.d.ts +11 -0
- package/dist/mcp/tools/apps-lifecycle.d.ts.map +1 -0
- package/dist/mcp/tools/apps-lifecycle.js +244 -0
- package/dist/mcp/tools/apps-lifecycle.js.map +1 -0
- package/dist/mcp/tools/apps-read.d.ts +9 -0
- package/dist/mcp/tools/apps-read.d.ts.map +1 -0
- package/dist/mcp/tools/apps-read.js +414 -0
- package/dist/mcp/tools/apps-read.js.map +1 -0
- package/dist/mcp/tools/artifacts.d.ts +8 -0
- package/dist/mcp/tools/artifacts.d.ts.map +1 -0
- package/dist/mcp/tools/artifacts.js +81 -0
- package/dist/mcp/tools/artifacts.js.map +1 -0
- package/dist/mcp/tools/audit.d.ts.map +1 -1
- package/dist/mcp/tools/audit.js +9 -17
- package/dist/mcp/tools/audit.js.map +1 -1
- package/dist/mcp/tools/design-center.d.ts +2 -3
- package/dist/mcp/tools/design-center.d.ts.map +1 -1
- package/dist/mcp/tools/design-center.js +41 -185
- package/dist/mcp/tools/design-center.js.map +1 -1
- package/dist/mcp/tools/exchange.d.ts +1 -2
- package/dist/mcp/tools/exchange.d.ts.map +1 -1
- package/dist/mcp/tools/exchange.js +51 -312
- package/dist/mcp/tools/exchange.js.map +1 -1
- package/dist/mcp/tools/governance.d.ts +8 -0
- package/dist/mcp/tools/governance.d.ts.map +1 -0
- package/dist/mcp/tools/governance.js +59 -0
- package/dist/mcp/tools/governance.js.map +1 -0
- package/dist/mcp/tools/identity.d.ts.map +1 -1
- package/dist/mcp/tools/identity.js +15 -36
- package/dist/mcp/tools/identity.js.map +1 -1
- package/dist/mcp/tools/index.d.ts +5 -1
- package/dist/mcp/tools/index.d.ts.map +1 -1
- package/dist/mcp/tools/index.js +5 -1
- package/dist/mcp/tools/index.js.map +1 -1
- package/dist/mcp/tools/logs.d.ts +48 -2
- package/dist/mcp/tools/logs.d.ts.map +1 -1
- package/dist/mcp/tools/logs.js +204 -54
- package/dist/mcp/tools/logs.js.map +1 -1
- package/dist/mcp/tools/monitoring.d.ts +1 -2
- package/dist/mcp/tools/monitoring.d.ts.map +1 -1
- package/dist/mcp/tools/monitoring.js +78 -285
- package/dist/mcp/tools/monitoring.js.map +1 -1
- package/dist/mcp/tools/object-store.d.ts +1 -1
- package/dist/mcp/tools/object-store.d.ts.map +1 -1
- package/dist/mcp/tools/object-store.js +19 -59
- package/dist/mcp/tools/object-store.js.map +1 -1
- package/dist/mcp/tools/profile.d.ts.map +1 -1
- package/dist/mcp/tools/profile.js +15 -35
- package/dist/mcp/tools/profile.js.map +1 -1
- package/dist/mcp/tools/shared.d.ts +43 -0
- package/dist/mcp/tools/shared.d.ts.map +1 -1
- package/dist/mcp/tools/shared.js +54 -0
- package/dist/mcp/tools/shared.js.map +1 -1
- package/dist/mcp.d.ts +3 -11
- package/dist/mcp.d.ts.map +1 -1
- package/dist/mcp.js +4 -56
- package/dist/mcp.js.map +1 -1
- package/dist/safety/deployment.d.ts +47 -2
- package/dist/safety/deployment.d.ts.map +1 -1
- package/dist/safety/deployment.js +139 -6
- package/dist/safety/deployment.js.map +1 -1
- package/dist/safety/guards.d.ts +2 -2
- package/dist/safety/guards.d.ts.map +1 -1
- package/dist/safety/guards.js +4 -4
- package/dist/safety/guards.js.map +1 -1
- package/dist/safety/paths.d.ts +29 -0
- package/dist/safety/paths.d.ts.map +1 -0
- package/dist/safety/paths.js +76 -0
- package/dist/safety/paths.js.map +1 -0
- package/dist/safety/runtime.d.ts +24 -0
- package/dist/safety/runtime.d.ts.map +1 -0
- package/dist/safety/runtime.js +59 -0
- package/dist/safety/runtime.js.map +1 -0
- package/dist/safety/secret-sources.d.ts +51 -0
- package/dist/safety/secret-sources.d.ts.map +1 -0
- package/dist/safety/secret-sources.js +124 -0
- package/dist/safety/secret-sources.js.map +1 -0
- package/dist/safety/secrets.d.ts +32 -0
- package/dist/safety/secrets.d.ts.map +1 -0
- package/dist/safety/secrets.js +91 -0
- package/dist/safety/secrets.js.map +1 -0
- package/dist/utils/errors.d.ts +14 -1
- package/dist/utils/errors.d.ts.map +1 -1
- package/dist/utils/errors.js +31 -3
- package/dist/utils/errors.js.map +1 -1
- package/dist/workflows/app-change.d.ts +124 -0
- package/dist/workflows/app-change.d.ts.map +1 -0
- package/dist/workflows/app-change.js +305 -0
- package/dist/workflows/app-change.js.map +1 -0
- package/dist/workflows/app-state.d.ts +60 -0
- package/dist/workflows/app-state.d.ts.map +1 -0
- package/dist/workflows/app-state.js +95 -0
- package/dist/workflows/app-state.js.map +1 -0
- package/dist/workflows/artifact-compare.d.ts +134 -0
- package/dist/workflows/artifact-compare.d.ts.map +1 -0
- package/dist/workflows/artifact-compare.js +419 -0
- package/dist/workflows/artifact-compare.js.map +1 -0
- package/dist/workflows/asset-naming.d.ts +72 -0
- package/dist/workflows/asset-naming.d.ts.map +1 -0
- package/dist/workflows/asset-naming.js +116 -0
- package/dist/workflows/asset-naming.js.map +1 -0
- package/dist/{api/DesignCenterWorkflow.d.ts → workflows/design-center.d.ts} +4 -4
- package/dist/workflows/design-center.d.ts.map +1 -0
- package/dist/{api/DesignCenterWorkflow.js → workflows/design-center.js} +1 -1
- package/dist/workflows/design-center.js.map +1 -0
- package/dist/workflows/jar-deployment.d.ts +156 -0
- package/dist/workflows/jar-deployment.d.ts.map +1 -0
- package/dist/workflows/jar-deployment.js +178 -0
- package/dist/workflows/jar-deployment.js.map +1 -0
- package/package.json +12 -4
- package/dist/analysis/utils.d.ts.map +0 -1
- package/dist/analysis/utils.js.map +0 -1
- package/dist/api/DesignCenterWorkflow.d.ts.map +0 -1
- package/dist/api/DesignCenterWorkflow.js.map +0 -1
- package/dist/api/index.d.ts +0 -13
- package/dist/api/index.d.ts.map +0 -1
- package/dist/api/index.js +0 -13
- package/dist/api/index.js.map +0 -1
- package/dist/mcp/tools/applications.d.ts +0 -4
- package/dist/mcp/tools/applications.d.ts.map +0 -1
- package/dist/mcp/tools/applications.js +0 -815
- package/dist/mcp/tools/applications.js.map +0 -1
- package/dist/utils/config.d.ts.map +0 -1
- package/dist/utils/config.js.map +0 -1
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deployment state model
|
|
3
|
+
* One interpretation of CloudHub 2.0 deployment, application and replica status, shared by
|
|
4
|
+
* status reads and every wait loop. It hides the transient FAILED the platform reports while
|
|
5
|
+
* a stopped app starts, and never treats a status left over from the previous spec as settled.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Poll until the deployment is terminal (settled, failed past the grace window, or degraded)
|
|
9
|
+
* or the timeout passes. Never throws for a failed deployment; the caller reads the snapshot.
|
|
10
|
+
*/
|
|
11
|
+
export async function waitForAppPhase(api, orgId, envId, deploymentId, options = {}) {
|
|
12
|
+
const startedAt = Date.now();
|
|
13
|
+
const timeoutMs = options.timeoutMs ?? 300_000;
|
|
14
|
+
const pollIntervalMs = options.pollIntervalMs ?? 5_000;
|
|
15
|
+
const phaseOptions = {
|
|
16
|
+
expectedSpecVersion: options.expectedSpecVersion,
|
|
17
|
+
previousModifiedDate: options.previousModifiedDate,
|
|
18
|
+
sinceChange: options.sinceChange ?? startedAt,
|
|
19
|
+
graceMs: options.graceMs,
|
|
20
|
+
};
|
|
21
|
+
while (true) {
|
|
22
|
+
const deployment = await api.getDeployment(orgId, envId, deploymentId);
|
|
23
|
+
const snapshot = deploymentPhase(deployment, phaseOptions);
|
|
24
|
+
options.onStatus?.(snapshot, deployment);
|
|
25
|
+
const waitedMs = Date.now() - startedAt;
|
|
26
|
+
if (snapshot.terminal)
|
|
27
|
+
return { deployment, snapshot, timedOut: false, waitedMs };
|
|
28
|
+
if (waitedMs >= timeoutMs)
|
|
29
|
+
return { deployment, snapshot, timedOut: true, waitedMs };
|
|
30
|
+
await new Promise((resolve) => setTimeout(resolve, pollIntervalMs));
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
export const DEFAULT_GRACE_MS = 90_000;
|
|
34
|
+
const STOPPED_REPLICA_STATES = new Set(['STOPPED', 'TERMINATED', 'DELETED']);
|
|
35
|
+
const FAILED_PLATFORM_STATUSES = new Set(['FAILED', 'DEPLOYMENT_FAILED']);
|
|
36
|
+
/** Interpret a deployment detail as a single phase. Pure; safe to call on every poll. */
|
|
37
|
+
export function deploymentPhase(deployment, options = {}) {
|
|
38
|
+
const now = options.now ?? Date.now();
|
|
39
|
+
const graceMs = options.graceMs ?? DEFAULT_GRACE_MS;
|
|
40
|
+
const since = options.sinceChange ?? deployment.lastModifiedDate ?? 0;
|
|
41
|
+
const withinGrace = now - since < graceMs;
|
|
42
|
+
const desiredState = deployment.application?.desiredState ?? 'STARTED';
|
|
43
|
+
const platformStatus = deployment.status;
|
|
44
|
+
const replicas = deployment.replicas ?? [];
|
|
45
|
+
const specVersion = options.expectedSpecVersion ?? deployment.desiredVersion;
|
|
46
|
+
const onSpec = (version) => !specVersion || version === specVersion;
|
|
47
|
+
const started = replicas.filter((r) => r.state === 'STARTED');
|
|
48
|
+
const startedOnSpec = started.filter((r) => onSpec(r.currentDeploymentVersion));
|
|
49
|
+
const failed = replicas.filter((r) => r.state === 'FAILED');
|
|
50
|
+
const counts = {
|
|
51
|
+
total: replicas.length,
|
|
52
|
+
started: started.length,
|
|
53
|
+
onSpec: startedOnSpec.length,
|
|
54
|
+
failed: failed.length,
|
|
55
|
+
};
|
|
56
|
+
const base = { desiredState, platformStatus, replicas: counts, specVersion };
|
|
57
|
+
const failureReason = failed.find((r) => r.reason)?.reason;
|
|
58
|
+
// A change sent after the deployment's last modification has not been picked up yet.
|
|
59
|
+
// Compared against the server's own earlier timestamp, so local clock skew cannot matter.
|
|
60
|
+
const stale = options.previousModifiedDate !== undefined &&
|
|
61
|
+
!options.expectedSpecVersion &&
|
|
62
|
+
(deployment.lastModifiedDate ?? 0) <= options.previousModifiedDate;
|
|
63
|
+
if (desiredState === 'STOPPED') {
|
|
64
|
+
const down = replicas.every((r) => STOPPED_REPLICA_STATES.has(r.state));
|
|
65
|
+
if (down && !stale && platformStatus !== 'APPLYING') {
|
|
66
|
+
return { ...base, phase: 'STOPPED', settled: true, terminal: true };
|
|
67
|
+
}
|
|
68
|
+
return { ...base, phase: 'STOPPING', settled: false, terminal: false };
|
|
69
|
+
}
|
|
70
|
+
const expected = deployment.target?.replicas ?? replicas.length;
|
|
71
|
+
// With autoscaling the replica count floats, so only an exact count is required without it.
|
|
72
|
+
const autoscaled = deployment.target?.deploymentSettings?.autoscaling?.enabled === true;
|
|
73
|
+
const countOk = autoscaled ? replicas.length > 0 : replicas.length === expected;
|
|
74
|
+
const allOnSpec = replicas.length > 0 && startedOnSpec.length === replicas.length && countOk;
|
|
75
|
+
if (!stale && platformStatus === 'APPLIED' && allOnSpec) {
|
|
76
|
+
return { ...base, phase: 'RUNNING', settled: true, terminal: true };
|
|
77
|
+
}
|
|
78
|
+
const platformFailed = FAILED_PLATFORM_STATUSES.has(platformStatus);
|
|
79
|
+
// Replica failure reasons also appear during normal start-up, so only time confirms a failure.
|
|
80
|
+
if ((platformFailed || failed.length > 0) && !withinGrace) {
|
|
81
|
+
const reason = failureReason ?? `Platform reports ${platformStatus}`;
|
|
82
|
+
if (startedOnSpec.length > 0)
|
|
83
|
+
return { ...base, phase: 'DEGRADED', settled: false, terminal: true, reason };
|
|
84
|
+
return { ...base, phase: 'FAILED', settled: false, terminal: true, reason };
|
|
85
|
+
}
|
|
86
|
+
const transitional = platformFailed || failed.length > 0 ? 'replicas still starting; failure not yet confirmed' : undefined;
|
|
87
|
+
if (started.length > 0 && startedOnSpec.length < started.length) {
|
|
88
|
+
return { ...base, phase: 'UPDATING', settled: false, terminal: false, reason: transitional ?? failureReason };
|
|
89
|
+
}
|
|
90
|
+
if (startedOnSpec.length > 0 && !allOnSpec) {
|
|
91
|
+
return { ...base, phase: 'UPDATING', settled: false, terminal: false, reason: transitional ?? failureReason };
|
|
92
|
+
}
|
|
93
|
+
return { ...base, phase: 'STARTING', settled: false, terminal: false, reason: transitional ?? failureReason };
|
|
94
|
+
}
|
|
95
|
+
//# sourceMappingURL=app-state.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"app-state.js","sourceRoot":"","sources":["../../src/workflows/app-state.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deployed artifact comparison
|
|
3
|
+
* Compares a Mule application jar with another jar or with a git checkout of its source.
|
|
4
|
+
* Only names, sizes, versions and hashes are reported, never file contents.
|
|
5
|
+
*/
|
|
6
|
+
import type { AnypointClient } from '../client/AnypointClient.js';
|
|
7
|
+
export interface JarEntryInfo {
|
|
8
|
+
size: number;
|
|
9
|
+
sha256: string;
|
|
10
|
+
/** Git blob ID of the entry bytes, for comparing with a git tree without reading files. */
|
|
11
|
+
gitSha1: string;
|
|
12
|
+
}
|
|
13
|
+
export interface PomInfo {
|
|
14
|
+
groupId?: string;
|
|
15
|
+
artifactId?: string;
|
|
16
|
+
version?: string;
|
|
17
|
+
/** Declared, non-test dependencies as `group:artifact:version`. */
|
|
18
|
+
dependencies: string[];
|
|
19
|
+
}
|
|
20
|
+
export interface JarIndex {
|
|
21
|
+
sha256: string;
|
|
22
|
+
size: number;
|
|
23
|
+
/** Every file entry except `repository/**`. */
|
|
24
|
+
entries: Map<string, JarEntryInfo>;
|
|
25
|
+
pom?: PomInfo;
|
|
26
|
+
pomVersion?: string;
|
|
27
|
+
/** Resolved dependencies from `classloader-model.json`, as `group:artifact:version`. */
|
|
28
|
+
resolvedDependencies?: string[];
|
|
29
|
+
skipped: {
|
|
30
|
+
repository: number;
|
|
31
|
+
directories: number;
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
export interface PathDifference {
|
|
35
|
+
path: string;
|
|
36
|
+
jarPath?: string;
|
|
37
|
+
deployedSize?: number;
|
|
38
|
+
repoSize?: number;
|
|
39
|
+
}
|
|
40
|
+
export interface DependencyDifferences {
|
|
41
|
+
changed: Array<{
|
|
42
|
+
dependency: string;
|
|
43
|
+
deployed: string;
|
|
44
|
+
repo: string;
|
|
45
|
+
}>;
|
|
46
|
+
missingInRepo: string[];
|
|
47
|
+
extraInRepo: string[];
|
|
48
|
+
}
|
|
49
|
+
export interface ArtifactComparison {
|
|
50
|
+
verdict: 'identical' | 'differs';
|
|
51
|
+
against: Record<string, unknown>;
|
|
52
|
+
sha256: {
|
|
53
|
+
deployed: string;
|
|
54
|
+
repo?: string;
|
|
55
|
+
match?: boolean;
|
|
56
|
+
};
|
|
57
|
+
pomVersion: {
|
|
58
|
+
deployed?: string;
|
|
59
|
+
repo?: string;
|
|
60
|
+
};
|
|
61
|
+
summary: {
|
|
62
|
+
compared: number;
|
|
63
|
+
changed: number;
|
|
64
|
+
missingInRepo: number;
|
|
65
|
+
extraInRepo: number;
|
|
66
|
+
unmapped: number;
|
|
67
|
+
skippedRepositoryEntries: number;
|
|
68
|
+
};
|
|
69
|
+
changed: PathDifference[];
|
|
70
|
+
missingInRepo: PathDifference[];
|
|
71
|
+
extraInRepo: PathDifference[];
|
|
72
|
+
dependencies: DependencyDifferences;
|
|
73
|
+
workingTreeChanges?: Array<{
|
|
74
|
+
path: string;
|
|
75
|
+
status: string;
|
|
76
|
+
}>;
|
|
77
|
+
notes: string[];
|
|
78
|
+
}
|
|
79
|
+
/** Git blob object ID: SHA-1 over `blob <len>\0` followed by the bytes. */
|
|
80
|
+
export declare function gitBlobSha1(bytes: Uint8Array): string;
|
|
81
|
+
/** Refuse refs that git could read as an option, or that hold unexpected characters. */
|
|
82
|
+
export declare function validateGitRef(ref: string): string;
|
|
83
|
+
/**
|
|
84
|
+
* Where a jar entry comes from in a Mule 4 project, most likely first:
|
|
85
|
+
* - `META-INF/maven/<g>/<a>/pom.xml` comes from `pom.xml`;
|
|
86
|
+
* - other `META-INF/**` entries and compiled `*.class` files are generated, so they map nowhere;
|
|
87
|
+
* - an `*.xml` entry comes from `src/main/mule/`, or else `src/main/resources/` (e.g. log4j2.xml);
|
|
88
|
+
* - anything else (`api/**`, `*.properties`, `*.yaml`, `*.json`, `*.dwl`, ...) comes from `src/main/resources/`.
|
|
89
|
+
*/
|
|
90
|
+
export declare function jarEntryToRepoCandidates(entry: string): string[];
|
|
91
|
+
/**
|
|
92
|
+
* Read a pom's own coordinates and its declared dependencies. `${...}` placeholders are
|
|
93
|
+
* resolved from `<properties>` and the project coordinates; anything else is left as is.
|
|
94
|
+
*/
|
|
95
|
+
export declare function parsePom(xml: string): PomInfo;
|
|
96
|
+
/** Index a Mule application jar: hashes per entry, the pom and the resolved dependencies. */
|
|
97
|
+
export declare function indexJar(bytes: Uint8Array, options?: {
|
|
98
|
+
maxUncompressedBytes?: number;
|
|
99
|
+
}): JarIndex;
|
|
100
|
+
/** Compare two dependency lists by `group:artifact`, reporting version changes and one-sided entries. */
|
|
101
|
+
export declare function diffDependencies(deployed?: string[], repo?: string[]): DependencyDifferences;
|
|
102
|
+
/**
|
|
103
|
+
* Compare the deployed jar with a local jar. "Repo" in the output means the local jar.
|
|
104
|
+
* Equal SHA-256 short-circuits; otherwise every entry outside `repository/` is compared,
|
|
105
|
+
* so a rebuild that only changed timestamps still counts as identical.
|
|
106
|
+
*/
|
|
107
|
+
export declare function compareJarToJar(deployedBytes: Uint8Array, localBytes: Uint8Array, localLabel?: string): ArtifactComparison;
|
|
108
|
+
/**
|
|
109
|
+
* Compare the deployed jar with a git checkout of its source, by git blob ID. `repoPath` is the
|
|
110
|
+
* Mule project directory (it may be a subdirectory of a larger repository).
|
|
111
|
+
*/
|
|
112
|
+
export declare function compareJarToGit(deployedBytes: Uint8Array, options: {
|
|
113
|
+
repoPath: string;
|
|
114
|
+
gitRef?: string;
|
|
115
|
+
includeWorkingTree?: boolean;
|
|
116
|
+
}): Promise<ArtifactComparison>;
|
|
117
|
+
/** Fetch the jar an application currently runs, from Exchange, bounded by `maxBytes`. */
|
|
118
|
+
export declare function fetchDeployedJar(client: AnypointClient, orgId: string, env: {
|
|
119
|
+
id: string;
|
|
120
|
+
name: string;
|
|
121
|
+
}, appName: string, options?: {
|
|
122
|
+
maxBytes?: number;
|
|
123
|
+
}): Promise<{
|
|
124
|
+
buffer: Buffer;
|
|
125
|
+
sha256: string;
|
|
126
|
+
md5Matches: boolean;
|
|
127
|
+
fileName: string;
|
|
128
|
+
ref: {
|
|
129
|
+
groupId: string;
|
|
130
|
+
artifactId: string;
|
|
131
|
+
version: string;
|
|
132
|
+
};
|
|
133
|
+
}>;
|
|
134
|
+
//# sourceMappingURL=artifact-compare.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"artifact-compare.d.ts","sourceRoot":"","sources":["../../src/workflows/artifact-compare.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,419 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deployed artifact comparison
|
|
3
|
+
* Compares a Mule application jar with another jar or with a git checkout of its source.
|
|
4
|
+
* Only names, sizes, versions and hashes are reported, never file contents.
|
|
5
|
+
*/
|
|
6
|
+
import { execFile } from 'node:child_process';
|
|
7
|
+
import { createHash } from 'node:crypto';
|
|
8
|
+
import * as fs from 'node:fs';
|
|
9
|
+
import * as path from 'node:path';
|
|
10
|
+
import { promisify } from 'node:util';
|
|
11
|
+
import { strFromU8, unzipSync } from 'fflate';
|
|
12
|
+
const execFileAsync = promisify(execFile);
|
|
13
|
+
/** Ceiling on the uncompressed size of the indexed entries, against zip bombs. */
|
|
14
|
+
const MAX_UNCOMPRESSED_BYTES = 512 * 1024 * 1024;
|
|
15
|
+
/** Longest list of paths returned per category; the summary keeps the full counts. */
|
|
16
|
+
const MAX_LISTED = 200;
|
|
17
|
+
/** Repo directories that end up in the jar. */
|
|
18
|
+
const MULE_DIR = 'src/main/mule/';
|
|
19
|
+
const RESOURCES_DIR = 'src/main/resources/';
|
|
20
|
+
/** Git blob object ID: SHA-1 over `blob <len>\0` followed by the bytes. */
|
|
21
|
+
export function gitBlobSha1(bytes) {
|
|
22
|
+
return createHash('sha1').update(`blob ${bytes.length}\0`).update(bytes).digest('hex');
|
|
23
|
+
}
|
|
24
|
+
/** Refuse refs that git could read as an option, or that hold unexpected characters. */
|
|
25
|
+
export function validateGitRef(ref) {
|
|
26
|
+
if (!ref || ref.startsWith('-') || ref.length > 200 || !/^[A-Za-z0-9._/@{}~^+-]+$/.test(ref)) {
|
|
27
|
+
throw new Error(`Invalid git ref: ${JSON.stringify(ref)}`);
|
|
28
|
+
}
|
|
29
|
+
return ref;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Where a jar entry comes from in a Mule 4 project, most likely first:
|
|
33
|
+
* - `META-INF/maven/<g>/<a>/pom.xml` comes from `pom.xml`;
|
|
34
|
+
* - other `META-INF/**` entries and compiled `*.class` files are generated, so they map nowhere;
|
|
35
|
+
* - an `*.xml` entry comes from `src/main/mule/`, or else `src/main/resources/` (e.g. log4j2.xml);
|
|
36
|
+
* - anything else (`api/**`, `*.properties`, `*.yaml`, `*.json`, `*.dwl`, ...) comes from `src/main/resources/`.
|
|
37
|
+
*/
|
|
38
|
+
export function jarEntryToRepoCandidates(entry) {
|
|
39
|
+
if (!entry || entry.endsWith('/') || entry.startsWith('repository/'))
|
|
40
|
+
return [];
|
|
41
|
+
if (/^META-INF\/maven\/[^/]+\/[^/]+\/pom\.xml$/.test(entry))
|
|
42
|
+
return ['pom.xml'];
|
|
43
|
+
if (entry.startsWith('META-INF/') || entry.endsWith('.class'))
|
|
44
|
+
return [];
|
|
45
|
+
if (entry.toLowerCase().endsWith('.xml'))
|
|
46
|
+
return [`${MULE_DIR}${entry}`, `${RESOURCES_DIR}${entry}`];
|
|
47
|
+
return [`${RESOURCES_DIR}${entry}`];
|
|
48
|
+
}
|
|
49
|
+
/** True for a repo path that the build copies into the jar. */
|
|
50
|
+
function isPackagedRepoPath(file) {
|
|
51
|
+
return file === 'pom.xml' || file.startsWith(MULE_DIR) || file.startsWith(RESOURCES_DIR);
|
|
52
|
+
}
|
|
53
|
+
function stripBlocks(xml, tags) {
|
|
54
|
+
let result = xml;
|
|
55
|
+
for (const tag of tags)
|
|
56
|
+
result = result.replace(new RegExp(`<${tag}>[\\s\\S]*?</${tag}>`, 'g'), '');
|
|
57
|
+
return result;
|
|
58
|
+
}
|
|
59
|
+
function firstTag(xml, tag) {
|
|
60
|
+
return new RegExp(`<${tag}>\\s*([^<]*?)\\s*</${tag}>`).exec(xml)?.[1];
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Read a pom's own coordinates and its declared dependencies. `${...}` placeholders are
|
|
64
|
+
* resolved from `<properties>` and the project coordinates; anything else is left as is.
|
|
65
|
+
*/
|
|
66
|
+
export function parsePom(xml) {
|
|
67
|
+
const clean = xml.replace(/<!--[\s\S]*?-->/g, '');
|
|
68
|
+
const properties = {};
|
|
69
|
+
const propertiesBlock = /<properties>([\s\S]*?)<\/properties>/.exec(clean)?.[1] ?? '';
|
|
70
|
+
for (const match of propertiesBlock.matchAll(/<([\w.-]+)>\s*([^<]*?)\s*<\/\1>/g))
|
|
71
|
+
properties[match[1]] = match[2];
|
|
72
|
+
const parent = /<parent>([\s\S]*?)<\/parent>/.exec(clean)?.[1] ?? '';
|
|
73
|
+
const top = stripBlocks(clean, [
|
|
74
|
+
'parent',
|
|
75
|
+
'dependencyManagement',
|
|
76
|
+
'build',
|
|
77
|
+
'profiles',
|
|
78
|
+
'reporting',
|
|
79
|
+
'pluginRepositories',
|
|
80
|
+
'repositories',
|
|
81
|
+
'distributionManagement',
|
|
82
|
+
]);
|
|
83
|
+
const own = stripBlocks(top, ['dependencies', 'properties']);
|
|
84
|
+
const groupId = firstTag(own, 'groupId') ?? firstTag(parent, 'groupId');
|
|
85
|
+
const artifactId = firstTag(own, 'artifactId');
|
|
86
|
+
const rawVersion = firstTag(own, 'version') ?? firstTag(parent, 'version');
|
|
87
|
+
const resolve = (value) => value?.replace(/\$\{([^}]+)\}/g, (placeholder, name) => {
|
|
88
|
+
if (name === 'project.version' || name === 'version')
|
|
89
|
+
return rawVersion ?? placeholder;
|
|
90
|
+
if (name === 'project.groupId' || name === 'groupId')
|
|
91
|
+
return groupId ?? placeholder;
|
|
92
|
+
if (name === 'project.artifactId' || name === 'artifactId')
|
|
93
|
+
return artifactId ?? placeholder;
|
|
94
|
+
return properties[name] ?? placeholder;
|
|
95
|
+
});
|
|
96
|
+
const dependencies = [];
|
|
97
|
+
const dependenciesBlock = /<dependencies>([\s\S]*?)<\/dependencies>/.exec(top)?.[1] ?? '';
|
|
98
|
+
for (const match of dependenciesBlock.matchAll(/<dependency>([\s\S]*?)<\/dependency>/g)) {
|
|
99
|
+
const body = stripBlocks(match[1], ['exclusions']);
|
|
100
|
+
if (firstTag(body, 'scope') === 'test')
|
|
101
|
+
continue;
|
|
102
|
+
const g = resolve(firstTag(body, 'groupId'));
|
|
103
|
+
const a = resolve(firstTag(body, 'artifactId'));
|
|
104
|
+
if (!g || !a)
|
|
105
|
+
continue;
|
|
106
|
+
dependencies.push(`${g}:${a}:${resolve(firstTag(body, 'version')) ?? 'managed'}`);
|
|
107
|
+
}
|
|
108
|
+
return { groupId: resolve(groupId), artifactId: resolve(artifactId), version: resolve(rawVersion), dependencies };
|
|
109
|
+
}
|
|
110
|
+
function parseClassloaderModel(text) {
|
|
111
|
+
try {
|
|
112
|
+
const model = JSON.parse(text);
|
|
113
|
+
return (model.dependencies ?? [])
|
|
114
|
+
.map((d) => d.artifactCoordinates)
|
|
115
|
+
.filter((c) => Boolean(c?.groupId && c.artifactId))
|
|
116
|
+
.map((c) => `${c.groupId}:${c.artifactId}:${c.version ?? '?'}`);
|
|
117
|
+
}
|
|
118
|
+
catch {
|
|
119
|
+
return undefined;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
/** Index a Mule application jar: hashes per entry, the pom and the resolved dependencies. */
|
|
123
|
+
export function indexJar(bytes, options = {}) {
|
|
124
|
+
const limit = options.maxUncompressedBytes ?? MAX_UNCOMPRESSED_BYTES;
|
|
125
|
+
const skipped = { repository: 0, directories: 0 };
|
|
126
|
+
let total = 0;
|
|
127
|
+
const files = unzipSync(bytes, {
|
|
128
|
+
filter(entry) {
|
|
129
|
+
if (entry.name.startsWith('repository/')) {
|
|
130
|
+
skipped.repository++;
|
|
131
|
+
return false;
|
|
132
|
+
}
|
|
133
|
+
if (entry.name.endsWith('/')) {
|
|
134
|
+
skipped.directories++;
|
|
135
|
+
return false;
|
|
136
|
+
}
|
|
137
|
+
total += entry.originalSize;
|
|
138
|
+
if (total > limit)
|
|
139
|
+
throw new Error(`Jar content exceeds ${limit} bytes uncompressed`);
|
|
140
|
+
return true;
|
|
141
|
+
},
|
|
142
|
+
});
|
|
143
|
+
const entries = new Map();
|
|
144
|
+
for (const [name, content] of Object.entries(files)) {
|
|
145
|
+
entries.set(name, {
|
|
146
|
+
size: content.length,
|
|
147
|
+
sha256: createHash('sha256').update(content).digest('hex'),
|
|
148
|
+
gitSha1: gitBlobSha1(content),
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
const pomXmlName = Object.keys(files).find((name) => /^META-INF\/maven\/[^/]+\/[^/]+\/pom\.xml$/.test(name));
|
|
152
|
+
const pomPropsName = Object.keys(files).find((name) => /^META-INF\/maven\/[^/]+\/[^/]+\/pom\.properties$/.test(name));
|
|
153
|
+
const pom = pomXmlName ? parsePom(strFromU8(files[pomXmlName])) : undefined;
|
|
154
|
+
const propsVersion = pomPropsName
|
|
155
|
+
? /^\s*version\s*[=:]\s*(\S+)\s*$/m.exec(strFromU8(files[pomPropsName]))?.[1]
|
|
156
|
+
: undefined;
|
|
157
|
+
const model = files['META-INF/mule-artifact/classloader-model.json'];
|
|
158
|
+
return {
|
|
159
|
+
sha256: createHash('sha256').update(bytes).digest('hex'),
|
|
160
|
+
size: bytes.length,
|
|
161
|
+
entries,
|
|
162
|
+
pom,
|
|
163
|
+
pomVersion: propsVersion ?? pom?.version,
|
|
164
|
+
resolvedDependencies: model ? parseClassloaderModel(strFromU8(model)) : undefined,
|
|
165
|
+
skipped,
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
function splitCoordinate(coordinate) {
|
|
169
|
+
const parts = coordinate.split(':');
|
|
170
|
+
return { key: parts.slice(0, 2).join(':'), version: parts.slice(2).join(':') };
|
|
171
|
+
}
|
|
172
|
+
/** Compare two dependency lists by `group:artifact`, reporting version changes and one-sided entries. */
|
|
173
|
+
export function diffDependencies(deployed = [], repo = []) {
|
|
174
|
+
const left = new Map(deployed.map((d) => [splitCoordinate(d).key, splitCoordinate(d).version]));
|
|
175
|
+
const right = new Map(repo.map((d) => [splitCoordinate(d).key, splitCoordinate(d).version]));
|
|
176
|
+
const result = { changed: [], missingInRepo: [], extraInRepo: [] };
|
|
177
|
+
for (const [key, version] of left) {
|
|
178
|
+
if (!right.has(key))
|
|
179
|
+
result.missingInRepo.push(`${key}:${version}`);
|
|
180
|
+
else if (right.get(key) !== version)
|
|
181
|
+
result.changed.push({ dependency: key, deployed: version, repo: right.get(key) });
|
|
182
|
+
}
|
|
183
|
+
for (const [key, version] of right)
|
|
184
|
+
if (!left.has(key))
|
|
185
|
+
result.extraInRepo.push(`${key}:${version}`);
|
|
186
|
+
return result;
|
|
187
|
+
}
|
|
188
|
+
function hasDependencyDifferences(diff) {
|
|
189
|
+
return diff.changed.length + diff.missingInRepo.length + diff.extraInRepo.length > 0;
|
|
190
|
+
}
|
|
191
|
+
function finish(base, lists, counts) {
|
|
192
|
+
const byPath = (a, b) => a.path.localeCompare(b.path);
|
|
193
|
+
const differs = lists.changed.length + lists.missingInRepo.length + lists.extraInRepo.length > 0 ||
|
|
194
|
+
hasDependencyDifferences(base.dependencies) ||
|
|
195
|
+
Boolean(base.pomVersion.deployed && base.pomVersion.repo && base.pomVersion.deployed !== base.pomVersion.repo);
|
|
196
|
+
const notes = [...base.notes];
|
|
197
|
+
for (const [name, list] of Object.entries(lists)) {
|
|
198
|
+
if (list.length > MAX_LISTED)
|
|
199
|
+
notes.push(`${name} lists the first ${MAX_LISTED} of ${list.length} paths`);
|
|
200
|
+
}
|
|
201
|
+
return {
|
|
202
|
+
verdict: differs ? 'differs' : 'identical',
|
|
203
|
+
...base,
|
|
204
|
+
summary: {
|
|
205
|
+
compared: counts.compared,
|
|
206
|
+
changed: lists.changed.length,
|
|
207
|
+
missingInRepo: lists.missingInRepo.length,
|
|
208
|
+
extraInRepo: lists.extraInRepo.length,
|
|
209
|
+
unmapped: counts.unmapped,
|
|
210
|
+
skippedRepositoryEntries: counts.skippedRepositoryEntries,
|
|
211
|
+
},
|
|
212
|
+
changed: lists.changed.sort(byPath).slice(0, MAX_LISTED),
|
|
213
|
+
missingInRepo: lists.missingInRepo.sort(byPath).slice(0, MAX_LISTED),
|
|
214
|
+
extraInRepo: lists.extraInRepo.sort(byPath).slice(0, MAX_LISTED),
|
|
215
|
+
notes,
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* Compare the deployed jar with a local jar. "Repo" in the output means the local jar.
|
|
220
|
+
* Equal SHA-256 short-circuits; otherwise every entry outside `repository/` is compared,
|
|
221
|
+
* so a rebuild that only changed timestamps still counts as identical.
|
|
222
|
+
*/
|
|
223
|
+
export function compareJarToJar(deployedBytes, localBytes, localLabel = 'local jar') {
|
|
224
|
+
const deployed = indexJar(deployedBytes);
|
|
225
|
+
const local = deployedBytes === localBytes ? deployed : indexJar(localBytes);
|
|
226
|
+
const changed = [];
|
|
227
|
+
const missingInRepo = [];
|
|
228
|
+
const extraInRepo = [];
|
|
229
|
+
if (deployed.sha256 !== local.sha256) {
|
|
230
|
+
for (const [name, entry] of deployed.entries) {
|
|
231
|
+
const other = local.entries.get(name);
|
|
232
|
+
if (!other)
|
|
233
|
+
missingInRepo.push({ path: name, deployedSize: entry.size });
|
|
234
|
+
else if (other.sha256 !== entry.sha256)
|
|
235
|
+
changed.push({ path: name, deployedSize: entry.size, repoSize: other.size });
|
|
236
|
+
}
|
|
237
|
+
for (const [name, entry] of local.entries) {
|
|
238
|
+
if (!deployed.entries.has(name))
|
|
239
|
+
extraInRepo.push({ path: name, repoSize: entry.size });
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
const notes = [];
|
|
243
|
+
if (!deployed.resolvedDependencies || !local.resolvedDependencies) {
|
|
244
|
+
notes.push('A jar has no classloader-model.json; dependencies compared from the declared pom dependencies');
|
|
245
|
+
}
|
|
246
|
+
const dependencies = deployed.resolvedDependencies && local.resolvedDependencies
|
|
247
|
+
? diffDependencies(deployed.resolvedDependencies, local.resolvedDependencies)
|
|
248
|
+
: diffDependencies(deployed.pom?.dependencies, local.pom?.dependencies);
|
|
249
|
+
return finish({
|
|
250
|
+
against: { type: 'jar', path: localLabel, size: local.size },
|
|
251
|
+
sha256: { deployed: deployed.sha256, repo: local.sha256, match: deployed.sha256 === local.sha256 },
|
|
252
|
+
pomVersion: { deployed: deployed.pomVersion, repo: local.pomVersion },
|
|
253
|
+
dependencies,
|
|
254
|
+
notes,
|
|
255
|
+
}, { changed, missingInRepo, extraInRepo }, {
|
|
256
|
+
compared: deployed.entries.size,
|
|
257
|
+
unmapped: 0,
|
|
258
|
+
skippedRepositoryEntries: deployed.skipped.repository,
|
|
259
|
+
});
|
|
260
|
+
}
|
|
261
|
+
async function git(repoPath, args) {
|
|
262
|
+
try {
|
|
263
|
+
const { stdout } = await execFileAsync('git', ['-C', repoPath, ...args], {
|
|
264
|
+
encoding: 'utf8',
|
|
265
|
+
maxBuffer: 64 * 1024 * 1024,
|
|
266
|
+
env: { ...process.env, GIT_OPTIONAL_LOCKS: '0', GIT_TERMINAL_PROMPT: '0' },
|
|
267
|
+
});
|
|
268
|
+
return stdout;
|
|
269
|
+
}
|
|
270
|
+
catch (error) {
|
|
271
|
+
const stderr = error.stderr?.trim();
|
|
272
|
+
throw new Error(`git ${args[0]} failed${stderr ? `: ${stderr.split('\n')[0]}` : ''}`);
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
/** List packaged files at a ref, relative to the project directory, without reading their contents. */
|
|
276
|
+
async function listTree(repoPath, ref) {
|
|
277
|
+
const out = await git(repoPath, ['ls-tree', '-r', '-l', '-z', ref, '--', 'pom.xml', MULE_DIR, RESOURCES_DIR]);
|
|
278
|
+
const entries = new Map();
|
|
279
|
+
for (const record of out.split('\0')) {
|
|
280
|
+
const tab = record.indexOf('\t');
|
|
281
|
+
if (tab < 0)
|
|
282
|
+
continue;
|
|
283
|
+
const [, type, oid, size] = record.slice(0, tab).split(/\s+/);
|
|
284
|
+
if (type !== 'blob')
|
|
285
|
+
continue;
|
|
286
|
+
entries.set(record.slice(tab + 1), { oid, size: Number(size) });
|
|
287
|
+
}
|
|
288
|
+
return entries;
|
|
289
|
+
}
|
|
290
|
+
/**
|
|
291
|
+
* Overlay uncommitted and untracked changes in the packaged paths onto a tree listing. Changed
|
|
292
|
+
* files are hashed as git blobs in place; their contents are never returned.
|
|
293
|
+
*/
|
|
294
|
+
async function overlayWorkingTree(repoPath, entries) {
|
|
295
|
+
const prefix = (await git(repoPath, ['rev-parse', '--show-prefix'])).trim();
|
|
296
|
+
const out = await git(repoPath, [
|
|
297
|
+
'status',
|
|
298
|
+
'--porcelain=v1',
|
|
299
|
+
'-z',
|
|
300
|
+
'--untracked-files=all',
|
|
301
|
+
'--',
|
|
302
|
+
'pom.xml',
|
|
303
|
+
MULE_DIR,
|
|
304
|
+
RESOURCES_DIR,
|
|
305
|
+
]);
|
|
306
|
+
const records = out.split('\0').filter(Boolean);
|
|
307
|
+
const changes = [];
|
|
308
|
+
const touched = [];
|
|
309
|
+
for (let i = 0; i < records.length; i++) {
|
|
310
|
+
const status = records[i].slice(0, 2);
|
|
311
|
+
const file = records[i].slice(3);
|
|
312
|
+
if (status[0] === 'R' || status[0] === 'C')
|
|
313
|
+
touched.push(records[++i]);
|
|
314
|
+
touched.push(file);
|
|
315
|
+
const relative = file.startsWith(prefix) ? file.slice(prefix.length) : file;
|
|
316
|
+
changes.push({ path: relative, status: status.trim() });
|
|
317
|
+
}
|
|
318
|
+
for (const file of touched) {
|
|
319
|
+
if (!file.startsWith(prefix))
|
|
320
|
+
continue;
|
|
321
|
+
const relative = file.slice(prefix.length);
|
|
322
|
+
const absolute = path.join(repoPath, relative);
|
|
323
|
+
let stat;
|
|
324
|
+
try {
|
|
325
|
+
stat = fs.lstatSync(absolute);
|
|
326
|
+
}
|
|
327
|
+
catch {
|
|
328
|
+
entries.delete(relative);
|
|
329
|
+
continue;
|
|
330
|
+
}
|
|
331
|
+
const bytes = stat.isSymbolicLink()
|
|
332
|
+
? Buffer.from(fs.readlinkSync(absolute))
|
|
333
|
+
: stat.isFile()
|
|
334
|
+
? fs.readFileSync(absolute)
|
|
335
|
+
: undefined;
|
|
336
|
+
if (!bytes)
|
|
337
|
+
continue;
|
|
338
|
+
entries.set(relative, { oid: gitBlobSha1(bytes), size: bytes.length });
|
|
339
|
+
}
|
|
340
|
+
return changes;
|
|
341
|
+
}
|
|
342
|
+
function readRepoPom(repoPath, ref, workingTree) {
|
|
343
|
+
if (workingTree) {
|
|
344
|
+
const file = path.join(repoPath, 'pom.xml');
|
|
345
|
+
return Promise.resolve(fs.existsSync(file) ? fs.readFileSync(file, 'utf8') : undefined);
|
|
346
|
+
}
|
|
347
|
+
return git(repoPath, ['show', `${ref}:./pom.xml`]).catch(() => undefined);
|
|
348
|
+
}
|
|
349
|
+
/**
|
|
350
|
+
* Compare the deployed jar with a git checkout of its source, by git blob ID. `repoPath` is the
|
|
351
|
+
* Mule project directory (it may be a subdirectory of a larger repository).
|
|
352
|
+
*/
|
|
353
|
+
export async function compareJarToGit(deployedBytes, options) {
|
|
354
|
+
const ref = validateGitRef(options.gitRef ?? 'HEAD');
|
|
355
|
+
if (options.includeWorkingTree && ref !== 'HEAD') {
|
|
356
|
+
throw new Error('includeWorkingTree compares the working tree, which is based on HEAD; omit gitRef');
|
|
357
|
+
}
|
|
358
|
+
const repoPath = path.resolve(options.repoPath);
|
|
359
|
+
const deployed = indexJar(deployedBytes);
|
|
360
|
+
const repo = await listTree(repoPath, ref);
|
|
361
|
+
const workingTreeChanges = options.includeWorkingTree ? await overlayWorkingTree(repoPath, repo) : undefined;
|
|
362
|
+
const changed = [];
|
|
363
|
+
const missingInRepo = [];
|
|
364
|
+
const claimed = new Set();
|
|
365
|
+
let compared = 0;
|
|
366
|
+
let unmapped = 0;
|
|
367
|
+
for (const [name, entry] of deployed.entries) {
|
|
368
|
+
const candidates = jarEntryToRepoCandidates(name);
|
|
369
|
+
if (candidates.length === 0) {
|
|
370
|
+
unmapped++;
|
|
371
|
+
continue;
|
|
372
|
+
}
|
|
373
|
+
compared++;
|
|
374
|
+
const match = candidates.find((candidate) => repo.has(candidate));
|
|
375
|
+
if (!match) {
|
|
376
|
+
missingInRepo.push({ path: candidates[0], jarPath: name, deployedSize: entry.size });
|
|
377
|
+
continue;
|
|
378
|
+
}
|
|
379
|
+
claimed.add(match);
|
|
380
|
+
const repoEntry = repo.get(match);
|
|
381
|
+
if (repoEntry.oid !== entry.gitSha1) {
|
|
382
|
+
changed.push({ path: match, jarPath: name, deployedSize: entry.size, repoSize: repoEntry.size });
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
const extraInRepo = [...repo.entries()]
|
|
386
|
+
.filter(([file]) => isPackagedRepoPath(file) && !claimed.has(file))
|
|
387
|
+
.map(([file, entry]) => ({ path: file, repoSize: entry.size }));
|
|
388
|
+
const pomText = await readRepoPom(repoPath, ref, Boolean(options.includeWorkingTree));
|
|
389
|
+
const repoPom = pomText ? parsePom(pomText) : undefined;
|
|
390
|
+
const notes = [
|
|
391
|
+
'Compiled classes and generated META-INF files are not compared',
|
|
392
|
+
'Resources changed by Maven filtering at build time show as changed',
|
|
393
|
+
];
|
|
394
|
+
if (!repoPom)
|
|
395
|
+
notes.push('No pom.xml found in the repo');
|
|
396
|
+
return finish({
|
|
397
|
+
against: {
|
|
398
|
+
type: 'git',
|
|
399
|
+
repoPath,
|
|
400
|
+
gitRef: ref,
|
|
401
|
+
includeWorkingTree: Boolean(options.includeWorkingTree),
|
|
402
|
+
},
|
|
403
|
+
sha256: { deployed: deployed.sha256 },
|
|
404
|
+
pomVersion: { deployed: deployed.pomVersion, repo: repoPom?.version },
|
|
405
|
+
dependencies: diffDependencies(deployed.pom?.dependencies, repoPom?.dependencies),
|
|
406
|
+
notes,
|
|
407
|
+
...(workingTreeChanges ? { workingTreeChanges } : {}),
|
|
408
|
+
}, { changed, missingInRepo, extraInRepo }, { compared, unmapped, skippedRepositoryEntries: deployed.skipped.repository });
|
|
409
|
+
}
|
|
410
|
+
/** Fetch the jar an application currently runs, from Exchange, bounded by `maxBytes`. */
|
|
411
|
+
export async function fetchDeployedJar(client, orgId, env, appName, options = {}) {
|
|
412
|
+
const deployment = await client.cloudHub2.findDetailByName(orgId, env.id, appName);
|
|
413
|
+
if (!deployment)
|
|
414
|
+
throw new Error(`Application "${appName}" not found in ${env.name}`);
|
|
415
|
+
const { groupId, artifactId, version } = deployment.application.ref;
|
|
416
|
+
const jar = await client.exchange.downloadAppJar(orgId, groupId, artifactId, version, options);
|
|
417
|
+
return { ref: { groupId, artifactId, version }, ...jar };
|
|
418
|
+
}
|
|
419
|
+
//# sourceMappingURL=artifact-compare.js.map
|