Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 10 additions & 5 deletions packages/sdk/src/cli/commands/apply/application.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {
} from "@tailor-proto/tailor/v1/application_resource_pb";
import { fetchAll, resolveStaticWebsiteUrls, type OperatorClient } from "@/cli/shared/client";
import { createChangeSet } from "./change-set";
import { areNormalizedEqual } from "./compare";
import { areNormalizedEqual, collectDiffLines } from "./compare";
import { buildMetaRequest } from "./label";
import type { ApplyPhase, PlanContext } from "@/cli/commands/apply/apply";
import type { Application } from "@/cli/services/application";
Expand Down Expand Up @@ -73,6 +73,7 @@ type CreateApplication = {

type UpdateApplication = {
name: string;
details?: string[];
request: MessageInitShape<typeof UpdateApplicationRequestSchema>;
metaRequest: MessageInitShape<typeof SetMetadataRequestSchema>;
};
Expand All @@ -86,7 +87,10 @@ type ComparableApplication = {
authNamespace: string;
authIdpConfigName: string;
cors: string[];
subgraphs: Array<{ serviceType: Subgraph_ServiceType; serviceNamespace: string }>;
subgraphs: Array<{
serviceType: Subgraph_ServiceType;
serviceNamespace: string;
}>;
allowedIpAddresses: string[];
disableIntrospection: boolean;
disabled: boolean;
Expand Down Expand Up @@ -209,14 +213,14 @@ export async function planApplication(context: PlanContext) {
},
});
}
changeSet.print();
changeSet.print({ detail: context.detailPlan });
return changeSet;
}

// Skip application create/update when there are no subgraphs
// (e.g. deploying only static web hosting)
if (application.subgraphs.length === 0) {
changeSet.print();
changeSet.print({ detail: context.detailPlan });
return changeSet;
}

Expand Down Expand Up @@ -285,6 +289,7 @@ export async function planApplication(context: PlanContext) {
} else {
changeSet.updates.push({
name: application.name,
details: collectDiffLines(normalizeComparableExistingApplication(existing), desired),
request,
metaRequest,
});
Expand All @@ -297,7 +302,7 @@ export async function planApplication(context: PlanContext) {
});
}

changeSet.print();
changeSet.print({ detail: context.detailPlan });
return changeSet;
}

Expand Down
12 changes: 11 additions & 1 deletion packages/sdk/src/cli/commands/apply/apply.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ export interface ApplyOptions {
noSchemaCheck?: boolean;
noCache?: boolean;
cleanCache?: boolean;
detailPlan?: boolean;
// NOTE(remiposo): Provide an option to run build-only for testing purposes.
// This could potentially be exposed as a CLI option.
buildOnly?: boolean;
Expand All @@ -61,6 +62,7 @@ export interface PlanContext {
forRemoval: boolean;
config: LoadedConfig;
noSchemaCheck?: boolean;
detailPlan?: boolean;
}

export type ApplyPhase = "create-update" | "delete" | "delete-resources" | "delete-services";
Expand Down Expand Up @@ -242,9 +244,16 @@ export async function apply(options?: ApplyOptions) {
forRemoval: false,
config,
noSchemaCheck: options?.noSchemaCheck,
detailPlan: options?.detailPlan,
};
const functionRegistry = await withSpan("plan.functionRegistry", () =>
planFunctionRegistry(client, workspaceId, application.name, functionEntries),
planFunctionRegistry(
client,
workspaceId,
application.name,
functionEntries,
options?.detailPlan,
),
);
const unchangedWorkflowJobs = new Set(
functionRegistry.changeSet.unchanged
Expand All @@ -269,6 +278,7 @@ export async function apply(options?: ApplyOptions) {
workflowService?.workflows ?? {},
workflowBuildResult?.mainJobDeps ?? {},
unchangedWorkflowJobs,
options?.detailPlan,
),
),
withSpan("plan.secretManager", () => planSecretManager(ctx)),
Expand Down
79 changes: 66 additions & 13 deletions packages/sdk/src/cli/commands/apply/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,12 @@ import { type AuthService } from "@/cli/services/auth/service";
import { fetchAll, resolveStaticWebsiteUrls, type OperatorClient } from "@/cli/shared/client";
import { OAuth2ClientSchema } from "@/parser/service/auth";
import { createChangeSet } from "./change-set";
import { areNormalizedEqual, normalizeProtoConfig, normalizeStringArray } from "./compare";
import {
areNormalizedEqual,
collectDiffLines,
normalizeProtoConfig,
normalizeStringArray,
} from "./compare";
import { authHookFunctionName } from "./function-registry";
import { idpClientSecretName, idpClientVaultName } from "./idp";
import { buildMetaRequest, sdkNameLabelKey, type WithLabel } from "./label";
Expand Down Expand Up @@ -289,15 +294,15 @@ export async function planAuth(context: PlanContext) {
planSCIMResources(client, workspaceId, auths, deletedServices),
]);

