@tailor-platform/sdk 1.73.2 → 1.74.0
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/CHANGELOG.md +43 -0
- package/dist/application-BndtMTFY.mjs +3 -0
- package/dist/{application-Bb9NNp5m.mjs → application-DwmoI6ZQ.mjs} +231 -153
- package/dist/application-DwmoI6ZQ.mjs.map +1 -0
- package/dist/cli/commands/deploy/apply-phases.d.mts +1 -0
- package/dist/cli/commands/executor/trigger.d.mts +1 -1
- package/dist/cli/index.mjs +102 -13
- package/dist/cli/index.mjs.map +1 -1
- package/dist/cli/lib.d.mts +2 -2
- package/dist/cli/lib.mjs +2 -2
- package/dist/cli/shared/client.d.mts +1 -1
- package/dist/completion/zsh-worker.zsh +28 -3
- package/dist/{repl-editor-BmQOyZJd.mjs → repl-editor-BCozyiNq.mjs} +3 -2
- package/dist/{repl-editor-BmQOyZJd.mjs.map → repl-editor-BCozyiNq.mjs.map} +1 -1
- package/dist/{runtime-B8O6JFS2.mjs → runtime-Zkg3SUwi.mjs} +1974 -1217
- package/dist/runtime-Zkg3SUwi.mjs.map +1 -0
- package/dist/{service-DiEtAsvc.mjs → service-BCRJ-3NV.mjs} +18 -1
- package/dist/{service-DiEtAsvc.mjs.map → service-BCRJ-3NV.mjs.map} +1 -1
- package/dist/service-CPMlIg3X.mjs +3 -0
- package/dist/tailor-proto/src/tailor/v1/aigateway_pb.d.mts +2 -2
- package/dist/tailor-proto/src/tailor/v1/aigateway_resource_pb.d.mts +1 -1
- package/dist/tailor-proto/src/tailor/v1/application_pb.d.mts +1 -1
- package/dist/tailor-proto/src/tailor/v1/application_resource_pb.d.mts +1 -1
- package/dist/tailor-proto/src/tailor/v1/auth_pb.d.mts +1 -1
- package/dist/tailor-proto/src/tailor/v1/auth_resource_pb.d.mts +1 -1
- package/dist/tailor-proto/src/tailor/v1/events_pb.d.mts +1 -1
- package/dist/tailor-proto/src/tailor/v1/events_resource_pb.d.mts +1 -1
- package/dist/tailor-proto/src/tailor/v1/executor_pb.d.mts +1 -1
- package/dist/tailor-proto/src/tailor/v1/executor_resource_pb.d.mts +1 -1
- package/dist/tailor-proto/src/tailor/v1/function_pb.d.mts +1 -1
- package/dist/tailor-proto/src/tailor/v1/function_registry_pb.d.mts +1 -1
- package/dist/tailor-proto/src/tailor/v1/function_resource_pb.d.mts +1 -1
- package/dist/tailor-proto/src/tailor/v1/http_adapter_resource_pb.d.mts +1 -1
- package/dist/tailor-proto/src/tailor/v1/idp_pb.d.mts +1 -1
- package/dist/tailor-proto/src/tailor/v1/idp_resource_pb.d.mts +1 -1
- package/dist/tailor-proto/src/tailor/v1/metadata_pb.d.mts +1 -1
- package/dist/tailor-proto/src/tailor/v1/metadata_resource_pb.d.mts +1 -1
- package/dist/tailor-proto/src/tailor/v1/meter_pb.d.mts +1 -1
- package/dist/tailor-proto/src/tailor/v1/pipeline_pb.d.mts +1 -1
- package/dist/tailor-proto/src/tailor/v1/pipeline_resource_pb.d.mts +1 -1
- package/dist/tailor-proto/src/tailor/v1/resource_pb.d.mts +1 -1
- package/dist/tailor-proto/src/tailor/v1/secret_manager_pb.d.mts +1 -1
- package/dist/tailor-proto/src/tailor/v1/secret_manager_resource_pb.d.mts +1 -1
- package/dist/tailor-proto/src/tailor/v1/service_pb.d.mts +2 -2
- package/dist/tailor-proto/src/tailor/v1/stateflow_pb.d.mts +1 -1
- package/dist/tailor-proto/src/tailor/v1/stateflow_resource_pb.d.mts +1 -1
- package/dist/tailor-proto/src/tailor/v1/staticwebsite_pb.d.mts +1 -1
- package/dist/tailor-proto/src/tailor/v1/staticwebsite_resource_pb.d.mts +1 -1
- package/dist/tailor-proto/src/tailor/v1/tailordb_pb.d.mts +1 -1
- package/dist/tailor-proto/src/tailor/v1/tailordb_resource_pb.d.mts +1 -1
- package/dist/tailor-proto/src/tailor/v1/telemetryrouter_pb.d.mts +1 -1
- package/dist/tailor-proto/src/tailor/v1/telemetryrouter_resource_pb.d.mts +1 -1
- package/dist/tailor-proto/src/tailor/v1/workflow_pb.d.mts +1 -1
- package/dist/tailor-proto/src/tailor/v1/workflow_resource_pb.d.mts +1 -1
- package/dist/tailor-proto/src/tailor/v1/workspace_pb.d.mts +2 -2
- package/dist/tailor-proto/src/tailor/v1/workspace_resource_pb.d.mts +1 -1
- package/dist/vitest/index.mjs +2 -1
- package/dist/vitest/index.mjs.map +1 -1
- package/docs/cli/application.md +21 -11
- package/docs/cli/setup.md +25 -0
- package/docs/cli-reference.md +1 -0
- package/docs/services/tailordb.md +23 -0
- package/package.json +8 -8
- package/dist/application-BXdCl6bp.mjs +0 -3
- package/dist/application-Bb9NNp5m.mjs.map +0 -1
- package/dist/runtime-B8O6JFS2.mjs.map +0 -1
- package/dist/service-BXmdFwSH.mjs +0 -3
|
@@ -2,8 +2,8 @@ import { t as db } from "./schema-DAdZSFOd.mjs";
|
|
|
2
2
|
import { $ as CreateUserProfileConfigRequestSchema, A as UpdatePipelineServiceRequestSchema, At as Condition_Operator, B as UpdateExecutorExecutorRequestSchema, Ct as UserProfileProviderConfig_UserProfileProviderType, D as CreatePipelineResolverRequestSchema, Dt as ApplicationSchemaUpdateAttemptStatus, E as UpdateSecretManagerSecretRequestSchema, Et as UpdateApplicationRequestSchema, F as IdPPermissionOperator, G as CreateAuthHookRequestSchema, H as ExecutorTargetType, I as IdPPermissionPermit, J as CreateAuthOAuth2ClientRequestSchema, K as CreateAuthIDPConfigRequestSchema, L as FunctionExecution_Status, M as CreateIdPServiceRequestSchema, Mt as PageDirection, N as UpdateIdPServiceRequestSchema, O as CreatePipelineServiceRequestSchema, Ot as Subgraph_ServiceType, P as IdPLang, Q as CreateTenantConfigRequestSchema, S as UpdateStaticWebsiteRequestSchema, St as TenantProviderConfig_TenantProviderType, T as CreateSecretManagerVaultRequestSchema, Tt as GetApplicationSchemaHealthResponse_ApplicationSchemaHealthStatus, U as ExecutorTriggerType, V as ExecutorJobStatus, W as CreateAuthConnectionRequestSchema, X as CreateAuthSCIMResourceRequestSchema, Y as CreateAuthSCIMConfigRequestSchema, Z as CreateAuthServiceRequestSchema, _ as TailorDBType_Permission_Operator, _t as AuthSCIMAttribute_Mutability, a as WorkspacePlatformUserRole, at as UpdateAuthSCIMConfigRequestSchema, b as AddCustomDomainRequestSchema, bt as AuthSCIMConfig_AuthorizationType, c as UpdateWorkflowRequestSchema, ct as UpdateTenantConfigRequestSchema, d as CreateTailorDBServiceRequestSchema, dt as AuthConnection_Type, et as UpdateAuthConnectionRequestSchema, f as CreateTailorDBTypeRequestSchema, ft as AuthHookPoint, g as TailorDBGQLPermission_Permit, gt as AuthOAuth2Client_GrantType, h as TailorDBGQLPermission_Operator, ht as AuthOAuth2Client_ClientType, it as UpdateAuthOAuth2ClientRequestSchema, j as PipelineResolver_OperationType, jt as FilterSchema, k as UpdatePipelineResolverRequestSchema, kt as ConditionSchema, l as WorkflowExecution_Status, lt as UpdateUserProfileConfigRequestSchema, m as TailorDBGQLPermission_Action, mt as AuthInvokerSchema, nt as UpdateAuthIDPConfigRequestSchema, o as CreateWorkflowJobFunctionRequestSchema, ot as UpdateAuthSCIMResourceRequestSchema, p as UpdateTailorDBTypeRequestSchema, pt as AuthIDPConfig_AuthType, q as CreateAuthMachineUserRequestSchema, rt as UpdateAuthMachineUserRequestSchema, s as CreateWorkflowRequestSchema, st as UpdateAuthServiceRequestSchema, t as OperatorService, tt as UpdateAuthHookRequestSchema, u as WorkflowJobExecution_Status, ut as AuthConnection_Status, v as TailorDBType_Permission_Permit, vt as AuthSCIMAttribute_Type, w as CreateSecretManagerSecretRequestSchema, wt as CreateApplicationRequestSchema, x as CreateStaticWebsiteRequestSchema, y as TailorDBType_PermitAction, yt as AuthSCIMAttribute_Uniqueness, z as CreateExecutorExecutorRequestSchema } from "./service_pb-CIrhGwHk.mjs";
|
|
3
3
|
import { t as assertDefined } from "./assert-DBxo8jPo.mjs";
|
|
4
4
|
import { a as parseBoolean, i as symbols, n as logger, r as styles, t as CIPromptError } from "./logger-BEiZZ3qT.mjs";
|
|
5
|
-
import {
|
|
6
|
-
import { o as loadFilesWithIgnores, t as createExecutorService } from "./service-
|
|
5
|
+
import { A as hasUserTokenEntry, C as getDistDir, E as loadConfig, F as loadPlatformClientConfig, G as fetchAll, H as writePlatformConfig, K as fetchMachineUserToken, L as loadWorkspaceId, M as loadConfigPath, P as loadMachineUserName, Q as getPlatformBaseUrl, R as platformConfigFromProfile, S as createBundleCache, T as hashFile, X as getConsoleBaseUrl, Z as getOAuth2ClientId, b as getPluginGenerationDependencies, c as HTTP_METHODS, d as buildResolverOperationHookExpr, et as initOperatorClient, f as assertUniqueLocalTailorDBTypeNames, g as platformBundleDefinePlugin, h as stringifyFunction, i as resolverBundleKey, j as loadAccessToken, m as TailorDBTypeSchema, n as generatePluginFilesIfNeeded, nt as resolveStaticWebsiteUrls, p as assertUniqueTailorDBTypeNamesWithExternal, q as fetchPaged, r as loadApplication, rt as byName, t as defineApplication, tt as isDefaultPlatform, u as buildExecutorArgsExpr, w as hashContent, x as hasGenerationHooks, z as readPlatformConfig } from "./application-DwmoI6ZQ.mjs";
|
|
6
|
+
import { o as loadFilesWithIgnores, t as createExecutorService } from "./service-BCRJ-3NV.mjs";
|
|
7
7
|
import { t as multiline } from "./multiline-sfHpTZZK.mjs";
|
|
8
8
|
import { t as readPackageJson } from "./package-json-8b0O9TlX.mjs";
|
|
9
9
|
import { i as userAgent } from "./secret-file-VSVGy1V0.mjs";
|
|
@@ -71,7 +71,9 @@ const durationArg = z.string().refine((val) => durationPattern.test(val), { mess
|
|
|
71
71
|
*/
|
|
72
72
|
function parseDuration(duration) {
|
|
73
73
|
const match = assertDefined(duration.match(durationPattern), `invalid duration format: ${duration}`);
|
|
74
|
-
|
|
74
|
+
const value = parseInt(assertDefined(match[1], "duration digits group missing"), 10);
|
|
75
|
+
const unit = assertDefined(match[2], "duration unit group missing");
|
|
76
|
+
return value * unitToMs[unit];
|
|
75
77
|
}
|
|
76
78
|
/**
|
|
77
79
|
* Schema for positive integer validation (from string input)
|
|
@@ -197,6 +199,18 @@ const configArg = { config: arg(z.string().default("tailor.config.ts"), {
|
|
|
197
199
|
}
|
|
198
200
|
}) };
|
|
199
201
|
/**
|
|
202
|
+
* Shared config arg for commands that accept one or more comma-separated config file paths
|
|
203
|
+
*/
|
|
204
|
+
const multiConfigArg = { config: arg(z.string().default("tailor.config.ts"), {
|
|
205
|
+
alias: "c",
|
|
206
|
+
description: "Path to SDK config file. Use comma-separated paths to deploy multiple apps together.",
|
|
207
|
+
env: "TAILOR_PLATFORM_SDK_CONFIG_PATH",
|
|
208
|
+
completion: {
|
|
209
|
+
type: "file",
|
|
210
|
+
extensions: ["ts"]
|
|
211
|
+
}
|
|
212
|
+
}) };
|
|
213
|
+
/**
|
|
200
214
|
* Arguments for commands that interact with deployed resources (includes config)
|
|
201
215
|
*/
|
|
202
216
|
const deploymentArgs = {
|
|
@@ -965,7 +979,7 @@ function createCacheStore(config) {
|
|
|
965
979
|
return path.join(config.cacheDir, BUNDLES_DIR);
|
|
966
980
|
}
|
|
967
981
|
function bundlePath(cacheKey) {
|
|
968
|
-
return path.join(bundlesDir(), `${cacheKey
|
|
982
|
+
return path.join(bundlesDir(), `${hashContent(cacheKey)}.js`);
|
|
969
983
|
}
|
|
970
984
|
function loadManifest() {
|
|
971
985
|
try {
|
|
@@ -1085,12 +1099,20 @@ function createCacheManager(options) {
|
|
|
1085
1099
|
enabled: true,
|
|
1086
1100
|
bundleCache: createBundleCache(store),
|
|
1087
1101
|
finalize() {
|
|
1088
|
-
const
|
|
1102
|
+
const currentManifest = store.getCurrentManifest() ?? {
|
|
1089
1103
|
version: 1,
|
|
1090
1104
|
sdkVersion: options.sdkVersion,
|
|
1091
1105
|
lockfileHash: options.lockfileHash,
|
|
1092
1106
|
entries: {}
|
|
1093
1107
|
};
|
|
1108
|
+
const latestManifest = store.loadManifest();
|
|
1109
|
+
const manifest = latestManifest?.sdkVersion === options.sdkVersion && latestManifest.lockfileHash === options.lockfileHash ? {
|
|
1110
|
+
...latestManifest,
|
|
1111
|
+
entries: {
|
|
1112
|
+
...latestManifest.entries,
|
|
1113
|
+
...currentManifest.entries
|
|
1114
|
+
}
|
|
1115
|
+
} : currentManifest;
|
|
1094
1116
|
manifest.sdkVersion = options.sdkVersion;
|
|
1095
1117
|
manifest.lockfileHash = options.lockfileHash;
|
|
1096
1118
|
store.saveManifest(manifest);
|
|
@@ -1894,6 +1916,210 @@ function trackRemainingResourceOwner(params) {
|
|
|
1894
1916
|
return owned;
|
|
1895
1917
|
}
|
|
1896
1918
|
|
|
1919
|
+
//#endregion
|
|
1920
|
+
//#region src/cli/commands/deploy/staticwebsite.ts
|
|
1921
|
+
/**
|
|
1922
|
+
* Apply static website changes for the given phase.
|
|
1923
|
+
* @param client - Operator client instance
|
|
1924
|
+
* @param result - Planned static website changes
|
|
1925
|
+
* @param phase - Apply phase
|
|
1926
|
+
* @returns Promise that resolves when static websites are applied
|
|
1927
|
+
*/
|
|
1928
|
+
async function applyStaticWebsite(client, result, phase = "create-update") {
|
|
1929
|
+
const { changeSet, customDomainChangeSet } = result;
|
|
1930
|
+
if (phase === "create-update") {
|
|
1931
|
+
await Promise.all([...changeSet.creates.map(async (create) => {
|
|
1932
|
+
await client.createStaticWebsite(create.request);
|
|
1933
|
+
await client.setMetadata(create.metaRequest);
|
|
1934
|
+
}), ...changeSet.updates.map(async (update) => {
|
|
1935
|
+
await client.updateStaticWebsite(update.request);
|
|
1936
|
+
await client.setMetadata(update.metaRequest);
|
|
1937
|
+
})]);
|
|
1938
|
+
await Promise.all([...customDomainChangeSet.creates.map(async (add) => {
|
|
1939
|
+
await client.addCustomDomain(add.request);
|
|
1940
|
+
await client.setMetadata(add.metaRequest);
|
|
1941
|
+
}), ...customDomainChangeSet.deletes.map((del) => client.removeCustomDomain(del.request))]);
|
|
1942
|
+
} else await Promise.all(changeSet.deletes.map((del) => client.deleteStaticWebsite(del.request)));
|
|
1943
|
+
}
|
|
1944
|
+
function customDomainTrn(workspaceId, websiteName, domain) {
|
|
1945
|
+
return `trn:v1:workspace:${workspaceId}:staticwebsite:${websiteName}:custom_domain:${domain}`;
|
|
1946
|
+
}
|
|
1947
|
+
function normalizeComparableStaticWebsiteShape(input) {
|
|
1948
|
+
return {
|
|
1949
|
+
description: input.description,
|
|
1950
|
+
allowedIpAddresses: input.allowedIpAddresses.toSorted()
|
|
1951
|
+
};
|
|
1952
|
+
}
|
|
1953
|
+
function normalizeComparableStaticWebsite(input) {
|
|
1954
|
+
return normalizeComparableStaticWebsiteShape({
|
|
1955
|
+
description: input.description || "",
|
|
1956
|
+
allowedIpAddresses: [...input.allowedIpAddresses || []]
|
|
1957
|
+
});
|
|
1958
|
+
}
|
|
1959
|
+
function areStaticWebsitesEqual(existing, desired) {
|
|
1960
|
+
return areNormalizedEqual(normalizeComparableStaticWebsite(existing), normalizeComparableStaticWebsite(desired));
|
|
1961
|
+
}
|
|
1962
|
+
/**
|
|
1963
|
+
* Static website names expected to exist after this deploy run.
|
|
1964
|
+
* @param context - Planning context
|
|
1965
|
+
* @returns Names from the deploy-run scope when set, otherwise the application's own websites
|
|
1966
|
+
*/
|
|
1967
|
+
function expectedLocalStaticWebsiteNames(context) {
|
|
1968
|
+
return context.expectedLocalStaticWebsiteNames ?? new Set(context.application.staticWebsiteServices.map((website) => website.name));
|
|
1969
|
+
}
|
|
1970
|
+
/**
|
|
1971
|
+
* Plan static website changes based on current and desired state.
|
|
1972
|
+
* @param context - Planning context
|
|
1973
|
+
* @returns Planned changes
|
|
1974
|
+
*/
|
|
1975
|
+
async function planStaticWebsite(context) {
|
|
1976
|
+
const { client, workspaceId, application, forRemoval } = context;
|
|
1977
|
+
const changeSet = createChangeSet("StaticWebsites");
|
|
1978
|
+
const customDomainChangeSet = createChangeSet("CustomDomains");
|
|
1979
|
+
const conflicts = [];
|
|
1980
|
+
const unmanaged = [];
|
|
1981
|
+
const resourceOwners = /* @__PURE__ */ new Set();
|
|
1982
|
+
const existingWebsites = await fetchExistingResourcesWithLabels({
|
|
1983
|
+
client,
|
|
1984
|
+
fetchPage: async (pageToken, pageSize) => {
|
|
1985
|
+
const { staticwebsites, nextPageToken } = await client.listStaticWebsites({
|
|
1986
|
+
workspaceId,
|
|
1987
|
+
pageToken,
|
|
1988
|
+
pageSize
|
|
1989
|
+
});
|
|
1990
|
+
return [staticwebsites, nextPageToken];
|
|
1991
|
+
},
|
|
1992
|
+
getName: (resource) => resource.name,
|
|
1993
|
+
getTrn: (name) => resourceTrn(workspaceId, "staticwebsite", name)
|
|
1994
|
+
});
|
|
1995
|
+
const ownedWebsiteNames = /* @__PURE__ */ new Set();
|
|
1996
|
+
const staticWebsiteServices = forRemoval ? [] : application.staticWebsiteServices;
|
|
1997
|
+
for (const websiteService of staticWebsiteServices) {
|
|
1998
|
+
const config = websiteService;
|
|
1999
|
+
const name = websiteService.name;
|
|
2000
|
+
const existing = existingWebsites[name];
|
|
2001
|
+
const metaRequest = await buildMetaRequest({
|
|
2002
|
+
trn: resourceTrn(workspaceId, "staticwebsite", name),
|
|
2003
|
+
appName: application.name,
|
|
2004
|
+
appId: application.id
|
|
2005
|
+
});
|
|
2006
|
+
const desired = normalizeComparableStaticWebsite(config);
|
|
2007
|
+
const request = {
|
|
2008
|
+
workspaceId,
|
|
2009
|
+
staticwebsite: {
|
|
2010
|
+
name,
|
|
2011
|
+
description: config.description || "",
|
|
2012
|
+
allowedIpAddresses: config.allowedIpAddresses || []
|
|
2013
|
+
}
|
|
2014
|
+
};
|
|
2015
|
+
if (existing) {
|
|
2016
|
+
const owned = trackDesiredResourceOwnership({
|
|
2017
|
+
labels: existing.allLabels,
|
|
2018
|
+
ownerLabel: existing.label,
|
|
2019
|
+
appName: application.name,
|
|
2020
|
+
appId: application.id,
|
|
2021
|
+
resourceType: "StaticWebsite",
|
|
2022
|
+
resourceName: name,
|
|
2023
|
+
conflicts,
|
|
2024
|
+
unmanaged
|
|
2025
|
+
});
|
|
2026
|
+
if (owned && hasMatchingSdkVersion(existing.allLabels, metaRequest.labels) && areStaticWebsitesEqual(existing.resource, desired)) changeSet.unchanged.push({ name });
|
|
2027
|
+
else changeSet.updates.push({
|
|
2028
|
+
name,
|
|
2029
|
+
request,
|
|
2030
|
+
metaRequest
|
|
2031
|
+
});
|
|
2032
|
+
if (owned) ownedWebsiteNames.add(name);
|
|
2033
|
+
delete existingWebsites[name];
|
|
2034
|
+
} else {
|
|
2035
|
+
changeSet.creates.push({
|
|
2036
|
+
name,
|
|
2037
|
+
request,
|
|
2038
|
+
metaRequest
|
|
2039
|
+
});
|
|
2040
|
+
ownedWebsiteNames.add(name);
|
|
2041
|
+
}
|
|
2042
|
+
}
|
|
2043
|
+
Object.entries(existingWebsites).forEach(([name]) => {
|
|
2044
|
+
const entry = existingWebsites[name];
|
|
2045
|
+
const label = entry?.label;
|
|
2046
|
+
if (trackRemainingResourceOwner({
|
|
2047
|
+
labels: entry?.allLabels,
|
|
2048
|
+
ownerLabel: label,
|
|
2049
|
+
appName: application.name,
|
|
2050
|
+
appId: application.id,
|
|
2051
|
+
resourceOwners
|
|
2052
|
+
})) changeSet.deletes.push({
|
|
2053
|
+
name,
|
|
2054
|
+
request: {
|
|
2055
|
+
workspaceId,
|
|
2056
|
+
name
|
|
2057
|
+
}
|
|
2058
|
+
});
|
|
2059
|
+
});
|
|
2060
|
+
const desiredDomainsByWebsite = /* @__PURE__ */ new Map();
|
|
2061
|
+
for (const service of staticWebsiteServices) if (service.customDomains !== void 0 && ownedWebsiteNames.has(service.name)) desiredDomainsByWebsite.set(service.name, service.customDomains);
|
|
2062
|
+
const existingDomainsByWebsite = /* @__PURE__ */ new Map();
|
|
2063
|
+
const websitesToFetchDomains = [...ownedWebsiteNames].filter((name) => !changeSet.creates.some((c) => c.name === name));
|
|
2064
|
+
await Promise.all(websitesToFetchDomains.map(async (name) => {
|
|
2065
|
+
try {
|
|
2066
|
+
const { customDomains } = await client.listCustomDomains({
|
|
2067
|
+
workspaceId,
|
|
2068
|
+
staticWebsiteName: name
|
|
2069
|
+
});
|
|
2070
|
+
const domainsWithLabels = await Promise.all(customDomains.map(async (d) => {
|
|
2071
|
+
const { metadata } = await client.getMetadata({ trn: customDomainTrn(workspaceId, name, d.domain) });
|
|
2072
|
+
return {
|
|
2073
|
+
domain: d.domain,
|
|
2074
|
+
allLabels: metadata?.labels
|
|
2075
|
+
};
|
|
2076
|
+
}));
|
|
2077
|
+
existingDomainsByWebsite.set(name, domainsWithLabels);
|
|
2078
|
+
} catch (error) {
|
|
2079
|
+
if (error instanceof ConnectError && error.code === Code.NotFound) return;
|
|
2080
|
+
throw error;
|
|
2081
|
+
}
|
|
2082
|
+
}));
|
|
2083
|
+
for (const name of ownedWebsiteNames) {
|
|
2084
|
+
const desired = new Set(desiredDomainsByWebsite.get(name) ?? []);
|
|
2085
|
+
const existingDomains = existingDomainsByWebsite.get(name) ?? [];
|
|
2086
|
+
const existingSet = new Set(existingDomains.map((d) => d.domain));
|
|
2087
|
+
const sdkOwnedDomains = new Set(existingDomains.filter((d) => isOwnedByApp(d.allLabels, application.name, application.id)).map((d) => d.domain));
|
|
2088
|
+
for (const domain of desired) if (!existingSet.has(domain)) {
|
|
2089
|
+
const metaRequest = await buildMetaRequest({
|
|
2090
|
+
trn: customDomainTrn(workspaceId, name, domain),
|
|
2091
|
+
appName: application.name,
|
|
2092
|
+
appId: application.id
|
|
2093
|
+
});
|
|
2094
|
+
customDomainChangeSet.creates.push({
|
|
2095
|
+
name: domain,
|
|
2096
|
+
request: {
|
|
2097
|
+
workspaceId,
|
|
2098
|
+
staticWebsiteName: name,
|
|
2099
|
+
domain
|
|
2100
|
+
},
|
|
2101
|
+
metaRequest
|
|
2102
|
+
});
|
|
2103
|
+
} else customDomainChangeSet.unchanged.push({ name: domain });
|
|
2104
|
+
if (desiredDomainsByWebsite.has(name)) {
|
|
2105
|
+
for (const domain of sdkOwnedDomains) if (!desired.has(domain)) customDomainChangeSet.deletes.push({
|
|
2106
|
+
name: domain,
|
|
2107
|
+
request: {
|
|
2108
|
+
workspaceId,
|
|
2109
|
+
domain
|
|
2110
|
+
}
|
|
2111
|
+
});
|
|
2112
|
+
}
|
|
2113
|
+
}
|
|
2114
|
+
return {
|
|
2115
|
+
changeSet,
|
|
2116
|
+
customDomainChangeSet,
|
|
2117
|
+
conflicts,
|
|
2118
|
+
unmanaged,
|
|
2119
|
+
resourceOwners
|
|
2120
|
+
};
|
|
2121
|
+
}
|
|
2122
|
+
|
|
1897
2123
|
//#endregion
|
|
1898
2124
|
//#region src/cli/commands/deploy/aigateway.ts
|
|
1899
2125
|
/**
|
|
@@ -1956,7 +2182,7 @@ async function planAIGateway(context) {
|
|
|
1956
2182
|
getTrn: (name) => resourceTrn(workspaceId, "aigateway", name)
|
|
1957
2183
|
});
|
|
1958
2184
|
const aiGatewayServices = forRemoval ? [] : application.aiGatewayServices;
|
|
1959
|
-
const expectedLocalWebsites =
|
|
2185
|
+
const expectedLocalWebsites = expectedLocalStaticWebsiteNames(context);
|
|
1960
2186
|
for (const gatewayService of aiGatewayServices) {
|
|
1961
2187
|
const config = gatewayService;
|
|
1962
2188
|
const name = gatewayService.name;
|
|
@@ -2156,23 +2382,26 @@ async function planApplication(context, httpAdapterBuildResult) {
|
|
|
2156
2382
|
if (idProvider) authIdpConfigName = idProvider.name;
|
|
2157
2383
|
} else if (application.config.auth) {
|
|
2158
2384
|
authNamespace = application.config.auth.name;
|
|
2159
|
-
|
|
2160
|
-
|
|
2161
|
-
|
|
2162
|
-
|
|
2163
|
-
|
|
2164
|
-
|
|
2165
|
-
|
|
2166
|
-
|
|
2167
|
-
|
|
2168
|
-
|
|
2169
|
-
|
|
2170
|
-
|
|
2385
|
+
if (context.externalAuthIdpConfigNames?.has(authNamespace)) authIdpConfigName = context.externalAuthIdpConfigNames.get(authNamespace);
|
|
2386
|
+
else {
|
|
2387
|
+
const idpConfigs = await fetchAll(async (pageToken, maxPageSize) => {
|
|
2388
|
+
try {
|
|
2389
|
+
const { idpConfigs, nextPageToken } = await client.listAuthIDPConfigs({
|
|
2390
|
+
workspaceId,
|
|
2391
|
+
namespaceName: assertDefined(authNamespace, "authNamespace must be set before listing IDP configs"),
|
|
2392
|
+
pageToken,
|
|
2393
|
+
pageSize: maxPageSize
|
|
2394
|
+
});
|
|
2395
|
+
return [idpConfigs, nextPageToken];
|
|
2396
|
+
} catch (error) {
|
|
2397
|
+
if (error instanceof ConnectError && error.code === Code.NotFound) return [[], ""];
|
|
2398
|
+
throw error;
|
|
2399
|
+
}
|
|
2400
|
+
});
|
|
2401
|
+
if (idpConfigs.length > 0) {
|
|
2402
|
+
const [firstConfig] = idpConfigs;
|
|
2403
|
+
if (firstConfig) authIdpConfigName = firstConfig.name;
|
|
2171
2404
|
}
|
|
2172
|
-
});
|
|
2173
|
-
if (idpConfigs.length > 0) {
|
|
2174
|
-
const [firstConfig] = idpConfigs;
|
|
2175
|
-
if (firstConfig) authIdpConfigName = firstConfig.name;
|
|
2176
2405
|
}
|
|
2177
2406
|
}
|
|
2178
2407
|
const metaRequest = await buildMetaRequest({
|
|
@@ -2180,7 +2409,7 @@ async function planApplication(context, httpAdapterBuildResult) {
|
|
|
2180
2409
|
appName: application.name,
|
|
2181
2410
|
appId: application.id
|
|
2182
2411
|
});
|
|
2183
|
-
const expectedLocalWebsites =
|
|
2412
|
+
const expectedLocalWebsites = expectedLocalStaticWebsiteNames(context);
|
|
2184
2413
|
const resolvedCors = await resolveStaticWebsiteUrls(client, workspaceId, application.config.cors, "CORS", { expectedLocalNames: expectedLocalWebsites });
|
|
2185
2414
|
const httpAdapters = buildHttpAdapters(application, httpAdapterBuildResult);
|
|
2186
2415
|
const desired = normalizeComparableApplication(application, authNamespace, authIdpConfigName, resolvedCors, httpAdapters);
|
|
@@ -2455,14 +2684,15 @@ async function planAuthConnections(client, workspaceId, appName, appId, auths) {
|
|
|
2455
2684
|
appId
|
|
2456
2685
|
});
|
|
2457
2686
|
if (existing) {
|
|
2458
|
-
|
|
2687
|
+
trackDesiredResourceOwnership({
|
|
2688
|
+
labels: existing.allLabels,
|
|
2689
|
+
ownerLabel: existing.label,
|
|
2690
|
+
appName,
|
|
2691
|
+
appId,
|
|
2459
2692
|
resourceType: "Auth connection",
|
|
2460
2693
|
resourceName: name,
|
|
2461
|
-
|
|
2462
|
-
|
|
2463
|
-
else unmanaged.push({
|
|
2464
|
-
resourceType: "Auth connection",
|
|
2465
|
-
resourceName: name
|
|
2694
|
+
conflicts,
|
|
2695
|
+
unmanaged
|
|
2466
2696
|
});
|
|
2467
2697
|
const secretChanged = hashValue(config.clientSecret) !== state.connections?.[name];
|
|
2468
2698
|
const updateMask = buildUpdateMask(existing.resource, config, secretChanged);
|
|
@@ -2488,12 +2718,13 @@ async function planAuthConnections(client, workspaceId, appName, appId, auths) {
|
|
|
2488
2718
|
}
|
|
2489
2719
|
for (const [name, entry] of Object.entries(existingConnections)) {
|
|
2490
2720
|
if (!entry) continue;
|
|
2491
|
-
|
|
2492
|
-
|
|
2493
|
-
|
|
2494
|
-
|
|
2495
|
-
|
|
2496
|
-
|
|
2721
|
+
if (trackRemainingResourceOwner({
|
|
2722
|
+
labels: entry.allLabels,
|
|
2723
|
+
ownerLabel: entry.label,
|
|
2724
|
+
appName,
|
|
2725
|
+
appId,
|
|
2726
|
+
resourceOwners
|
|
2727
|
+
})) changeSet.deletes.push({
|
|
2497
2728
|
name,
|
|
2498
2729
|
request: {
|
|
2499
2730
|
workspaceId,
|
|
@@ -2656,9 +2887,9 @@ function authHookFunctionName(authName, hookPoint) {
|
|
|
2656
2887
|
function collectFunctionEntries(application, workflowJobs, bundledScripts) {
|
|
2657
2888
|
const entries = [];
|
|
2658
2889
|
for (const app of application.applications) for (const pipeline of app.resolverServices) for (const resolver of Object.values(pipeline.resolvers)) {
|
|
2659
|
-
const content = bundledScripts.resolvers.get(resolver.name);
|
|
2890
|
+
const content = bundledScripts.resolvers.get(resolverBundleKey(pipeline.namespace, resolver.name));
|
|
2660
2891
|
if (!content) {
|
|
2661
|
-
logger.warn(`Bundled code not found for resolver: ${resolver.name}`);
|
|
2892
|
+
logger.warn(`Bundled code not found for resolver: ${pipeline.namespace}/${resolver.name}`);
|
|
2662
2893
|
continue;
|
|
2663
2894
|
}
|
|
2664
2895
|
entries.push({
|
|
@@ -2762,15 +2993,15 @@ async function planFunctionRegistry(client, workspaceId, appName, appId, entries
|
|
|
2762
2993
|
appId
|
|
2763
2994
|
});
|
|
2764
2995
|
if (existing) {
|
|
2765
|
-
const owned =
|
|
2766
|
-
|
|
2767
|
-
|
|
2768
|
-
|
|
2769
|
-
|
|
2770
|
-
else conflicts.push({
|
|
2996
|
+
const owned = trackDesiredResourceOwnership({
|
|
2997
|
+
labels: existing.allLabels,
|
|
2998
|
+
ownerLabel: existing.label,
|
|
2999
|
+
appName,
|
|
3000
|
+
appId,
|
|
2771
3001
|
resourceType: "Function registry",
|
|
2772
3002
|
resourceName: entry.name,
|
|
2773
|
-
|
|
3003
|
+
conflicts,
|
|
3004
|
+
unmanaged
|
|
2774
3005
|
});
|
|
2775
3006
|
if (existing.resource.contentHash === entry.contentHash && owned && hasMatchingSdkVersion(existing.allLabels, metaRequest.labels)) changeSet.unchanged.push({ name: entry.name });
|
|
2776
3007
|
else changeSet.updates.push({
|
|
@@ -2787,10 +3018,13 @@ async function planFunctionRegistry(client, workspaceId, appName, appId, entries
|
|
|
2787
3018
|
}
|
|
2788
3019
|
for (const [name, existing] of Object.entries(existingMap)) {
|
|
2789
3020
|
if (!existing) continue;
|
|
2790
|
-
|
|
2791
|
-
|
|
2792
|
-
|
|
2793
|
-
|
|
3021
|
+
if (trackRemainingResourceOwner({
|
|
3022
|
+
labels: existing.allLabels,
|
|
3023
|
+
ownerLabel: existing.label,
|
|
3024
|
+
appName,
|
|
3025
|
+
appId,
|
|
3026
|
+
resourceOwners
|
|
3027
|
+
})) changeSet.deletes.push({
|
|
2794
3028
|
name,
|
|
2795
3029
|
workspaceId
|
|
2796
3030
|
});
|
|
@@ -3314,7 +3548,7 @@ async function applyIdP(client, result, phase = "create-update") {
|
|
|
3314
3548
|
async function planIdP(context) {
|
|
3315
3549
|
const { client, workspaceId, application, forRemoval, forceApplyAll = false, idpUserTriggerTargets } = context;
|
|
3316
3550
|
const idps = forRemoval ? [] : application.idpServices;
|
|
3317
|
-
const expectedLocalWebsites =
|
|
3551
|
+
const expectedLocalWebsites = expectedLocalStaticWebsiteNames(context);
|
|
3318
3552
|
const { changeSet: serviceChangeSet, conflicts, unmanaged, resourceOwners } = await planServices$3(client, workspaceId, application.name, application.id, idps, idpUserTriggerTargets ?? /* @__PURE__ */ new Set(), expectedLocalWebsites);
|
|
3319
3553
|
return {
|
|
3320
3554
|
changeSet: {
|
|
@@ -3484,17 +3718,16 @@ async function planServices$3(client, workspaceId, appName, appId, idps, idpUser
|
|
|
3484
3718
|
permission: protoPermission
|
|
3485
3719
|
};
|
|
3486
3720
|
if (existing) {
|
|
3487
|
-
|
|
3488
|
-
|
|
3489
|
-
|
|
3490
|
-
|
|
3491
|
-
|
|
3492
|
-
else conflicts.push({
|
|
3721
|
+
if (trackDesiredResourceOwnership({
|
|
3722
|
+
labels: existing.allLabels,
|
|
3723
|
+
ownerLabel: existing.label,
|
|
3724
|
+
appName,
|
|
3725
|
+
appId,
|
|
3493
3726
|
resourceType: "IdP service",
|
|
3494
3727
|
resourceName: idp.name,
|
|
3495
|
-
|
|
3496
|
-
|
|
3497
|
-
|
|
3728
|
+
conflicts,
|
|
3729
|
+
unmanaged
|
|
3730
|
+
}) && hasMatchingSdkVersion(existing.allLabels, metaRequest.labels) && areIdPServicesEqual(existing.resource, desired)) changeSet.unchanged.push({ name: namespaceName });
|
|
3498
3731
|
else changeSet.updates.push({
|
|
3499
3732
|
name: namespaceName,
|
|
3500
3733
|
request,
|
|
@@ -3509,10 +3742,13 @@ async function planServices$3(client, workspaceId, appName, appId, idps, idpUser
|
|
|
3509
3742
|
}
|
|
3510
3743
|
Object.entries(existingServices).forEach(([namespaceName]) => {
|
|
3511
3744
|
const entry = existingServices[namespaceName];
|
|
3512
|
-
|
|
3513
|
-
|
|
3514
|
-
|
|
3515
|
-
|
|
3745
|
+
if (trackRemainingResourceOwner({
|
|
3746
|
+
labels: entry?.allLabels,
|
|
3747
|
+
ownerLabel: entry?.label,
|
|
3748
|
+
appName,
|
|
3749
|
+
appId,
|
|
3750
|
+
resourceOwners
|
|
3751
|
+
})) changeSet.deletes.push({
|
|
3516
3752
|
name: namespaceName,
|
|
3517
3753
|
request: {
|
|
3518
3754
|
workspaceId,
|
|
@@ -3701,7 +3937,7 @@ function protoIdPOperand(operand) {
|
|
|
3701
3937
|
*/
|
|
3702
3938
|
async function applyAuth(client, result, phase = "create-update") {
|
|
3703
3939
|
const { changeSet } = result;
|
|
3704
|
-
|
|
3940
|
+
const applyServices = async () => {
|
|
3705
3941
|
await Promise.all([...changeSet.service.creates.map(async (create) => {
|
|
3706
3942
|
await client.createAuthService(create.request);
|
|
3707
3943
|
await client.setMetadata(create.metaRequest);
|
|
@@ -3709,6 +3945,11 @@ async function applyAuth(client, result, phase = "create-update") {
|
|
|
3709
3945
|
await client.updateAuthService(update.request);
|
|
3710
3946
|
await client.setMetadata(update.metaRequest);
|
|
3711
3947
|
})]);
|
|
3948
|
+
};
|
|
3949
|
+
const applyMachineUsers = async () => {
|
|
3950
|
+
await Promise.all([...changeSet.machineUser.creates.map((create) => client.createAuthMachineUser(create.request)), ...changeSet.machineUser.updates.map((update) => client.updateAuthMachineUser(update.request))]);
|
|
3951
|
+
};
|
|
3952
|
+
const applyCreateUpdateDependents = async () => {
|
|
3712
3953
|
await applyAuthConnections(client, { changeSet: changeSet.connection }, "create-update");
|
|
3713
3954
|
await Promise.all([...changeSet.idpConfig.creates.map(async (create) => {
|
|
3714
3955
|
if (create.idpConfig.kind === "BuiltInIdP") assertDefined(create.request.idpConfig, "request missing idpConfig").config = await protoBuiltinIdPConfig(client, assertDefined(create.request.workspaceId, "request missing workspaceId"), create.idpConfig);
|
|
@@ -3719,7 +3960,6 @@ async function applyAuth(client, result, phase = "create-update") {
|
|
|
3719
3960
|
})]);
|
|
3720
3961
|
await Promise.all([...changeSet.userProfileConfig.creates.map((create) => client.createUserProfileConfig(create.request)), ...changeSet.userProfileConfig.updates.map((update) => client.updateUserProfileConfig(update.request))]);
|
|
3721
3962
|
await Promise.all([...changeSet.tenantConfig.creates.map((create) => client.createTenantConfig(create.request)), ...changeSet.tenantConfig.updates.map((update) => client.updateTenantConfig(update.request))]);
|
|
3722
|
-
await Promise.all([...changeSet.machineUser.creates.map((create) => client.createAuthMachineUser(create.request)), ...changeSet.machineUser.updates.map((update) => client.updateAuthMachineUser(update.request))]);
|
|
3723
3963
|
await Promise.all([...changeSet.authHook.creates.map((create) => client.createAuthHook(create.request)), ...changeSet.authHook.updates.map((update) => client.updateAuthHook(update.request))]);
|
|
3724
3964
|
await Promise.all([...changeSet.oauth2Client.creates.map(async (create) => {
|
|
3725
3965
|
const oauth2Client = assertDefined(create.request.oauth2Client, "request missing oauth2Client");
|
|
@@ -3738,7 +3978,13 @@ async function applyAuth(client, result, phase = "create-update") {
|
|
|
3738
3978
|
}
|
|
3739
3979
|
await Promise.all([...changeSet.scim.creates.map((create) => client.createAuthSCIMConfig(create.request)), ...changeSet.scim.updates.map((update) => client.updateAuthSCIMConfig(update.request))]);
|
|
3740
3980
|
await Promise.all([...changeSet.scimResource.creates.map((create) => client.createAuthSCIMResource(create.request)), ...changeSet.scimResource.updates.map((update) => client.updateAuthSCIMResource(update.request))]);
|
|
3741
|
-
}
|
|
3981
|
+
};
|
|
3982
|
+
if (phase === "create-update-prerequisites" || phase === "create-update") {
|
|
3983
|
+
await applyServices();
|
|
3984
|
+
await applyMachineUsers();
|
|
3985
|
+
}
|
|
3986
|
+
if (phase === "create-update-dependents" || phase === "create-update") await applyCreateUpdateDependents();
|
|
3987
|
+
if (phase === "delete-resources") {
|
|
3742
3988
|
await Promise.all(changeSet.scimResource.deletes.map((del) => client.deleteAuthSCIMResource(del.request)));
|
|
3743
3989
|
await Promise.all(changeSet.scim.deletes.map((del) => client.deleteAuthSCIMConfig(del.request)));
|
|
3744
3990
|
await Promise.all(changeSet.oauth2Client.deletes.map((del) => client.deleteAuthOAuth2Client(del.request)));
|
|
@@ -3748,7 +3994,7 @@ async function applyAuth(client, result, phase = "create-update") {
|
|
|
3748
3994
|
await Promise.all(changeSet.userProfileConfig.deletes.map((del) => client.deleteUserProfileConfig(del.request)));
|
|
3749
3995
|
await Promise.all(changeSet.idpConfig.deletes.map((del) => client.deleteAuthIDPConfig(del.request)));
|
|
3750
3996
|
await applyAuthConnections(client, { changeSet: changeSet.connection }, "delete-resources");
|
|
3751
|
-
} else await Promise.all(changeSet.service.deletes.map((del) => client.deleteAuthService(del.request)));
|
|
3997
|
+
} else if (phase === "delete-services") await Promise.all(changeSet.service.deletes.map((del) => client.deleteAuthService(del.request)));
|
|
3752
3998
|
}
|
|
3753
3999
|
/**
|
|
3754
4000
|
* Plan auth-related changes based on current and desired state.
|
|
@@ -3764,7 +4010,7 @@ async function planAuth(context) {
|
|
|
3764
4010
|
}
|
|
3765
4011
|
const { changeSet: serviceChangeSet, conflicts, unmanaged, resourceOwners } = await planServices$2(client, workspaceId, application.name, application.id, auths, forceApplyAll);
|
|
3766
4012
|
const deletedServices = serviceChangeSet.deletes.map((del) => del.name);
|
|
3767
|
-
const expectedLocalWebsites =
|
|
4013
|
+
const expectedLocalWebsites = expectedLocalStaticWebsiteNames(context);
|
|
3768
4014
|
const [idpConfigChangeSet, userProfileConfigChangeSet, tenantConfigChangeSet, machineUserChangeSet, authHookChangeSet, oauth2ClientChangeSet, scimChangeSet, scimResourceChangeSet, connectionResult] = await Promise.all([
|
|
3769
4015
|
planIdPConfigs(client, workspaceId, auths, deletedServices, forceApplyAll),
|
|
3770
4016
|
planUserProfileConfigs(client, workspaceId, auths, deletedServices, forceApplyAll),
|
|
@@ -3826,15 +4072,15 @@ async function planServices$2(client, workspaceId, appName, appId, auths, forceA
|
|
|
3826
4072
|
publishSessionEvents: config.publishSessionEvents
|
|
3827
4073
|
};
|
|
3828
4074
|
if (existing) {
|
|
3829
|
-
const owned =
|
|
3830
|
-
|
|
3831
|
-
|
|
3832
|
-
|
|
3833
|
-
|
|
3834
|
-
else conflicts.push({
|
|
4075
|
+
const owned = trackDesiredResourceOwnership({
|
|
4076
|
+
labels: existing.allLabels,
|
|
4077
|
+
ownerLabel: existing.label,
|
|
4078
|
+
appName,
|
|
4079
|
+
appId,
|
|
3835
4080
|
resourceType: "Auth service",
|
|
3836
4081
|
resourceName: config.name,
|
|
3837
|
-
|
|
4082
|
+
conflicts,
|
|
4083
|
+
unmanaged
|
|
3838
4084
|
});
|
|
3839
4085
|
if (!forceApplyAll && existing.resource.publishSessionEvents === (config.publishSessionEvents ?? false) && owned) changeSet.unchanged.push({ name: config.name });
|
|
3840
4086
|
else changeSet.updates.push({
|
|
@@ -3851,10 +4097,13 @@ async function planServices$2(client, workspaceId, appName, appId, auths, forceA
|
|
|
3851
4097
|
}
|
|
3852
4098
|
Object.entries(existingServices).forEach(([namespaceName]) => {
|
|
3853
4099
|
const entry = existingServices[namespaceName];
|
|
3854
|
-
|
|
3855
|
-
|
|
3856
|
-
|
|
3857
|
-
|
|
4100
|
+
if (trackRemainingResourceOwner({
|
|
4101
|
+
labels: entry?.allLabels,
|
|
4102
|
+
ownerLabel: entry?.label,
|
|
4103
|
+
appName,
|
|
4104
|
+
appId,
|
|
4105
|
+
resourceOwners
|
|
4106
|
+
})) changeSet.deletes.push({
|
|
3858
4107
|
name: namespaceName,
|
|
3859
4108
|
request: {
|
|
3860
4109
|
workspaceId,
|
|
@@ -4819,509 +5068,310 @@ async function planAuthHooks(client, workspaceId, auths, deletedServices, forceA
|
|
|
4819
5068
|
}
|
|
4820
5069
|
|
|
4821
5070
|
//#endregion
|
|
4822
|
-
//#region src/cli/commands/deploy/
|
|
4823
|
-
|
|
4824
|
-
|
|
4825
|
-
|
|
4826
|
-
|
|
4827
|
-
|
|
4828
|
-
|
|
4829
|
-
|
|
4830
|
-
|
|
4831
|
-
|
|
4832
|
-
|
|
4833
|
-
|
|
4834
|
-
|
|
4835
|
-
|
|
4836
|
-
|
|
4837
|
-
|
|
4838
|
-
|
|
4839
|
-
|
|
4840
|
-
|
|
4841
|
-
|
|
5071
|
+
//#region src/cli/commands/deploy/auth-invoker.ts
|
|
5072
|
+
/**
|
|
5073
|
+
* Normalize an authInvoker value to the object form required by the proto payload.
|
|
5074
|
+
*
|
|
5075
|
+
* Accepts either:
|
|
5076
|
+
* - `undefined` — returns undefined
|
|
5077
|
+
* - a plain string (machine user name) — expands to `{ namespace, machineUserName }` using `authNamespace`
|
|
5078
|
+
* - an object `{ namespace, machineUserName }` — returned as-is
|
|
5079
|
+
* @param authInvoker - String machine user name or object form
|
|
5080
|
+
* @param authNamespace - Auth service namespace (required when authInvoker is a string)
|
|
5081
|
+
* @param context - Contextual label used in error messages (e.g. `resolver "foo"`)
|
|
5082
|
+
* @returns Object form of auth invoker, or undefined
|
|
5083
|
+
*/
|
|
5084
|
+
function normalizeAuthInvoker(authInvoker, authNamespace, context) {
|
|
5085
|
+
if (authInvoker === void 0) return void 0;
|
|
5086
|
+
if (typeof authInvoker === "string") {
|
|
5087
|
+
if (!authNamespace) throw new Error(`${context} uses a string authInvoker ("${authInvoker}"), but no Auth service is configured. Configure an Auth service or use the object form { namespace, machineUserName }.`);
|
|
5088
|
+
return {
|
|
5089
|
+
namespace: authNamespace,
|
|
5090
|
+
machineUserName: authInvoker
|
|
5091
|
+
};
|
|
4842
5092
|
}
|
|
5093
|
+
return authInvoker;
|
|
4843
5094
|
}
|
|
4844
|
-
|
|
4845
|
-
|
|
4846
|
-
|
|
4847
|
-
|
|
4848
|
-
|
|
4849
|
-
|
|
5095
|
+
|
|
5096
|
+
//#endregion
|
|
5097
|
+
//#region src/cli/commands/deploy/executor.ts
|
|
5098
|
+
/**
|
|
5099
|
+
* Apply executor-related changes for the given phase.
|
|
5100
|
+
* @param client - Operator client instance
|
|
5101
|
+
* @param result - Planned executor changes
|
|
5102
|
+
* @param phase - Apply phase (defaults to "create-update")
|
|
5103
|
+
* @returns Promise that resolves when executors are applied
|
|
5104
|
+
*/
|
|
5105
|
+
async function applyExecutor(client, result, phase = "create-update") {
|
|
5106
|
+
const { changeSet } = result;
|
|
5107
|
+
if (phase === "create-update") await Promise.all([...changeSet.creates.map(async (create) => {
|
|
5108
|
+
await client.createExecutorExecutor(create.request);
|
|
5109
|
+
await client.setMetadata(create.metaRequest);
|
|
5110
|
+
}), ...changeSet.updates.map(async (update) => {
|
|
5111
|
+
await client.updateExecutorExecutor(update.request);
|
|
5112
|
+
await client.setMetadata(update.metaRequest);
|
|
5113
|
+
})]);
|
|
5114
|
+
else await Promise.all(changeSet.deletes.map((del) => client.deleteExecutorExecutor(del.request)));
|
|
4850
5115
|
}
|
|
4851
5116
|
/**
|
|
4852
|
-
*
|
|
4853
|
-
*
|
|
4854
|
-
*
|
|
4855
|
-
* a wrapper that re-exports another config).
|
|
4856
|
-
* @param configPath - Absolute path to the config file
|
|
4857
|
-
* @returns Resolved id and whether it was newly injected, or null if skipped
|
|
5117
|
+
* Plan executor-related changes based on current and desired state.
|
|
5118
|
+
* @param context - Planning context
|
|
5119
|
+
* @returns Planned changes
|
|
4858
5120
|
*/
|
|
4859
|
-
async function
|
|
4860
|
-
const
|
|
4861
|
-
const
|
|
4862
|
-
const
|
|
4863
|
-
|
|
4864
|
-
|
|
4865
|
-
|
|
4866
|
-
|
|
4867
|
-
|
|
4868
|
-
|
|
4869
|
-
|
|
4870
|
-
|
|
4871
|
-
|
|
4872
|
-
|
|
4873
|
-
|
|
4874
|
-
|
|
4875
|
-
|
|
4876
|
-
|
|
4877
|
-
|
|
4878
|
-
|
|
5121
|
+
async function planExecutor(context) {
|
|
5122
|
+
const { client, workspaceId, application, forRemoval } = context;
|
|
5123
|
+
const changeSet = createChangeSet("Executors");
|
|
5124
|
+
const conflicts = [];
|
|
5125
|
+
const unmanaged = [];
|
|
5126
|
+
const resourceOwners = /* @__PURE__ */ new Set();
|
|
5127
|
+
const existingExecutors = await fetchExistingResourcesWithLabels({
|
|
5128
|
+
client,
|
|
5129
|
+
fetchPage: async (pageToken, pageSize) => {
|
|
5130
|
+
const { executors, nextPageToken } = await client.listExecutorExecutors({
|
|
5131
|
+
workspaceId,
|
|
5132
|
+
pageToken,
|
|
5133
|
+
pageSize
|
|
5134
|
+
});
|
|
5135
|
+
return [executors, nextPageToken];
|
|
5136
|
+
},
|
|
5137
|
+
getName: (resource) => resource.name,
|
|
5138
|
+
getTrn: (name) => resourceTrn(workspaceId, "executor", name)
|
|
5139
|
+
});
|
|
5140
|
+
const executors = forRemoval ? {} : await application.executorService?.loadExecutors() ?? {};
|
|
5141
|
+
for (const executor of Object.values(executors)) {
|
|
5142
|
+
const existing = existingExecutors[executor.name];
|
|
5143
|
+
const metaRequest = await buildMetaRequest({
|
|
5144
|
+
trn: resourceTrn(workspaceId, "executor", executor.name),
|
|
5145
|
+
appName: application.name,
|
|
5146
|
+
appId: application.id
|
|
5147
|
+
});
|
|
5148
|
+
const desiredExecutor = protoExecutor(context, executor);
|
|
5149
|
+
if (existing) {
|
|
5150
|
+
if (trackDesiredResourceOwnership({
|
|
5151
|
+
labels: existing.allLabels,
|
|
5152
|
+
ownerLabel: existing.label,
|
|
5153
|
+
appName: application.name,
|
|
5154
|
+
appId: application.id,
|
|
5155
|
+
resourceType: "Executor",
|
|
5156
|
+
resourceName: executor.name,
|
|
5157
|
+
conflicts,
|
|
5158
|
+
unmanaged
|
|
5159
|
+
}) && hasMatchingSdkVersion(existing.allLabels, metaRequest.labels) && areExecutorsEqual(existing.resource, desiredExecutor)) changeSet.unchanged.push({ name: executor.name });
|
|
5160
|
+
else changeSet.updates.push({
|
|
5161
|
+
name: executor.name,
|
|
5162
|
+
request: {
|
|
5163
|
+
workspaceId,
|
|
5164
|
+
executor: desiredExecutor
|
|
5165
|
+
},
|
|
5166
|
+
metaRequest
|
|
5167
|
+
});
|
|
5168
|
+
delete existingExecutors[executor.name];
|
|
5169
|
+
} else changeSet.creates.push({
|
|
5170
|
+
name: executor.name,
|
|
5171
|
+
request: {
|
|
5172
|
+
workspaceId,
|
|
5173
|
+
executor: desiredExecutor
|
|
5174
|
+
},
|
|
5175
|
+
metaRequest
|
|
5176
|
+
});
|
|
4879
5177
|
}
|
|
4880
|
-
|
|
4881
|
-
|
|
4882
|
-
|
|
4883
|
-
|
|
5178
|
+
Object.entries(existingExecutors).forEach(([name]) => {
|
|
5179
|
+
const entry = existingExecutors[name];
|
|
5180
|
+
const label = entry?.label;
|
|
5181
|
+
if (trackRemainingResourceOwner({
|
|
5182
|
+
labels: entry?.allLabels,
|
|
5183
|
+
ownerLabel: label,
|
|
5184
|
+
appName: application.name,
|
|
5185
|
+
appId: application.id,
|
|
5186
|
+
resourceOwners
|
|
5187
|
+
})) changeSet.deletes.push({
|
|
5188
|
+
name,
|
|
5189
|
+
request: {
|
|
5190
|
+
workspaceId,
|
|
5191
|
+
name
|
|
5192
|
+
}
|
|
5193
|
+
});
|
|
5194
|
+
});
|
|
4884
5195
|
return {
|
|
4885
|
-
|
|
4886
|
-
|
|
5196
|
+
changeSet,
|
|
5197
|
+
conflicts,
|
|
5198
|
+
unmanaged,
|
|
5199
|
+
resourceOwners
|
|
4887
5200
|
};
|
|
4888
5201
|
}
|
|
5202
|
+
function isFunctionBackedExecutor(executor) {
|
|
5203
|
+
return executor?.targetType === ExecutorTargetType.FUNCTION || executor?.targetType === ExecutorTargetType.JOB_FUNCTION;
|
|
5204
|
+
}
|
|
4889
5205
|
/**
|
|
4890
|
-
*
|
|
4891
|
-
*
|
|
4892
|
-
*
|
|
4893
|
-
* no usable `id` property.
|
|
4894
|
-
* @param configPath - Absolute path to the config file
|
|
4895
|
-
* @returns The existing id (or null when absent), or null for wrapper configs
|
|
5206
|
+
* Build desired executor configs keyed by executor name from create/update changes.
|
|
5207
|
+
* @param changeSet - Executor create/update changes
|
|
5208
|
+
* @returns Executor configs keyed by name
|
|
4896
5209
|
*/
|
|
4897
|
-
|
|
4898
|
-
|
|
4899
|
-
const calls = [];
|
|
4900
|
-
findDefineConfigCalls(program, calls);
|
|
4901
|
-
if (calls.length === 0) return null;
|
|
4902
|
-
if (calls.length > 1) throw new Error(`Multiple defineConfig() calls found in ${configPath}. Only one is supported.`);
|
|
4903
|
-
const { configObj } = assertDefined(calls[0], "defineConfig call site missing");
|
|
4904
|
-
if (!configObj) throw new Error(`defineConfig() argument must be an inline object literal in ${configPath} so the SDK can manage the 'id' field.`);
|
|
4905
|
-
const idProp = findIdProperty(configObj);
|
|
4906
|
-
if (!idProp || idProp.value.type !== "Literal") return { id: null };
|
|
4907
|
-
const value = idProp.value.value;
|
|
4908
|
-
return { id: typeof value === "string" && value !== "" ? value : null };
|
|
5210
|
+
function buildPlannedExecutorsByName(changeSet) {
|
|
5211
|
+
return Object.fromEntries([...changeSet.creates, ...changeSet.updates].map((item) => [item.name, item.request.executor]));
|
|
4909
5212
|
}
|
|
4910
5213
|
/**
|
|
4911
|
-
*
|
|
4912
|
-
*
|
|
4913
|
-
*
|
|
4914
|
-
* @param
|
|
5214
|
+
* Format executor changes for grouped dry-run display.
|
|
5215
|
+
* @param changeSet - Executor changes
|
|
5216
|
+
* @param executors - Desired executor configs keyed by name
|
|
5217
|
+
* @param functionRegistryExecutorChanges - Related function registry changes for executors
|
|
5218
|
+
* @returns Display entries for executor output
|
|
4915
5219
|
*/
|
|
4916
|
-
|
|
4917
|
-
|
|
4918
|
-
|
|
4919
|
-
|
|
4920
|
-
|
|
5220
|
+
function formatExecutorChangeEntries(changeSet, executors, functionRegistryExecutorChanges) {
|
|
5221
|
+
return formatChangeEntriesWithFunctionRegistry("executor", changeSet, functionRegistryExecutorChanges, (item, action) => {
|
|
5222
|
+
if (action === "delete") return [executorFunctionName(item.name)];
|
|
5223
|
+
const executor = executors[item.name];
|
|
5224
|
+
return executor && isFunctionBackedExecutor(executor) ? [executorFunctionName(item.name)] : [];
|
|
5225
|
+
});
|
|
4921
5226
|
}
|
|
4922
|
-
|
|
4923
|
-
|
|
4924
|
-
|
|
4925
|
-
|
|
4926
|
-
|
|
4927
|
-
|
|
4928
|
-
|
|
4929
|
-
|
|
4930
|
-
|
|
4931
|
-
|
|
4932
|
-
|
|
4933
|
-
|
|
4934
|
-
|
|
4935
|
-
|
|
4936
|
-
|
|
4937
|
-
|
|
4938
|
-
|
|
4939
|
-
|
|
4940
|
-
|
|
4941
|
-
|
|
4942
|
-
|
|
4943
|
-
|
|
4944
|
-
|
|
4945
|
-
|
|
4946
|
-
|
|
4947
|
-
|
|
4948
|
-
|
|
4949
|
-
|
|
4950
|
-
|
|
4951
|
-
|
|
4952
|
-
|
|
4953
|
-
|
|
5227
|
+
function normalizeComparableExecutor(executor) {
|
|
5228
|
+
const normalized = normalizeProtoConfig(executor);
|
|
5229
|
+
const webhookHeaders = normalized.targetConfig?.config?.case === "webhook" ? (normalized.targetConfig.config.value.headers ?? []).toSorted((left, right) => (left.key ?? "").localeCompare(right.key ?? "")) : void 0;
|
|
5230
|
+
const triggerConfig = normalized.triggerConfig?.config?.case === "incomingWebhook" ? {
|
|
5231
|
+
...normalized.triggerConfig,
|
|
5232
|
+
config: {
|
|
5233
|
+
...normalized.triggerConfig.config,
|
|
5234
|
+
value: {
|
|
5235
|
+
...normalized.triggerConfig.config.value,
|
|
5236
|
+
secret: void 0
|
|
5237
|
+
}
|
|
5238
|
+
}
|
|
5239
|
+
} : normalized.triggerConfig?.config?.case === "event" ? {
|
|
5240
|
+
...normalized.triggerConfig,
|
|
5241
|
+
config: {
|
|
5242
|
+
...normalized.triggerConfig.config,
|
|
5243
|
+
value: {
|
|
5244
|
+
...normalized.triggerConfig.config.value,
|
|
5245
|
+
eventType: void 0
|
|
5246
|
+
}
|
|
5247
|
+
}
|
|
5248
|
+
} : normalized.triggerConfig;
|
|
5249
|
+
return {
|
|
5250
|
+
name: normalized.name,
|
|
5251
|
+
description: normalized.description ?? "",
|
|
5252
|
+
disabled: normalized.disabled ?? false,
|
|
5253
|
+
triggerType: normalized.triggerType,
|
|
5254
|
+
triggerConfig,
|
|
5255
|
+
targetType: normalized.targetType,
|
|
5256
|
+
targetConfig: normalized.targetConfig?.config?.case === "webhook" ? {
|
|
5257
|
+
...normalized.targetConfig,
|
|
5258
|
+
config: {
|
|
5259
|
+
...normalized.targetConfig.config,
|
|
5260
|
+
value: {
|
|
5261
|
+
...normalized.targetConfig.config.value,
|
|
5262
|
+
headers: webhookHeaders
|
|
5263
|
+
}
|
|
5264
|
+
}
|
|
5265
|
+
} : normalized.targetConfig?.config?.case === "function" ? {
|
|
5266
|
+
...normalized.targetConfig,
|
|
5267
|
+
config: {
|
|
5268
|
+
...normalized.targetConfig.config,
|
|
5269
|
+
value: {
|
|
5270
|
+
...normalized.targetConfig.config.value,
|
|
5271
|
+
script: void 0
|
|
5272
|
+
}
|
|
5273
|
+
}
|
|
5274
|
+
} : normalized.targetConfig
|
|
5275
|
+
};
|
|
4954
5276
|
}
|
|
4955
|
-
|
|
4956
|
-
|
|
4957
|
-
const idLiteral = `id: ${JSON.stringify(id)}`;
|
|
4958
|
-
if (configObj.properties.length > 0) {
|
|
4959
|
-
const firstProp = assertDefined(configObj.properties[0], "first property missing");
|
|
4960
|
-
const lineStart = source.lastIndexOf("\n", firstProp.start - 1) + 1;
|
|
4961
|
-
const indent = source.slice(lineStart, firstProp.start);
|
|
4962
|
-
if (!/^[\t ]*$/.test(indent)) return source.slice(0, firstProp.start) + `${idLiteral}, ` + source.slice(firstProp.start);
|
|
4963
|
-
const insertion = `${idComment}\n${indent}${idLiteral},\n${indent}`;
|
|
4964
|
-
return source.slice(0, firstProp.start) + insertion + source.slice(firstProp.start);
|
|
4965
|
-
}
|
|
4966
|
-
const openBracePos = configObj.start + 1;
|
|
4967
|
-
const braceLineStart = source.lastIndexOf("\n", configObj.start) + 1;
|
|
4968
|
-
const baseIndent = source.slice(braceLineStart).match(/^[\t ]*/)?.[0] ?? "";
|
|
4969
|
-
const innerIndent = `${baseIndent} `;
|
|
4970
|
-
const insertion = `\n${innerIndent}${idComment}\n${innerIndent}${idLiteral},\n${baseIndent}`;
|
|
4971
|
-
return source.slice(0, openBracePos) + insertion + source.slice(openBracePos);
|
|
5277
|
+
function areExecutorsEqual(existing, desired) {
|
|
5278
|
+
return areNormalizedEqual(normalizeComparableExecutor(existing), normalizeComparableExecutor(desired));
|
|
4972
5279
|
}
|
|
4973
|
-
|
|
4974
|
-
//#endregion
|
|
4975
|
-
//#region src/cli/commands/deploy/confirm.ts
|
|
4976
5280
|
/**
|
|
4977
|
-
*
|
|
4978
|
-
*
|
|
4979
|
-
*
|
|
4980
|
-
*
|
|
4981
|
-
* @param
|
|
4982
|
-
* @
|
|
4983
|
-
* @param yes - Whether to auto-confirm without prompting
|
|
4984
|
-
* @returns Promise that resolves when confirmation completes
|
|
5281
|
+
* Resolve a resource's namespace from same-run peer configs, throwing when the
|
|
5282
|
+
* name is claimed by more than one namespace in the deploy run.
|
|
5283
|
+
* @param sameRunNamespaces - Namespaces keyed by resource name across the deploy run
|
|
5284
|
+
* @param resourceName - Resource name to resolve
|
|
5285
|
+
* @param resourceLabel - Resource label used in error messages
|
|
5286
|
+
* @returns The owning namespace, or undefined when the name is unknown
|
|
4985
5287
|
*/
|
|
4986
|
-
|
|
4987
|
-
if (
|
|
4988
|
-
const
|
|
4989
|
-
|
|
4990
|
-
|
|
4991
|
-
if (nameMismatches.length > 0) await confirmNameMismatch(nameMismatches, appName, yes);
|
|
4992
|
-
}
|
|
4993
|
-
async function confirmIdRegeneration(conflicts, appName, yes) {
|
|
4994
|
-
logger.warn(`Application id was regenerated for "${appName}":`);
|
|
4995
|
-
logger.log(" These resources still carry the previous id.");
|
|
4996
|
-
logger.newline();
|
|
4997
|
-
logger.log(` ${styles.info("Resources")}:`);
|
|
4998
|
-
for (const c of conflicts) logger.log(` • ${styles.bold(c.resourceType)} ${styles.info(`"${c.resourceName}"`)}`);
|
|
4999
|
-
if (yes) {
|
|
5000
|
-
logger.success("Re-tagging resources with the new id (--yes flag specified)...", { mode: "plain" });
|
|
5001
|
-
return;
|
|
5002
|
-
}
|
|
5003
|
-
if (!await prompt.confirm({
|
|
5004
|
-
message: `Re-tag these resources with the new id for "${appName}"?\n${styles.dim("(The id in tailor.config.ts was removed since the previous deploy, so a new one was generated)")}`,
|
|
5005
|
-
default: false
|
|
5006
|
-
})) throw new Error(multiline`
|
|
5007
|
-
Apply cancelled. Resources remain tagged with the previous id.
|
|
5008
|
-
To override, run again and confirm, or use --yes flag.
|
|
5009
|
-
`);
|
|
5010
|
-
}
|
|
5011
|
-
async function confirmNameMismatch(conflicts, appName, yes) {
|
|
5012
|
-
const currentOwners = [...new Set(conflicts.map((c) => c.currentOwner))];
|
|
5013
|
-
logger.warn("Application name mismatch detected:");
|
|
5014
|
-
logger.log(` ${styles.warning("Current application(s)")}: ${currentOwners.map((o) => styles.bold(`"${o}"`)).join(", ")}`);
|
|
5015
|
-
logger.log(` ${styles.success("New application")}: ${styles.bold(`"${appName}"`)}`);
|
|
5016
|
-
logger.newline();
|
|
5017
|
-
logger.log(` ${styles.info("Resources")}:`);
|
|
5018
|
-
for (const c of conflicts) logger.log(` • ${styles.bold(c.resourceType)} ${styles.info(`"${c.resourceName}"`)}`);
|
|
5019
|
-
if (yes) {
|
|
5020
|
-
logger.success("Updating resources (--yes flag specified)...", { mode: "plain" });
|
|
5021
|
-
return;
|
|
5022
|
-
}
|
|
5023
|
-
const promptMessage = currentOwners.length === 1 ? `Update these resources to be managed by "${appName}"?\n${styles.dim("(Common when renaming your application)")}` : `Update these resources to be managed by "${appName}"?`;
|
|
5024
|
-
if (!await prompt.confirm({
|
|
5025
|
-
message: promptMessage,
|
|
5026
|
-
default: false
|
|
5027
|
-
})) throw new Error(multiline`
|
|
5028
|
-
Apply cancelled. Resources remain managed by their current applications.
|
|
5029
|
-
To override, run again and confirm, or use --yes flag.
|
|
5030
|
-
`);
|
|
5288
|
+
function resolveSameRunNamespace(sameRunNamespaces, resourceName, resourceLabel) {
|
|
5289
|
+
if (!sameRunNamespaces?.has(resourceName)) return;
|
|
5290
|
+
const namespace = sameRunNamespaces.get(resourceName);
|
|
5291
|
+
if (!namespace) throw new Error(`${resourceLabel} "${resourceName}" is defined in multiple namespaces in this deploy run. Move the trigger to the application that owns it or use unique names.`);
|
|
5292
|
+
return namespace;
|
|
5031
5293
|
}
|
|
5032
5294
|
/**
|
|
5033
|
-
*
|
|
5034
|
-
* @param
|
|
5035
|
-
* @param
|
|
5036
|
-
* @
|
|
5037
|
-
* @returns Promise that resolves when confirmation completes
|
|
5295
|
+
* Find the local TailorDB namespace that declares the given type.
|
|
5296
|
+
* @param application - Loaded application
|
|
5297
|
+
* @param typeName - TailorDB type name to look up
|
|
5298
|
+
* @returns The declaring namespace, or undefined when no local service has it
|
|
5038
5299
|
*/
|
|
5039
|
-
|
|
5040
|
-
|
|
5041
|
-
logger.warn("Existing resources not tracked by tailor-sdk were found:");
|
|
5042
|
-
logger.log(` ${styles.info("Resources")}:`);
|
|
5043
|
-
for (const r of resources) logger.log(` • ${styles.bold(r.resourceType)} ${styles.info(`"${r.resourceName}"`)}`);
|
|
5044
|
-
logger.newline();
|
|
5045
|
-
logger.log(" These resources may have been created by older SDK versions, Terraform, or CUE.");
|
|
5046
|
-
logger.log(" To continue, confirm that tailor-sdk should manage them.");
|
|
5047
|
-
logger.log(" If they are managed by another tool (e.g., Terraform), cancel and manage them there instead.");
|
|
5048
|
-
if (yes) {
|
|
5049
|
-
logger.success(`Adding to "${appName}" (--yes flag specified)...`, { mode: "plain" });
|
|
5050
|
-
return;
|
|
5051
|
-
}
|
|
5052
|
-
if (!await prompt.confirm({
|
|
5053
|
-
message: `Allow tailor-sdk to manage these resources for "${appName}"?`,
|
|
5054
|
-
default: false
|
|
5055
|
-
})) throw new Error(multiline`
|
|
5056
|
-
Apply cancelled. Resources remain unmanaged.
|
|
5057
|
-
To override, run again and confirm, or use --yes flag.
|
|
5058
|
-
`);
|
|
5300
|
+
function findTailorDBNamespace(application, typeName) {
|
|
5301
|
+
return application.tailorDBServices.find((service) => Object.hasOwn(service.types, typeName))?.namespace;
|
|
5059
5302
|
}
|
|
5060
5303
|
/**
|
|
5061
|
-
*
|
|
5062
|
-
* @param
|
|
5063
|
-
* @param
|
|
5064
|
-
* @returns
|
|
5065
|
-
*/
|
|
5066
|
-
|
|
5067
|
-
|
|
5068
|
-
|
|
5069
|
-
|
|
5070
|
-
|
|
5071
|
-
|
|
5072
|
-
|
|
5073
|
-
|
|
5074
|
-
|
|
5075
|
-
|
|
5076
|
-
|
|
5077
|
-
|
|
5078
|
-
|
|
5079
|
-
|
|
5080
|
-
|
|
5081
|
-
|
|
5082
|
-
|
|
5083
|
-
|
|
5304
|
+
* Find the local resolver namespace that declares the given resolver.
|
|
5305
|
+
* @param application - Loaded application
|
|
5306
|
+
* @param resolverName - Resolver name to look up
|
|
5307
|
+
* @returns The declaring namespace, or undefined when no local service has it
|
|
5308
|
+
*/
|
|
5309
|
+
function findResolverNamespace(application, resolverName) {
|
|
5310
|
+
return application.resolverServices.find((service) => Object.values(service.resolvers).some((resolver) => resolver.name === resolverName))?.namespace;
|
|
5311
|
+
}
|
|
5312
|
+
function resolveNamespace(params) {
|
|
5313
|
+
const { resourceLabel, resourceName, localNamespaces, findLocalNamespace, sameRunNamespaces } = params;
|
|
5314
|
+
const localNamespace = findLocalNamespace();
|
|
5315
|
+
if (localNamespace !== void 0) return localNamespace;
|
|
5316
|
+
const sameRunNamespace = resolveSameRunNamespace(sameRunNamespaces, resourceName, resourceLabel);
|
|
5317
|
+
if (sameRunNamespace !== void 0) return sameRunNamespace;
|
|
5318
|
+
const sameRunAvailableNamespaces = sameRunNamespaces ? [...new Set([...sameRunNamespaces.values()].filter((value) => value !== void 0))] : [];
|
|
5319
|
+
const availableNamespaces = [...localNamespaces, ...sameRunAvailableNamespaces];
|
|
5320
|
+
throw new Error(`${resourceLabel} "${resourceName}" not found in any namespace. Available namespaces: ${availableNamespaces.join(", ")}`);
|
|
5321
|
+
}
|
|
5322
|
+
function resolveTailorDBNamespace(application, typeName, sameRunNamespaces) {
|
|
5323
|
+
return resolveNamespace({
|
|
5324
|
+
resourceLabel: "TailorDB type",
|
|
5325
|
+
resourceName: typeName,
|
|
5326
|
+
localNamespaces: application.tailorDBServices.map((service) => service.namespace),
|
|
5327
|
+
findLocalNamespace: () => findTailorDBNamespace(application, typeName),
|
|
5328
|
+
sameRunNamespaces
|
|
5329
|
+
});
|
|
5330
|
+
}
|
|
5331
|
+
function resolveResolverNamespace(application, resolverName, sameRunNamespaces) {
|
|
5332
|
+
return resolveNamespace({
|
|
5333
|
+
resourceLabel: "Resolver",
|
|
5334
|
+
resourceName: resolverName,
|
|
5335
|
+
localNamespaces: application.resolverServices.map((service) => service.namespace),
|
|
5336
|
+
findLocalNamespace: () => findResolverNamespace(application, resolverName),
|
|
5337
|
+
sameRunNamespaces
|
|
5338
|
+
});
|
|
5084
5339
|
}
|
|
5085
|
-
|
|
5086
|
-
//#endregion
|
|
5087
|
-
//#region src/cli/commands/deploy/auth-invoker.ts
|
|
5088
5340
|
/**
|
|
5089
|
-
*
|
|
5090
|
-
*
|
|
5091
|
-
*
|
|
5092
|
-
*
|
|
5093
|
-
* - a plain string (machine user name) — expands to `{ namespace, machineUserName }` using `authNamespace`
|
|
5094
|
-
* - an object `{ namespace, machineUserName }` — returned as-is
|
|
5095
|
-
* @param authInvoker - String machine user name or object form
|
|
5096
|
-
* @param authNamespace - Auth service namespace (required when authInvoker is a string)
|
|
5097
|
-
* @param context - Contextual label used in error messages (e.g. `resolver "foo"`)
|
|
5098
|
-
* @returns Object form of auth invoker, or undefined
|
|
5341
|
+
* Collect IdP names declared by the application, including external IdP
|
|
5342
|
+
* subgraphs.
|
|
5343
|
+
* @param application - Loaded application
|
|
5344
|
+
* @returns IdP names visible from the application config
|
|
5099
5345
|
*/
|
|
5100
|
-
function
|
|
5101
|
-
|
|
5102
|
-
if (
|
|
5103
|
-
|
|
5104
|
-
|
|
5105
|
-
|
|
5106
|
-
|
|
5107
|
-
|
|
5346
|
+
function collectApplicationIdpNames(application) {
|
|
5347
|
+
const names = new Set(application.idpServices.map((idp) => idp.name));
|
|
5348
|
+
for (const subgraph of application.subgraphs) if (subgraph.Type === "idp") names.add(subgraph.Name);
|
|
5349
|
+
return names;
|
|
5350
|
+
}
|
|
5351
|
+
function resolveIdpNamespace(application, executorName, idpName, sameRunIdpNames) {
|
|
5352
|
+
const localIdpNames = collectApplicationIdpNames(application);
|
|
5353
|
+
if (idpName !== void 0) {
|
|
5354
|
+
const candidateNames = sameRunIdpNames ?? localIdpNames;
|
|
5355
|
+
if (!candidateNames.has(idpName)) {
|
|
5356
|
+
const available = [...candidateNames].join(", ");
|
|
5357
|
+
throw new Error(`Executor "${executorName}" specifies IdP "${idpName}" in its idpUser trigger, but no IdP with that name is configured. Available IdPs: ${available}`);
|
|
5358
|
+
}
|
|
5359
|
+
return idpName;
|
|
5108
5360
|
}
|
|
5109
|
-
|
|
5110
|
-
|
|
5111
|
-
|
|
5112
|
-
|
|
5113
|
-
|
|
5114
|
-
|
|
5115
|
-
* Apply executor-related changes for the given phase.
|
|
5116
|
-
* @param client - Operator client instance
|
|
5117
|
-
* @param result - Planned executor changes
|
|
5118
|
-
* @param phase - Apply phase (defaults to "create-update")
|
|
5119
|
-
* @returns Promise that resolves when executors are applied
|
|
5120
|
-
*/
|
|
5121
|
-
async function applyExecutor(client, result, phase = "create-update") {
|
|
5122
|
-
const { changeSet } = result;
|
|
5123
|
-
if (phase === "create-update") await Promise.all([...changeSet.creates.map(async (create) => {
|
|
5124
|
-
await client.createExecutorExecutor(create.request);
|
|
5125
|
-
await client.setMetadata(create.metaRequest);
|
|
5126
|
-
}), ...changeSet.updates.map(async (update) => {
|
|
5127
|
-
await client.updateExecutorExecutor(update.request);
|
|
5128
|
-
await client.setMetadata(update.metaRequest);
|
|
5129
|
-
})]);
|
|
5130
|
-
else await Promise.all(changeSet.deletes.map((del) => client.deleteExecutorExecutor(del.request)));
|
|
5131
|
-
}
|
|
5132
|
-
/**
|
|
5133
|
-
* Plan executor-related changes based on current and desired state.
|
|
5134
|
-
* @param context - Planning context
|
|
5135
|
-
* @returns Planned changes
|
|
5136
|
-
*/
|
|
5137
|
-
async function planExecutor(context) {
|
|
5138
|
-
const { client, workspaceId, application, forRemoval } = context;
|
|
5139
|
-
const changeSet = createChangeSet("Executors");
|
|
5140
|
-
const conflicts = [];
|
|
5141
|
-
const unmanaged = [];
|
|
5142
|
-
const resourceOwners = /* @__PURE__ */ new Set();
|
|
5143
|
-
const existingExecutors = await fetchExistingResourcesWithLabels({
|
|
5144
|
-
client,
|
|
5145
|
-
fetchPage: async (pageToken, pageSize) => {
|
|
5146
|
-
const { executors, nextPageToken } = await client.listExecutorExecutors({
|
|
5147
|
-
workspaceId,
|
|
5148
|
-
pageToken,
|
|
5149
|
-
pageSize
|
|
5150
|
-
});
|
|
5151
|
-
return [executors, nextPageToken];
|
|
5152
|
-
},
|
|
5153
|
-
getName: (resource) => resource.name,
|
|
5154
|
-
getTrn: (name) => resourceTrn(workspaceId, "executor", name)
|
|
5155
|
-
});
|
|
5156
|
-
const executors = forRemoval ? {} : await application.executorService?.loadExecutors() ?? {};
|
|
5157
|
-
for (const executor of Object.values(executors)) {
|
|
5158
|
-
const existing = existingExecutors[executor.name];
|
|
5159
|
-
const metaRequest = await buildMetaRequest({
|
|
5160
|
-
trn: resourceTrn(workspaceId, "executor", executor.name),
|
|
5161
|
-
appName: application.name,
|
|
5162
|
-
appId: application.id
|
|
5163
|
-
});
|
|
5164
|
-
const desiredExecutor = protoExecutor(application, executor);
|
|
5165
|
-
if (existing) {
|
|
5166
|
-
if (trackDesiredResourceOwnership({
|
|
5167
|
-
labels: existing.allLabels,
|
|
5168
|
-
ownerLabel: existing.label,
|
|
5169
|
-
appName: application.name,
|
|
5170
|
-
appId: application.id,
|
|
5171
|
-
resourceType: "Executor",
|
|
5172
|
-
resourceName: executor.name,
|
|
5173
|
-
conflicts,
|
|
5174
|
-
unmanaged
|
|
5175
|
-
}) && hasMatchingSdkVersion(existing.allLabels, metaRequest.labels) && areExecutorsEqual(existing.resource, desiredExecutor)) changeSet.unchanged.push({ name: executor.name });
|
|
5176
|
-
else changeSet.updates.push({
|
|
5177
|
-
name: executor.name,
|
|
5178
|
-
request: {
|
|
5179
|
-
workspaceId,
|
|
5180
|
-
executor: desiredExecutor
|
|
5181
|
-
},
|
|
5182
|
-
metaRequest
|
|
5183
|
-
});
|
|
5184
|
-
delete existingExecutors[executor.name];
|
|
5185
|
-
} else changeSet.creates.push({
|
|
5186
|
-
name: executor.name,
|
|
5187
|
-
request: {
|
|
5188
|
-
workspaceId,
|
|
5189
|
-
executor: desiredExecutor
|
|
5190
|
-
},
|
|
5191
|
-
metaRequest
|
|
5192
|
-
});
|
|
5193
|
-
}
|
|
5194
|
-
Object.entries(existingExecutors).forEach(([name]) => {
|
|
5195
|
-
const entry = existingExecutors[name];
|
|
5196
|
-
const label = entry?.label;
|
|
5197
|
-
if (trackRemainingResourceOwner({
|
|
5198
|
-
labels: entry?.allLabels,
|
|
5199
|
-
ownerLabel: label,
|
|
5200
|
-
appName: application.name,
|
|
5201
|
-
appId: application.id,
|
|
5202
|
-
resourceOwners
|
|
5203
|
-
})) changeSet.deletes.push({
|
|
5204
|
-
name,
|
|
5205
|
-
request: {
|
|
5206
|
-
workspaceId,
|
|
5207
|
-
name
|
|
5208
|
-
}
|
|
5209
|
-
});
|
|
5210
|
-
});
|
|
5211
|
-
return {
|
|
5212
|
-
changeSet,
|
|
5213
|
-
conflicts,
|
|
5214
|
-
unmanaged,
|
|
5215
|
-
resourceOwners
|
|
5216
|
-
};
|
|
5217
|
-
}
|
|
5218
|
-
function isFunctionBackedExecutor(executor) {
|
|
5219
|
-
return executor?.targetType === ExecutorTargetType.FUNCTION || executor?.targetType === ExecutorTargetType.JOB_FUNCTION;
|
|
5220
|
-
}
|
|
5221
|
-
/**
|
|
5222
|
-
* Build desired executor configs keyed by executor name from create/update changes.
|
|
5223
|
-
* @param changeSet - Executor create/update changes
|
|
5224
|
-
* @returns Executor configs keyed by name
|
|
5225
|
-
*/
|
|
5226
|
-
function buildPlannedExecutorsByName(changeSet) {
|
|
5227
|
-
return Object.fromEntries([...changeSet.creates, ...changeSet.updates].map((item) => [item.name, item.request.executor]));
|
|
5228
|
-
}
|
|
5229
|
-
/**
|
|
5230
|
-
* Format executor changes for grouped dry-run display.
|
|
5231
|
-
* @param changeSet - Executor changes
|
|
5232
|
-
* @param executors - Desired executor configs keyed by name
|
|
5233
|
-
* @param functionRegistryExecutorChanges - Related function registry changes for executors
|
|
5234
|
-
* @returns Display entries for executor output
|
|
5235
|
-
*/
|
|
5236
|
-
function formatExecutorChangeEntries(changeSet, executors, functionRegistryExecutorChanges) {
|
|
5237
|
-
return formatChangeEntriesWithFunctionRegistry("executor", changeSet, functionRegistryExecutorChanges, (item, action) => {
|
|
5238
|
-
if (action === "delete") return [executorFunctionName(item.name)];
|
|
5239
|
-
const executor = executors[item.name];
|
|
5240
|
-
return executor && isFunctionBackedExecutor(executor) ? [executorFunctionName(item.name)] : [];
|
|
5241
|
-
});
|
|
5242
|
-
}
|
|
5243
|
-
function normalizeComparableExecutor(executor) {
|
|
5244
|
-
const normalized = normalizeProtoConfig(executor);
|
|
5245
|
-
const webhookHeaders = normalized.targetConfig?.config?.case === "webhook" ? (normalized.targetConfig.config.value.headers ?? []).toSorted((left, right) => (left.key ?? "").localeCompare(right.key ?? "")) : void 0;
|
|
5246
|
-
const triggerConfig = normalized.triggerConfig?.config?.case === "incomingWebhook" ? {
|
|
5247
|
-
...normalized.triggerConfig,
|
|
5248
|
-
config: {
|
|
5249
|
-
...normalized.triggerConfig.config,
|
|
5250
|
-
value: {
|
|
5251
|
-
...normalized.triggerConfig.config.value,
|
|
5252
|
-
secret: void 0
|
|
5253
|
-
}
|
|
5254
|
-
}
|
|
5255
|
-
} : normalized.triggerConfig?.config?.case === "event" ? {
|
|
5256
|
-
...normalized.triggerConfig,
|
|
5257
|
-
config: {
|
|
5258
|
-
...normalized.triggerConfig.config,
|
|
5259
|
-
value: {
|
|
5260
|
-
...normalized.triggerConfig.config.value,
|
|
5261
|
-
eventType: void 0
|
|
5262
|
-
}
|
|
5263
|
-
}
|
|
5264
|
-
} : normalized.triggerConfig;
|
|
5265
|
-
return {
|
|
5266
|
-
name: normalized.name,
|
|
5267
|
-
description: normalized.description ?? "",
|
|
5268
|
-
disabled: normalized.disabled ?? false,
|
|
5269
|
-
triggerType: normalized.triggerType,
|
|
5270
|
-
triggerConfig,
|
|
5271
|
-
targetType: normalized.targetType,
|
|
5272
|
-
targetConfig: normalized.targetConfig?.config?.case === "webhook" ? {
|
|
5273
|
-
...normalized.targetConfig,
|
|
5274
|
-
config: {
|
|
5275
|
-
...normalized.targetConfig.config,
|
|
5276
|
-
value: {
|
|
5277
|
-
...normalized.targetConfig.config.value,
|
|
5278
|
-
headers: webhookHeaders
|
|
5279
|
-
}
|
|
5280
|
-
}
|
|
5281
|
-
} : normalized.targetConfig?.config?.case === "function" ? {
|
|
5282
|
-
...normalized.targetConfig,
|
|
5283
|
-
config: {
|
|
5284
|
-
...normalized.targetConfig.config,
|
|
5285
|
-
value: {
|
|
5286
|
-
...normalized.targetConfig.config.value,
|
|
5287
|
-
script: void 0
|
|
5288
|
-
}
|
|
5289
|
-
}
|
|
5290
|
-
} : normalized.targetConfig
|
|
5291
|
-
};
|
|
5292
|
-
}
|
|
5293
|
-
function areExecutorsEqual(existing, desired) {
|
|
5294
|
-
return areNormalizedEqual(normalizeComparableExecutor(existing), normalizeComparableExecutor(desired));
|
|
5295
|
-
}
|
|
5296
|
-
function resolveTailorDBNamespace(application, typeName) {
|
|
5297
|
-
for (const service of application.tailorDBServices) if (Object.hasOwn(service.types, typeName)) return service.namespace;
|
|
5298
|
-
throw new Error(`TailorDB type "${typeName}" not found in any namespace. Available namespaces: ${application.tailorDBServices.map((s) => s.namespace).join(", ")}`);
|
|
5299
|
-
}
|
|
5300
|
-
function resolveResolverNamespace(application, resolverName) {
|
|
5301
|
-
for (const service of application.resolverServices) if (Object.values(service.resolvers).some((r) => r.name === resolverName)) return service.namespace;
|
|
5302
|
-
throw new Error(`Resolver "${resolverName}" not found in any namespace. Available namespaces: ${application.resolverServices.map((s) => s.namespace).join(", ")}`);
|
|
5303
|
-
}
|
|
5304
|
-
function resolveIdpNamespace(application, executorName, idpName) {
|
|
5305
|
-
if (application.idpServices.length === 0) throw new Error(`Executor "${executorName}" uses an idpUser trigger but no IdP is configured.`);
|
|
5306
|
-
if (idpName !== void 0) {
|
|
5307
|
-
const found = application.idpServices.find((idp) => idp.name === idpName);
|
|
5308
|
-
if (!found) {
|
|
5309
|
-
const available = application.idpServices.map((idp) => idp.name).join(", ");
|
|
5310
|
-
throw new Error(`Executor "${executorName}" specifies IdP "${idpName}" in its idpUser trigger, but no IdP with that name is configured. Available IdPs: ${available}`);
|
|
5311
|
-
}
|
|
5312
|
-
return found.name;
|
|
5313
|
-
}
|
|
5314
|
-
if (application.idpServices.length > 1) {
|
|
5315
|
-
const available = application.idpServices.map((idp) => idp.name).join(", ");
|
|
5316
|
-
throw new Error(`Executor "${executorName}" uses an idpUser trigger but the project defines multiple IdPs (${available}). Specify which IdP to subscribe to via the trigger's "idp" option.`);
|
|
5317
|
-
}
|
|
5318
|
-
return assertDefined(application.idpServices[0], "idp service missing").name;
|
|
5361
|
+
if (localIdpNames.size === 0) throw new Error(`Executor "${executorName}" uses an idpUser trigger but no IdP is configured.`);
|
|
5362
|
+
if (localIdpNames.size > 1) {
|
|
5363
|
+
const available = [...localIdpNames].join(", ");
|
|
5364
|
+
throw new Error(`Executor "${executorName}" uses an idpUser trigger but the project defines multiple IdPs (${available}). Specify which IdP to subscribe to via the trigger's "idp" option.`);
|
|
5365
|
+
}
|
|
5366
|
+
return assertDefined([...localIdpNames][0], "idp service missing");
|
|
5319
5367
|
}
|
|
5320
5368
|
function resolveAuthNamespace(application) {
|
|
5321
|
-
|
|
5322
|
-
|
|
5369
|
+
const authNamespace = application.authService?.config.name ?? application.config.auth?.name;
|
|
5370
|
+
if (!authNamespace) throw new Error("No Auth service configured");
|
|
5371
|
+
return authNamespace;
|
|
5323
5372
|
}
|
|
5324
|
-
function protoExecutor(
|
|
5373
|
+
function protoExecutor(context, executor) {
|
|
5374
|
+
const { application } = context;
|
|
5325
5375
|
const appName = application.name;
|
|
5326
5376
|
const env = application.env;
|
|
5327
5377
|
const trigger = executor.trigger;
|
|
@@ -5351,7 +5401,7 @@ function protoExecutor(application, executor) {
|
|
|
5351
5401
|
case: "tailordb",
|
|
5352
5402
|
value: {
|
|
5353
5403
|
eventTypes: trigger.events,
|
|
5354
|
-
namespaceName: resolveTailorDBNamespace(application, trigger.typeName),
|
|
5404
|
+
namespaceName: resolveTailorDBNamespace(application, trigger.typeName, context.tailorDBTypeNamespaces),
|
|
5355
5405
|
typeName: trigger.typeName,
|
|
5356
5406
|
...trigger.condition ? { condition: { expr: `(${stringifyFunction(trigger.condition)})(${argsExpr})` } } : {}
|
|
5357
5407
|
}
|
|
@@ -5363,7 +5413,7 @@ function protoExecutor(application, executor) {
|
|
|
5363
5413
|
case: "pipeline",
|
|
5364
5414
|
value: {
|
|
5365
5415
|
eventTypes: ["pipeline.resolver.executed"],
|
|
5366
|
-
namespaceName: resolveResolverNamespace(application, trigger.resolverName),
|
|
5416
|
+
namespaceName: resolveResolverNamespace(application, trigger.resolverName, context.resolverNamespaces),
|
|
5367
5417
|
resolverName: trigger.resolverName,
|
|
5368
5418
|
...trigger.condition ? { condition: { expr: `(${stringifyFunction(trigger.condition)})(${argsExpr})` } } : {}
|
|
5369
5419
|
}
|
|
@@ -5385,7 +5435,7 @@ function protoExecutor(application, executor) {
|
|
|
5385
5435
|
case: "idp",
|
|
5386
5436
|
value: {
|
|
5387
5437
|
eventTypes: trigger.events,
|
|
5388
|
-
namespaceName: resolveIdpNamespace(application, executor.name, trigger.idp)
|
|
5438
|
+
namespaceName: resolveIdpNamespace(application, executor.name, trigger.idp, context.idpNames)
|
|
5389
5439
|
}
|
|
5390
5440
|
});
|
|
5391
5441
|
break;
|
|
@@ -5560,7 +5610,8 @@ async function planPipeline(context) {
|
|
|
5560
5610
|
}
|
|
5561
5611
|
const executors = forRemoval ? [] : Object.values(await application.executorService?.loadExecutors() ?? {});
|
|
5562
5612
|
const { changeSet: serviceChangeSet, conflicts, unmanaged, resourceOwners } = await planServices$1(client, workspaceId, application.name, application.id, pipelines);
|
|
5563
|
-
const
|
|
5613
|
+
const deletedServices = serviceChangeSet.deletes.map((del) => del.name);
|
|
5614
|
+
const { changeSet: resolverChangeSet } = await planResolvers(client, workspaceId, pipelines, executors, context.executorUsedResolvers ?? /* @__PURE__ */ new Set(), deletedServices, application.env, application.authService?.config.name, forceApplyAll);
|
|
5564
5615
|
return {
|
|
5565
5616
|
changeSet: {
|
|
5566
5617
|
service: serviceChangeSet,
|
|
@@ -5597,17 +5648,16 @@ async function planServices$1(client, workspaceId, appName, appId, pipelines) {
|
|
|
5597
5648
|
appId
|
|
5598
5649
|
});
|
|
5599
5650
|
if (existing) {
|
|
5600
|
-
|
|
5601
|
-
|
|
5602
|
-
|
|
5603
|
-
|
|
5604
|
-
|
|
5605
|
-
else conflicts.push({
|
|
5651
|
+
if (trackDesiredResourceOwnership({
|
|
5652
|
+
labels: existing.allLabels,
|
|
5653
|
+
ownerLabel: existing.label,
|
|
5654
|
+
appName,
|
|
5655
|
+
appId,
|
|
5606
5656
|
resourceType: "Pipeline service",
|
|
5607
5657
|
resourceName: pipeline.namespace,
|
|
5608
|
-
|
|
5609
|
-
|
|
5610
|
-
|
|
5658
|
+
conflicts,
|
|
5659
|
+
unmanaged
|
|
5660
|
+
}) && hasMatchingSdkVersion(existing.allLabels, metaRequest.labels)) changeSet.unchanged.push({ name: pipeline.namespace });
|
|
5611
5661
|
else changeSet.updates.push({
|
|
5612
5662
|
name: pipeline.namespace,
|
|
5613
5663
|
request: {
|
|
@@ -5628,10 +5678,13 @@ async function planServices$1(client, workspaceId, appName, appId, pipelines) {
|
|
|
5628
5678
|
}
|
|
5629
5679
|
Object.entries(existingServices).forEach(([namespaceName]) => {
|
|
5630
5680
|
const entry = existingServices[namespaceName];
|
|
5631
|
-
|
|
5632
|
-
|
|
5633
|
-
|
|
5634
|
-
|
|
5681
|
+
if (trackRemainingResourceOwner({
|
|
5682
|
+
labels: entry?.allLabels,
|
|
5683
|
+
ownerLabel: entry?.label,
|
|
5684
|
+
appName,
|
|
5685
|
+
appId,
|
|
5686
|
+
resourceOwners
|
|
5687
|
+
})) changeSet.deletes.push({
|
|
5635
5688
|
name: namespaceName,
|
|
5636
5689
|
request: {
|
|
5637
5690
|
workspaceId,
|
|
@@ -5646,7 +5699,7 @@ async function planServices$1(client, workspaceId, appName, appId, pipelines) {
|
|
|
5646
5699
|
resourceOwners
|
|
5647
5700
|
};
|
|
5648
5701
|
}
|
|
5649
|
-
async function planResolvers(client, workspaceId, pipelines, executors, deletedServices, env, authNamespace, forceApplyAll = false) {
|
|
5702
|
+
async function planResolvers(client, workspaceId, pipelines, executors, initialExecutorUsedResolvers, deletedServices, env, authNamespace, forceApplyAll = false) {
|
|
5650
5703
|
const changeSet = createChangeSet("Pipeline resolvers");
|
|
5651
5704
|
const fetchResolvers = (namespaceName) => {
|
|
5652
5705
|
return fetchAll(async (pageToken, maxPageSize) => {
|
|
@@ -5664,7 +5717,7 @@ async function planResolvers(client, workspaceId, pipelines, executors, deletedS
|
|
|
5664
5717
|
}
|
|
5665
5718
|
});
|
|
5666
5719
|
};
|
|
5667
|
-
const executorUsedResolvers =
|
|
5720
|
+
const executorUsedResolvers = new Set(initialExecutorUsedResolvers);
|
|
5668
5721
|
for (const executor of executors) if (executor.trigger.kind === "resolverExecuted") executorUsedResolvers.add(executor.trigger.resolverName);
|
|
5669
5722
|
for (const pipeline of pipelines) for (const resolver of Object.values(pipeline.resolvers)) if (executorUsedResolvers.has(resolver.name) && resolver.publishEvents === false) throw new Error(`Resolver "${resolver.name}" has publishEvents set to false, but it is used by an executor with a resolverExecuted trigger. Either remove the publishEvents: false setting or remove the executor trigger for this resolver.`);
|
|
5670
5723
|
for (const pipeline of pipelines) {
|
|
@@ -5925,17 +5978,16 @@ async function planSecretManager(context) {
|
|
|
5925
5978
|
appName: application.name,
|
|
5926
5979
|
appId: application.id
|
|
5927
5980
|
});
|
|
5928
|
-
|
|
5929
|
-
|
|
5930
|
-
|
|
5931
|
-
|
|
5932
|
-
|
|
5933
|
-
else conflicts.push({
|
|
5981
|
+
if (trackDesiredResourceOwnership({
|
|
5982
|
+
labels: existing.allLabels,
|
|
5983
|
+
ownerLabel: existing.label,
|
|
5984
|
+
appName: application.name,
|
|
5985
|
+
appId: application.id,
|
|
5934
5986
|
resourceType: "Secret Manager vault",
|
|
5935
5987
|
resourceName: vaultName,
|
|
5936
|
-
|
|
5937
|
-
|
|
5938
|
-
|
|
5988
|
+
conflicts,
|
|
5989
|
+
unmanaged
|
|
5990
|
+
}) && hasMatchingSdkVersion(existing.allLabels, metaRequest.labels)) vaultChangeSet.unchanged.push({ name: vaultName });
|
|
5939
5991
|
else vaultChangeSet.updates.push({
|
|
5940
5992
|
name: vaultName,
|
|
5941
5993
|
workspaceId
|
|
@@ -5995,10 +6047,13 @@ async function planSecretManager(context) {
|
|
|
5995
6047
|
}));
|
|
5996
6048
|
for (const [name, entry] of Object.entries(existingVaults)) {
|
|
5997
6049
|
if (!entry) continue;
|
|
5998
|
-
|
|
5999
|
-
|
|
6000
|
-
|
|
6001
|
-
|
|
6050
|
+
if (trackRemainingResourceOwner({
|
|
6051
|
+
labels: entry.allLabels,
|
|
6052
|
+
ownerLabel: entry.label,
|
|
6053
|
+
appName: application.name,
|
|
6054
|
+
appId: application.id,
|
|
6055
|
+
resourceOwners
|
|
6056
|
+
})) {
|
|
6002
6057
|
const secrets = await fetchAll(async (pageToken, maxPageSize) => {
|
|
6003
6058
|
try {
|
|
6004
6059
|
const { secrets, nextPageToken } = await client.listSecretManagerSecrets({
|
|
@@ -6097,213 +6152,17 @@ async function applySecretManager(client, result, phase = "create-update", appli
|
|
|
6097
6152
|
}
|
|
6098
6153
|
|
|
6099
6154
|
//#endregion
|
|
6100
|
-
//#region src/cli/commands/
|
|
6155
|
+
//#region src/cli/commands/tailordb/migrate/config.ts
|
|
6101
6156
|
/**
|
|
6102
|
-
*
|
|
6103
|
-
* @param client - Operator client instance
|
|
6104
|
-
* @param result - Planned static website changes
|
|
6105
|
-
* @param phase - Apply phase
|
|
6106
|
-
* @returns Promise that resolves when static websites are applied
|
|
6157
|
+
* Migration configuration utilities
|
|
6107
6158
|
*/
|
|
6108
|
-
|
|
6109
|
-
|
|
6110
|
-
if (
|
|
6111
|
-
|
|
6112
|
-
|
|
6113
|
-
|
|
6114
|
-
|
|
6115
|
-
await client.updateStaticWebsite(update.request);
|
|
6116
|
-
await client.setMetadata(update.metaRequest);
|
|
6117
|
-
})]);
|
|
6118
|
-
await Promise.all([...customDomainChangeSet.creates.map(async (add) => {
|
|
6119
|
-
await client.addCustomDomain(add.request);
|
|
6120
|
-
await client.setMetadata(add.metaRequest);
|
|
6121
|
-
}), ...customDomainChangeSet.deletes.map((del) => client.removeCustomDomain(del.request))]);
|
|
6122
|
-
} else await Promise.all(changeSet.deletes.map((del) => client.deleteStaticWebsite(del.request)));
|
|
6123
|
-
}
|
|
6124
|
-
function customDomainTrn(workspaceId, websiteName, domain) {
|
|
6125
|
-
return `trn:v1:workspace:${workspaceId}:staticwebsite:${websiteName}:custom_domain:${domain}`;
|
|
6126
|
-
}
|
|
6127
|
-
function normalizeComparableStaticWebsiteShape(input) {
|
|
6128
|
-
return {
|
|
6129
|
-
description: input.description,
|
|
6130
|
-
allowedIpAddresses: input.allowedIpAddresses.toSorted()
|
|
6131
|
-
};
|
|
6132
|
-
}
|
|
6133
|
-
function normalizeComparableStaticWebsite(input) {
|
|
6134
|
-
return normalizeComparableStaticWebsiteShape({
|
|
6135
|
-
description: input.description || "",
|
|
6136
|
-
allowedIpAddresses: [...input.allowedIpAddresses || []]
|
|
6137
|
-
});
|
|
6138
|
-
}
|
|
6139
|
-
function areStaticWebsitesEqual(existing, desired) {
|
|
6140
|
-
return areNormalizedEqual(normalizeComparableStaticWebsite(existing), normalizeComparableStaticWebsite(desired));
|
|
6141
|
-
}
|
|
6142
|
-
/**
|
|
6143
|
-
* Plan static website changes based on current and desired state.
|
|
6144
|
-
* @param context - Planning context
|
|
6145
|
-
* @returns Planned changes
|
|
6146
|
-
*/
|
|
6147
|
-
async function planStaticWebsite(context) {
|
|
6148
|
-
const { client, workspaceId, application, forRemoval } = context;
|
|
6149
|
-
const changeSet = createChangeSet("StaticWebsites");
|
|
6150
|
-
const customDomainChangeSet = createChangeSet("CustomDomains");
|
|
6151
|
-
const conflicts = [];
|
|
6152
|
-
const unmanaged = [];
|
|
6153
|
-
const resourceOwners = /* @__PURE__ */ new Set();
|
|
6154
|
-
const existingWebsites = await fetchExistingResourcesWithLabels({
|
|
6155
|
-
client,
|
|
6156
|
-
fetchPage: async (pageToken, pageSize) => {
|
|
6157
|
-
const { staticwebsites, nextPageToken } = await client.listStaticWebsites({
|
|
6158
|
-
workspaceId,
|
|
6159
|
-
pageToken,
|
|
6160
|
-
pageSize
|
|
6161
|
-
});
|
|
6162
|
-
return [staticwebsites, nextPageToken];
|
|
6163
|
-
},
|
|
6164
|
-
getName: (resource) => resource.name,
|
|
6165
|
-
getTrn: (name) => resourceTrn(workspaceId, "staticwebsite", name)
|
|
6166
|
-
});
|
|
6167
|
-
const ownedWebsiteNames = /* @__PURE__ */ new Set();
|
|
6168
|
-
const staticWebsiteServices = forRemoval ? [] : application.staticWebsiteServices;
|
|
6169
|
-
for (const websiteService of staticWebsiteServices) {
|
|
6170
|
-
const config = websiteService;
|
|
6171
|
-
const name = websiteService.name;
|
|
6172
|
-
const existing = existingWebsites[name];
|
|
6173
|
-
const metaRequest = await buildMetaRequest({
|
|
6174
|
-
trn: resourceTrn(workspaceId, "staticwebsite", name),
|
|
6175
|
-
appName: application.name,
|
|
6176
|
-
appId: application.id
|
|
6177
|
-
});
|
|
6178
|
-
const desired = normalizeComparableStaticWebsite(config);
|
|
6179
|
-
const request = {
|
|
6180
|
-
workspaceId,
|
|
6181
|
-
staticwebsite: {
|
|
6182
|
-
name,
|
|
6183
|
-
description: config.description || "",
|
|
6184
|
-
allowedIpAddresses: config.allowedIpAddresses || []
|
|
6185
|
-
}
|
|
6186
|
-
};
|
|
6187
|
-
if (existing) {
|
|
6188
|
-
const owned = trackDesiredResourceOwnership({
|
|
6189
|
-
labels: existing.allLabels,
|
|
6190
|
-
ownerLabel: existing.label,
|
|
6191
|
-
appName: application.name,
|
|
6192
|
-
appId: application.id,
|
|
6193
|
-
resourceType: "StaticWebsite",
|
|
6194
|
-
resourceName: name,
|
|
6195
|
-
conflicts,
|
|
6196
|
-
unmanaged
|
|
6197
|
-
});
|
|
6198
|
-
if (owned && hasMatchingSdkVersion(existing.allLabels, metaRequest.labels) && areStaticWebsitesEqual(existing.resource, desired)) changeSet.unchanged.push({ name });
|
|
6199
|
-
else changeSet.updates.push({
|
|
6200
|
-
name,
|
|
6201
|
-
request,
|
|
6202
|
-
metaRequest
|
|
6203
|
-
});
|
|
6204
|
-
if (owned) ownedWebsiteNames.add(name);
|
|
6205
|
-
delete existingWebsites[name];
|
|
6206
|
-
} else {
|
|
6207
|
-
changeSet.creates.push({
|
|
6208
|
-
name,
|
|
6209
|
-
request,
|
|
6210
|
-
metaRequest
|
|
6211
|
-
});
|
|
6212
|
-
ownedWebsiteNames.add(name);
|
|
6213
|
-
}
|
|
6214
|
-
}
|
|
6215
|
-
Object.entries(existingWebsites).forEach(([name]) => {
|
|
6216
|
-
const entry = existingWebsites[name];
|
|
6217
|
-
const label = entry?.label;
|
|
6218
|
-
if (trackRemainingResourceOwner({
|
|
6219
|
-
labels: entry?.allLabels,
|
|
6220
|
-
ownerLabel: label,
|
|
6221
|
-
appName: application.name,
|
|
6222
|
-
appId: application.id,
|
|
6223
|
-
resourceOwners
|
|
6224
|
-
})) changeSet.deletes.push({
|
|
6225
|
-
name,
|
|
6226
|
-
request: {
|
|
6227
|
-
workspaceId,
|
|
6228
|
-
name
|
|
6229
|
-
}
|
|
6230
|
-
});
|
|
6231
|
-
});
|
|
6232
|
-
const desiredDomainsByWebsite = /* @__PURE__ */ new Map();
|
|
6233
|
-
for (const service of staticWebsiteServices) if (service.customDomains !== void 0 && ownedWebsiteNames.has(service.name)) desiredDomainsByWebsite.set(service.name, service.customDomains);
|
|
6234
|
-
const existingDomainsByWebsite = /* @__PURE__ */ new Map();
|
|
6235
|
-
const websitesToFetchDomains = [...ownedWebsiteNames].filter((name) => !changeSet.creates.some((c) => c.name === name));
|
|
6236
|
-
await Promise.all(websitesToFetchDomains.map(async (name) => {
|
|
6237
|
-
try {
|
|
6238
|
-
const { customDomains } = await client.listCustomDomains({
|
|
6239
|
-
workspaceId,
|
|
6240
|
-
staticWebsiteName: name
|
|
6241
|
-
});
|
|
6242
|
-
const domainsWithLabels = await Promise.all(customDomains.map(async (d) => {
|
|
6243
|
-
const { metadata } = await client.getMetadata({ trn: customDomainTrn(workspaceId, name, d.domain) });
|
|
6244
|
-
return {
|
|
6245
|
-
domain: d.domain,
|
|
6246
|
-
allLabels: metadata?.labels
|
|
6247
|
-
};
|
|
6248
|
-
}));
|
|
6249
|
-
existingDomainsByWebsite.set(name, domainsWithLabels);
|
|
6250
|
-
} catch (error) {
|
|
6251
|
-
if (error instanceof ConnectError && error.code === Code.NotFound) return;
|
|
6252
|
-
throw error;
|
|
6253
|
-
}
|
|
6254
|
-
}));
|
|
6255
|
-
for (const name of ownedWebsiteNames) {
|
|
6256
|
-
const desired = new Set(desiredDomainsByWebsite.get(name) ?? []);
|
|
6257
|
-
const existingDomains = existingDomainsByWebsite.get(name) ?? [];
|
|
6258
|
-
const existingSet = new Set(existingDomains.map((d) => d.domain));
|
|
6259
|
-
const sdkOwnedDomains = new Set(existingDomains.filter((d) => isOwnedByApp(d.allLabels, application.name, application.id)).map((d) => d.domain));
|
|
6260
|
-
for (const domain of desired) if (!existingSet.has(domain)) {
|
|
6261
|
-
const metaRequest = await buildMetaRequest({
|
|
6262
|
-
trn: customDomainTrn(workspaceId, name, domain),
|
|
6263
|
-
appName: application.name,
|
|
6264
|
-
appId: application.id
|
|
6265
|
-
});
|
|
6266
|
-
customDomainChangeSet.creates.push({
|
|
6267
|
-
name: domain,
|
|
6268
|
-
request: {
|
|
6269
|
-
workspaceId,
|
|
6270
|
-
staticWebsiteName: name,
|
|
6271
|
-
domain
|
|
6272
|
-
},
|
|
6273
|
-
metaRequest
|
|
6274
|
-
});
|
|
6275
|
-
} else customDomainChangeSet.unchanged.push({ name: domain });
|
|
6276
|
-
if (desiredDomainsByWebsite.has(name)) {
|
|
6277
|
-
for (const domain of sdkOwnedDomains) if (!desired.has(domain)) customDomainChangeSet.deletes.push({
|
|
6278
|
-
name: domain,
|
|
6279
|
-
request: {
|
|
6280
|
-
workspaceId,
|
|
6281
|
-
domain
|
|
6282
|
-
}
|
|
6283
|
-
});
|
|
6284
|
-
}
|
|
6285
|
-
}
|
|
6286
|
-
return {
|
|
6287
|
-
changeSet,
|
|
6288
|
-
customDomainChangeSet,
|
|
6289
|
-
conflicts,
|
|
6290
|
-
unmanaged,
|
|
6291
|
-
resourceOwners
|
|
6292
|
-
};
|
|
6293
|
-
}
|
|
6294
|
-
|
|
6295
|
-
//#endregion
|
|
6296
|
-
//#region src/cli/commands/tailordb/migrate/config.ts
|
|
6297
|
-
/**
|
|
6298
|
-
* Migration configuration utilities
|
|
6299
|
-
*/
|
|
6300
|
-
function hasMigrationConfig(dbConfig) {
|
|
6301
|
-
if (typeof dbConfig !== "object" || dbConfig === null) return false;
|
|
6302
|
-
if (!("migration" in dbConfig)) return false;
|
|
6303
|
-
const migration = dbConfig.migration;
|
|
6304
|
-
if (typeof migration !== "object" || migration === null) return false;
|
|
6305
|
-
if (!("directory" in migration)) return false;
|
|
6306
|
-
return typeof migration.directory === "string";
|
|
6159
|
+
function hasMigrationConfig(dbConfig) {
|
|
6160
|
+
if (typeof dbConfig !== "object" || dbConfig === null) return false;
|
|
6161
|
+
if (!("migration" in dbConfig)) return false;
|
|
6162
|
+
const migration = dbConfig.migration;
|
|
6163
|
+
if (typeof migration !== "object" || migration === null) return false;
|
|
6164
|
+
if (!("directory" in migration)) return false;
|
|
6165
|
+
return typeof migration.directory === "string";
|
|
6307
6166
|
}
|
|
6308
6167
|
/**
|
|
6309
6168
|
* Get namespaces that have migrations configured
|
|
@@ -6919,42 +6778,56 @@ function copySnapshotRecord(record) {
|
|
|
6919
6778
|
return copy;
|
|
6920
6779
|
}
|
|
6921
6780
|
/**
|
|
6922
|
-
* Normalize a snapshot field
|
|
6923
|
-
*
|
|
6924
|
-
* when omitted, which avoids false drift between local
|
|
6925
|
-
* may be omitted) and the platform (which always
|
|
6781
|
+
* Normalize a snapshot field into the canonical form for comparison, returning
|
|
6782
|
+
* a new object rather than mutating the input. Currently fills in the platform
|
|
6783
|
+
* default decimal scale when omitted, which avoids false drift between local
|
|
6784
|
+
* schemas (where scale may be omitted) and the platform (which always
|
|
6785
|
+
* materializes a scale).
|
|
6926
6786
|
* @param {SnapshotFieldConfig} field - Field configuration to normalize
|
|
6927
|
-
* @returns
|
|
6787
|
+
* @returns {SnapshotFieldConfig} A new, normalized field object
|
|
6928
6788
|
*/
|
|
6929
6789
|
function normalizeSnapshotField(field) {
|
|
6930
|
-
|
|
6931
|
-
|
|
6932
|
-
|
|
6790
|
+
const fields = field.fields ? Object.fromEntries(Object.entries(field.fields).map(([name, nested]) => [name, normalizeSnapshotField(nested)])) : void 0;
|
|
6791
|
+
return {
|
|
6792
|
+
...field,
|
|
6793
|
+
...field.type === "decimal" && field.scale === void 0 && { scale: 6 },
|
|
6794
|
+
...fields && { fields }
|
|
6795
|
+
};
|
|
6933
6796
|
}
|
|
6934
6797
|
/**
|
|
6935
|
-
* Normalize a snapshot type
|
|
6936
|
-
* Currently fills:
|
|
6798
|
+
* Normalize a snapshot type into the canonical comparison shape, returning a
|
|
6799
|
+
* new object rather than mutating the input. Currently fills:
|
|
6937
6800
|
* - `pluralForm` via inflection when missing (legacy snapshots written
|
|
6938
6801
|
* before `pluralForm` became required may omit it)
|
|
6939
6802
|
* - per-field `scale` defaults via {@link normalizeSnapshotField}
|
|
6940
6803
|
*
|
|
6941
6804
|
* Idempotent — safe to call multiple times on the same input.
|
|
6942
6805
|
* @param {TailorDBSnapshotType} type - Snapshot type to normalize
|
|
6943
|
-
* @returns
|
|
6806
|
+
* @returns {TailorDBSnapshotType} A new, normalized snapshot type object
|
|
6944
6807
|
*/
|
|
6945
6808
|
function normalizeSnapshotType(type) {
|
|
6946
|
-
|
|
6947
|
-
|
|
6948
|
-
|
|
6809
|
+
const pluralForm = type.pluralForm || inflection.pluralize(type.name);
|
|
6810
|
+
const fields = createSnapshotRecord();
|
|
6811
|
+
for (const [fieldName, field] of Object.entries(type.fields)) fields[fieldName] = normalizeSnapshotField(field);
|
|
6812
|
+
return {
|
|
6813
|
+
...type,
|
|
6814
|
+
pluralForm,
|
|
6815
|
+
fields
|
|
6816
|
+
};
|
|
6949
6817
|
}
|
|
6950
6818
|
/**
|
|
6951
|
-
* Normalize a schema snapshot
|
|
6819
|
+
* Normalize a schema snapshot into the canonical comparison shape, returning a
|
|
6820
|
+
* new object rather than mutating the input.
|
|
6952
6821
|
* @param {SchemaSnapshot} snapshot - Schema snapshot to normalize
|
|
6953
|
-
* @returns
|
|
6822
|
+
* @returns {NormalizedSchemaSnapshot} A new schema snapshot object branded as normalized
|
|
6954
6823
|
*/
|
|
6955
6824
|
function normalizeSchemaSnapshot(snapshot) {
|
|
6956
|
-
|
|
6957
|
-
|
|
6825
|
+
const types = createSnapshotRecord();
|
|
6826
|
+
for (const [typeName, type] of Object.entries(snapshot.types)) types[typeName] = normalizeSnapshotType(type);
|
|
6827
|
+
return {
|
|
6828
|
+
...snapshot,
|
|
6829
|
+
types
|
|
6830
|
+
};
|
|
6958
6831
|
}
|
|
6959
6832
|
/**
|
|
6960
6833
|
* Validate that a migration number follows the expected format (4-digit number)
|
|
@@ -7037,8 +6910,7 @@ function createSnapshotFieldConfig(field) {
|
|
|
7037
6910
|
config.fields = {};
|
|
7038
6911
|
for (const [nestedName, nestedConfig] of Object.entries(field.config.fields)) config.fields[nestedName] = createSnapshotFieldConfigFromOperatorConfig(nestedConfig);
|
|
7039
6912
|
}
|
|
7040
|
-
normalizeSnapshotField(config);
|
|
7041
|
-
return config;
|
|
6913
|
+
return normalizeSnapshotField(config);
|
|
7042
6914
|
}
|
|
7043
6915
|
/**
|
|
7044
6916
|
* Create a snapshot field config from an OperatorFieldConfig (for nested fields)
|
|
@@ -7083,8 +6955,7 @@ function createSnapshotFieldConfigFromOperatorConfig(fieldConfig) {
|
|
|
7083
6955
|
config.fields = {};
|
|
7084
6956
|
for (const [nestedName, nestedConfig] of Object.entries(fieldConfig.fields)) config.fields[nestedName] = createSnapshotFieldConfigFromOperatorConfig(nestedConfig);
|
|
7085
6957
|
}
|
|
7086
|
-
normalizeSnapshotField(config);
|
|
7087
|
-
return config;
|
|
6958
|
+
return normalizeSnapshotField(config);
|
|
7088
6959
|
}
|
|
7089
6960
|
/**
|
|
7090
6961
|
* Create a snapshot type from a parsed type
|
|
@@ -10301,7 +10172,7 @@ async function planTailorDB(context) {
|
|
|
10301
10172
|
tailordbs.push(toTailorDBDeployInput(tailordb));
|
|
10302
10173
|
}
|
|
10303
10174
|
const executors = forRemoval ? [] : Object.values(await application.executorService?.loadExecutors() ?? {});
|
|
10304
|
-
const executorUsedTypes =
|
|
10175
|
+
const executorUsedTypes = new Set(context.executorUsedTailorDBTypes ?? []);
|
|
10305
10176
|
for (const executor of executors) if (executor.trigger.kind === "tailordb") executorUsedTypes.add(executor.trigger.typeName);
|
|
10306
10177
|
const { changeSet: serviceChangeSet, conflicts, unmanaged, resourceOwners } = await planServices(client, workspaceId, application.name, application.id, tailordbs);
|
|
10307
10178
|
const deletedServices = serviceChangeSet.deletes.map((del) => del.name);
|
|
@@ -10406,17 +10277,16 @@ async function planServices(client, workspaceId, appName, appId, tailordbs) {
|
|
|
10406
10277
|
existingLabels: existing?.allLabels
|
|
10407
10278
|
});
|
|
10408
10279
|
if (existing) {
|
|
10409
|
-
|
|
10410
|
-
|
|
10411
|
-
|
|
10412
|
-
|
|
10413
|
-
|
|
10414
|
-
else conflicts.push({
|
|
10280
|
+
if (trackDesiredResourceOwnership({
|
|
10281
|
+
labels: existing.allLabels,
|
|
10282
|
+
ownerLabel: existing.label,
|
|
10283
|
+
appName,
|
|
10284
|
+
appId,
|
|
10415
10285
|
resourceType: "TailorDB service",
|
|
10416
10286
|
resourceName: tailordb.namespace,
|
|
10417
|
-
|
|
10418
|
-
|
|
10419
|
-
|
|
10287
|
+
conflicts,
|
|
10288
|
+
unmanaged
|
|
10289
|
+
}) && hasMatchingSdkVersion(existing.allLabels, metaRequest.labels) && areTailorDBServicesEqual(existing.resource, tailordb)) changeSet.unchanged.push({ name: tailordb.namespace });
|
|
10420
10290
|
else changeSet.updates.push({
|
|
10421
10291
|
name: tailordb.namespace,
|
|
10422
10292
|
metaRequest
|
|
@@ -10434,10 +10304,13 @@ async function planServices(client, workspaceId, appName, appId, tailordbs) {
|
|
|
10434
10304
|
}
|
|
10435
10305
|
Object.entries(existingServices).forEach(([namespaceName]) => {
|
|
10436
10306
|
const entry = existingServices[namespaceName];
|
|
10437
|
-
|
|
10438
|
-
|
|
10439
|
-
|
|
10440
|
-
|
|
10307
|
+
if (trackRemainingResourceOwner({
|
|
10308
|
+
labels: entry?.allLabels,
|
|
10309
|
+
ownerLabel: entry?.label,
|
|
10310
|
+
appName,
|
|
10311
|
+
appId,
|
|
10312
|
+
resourceOwners
|
|
10313
|
+
})) changeSet.deletes.push({
|
|
10441
10314
|
name: namespaceName,
|
|
10442
10315
|
request: {
|
|
10443
10316
|
workspaceId,
|
|
@@ -11090,19 +10963,340 @@ function getExistingWorkflowJobNames(existing) {
|
|
|
11090
10963
|
if (existing.mainJobFunctionName) jobNames.add(existing.mainJobFunctionName);
|
|
11091
10964
|
return [...jobNames].toSorted();
|
|
11092
10965
|
}
|
|
11093
|
-
function normalizeRetryPolicyForCompare(policy) {
|
|
11094
|
-
return {
|
|
11095
|
-
maxRetries: policy.maxRetries,
|
|
11096
|
-
backoffMultiplier: policy.backoffMultiplier,
|
|
11097
|
-
initialBackoff: {
|
|
11098
|
-
seconds: String(policy.initialBackoff.seconds),
|
|
11099
|
-
nanos: policy.initialBackoff.nanos
|
|
11100
|
-
},
|
|
11101
|
-
maxBackoff: {
|
|
11102
|
-
seconds: String(policy.maxBackoff.seconds),
|
|
11103
|
-
nanos: policy.maxBackoff.nanos
|
|
11104
|
-
}
|
|
11105
|
-
};
|
|
10966
|
+
function normalizeRetryPolicyForCompare(policy) {
|
|
10967
|
+
return {
|
|
10968
|
+
maxRetries: policy.maxRetries,
|
|
10969
|
+
backoffMultiplier: policy.backoffMultiplier,
|
|
10970
|
+
initialBackoff: {
|
|
10971
|
+
seconds: String(policy.initialBackoff.seconds),
|
|
10972
|
+
nanos: policy.initialBackoff.nanos
|
|
10973
|
+
},
|
|
10974
|
+
maxBackoff: {
|
|
10975
|
+
seconds: String(policy.maxBackoff.seconds),
|
|
10976
|
+
nanos: policy.maxBackoff.nanos
|
|
10977
|
+
}
|
|
10978
|
+
};
|
|
10979
|
+
}
|
|
10980
|
+
|
|
10981
|
+
//#endregion
|
|
10982
|
+
//#region src/cli/commands/deploy/apply-phases.ts
|
|
10983
|
+
/**
|
|
10984
|
+
* Apply planned deploy changes for one or more applications.
|
|
10985
|
+
* @param client - Operator client instance
|
|
10986
|
+
* @param workspaceId - Target workspace ID
|
|
10987
|
+
* @param deployments - Planned deployments to apply
|
|
10988
|
+
*/
|
|
10989
|
+
async function applyDeploymentPlans(client, workspaceId, deployments) {
|
|
10990
|
+
const forEachDeployment = async (apply) => {
|
|
10991
|
+
for (const deployment of deployments) await apply(deployment);
|
|
10992
|
+
};
|
|
10993
|
+
await withSpan("apply.createUpdateServices", async () => {
|
|
10994
|
+
await forEachDeployment((d) => applySecretManager(client, d.secretManager, "create-update", d.application));
|
|
10995
|
+
await forEachDeployment((d) => applyFunctionRegistry(client, workspaceId, d.functionRegistry, "create-update"));
|
|
10996
|
+
await forEachDeployment((d) => applyStaticWebsite(client, d.staticWebsite, "create-update"));
|
|
10997
|
+
await forEachDeployment((d) => applyAIGateway(client, d.aiGateway, "create-update"));
|
|
10998
|
+
await forEachDeployment((d) => applyIdP(client, d.idp, "create-update"));
|
|
10999
|
+
await forEachDeployment((d) => applyAuth(client, d.auth, "create-update-prerequisites"));
|
|
11000
|
+
await forEachDeployment((d) => applyTailorDB(client, d.tailorDB, "create-update"));
|
|
11001
|
+
await forEachDeployment((d) => applyAuth(client, d.auth, "create-update-dependents"));
|
|
11002
|
+
await forEachDeployment((d) => applyPipeline(client, d.pipeline, "create-update"));
|
|
11003
|
+
});
|
|
11004
|
+
await withSpan("apply.deleteSubgraphResources", async () => {
|
|
11005
|
+
await forEachDeployment((d) => applyPipeline(client, d.pipeline, "delete-resources"));
|
|
11006
|
+
await forEachDeployment((d) => applyAuth(client, d.auth, "delete-resources"));
|
|
11007
|
+
await forEachDeployment((d) => applyIdP(client, d.idp, "delete-resources"));
|
|
11008
|
+
});
|
|
11009
|
+
await withSpan("apply.createUpdateApplication", async () => {
|
|
11010
|
+
await forEachDeployment((d) => applyApplication(client, d.app, "create-update"));
|
|
11011
|
+
});
|
|
11012
|
+
await withSpan("apply.createUpdateDependentServices", async () => {
|
|
11013
|
+
await forEachDeployment((d) => applyExecutor(client, d.executor, "create-update"));
|
|
11014
|
+
await forEachDeployment((d) => applyWorkflow(client, d.workflow, "create-update"));
|
|
11015
|
+
});
|
|
11016
|
+
await withSpan("apply.deleteDependentServices", async () => {
|
|
11017
|
+
await forEachDeployment((d) => applyWorkflow(client, d.workflow, "delete"));
|
|
11018
|
+
await forEachDeployment((d) => applyExecutor(client, d.executor, "delete"));
|
|
11019
|
+
await forEachDeployment((d) => applyStaticWebsite(client, d.staticWebsite, "delete"));
|
|
11020
|
+
await forEachDeployment((d) => applyAIGateway(client, d.aiGateway, "delete"));
|
|
11021
|
+
await forEachDeployment((d) => applySecretManager(client, d.secretManager, "delete", d.application));
|
|
11022
|
+
});
|
|
11023
|
+
await withSpan("apply.deleteApplication", async () => {
|
|
11024
|
+
await forEachDeployment((d) => applyApplication(client, d.app, "delete"));
|
|
11025
|
+
});
|
|
11026
|
+
await withSpan("apply.deleteSubgraphServices", async () => {
|
|
11027
|
+
await forEachDeployment((d) => applyPipeline(client, d.pipeline, "delete-services"));
|
|
11028
|
+
await forEachDeployment((d) => applyAuth(client, d.auth, "delete-services"));
|
|
11029
|
+
await forEachDeployment((d) => applyIdP(client, d.idp, "delete-services"));
|
|
11030
|
+
await forEachDeployment((d) => applyTailorDB(client, d.tailorDB, "delete-services"));
|
|
11031
|
+
});
|
|
11032
|
+
await withSpan("apply.cleanup", async () => {
|
|
11033
|
+
await forEachDeployment((d) => applyFunctionRegistry(client, workspaceId, d.functionRegistry, "delete"));
|
|
11034
|
+
});
|
|
11035
|
+
}
|
|
11036
|
+
|
|
11037
|
+
//#endregion
|
|
11038
|
+
//#region src/cli/commands/deploy/config-id-injector.ts
|
|
11039
|
+
const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
11040
|
+
function findDefineConfigCalls(node, results) {
|
|
11041
|
+
if (!node || typeof node !== "object") return;
|
|
11042
|
+
const n = node;
|
|
11043
|
+
if (n.type === "CallExpression") {
|
|
11044
|
+
const ce = n;
|
|
11045
|
+
if (ce.callee.type === "Identifier" && ce.callee.name === "defineConfig") {
|
|
11046
|
+
const arg = ce.arguments[0];
|
|
11047
|
+
const configObj = arg && arg.type === "ObjectExpression" ? arg : null;
|
|
11048
|
+
results.push({
|
|
11049
|
+
callExpr: ce,
|
|
11050
|
+
configObj
|
|
11051
|
+
});
|
|
11052
|
+
}
|
|
11053
|
+
}
|
|
11054
|
+
for (const key of Object.keys(n)) {
|
|
11055
|
+
const child = n[key];
|
|
11056
|
+
if (Array.isArray(child)) for (const c of child) findDefineConfigCalls(c, results);
|
|
11057
|
+
else if (child && typeof child === "object") findDefineConfigCalls(child, results);
|
|
11058
|
+
}
|
|
11059
|
+
}
|
|
11060
|
+
function findIdProperty(obj) {
|
|
11061
|
+
for (const prop of obj.properties) {
|
|
11062
|
+
if (prop.type !== "Property") continue;
|
|
11063
|
+
if ((prop.key.type === "Identifier" ? prop.key.name : prop.key.type === "Literal" ? prop.key.value : null) === "id") return prop;
|
|
11064
|
+
}
|
|
11065
|
+
return null;
|
|
11066
|
+
}
|
|
11067
|
+
/**
|
|
11068
|
+
* Ensure `tailor.config.ts` has an `id` property on the `defineConfig({...})`
|
|
11069
|
+
* argument. Generates a UUID when missing and writes it back to the file.
|
|
11070
|
+
* Returns null when the file does not contain a `defineConfig()` call (e.g.
|
|
11071
|
+
* a wrapper that re-exports another config).
|
|
11072
|
+
* @param configPath - Absolute path to the config file
|
|
11073
|
+
* @returns Resolved id and whether it was newly injected, or null if skipped
|
|
11074
|
+
*/
|
|
11075
|
+
async function ensureConfigId(configPath) {
|
|
11076
|
+
const source = await fs$1.promises.readFile(configPath, "utf-8");
|
|
11077
|
+
const { program } = parseSync(configPath, source);
|
|
11078
|
+
const calls = [];
|
|
11079
|
+
findDefineConfigCalls(program, calls);
|
|
11080
|
+
if (calls.length === 0) return null;
|
|
11081
|
+
if (calls.length > 1) throw new Error(`Multiple defineConfig() calls found in ${configPath}. Only one is supported.`);
|
|
11082
|
+
const { configObj } = assertDefined(calls[0], "defineConfig call site missing");
|
|
11083
|
+
if (!configObj) throw new Error(`defineConfig() argument must be an inline object literal in ${configPath} so the SDK can manage the 'id' field.`);
|
|
11084
|
+
const idProp = findIdProperty(configObj);
|
|
11085
|
+
if (idProp) {
|
|
11086
|
+
const value = idProp.value;
|
|
11087
|
+
if (value.type !== "Literal") throw new Error(`'id' field in ${configPath} must be a string literal. To use this config for a separate app, delete it.`);
|
|
11088
|
+
const literalValue = value.value;
|
|
11089
|
+
if (typeof literalValue !== "string" || literalValue === "") throw new Error(`'id' field in ${configPath} must be a non-empty string literal. To use this config for a separate app, delete it.`);
|
|
11090
|
+
if (!uuidRegex.test(literalValue)) throw new Error(`'id' field in ${configPath} must be a UUID. To use this config for a separate app, delete it.`);
|
|
11091
|
+
return {
|
|
11092
|
+
id: literalValue,
|
|
11093
|
+
injected: false
|
|
11094
|
+
};
|
|
11095
|
+
}
|
|
11096
|
+
const id = crypto.randomUUID();
|
|
11097
|
+
const newSource = insertIdProperty(source, configObj, id);
|
|
11098
|
+
await fs$1.promises.writeFile(configPath, newSource, "utf-8");
|
|
11099
|
+
logger.info(`Generated app id and wrote to ${configPath}: ${id}`);
|
|
11100
|
+
return {
|
|
11101
|
+
id,
|
|
11102
|
+
injected: true
|
|
11103
|
+
};
|
|
11104
|
+
}
|
|
11105
|
+
/**
|
|
11106
|
+
* Read the resolved `defineConfig({...})` `id` from a config file without
|
|
11107
|
+
* mutating it. Returns null when the file has no inline `defineConfig()` call
|
|
11108
|
+
* (wrapper/re-export config), and `{ id: null }` when the call exists but has
|
|
11109
|
+
* no usable `id` property.
|
|
11110
|
+
* @param configPath - Absolute path to the config file
|
|
11111
|
+
* @returns The existing id (or null when absent), or null for wrapper configs
|
|
11112
|
+
*/
|
|
11113
|
+
async function readConfigId(configPath) {
|
|
11114
|
+
const { program } = parseSync(configPath, await fs$1.promises.readFile(configPath, "utf-8"));
|
|
11115
|
+
const calls = [];
|
|
11116
|
+
findDefineConfigCalls(program, calls);
|
|
11117
|
+
if (calls.length === 0) return null;
|
|
11118
|
+
if (calls.length > 1) throw new Error(`Multiple defineConfig() calls found in ${configPath}. Only one is supported.`);
|
|
11119
|
+
const { configObj } = assertDefined(calls[0], "defineConfig call site missing");
|
|
11120
|
+
if (!configObj) throw new Error(`defineConfig() argument must be an inline object literal in ${configPath} so the SDK can manage the 'id' field.`);
|
|
11121
|
+
const idProp = findIdProperty(configObj);
|
|
11122
|
+
if (!idProp || idProp.value.type !== "Literal") return { id: null };
|
|
11123
|
+
const value = idProp.value.value;
|
|
11124
|
+
return { id: typeof value === "string" && value !== "" ? value : null };
|
|
11125
|
+
}
|
|
11126
|
+
/**
|
|
11127
|
+
* Read-only CI check: the config must already carry a valid app id.
|
|
11128
|
+
* Wrapper/re-export configs (no inline defineConfig call) are exempt,
|
|
11129
|
+
* mirroring the local behavior where {@link ensureConfigId} no-ops.
|
|
11130
|
+
* @param configPath - Absolute path to the config file
|
|
11131
|
+
*/
|
|
11132
|
+
async function assertConfigIdInCI(configPath) {
|
|
11133
|
+
const result = await readConfigId(configPath);
|
|
11134
|
+
if (result === null) return;
|
|
11135
|
+
if (!result.id) throw new Error("tailor.config.ts is missing an 'id'. CI does not auto-generate one (each run would be treated as a separate app and break resource ownership). Run 'tailor-sdk setup' or 'tailor-sdk apply' locally and commit the injected id.");
|
|
11136
|
+
if (!uuidRegex.test(result.id)) throw new Error(`'id' in ${configPath} must be a UUID. To use this config for a separate app, delete it.`);
|
|
11137
|
+
}
|
|
11138
|
+
/**
|
|
11139
|
+
* Ensure the config has an app id for a deploy run.
|
|
11140
|
+
*
|
|
11141
|
+
* Locally, the id is auto-injected when missing (via {@link ensureConfigId}).
|
|
11142
|
+
* In CI, the id is never auto-injected — a missing id is a hard error, because
|
|
11143
|
+
* generating one per run would create a fresh app each time and break resource
|
|
11144
|
+
* ownership. CI dry-runs (plan) perform the same check read-only, so a
|
|
11145
|
+
* forgotten id fails at PR time instead of at deploy. Ephemeral pipelines that
|
|
11146
|
+
* intentionally deploy a fresh app per run (such as e2e harnesses) can opt
|
|
11147
|
+
* back into injection with `TAILOR_PLATFORM_SDK_ALLOW_CI_ID_INJECTION=true`.
|
|
11148
|
+
* Local dry-run and build-only flows skip both injection and the check (no
|
|
11149
|
+
* on-disk side effects are expected, and build-only never talks to the
|
|
11150
|
+
* platform).
|
|
11151
|
+
* @param obj - Inputs
|
|
11152
|
+
* @param obj.configPath - Absolute path to the config file
|
|
11153
|
+
* @param obj.dryRun - Whether this is a dry-run
|
|
11154
|
+
* @param obj.buildOnly - Whether this is a build-only run
|
|
11155
|
+
*/
|
|
11156
|
+
async function ensureConfigIdForDeploy(obj) {
|
|
11157
|
+
const { configPath, dryRun, buildOnly } = obj;
|
|
11158
|
+
if (buildOnly) return;
|
|
11159
|
+
const allowCIInjection = parseBoolean(process.env.TAILOR_PLATFORM_SDK_ALLOW_CI_ID_INJECTION) === true;
|
|
11160
|
+
const strictCI = isCI && !allowCIInjection;
|
|
11161
|
+
if (dryRun) {
|
|
11162
|
+
if (strictCI) await assertConfigIdInCI(configPath);
|
|
11163
|
+
return;
|
|
11164
|
+
}
|
|
11165
|
+
if (strictCI) {
|
|
11166
|
+
await assertConfigIdInCI(configPath);
|
|
11167
|
+
return;
|
|
11168
|
+
}
|
|
11169
|
+
await ensureConfigId(configPath);
|
|
11170
|
+
}
|
|
11171
|
+
const idComment = "// SDK-managed app id — do not edit, except when copying this config to a separate app.";
|
|
11172
|
+
function insertIdProperty(source, configObj, id) {
|
|
11173
|
+
const idLiteral = `id: ${JSON.stringify(id)}`;
|
|
11174
|
+
if (configObj.properties.length > 0) {
|
|
11175
|
+
const firstProp = assertDefined(configObj.properties[0], "first property missing");
|
|
11176
|
+
const lineStart = source.lastIndexOf("\n", firstProp.start - 1) + 1;
|
|
11177
|
+
const indent = source.slice(lineStart, firstProp.start);
|
|
11178
|
+
if (!/^[\t ]*$/.test(indent)) return source.slice(0, firstProp.start) + `${idLiteral}, ` + source.slice(firstProp.start);
|
|
11179
|
+
const insertion = `${idComment}\n${indent}${idLiteral},\n${indent}`;
|
|
11180
|
+
return source.slice(0, firstProp.start) + insertion + source.slice(firstProp.start);
|
|
11181
|
+
}
|
|
11182
|
+
const openBracePos = configObj.start + 1;
|
|
11183
|
+
const braceLineStart = source.lastIndexOf("\n", configObj.start) + 1;
|
|
11184
|
+
const baseIndent = source.slice(braceLineStart).match(/^[\t ]*/)?.[0] ?? "";
|
|
11185
|
+
const innerIndent = `${baseIndent} `;
|
|
11186
|
+
const insertion = `\n${innerIndent}${idComment}\n${innerIndent}${idLiteral},\n${baseIndent}`;
|
|
11187
|
+
return source.slice(0, openBracePos) + insertion + source.slice(openBracePos);
|
|
11188
|
+
}
|
|
11189
|
+
|
|
11190
|
+
//#endregion
|
|
11191
|
+
//#region src/cli/commands/deploy/confirm.ts
|
|
11192
|
+
/**
|
|
11193
|
+
* Confirm reassignment of resources when owner conflicts are detected.
|
|
11194
|
+
* Splits into two scenarios: id regeneration (same sdk-name, different
|
|
11195
|
+
* sdk-app-id) and name mismatch (different sdk-name). Each gets its own
|
|
11196
|
+
* prompt because the user-facing meaning is different.
|
|
11197
|
+
* @param conflicts - Detected owner conflicts
|
|
11198
|
+
* @param appName - Target application name
|
|
11199
|
+
* @param yes - Whether to auto-confirm without prompting
|
|
11200
|
+
* @returns Promise that resolves when confirmation completes
|
|
11201
|
+
*/
|
|
11202
|
+
async function confirmOwnerConflict(conflicts, appName, yes) {
|
|
11203
|
+
if (conflicts.length === 0) return;
|
|
11204
|
+
const idRegenerated = conflicts.filter((c) => c.currentOwner === appName);
|
|
11205
|
+
const nameMismatches = conflicts.filter((c) => c.currentOwner !== appName);
|
|
11206
|
+
if (idRegenerated.length > 0) await confirmIdRegeneration(idRegenerated, appName, yes);
|
|
11207
|
+
if (nameMismatches.length > 0) await confirmNameMismatch(nameMismatches, appName, yes);
|
|
11208
|
+
}
|
|
11209
|
+
async function confirmIdRegeneration(conflicts, appName, yes) {
|
|
11210
|
+
logger.warn(`Application id was regenerated for "${appName}":`);
|
|
11211
|
+
logger.log(" These resources still carry the previous id.");
|
|
11212
|
+
logger.newline();
|
|
11213
|
+
logger.log(` ${styles.info("Resources")}:`);
|
|
11214
|
+
for (const c of conflicts) logger.log(` • ${styles.bold(c.resourceType)} ${styles.info(`"${c.resourceName}"`)}`);
|
|
11215
|
+
if (yes) {
|
|
11216
|
+
logger.success("Re-tagging resources with the new id (--yes flag specified)...", { mode: "plain" });
|
|
11217
|
+
return;
|
|
11218
|
+
}
|
|
11219
|
+
if (!await prompt.confirm({
|
|
11220
|
+
message: `Re-tag these resources with the new id for "${appName}"?\n${styles.dim("(The id in tailor.config.ts was removed since the previous deploy, so a new one was generated)")}`,
|
|
11221
|
+
default: false
|
|
11222
|
+
})) throw new Error(multiline`
|
|
11223
|
+
Apply cancelled. Resources remain tagged with the previous id.
|
|
11224
|
+
To override, run again and confirm, or use --yes flag.
|
|
11225
|
+
`);
|
|
11226
|
+
}
|
|
11227
|
+
async function confirmNameMismatch(conflicts, appName, yes) {
|
|
11228
|
+
const currentOwners = [...new Set(conflicts.map((c) => c.currentOwner))];
|
|
11229
|
+
logger.warn("Application name mismatch detected:");
|
|
11230
|
+
logger.log(` ${styles.warning("Current application(s)")}: ${currentOwners.map((o) => styles.bold(`"${o}"`)).join(", ")}`);
|
|
11231
|
+
logger.log(` ${styles.success("New application")}: ${styles.bold(`"${appName}"`)}`);
|
|
11232
|
+
logger.newline();
|
|
11233
|
+
logger.log(` ${styles.info("Resources")}:`);
|
|
11234
|
+
for (const c of conflicts) logger.log(` • ${styles.bold(c.resourceType)} ${styles.info(`"${c.resourceName}"`)}`);
|
|
11235
|
+
if (yes) {
|
|
11236
|
+
logger.success("Updating resources (--yes flag specified)...", { mode: "plain" });
|
|
11237
|
+
return;
|
|
11238
|
+
}
|
|
11239
|
+
const promptMessage = currentOwners.length === 1 ? `Update these resources to be managed by "${appName}"?\n${styles.dim("(Common when renaming your application)")}` : `Update these resources to be managed by "${appName}"?`;
|
|
11240
|
+
if (!await prompt.confirm({
|
|
11241
|
+
message: promptMessage,
|
|
11242
|
+
default: false
|
|
11243
|
+
})) throw new Error(multiline`
|
|
11244
|
+
Apply cancelled. Resources remain managed by their current applications.
|
|
11245
|
+
To override, run again and confirm, or use --yes flag.
|
|
11246
|
+
`);
|
|
11247
|
+
}
|
|
11248
|
+
/**
|
|
11249
|
+
* Confirm allowing tailor-sdk to manage previously unmanaged resources.
|
|
11250
|
+
* @param resources - Unmanaged resources
|
|
11251
|
+
* @param appName - Target application name
|
|
11252
|
+
* @param yes - Whether to auto-confirm without prompting
|
|
11253
|
+
* @returns Promise that resolves when confirmation completes
|
|
11254
|
+
*/
|
|
11255
|
+
async function confirmUnmanagedResources(resources, appName, yes) {
|
|
11256
|
+
if (resources.length === 0) return;
|
|
11257
|
+
logger.warn("Existing resources not tracked by tailor-sdk were found:");
|
|
11258
|
+
logger.log(` ${styles.info("Resources")}:`);
|
|
11259
|
+
for (const r of resources) logger.log(` • ${styles.bold(r.resourceType)} ${styles.info(`"${r.resourceName}"`)}`);
|
|
11260
|
+
logger.newline();
|
|
11261
|
+
logger.log(" These resources may have been created by older SDK versions, Terraform, or CUE.");
|
|
11262
|
+
logger.log(" To continue, confirm that tailor-sdk should manage them.");
|
|
11263
|
+
logger.log(" If they are managed by another tool (e.g., Terraform), cancel and manage them there instead.");
|
|
11264
|
+
if (yes) {
|
|
11265
|
+
logger.success(`Adding to "${appName}" (--yes flag specified)...`, { mode: "plain" });
|
|
11266
|
+
return;
|
|
11267
|
+
}
|
|
11268
|
+
if (!await prompt.confirm({
|
|
11269
|
+
message: `Allow tailor-sdk to manage these resources for "${appName}"?`,
|
|
11270
|
+
default: false
|
|
11271
|
+
})) throw new Error(multiline`
|
|
11272
|
+
Apply cancelled. Resources remain unmanaged.
|
|
11273
|
+
To override, run again and confirm, or use --yes flag.
|
|
11274
|
+
`);
|
|
11275
|
+
}
|
|
11276
|
+
/**
|
|
11277
|
+
* Confirm deletion of important resources.
|
|
11278
|
+
* @param resources - Resources scheduled for deletion
|
|
11279
|
+
* @param yes - Whether to auto-confirm without prompting
|
|
11280
|
+
* @returns Promise that resolves when confirmation completes
|
|
11281
|
+
*/
|
|
11282
|
+
async function confirmImportantResourceDeletion(resources, yes) {
|
|
11283
|
+
if (resources.length === 0) return;
|
|
11284
|
+
logger.warn("The following resources will be deleted:");
|
|
11285
|
+
logger.log(` ${styles.info("Resources")}:`);
|
|
11286
|
+
for (const r of resources) logger.log(` • ${styles.bold(r.resourceType)} ${styles.error(`"${r.resourceName}"`)}`);
|
|
11287
|
+
logger.newline();
|
|
11288
|
+
logger.log(styles.warning(" Deleting these resources will permanently remove all associated data."));
|
|
11289
|
+
if (yes) {
|
|
11290
|
+
logger.success("Deleting resources (--yes flag specified)...", { mode: "plain" });
|
|
11291
|
+
return;
|
|
11292
|
+
}
|
|
11293
|
+
if (!await prompt.confirm({
|
|
11294
|
+
message: "Are you sure you want to delete these resources?",
|
|
11295
|
+
default: false
|
|
11296
|
+
})) throw new Error(multiline`
|
|
11297
|
+
Apply cancelled. Resources will not be deleted.
|
|
11298
|
+
To override, run again and confirm, or use --yes flag.
|
|
11299
|
+
`);
|
|
11106
11300
|
}
|
|
11107
11301
|
|
|
11108
11302
|
//#endregion
|
|
@@ -11336,17 +11530,126 @@ async function validatePlan(input) {
|
|
|
11336
11530
|
*/
|
|
11337
11531
|
function collectIdpUserTriggerTargets(application) {
|
|
11338
11532
|
const targets = /* @__PURE__ */ new Set();
|
|
11339
|
-
const idps = application
|
|
11533
|
+
const idps = collectApplicationIdpNames(application);
|
|
11340
11534
|
for (const executor of Object.values(application.executorService?.executors ?? {})) {
|
|
11341
11535
|
if (executor.trigger.kind !== "idpUser") continue;
|
|
11342
11536
|
if (executor.trigger.idp != null) targets.add(executor.trigger.idp);
|
|
11343
|
-
else if (idps.
|
|
11537
|
+
else if (idps.size === 1) {
|
|
11344
11538
|
const [idp] = idps;
|
|
11345
|
-
if (idp) targets.add(idp
|
|
11539
|
+
if (idp) targets.add(idp);
|
|
11346
11540
|
}
|
|
11347
11541
|
}
|
|
11348
11542
|
return targets;
|
|
11349
11543
|
}
|
|
11544
|
+
function collectDeployIdpUserTriggerTargets(targets) {
|
|
11545
|
+
const triggerTargets = /* @__PURE__ */ new Set();
|
|
11546
|
+
for (const target of targets) for (const idpName of collectIdpUserTriggerTargets(target.application)) triggerTargets.add(idpName);
|
|
11547
|
+
return triggerTargets;
|
|
11548
|
+
}
|
|
11549
|
+
function collectExecutorUsedTailorDBTypes(owner, targets) {
|
|
11550
|
+
const usedTypes = /* @__PURE__ */ new Set();
|
|
11551
|
+
const ownerNamespaces = new Set(owner.application.tailorDBServices.map((service) => service.namespace));
|
|
11552
|
+
const applications = targets.map((target) => target.application);
|
|
11553
|
+
for (const target of targets) {
|
|
11554
|
+
const visibleNamespaces = collectVisibleTailorDBTypeNamespaces(target.application, applications);
|
|
11555
|
+
for (const executor of Object.values(target.application.executorService?.executors ?? {})) if (executor.trigger.kind === "tailordb") {
|
|
11556
|
+
const namespace = findTailorDBNamespace(target.application, executor.trigger.typeName) ?? resolveSameRunNamespace(visibleNamespaces, executor.trigger.typeName, "TailorDB type");
|
|
11557
|
+
if (namespace && ownerNamespaces.has(namespace)) usedTypes.add(executor.trigger.typeName);
|
|
11558
|
+
}
|
|
11559
|
+
}
|
|
11560
|
+
return usedTypes;
|
|
11561
|
+
}
|
|
11562
|
+
function collectExecutorUsedResolvers(owner, targets) {
|
|
11563
|
+
const usedResolvers = /* @__PURE__ */ new Set();
|
|
11564
|
+
const ownerNamespaces = new Set(owner.application.resolverServices.map((service) => service.namespace));
|
|
11565
|
+
const applications = targets.map((target) => target.application);
|
|
11566
|
+
for (const target of targets) {
|
|
11567
|
+
const visibleNamespaces = collectVisibleResolverNamespaces(target.application, applications);
|
|
11568
|
+
for (const executor of Object.values(target.application.executorService?.executors ?? {})) if (executor.trigger.kind === "resolverExecuted") {
|
|
11569
|
+
const namespace = findResolverNamespace(target.application, executor.trigger.resolverName) ?? resolveSameRunNamespace(visibleNamespaces, executor.trigger.resolverName, "Resolver");
|
|
11570
|
+
if (namespace && ownerNamespaces.has(namespace)) usedResolvers.add(executor.trigger.resolverName);
|
|
11571
|
+
}
|
|
11572
|
+
}
|
|
11573
|
+
return usedResolvers;
|
|
11574
|
+
}
|
|
11575
|
+
function collectExpectedLocalStaticWebsiteNames(targets) {
|
|
11576
|
+
const websiteNames = /* @__PURE__ */ new Set();
|
|
11577
|
+
for (const target of targets) for (const website of target.application.staticWebsiteServices) websiteNames.add(website.name);
|
|
11578
|
+
return websiteNames;
|
|
11579
|
+
}
|
|
11580
|
+
function addPossiblyAmbiguousNamespace(namespaces, key, namespace) {
|
|
11581
|
+
if (namespaces.has(key)) {
|
|
11582
|
+
if (namespaces.get(key) !== namespace) namespaces.set(key, void 0);
|
|
11583
|
+
return;
|
|
11584
|
+
}
|
|
11585
|
+
namespaces.set(key, namespace);
|
|
11586
|
+
}
|
|
11587
|
+
function collectVisibleResources(params) {
|
|
11588
|
+
const { application, applications, visibleKeysOf, resourcesOf, createResult, addResource } = params;
|
|
11589
|
+
const visibleKeys = visibleKeysOf(application);
|
|
11590
|
+
const result = createResult();
|
|
11591
|
+
for (const candidate of applications) for (const resource of resourcesOf(candidate)) {
|
|
11592
|
+
if (!visibleKeys.has(resource.visibilityKey)) continue;
|
|
11593
|
+
addResource(result, resource.resourceKey, resource.visibilityKey);
|
|
11594
|
+
}
|
|
11595
|
+
return result;
|
|
11596
|
+
}
|
|
11597
|
+
function collectApplicationTailorDBNamespaces(application) {
|
|
11598
|
+
return /* @__PURE__ */ new Set([...application.tailorDBServices.map((service) => service.namespace), ...application.externalTailorDBNamespaces]);
|
|
11599
|
+
}
|
|
11600
|
+
function* tailorDBTypeResources(application) {
|
|
11601
|
+
for (const service of application.tailorDBServices) for (const typeName of Object.keys(service.types)) yield {
|
|
11602
|
+
visibilityKey: service.namespace,
|
|
11603
|
+
resourceKey: typeName
|
|
11604
|
+
};
|
|
11605
|
+
}
|
|
11606
|
+
function collectVisibleTailorDBTypeNamespaces(application, applications) {
|
|
11607
|
+
return collectVisibleResources({
|
|
11608
|
+
application,
|
|
11609
|
+
applications,
|
|
11610
|
+
visibleKeysOf: collectApplicationTailorDBNamespaces,
|
|
11611
|
+
resourcesOf: tailorDBTypeResources,
|
|
11612
|
+
createResult: () => /* @__PURE__ */ new Map(),
|
|
11613
|
+
addResource: addPossiblyAmbiguousNamespace
|
|
11614
|
+
});
|
|
11615
|
+
}
|
|
11616
|
+
function collectApplicationResolverNamespaces(application) {
|
|
11617
|
+
return new Set(application.subgraphs.filter((subgraph) => subgraph.Type === "pipeline").map((subgraph) => subgraph.Name));
|
|
11618
|
+
}
|
|
11619
|
+
function* idpNameResources(application) {
|
|
11620
|
+
for (const name of collectApplicationIdpNames(application)) yield {
|
|
11621
|
+
visibilityKey: name,
|
|
11622
|
+
resourceKey: name
|
|
11623
|
+
};
|
|
11624
|
+
}
|
|
11625
|
+
function collectVisibleIdpNames(application, applications) {
|
|
11626
|
+
return collectVisibleResources({
|
|
11627
|
+
application,
|
|
11628
|
+
applications,
|
|
11629
|
+
visibleKeysOf: collectApplicationIdpNames,
|
|
11630
|
+
resourcesOf: idpNameResources,
|
|
11631
|
+
createResult: () => /* @__PURE__ */ new Set(),
|
|
11632
|
+
addResource: (names, name) => {
|
|
11633
|
+
names.add(name);
|
|
11634
|
+
}
|
|
11635
|
+
});
|
|
11636
|
+
}
|
|
11637
|
+
function* resolverResources(application) {
|
|
11638
|
+
for (const service of application.resolverServices) for (const resolver of Object.values(service.resolvers)) yield {
|
|
11639
|
+
visibilityKey: service.namespace,
|
|
11640
|
+
resourceKey: resolver.name
|
|
11641
|
+
};
|
|
11642
|
+
}
|
|
11643
|
+
function collectVisibleResolverNamespaces(application, applications) {
|
|
11644
|
+
return collectVisibleResources({
|
|
11645
|
+
application,
|
|
11646
|
+
applications,
|
|
11647
|
+
visibleKeysOf: collectApplicationResolverNamespaces,
|
|
11648
|
+
resourcesOf: resolverResources,
|
|
11649
|
+
createResult: () => /* @__PURE__ */ new Map(),
|
|
11650
|
+
addResource: addPossiblyAmbiguousNamespace
|
|
11651
|
+
});
|
|
11652
|
+
}
|
|
11350
11653
|
async function shouldForceApplyAll(client, workspaceId, application, functionEntries) {
|
|
11351
11654
|
const desiredLabels = (await buildMetaRequest({
|
|
11352
11655
|
trn: resourceTrn(workspaceId, "application", application.name),
|
|
@@ -11395,27 +11698,19 @@ async function shouldForceApplyAll(client, workspaceId, application, functionEnt
|
|
|
11395
11698
|
}
|
|
11396
11699
|
/**
|
|
11397
11700
|
* Decide which renamed-away applications should be deleted. Excludes the
|
|
11398
|
-
*
|
|
11399
|
-
* by name would destroy
|
|
11701
|
+
* deploy targets themselves: id regeneration alone keeps the name unchanged,
|
|
11702
|
+
* so deleting by name would destroy a live app.
|
|
11400
11703
|
* @param params - Inputs for the computation
|
|
11401
11704
|
* @param params.conflicts - Detected owner conflicts across all services
|
|
11402
11705
|
* @param params.resourceOwners - App names that still own resources we don't manage
|
|
11403
|
-
* @param params.
|
|
11706
|
+
* @param params.protectedAppNames - App names that must not be deleted
|
|
11404
11707
|
* @returns Names of empty old applications that should be deleted
|
|
11405
11708
|
*/
|
|
11406
11709
|
function computeRenamedAppDeletions(params) {
|
|
11407
|
-
const { conflicts, resourceOwners,
|
|
11408
|
-
return [...new Set(conflicts.map((c) => c.currentOwner))].filter((owner) => !resourceOwners.has(owner) && owner
|
|
11710
|
+
const { conflicts, resourceOwners, protectedAppNames } = params;
|
|
11711
|
+
return [...new Set(conflicts.map((c) => c.currentOwner))].filter((owner) => !resourceOwners.has(owner) && !protectedAppNames.has(owner));
|
|
11409
11712
|
}
|
|
11410
|
-
|
|
11411
|
-
* Format and output the plan results, then return a summary of change counts.
|
|
11412
|
-
* In JSON dry-run mode a JSON payload is written to stdout. In all other modes
|
|
11413
|
-
* the human-readable diff goes to stdout (dry-run) or stderr (apply).
|
|
11414
|
-
* @param results - Planned results across all services
|
|
11415
|
-
* @param opts - Output options (dry-run mode flag)
|
|
11416
|
-
* @returns Aggregated plan summary counts
|
|
11417
|
-
*/
|
|
11418
|
-
function printPlanResults(results, opts) {
|
|
11713
|
+
function buildPlanReport(results) {
|
|
11419
11714
|
const executorEntries = formatExecutorChangeEntries(results.executor.changeSet, buildPlannedExecutorsByName(results.executor.changeSet), results.functionRegistry.executorFunctionChanges);
|
|
11420
11715
|
const resolverEntries = formatResolverChangeEntries(results.pipeline.changeSet.resolver, results.functionRegistry.resolverFunctionChanges);
|
|
11421
11716
|
const workflowEntries = formatWorkflowChangeEntries(results.workflow.changeSet, results.functionRegistry.workflowJobChanges);
|
|
@@ -11482,132 +11777,808 @@ function printPlanResults(results, opts) {
|
|
|
11482
11777
|
...results.workflow.conflicts,
|
|
11483
11778
|
...results.secretManager.conflicts
|
|
11484
11779
|
];
|
|
11485
|
-
|
|
11486
|
-
|
|
11487
|
-
|
|
11488
|
-
|
|
11489
|
-
|
|
11490
|
-
|
|
11491
|
-
|
|
11492
|
-
|
|
11493
|
-
|
|
11494
|
-
|
|
11495
|
-
|
|
11496
|
-
|
|
11497
|
-
|
|
11498
|
-
|
|
11499
|
-
|
|
11500
|
-
|
|
11501
|
-
|
|
11502
|
-
|
|
11503
|
-
|
|
11504
|
-
|
|
11505
|
-
|
|
11506
|
-
|
|
11507
|
-
|
|
11508
|
-
|
|
11509
|
-
|
|
11510
|
-
|
|
11511
|
-
|
|
11512
|
-
|
|
11513
|
-
|
|
11514
|
-
|
|
11515
|
-
|
|
11516
|
-
|
|
11517
|
-
|
|
11518
|
-
|
|
11519
|
-
|
|
11520
|
-
|
|
11521
|
-
|
|
11522
|
-
|
|
11523
|
-
|
|
11524
|
-
|
|
11525
|
-
|
|
11526
|
-
|
|
11527
|
-
|
|
11528
|
-
|
|
11529
|
-
|
|
11530
|
-
|
|
11531
|
-
|
|
11532
|
-
|
|
11533
|
-
|
|
11534
|
-
|
|
11535
|
-
|
|
11536
|
-
|
|
11537
|
-
|
|
11538
|
-
|
|
11539
|
-
|
|
11540
|
-
|
|
11541
|
-
|
|
11542
|
-
|
|
11543
|
-
|
|
11544
|
-
|
|
11545
|
-
|
|
11546
|
-
|
|
11547
|
-
|
|
11548
|
-
...buildGroupedDisplayLines(
|
|
11549
|
-
...
|
|
11550
|
-
...results.
|
|
11551
|
-
...results.
|
|
11552
|
-
|
|
11553
|
-
|
|
11554
|
-
|
|
11555
|
-
|
|
11556
|
-
|
|
11557
|
-
|
|
11558
|
-
|
|
11559
|
-
|
|
11560
|
-
|
|
11780
|
+
const changes = [
|
|
11781
|
+
...allDisplayEntries,
|
|
11782
|
+
...tailorDBServiceActions.map(({ action, name }) => ({
|
|
11783
|
+
action,
|
|
11784
|
+
name,
|
|
11785
|
+
labels: ["tailorDB"],
|
|
11786
|
+
namespace: void 0
|
|
11787
|
+
})),
|
|
11788
|
+
...pipelineServiceActions.map(({ action, name }) => ({
|
|
11789
|
+
action,
|
|
11790
|
+
name,
|
|
11791
|
+
labels: ["pipeline"],
|
|
11792
|
+
namespace: void 0
|
|
11793
|
+
})),
|
|
11794
|
+
...idpServiceActions.map(({ action, name }) => ({
|
|
11795
|
+
action,
|
|
11796
|
+
name,
|
|
11797
|
+
labels: ["idp"],
|
|
11798
|
+
namespace: void 0
|
|
11799
|
+
})),
|
|
11800
|
+
...authServiceActions.map(({ action, name }) => ({
|
|
11801
|
+
action,
|
|
11802
|
+
name,
|
|
11803
|
+
labels: ["auth"],
|
|
11804
|
+
namespace: void 0
|
|
11805
|
+
})),
|
|
11806
|
+
...formatChangeSetEntries(otherFunctionRegistryChanges),
|
|
11807
|
+
...formatChangeSetEntries(results.staticWebsite.changeSet, ["staticWebsite"]),
|
|
11808
|
+
...formatChangeSetEntries(results.staticWebsite.customDomainChangeSet, ["customDomain"]),
|
|
11809
|
+
...formatChangeSetEntries(results.aiGateway.changeSet, ["aiGateway"]),
|
|
11810
|
+
...formatChangeSetEntries(results.app, ["application"]),
|
|
11811
|
+
...formatChangeSetEntries(results.secretManager.vaultChangeSet, ["vault"]),
|
|
11812
|
+
...formatChangeSetEntries(results.secretManager.secretChangeSet, ["secret"])
|
|
11813
|
+
].map(({ action, name, labels, namespace }) => ({
|
|
11814
|
+
action,
|
|
11815
|
+
name,
|
|
11816
|
+
labels,
|
|
11817
|
+
namespace
|
|
11818
|
+
}));
|
|
11819
|
+
const warnings = [...allUnmanaged.map(({ resourceType, resourceName }) => ({
|
|
11820
|
+
type: "unmanaged",
|
|
11821
|
+
resourceType,
|
|
11822
|
+
name: resourceName
|
|
11823
|
+
})), ...results.secretManager.skippedSecrets.map((name) => ({
|
|
11824
|
+
type: "skippedSecret",
|
|
11825
|
+
resourceType: "secret",
|
|
11826
|
+
name
|
|
11827
|
+
}))];
|
|
11828
|
+
const conflicts = allConflicts.map(({ resourceType, resourceName, currentOwner }) => ({
|
|
11829
|
+
resourceType,
|
|
11830
|
+
name: resourceName,
|
|
11831
|
+
currentOwner
|
|
11832
|
+
}));
|
|
11833
|
+
const allLines = [
|
|
11834
|
+
...buildGroupedDisplayLines(results.functionRegistry.changeSet.title, formatChangeSetEntries(otherFunctionRegistryChanges)),
|
|
11835
|
+
...results.staticWebsite.changeSet.lines(),
|
|
11836
|
+
...results.staticWebsite.customDomainChangeSet.lines(),
|
|
11837
|
+
...results.aiGateway.changeSet.lines(),
|
|
11838
|
+
...results.app.lines(),
|
|
11839
|
+
...buildGroupedDisplayLines("TailorDB", tailorDBEntries, tailorDBServiceActions),
|
|
11840
|
+
...buildGroupedDisplayLines("Resolver", pipelineEntries, pipelineServiceActions),
|
|
11841
|
+
...buildGroupedDisplayLines("Executor", executorEntries),
|
|
11842
|
+
...buildGroupedDisplayLines("Workflow", workflowEntries),
|
|
11843
|
+
...buildGroupedDisplayLines("IdP", idpEntries, idpServiceActions),
|
|
11844
|
+
...buildGroupedDisplayLines("Auth", authEntries, authServiceActions),
|
|
11845
|
+
...results.secretManager.vaultChangeSet.lines(),
|
|
11846
|
+
...results.secretManager.secretChangeSet.lines()
|
|
11847
|
+
];
|
|
11848
|
+
if (allUnmanaged.length > 0) {
|
|
11849
|
+
allLines.push(styles.bold("Unmanaged resources (not in config):"));
|
|
11850
|
+
for (const { resourceType, resourceName } of allUnmanaged) allLines.push(` ${styles.warning("⚠")} ${styles.bold(resourceType)} "${resourceName}"`);
|
|
11851
|
+
}
|
|
11852
|
+
if (results.secretManager.skippedSecrets.length > 0) {
|
|
11853
|
+
allLines.push(styles.bold("Secret Manager secrets (skipped - no value provided):"));
|
|
11854
|
+
for (const name of results.secretManager.skippedSecrets) allLines.push(` ${styles.dim("○")} ${name}`);
|
|
11855
|
+
}
|
|
11856
|
+
if (allConflicts.length > 0) {
|
|
11857
|
+
allLines.push(styles.bold("Owner conflicts (will require confirmation on apply):"));
|
|
11858
|
+
for (const { resourceType, resourceName, currentOwner } of allConflicts) allLines.push(` ${styles.warning("!")} ${styles.bold(resourceType)} "${resourceName}" — owned by "${currentOwner}"`);
|
|
11859
|
+
}
|
|
11860
|
+
allLines.push(formatPlanSummary(summary));
|
|
11861
|
+
return {
|
|
11862
|
+
summary,
|
|
11863
|
+
json: {
|
|
11864
|
+
summary,
|
|
11865
|
+
changes,
|
|
11866
|
+
warnings,
|
|
11867
|
+
conflicts
|
|
11868
|
+
},
|
|
11869
|
+
lines: allLines
|
|
11870
|
+
};
|
|
11871
|
+
}
|
|
11872
|
+
/**
|
|
11873
|
+
* Format and output the plan results, then return a summary of change counts.
|
|
11874
|
+
* In JSON dry-run mode a JSON payload is written to stdout. In all other modes
|
|
11875
|
+
* the human-readable diff goes to stdout (dry-run) or stderr (apply).
|
|
11876
|
+
* @param results - Planned results across all services
|
|
11877
|
+
* @param opts - Output options (dry-run mode flag)
|
|
11878
|
+
* @returns Aggregated plan summary counts
|
|
11879
|
+
*/
|
|
11880
|
+
function printPlanResults(results, opts) {
|
|
11881
|
+
const report = buildPlanReport(results);
|
|
11882
|
+
if (logger.jsonMode && opts?.dryRun) {
|
|
11883
|
+
logger.out(report.json);
|
|
11884
|
+
return report.summary;
|
|
11885
|
+
}
|
|
11886
|
+
const output = report.lines.join("\n");
|
|
11887
|
+
if (opts?.dryRun) logger.out(output);
|
|
11888
|
+
else logger.log(output);
|
|
11889
|
+
return report.summary;
|
|
11890
|
+
}
|
|
11891
|
+
/**
|
|
11892
|
+
* Summarize plan counts from display entries, service actions, and non-grouped changesets.
|
|
11893
|
+
* @param results - Planned apply results
|
|
11894
|
+
* @param displayEntries - All grouped display entries across sections
|
|
11895
|
+
* @param serviceActions - All service-level namespace actions
|
|
11896
|
+
* @returns Aggregated plan summary
|
|
11897
|
+
*/
|
|
11898
|
+
function summarizePlanResults(results, displayEntries, serviceActions) {
|
|
11899
|
+
const summary = {
|
|
11900
|
+
create: 0,
|
|
11901
|
+
update: 0,
|
|
11902
|
+
delete: 0,
|
|
11903
|
+
replace: 0
|
|
11904
|
+
};
|
|
11905
|
+
for (const entry of displayEntries) summary[entry.action] += 1;
|
|
11906
|
+
for (const sa of serviceActions) summary[sa.action] += 1;
|
|
11907
|
+
const { otherChanges } = splitFunctionRegistryChanges(results.functionRegistry.changeSet);
|
|
11908
|
+
const nonGrouped = summarizeChangeSets([
|
|
11909
|
+
otherChanges,
|
|
11910
|
+
results.staticWebsite.changeSet,
|
|
11911
|
+
results.staticWebsite.customDomainChangeSet,
|
|
11912
|
+
results.aiGateway.changeSet,
|
|
11913
|
+
results.app,
|
|
11914
|
+
results.secretManager.vaultChangeSet,
|
|
11915
|
+
results.secretManager.secretChangeSet
|
|
11916
|
+
]);
|
|
11917
|
+
summary.create += nonGrouped.create;
|
|
11918
|
+
summary.update += nonGrouped.update;
|
|
11919
|
+
summary.delete += nonGrouped.delete;
|
|
11920
|
+
summary.replace += nonGrouped.replace;
|
|
11921
|
+
return summary;
|
|
11922
|
+
}
|
|
11923
|
+
/**
|
|
11924
|
+
* Parse the deploy config option into one or more config paths.
|
|
11925
|
+
* @param configPath - Raw `--config` option value
|
|
11926
|
+
* @returns Config paths, or one undefined entry to preserve default config lookup
|
|
11927
|
+
*/
|
|
11928
|
+
function parseDeployConfigPaths(configPath) {
|
|
11929
|
+
const rawConfigPath = configPath ?? process.env.TAILOR_PLATFORM_SDK_CONFIG_PATH;
|
|
11930
|
+
if (rawConfigPath === void 0) return [void 0];
|
|
11931
|
+
const configPaths = rawConfigPath.split(",").map((entry) => entry.trim());
|
|
11932
|
+
if (configPaths.some((entry) => entry.length === 0)) throw new Error("--config must contain one or more non-empty config paths.");
|
|
11933
|
+
return configPaths;
|
|
11934
|
+
}
|
|
11935
|
+
async function buildDeploymentTarget(params) {
|
|
11936
|
+
const { configPath, dryRun, buildOnly, noCache, packageVersion, cacheDir } = params;
|
|
11937
|
+
const { config, plugins } = await withSpan("build.loadConfig", async () => {
|
|
11938
|
+
const foundPath = loadConfigPath(configPath);
|
|
11939
|
+
if (foundPath) {
|
|
11940
|
+
const resolvedPath = path.resolve(process.cwd(), foundPath);
|
|
11941
|
+
if (fs$1.existsSync(resolvedPath)) await ensureConfigIdForDeploy({
|
|
11942
|
+
configPath: resolvedPath,
|
|
11943
|
+
dryRun,
|
|
11944
|
+
buildOnly
|
|
11945
|
+
});
|
|
11946
|
+
}
|
|
11947
|
+
return loadConfig(configPath);
|
|
11948
|
+
});
|
|
11949
|
+
const configDir = path.dirname(config.path);
|
|
11950
|
+
const lockfilePath = findUpSync("pnpm-lock.yaml", { cwd: configDir }) ?? findUpSync("package-lock.json", { cwd: configDir }) ?? findUpSync("yarn.lock", { cwd: configDir }) ?? findUpSync("bun.lock", { cwd: configDir });
|
|
11951
|
+
const cacheManager = createCacheManager({
|
|
11952
|
+
enabled: !noCache,
|
|
11953
|
+
cacheDir,
|
|
11954
|
+
sdkVersion: packageVersion,
|
|
11955
|
+
lockfileHash: lockfilePath ? hashFile(lockfilePath) : void 0
|
|
11956
|
+
});
|
|
11957
|
+
let pluginManager;
|
|
11958
|
+
if (plugins.length > 0) pluginManager = new PluginManager(plugins);
|
|
11959
|
+
await withSpan("build.generateUserTypes", () => generateUserTypes({
|
|
11960
|
+
config,
|
|
11961
|
+
configPath: config.path
|
|
11962
|
+
}));
|
|
11963
|
+
let application;
|
|
11964
|
+
let workflowBuildResult;
|
|
11965
|
+
let httpAdapterBuildResult;
|
|
11966
|
+
let bundledScripts;
|
|
11967
|
+
try {
|
|
11968
|
+
const result = await withSpan("build.loadApplication", () => loadApplication({
|
|
11969
|
+
config,
|
|
11970
|
+
pluginManager,
|
|
11971
|
+
bundleCache: cacheManager.bundleCache
|
|
11972
|
+
}));
|
|
11973
|
+
application = result.application;
|
|
11974
|
+
workflowBuildResult = result.workflowBuildResult;
|
|
11975
|
+
httpAdapterBuildResult = result.httpAdapterBuildResult;
|
|
11976
|
+
bundledScripts = result.bundledScripts;
|
|
11977
|
+
} finally {
|
|
11978
|
+
cacheManager.finalize();
|
|
11979
|
+
}
|
|
11980
|
+
return {
|
|
11981
|
+
config,
|
|
11982
|
+
application,
|
|
11983
|
+
workflowBuildResult,
|
|
11984
|
+
httpAdapterBuildResult,
|
|
11985
|
+
bundledScripts
|
|
11986
|
+
};
|
|
11987
|
+
}
|
|
11988
|
+
async function buildDeploymentTargets(params) {
|
|
11989
|
+
const { configPaths, buildTarget = buildDeploymentTarget, ...targetParams } = params;
|
|
11990
|
+
return Promise.all(configPaths.map((configPath) => buildTarget({
|
|
11991
|
+
...targetParams,
|
|
11992
|
+
configPath
|
|
11993
|
+
})));
|
|
11994
|
+
}
|
|
11995
|
+
function setBundledScript(target, name, code, kind) {
|
|
11996
|
+
if (target.has(name)) throw new Error(`Duplicate ${kind} bundle name "${name}" across config files.`);
|
|
11997
|
+
target.set(name, code);
|
|
11998
|
+
}
|
|
11999
|
+
function addBundledScripts(target, source, kind) {
|
|
12000
|
+
for (const [name, code] of source) setBundledScript(target, name, code, kind);
|
|
12001
|
+
}
|
|
12002
|
+
function collectDuplicateBundleNames(targets, select) {
|
|
12003
|
+
const seen = /* @__PURE__ */ new Set();
|
|
12004
|
+
const duplicates = /* @__PURE__ */ new Set();
|
|
12005
|
+
for (const target of targets) for (const name of select(target).keys()) {
|
|
12006
|
+
if (seen.has(name)) duplicates.add(name);
|
|
12007
|
+
seen.add(name);
|
|
12008
|
+
}
|
|
12009
|
+
return duplicates;
|
|
12010
|
+
}
|
|
12011
|
+
function addResolverBundledScripts(target, source, duplicateNames) {
|
|
12012
|
+
const consumedNames = /* @__PURE__ */ new Set();
|
|
12013
|
+
for (const service of source.application.resolverServices) for (const resolver of Object.values(service.resolvers)) {
|
|
12014
|
+
const code = source.bundledScripts.resolvers.get(resolver.name);
|
|
12015
|
+
if (code === void 0) continue;
|
|
12016
|
+
setBundledScript(target, duplicateNames.has(resolver.name) ? resolverFunctionName(service.namespace, resolver.name) : resolver.name, code, "resolver");
|
|
12017
|
+
consumedNames.add(resolver.name);
|
|
12018
|
+
}
|
|
12019
|
+
for (const [name, code] of source.bundledScripts.resolvers) if (!consumedNames.has(name)) setBundledScript(target, name, code, "resolver");
|
|
12020
|
+
}
|
|
12021
|
+
/**
|
|
12022
|
+
* Merge per-config bundled scripts into one build-only result.
|
|
12023
|
+
* @param targets - Built deployment targets to merge
|
|
12024
|
+
* @returns Combined bundled scripts across all targets
|
|
12025
|
+
*/
|
|
12026
|
+
function mergeBundledScripts(targets) {
|
|
12027
|
+
const bundledScripts = {
|
|
12028
|
+
resolvers: /* @__PURE__ */ new Map(),
|
|
12029
|
+
executors: /* @__PURE__ */ new Map(),
|
|
12030
|
+
workflowJobs: /* @__PURE__ */ new Map(),
|
|
12031
|
+
authHooks: /* @__PURE__ */ new Map()
|
|
12032
|
+
};
|
|
12033
|
+
const duplicateResolverNames = collectDuplicateBundleNames(targets, (target) => target.bundledScripts.resolvers);
|
|
12034
|
+
for (const target of targets) {
|
|
12035
|
+
addResolverBundledScripts(bundledScripts.resolvers, target, duplicateResolverNames);
|
|
12036
|
+
addBundledScripts(bundledScripts.executors, target.bundledScripts.executors, "executor");
|
|
12037
|
+
addBundledScripts(bundledScripts.workflowJobs, target.bundledScripts.workflowJobs, "workflow job");
|
|
12038
|
+
addBundledScripts(bundledScripts.authHooks, target.bundledScripts.authHooks, "auth hook");
|
|
12039
|
+
}
|
|
12040
|
+
return bundledScripts;
|
|
12041
|
+
}
|
|
12042
|
+
/**
|
|
12043
|
+
* Reject resource names that collide across configs. Each checked resource
|
|
12044
|
+
* type is workspace-global (its TRN is not qualified by namespace or app), so
|
|
12045
|
+
* a duplicate name would make two configs target the same platform resource:
|
|
12046
|
+
* one config's apply could overwrite the other's, or a fresh create could be
|
|
12047
|
+
* attempted twice. Resolver and auth-hook function names are namespace- or
|
|
12048
|
+
* app-qualified and are intentionally excluded.
|
|
12049
|
+
* @param targets - Built deployment targets to check
|
|
12050
|
+
*/
|
|
12051
|
+
function assertUniqueGlobalResourceNames(targets) {
|
|
12052
|
+
for (const check of DEPLOY_MANAGED_RESOURCE_DEFINITIONS) {
|
|
12053
|
+
const seen = /* @__PURE__ */ new Set();
|
|
12054
|
+
for (const target of targets) for (const name of check.namesOf(target)) {
|
|
12055
|
+
if (seen.has(name)) throw new Error(`Duplicate ${check.resourceLabel} name "${name}" across config files. ${check.resourceLabel} names must be unique across all configs in a single deploy.`);
|
|
12056
|
+
seen.add(name);
|
|
12057
|
+
}
|
|
12058
|
+
}
|
|
12059
|
+
}
|
|
12060
|
+
function collectPlannedExternalTailorDBServices(target, targets) {
|
|
12061
|
+
const externalNamespaces = new Set(target.application.externalTailorDBNamespaces);
|
|
12062
|
+
if (externalNamespaces.size === 0) return [];
|
|
12063
|
+
return targets.flatMap((candidate) => candidate.application.tailorDBServices.filter((service) => externalNamespaces.has(service.namespace)));
|
|
12064
|
+
}
|
|
12065
|
+
function collectExternalAuthIdpConfigNames(targets) {
|
|
12066
|
+
const idpConfigNames = /* @__PURE__ */ new Map();
|
|
12067
|
+
for (const target of targets) {
|
|
12068
|
+
const authService = target.application.authService;
|
|
12069
|
+
if (!authService) continue;
|
|
12070
|
+
const { name } = authService.config;
|
|
12071
|
+
const idpConfigName = authService.config.idProvider?.name;
|
|
12072
|
+
if (idpConfigNames.has(name) && idpConfigNames.get(name) !== idpConfigName) throw new Error(`Auth namespace "${name}" is defined by multiple config files with different IdP configs. Auth namespace names must be unique across all configs in a single deploy.`);
|
|
12073
|
+
idpConfigNames.set(name, idpConfigName);
|
|
12074
|
+
}
|
|
12075
|
+
return idpConfigNames;
|
|
12076
|
+
}
|
|
12077
|
+
function collectDeployRunPlanInputs(targets) {
|
|
12078
|
+
return {
|
|
12079
|
+
idpUserTriggerTargets: collectDeployIdpUserTriggerTargets(targets),
|
|
12080
|
+
expectedLocalStaticWebsiteNames: collectExpectedLocalStaticWebsiteNames(targets),
|
|
12081
|
+
externalAuthIdpConfigNames: collectExternalAuthIdpConfigNames(targets)
|
|
12082
|
+
};
|
|
12083
|
+
}
|
|
12084
|
+
async function planDeploymentTarget(params) {
|
|
12085
|
+
const { target, targets, runInputs, client, workspaceId, noSchemaCheck } = params;
|
|
12086
|
+
const { config, application, workflowBuildResult, httpAdapterBuildResult, bundledScripts } = target;
|
|
12087
|
+
await withSpan("plan.validateTailorDBTypeNames", () => assertUniqueTailorDBTypeNamesWithExternal({
|
|
12088
|
+
client,
|
|
12089
|
+
workspaceId,
|
|
12090
|
+
tailorDBServices: application.tailorDBServices,
|
|
12091
|
+
externalTailorDBNamespaces: application.externalTailorDBNamespaces,
|
|
12092
|
+
plannedExternalTailorDBServices: collectPlannedExternalTailorDBServices(target, targets)
|
|
12093
|
+
}));
|
|
12094
|
+
const workflowService = application.workflowService;
|
|
12095
|
+
const functionEntries = collectFunctionEntries(application, filterBundledWorkflowJobs(workflowService?.jobs ?? [], workflowBuildResult?.usedJobNames ?? []), bundledScripts);
|
|
12096
|
+
const forceApplyAll = await withSpan("plan.detectSdkVersionChange", () => shouldForceApplyAll(client, workspaceId, application, functionEntries));
|
|
12097
|
+
return withSpan("plan", async () => {
|
|
12098
|
+
const applications = targets.map((target) => target.application);
|
|
12099
|
+
const tailorDBTypeNamespaces = collectVisibleTailorDBTypeNamespaces(application, applications);
|
|
12100
|
+
const resolverNamespaces = collectVisibleResolverNamespaces(application, applications);
|
|
12101
|
+
const idpNames = collectVisibleIdpNames(application, applications);
|
|
12102
|
+
const ctx = {
|
|
12103
|
+
client,
|
|
12104
|
+
workspaceId,
|
|
12105
|
+
application,
|
|
12106
|
+
forRemoval: false,
|
|
12107
|
+
config,
|
|
12108
|
+
noSchemaCheck,
|
|
12109
|
+
forceApplyAll,
|
|
12110
|
+
...runInputs,
|
|
12111
|
+
executorUsedTailorDBTypes: collectExecutorUsedTailorDBTypes(target, targets),
|
|
12112
|
+
executorUsedResolvers: collectExecutorUsedResolvers(target, targets),
|
|
12113
|
+
tailorDBTypeNamespaces,
|
|
12114
|
+
resolverNamespaces,
|
|
12115
|
+
idpNames
|
|
12116
|
+
};
|
|
12117
|
+
const functionRegistry = await withSpan("plan.functionRegistry", () => planFunctionRegistry(client, workspaceId, application.name, application.id, functionEntries));
|
|
12118
|
+
const unchangedWorkflowJobs = new Set(functionRegistry.changeSet.unchanged.filter((entry) => entry.name.startsWith(WORKFLOW_PREFIX)).map((entry) => entry.name.slice(WORKFLOW_PREFIX.length)));
|
|
12119
|
+
const [tailorDB, staticWebsite, aiGateway, idp, auth, pipeline, app, executor, workflow, secretManager] = await Promise.all([
|
|
12120
|
+
withSpan("plan.tailorDB", () => planTailorDB(ctx)),
|
|
12121
|
+
withSpan("plan.staticWebsite", () => planStaticWebsite(ctx)),
|
|
12122
|
+
withSpan("plan.aiGateway", () => planAIGateway(ctx)),
|
|
12123
|
+
withSpan("plan.idp", () => planIdP(ctx)),
|
|
12124
|
+
withSpan("plan.auth", () => planAuth(ctx)),
|
|
12125
|
+
withSpan("plan.pipeline", () => planPipeline(ctx)),
|
|
12126
|
+
withSpan("plan.application", () => planApplication(ctx, httpAdapterBuildResult)),
|
|
12127
|
+
withSpan("plan.executor", () => planExecutor(ctx)),
|
|
12128
|
+
withSpan("plan.workflow", () => planWorkflow(client, workspaceId, application.name, application.id, workflowService?.workflows ?? {}, workflowBuildResult?.mainJobDeps ?? {}, unchangedWorkflowJobs)),
|
|
12129
|
+
withSpan("plan.secretManager", () => planSecretManager(ctx))
|
|
12130
|
+
]);
|
|
12131
|
+
return {
|
|
12132
|
+
application,
|
|
12133
|
+
functionRegistry,
|
|
12134
|
+
tailorDB,
|
|
12135
|
+
staticWebsite,
|
|
12136
|
+
aiGateway,
|
|
12137
|
+
idp,
|
|
12138
|
+
auth,
|
|
12139
|
+
pipeline,
|
|
12140
|
+
app,
|
|
12141
|
+
executor,
|
|
12142
|
+
workflow,
|
|
12143
|
+
secretManager
|
|
12144
|
+
};
|
|
12145
|
+
});
|
|
12146
|
+
}
|
|
12147
|
+
async function planDeploymentTargets(params) {
|
|
12148
|
+
const { targets, planTarget = planDeploymentTarget, ...planParams } = params;
|
|
12149
|
+
return Promise.all(targets.map((target) => planTarget({
|
|
12150
|
+
...planParams,
|
|
12151
|
+
target,
|
|
12152
|
+
targets
|
|
12153
|
+
})));
|
|
12154
|
+
}
|
|
12155
|
+
function deploymentPlanResults(deployment) {
|
|
12156
|
+
const { application: _application, ...results } = deployment;
|
|
12157
|
+
return results;
|
|
12158
|
+
}
|
|
12159
|
+
function collectOwnerConflicts(results) {
|
|
12160
|
+
return [
|
|
12161
|
+
...results.functionRegistry.conflicts,
|
|
12162
|
+
...results.tailorDB.conflicts,
|
|
12163
|
+
...results.staticWebsite.conflicts,
|
|
12164
|
+
...results.aiGateway.conflicts,
|
|
12165
|
+
...results.idp.conflicts,
|
|
12166
|
+
...results.auth.conflicts,
|
|
12167
|
+
...results.pipeline.conflicts,
|
|
12168
|
+
...results.executor.conflicts,
|
|
12169
|
+
...results.workflow.conflicts,
|
|
12170
|
+
...results.secretManager.conflicts
|
|
12171
|
+
];
|
|
12172
|
+
}
|
|
12173
|
+
function collectUnmanagedResources(results) {
|
|
12174
|
+
return [
|
|
12175
|
+
...results.functionRegistry.unmanaged,
|
|
12176
|
+
...results.tailorDB.unmanaged,
|
|
12177
|
+
...results.staticWebsite.unmanaged,
|
|
12178
|
+
...results.aiGateway.unmanaged,
|
|
12179
|
+
...results.idp.unmanaged,
|
|
12180
|
+
...results.auth.unmanaged,
|
|
12181
|
+
...results.pipeline.unmanaged,
|
|
12182
|
+
...results.executor.unmanaged,
|
|
12183
|
+
...results.workflow.unmanaged,
|
|
12184
|
+
...results.secretManager.unmanaged
|
|
11561
12185
|
];
|
|
11562
|
-
|
|
11563
|
-
|
|
11564
|
-
|
|
12186
|
+
}
|
|
12187
|
+
function collectResourceOwners(results) {
|
|
12188
|
+
return /* @__PURE__ */ new Set([
|
|
12189
|
+
...results.functionRegistry.resourceOwners,
|
|
12190
|
+
...results.tailorDB.resourceOwners,
|
|
12191
|
+
...results.staticWebsite.resourceOwners,
|
|
12192
|
+
...results.aiGateway.resourceOwners,
|
|
12193
|
+
...results.idp.resourceOwners,
|
|
12194
|
+
...results.auth.resourceOwners,
|
|
12195
|
+
...results.pipeline.resourceOwners,
|
|
12196
|
+
...results.executor.resourceOwners,
|
|
12197
|
+
...results.workflow.resourceOwners,
|
|
12198
|
+
...results.secretManager.resourceOwners
|
|
12199
|
+
]);
|
|
12200
|
+
}
|
|
12201
|
+
function collectImportantResourceDeletions(results) {
|
|
12202
|
+
const importantDeletions = [];
|
|
12203
|
+
for (const del of results.tailorDB.changeSet.type.deletes) importantDeletions.push({
|
|
12204
|
+
resourceType: "TailorDB type",
|
|
12205
|
+
resourceName: del.name
|
|
12206
|
+
});
|
|
12207
|
+
for (const del of results.staticWebsite.changeSet.deletes) importantDeletions.push({
|
|
12208
|
+
resourceType: "StaticWebsite",
|
|
12209
|
+
resourceName: del.name
|
|
12210
|
+
});
|
|
12211
|
+
for (const del of results.aiGateway.changeSet.deletes) importantDeletions.push({
|
|
12212
|
+
resourceType: "AIGateway",
|
|
12213
|
+
resourceName: del.name
|
|
12214
|
+
});
|
|
12215
|
+
for (const del of results.auth.changeSet.oauth2Client.deletes) importantDeletions.push({
|
|
12216
|
+
resourceType: "OAuth2 client",
|
|
12217
|
+
resourceName: del.name
|
|
12218
|
+
});
|
|
12219
|
+
for (const replace of results.auth.changeSet.oauth2Client.replaces) importantDeletions.push({
|
|
12220
|
+
resourceType: "OAuth2 client (client type change)",
|
|
12221
|
+
resourceName: replace.name
|
|
12222
|
+
});
|
|
12223
|
+
for (const del of results.auth.changeSet.connection.deletes) importantDeletions.push({
|
|
12224
|
+
resourceType: "Auth connection",
|
|
12225
|
+
resourceName: del.name
|
|
12226
|
+
});
|
|
12227
|
+
for (const del of results.secretManager.vaultChangeSet.deletes) importantDeletions.push({
|
|
12228
|
+
resourceType: "Secret Manager vault",
|
|
12229
|
+
resourceName: del.name
|
|
12230
|
+
});
|
|
12231
|
+
for (const del of results.secretManager.secretChangeSet.deletes) importantDeletions.push({
|
|
12232
|
+
resourceType: "Secret Manager secret",
|
|
12233
|
+
resourceName: del.name
|
|
12234
|
+
});
|
|
12235
|
+
return importantDeletions;
|
|
12236
|
+
}
|
|
12237
|
+
function readResourceField(item, field) {
|
|
12238
|
+
const itemRecord = item;
|
|
12239
|
+
for (const requestField of [
|
|
12240
|
+
"request",
|
|
12241
|
+
"deleteRequest",
|
|
12242
|
+
"createRequest"
|
|
12243
|
+
]) {
|
|
12244
|
+
const request = itemRecord[requestField];
|
|
12245
|
+
if (request && typeof request === "object" && field in request) {
|
|
12246
|
+
const value = request[field];
|
|
12247
|
+
return value == null ? void 0 : String(value);
|
|
12248
|
+
}
|
|
11565
12249
|
}
|
|
11566
|
-
|
|
11567
|
-
|
|
11568
|
-
|
|
12250
|
+
const value = itemRecord[field];
|
|
12251
|
+
return value == null ? void 0 : String(value);
|
|
12252
|
+
}
|
|
12253
|
+
function managedResourceName(group, item) {
|
|
12254
|
+
if (group.getName) return group.getName(item);
|
|
12255
|
+
return item.name;
|
|
12256
|
+
}
|
|
12257
|
+
function managedResourceKey(group, item) {
|
|
12258
|
+
const namespace = group.namespaceFields?.map((field) => readResourceField(item, field)).find((value) => value !== void 0);
|
|
12259
|
+
const name = managedResourceName(group, item);
|
|
12260
|
+
return namespace !== void 0 ? `${group.resourceType}:${namespace}:${name}` : `${group.resourceType}:${name}`;
|
|
12261
|
+
}
|
|
12262
|
+
function managedNamespaceOwnerKey(group, item) {
|
|
12263
|
+
if (!group.namespaceOwnerResourceType) return;
|
|
12264
|
+
const namespace = group.namespaceFields?.map((field) => readResourceField(item, field)).find((value) => value !== void 0);
|
|
12265
|
+
return namespace !== void 0 ? `${group.namespaceOwnerResourceType}:${namespace}` : void 0;
|
|
12266
|
+
}
|
|
12267
|
+
function addManagedResourceClaims(claims, group, item) {
|
|
12268
|
+
claims.add(managedResourceKey(group, item));
|
|
12269
|
+
const namespaceOwnerKey = managedNamespaceOwnerKey(group, item);
|
|
12270
|
+
if (namespaceOwnerKey) claims.add(namespaceOwnerKey);
|
|
12271
|
+
}
|
|
12272
|
+
function isManagedResourceClaimed(claims, group, item) {
|
|
12273
|
+
if (claims.has(managedResourceKey(group, item))) return true;
|
|
12274
|
+
const namespaceOwnerKey = managedNamespaceOwnerKey(group, item);
|
|
12275
|
+
return namespaceOwnerKey ? claims.has(namespaceOwnerKey) : false;
|
|
12276
|
+
}
|
|
12277
|
+
function retainDeletesNotClaimed(group, otherClaims) {
|
|
12278
|
+
let writeIndex = 0;
|
|
12279
|
+
for (const item of group.changeSet.deletes) {
|
|
12280
|
+
if (isManagedResourceClaimed(otherClaims, group, item)) {
|
|
12281
|
+
logger.debug(`Skipping delete of ${managedResourceKey(group, item)}: still managed by another config in this deploy.`);
|
|
12282
|
+
continue;
|
|
12283
|
+
}
|
|
12284
|
+
group.changeSet.deletes[writeIndex] = item;
|
|
12285
|
+
writeIndex += 1;
|
|
11569
12286
|
}
|
|
11570
|
-
|
|
11571
|
-
|
|
11572
|
-
|
|
12287
|
+
group.changeSet.deletes.length = writeIndex;
|
|
12288
|
+
}
|
|
12289
|
+
function workflowJobFunctionItems(items) {
|
|
12290
|
+
const jobNames = /* @__PURE__ */ new Set();
|
|
12291
|
+
for (const item of items) {
|
|
12292
|
+
const usedJobNames = item.usedJobNames;
|
|
12293
|
+
if (!Array.isArray(usedJobNames)) continue;
|
|
12294
|
+
for (const jobName of usedJobNames) if (typeof jobName === "string") jobNames.add(jobName);
|
|
11573
12295
|
}
|
|
11574
|
-
|
|
11575
|
-
const output = allLines.join("\n");
|
|
11576
|
-
if (opts?.dryRun) logger.out(output);
|
|
11577
|
-
else logger.log(output);
|
|
11578
|
-
return summary;
|
|
12296
|
+
return [...jobNames].map((name) => ({ name }));
|
|
11579
12297
|
}
|
|
11580
|
-
|
|
11581
|
-
|
|
11582
|
-
|
|
11583
|
-
|
|
11584
|
-
|
|
11585
|
-
|
|
11586
|
-
|
|
11587
|
-
|
|
11588
|
-
|
|
12298
|
+
function workflowJobFunctionResourceGroup(results) {
|
|
12299
|
+
return {
|
|
12300
|
+
changeSet: {
|
|
12301
|
+
creates: workflowJobFunctionItems(results.workflow.changeSet.creates),
|
|
12302
|
+
updates: workflowJobFunctionItems(results.workflow.changeSet.updates),
|
|
12303
|
+
replaces: workflowJobFunctionItems(results.workflow.changeSet.replaces),
|
|
12304
|
+
unchanged: [...results.workflow.unchangedWorkflowJobNames].map((name) => ({ name })),
|
|
12305
|
+
deletes: results.workflow.jobFunctionDeletes
|
|
12306
|
+
},
|
|
12307
|
+
resourceType: "workflow_job_function",
|
|
12308
|
+
getName: (item) => "jobFunctionName" in item ? item.jobFunctionName : item.name
|
|
12309
|
+
};
|
|
12310
|
+
}
|
|
12311
|
+
const MANAGED_RESOURCE_NAMESPACE_FIELDS = [
|
|
12312
|
+
"namespaceName",
|
|
12313
|
+
"authNamespace",
|
|
12314
|
+
"vaultName",
|
|
12315
|
+
"staticWebsiteName"
|
|
12316
|
+
];
|
|
12317
|
+
const DEPLOY_MANAGED_RESOURCE_DEFINITIONS = [
|
|
12318
|
+
{
|
|
12319
|
+
resourceLabel: "Application",
|
|
12320
|
+
resourceType: "application",
|
|
12321
|
+
namesOf: (target) => [target.application.name],
|
|
12322
|
+
changeSetOf: (results) => results.app
|
|
12323
|
+
},
|
|
12324
|
+
{
|
|
12325
|
+
resourceLabel: "Executor",
|
|
12326
|
+
resourceType: "executor",
|
|
12327
|
+
namesOf: (target) => Object.values(target.application.executorService?.executors ?? {}).map((executor) => executor.name),
|
|
12328
|
+
changeSetOf: (results) => results.executor.changeSet
|
|
12329
|
+
},
|
|
12330
|
+
{
|
|
12331
|
+
resourceLabel: "Workflow job",
|
|
12332
|
+
resourceType: "workflow_job_function",
|
|
12333
|
+
namesOf: (target) => target.bundledScripts.workflowJobs.keys(),
|
|
12334
|
+
changeSetOf: (results) => workflowJobFunctionResourceGroup(results).changeSet,
|
|
12335
|
+
getName: (item) => "jobFunctionName" in item ? item.jobFunctionName : item.name
|
|
12336
|
+
},
|
|
12337
|
+
{
|
|
12338
|
+
resourceLabel: "Workflow",
|
|
12339
|
+
resourceType: "workflow",
|
|
12340
|
+
namesOf: (target) => Object.values(target.application.workflowService?.workflows ?? {}).map((workflow) => workflow.name),
|
|
12341
|
+
changeSetOf: (results) => results.workflow.changeSet
|
|
12342
|
+
},
|
|
12343
|
+
{
|
|
12344
|
+
resourceLabel: "Auth connection",
|
|
12345
|
+
resourceType: "auth.connection",
|
|
12346
|
+
namespaceFields: MANAGED_RESOURCE_NAMESPACE_FIELDS,
|
|
12347
|
+
namespaceOwnerResourceType: "auth.service",
|
|
12348
|
+
namesOf: (target) => Object.keys(target.application.authService?.connections ?? {}),
|
|
12349
|
+
changeSetOf: (results) => results.auth.changeSet.connection
|
|
12350
|
+
},
|
|
12351
|
+
{
|
|
12352
|
+
resourceLabel: "StaticWebsite",
|
|
12353
|
+
resourceType: "staticwebsite",
|
|
12354
|
+
namesOf: (target) => target.application.staticWebsiteServices.map((service) => service.name),
|
|
12355
|
+
changeSetOf: (results) => results.staticWebsite.changeSet
|
|
12356
|
+
},
|
|
12357
|
+
{
|
|
12358
|
+
resourceLabel: "TailorDB namespace",
|
|
12359
|
+
resourceType: "tailordb.service",
|
|
12360
|
+
namesOf: (target) => target.application.tailorDBServices.map((service) => service.namespace),
|
|
12361
|
+
changeSetOf: (results) => results.tailorDB.changeSet.service
|
|
12362
|
+
},
|
|
12363
|
+
{
|
|
12364
|
+
resourceLabel: "Auth namespace",
|
|
12365
|
+
resourceType: "auth.service",
|
|
12366
|
+
namesOf: (target) => {
|
|
12367
|
+
const name = target.application.authService?.config.name;
|
|
12368
|
+
return name === void 0 ? [] : [name];
|
|
12369
|
+
},
|
|
12370
|
+
changeSetOf: (results) => results.auth.changeSet.service
|
|
12371
|
+
},
|
|
12372
|
+
{
|
|
12373
|
+
resourceLabel: "IdP namespace",
|
|
12374
|
+
resourceType: "idp.service",
|
|
12375
|
+
namesOf: (target) => target.application.idpServices.map((idp) => idp.name),
|
|
12376
|
+
changeSetOf: (results) => results.idp.changeSet.service
|
|
12377
|
+
},
|
|
12378
|
+
{
|
|
12379
|
+
resourceLabel: "Resolver namespace",
|
|
12380
|
+
resourceType: "pipeline.service",
|
|
12381
|
+
namesOf: (target) => target.application.resolverServices.map((service) => service.namespace),
|
|
12382
|
+
changeSetOf: (results) => results.pipeline.changeSet.service
|
|
12383
|
+
},
|
|
12384
|
+
{
|
|
12385
|
+
resourceLabel: "AIGateway",
|
|
12386
|
+
resourceType: "aigateway",
|
|
12387
|
+
namesOf: (target) => target.application.aiGatewayServices.map((service) => service.name),
|
|
12388
|
+
changeSetOf: (results) => results.aiGateway.changeSet
|
|
12389
|
+
},
|
|
12390
|
+
{
|
|
12391
|
+
resourceLabel: "Secret Manager vault",
|
|
12392
|
+
resourceType: "secret.vault",
|
|
12393
|
+
namesOf: (target) => target.application.secrets.map((vault) => vault.vaultName),
|
|
12394
|
+
changeSetOf: (results) => results.secretManager.vaultChangeSet
|
|
12395
|
+
}
|
|
12396
|
+
];
|
|
12397
|
+
function managedResourceGroupFromDefinition(definition, results) {
|
|
12398
|
+
const { changeSetOf, namesOf: _namesOf, resourceLabel: _resourceLabel, ...group } = definition;
|
|
12399
|
+
return {
|
|
12400
|
+
...group,
|
|
12401
|
+
changeSet: changeSetOf(results)
|
|
12402
|
+
};
|
|
12403
|
+
}
|
|
12404
|
+
function managedResourceGroups(results) {
|
|
12405
|
+
return [
|
|
12406
|
+
{
|
|
12407
|
+
changeSet: results.functionRegistry.changeSet,
|
|
12408
|
+
resourceType: "function_registry"
|
|
12409
|
+
},
|
|
12410
|
+
...DEPLOY_MANAGED_RESOURCE_DEFINITIONS.map((definition) => managedResourceGroupFromDefinition(definition, results)),
|
|
12411
|
+
{
|
|
12412
|
+
changeSet: results.tailorDB.changeSet.type,
|
|
12413
|
+
resourceType: "tailordb.type",
|
|
12414
|
+
namespaceFields: MANAGED_RESOURCE_NAMESPACE_FIELDS,
|
|
12415
|
+
namespaceOwnerResourceType: "tailordb.service"
|
|
12416
|
+
},
|
|
12417
|
+
{
|
|
12418
|
+
changeSet: results.tailorDB.changeSet.gqlPermission,
|
|
12419
|
+
resourceType: "tailordb.gql_permission",
|
|
12420
|
+
namespaceFields: MANAGED_RESOURCE_NAMESPACE_FIELDS,
|
|
12421
|
+
namespaceOwnerResourceType: "tailordb.service"
|
|
12422
|
+
},
|
|
12423
|
+
{
|
|
12424
|
+
changeSet: results.staticWebsite.customDomainChangeSet,
|
|
12425
|
+
resourceType: "staticwebsite.custom_domain",
|
|
12426
|
+
namespaceFields: MANAGED_RESOURCE_NAMESPACE_FIELDS,
|
|
12427
|
+
namespaceOwnerResourceType: "staticwebsite"
|
|
12428
|
+
},
|
|
12429
|
+
{
|
|
12430
|
+
changeSet: results.idp.changeSet.client,
|
|
12431
|
+
resourceType: "idp.client",
|
|
12432
|
+
namespaceFields: MANAGED_RESOURCE_NAMESPACE_FIELDS,
|
|
12433
|
+
namespaceOwnerResourceType: "idp.service"
|
|
12434
|
+
},
|
|
12435
|
+
{
|
|
12436
|
+
changeSet: results.auth.changeSet.idpConfig,
|
|
12437
|
+
resourceType: "auth.idp_config",
|
|
12438
|
+
namespaceFields: MANAGED_RESOURCE_NAMESPACE_FIELDS,
|
|
12439
|
+
namespaceOwnerResourceType: "auth.service"
|
|
12440
|
+
},
|
|
12441
|
+
{
|
|
12442
|
+
changeSet: results.auth.changeSet.userProfileConfig,
|
|
12443
|
+
resourceType: "auth.user_profile_config",
|
|
12444
|
+
namespaceFields: MANAGED_RESOURCE_NAMESPACE_FIELDS,
|
|
12445
|
+
namespaceOwnerResourceType: "auth.service"
|
|
12446
|
+
},
|
|
12447
|
+
{
|
|
12448
|
+
changeSet: results.auth.changeSet.tenantConfig,
|
|
12449
|
+
resourceType: "auth.tenant_config",
|
|
12450
|
+
namespaceFields: MANAGED_RESOURCE_NAMESPACE_FIELDS,
|
|
12451
|
+
namespaceOwnerResourceType: "auth.service"
|
|
12452
|
+
},
|
|
12453
|
+
{
|
|
12454
|
+
changeSet: results.auth.changeSet.machineUser,
|
|
12455
|
+
resourceType: "auth.machine_user",
|
|
12456
|
+
namespaceFields: MANAGED_RESOURCE_NAMESPACE_FIELDS,
|
|
12457
|
+
namespaceOwnerResourceType: "auth.service"
|
|
12458
|
+
},
|
|
12459
|
+
{
|
|
12460
|
+
changeSet: results.auth.changeSet.oauth2Client,
|
|
12461
|
+
resourceType: "auth.oauth2_client",
|
|
12462
|
+
namespaceFields: MANAGED_RESOURCE_NAMESPACE_FIELDS,
|
|
12463
|
+
namespaceOwnerResourceType: "auth.service"
|
|
12464
|
+
},
|
|
12465
|
+
{
|
|
12466
|
+
changeSet: results.auth.changeSet.authHook,
|
|
12467
|
+
resourceType: "auth.hook",
|
|
12468
|
+
namespaceFields: MANAGED_RESOURCE_NAMESPACE_FIELDS,
|
|
12469
|
+
namespaceOwnerResourceType: "auth.service"
|
|
12470
|
+
},
|
|
12471
|
+
{
|
|
12472
|
+
changeSet: results.auth.changeSet.scim,
|
|
12473
|
+
resourceType: "auth.scim",
|
|
12474
|
+
namespaceFields: MANAGED_RESOURCE_NAMESPACE_FIELDS,
|
|
12475
|
+
namespaceOwnerResourceType: "auth.service"
|
|
12476
|
+
},
|
|
12477
|
+
{
|
|
12478
|
+
changeSet: results.auth.changeSet.scimResource,
|
|
12479
|
+
resourceType: "auth.scim_resource",
|
|
12480
|
+
namespaceFields: MANAGED_RESOURCE_NAMESPACE_FIELDS,
|
|
12481
|
+
namespaceOwnerResourceType: "auth.service"
|
|
12482
|
+
},
|
|
12483
|
+
{
|
|
12484
|
+
changeSet: results.pipeline.changeSet.resolver,
|
|
12485
|
+
resourceType: "pipeline.resolver",
|
|
12486
|
+
namespaceFields: MANAGED_RESOURCE_NAMESPACE_FIELDS,
|
|
12487
|
+
namespaceOwnerResourceType: "pipeline.service"
|
|
12488
|
+
},
|
|
12489
|
+
{
|
|
12490
|
+
changeSet: results.secretManager.secretChangeSet,
|
|
12491
|
+
resourceType: "secret.secret",
|
|
12492
|
+
namespaceFields: MANAGED_RESOURCE_NAMESPACE_FIELDS,
|
|
12493
|
+
namespaceOwnerResourceType: "secret.vault"
|
|
12494
|
+
}
|
|
12495
|
+
];
|
|
12496
|
+
}
|
|
12497
|
+
function dropCrossDeploymentManagedDeletes(deployments) {
|
|
12498
|
+
const groupsByDeployment = deployments.map((deployment) => managedResourceGroups(deploymentPlanResults(deployment)));
|
|
12499
|
+
const claimsByDeployment = groupsByDeployment.map((groups) => groups.reduce((claims, group) => {
|
|
12500
|
+
for (const item of [
|
|
12501
|
+
...group.changeSet.creates,
|
|
12502
|
+
...group.changeSet.updates,
|
|
12503
|
+
...group.changeSet.replaces,
|
|
12504
|
+
...group.changeSet.unchanged
|
|
12505
|
+
]) addManagedResourceClaims(claims, group, item);
|
|
12506
|
+
return claims;
|
|
12507
|
+
}, /* @__PURE__ */ new Set()));
|
|
12508
|
+
groupsByDeployment.forEach((groups, deploymentIndex) => {
|
|
12509
|
+
const otherClaims = /* @__PURE__ */ new Set();
|
|
12510
|
+
claimsByDeployment.forEach((claims, claimIndex) => {
|
|
12511
|
+
if (claimIndex === deploymentIndex) return;
|
|
12512
|
+
for (const claim of claims) otherClaims.add(claim);
|
|
12513
|
+
});
|
|
12514
|
+
for (const group of groups) retainDeletesNotClaimed(group, otherClaims);
|
|
12515
|
+
});
|
|
12516
|
+
}
|
|
12517
|
+
function collectDeploymentResourceOwners(deployments) {
|
|
12518
|
+
const owners = /* @__PURE__ */ new Set();
|
|
12519
|
+
for (const deployment of deployments) for (const owner of collectResourceOwners(deploymentPlanResults(deployment))) owners.add(owner);
|
|
12520
|
+
return owners;
|
|
12521
|
+
}
|
|
12522
|
+
async function confirmDeploymentPlans(params) {
|
|
12523
|
+
const { deployments, yes } = params;
|
|
12524
|
+
const targetAppNames = new Set(deployments.map((deployment) => deployment.application.name));
|
|
12525
|
+
const resourceOwners = collectDeploymentResourceOwners(deployments);
|
|
12526
|
+
const scheduledRenamedAppDeletes = /* @__PURE__ */ new Set();
|
|
12527
|
+
const importantDeletions = [];
|
|
12528
|
+
for (const deployment of deployments) {
|
|
12529
|
+
const results = deploymentPlanResults(deployment);
|
|
12530
|
+
const conflicts = collectOwnerConflicts(results);
|
|
12531
|
+
await confirmOwnerConflict(conflicts, deployment.application.name, yes);
|
|
12532
|
+
await confirmUnmanagedResources(collectUnmanagedResources(results), deployment.application.name, yes);
|
|
12533
|
+
importantDeletions.push(...collectImportantResourceDeletions(results));
|
|
12534
|
+
const emptyApps = computeRenamedAppDeletions({
|
|
12535
|
+
conflicts,
|
|
12536
|
+
resourceOwners,
|
|
12537
|
+
protectedAppNames: targetAppNames
|
|
12538
|
+
});
|
|
12539
|
+
for (const emptyApp of emptyApps) {
|
|
12540
|
+
if (scheduledRenamedAppDeletes.has(emptyApp)) continue;
|
|
12541
|
+
scheduledRenamedAppDeletes.add(emptyApp);
|
|
12542
|
+
deployment.app.deletes.push({
|
|
12543
|
+
name: emptyApp,
|
|
12544
|
+
request: {
|
|
12545
|
+
workspaceId: deployment.tailorDB.context.workspaceId,
|
|
12546
|
+
applicationName: emptyApp
|
|
12547
|
+
}
|
|
12548
|
+
});
|
|
12549
|
+
}
|
|
12550
|
+
}
|
|
12551
|
+
await confirmImportantResourceDeletion(importantDeletions, yes);
|
|
12552
|
+
}
|
|
12553
|
+
function sumPlanSummaries(summaries) {
|
|
12554
|
+
return summaries.reduce((acc, summary) => ({
|
|
12555
|
+
create: acc.create + summary.create,
|
|
12556
|
+
update: acc.update + summary.update,
|
|
12557
|
+
delete: acc.delete + summary.delete,
|
|
12558
|
+
replace: acc.replace + summary.replace
|
|
12559
|
+
}), {
|
|
11589
12560
|
create: 0,
|
|
11590
12561
|
update: 0,
|
|
11591
12562
|
delete: 0,
|
|
11592
12563
|
replace: 0
|
|
11593
|
-
};
|
|
11594
|
-
|
|
11595
|
-
|
|
11596
|
-
|
|
11597
|
-
|
|
11598
|
-
|
|
11599
|
-
|
|
11600
|
-
|
|
11601
|
-
|
|
11602
|
-
|
|
11603
|
-
|
|
11604
|
-
|
|
11605
|
-
|
|
11606
|
-
|
|
11607
|
-
|
|
11608
|
-
|
|
11609
|
-
|
|
11610
|
-
|
|
12564
|
+
});
|
|
12565
|
+
}
|
|
12566
|
+
function printDeploymentPlans(deployments, opts) {
|
|
12567
|
+
if (logger.jsonMode && opts?.dryRun) {
|
|
12568
|
+
const reports = deployments.map((deployment) => buildPlanReport(deploymentPlanResults(deployment)));
|
|
12569
|
+
const summary = sumPlanSummaries(reports.map((report) => report.summary));
|
|
12570
|
+
logger.out({
|
|
12571
|
+
summary,
|
|
12572
|
+
changes: reports.flatMap((report) => report.json.changes),
|
|
12573
|
+
warnings: reports.flatMap((report) => report.json.warnings),
|
|
12574
|
+
conflicts: reports.flatMap((report) => report.json.conflicts)
|
|
12575
|
+
});
|
|
12576
|
+
return summary;
|
|
12577
|
+
}
|
|
12578
|
+
return sumPlanSummaries(deployments.map((deployment) => printPlanResults(deploymentPlanResults(deployment), opts)));
|
|
12579
|
+
}
|
|
12580
|
+
async function validateDeploymentPlans(deployments) {
|
|
12581
|
+
for (const deployment of deployments) await validatePlan(deploymentPlanResults(deployment));
|
|
11611
12582
|
}
|
|
11612
12583
|
/**
|
|
11613
12584
|
* Deploy the configured application to the Tailor platform.
|
|
@@ -11617,21 +12588,10 @@ function summarizePlanResults(results, displayEntries, serviceActions) {
|
|
|
11617
12588
|
async function deploy(options) {
|
|
11618
12589
|
return withSpan("deploy", async (rootSpan) => {
|
|
11619
12590
|
rootSpan.setAttribute("deploy.dry_run", options?.dryRun ?? false);
|
|
11620
|
-
const
|
|
12591
|
+
const configPaths = parseDeployConfigPaths(options?.configPath);
|
|
12592
|
+
const { targets, buildOnly } = await withSpan("build", async () => {
|
|
11621
12593
|
const dryRun = options?.dryRun ?? false;
|
|
11622
12594
|
const buildOnly = options?.buildOnly ?? parseBoolean(process.env.TAILOR_PLATFORM_SDK_BUILD_ONLY) === true;
|
|
11623
|
-
const { config, plugins } = await withSpan("build.loadConfig", async () => {
|
|
11624
|
-
const foundPath = loadConfigPath(options?.configPath);
|
|
11625
|
-
if (foundPath) {
|
|
11626
|
-
const resolvedPath = path.resolve(process.cwd(), foundPath);
|
|
11627
|
-
if (fs$1.existsSync(resolvedPath)) await ensureConfigIdForDeploy({
|
|
11628
|
-
configPath: resolvedPath,
|
|
11629
|
-
dryRun,
|
|
11630
|
-
buildOnly
|
|
11631
|
-
});
|
|
11632
|
-
}
|
|
11633
|
-
return loadConfig(options?.configPath);
|
|
11634
|
-
});
|
|
11635
12595
|
const noCache = options?.noCache ?? false;
|
|
11636
12596
|
const packageJson = await readPackageJson();
|
|
11637
12597
|
const cacheDir = path.resolve(getDistDir(), "cache");
|
|
@@ -11642,256 +12602,51 @@ async function deploy(options) {
|
|
|
11642
12602
|
});
|
|
11643
12603
|
logger.info("Bundle cache cleaned");
|
|
11644
12604
|
}
|
|
11645
|
-
const configDir = path.dirname(config.path);
|
|
11646
|
-
const lockfilePath = findUpSync("pnpm-lock.yaml", { cwd: configDir }) ?? findUpSync("package-lock.json", { cwd: configDir }) ?? findUpSync("yarn.lock", { cwd: configDir }) ?? findUpSync("bun.lock", { cwd: configDir });
|
|
11647
|
-
const cacheManager = createCacheManager({
|
|
11648
|
-
enabled: !noCache,
|
|
11649
|
-
cacheDir,
|
|
11650
|
-
sdkVersion: packageJson.version ?? "unknown",
|
|
11651
|
-
lockfileHash: lockfilePath ? hashFile(lockfilePath) : void 0
|
|
11652
|
-
});
|
|
11653
|
-
let pluginManager;
|
|
11654
|
-
if (plugins.length > 0) pluginManager = new PluginManager(plugins);
|
|
11655
|
-
await withSpan("build.generateUserTypes", () => generateUserTypes({
|
|
11656
|
-
config,
|
|
11657
|
-
configPath: config.path
|
|
11658
|
-
}));
|
|
11659
|
-
let application;
|
|
11660
|
-
let workflowBuildResult;
|
|
11661
|
-
let httpAdapterBuildResult;
|
|
11662
|
-
let bundledScripts;
|
|
11663
|
-
try {
|
|
11664
|
-
const result = await withSpan("build.loadApplication", () => loadApplication({
|
|
11665
|
-
config,
|
|
11666
|
-
pluginManager,
|
|
11667
|
-
bundleCache: cacheManager.bundleCache
|
|
11668
|
-
}));
|
|
11669
|
-
application = result.application;
|
|
11670
|
-
workflowBuildResult = result.workflowBuildResult;
|
|
11671
|
-
httpAdapterBuildResult = result.httpAdapterBuildResult;
|
|
11672
|
-
bundledScripts = result.bundledScripts;
|
|
11673
|
-
} finally {
|
|
11674
|
-
cacheManager.finalize();
|
|
11675
|
-
}
|
|
11676
12605
|
return {
|
|
11677
|
-
|
|
11678
|
-
|
|
11679
|
-
|
|
11680
|
-
|
|
11681
|
-
|
|
11682
|
-
|
|
11683
|
-
|
|
12606
|
+
targets: await buildDeploymentTargets({
|
|
12607
|
+
configPaths,
|
|
12608
|
+
dryRun,
|
|
12609
|
+
buildOnly,
|
|
12610
|
+
noCache,
|
|
12611
|
+
packageVersion: packageJson.version ?? "unknown",
|
|
12612
|
+
cacheDir
|
|
12613
|
+
}),
|
|
11684
12614
|
buildOnly
|
|
11685
12615
|
};
|
|
11686
12616
|
});
|
|
11687
|
-
if (buildOnly) return { bundledScripts };
|
|
12617
|
+
if (buildOnly) return { bundledScripts: mergeBundledScripts(targets) };
|
|
12618
|
+
assertUniqueGlobalResourceNames(targets);
|
|
11688
12619
|
const client = await initOperatorClient(await loadAccessToken({ profile: options?.profile }));
|
|
11689
12620
|
const workspaceId = await loadWorkspaceId({
|
|
11690
12621
|
workspaceId: options?.workspaceId,
|
|
11691
12622
|
profile: options?.profile
|
|
11692
12623
|
});
|
|
11693
|
-
rootSpan.setAttribute("app.name", application.name);
|
|
12624
|
+
rootSpan.setAttribute("app.name", targets.map((target) => target.application.name).join(","));
|
|
11694
12625
|
rootSpan.setAttribute("workspace.id", workspaceId);
|
|
11695
|
-
|
|
12626
|
+
const deployments = await planDeploymentTargets({
|
|
12627
|
+
targets,
|
|
12628
|
+
runInputs: collectDeployRunPlanInputs(targets),
|
|
11696
12629
|
client,
|
|
11697
12630
|
workspaceId,
|
|
11698
|
-
|
|
11699
|
-
|
|
11700
|
-
}));
|
|
11701
|
-
const workflowService = application.workflowService;
|
|
11702
|
-
const functionEntries = collectFunctionEntries(application, filterBundledWorkflowJobs(workflowService?.jobs ?? [], workflowBuildResult?.usedJobNames ?? []), bundledScripts);
|
|
12631
|
+
noSchemaCheck: options?.noSchemaCheck
|
|
12632
|
+
});
|
|
11703
12633
|
const dryRun = options?.dryRun ?? false;
|
|
11704
12634
|
const yes = options?.yes ?? false;
|
|
11705
|
-
|
|
11706
|
-
const { functionRegistry, tailorDB, staticWebsite, aiGateway, idp, auth, pipeline, app, executor, workflow, secretManager } = await withSpan("plan", async () => {
|
|
11707
|
-
const idpUserTriggerTargets = collectIdpUserTriggerTargets(application);
|
|
11708
|
-
const ctx = {
|
|
11709
|
-
client,
|
|
11710
|
-
workspaceId,
|
|
11711
|
-
application,
|
|
11712
|
-
forRemoval: false,
|
|
11713
|
-
config,
|
|
11714
|
-
noSchemaCheck: options?.noSchemaCheck,
|
|
11715
|
-
forceApplyAll,
|
|
11716
|
-
idpUserTriggerTargets
|
|
11717
|
-
};
|
|
11718
|
-
const functionRegistry = await withSpan("plan.functionRegistry", () => planFunctionRegistry(client, workspaceId, application.name, application.id, functionEntries));
|
|
11719
|
-
const unchangedWorkflowJobs = new Set(functionRegistry.changeSet.unchanged.filter((entry) => entry.name.startsWith(WORKFLOW_PREFIX)).map((entry) => entry.name.slice(WORKFLOW_PREFIX.length)));
|
|
11720
|
-
const [tailorDB, staticWebsite, aiGateway, idp, auth, pipeline, app, executor, workflow, secretManager] = await Promise.all([
|
|
11721
|
-
withSpan("plan.tailorDB", () => planTailorDB(ctx)),
|
|
11722
|
-
withSpan("plan.staticWebsite", () => planStaticWebsite(ctx)),
|
|
11723
|
-
withSpan("plan.aiGateway", () => planAIGateway(ctx)),
|
|
11724
|
-
withSpan("plan.idp", () => planIdP(ctx)),
|
|
11725
|
-
withSpan("plan.auth", () => planAuth(ctx)),
|
|
11726
|
-
withSpan("plan.pipeline", () => planPipeline(ctx)),
|
|
11727
|
-
withSpan("plan.application", () => planApplication(ctx, httpAdapterBuildResult)),
|
|
11728
|
-
withSpan("plan.executor", () => planExecutor(ctx)),
|
|
11729
|
-
withSpan("plan.workflow", () => planWorkflow(client, workspaceId, application.name, application.id, workflowService?.workflows ?? {}, workflowBuildResult?.mainJobDeps ?? {}, unchangedWorkflowJobs)),
|
|
11730
|
-
withSpan("plan.secretManager", () => planSecretManager(ctx))
|
|
11731
|
-
]);
|
|
11732
|
-
return {
|
|
11733
|
-
functionRegistry,
|
|
11734
|
-
tailorDB,
|
|
11735
|
-
staticWebsite,
|
|
11736
|
-
aiGateway,
|
|
11737
|
-
idp,
|
|
11738
|
-
auth,
|
|
11739
|
-
pipeline,
|
|
11740
|
-
app,
|
|
11741
|
-
executor,
|
|
11742
|
-
workflow,
|
|
11743
|
-
secretManager
|
|
11744
|
-
};
|
|
11745
|
-
});
|
|
12635
|
+
dropCrossDeploymentManagedDeletes(deployments);
|
|
11746
12636
|
await withSpan("confirm", async () => {
|
|
11747
|
-
|
|
11748
|
-
|
|
11749
|
-
|
|
11750
|
-
...staticWebsite.conflicts,
|
|
11751
|
-
...aiGateway.conflicts,
|
|
11752
|
-
...idp.conflicts,
|
|
11753
|
-
...auth.conflicts,
|
|
11754
|
-
...pipeline.conflicts,
|
|
11755
|
-
...executor.conflicts,
|
|
11756
|
-
...workflow.conflicts,
|
|
11757
|
-
...secretManager.conflicts
|
|
11758
|
-
];
|
|
11759
|
-
await confirmOwnerConflict(allConflicts, application.name, yes);
|
|
11760
|
-
await confirmUnmanagedResources([
|
|
11761
|
-
...functionRegistry.unmanaged,
|
|
11762
|
-
...tailorDB.unmanaged,
|
|
11763
|
-
...staticWebsite.unmanaged,
|
|
11764
|
-
...aiGateway.unmanaged,
|
|
11765
|
-
...idp.unmanaged,
|
|
11766
|
-
...auth.unmanaged,
|
|
11767
|
-
...pipeline.unmanaged,
|
|
11768
|
-
...executor.unmanaged,
|
|
11769
|
-
...workflow.unmanaged,
|
|
11770
|
-
...secretManager.unmanaged
|
|
11771
|
-
], application.name, yes);
|
|
11772
|
-
const importantDeletions = [];
|
|
11773
|
-
for (const del of tailorDB.changeSet.type.deletes) importantDeletions.push({
|
|
11774
|
-
resourceType: "TailorDB type",
|
|
11775
|
-
resourceName: del.name
|
|
11776
|
-
});
|
|
11777
|
-
for (const del of staticWebsite.changeSet.deletes) importantDeletions.push({
|
|
11778
|
-
resourceType: "StaticWebsite",
|
|
11779
|
-
resourceName: del.name
|
|
11780
|
-
});
|
|
11781
|
-
for (const del of aiGateway.changeSet.deletes) importantDeletions.push({
|
|
11782
|
-
resourceType: "AIGateway",
|
|
11783
|
-
resourceName: del.name
|
|
11784
|
-
});
|
|
11785
|
-
for (const del of auth.changeSet.oauth2Client.deletes) importantDeletions.push({
|
|
11786
|
-
resourceType: "OAuth2 client",
|
|
11787
|
-
resourceName: del.name
|
|
11788
|
-
});
|
|
11789
|
-
for (const replace of auth.changeSet.oauth2Client.replaces) importantDeletions.push({
|
|
11790
|
-
resourceType: "OAuth2 client (client type change)",
|
|
11791
|
-
resourceName: replace.name
|
|
11792
|
-
});
|
|
11793
|
-
for (const del of auth.changeSet.connection.deletes) importantDeletions.push({
|
|
11794
|
-
resourceType: "Auth connection",
|
|
11795
|
-
resourceName: del.name
|
|
11796
|
-
});
|
|
11797
|
-
for (const del of secretManager.vaultChangeSet.deletes) importantDeletions.push({
|
|
11798
|
-
resourceType: "Secret Manager vault",
|
|
11799
|
-
resourceName: del.name
|
|
11800
|
-
});
|
|
11801
|
-
for (const del of secretManager.secretChangeSet.deletes) importantDeletions.push({
|
|
11802
|
-
resourceType: "Secret Manager secret",
|
|
11803
|
-
resourceName: del.name
|
|
11804
|
-
});
|
|
11805
|
-
await confirmImportantResourceDeletion(importantDeletions, yes);
|
|
11806
|
-
const emptyApps = computeRenamedAppDeletions({
|
|
11807
|
-
conflicts: allConflicts,
|
|
11808
|
-
resourceOwners: /* @__PURE__ */ new Set([
|
|
11809
|
-
...functionRegistry.resourceOwners,
|
|
11810
|
-
...tailorDB.resourceOwners,
|
|
11811
|
-
...staticWebsite.resourceOwners,
|
|
11812
|
-
...aiGateway.resourceOwners,
|
|
11813
|
-
...idp.resourceOwners,
|
|
11814
|
-
...auth.resourceOwners,
|
|
11815
|
-
...pipeline.resourceOwners,
|
|
11816
|
-
...executor.resourceOwners,
|
|
11817
|
-
...workflow.resourceOwners,
|
|
11818
|
-
...secretManager.resourceOwners
|
|
11819
|
-
]),
|
|
11820
|
-
targetAppName: application.name
|
|
11821
|
-
});
|
|
11822
|
-
for (const emptyApp of emptyApps) app.deletes.push({
|
|
11823
|
-
name: emptyApp,
|
|
11824
|
-
request: {
|
|
11825
|
-
workspaceId,
|
|
11826
|
-
applicationName: emptyApp
|
|
11827
|
-
}
|
|
12637
|
+
await confirmDeploymentPlans({
|
|
12638
|
+
deployments,
|
|
12639
|
+
yes
|
|
11828
12640
|
});
|
|
11829
12641
|
});
|
|
11830
|
-
const planSummary =
|
|
11831
|
-
functionRegistry,
|
|
11832
|
-
tailorDB,
|
|
11833
|
-
staticWebsite,
|
|
11834
|
-
aiGateway,
|
|
11835
|
-
idp,
|
|
11836
|
-
auth,
|
|
11837
|
-
pipeline,
|
|
11838
|
-
app,
|
|
11839
|
-
executor,
|
|
11840
|
-
workflow,
|
|
11841
|
-
secretManager
|
|
11842
|
-
}, { dryRun: options?.dryRun });
|
|
12642
|
+
const planSummary = printDeploymentPlans(deployments, { dryRun: options?.dryRun });
|
|
11843
12643
|
if (options?.noValidate) logger.warn("Client-side validation skipped (--no-validate).");
|
|
11844
|
-
else await
|
|
11845
|
-
functionRegistry,
|
|
11846
|
-
tailorDB,
|
|
11847
|
-
staticWebsite,
|
|
11848
|
-
idp,
|
|
11849
|
-
auth,
|
|
11850
|
-
pipeline,
|
|
11851
|
-
app,
|
|
11852
|
-
executor,
|
|
11853
|
-
workflow,
|
|
11854
|
-
secretManager
|
|
11855
|
-
});
|
|
12644
|
+
else await validateDeploymentPlans(deployments);
|
|
11856
12645
|
if (dryRun) {
|
|
11857
12646
|
logger.info("Dry run enabled. No changes applied.");
|
|
11858
12647
|
return;
|
|
11859
12648
|
}
|
|
11860
|
-
await
|
|
11861
|
-
await applySecretManager(client, secretManager, "create-update", application);
|
|
11862
|
-
await applyFunctionRegistry(client, workspaceId, functionRegistry, "create-update");
|
|
11863
|
-
await applyStaticWebsite(client, staticWebsite, "create-update");
|
|
11864
|
-
await applyAIGateway(client, aiGateway, "create-update");
|
|
11865
|
-
await applyIdP(client, idp, "create-update");
|
|
11866
|
-
await applyAuth(client, auth, "create-update");
|
|
11867
|
-
await applyTailorDB(client, tailorDB, "create-update");
|
|
11868
|
-
await applyPipeline(client, pipeline, "create-update");
|
|
11869
|
-
});
|
|
11870
|
-
await withSpan("apply.deleteSubgraphResources", async () => {
|
|
11871
|
-
await applyPipeline(client, pipeline, "delete-resources");
|
|
11872
|
-
await applyAuth(client, auth, "delete-resources");
|
|
11873
|
-
await applyIdP(client, idp, "delete-resources");
|
|
11874
|
-
});
|
|
11875
|
-
await withSpan("apply.createUpdateApplication", () => applyApplication(client, app, "create-update"));
|
|
11876
|
-
await withSpan("apply.createUpdateDependentServices", async () => {
|
|
11877
|
-
await applyExecutor(client, executor, "create-update");
|
|
11878
|
-
await applyWorkflow(client, workflow, "create-update");
|
|
11879
|
-
});
|
|
11880
|
-
await withSpan("apply.deleteDependentServices", async () => {
|
|
11881
|
-
await applyWorkflow(client, workflow, "delete");
|
|
11882
|
-
await applyExecutor(client, executor, "delete");
|
|
11883
|
-
await applyStaticWebsite(client, staticWebsite, "delete");
|
|
11884
|
-
await applyAIGateway(client, aiGateway, "delete");
|
|
11885
|
-
await applySecretManager(client, secretManager, "delete");
|
|
11886
|
-
});
|
|
11887
|
-
await withSpan("apply.deleteApplication", () => applyApplication(client, app, "delete"));
|
|
11888
|
-
await withSpan("apply.deleteSubgraphServices", async () => {
|
|
11889
|
-
await applyPipeline(client, pipeline, "delete-services");
|
|
11890
|
-
await applyAuth(client, auth, "delete-services");
|
|
11891
|
-
await applyIdP(client, idp, "delete-services");
|
|
11892
|
-
await applyTailorDB(client, tailorDB, "delete-services");
|
|
11893
|
-
});
|
|
11894
|
-
await withSpan("apply.cleanup", () => applyFunctionRegistry(client, workspaceId, functionRegistry, "delete"));
|
|
12649
|
+
await applyDeploymentPlans(client, workspaceId, deployments);
|
|
11895
12650
|
if (logger.jsonMode) logger.out({
|
|
11896
12651
|
summary: planSummary,
|
|
11897
12652
|
status: "applied"
|
|
@@ -16031,10 +16786,12 @@ function createWorkspaceFolderNameResolver(client) {
|
|
|
16031
16786
|
};
|
|
16032
16787
|
}
|
|
16033
16788
|
async function workspaceInfoWithFolderName(client, workspace) {
|
|
16034
|
-
|
|
16789
|
+
const folderName = await resolveWorkspaceFolderName(client, workspace);
|
|
16790
|
+
return workspaceInfo(workspace, folderName);
|
|
16035
16791
|
}
|
|
16036
16792
|
async function workspaceDetailsWithFolderName(client, workspace) {
|
|
16037
|
-
|
|
16793
|
+
const folderName = await resolveWorkspaceFolderName(client, workspace);
|
|
16794
|
+
return workspaceDetails(workspace, folderName);
|
|
16038
16795
|
}
|
|
16039
16796
|
async function workspaceInfosWithFolderNames(client, workspaces) {
|
|
16040
16797
|
const resolveFolderName = createWorkspaceFolderNameResolver(client);
|
|
@@ -16705,7 +17462,7 @@ async function generate(options) {
|
|
|
16705
17462
|
if (options.init) await handleInitOption(namespacesWithMigrations, options.yes);
|
|
16706
17463
|
let pluginManager;
|
|
16707
17464
|
if (plugins.length > 0) pluginManager = new PluginManager(plugins);
|
|
16708
|
-
const { defineApplication } = await import("./application-
|
|
17465
|
+
const { defineApplication } = await import("./application-BndtMTFY.mjs");
|
|
16709
17466
|
const application = defineApplication({
|
|
16710
17467
|
config,
|
|
16711
17468
|
pluginManager
|
|
@@ -18635,7 +19392,7 @@ async function runRepl(options) {
|
|
|
18635
19392
|
const execute = await prepareQueryExecutor(options);
|
|
18636
19393
|
const historyPath = getReplHistoryPath(options.engine, options.profile, options.workspaceId);
|
|
18637
19394
|
const validate = createReplValidator(options.engine);
|
|
18638
|
-
const { highlightSqlLine, highlightGraphqlLine, replTransform } = await import("./repl-editor-
|
|
19395
|
+
const { highlightSqlLine, highlightGraphqlLine, replTransform } = await import("./repl-editor-BCozyiNq.mjs");
|
|
18639
19396
|
const highlight = options.engine === "sql" ? highlightSqlLine : highlightGraphqlLine;
|
|
18640
19397
|
const prompt = createPrompt({
|
|
18641
19398
|
prefix: "",
|
|
@@ -18969,5 +19726,5 @@ function isDeno() {
|
|
|
18969
19726
|
}
|
|
18970
19727
|
|
|
18971
19728
|
//#endregion
|
|
18972
|
-
export { getOrganization as $,
|
|
18973
|
-
//# sourceMappingURL=runtime-
|
|
19729
|
+
export { getOrganization as $, compareSnapshotWithRemote as $t, listCommand$3 as A, apiCommand as An, getExecutorWaitFailureMessage as At, show as B, pagedLogArgs as Bn, listWorkflowExecutions as Bt, listCommand$2 as C, hasChanges as Cn, listWebhookExecutors as Ct, waitWorkflowExecution as D, PluginManager as Dn, listCommand$9 as Dt, waitCommand as E, sdkNameLabelKey as En, triggerExecutor as Et, generateCommand as F, configArg as Fn, startWorkflow as Ft, removeCommand$1 as G, deploy as Gt, extractOwnedNamespaces as H, toPageDirection as Hn, formatKeyValueTable as Ht, generateMigrationScript as I, confirmationArgs as In, getCommand$5 as It, organizationTree as J, waitForExecution as Jt, updateCommand$1 as K, ensureConfigId as Kt, writeDbTypesFile as L, deploymentArgs as Ln, getWorkflow as Lt, truncate as M, assertWritable as Mn, listExecutorJobs as Mt, truncateCommand as N, defineAppCommand as Nn, watchExecutorJob as Nt, resumeCommand as O, generateUserTypes as On, listExecutors as Ot, generate as P, commonArgs as Pn, startCommand as Pt, getCommand$1 as Q, parseMigrationLabelNumber as Qt, getConfiguredEditorCommand as R, isVerbose as Rn, executionsCommand as Rt, listApps as S, formatMigrationDiff as Sn, getFunctionRegistry as St, healthCommand as T, resourceTrn as Tn, triggerCommand as Tt, logBetaWarning as U, workspaceArgs as Un, getCommand$6 as Ut, showCommand as V, paginationArgs as Vn, functionExecutionStatusToString as Vt, remove as W, getExecutor as Wt, listCommand$4 as X, MIGRATION_LABEL_KEY as Xt, treeCommand as Y, bundleMigrationScript as Yt, listOrganizations as Z, handleOptionalToRequiredError as Zt, getWorkspace as _, loadDiff as _n, listMachineUsers as _t, updateUser as a, MIGRATE_FILE_NAME as an, getFolder as at, createCommand as b, parseMigrationNumberArg as bn, listFunctionRegistries as bt, listCommand as c, compareLocalTypesWithSnapshot as cn, createCommand$1 as ct, inviteUser as d, getLatestMigrationNumber as dn, listOAuth2Clients as dt, generateAllTypeManifestsFromSnapshot as en, updateCommand$2 as et, restoreCommand as f, getMigrationDirPath as fn, getCommand$3 as ft, getCommand as g, isValidMigrationNumber as gn, listCommand$7 as gt, listWorkspaces as h, getNextMigrationNumber as hn, tokenCommand as ht, updateCommand as i, INITIAL_SCHEMA_NUMBER as in, getCommand$2 as it, listWorkflows as j, apiCall as jn, jobsCommand as jt, resumeWorkflow as k, prompt as kn, getExecutorJob as kt, listUsers as l, compareSnapshots as ln, createFolder as lt, listCommand$1 as m, getMigrationFiles as mn, getMachineUserToken as mt, query as n, DB_TYPES_FILE_NAME as nn, listCommand$5 as nt, removeCommand as o, SCHEMA_FILE_NAME as on, deleteCommand$1 as ot, restoreWorkspace as p, getMigrationFilePath as pn, getOAuth2Client as pt, updateOrganization as q, executeScript as qt, queryCommand as r, DIFF_FILE_NAME as rn, listFolders as rt, removeUser as s, assertValidMigrationFiles as sn, deleteFolder as st, isNativeTypeScriptRuntime as t, protoGqlPermission as tn, updateFolder as tt, inviteCommand as u, createSnapshotFromLocalTypes as un, listCommand$6 as ut, deleteCommand as v, reconstructSnapshotFromMigrations as vn, generate$1 as vt, getAppHealth as w, getNamespacesWithMigrations as wn, webhookCommand as wt, createWorkspace as x, formatDiffSummary as xn, getCommand$4 as xt, deleteWorkspace as y, formatMigrationNumber as yn, listCommand$8 as yt, openInConfiguredEditor as z, multiConfigArg as zn, getWorkflowExecution as zt };
|
|
19730
|
+
//# sourceMappingURL=runtime-Zkg3SUwi.mjs.map
|