@@ -32,6 +32,7 @@ import {
3232} from "./services/confirm" ;
3333import { applyExecutor , planExecutor } from "./services/executor" ;
3434import { applyIdP , planIdP } from "./services/idp" ;
35+ import { createChangeSet } from "./services/index" ;
3536import { applyPipeline , planPipeline } from "./services/resolver" ;
3637import { applyStaticWebsite , planStaticWebsite } from "./services/staticwebsite" ;
3738import { applyTailorDB , planTailorDB } from "./services/tailordb" ;
@@ -76,39 +77,18 @@ export async function apply(options?: ApplyOptions) {
7677 const yes = options ?. yes ?? false ;
7778 const buildOnly = options ?. buildOnly ?? process . env . TAILOR_PLATFORM_SDK_BUILD_ONLY === "true" ;
7879
80+ // TailorDB only mode: skip other services when migration version is specified
81+ const tailorDBOnlyMode = ! ! process . env . TAILOR_INTERNAL_APPLY_MIGRATION_VERSION ;
82+
7983 // Generate user types from loaded config
8084 await generateUserTypes ( config , config . path ) ;
8185 const application = defineApplication ( config ) ;
8286
83- // Load files first (before building)
84- // Load workflows first and collect jobs for bundling
85- let workflowResult : WorkflowLoadResult | undefined ;
86- if ( application . workflowConfig ) {
87- workflowResult = await loadAndCollectJobs ( application . workflowConfig ) ;
88- }
89-
90- // Build trigger context for workflow/job trigger transformation
91- const triggerContext = await buildTriggerContext ( application . workflowConfig ) ;
92-
93- // Build functions (using already loaded data)
94- for ( const app of application . applications ) {
95- for ( const pipeline of app . resolverServices ) {
96- await buildPipeline ( pipeline . namespace , pipeline . config , triggerContext ) ;
97- }
98- }
99- if ( application . executorService ) {
100- await buildExecutor ( application . executorService . config , triggerContext ) ;
101- }
102- let workflowBuildResult : BundleWorkflowJobsResult | undefined ;
103- if ( workflowResult && workflowResult . jobs . length > 0 ) {
104- const mainJobNames = workflowResult . workflowSources . map ( ( ws ) => ws . workflow . mainJob . name ) ;
105- workflowBuildResult = await buildWorkflow (
106- workflowResult . jobs ,
107- mainJobNames ,
108- application . env ,
109- triggerContext ,
110- ) ;
111- }
87+ // Build services (skipped in TailorDB only mode)
88+ const { workflowResult, workflowBuildResult } = await buildServices (
89+ application ,
90+ tailorDBOnlyMode ,
91+ ) ;
11292 if ( buildOnly ) return ;
11393
11494 // Initialize client
@@ -122,23 +102,15 @@ export async function apply(options?: ApplyOptions) {
122102 profile : options ?. profile ,
123103 } ) ;
124104
125- // Load remaining files and print logs
126- // Order: TailorDB → Resolver → Executor → Workflow
127- for ( const tailordb of application . tailorDBServices ) {
128- await tailordb . loadTypes ( ) ;
129- }
105+ // Load services (non-TailorDB services skipped in TailorDB only mode)
106+ await loadServices ( application , workflowResult , tailorDBOnlyMode ) ;
107+ logger . newline ( ) ;
130108
131- for ( const pipeline of application . resolverServices ) {
132- await pipeline . loadResolvers ( ) ;
133- }
134- if ( application . executorService ) {
135- await application . executorService . loadExecutors ( ) ;
109+ // Log TailorDB only mode
110+ if ( tailorDBOnlyMode ) {
111+ logger . info ( "TailorDB only mode: other services will be skipped" ) ;
112+ logger . newline ( ) ;
136113 }
137- // Print workflow loading logs last (workflows were already loaded for bundling)
138- if ( workflowResult ) {
139- printLoadedWorkflows ( workflowResult ) ;
140- }
141- logger . newline ( ) ;
142114
143115 // Phase 1: Plan
144116 const ctx : PlanContext = {
@@ -149,20 +121,16 @@ export async function apply(options?: ApplyOptions) {
149121 config,
150122 noSchemaCheck : options ?. noSchemaCheck ,
151123 } ;
152- const tailorDB = await planTailorDB ( ctx ) ;
153- const staticWebsite = await planStaticWebsite ( ctx ) ;
154- const idp = await planIdP ( ctx ) ;
155- const auth = await planAuth ( ctx ) ;
156- const pipeline = await planPipeline ( ctx ) ;
157- const app = await planApplication ( ctx ) ;
158- const executor = await planExecutor ( ctx ) ;
159- const workflow = await planWorkflow (
160- client ,
161- workspaceId ,
162- application . name ,
163- workflowResult ?. workflows ?? { } ,
164- workflowBuildResult ?. mainJobDeps ?? { } ,
165- ) ;
124+ const { tailorDB, staticWebsite, idp, auth, pipeline, app, executor, workflow } =
125+ await planServices (
126+ ctx ,
127+ client ,
128+ workspaceId ,
129+ application . name ,
130+ workflowResult ,
131+ workflowBuildResult ,
132+ tailorDBOnlyMode ,
133+ ) ;
166134
167135 // Confirm conflicts
168136 const allConflicts : OwnerConflict [ ] = [
@@ -238,6 +206,7 @@ export async function apply(options?: ApplyOptions) {
238206 await applyTailorDB ( client , tailorDB , "create-update" ) ;
239207
240208 // Other services: Apply after TailorDB migrations complete
209+ // (empty plans in TailorDB only mode - apply functions do nothing)
241210 await applyStaticWebsite ( client , staticWebsite , "create-update" ) ;
242211 await applyIdP ( client , idp , "create-update" ) ;
243212 await applyAuth ( client , auth , "create-update" ) ;
@@ -299,6 +268,204 @@ async function buildWorkflow(
299268 return bundleWorkflowJobs ( collectedJobs , mainJobNames , env , triggerContext ) ;
300269}
301270
271+ // ============================================================================
272+ // Service orchestration functions
273+ // ============================================================================
274+
275+ interface BuildServicesResult {
276+ workflowResult : WorkflowLoadResult | undefined ;
277+ workflowBuildResult : BundleWorkflowJobsResult | undefined ;
278+ }
279+
280+ async function buildServices (
281+ application : Readonly < Application > ,
282+ tailorDBOnly : boolean ,
283+ ) : Promise < BuildServicesResult > {
284+ if ( tailorDBOnly ) {
285+ return { workflowResult : undefined , workflowBuildResult : undefined } ;
286+ }
287+
288+ let workflowResult : WorkflowLoadResult | undefined ;
289+ if ( application . workflowConfig ) {
290+ workflowResult = await loadAndCollectJobs ( application . workflowConfig ) ;
291+ }
292+
293+ const triggerContext = await buildTriggerContext ( application . workflowConfig ) ;
294+
295+ for ( const app of application . applications ) {
296+ for ( const pipeline of app . resolverServices ) {
297+ await buildPipeline ( pipeline . namespace , pipeline . config , triggerContext ) ;
298+ }
299+ }
300+ if ( application . executorService ) {
301+ await buildExecutor ( application . executorService . config , triggerContext ) ;
302+ }
303+
304+ let workflowBuildResult : BundleWorkflowJobsResult | undefined ;
305+ if ( workflowResult && workflowResult . jobs . length > 0 ) {
306+ const mainJobNames = workflowResult . workflowSources . map ( ( ws ) => ws . workflow . mainJob . name ) ;
307+ workflowBuildResult = await buildWorkflow (
308+ workflowResult . jobs ,
309+ mainJobNames ,
310+ application . env ,
311+ triggerContext ,
312+ ) ;
313+ }
314+
315+ return { workflowResult, workflowBuildResult } ;
316+ }
317+
318+ async function loadServices (
319+ application : Readonly < Application > ,
320+ workflowResult : WorkflowLoadResult | undefined ,
321+ tailorDBOnly : boolean ,
322+ ) {
323+ // Always load TailorDB types
324+ for ( const tailordb of application . tailorDBServices ) {
325+ await tailordb . loadTypes ( ) ;
326+ }
327+
328+ if ( tailorDBOnly ) {
329+ return ;
330+ }
331+
332+ // Load other services
333+ for ( const pipeline of application . resolverServices ) {
334+ await pipeline . loadResolvers ( ) ;
335+ }
336+ if ( application . executorService ) {
337+ await application . executorService . loadExecutors ( ) ;
338+ }
339+ if ( workflowResult ) {
340+ printLoadedWorkflows ( workflowResult ) ;
341+ }
342+ }
343+
344+ // Empty plan helpers - using type assertions since the plans are empty and won't be used
345+ type StaticWebsitePlanResult = Awaited < ReturnType < typeof planStaticWebsite > > ;
346+ type IdPPlanResult = Awaited < ReturnType < typeof planIdP > > ;
347+ type AuthPlanResult = Awaited < ReturnType < typeof planAuth > > ;
348+ type PipelinePlanResult = Awaited < ReturnType < typeof planPipeline > > ;
349+ type ApplicationPlanResult = Awaited < ReturnType < typeof planApplication > > ;
350+ type ExecutorPlanResult = Awaited < ReturnType < typeof planExecutor > > ;
351+ type WorkflowPlanResult = Awaited < ReturnType < typeof planWorkflow > > ;
352+
353+ function emptyStaticWebsitePlan ( ) : StaticWebsitePlanResult {
354+ return {
355+ changeSet : createChangeSet ( "StaticWebsites" ) ,
356+ conflicts : [ ] ,
357+ unmanaged : [ ] ,
358+ resourceOwners : new Set < string > ( ) ,
359+ } as unknown as StaticWebsitePlanResult ;
360+ }
361+
362+ function emptyIdPPlan ( ) : IdPPlanResult {
363+ return {
364+ changeSet : {
365+ service : createChangeSet ( "IdP services" ) ,
366+ client : createChangeSet ( "IdP clients" ) ,
367+ } ,
368+ conflicts : [ ] ,
369+ unmanaged : [ ] ,
370+ resourceOwners : new Set < string > ( ) ,
371+ } as unknown as IdPPlanResult ;
372+ }
373+
374+ function emptyAuthPlan ( ) : AuthPlanResult {
375+ return {
376+ changeSet : {
377+ service : createChangeSet ( "Auth services" ) ,
378+ idpConfig : createChangeSet ( "IdP configs" ) ,
379+ oauth2Client : createChangeSet ( "OAuth2 clients" ) ,
380+ machineUser : createChangeSet ( "Machine users" ) ,
381+ userProfileConfig : createChangeSet ( "User profile configs" ) ,
382+ tenantConfig : createChangeSet ( "Tenant configs" ) ,
383+ scim : createChangeSet ( "SCIM configs" ) ,
384+ scimResource : createChangeSet ( "SCIM resources" ) ,
385+ } ,
386+ conflicts : [ ] ,
387+ unmanaged : [ ] ,
388+ resourceOwners : new Set < string > ( ) ,
389+ } as unknown as AuthPlanResult ;
390+ }
391+
392+ function emptyPipelinePlan ( ) : PipelinePlanResult {
393+ return {
394+ changeSet : {
395+ service : createChangeSet ( "Pipeline services" ) ,
396+ resolver : createChangeSet ( "Resolvers" ) ,
397+ } ,
398+ conflicts : [ ] ,
399+ unmanaged : [ ] ,
400+ resourceOwners : new Set < string > ( ) ,
401+ } as unknown as PipelinePlanResult ;
402+ }
403+
404+ function emptyApplicationPlan ( ) : ApplicationPlanResult {
405+ return createChangeSet ( "Applications" ) as unknown as ApplicationPlanResult ;
406+ }
407+
408+ function emptyExecutorPlan ( ) : ExecutorPlanResult {
409+ return {
410+ changeSet : createChangeSet ( "Executors" ) ,
411+ conflicts : [ ] ,
412+ unmanaged : [ ] ,
413+ resourceOwners : new Set < string > ( ) ,
414+ } as unknown as ExecutorPlanResult ;
415+ }
416+
417+ function emptyWorkflowPlan ( ) : WorkflowPlanResult {
418+ return {
419+ changeSet : createChangeSet ( "Workflows" ) ,
420+ conflicts : [ ] ,
421+ unmanaged : [ ] ,
422+ resourceOwners : new Set < string > ( ) ,
423+ appName : "" ,
424+ } as unknown as WorkflowPlanResult ;
425+ }
426+
427+ async function planServices (
428+ ctx : PlanContext ,
429+ client : OperatorClient ,
430+ workspaceId : string ,
431+ appName : string ,
432+ workflowResult : WorkflowLoadResult | undefined ,
433+ workflowBuildResult : BundleWorkflowJobsResult | undefined ,
434+ tailorDBOnly : boolean ,
435+ ) {
436+ const tailorDB = await planTailorDB ( ctx ) ;
437+
438+ if ( tailorDBOnly ) {
439+ return {
440+ tailorDB,
441+ staticWebsite : emptyStaticWebsitePlan ( ) ,
442+ idp : emptyIdPPlan ( ) ,
443+ auth : emptyAuthPlan ( ) ,
444+ pipeline : emptyPipelinePlan ( ) ,
445+ app : emptyApplicationPlan ( ) ,
446+ executor : emptyExecutorPlan ( ) ,
447+ workflow : emptyWorkflowPlan ( ) ,
448+ } ;
449+ }
450+
451+ return {
452+ tailorDB,
453+ staticWebsite : await planStaticWebsite ( ctx ) ,
454+ idp : await planIdP ( ctx ) ,
455+ auth : await planAuth ( ctx ) ,
456+ pipeline : await planPipeline ( ctx ) ,
457+ app : await planApplication ( ctx ) ,
458+ executor : await planExecutor ( ctx ) ,
459+ workflow : await planWorkflow (
460+ client ,
461+ workspaceId ,
462+ appName ,
463+ workflowResult ?. workflows ?? { } ,
464+ workflowBuildResult ?. mainJobDeps ?? { } ,
465+ ) ,
466+ } ;
467+ }
468+
302469export const applyCommand = defineCommand ( {
303470 name : "apply" ,
304471 description : "Apply Tailor configuration to deploy your application." ,
0 commit comments