serviceChangeSet.print();
idpConfigChangeSet.print();
userProfileConfigChangeSet.print();
tenantConfigChangeSet.print();
machineUserChangeSet.print();
authHookChangeSet.print();
oauth2ClientChangeSet.print();
scimChangeSet.print();
scimResourceChangeSet.print();
serviceChangeSet.print({ detail: context.detailPlan });
idpConfigChangeSet.print({ detail: context.detailPlan });
userProfileConfigChangeSet.print({ detail: context.detailPlan });
tenantConfigChangeSet.print({ detail: context.detailPlan });
machineUserChangeSet.print({ detail: context.detailPlan });
authHookChangeSet.print({ detail: context.detailPlan });
oauth2ClientChangeSet.print({ detail: context.detailPlan });
scimChangeSet.print({ detail: context.detailPlan });
scimResourceChangeSet.print({ detail: context.detailPlan });
return {
changeSet: {
service: serviceChangeSet,
Expand All @@ -324,6 +329,7 @@ type CreateService = {

type UpdateService = {
name: string;
details?: string[];
request: MessageInitShape<typeof UpdateAuthServiceRequestSchema>;
metaRequest: MessageInitShape<typeof SetMetadataRequestSchema>;
};
Expand Down Expand Up @@ -411,6 +417,14 @@ async function planServices(
} else {
changeSet.updates.push({
name: config.name,
details: collectDiffLines(
{
publishSessionEvents: existing.resource.publishSessionEvents,
},
{
publishSessionEvents: config.publishSessionEvents ?? false,
},
),
request,
metaRequest,
});
Expand Down Expand Up @@ -452,6 +466,7 @@ type CreateIdPConfig = {

type UpdateIdPConfig = {
name: string;
details?: string[];
idpConfig: Readonly<IdProviderConfig>;
request: MessageInitShape<typeof UpdateAuthIDPConfigRequestSchema>;
};
Expand Down Expand Up @@ -521,11 +536,43 @@ async function planIdPConfigs(
existingMap.delete(idpConfig.name);
continue;
}
const existingComparable = normalizeProtoConfig({
name: existing.name,
authType: existing.authType,
config:
existing.config?.config.case === "oidc"
? {
config: {
case: "oidc",
value: {
...existing.config.config.value,
issuerUrl: existing.config.config.value.issuerUrl || undefined,
},
},
}
: existing.config,
});
const desiredComparableNormalized = normalizeProtoConfig({
...desiredComparable,
config:
desiredComparable.config?.config?.case === "oidc"
? {
config: {
case: "oidc",
value: {
...desiredComparable.config.config.value,
issuerUrl: desiredComparable.config.config.value.issuerUrl || undefined,
},
},
}
: desiredComparable.config,
});
if (areAuthIdPConfigsEqual(existing, desiredComparable)) {
changeSet.unchanged.push({ name: idpConfig.name });
} else {
changeSet.updates.push({
name: idpConfig.name,
details: collectDiffLines(existingComparable, desiredComparableNormalized),
idpConfig,
request: {
workspaceId,
Expand Down Expand Up @@ -1166,7 +1213,9 @@ function normalizeComparableUserProfileConfig(
| MessageInitShape<typeof UserProfileProviderConfigSchema>
| {
providerType?: UserProfileProviderConfig_UserProfileProviderType;
config?: { config?: { case?: string; value?: Record<string, unknown> } };
config?: {
config?: { case?: string; value?: Record<string, unknown> };
};
},
) {
const comparableConfig = config.config?.config;
Expand Down Expand Up @@ -1211,7 +1260,9 @@ function normalizeComparableTenantProviderConfig(
| undefined
| {
providerType?: TenantProviderConfig_TenantProviderType;
config?: { config?: { case?: string; value?: Record<string, unknown> } };
config?: {
config?: { case?: string; value?: Record<string, unknown> };
};
},
) {
return normalizeProtoConfig(config);
Expand All @@ -1223,7 +1274,9 @@ function areTenantProviderConfigsEqual(
| undefined
| {
providerType?: TenantProviderConfig_TenantProviderType;
config?: { config?: { case?: string; value?: Record<string, unknown> } };
config?: {
config?: { case?: string; value?: Record<string, unknown> };
};
},
desired: MessageInitShape<typeof TenantProviderConfigSchema>,
) {
Expand Down
34 changes: 33 additions & 1 deletion packages/sdk/src/cli/commands/apply/change-set.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,23 @@
import { describe, expect, test } from "vitest";
import { describe, expect, test, vi } from "vitest";
import { createChangeSet, formatPlanSummary, summarizeChangeSets } from "./change-set";
import type { HasName } from "./change-set";

vi.mock("@/cli/shared/logger", () => ({
logger: {
log: vi.fn(),
info: vi.fn(),
},
styles: {
bold: (value: string) => value,
},
symbols: {
create: "+",
update: "~",
delete: "-",
replace: "+/-",
},
}));

function createNamedChangeSet(title: string) {
return createChangeSet<HasName, HasName, HasName, HasName>(title);
}
Expand Down Expand Up @@ -58,3 +74,19 @@ describe("formatPlanSummary", () => {
).toBe("Plan: 1 to create, 2 to update, 0 to delete, 3 to replace, 15 unchanged");
});
});

describe("ChangeSet.print", () => {
test("prints detail lines only when detail output is enabled", async () => {
const { logger } = await import("@/cli/shared/logger");
const changeSet = createNamedChangeSet("Applications");
changeSet.updates.push({ name: "app-a", details: ['cors[0]: remote="a" local="b"'] });

changeSet.print();
expect(logger.info).not.toHaveBeenCalled();

changeSet.print({ detail: true });
expect(logger.info).toHaveBeenCalledWith(' cors[0]: remote="a" local="b"', {
mode: "plain",
});
});
});
30 changes: 24 additions & 6 deletions packages/sdk/src/cli/commands/apply/change-set.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { logger, styles, symbols } from "@/cli/shared/logger";

export interface HasName {
name: string;
details?: string[];
}

export type ChangeSet<
Expand All @@ -17,7 +18,7 @@ export type ChangeSet<
readonly replaces: R[];
readonly unchanged: HasName[];
isEmpty: () => boolean;
print: () => void;
print: (options?: { detail?: boolean }) => void;
};

export interface PlanSummary {
Expand Down Expand Up @@ -56,15 +57,32 @@ export function createChangeSet<
replaces,
unchanged,
isEmpty,
print: () => {
print: (options) => {
if (isEmpty()) {
return;
}
logger.log(styles.bold(`${title}:`));
creates.forEach((item) => logger.log(` ${symbols.create} ${item.name}`));
deletes.forEach((item) => logger.log(` ${symbols.delete} ${item.name}`));
updates.forEach((item) => logger.log(` ${symbols.update} ${item.name}`));
replaces.forEach((item) => logger.log(` ${symbols.replace} ${item.name}`));
const printItem = (symbol: string, item: HasName, fallback: string[]) => {
logger.log(` ${symbol} ${item.name}`);
if (!options?.detail) {
return;
}
for (const detail of item.details ?? fallback) {
logger.info(` ${detail}`, { mode: "plain" });
}
};
creates.forEach((item) =>
printItem(symbols.create, item, ["resource: remote=missing local=present"]),
);
deletes.forEach((item) =>
printItem(symbols.delete, item, ["resource: remote=present local=missing"]),
);
updates.forEach((item) =>
printItem(symbols.update, item, ["resource: remote and local differ"]),
);
replaces.forEach((item) =>
printItem(symbols.replace, item, ["resource: replacement required"]),
);
},
};
}
Expand Down
Loading
Loading