@tailor-platform/sdk 2.0.0-next.2 → 2.0.0-next.4
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 +511 -10
- package/README.md +9 -9
- package/agent-skills/{tailor-sdk → tailor}/SKILL.md +3 -1
- package/dist/aigateway-DR4Kwuik.mjs +9 -0
- package/dist/aigateway-DR4Kwuik.mjs.map +1 -0
- package/dist/application-BO4ZF8wP.mjs +3 -0
- package/dist/application-BX13RArL.mjs +6669 -0
- package/dist/application-BX13RArL.mjs.map +1 -0
- package/dist/authconnection-CZvi9ANm.mjs +9 -0
- package/dist/authconnection-CZvi9ANm.mjs.map +1 -0
- package/dist/brand-Eo4pLXPJ.mjs.map +1 -1
- package/dist/cli/cache/bundle-cache.d.mts +1 -0
- package/dist/cli/cache/store.d.mts +1 -0
- package/dist/cli/cache/types.d.mts +1 -0
- package/dist/cli/commands/api/api-call.d.mts +19 -0
- package/dist/cli/commands/api/index.d.mts +3 -0
- package/dist/cli/commands/deploy/aigateway.d.mts +1 -0
- package/dist/cli/commands/deploy/application.d.mts +1 -0
- package/dist/cli/commands/deploy/apply-phases.d.mts +1 -0
- package/dist/cli/commands/deploy/auth.d.mts +1 -0
- package/dist/cli/commands/deploy/deploy.d.mts +24 -0
- package/dist/cli/commands/deploy/executor.d.mts +1 -0
- package/dist/cli/commands/deploy/function-registry-types.d.mts +12 -0
- package/dist/cli/commands/deploy/function-registry.d.mts +2 -0
- package/dist/cli/commands/deploy/idp.d.mts +1 -0
- package/dist/cli/commands/deploy/resolver.d.mts +1 -0
- package/dist/cli/commands/deploy/secret-manager.d.mts +1 -0
- package/dist/cli/commands/deploy/staticwebsite.d.mts +1 -0
- package/dist/cli/commands/deploy/tailordb/index.d.mts +1 -0
- package/dist/cli/commands/deploy/types.d.mts +1 -0
- package/dist/cli/commands/deploy/workflow-execution-policy.d.mts +1 -0
- package/dist/cli/commands/deploy/workflow.d.mts +1 -0
- package/dist/cli/commands/executor/get.d.mts +29 -0
- package/dist/cli/commands/executor/jobs.d.mts +123 -0
- package/dist/cli/commands/executor/list.d.mts +19 -0
- package/dist/cli/commands/executor/transform.d.mts +41 -0
- package/dist/cli/commands/executor/trigger.d.mts +41 -0
- package/dist/cli/commands/executor/webhook.d.mts +22 -0
- package/dist/cli/commands/function/get.d.mts +18 -0
- package/dist/cli/commands/function/list.d.mts +22 -0
- package/dist/cli/commands/function/transform.d.mts +11 -0
- package/dist/cli/commands/generate/options.d.mts +7 -0
- package/dist/cli/commands/generate/seed/bundler.d.mts +26 -0
- package/dist/cli/commands/generate/service.d.mts +11 -0
- package/dist/cli/commands/machineuser/list.d.mts +27 -0
- package/dist/cli/commands/machineuser/token.d.mts +22 -0
- package/dist/cli/commands/oauth2client/get.d.mts +18 -0
- package/dist/cli/commands/oauth2client/list.d.mts +20 -0
- package/dist/cli/commands/oauth2client/transform.d.mts +20 -0
- package/dist/cli/commands/organization/folder/create.d.mts +18 -0
- package/dist/cli/commands/organization/folder/delete.d.mts +16 -0
- package/dist/cli/commands/organization/folder/get.d.mts +17 -0
- package/dist/cli/commands/organization/folder/list.d.mts +22 -0
- package/dist/cli/commands/organization/folder/update.d.mts +18 -0
- package/dist/cli/commands/organization/get.d.mts +16 -0
- package/dist/cli/commands/organization/list.d.mts +15 -0
- package/dist/cli/commands/organization/transform.d.mts +27 -0
- package/dist/cli/commands/organization/tree.d.mts +25 -0
- package/dist/cli/commands/organization/update.d.mts +17 -0
- package/dist/cli/commands/remove.d.mts +16 -0
- package/dist/cli/commands/show.d.mts +34 -0
- package/dist/cli/commands/tailordb/migrate/bundler.d.mts +28 -0
- package/dist/cli/commands/tailordb/migrate/config.d.mts +19 -0
- package/dist/cli/commands/tailordb/migrate/diff-calculator.d.mts +223 -0
- package/dist/cli/commands/tailordb/migrate/generate.d.mts +17 -0
- package/dist/cli/commands/tailordb/migrate/snapshot-types.d.mts +202 -0
- package/dist/cli/commands/tailordb/migrate/snapshot.d.mts +98 -0
- package/dist/cli/commands/tailordb/migrate/types.d.mts +7 -0
- package/dist/cli/commands/tailordb/truncate.d.mts +19 -0
- package/dist/cli/commands/workflow/executions.d.mts +75 -0
- package/dist/cli/commands/workflow/get.d.mts +29 -0
- package/dist/cli/commands/workflow/list.d.mts +19 -0
- package/dist/cli/commands/workflow/resume.d.mts +23 -0
- package/dist/cli/commands/workflow/start.d.mts +61 -0
- package/dist/cli/commands/workflow/status.d.mts +5 -0
- package/dist/cli/commands/workflow/transform.d.mts +33 -0
- package/dist/cli/commands/workflow/wait.d.mts +16 -0
- package/dist/cli/commands/workflow/waiter.d.mts +23 -0
- package/dist/cli/commands/workspace/app/health.d.mts +18 -0
- package/dist/cli/commands/workspace/app/list.d.mts +22 -0
- package/dist/cli/commands/workspace/app/transform.d.mts +18 -0
- package/dist/cli/commands/workspace/create.d.mts +25 -0
- package/dist/cli/commands/workspace/delete.d.mts +15 -0
- package/dist/cli/commands/workspace/get.d.mts +17 -0
- package/dist/cli/commands/workspace/list.d.mts +17 -0
- package/dist/cli/commands/workspace/restore.d.mts +15 -0
- package/dist/cli/commands/workspace/transform.d.mts +16 -0
- package/dist/cli/commands/workspace/user/invite.d.mts +22 -0
- package/dist/cli/commands/workspace/user/list.d.mts +22 -0
- package/dist/cli/commands/workspace/user/remove.d.mts +17 -0
- package/dist/cli/commands/workspace/user/transform.d.mts +8 -0
- package/dist/cli/commands/workspace/user/update.d.mts +22 -0
- package/dist/cli/erd-viewer-assets/app.js +279 -36
- package/dist/cli/erd-viewer-assets/index.html +4 -0
- package/dist/cli/erd-viewer-assets/styles.css +252 -5
- package/dist/cli/index.d.mts +18 -3
- package/dist/cli/index.mjs +2237 -520
- package/dist/cli/index.mjs.map +1 -1
- package/dist/cli/lib.d.mts +81 -21107
- package/dist/cli/lib.mjs +7 -10
- package/dist/cli/lib.mjs.map +1 -1
- package/dist/cli/query/index.d.mts +39 -0
- package/dist/cli/services/application.d.mts +1 -0
- package/dist/cli/services/auth/service.d.mts +1 -0
- package/dist/cli/services/executor/service.d.mts +1 -0
- package/dist/cli/services/http-adapter/bundler.d.mts +1 -0
- package/dist/cli/services/http-adapter/service.d.mts +1 -0
- package/dist/cli/services/resolver/service.d.mts +1 -0
- package/dist/cli/services/tailordb/service.d.mts +1 -0
- package/dist/cli/services/workflow/bundler.d.mts +1 -0
- package/dist/cli/services/workflow/service.d.mts +1 -0
- package/dist/cli/shared/args.d.mts +13 -0
- package/dist/cli/shared/client.d.mts +1273 -0
- package/dist/cli/shared/config-loader.d.mts +26 -0
- package/dist/cli/shared/context.d.mts +28 -0
- package/dist/cli/shared/script-executor.d.mts +75 -0
- package/dist/cli/shared/seed-chunker.d.mts +46 -0
- package/dist/cli/shared/trigger-context.d.mts +1 -0
- package/dist/cli/shared/type-generator.d.mts +20 -0
- package/dist/cli/ts-hook.d.mts +4 -0
- package/dist/cli/ts-hook.mjs +333 -0
- package/dist/completion/zsh-worker.zsh +1531 -1131
- package/dist/configure/config/index.d.mts +21 -0
- package/dist/configure/config/types.d.mts +105 -0
- package/dist/configure/index.d.mts +124 -9
- package/dist/configure/index.mjs +136 -23
- package/dist/configure/index.mjs.map +1 -1
- package/dist/configure/services/aigateway/index.d.mts +17 -0
- package/dist/configure/services/aigateway/types.d.mts +11 -0
- package/dist/configure/services/auth/index.d.mts +34 -0
- package/dist/configure/services/auth/types.d.mts +172 -0
- package/dist/configure/services/executor/executor.d.mts +81 -0
- package/dist/configure/services/executor/index.d.mts +8 -0
- package/dist/configure/services/executor/operation.d.mts +45 -0
- package/dist/configure/services/executor/trigger/event.d.mts +272 -0
- package/dist/configure/services/executor/trigger/index.d.mts +8 -0
- package/dist/configure/services/executor/trigger/schedule.d.mts +25 -0
- package/dist/configure/services/executor/trigger/webhook.d.mts +44 -0
- package/dist/configure/services/executor/types.d.mts +2 -0
- package/dist/configure/services/http-adapter/http-adapter.d.mts +125 -0
- package/dist/configure/services/http-adapter/index.d.mts +2 -0
- package/dist/configure/services/idp/index.d.mts +64 -0
- package/dist/configure/services/idp/permission.d.mts +80 -0
- package/dist/configure/services/idp/types.d.mts +20 -0
- package/dist/configure/services/index.d.mts +34 -0
- package/dist/configure/services/resolver/index.d.mts +5 -0
- package/dist/configure/services/resolver/resolver.d.mts +75 -0
- package/dist/configure/services/resolver/types.d.mts +6 -0
- package/dist/configure/services/secrets/index.d.mts +38 -0
- package/dist/configure/services/secrets/types.d.mts +11 -0
- package/dist/configure/services/staticwebsite/index.d.mts +19 -0
- package/dist/configure/services/staticwebsite/types.d.mts +11 -0
- package/dist/configure/services/tailordb/index.d.mts +4 -0
- package/dist/configure/services/tailordb/permission.d.mts +119 -0
- package/dist/configure/services/tailordb/schema.d.mts +456 -0
- package/dist/configure/services/tailordb/types.d.mts +149 -0
- package/dist/configure/services/workflow/execution-policy.d.mts +71 -0
- package/dist/configure/services/workflow/execution-policy.types.d.mts +104 -0
- package/dist/configure/services/workflow/index.d.mts +8 -0
- package/dist/configure/services/workflow/job.d.mts +84 -0
- package/dist/configure/services/workflow/types.d.mts +2 -0
- package/dist/configure/services/workflow/wait-point.d.mts +73 -0
- package/dist/configure/services/workflow/workflow.d.mts +54 -0
- package/dist/configure/types/aigateway-name.d.mts +12 -0
- package/dist/configure/types/connection-name.d.mts +12 -0
- package/dist/configure/types/field-runtime.d.mts +17 -0
- package/dist/configure/types/field.d.mts +9 -0
- package/dist/configure/types/field.types.d.mts +109 -0
- package/dist/configure/types/idp-name.d.mts +12 -0
- package/dist/configure/types/index.d.mts +4 -0
- package/dist/configure/types/machine-user.d.mts +2 -0
- package/dist/configure/types/type.d.mts +82 -0
- package/dist/{context-Bd266-ru.mjs → context-DYQVYWqZ.mjs} +4 -5
- package/dist/context-DYQVYWqZ.mjs.map +1 -0
- package/dist/{crashreport-DFq-vsU0.mjs → crashreport-Cg-pqRJV.mjs} +6 -6
- package/dist/{crashreport-DFq-vsU0.mjs.map → crashreport-Cg-pqRJV.mjs.map} +1 -1
- package/dist/{crashreport-BMWcxeSE.mjs → crashreport-D3RjuSYb.mjs} +1 -1
- package/dist/enum-constants-j9QBF0cB.mjs.map +1 -1
- package/dist/{errors-Dtf2WPaW.mjs → errors-118wUDRr.mjs} +2 -2
- package/dist/errors-118wUDRr.mjs.map +1 -0
- package/dist/field-runtime-CAnh_oow.mjs +183 -0
- package/dist/field-runtime-CAnh_oow.mjs.map +1 -0
- package/dist/{file-BbdFGdMV.mjs → file-BUcmVIH2.mjs} +12 -14
- package/dist/file-BUcmVIH2.mjs.map +1 -0
- package/dist/{file-utils-CYZnO1pX.mjs → file-utils-yEiIS4nJ.mjs} +2 -2
- package/dist/file-utils-yEiIS4nJ.mjs.map +1 -0
- package/dist/{globals-Cf0sxIt8.mjs → globals-BjhJJ2dN.mjs} +5 -47
- package/dist/globals-BjhJJ2dN.mjs.map +1 -0
- package/dist/iconv-QWaZh0TT.mjs +38 -0
- package/dist/iconv-QWaZh0TT.mjs.map +1 -0
- package/dist/{idp-BDbK5gjm.mjs → idp-RA_swu-h.mjs} +12 -5
- package/dist/idp-RA_swu-h.mjs.map +1 -0
- package/dist/{interceptor-D-q1rvRl.mjs → interceptor-e33JtsC-.mjs} +2 -2
- package/dist/interceptor-e33JtsC-.mjs.map +1 -0
- package/dist/kysely/index.d.mts +2 -3
- package/dist/{kysely-type-DR8uzZTA.mjs → kysely-type-D-zflMGf.mjs} +2 -2
- package/dist/{kysely-type-DR8uzZTA.mjs.map → kysely-type-D-zflMGf.mjs.map} +1 -1
- package/dist/{logger-CxF-Ex5d.mjs → logger-BEiZZ3qT.mjs} +3 -3
- package/dist/{logger-CxF-Ex5d.mjs.map → logger-BEiZZ3qT.mjs.map} +1 -1
- package/dist/parser/service/auth/index.d.mts +1 -0
- package/dist/parser/service/auth/schema.d.mts +1 -0
- package/dist/parser/service/http-adapter/index.d.mts +1 -0
- package/dist/parser/service/http-adapter/schema.d.mts +1 -0
- package/dist/parser/service/idp/types.d.mts +4 -0
- package/dist/parser/service/tailordb/types.d.mts +126 -0
- package/dist/platform-serialize-RoRtBS0v.mjs +46 -0
- package/dist/platform-serialize-RoRtBS0v.mjs.map +1 -0
- package/dist/plugin/builtin/enum-constants/index.d.mts +16 -1
- package/dist/plugin/builtin/file-utils/index.d.mts +16 -1
- package/dist/plugin/builtin/file-utils/index.mjs +1 -1
- package/dist/plugin/builtin/kysely-type/index.d.mts +16 -1
- package/dist/plugin/builtin/kysely-type/index.mjs +1 -1
- package/dist/plugin/builtin/seed/index.d.mts +45 -1
- package/dist/plugin/builtin/seed/index.mjs +1 -1
- package/dist/plugin/get-generated-type.d.mts +17 -0
- package/dist/plugin/index.d.mts +3 -118
- package/dist/plugin/index.mjs +1 -1
- package/dist/plugin/index.mjs.map +1 -1
- package/dist/plugin/manager.d.mts +1 -0
- package/dist/plugin/types.d.mts +243 -0
- package/dist/plugin/with-context.d.mts +102 -0
- package/dist/{runtime-CY4JvrDj.mjs → register-ts-hook-BM1caLT0.mjs} +4128 -2744
- package/dist/register-ts-hook-BM1caLT0.mjs.map +1 -0
- package/dist/{registry-DH4m7eYo.mjs → registry-BSbq6SPo.mjs} +4 -3
- package/dist/registry-BSbq6SPo.mjs.map +1 -0
- package/dist/{repl-editor-DmGr9zMw.mjs → repl-editor-BCozyiNq.mjs} +6 -5
- package/dist/{repl-editor-DmGr9zMw.mjs.map → repl-editor-BCozyiNq.mjs.map} +1 -1
- package/dist/resource_pb-BRv7AMXK.mjs +49 -0
- package/dist/resource_pb-BRv7AMXK.mjs.map +1 -0
- package/dist/runtime/aigateway.d.mts +26 -0
- package/dist/runtime/aigateway.mjs +3 -0
- package/dist/runtime/authconnection.d.mts +22 -2
- package/dist/runtime/authconnection.mjs +2 -2
- package/dist/runtime/context.d.mts +55 -2
- package/dist/runtime/context.mjs +2 -2
- package/dist/runtime/file.d.mts +170 -2
- package/dist/runtime/file.mjs +2 -2
- package/dist/runtime/globals.d.mts +31 -19
- package/dist/runtime/iconv.d.mts +73 -2
- package/dist/runtime/iconv.mjs +2 -2
- package/dist/runtime/idp.d.mts +129 -2
- package/dist/runtime/idp.mjs +2 -2
- package/dist/runtime/index.d.mts +47 -9
- package/dist/runtime/index.mjs +9 -8
- package/dist/runtime/secretmanager.d.mts +41 -2
- package/dist/runtime/secretmanager.mjs +2 -2
- package/dist/{types-BZ7QKVE8.d.mts → runtime/types.d.mts} +4 -5
- package/dist/runtime/workflow.d.mts +113 -2
- package/dist/runtime/workflow.mjs +2 -2
- package/dist/{schema-Dtw9Orye.mjs → schema-C4LYbXVG.mjs} +21 -186
- package/dist/schema-C4LYbXVG.mjs.map +1 -0
- package/dist/{secret-file-VSVGy1V0.mjs → secret-file-aoNXJmKa.mjs} +3 -3
- package/dist/secret-file-aoNXJmKa.mjs.map +1 -0
- package/dist/secretmanager-Bd45j7an.mjs +98 -0
- package/dist/secretmanager-Bd45j7an.mjs.map +1 -0
- package/dist/secretmanager-CYlpffsz.mjs +13 -0
- package/dist/secretmanager-CYlpffsz.mjs.map +1 -0
- package/dist/seed/index.d.mts +1 -2
- package/dist/{seed-izIEyP3z.mjs → seed-CRSQJI_z.mjs} +14 -3
- package/dist/seed-CRSQJI_z.mjs.map +1 -0
- package/dist/service-CkIwEctX.mjs +3 -0
- package/dist/{service-DU1mVzri2.mjs → service-DeZeYa06.mjs} +3 -3
- package/dist/service-DeZeYa06.mjs.map +1 -0
- package/dist/{service-DjyqbCaJ.mjs → service-ufH7sS8o.mjs} +396 -58
- package/dist/service-ufH7sS8o.mjs.map +1 -0
- package/dist/service_pb-B2FF59sP.mjs +3 -0
- package/dist/service_pb-B5w9rjPY.mjs +955 -0
- package/dist/service_pb-B5w9rjPY.mjs.map +1 -0
- package/dist/tailor-proto/src/tailor/v1/aigateway_pb.d.mts +204 -0
- package/dist/tailor-proto/src/tailor/v1/aigateway_resource_pb.d.mts +58 -0
- package/dist/tailor-proto/src/tailor/v1/application_pb.d.mts +316 -0
- package/dist/tailor-proto/src/tailor/v1/application_resource_pb.d.mts +161 -0
- package/dist/tailor-proto/src/tailor/v1/auth_pb.d.mts +2070 -0
- package/dist/tailor-proto/src/tailor/v1/auth_resource_pb.d.mts +861 -0
- package/dist/tailor-proto/src/tailor/v1/events_pb.d.mts +112 -0
- package/dist/tailor-proto/src/tailor/v1/events_resource_pb.d.mts +69 -0
- package/dist/tailor-proto/src/tailor/v1/executor_pb.d.mts +439 -0
- package/dist/tailor-proto/src/tailor/v1/executor_resource_pb.d.mts +564 -0
- package/dist/tailor-proto/src/tailor/v1/function_pb.d.mts +145 -0
- package/dist/tailor-proto/src/tailor/v1/function_registry_pb.d.mts +451 -0
- package/dist/tailor-proto/src/tailor/v1/function_resource_pb.d.mts +192 -0
- package/dist/tailor-proto/src/tailor/v1/http_adapter_resource_pb.d.mts +50 -0
- package/dist/tailor-proto/src/tailor/v1/idp_pb.d.mts +407 -0
- package/dist/tailor-proto/src/tailor/v1/idp_resource_pb.d.mts +430 -0
- package/dist/tailor-proto/src/tailor/v1/metadata_pb.d.mts +73 -0
- package/dist/tailor-proto/src/tailor/v1/metadata_resource_pb.d.mts +17 -0
- package/dist/tailor-proto/src/tailor/v1/meter_pb.d.mts +309 -0
- package/dist/tailor-proto/src/tailor/v1/pipeline_pb.d.mts +591 -0
- package/dist/tailor-proto/src/tailor/v1/pipeline_resource_pb.d.mts +330 -0
- package/dist/tailor-proto/src/tailor/v1/resource_pb.d.mts +113 -0
- package/dist/tailor-proto/src/tailor/v1/secret_manager_pb.d.mts +346 -0
- package/dist/tailor-proto/src/tailor/v1/secret_manager_resource_pb.d.mts +54 -0
- package/dist/tailor-proto/src/tailor/v1/service_pb.d.mts +3576 -0
- package/dist/tailor-proto/src/tailor/v1/stateflow_pb.d.mts +187 -0
- package/dist/tailor-proto/src/tailor/v1/stateflow_resource_pb.d.mts +29 -0
- package/dist/tailor-proto/src/tailor/v1/staticwebsite_pb.d.mts +429 -0
- package/dist/tailor-proto/src/tailor/v1/staticwebsite_resource_pb.d.mts +90 -0
- package/dist/tailor-proto/src/tailor/v1/tailordb_pb.d.mts +684 -0
- package/dist/tailor-proto/src/tailor/v1/tailordb_resource_pb.d.mts +797 -0
- package/dist/tailor-proto/src/tailor/v1/telemetryrouter_pb.d.mts +330 -0
- package/dist/tailor-proto/src/tailor/v1/telemetryrouter_resource_pb.d.mts +183 -0
- package/dist/tailor-proto/src/tailor/v1/workflow_pb.d.mts +862 -0
- package/dist/tailor-proto/src/tailor/v1/workflow_resource_pb.d.mts +310 -0
- package/dist/tailor-proto/src/tailor/v1/workspace_pb.d.mts +2015 -0
- package/dist/tailor-proto/src/tailor/v1/workspace_resource_pb.d.mts +483 -0
- package/dist/{telemetry-CdqJEzkj.mjs → telemetry-BRVdwh14.mjs} +3 -3
- package/dist/telemetry-BRVdwh14.mjs.map +1 -0
- package/dist/telemetry-CkbkeJxl.mjs +3 -0
- package/dist/types/aigateway.generated.d.mts +9 -0
- package/dist/types/app-config.generated.d.mts +4 -0
- package/dist/types/auth-connection.generated.d.mts +20 -0
- package/dist/types/auth.generated.d.mts +145 -0
- package/dist/types/executor.generated.d.mts +177 -0
- package/dist/types/helpers.d.mts +39 -0
- package/dist/types/http-adapter.generated.d.mts +17 -0
- package/dist/{workflow.generated-ClEjBYhm.d.mts → types/idp.generated.d.mts} +98 -185
- package/dist/types/resolver.generated.d.mts +39 -0
- package/dist/types/secrets.generated.d.mts +14 -0
- package/dist/types/staticwebsite.generated.d.mts +10 -0
- package/dist/{types-CdcQh4Z2.d.mts → types/tailordb.generated.d.mts} +2 -287
- package/dist/types/workflow.generated.d.mts +12 -0
- package/dist/types-C-lQ4se3.mjs +4 -0
- package/dist/types-CXF2OYdR.mjs +199 -0
- package/dist/types-CXF2OYdR.mjs.map +1 -0
- package/dist/utils/test/index.d.mts +5 -89
- package/dist/utils/test/index.mjs.map +1 -1
- package/dist/utils/test/mock.d.mts +87 -0
- package/dist/vitest/environment.d.mts +1 -2
- package/dist/vitest/environment.mjs +2 -2
- package/dist/vitest/environment.mjs.map +1 -1
- package/dist/vitest/index.d.mts +12 -428
- package/dist/vitest/index.mjs +740 -9
- package/dist/vitest/index.mjs.map +1 -1
- package/dist/vitest/mock-kysely.d.mts +62 -0
- package/dist/vitest/mock.d.mts +9 -0
- package/dist/vitest/mocks/aigateway.d.mts +30 -0
- package/dist/vitest/mocks/authconnection.d.mts +28 -0
- package/dist/vitest/mocks/file.d.mts +41 -0
- package/dist/vitest/mocks/iconv.d.mts +27 -0
- package/dist/vitest/mocks/idp.d.mts +44 -0
- package/dist/vitest/mocks/secretmanager.d.mts +33 -0
- package/dist/vitest/mocks/tailordb.d.mts +71 -0
- package/dist/vitest/mocks/workflow.d.mts +117 -0
- package/dist/vitest/setup.d.mts +1 -2
- package/dist/vitest/setup.mjs +2 -2
- package/dist/vitest/workflow-local.d.mts +40 -0
- package/dist/workflow-GNuSnsTx.mjs +22 -0
- package/dist/workflow-GNuSnsTx.mjs.map +1 -0
- package/docs/cli/application.md +98 -250
- package/docs/cli/auth.md +51 -295
- package/docs/cli/completion.md +1 -25
- package/docs/cli/crashreport.md +3 -61
- package/docs/cli/executor.md +23 -187
- package/docs/cli/function.md +14 -130
- package/docs/cli/organization.md +11 -221
- package/docs/cli/plugin.md +29 -0
- package/docs/cli/query.md +2 -22
- package/docs/cli/secret.md +71 -251
- package/docs/cli/setup.md +137 -41
- package/docs/cli/skills.md +50 -39
- package/docs/cli/staticwebsite.md +32 -180
- package/docs/cli/tailordb.md +97 -323
- package/docs/cli/upgrade.md +2 -22
- package/docs/cli/user.md +78 -247
- package/docs/cli/workflow.md +44 -203
- package/docs/cli/workspace.md +165 -538
- package/docs/cli-reference.md +167 -67
- package/docs/configuration.md +34 -4
- package/docs/github-actions.md +46 -19
- package/docs/migration/v2.md +397 -26
- package/docs/multi-environment.md +29 -7
- package/docs/plugin/custom.md +4 -4
- package/docs/plugin/index.md +8 -8
- package/docs/quickstart.md +8 -7
- package/docs/runtime.md +9 -3
- package/docs/services/aigateway.md +18 -2
- package/docs/services/auth.md +34 -42
- package/docs/services/http-adapter.md +16 -1
- package/docs/services/idp.md +55 -2
- package/docs/services/resolver.md +2 -2
- package/docs/services/secret.md +11 -11
- package/docs/services/staticwebsite.md +8 -2
- package/docs/services/tailordb-migration.md +25 -23
- package/docs/services/tailordb.md +55 -32
- package/docs/services/workflow.md +92 -14
- package/docs/testing.md +49 -3
- package/package.json +42 -31
- package/postinstall.mjs +4 -6
- package/dist/application-Dtqap5jM.mjs +0 -3
- package/dist/application-XuMWK4eq.mjs +0 -7257
- package/dist/application-XuMWK4eq.mjs.map +0 -1
- package/dist/authconnection-D2MhtTN5.mjs +0 -15
- package/dist/authconnection-D2MhtTN5.mjs.map +0 -1
- package/dist/authconnection-DvUQAjQS.d.mts +0 -39
- package/dist/context-Bd266-ru.mjs.map +0 -1
- package/dist/context-C2lEi9uw.d.mts +0 -47
- package/dist/errors-Dtf2WPaW.mjs.map +0 -1
- package/dist/field-DOsJCPFa.mjs +0 -22
- package/dist/field-DOsJCPFa.mjs.map +0 -1
- package/dist/file-BbdFGdMV.mjs.map +0 -1
- package/dist/file-Dq3NIt_F.d.mts +0 -211
- package/dist/file-utils-CYZnO1pX.mjs.map +0 -1
- package/dist/globals-Cf0sxIt8.mjs.map +0 -1
- package/dist/http-adapter.generated-DFsXDdm5.d.mts +0 -581
- package/dist/iconv-Co-TOPuH.d.mts +0 -122
- package/dist/iconv-D2vi8G36.mjs +0 -63
- package/dist/iconv-D2vi8G36.mjs.map +0 -1
- package/dist/idp-BDbK5gjm.mjs.map +0 -1
- package/dist/idp-DrhVrLmV.d.mts +0 -160
- package/dist/index-BI-_j9Z3.d.mts +0 -1013
- package/dist/index-C4JirJH8.d.mts +0 -18
- package/dist/index-CZfWhr0a.d.mts +0 -18
- package/dist/index-Cg8VKAdN.d.mts +0 -46
- package/dist/index-DYRjoLXD.d.mts +0 -47
- package/dist/index-lFpcjHPU.d.mts +0 -201
- package/dist/index-nW7hE6oE.d.mts +0 -18
- package/dist/interceptor-D-q1rvRl.mjs.map +0 -1
- package/dist/mock-FPxmnt-y.mjs +0 -759
- package/dist/mock-FPxmnt-y.mjs.map +0 -1
- package/dist/registry-DH4m7eYo.mjs.map +0 -1
- package/dist/rolldown-runtime-DXywRVcq.mjs +0 -20
- package/dist/runtime-CY4JvrDj.mjs.map +0 -1
- package/dist/schema-Dtw9Orye.mjs.map +0 -1
- package/dist/secret-file-VSVGy1V0.mjs.map +0 -1
- package/dist/secretmanager-B3n4KHfm.d.mts +0 -55
- package/dist/secretmanager-BVxw3ih_.mjs +0 -24
- package/dist/secretmanager-BVxw3ih_.mjs.map +0 -1
- package/dist/seed-izIEyP3z.mjs.map +0 -1
- package/dist/service-DCqIWibD.mjs +0 -3
- package/dist/service-DU1mVzri2.mjs.map +0 -1
- package/dist/service-DjyqbCaJ.mjs.map +0 -1
- package/dist/telemetry-CdqJEzkj.mjs.map +0 -1
- package/dist/telemetry-ClwW5ohF.mjs +0 -3
- package/dist/types-74etvaxy.mjs +0 -4
- package/dist/types-BDRml5C3.d.mts +0 -574
- package/dist/types-BQijbo4m.mjs +0 -371
- package/dist/types-BQijbo4m.mjs.map +0 -1
- package/dist/types-BX4q6Mo6.d.mts +0 -339
- package/dist/workflow-BOmaZwwG.mjs +0 -43
- package/dist/workflow-BOmaZwwG.mjs.map +0 -1
- package/dist/workflow-BVy4XWjS.d.mts +0 -101
package/dist/cli/index.mjs
CHANGED
|
@@ -1,28 +1,31 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { A as loadAccessToken, At as FunctionExecution_Type, B as fetchAll, C as getDistDir, D as deleteUserTokens, E as loadConfig, F as removeLegacyUserAlias, G as initOAuth2Client, H as fetchPaged, I as resolveTokens, K as initOperatorClient, L as saveUserTokens, M as loadMachineUserName, N as loadWorkspaceId, O as fetchLatestToken, P as readPlatformConfig, R as writePlatformConfig, U as fetchPlatformMachineUserToken, W as fetchUserInfo, _ as composeFunctionTreeshakeOptions, dn as PATScope, g as platformBundleDefinePlugin, ht as CustomDomainStatus, i as resolveInlineSourcemap, in as AuthInvokerSchema, k as findConfigUserKey, l as INVOKER_EXPR, o as WorkflowJobSchema, s as ResolverSchema, t as defineApplication, v as createLogLevelTreeshakeOptions, w as hashContent$1, y as resolveBundleLogLevel, z as closeConnectionPool } from "../application-XuMWK4eq.mjs";
|
|
3
2
|
import { t as assertDefined } from "../assert-DBxo8jPo.mjs";
|
|
4
|
-
import { n as logger, r as styles } from "../logger-
|
|
5
|
-
import { $ as
|
|
6
|
-
import {
|
|
3
|
+
import { n as logger, r as styles } from "../logger-BEiZZ3qT.mjs";
|
|
4
|
+
import { $ as fetchUserInfo, A as hasAnyUserTokenEntry, B as readPlatformConfig, D as loadConfig, Et as workspaceArgs, F as loadMachineUserName, G as writePlatformConfig, H as resolveConfigUser, I as loadPlatformClientConfig, J as fetchAll, K as closeConnectionPool, L as loadStoredUserTokens, M as loadAccessToken, N as loadConfigPath, O as deleteUserTokens, P as loadConsoleBaseUrl, Q as fetchPlatformMachineUserToken, R as loadWorkspaceId, T as hashContent$1, Tt as toPageDirection, U as resolveUserTokenKey, V as removeLegacyUserAlias, W as saveUserTokens, Y as fetchAllTolerant, Z as fetchPaged, _ as platformBundleDefinePlugin, a as resolveInlineSourcemap, at as initOperatorClient, b as resolveBundleLogLevel, bt as pagedLogArgs, c as WorkflowJobSchema, d as INVOKER_EXPR, dt as confirmationArgs, ft as deploymentArgs, gt as multiConfigArg, ht as isVerbose, it as initOAuth2Client, j as hasUserTokenEntry, k as fetchLatestToken, l as ResolverSchema, lt as commonArgs, nt as getOrNull, ot as isDefaultPlatform, q as defaultPlatformBaseUrl, rt as getPlatformBaseUrl, t as defineApplication, tt as getOAuth2ClientId, ut as configArg, v as composeFunctionTreeshakeOptions, w as getDistDir, wt as resolveMachineUserInputSource, xt as paginationArgs, y as createLogLevelTreeshakeOptions, z as platformConfigFromProfile } from "../application-BX13RArL.mjs";
|
|
5
|
+
import { $t as compareSnapshotWithRemote, A as listCommand$13, An as apiCommand, C as listCommand$14, Cn as hasChanges, Dn as PluginManager, Dt as listCommand$7, E as waitCommand, En as sdkNameLabelKey, F as generateCommand$1, G as removeCommand$1, Gt as deploy, H as extractOwnedNamespaces, Ht as formatKeyValueTable, I as generateMigrationScript, It as getCommand$6, K as updateCommand$3, Kt as ensureConfigId, L as writeDbTypesFile, Mn as assertWritable, N as truncateCommand, Nn as defineAppCommand, O as resumeCommand, On as generateUserTypes, Pt as startCommand, Q as getCommand$5, Qt as parseMigrationLabelNumber, R as getConfiguredEditorCommand, Rt as executionsCommand, Sn as formatMigrationDiff, T as healthCommand, Tn as resourceTrn, Tt as triggerCommand, U as logBetaWarning, Ut as getCommand$1, V as showCommand, Vt as functionExecutionStatusToString, X as listCommand$12, Xt as MIGRATION_LABEL_KEY, Y as treeCommand, Zt as handleOptionalToRequiredError, _n as loadDiff, b as createCommand$4, bn as parseMigrationNumberArg, c as listCommand$15, cn as compareLocalTypesWithSnapshot, ct as createCommand$3, dn as getLatestMigrationNumber, en as generateAllTypeManifestsFromSnapshot, et as updateCommand$2, f as restoreCommand, ft as getCommand$3, g as getCommand$7, gn as isValidMigrationNumber, gt as listCommand$9, ht as tokenCommand$1, i as updateCommand$4, in as INITIAL_SCHEMA_NUMBER, it as getCommand$4, jt as jobsCommand, kn as prompt, m as listCommand$16, mn as getMigrationFiles, nt as listCommand$11, o as removeCommand, ot as deleteCommand$4, pn as getMigrationFilePath, qt as executeScript, r as queryCommand, sn as assertValidMigrationFiles, t as registerTsHook, tn as protoGqlPermission, u as inviteCommand, un as createSnapshotFromLocalTypes, ut as listCommand$10, v as deleteCommand$5, vn as reconstructSnapshotFromMigrations, vt as generate, wn as getNamespacesWithMigrations, wt as webhookCommand, xt as getCommand$2, yn as formatMigrationNumber, yt as listCommand$8, z as openInConfiguredEditor } from "../register-ts-hook-BM1caLT0.mjs";
|
|
6
|
+
import { i as ExecutorSchema, r as stripExecutorTriggerArgs } from "../service-ufH7sS8o.mjs";
|
|
7
7
|
import { t as multiline } from "../multiline-sfHpTZZK.mjs";
|
|
8
8
|
import { t as readPackageJson } from "../package-json-8b0O9TlX.mjs";
|
|
9
|
-
import { i as userAgent } from "../secret-file-
|
|
10
|
-
import { n as isCLIError } from "../errors-
|
|
11
|
-
import {
|
|
9
|
+
import { i as userAgent } from "../secret-file-aoNXJmKa.mjs";
|
|
10
|
+
import { n as isCLIError } from "../errors-118wUDRr.mjs";
|
|
11
|
+
import { B as FunctionExecution_Type, Ct as PATScope, T as CustomDomainStatus, gt as AuthInvokerSchema } from "../service_pb-B5w9rjPY.mjs";
|
|
12
|
+
import { a as JSON_FOOTER_MARKER, i as CRASH_LOG_EXTENSION, o as parseCrashReportConfig, r as sendCrashReport, t as initCrashReporting } from "../crashreport-Cg-pqRJV.mjs";
|
|
12
13
|
import { t as isPluginGeneratedType } from "../type-source-DH_LH20p.mjs";
|
|
14
|
+
import * as path from "pathe";
|
|
15
|
+
import { dirname, resolve } from "pathe";
|
|
16
|
+
import { resolvePackageJSON, resolveTSConfig } from "pkg-types";
|
|
13
17
|
import { arg, defineCommand, runCommand, runMain } from "politty";
|
|
14
18
|
import { withCompletionCommand } from "politty/completion";
|
|
19
|
+
import { withSkillCommand } from "politty/skill";
|
|
15
20
|
import { z } from "zod";
|
|
16
21
|
import { create } from "@bufbuild/protobuf";
|
|
17
22
|
import * as fs$1 from "node:fs";
|
|
23
|
+
import { accessSync, constants, readdirSync } from "node:fs";
|
|
18
24
|
import { timestampDate } from "@bufbuild/protobuf/wkt";
|
|
19
|
-
import * as path from "pathe";
|
|
20
|
-
import { dirname, resolve } from "pathe";
|
|
21
25
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
22
26
|
import { generateCodeVerifier } from "@badgateway/oauth2-client";
|
|
23
27
|
import { Code, ConnectError } from "@connectrpc/connect";
|
|
24
28
|
import pLimit from "p-limit";
|
|
25
|
-
import { resolvePackageJSON, resolveTSConfig } from "pkg-types";
|
|
26
29
|
import * as crypto from "node:crypto";
|
|
27
30
|
import { createHash } from "node:crypto";
|
|
28
31
|
import * as http from "node:http";
|
|
@@ -30,6 +33,7 @@ import open from "open";
|
|
|
30
33
|
import * as rolldown from "rolldown";
|
|
31
34
|
import * as fsPromises from "node:fs/promises";
|
|
32
35
|
import { glob } from "node:fs/promises";
|
|
36
|
+
import * as os from "node:os";
|
|
33
37
|
import { setTimeout as setTimeout$1 } from "node:timers/promises";
|
|
34
38
|
import { TraceMap, generatedPositionFor, originalPositionFor } from "@jridgewell/trace-mapping";
|
|
35
39
|
import { spawn, spawnSync } from "node:child_process";
|
|
@@ -37,6 +41,26 @@ import { watch } from "chokidar";
|
|
|
37
41
|
import * as fs from "fs";
|
|
38
42
|
import { lookup } from "mime-types";
|
|
39
43
|
|
|
44
|
+
//#region src/cli/commands/auth/token.ts
|
|
45
|
+
const tokenCommand = defineAppCommand({
|
|
46
|
+
name: "token",
|
|
47
|
+
description: "Print a valid Tailor Platform access token to stdout, refreshing it first if expired.",
|
|
48
|
+
args: z.strictObject({ profile: workspaceArgs.profile }),
|
|
49
|
+
run: async ({ profile }) => {
|
|
50
|
+
const token = await loadAccessToken({ profile });
|
|
51
|
+
logger.out(token);
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
//#endregion
|
|
56
|
+
//#region src/cli/commands/auth/index.ts
|
|
57
|
+
const authCommand = defineCommand({
|
|
58
|
+
name: "auth",
|
|
59
|
+
description: "Authentication helpers for scripts and plugins.",
|
|
60
|
+
subCommands: { token: tokenCommand }
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
//#endregion
|
|
40
64
|
//#region src/cli/commands/authconnection/args.ts
|
|
41
65
|
/**
|
|
42
66
|
* Arguments for identifying an auth connection
|
|
@@ -67,13 +91,13 @@ function randomState$1() {
|
|
|
67
91
|
const authorizeAuthConnectionCommand = defineAppCommand({
|
|
68
92
|
name: "authorize",
|
|
69
93
|
description: "Authorize an auth connection via OAuth2 flow.",
|
|
70
|
-
args: z.
|
|
94
|
+
args: z.strictObject({
|
|
71
95
|
...workspaceArgs,
|
|
72
96
|
...connectionNameArgs,
|
|
73
97
|
scopes: z.string().optional().default(defaultScopes).describe("OAuth2 scopes to request (comma-separated)"),
|
|
74
98
|
port: z.coerce.number().optional().default(defaultPort).describe("Local callback server port"),
|
|
75
99
|
"no-browser": z.boolean().optional().default(false).describe("Don't open browser automatically")
|
|
76
|
-
})
|
|
100
|
+
}),
|
|
77
101
|
run: async (args) => {
|
|
78
102
|
await assertWritable({ profile: args.profile });
|
|
79
103
|
const client = await initOperatorClient(await loadAccessToken({ profile: args.profile }));
|
|
@@ -147,13 +171,13 @@ const authorizeAuthConnectionCommand = defineAppCommand({
|
|
|
147
171
|
clearTimeout(timeout);
|
|
148
172
|
const code = err.code;
|
|
149
173
|
const portHint = code === "EADDRINUSE" || code === "EACCES" ? `Try a different port with --port, or authorize via the Console instead:` : `Authorize via the Console instead:`;
|
|
150
|
-
logger.warn(`Could not start the local callback server on port ${args.port}${code ? ` (${code})` : ""}.\n${portHint}\n tailor
|
|
174
|
+
logger.warn(`Could not start the local callback server on port ${args.port}${code ? ` (${code})` : ""}.\n${portHint}\n tailor authconnection open`);
|
|
151
175
|
reject(err);
|
|
152
176
|
});
|
|
153
177
|
server.listen(args.port, async () => {
|
|
154
178
|
const authorizeUrl = authUrl.toString();
|
|
155
179
|
logger.info(args["no-browser"] ? `Open this URL in your browser to authorize:\n\n${authorizeUrl}\n` : `Opening browser for authorization:\n\n${authorizeUrl}\n`);
|
|
156
|
-
logger.info("If this flow doesn't complete, you can authorize via the Console instead:\n tailor
|
|
180
|
+
logger.info("If this flow doesn't complete, you can authorize via the Console instead:\n tailor authconnection open");
|
|
157
181
|
if (!args["no-browser"]) try {
|
|
158
182
|
await open(authorizeUrl);
|
|
159
183
|
} catch {
|
|
@@ -170,11 +194,11 @@ const authorizeAuthConnectionCommand = defineAppCommand({
|
|
|
170
194
|
const deleteAuthConnectionCommand = defineAppCommand({
|
|
171
195
|
name: "delete",
|
|
172
196
|
description: "Delete an auth connection entirely.",
|
|
173
|
-
args: z.
|
|
197
|
+
args: z.strictObject({
|
|
174
198
|
...workspaceArgs,
|
|
175
199
|
...connectionNameArgs,
|
|
176
200
|
...confirmationArgs
|
|
177
|
-
})
|
|
201
|
+
}),
|
|
178
202
|
run: async (args) => {
|
|
179
203
|
await assertWritable({ profile: args.profile });
|
|
180
204
|
const client = await initOperatorClient(await loadAccessToken({ profile: args.profile }));
|
|
@@ -219,10 +243,10 @@ function connectionInfo(connection) {
|
|
|
219
243
|
const listAuthConnectionCommand = defineAppCommand({
|
|
220
244
|
name: "list",
|
|
221
245
|
description: "List all auth connections.",
|
|
222
|
-
args: z.
|
|
246
|
+
args: z.strictObject({
|
|
223
247
|
...workspaceArgs,
|
|
224
248
|
...paginationArgs()
|
|
225
|
-
})
|
|
249
|
+
}),
|
|
226
250
|
run: async (args) => {
|
|
227
251
|
const client = await initOperatorClient(await loadAccessToken({ profile: args.profile }));
|
|
228
252
|
const workspaceId = await loadWorkspaceId({
|
|
@@ -253,18 +277,21 @@ const listAuthConnectionCommand = defineAppCommand({
|
|
|
253
277
|
|
|
254
278
|
//#endregion
|
|
255
279
|
//#region src/cli/commands/authconnection/open.ts
|
|
256
|
-
const consoleBaseUrl$1 = "https://console.tailor.tech";
|
|
257
280
|
const openAuthConnectionCommand = defineAppCommand({
|
|
258
281
|
name: "open",
|
|
259
282
|
description: "Open the auth connections page in the Tailor Platform Console.",
|
|
260
|
-
args: z.
|
|
283
|
+
args: z.strictObject({ ...workspaceArgs }),
|
|
261
284
|
run: async (args) => {
|
|
262
285
|
const workspaceId = await loadWorkspaceId({
|
|
263
286
|
workspaceId: args["workspace-id"],
|
|
264
287
|
profile: args.profile
|
|
265
288
|
});
|
|
266
289
|
const consolePath = `/workspaces/${workspaceId}/settings/connections`;
|
|
267
|
-
const
|
|
290
|
+
const consoleBaseUrl = await loadConsoleBaseUrl({
|
|
291
|
+
profile: args.profile,
|
|
292
|
+
...args["workspace-id"] !== void 0 ? { allowMissingProfile: true } : {}
|
|
293
|
+
});
|
|
294
|
+
const consoleUrl = new URL(consolePath, consoleBaseUrl).toString();
|
|
268
295
|
const jsonOutput = logger.jsonMode;
|
|
269
296
|
logger.info("Opening auth connections page in Tailor Platform Console...");
|
|
270
297
|
let opened = true;
|
|
@@ -294,11 +321,11 @@ const revokeAuthConnectionCommand = defineAppCommand({
|
|
|
294
321
|
name: "revoke",
|
|
295
322
|
description: "Revoke an auth connection's tokens (keeps the connection; use 'delete' to remove it).",
|
|
296
323
|
notes: "Revoke invalidates the connection's active session and tokens but keeps the connection and its stored credentials, so it can be re-authorized later. Use `delete` to remove the connection entirely.",
|
|
297
|
-
args: z.
|
|
324
|
+
args: z.strictObject({
|
|
298
325
|
...workspaceArgs,
|
|
299
326
|
...connectionNameArgs,
|
|
300
327
|
...confirmationArgs
|
|
301
|
-
})
|
|
328
|
+
}),
|
|
302
329
|
run: async (args) => {
|
|
303
330
|
await assertWritable({ profile: args.profile });
|
|
304
331
|
const client = await initOperatorClient(await loadAccessToken({ profile: args.profile }));
|
|
@@ -355,10 +382,10 @@ function formatCrashReportFiles(files, localDir) {
|
|
|
355
382
|
path: path.join(localDir, file)
|
|
356
383
|
}));
|
|
357
384
|
}
|
|
358
|
-
const listCommand$
|
|
385
|
+
const listCommand$6 = defineAppCommand({
|
|
359
386
|
name: "list",
|
|
360
387
|
description: "List local crash report files.",
|
|
361
|
-
args: z.
|
|
388
|
+
args: z.strictObject({ ...paginationArgs() }),
|
|
362
389
|
run: async (args) => {
|
|
363
390
|
const config = parseCrashReportConfig();
|
|
364
391
|
const jsonOutput = logger.jsonMode;
|
|
@@ -398,14 +425,14 @@ const listCommand$5 = defineAppCommand({
|
|
|
398
425
|
const sendCommand = defineAppCommand({
|
|
399
426
|
name: "send",
|
|
400
427
|
description: "Submit a crash report to help improve the SDK.",
|
|
401
|
-
args: z.
|
|
428
|
+
args: z.strictObject({ file: arg(z.string(), {
|
|
402
429
|
description: "Path to the crash report file",
|
|
403
430
|
required: true,
|
|
404
431
|
completion: {
|
|
405
432
|
type: "file",
|
|
406
433
|
extensions: ["log"]
|
|
407
434
|
}
|
|
408
|
-
}) })
|
|
435
|
+
}) }),
|
|
409
436
|
run: async (args) => {
|
|
410
437
|
let content;
|
|
411
438
|
try {
|
|
@@ -454,11 +481,11 @@ const crashReportCommand = defineCommand({
|
|
|
454
481
|
name: "crashreport",
|
|
455
482
|
description: "Manage crash reports.",
|
|
456
483
|
subCommands: {
|
|
457
|
-
list: listCommand$
|
|
484
|
+
list: listCommand$6,
|
|
458
485
|
send: sendCommand
|
|
459
486
|
},
|
|
460
487
|
async run() {
|
|
461
|
-
await runCommand(listCommand$
|
|
488
|
+
await runCommand(listCommand$6, []);
|
|
462
489
|
}
|
|
463
490
|
});
|
|
464
491
|
|
|
@@ -467,8 +494,9 @@ const crashReportCommand = defineCommand({
|
|
|
467
494
|
const deployCommand$1 = defineAppCommand({
|
|
468
495
|
name: "deploy",
|
|
469
496
|
description: "Deploy your application by applying the Tailor configuration.",
|
|
470
|
-
args: z.
|
|
471
|
-
...
|
|
497
|
+
args: z.strictObject({
|
|
498
|
+
...workspaceArgs,
|
|
499
|
+
...multiConfigArg,
|
|
472
500
|
...confirmationArgs,
|
|
473
501
|
"dry-run": arg(z.boolean().optional(), {
|
|
474
502
|
alias: "d",
|
|
@@ -478,10 +506,10 @@ const deployCommand$1 = defineAppCommand({
|
|
|
478
506
|
"no-validate": arg(z.boolean().optional(), { description: "Skip client-side validation against platform resource constraints" }),
|
|
479
507
|
"no-cache": arg(z.boolean().optional(), { description: "Disable bundle caching for this run" }),
|
|
480
508
|
"clean-cache": arg(z.boolean().optional(), { description: "Clean the bundle cache before building" })
|
|
481
|
-
})
|
|
509
|
+
}),
|
|
482
510
|
run: async (args) => {
|
|
483
511
|
await assertWritable({ profile: args.profile });
|
|
484
|
-
const { initTelemetry } = await import("../telemetry-
|
|
512
|
+
const { initTelemetry } = await import("../telemetry-CkbkeJxl.mjs");
|
|
485
513
|
await initTelemetry();
|
|
486
514
|
await deploy({
|
|
487
515
|
workspaceId: args["workspace-id"],
|
|
@@ -505,12 +533,12 @@ const executorCommand = defineCommand({
|
|
|
505
533
|
subCommands: {
|
|
506
534
|
trigger: triggerCommand,
|
|
507
535
|
jobs: jobsCommand,
|
|
508
|
-
list: listCommand$
|
|
536
|
+
list: listCommand$7,
|
|
509
537
|
get: getCommand$1,
|
|
510
538
|
webhook: webhookCommand
|
|
511
539
|
},
|
|
512
540
|
async run() {
|
|
513
|
-
await runCommand(listCommand$
|
|
541
|
+
await runCommand(listCommand$7, []);
|
|
514
542
|
}
|
|
515
543
|
});
|
|
516
544
|
|
|
@@ -1020,14 +1048,14 @@ Stack traces are mapped only when the execution includes a content hash for the
|
|
|
1020
1048
|
desc: "Get execution details as JSON"
|
|
1021
1049
|
}
|
|
1022
1050
|
],
|
|
1023
|
-
args: z.
|
|
1051
|
+
args: z.strictObject({
|
|
1024
1052
|
...workspaceArgs,
|
|
1025
1053
|
...pagedLogArgs,
|
|
1026
1054
|
"execution-id": arg(z.string().optional(), {
|
|
1027
1055
|
positional: true,
|
|
1028
1056
|
description: "Execution ID (if provided, shows details with logs)"
|
|
1029
1057
|
})
|
|
1030
|
-
})
|
|
1058
|
+
}),
|
|
1031
1059
|
run: async (args) => {
|
|
1032
1060
|
const client = await initOperatorClient(await loadAccessToken({ profile: args.profile }));
|
|
1033
1061
|
const workspaceId = await loadWorkspaceId({
|
|
@@ -1245,7 +1273,7 @@ async function detectFunctionType(options) {
|
|
|
1245
1273
|
const rawInput = module.default.input;
|
|
1246
1274
|
let inputSchema;
|
|
1247
1275
|
if (rawInput) {
|
|
1248
|
-
const { t } = await import("../types-
|
|
1276
|
+
const { t } = await import("../types-C-lQ4se3.mjs");
|
|
1249
1277
|
inputSchema = t.object(rawInput);
|
|
1250
1278
|
}
|
|
1251
1279
|
return {
|
|
@@ -1255,7 +1283,7 @@ async function detectFunctionType(options) {
|
|
|
1255
1283
|
inputSchema
|
|
1256
1284
|
};
|
|
1257
1285
|
}
|
|
1258
|
-
const executorResult = ExecutorSchema.safeParse(module.default);
|
|
1286
|
+
const executorResult = ExecutorSchema.safeParse(stripExecutorTriggerArgs(module.default));
|
|
1259
1287
|
if (executorResult.success) {
|
|
1260
1288
|
const { operation } = executorResult.data;
|
|
1261
1289
|
if (operation.kind === "function" || operation.kind === "jobFunction") return {
|
|
@@ -1337,7 +1365,7 @@ function deriveNameFromPath(filePath) {
|
|
|
1337
1365
|
//#endregion
|
|
1338
1366
|
//#region src/cli/commands/function/test-run.ts
|
|
1339
1367
|
/**
|
|
1340
|
-
* `tailor
|
|
1368
|
+
* `tailor function test-run` command
|
|
1341
1369
|
*
|
|
1342
1370
|
* Bundles and executes a function on the Tailor Platform server
|
|
1343
1371
|
* without deploying (applying) the application.
|
|
@@ -1397,6 +1425,7 @@ When a \`.js\` file is provided, detection and bundling are skipped and the file
|
|
|
1397
1425
|
const authNamespace = resolveAuthNamespace(config.auth);
|
|
1398
1426
|
const machineUserName = await resolveMachineUserName({
|
|
1399
1427
|
cliMachineUser: args["machine-user"],
|
|
1428
|
+
cliMachineUserSource: resolveMachineUserInputSource(args["machine-user"]),
|
|
1400
1429
|
profile: args.profile,
|
|
1401
1430
|
authConfig: config.auth
|
|
1402
1431
|
});
|
|
@@ -1517,9 +1546,10 @@ function resolveAuthNamespace(authConfig) {
|
|
|
1517
1546
|
* @returns Resolved machine user name
|
|
1518
1547
|
*/
|
|
1519
1548
|
async function resolveMachineUserName(options) {
|
|
1520
|
-
const { cliMachineUser, profile, authConfig } = options;
|
|
1549
|
+
const { cliMachineUser, cliMachineUserSource, profile, authConfig } = options;
|
|
1521
1550
|
const resolved = await loadMachineUserName({
|
|
1522
1551
|
machineUser: cliMachineUser,
|
|
1552
|
+
machineUserSource: cliMachineUserSource,
|
|
1523
1553
|
profile
|
|
1524
1554
|
});
|
|
1525
1555
|
if (resolved) return resolved;
|
|
@@ -1530,7 +1560,7 @@ async function resolveMachineUserName(options) {
|
|
|
1530
1560
|
if (keys.length > 0) return assertDefined(keys[0], "machine user key missing");
|
|
1531
1561
|
}
|
|
1532
1562
|
}
|
|
1533
|
-
throw new Error("Machine user is required. Provide --machine-user, set TAILOR_PLATFORM_MACHINE_USER_NAME, set a profile default with 'tailor
|
|
1563
|
+
throw new Error("Machine user is required. Provide --machine-user, set TAILOR_PLATFORM_MACHINE_USER_NAME, set a profile default with 'tailor profile update <profile> --machine-user <name>', or ensure tailor.config.ts has machine users configured.");
|
|
1534
1564
|
}
|
|
1535
1565
|
/**
|
|
1536
1566
|
* Resolve full machine user info: name, id (from API), and attributes (from config).
|
|
@@ -1573,12 +1603,12 @@ const functionCommand = defineCommand({
|
|
|
1573
1603
|
description: "Manage functions",
|
|
1574
1604
|
subCommands: {
|
|
1575
1605
|
get: getCommand$2,
|
|
1576
|
-
list: listCommand$
|
|
1606
|
+
list: listCommand$8,
|
|
1577
1607
|
logs: logsCommand,
|
|
1578
1608
|
"test-run": testRunCommand
|
|
1579
1609
|
},
|
|
1580
1610
|
async run() {
|
|
1581
|
-
await runCommand(listCommand$
|
|
1611
|
+
await runCommand(listCommand$8, []);
|
|
1582
1612
|
}
|
|
1583
1613
|
});
|
|
1584
1614
|
|
|
@@ -1587,7 +1617,7 @@ const functionCommand = defineCommand({
|
|
|
1587
1617
|
const generateCommand = defineAppCommand({
|
|
1588
1618
|
name: "generate",
|
|
1589
1619
|
description: "Generate files using Tailor configuration.",
|
|
1590
|
-
args: z.
|
|
1620
|
+
args: z.strictObject({
|
|
1591
1621
|
config: arg(z.string().default("tailor.config.ts"), {
|
|
1592
1622
|
alias: "c",
|
|
1593
1623
|
description: "Path to SDK config file"
|
|
@@ -1596,9 +1626,9 @@ const generateCommand = defineAppCommand({
|
|
|
1596
1626
|
alias: "W",
|
|
1597
1627
|
description: "Watch for type/resolver changes and regenerate"
|
|
1598
1628
|
})
|
|
1599
|
-
})
|
|
1629
|
+
}),
|
|
1600
1630
|
run: async (args) => {
|
|
1601
|
-
const { initTelemetry } = await import("../telemetry-
|
|
1631
|
+
const { initTelemetry } = await import("../telemetry-CkbkeJxl.mjs");
|
|
1602
1632
|
await initTelemetry();
|
|
1603
1633
|
await generate({
|
|
1604
1634
|
configPath: args.config,
|
|
@@ -1625,7 +1655,7 @@ const detectPackageManager$1 = () => {
|
|
|
1625
1655
|
const initCommand = defineAppCommand({
|
|
1626
1656
|
name: "init",
|
|
1627
1657
|
description: "Initialize a new project using create-sdk.",
|
|
1628
|
-
args: z.
|
|
1658
|
+
args: z.strictObject({
|
|
1629
1659
|
name: arg(z.string().optional(), {
|
|
1630
1660
|
positional: true,
|
|
1631
1661
|
description: "Project name"
|
|
@@ -1634,7 +1664,7 @@ const initCommand = defineAppCommand({
|
|
|
1634
1664
|
alias: "t",
|
|
1635
1665
|
description: "Template name"
|
|
1636
1666
|
})
|
|
1637
|
-
})
|
|
1667
|
+
}),
|
|
1638
1668
|
run: async (args) => {
|
|
1639
1669
|
const packageJson = await readPackageJson();
|
|
1640
1670
|
const version = packageJson.version && packageJson.version !== "0.0.0" ? packageJson.version : "latest";
|
|
@@ -1662,8 +1692,15 @@ const redirectUri = `http://localhost:${redirectPort}/callback`;
|
|
|
1662
1692
|
function randomState() {
|
|
1663
1693
|
return crypto.randomBytes(32).toString("base64url");
|
|
1664
1694
|
}
|
|
1665
|
-
|
|
1666
|
-
|
|
1695
|
+
function assertProfileLoginUser(args, authenticatedUser, authenticatedSubject) {
|
|
1696
|
+
if (args.profile && args.profileUser && authenticatedUser !== args.profileUser && authenticatedSubject !== args.profileUser) throw new Error(`Profile "${args.profile}" is configured for "${args.profileUser}", but login authenticated "${authenticatedUser}".`);
|
|
1697
|
+
}
|
|
1698
|
+
function shouldUpdateCurrentUser(profile, platformConfig) {
|
|
1699
|
+
if (!profile) return true;
|
|
1700
|
+
return isDefaultPlatform(platformConfig);
|
|
1701
|
+
}
|
|
1702
|
+
const startAuthServer = async (args = {}) => {
|
|
1703
|
+
const client = initOAuth2Client(args.platformConfig);
|
|
1667
1704
|
const state = randomState();
|
|
1668
1705
|
const codeVerifier = await generateCodeVerifier();
|
|
1669
1706
|
return new Promise((resolve, reject) => {
|
|
@@ -1675,14 +1712,18 @@ const startAuthServer = async () => {
|
|
|
1675
1712
|
state,
|
|
1676
1713
|
codeVerifier
|
|
1677
1714
|
});
|
|
1678
|
-
const userInfo = await fetchUserInfo(tokens.accessToken);
|
|
1715
|
+
const userInfo = await fetchUserInfo(tokens.accessToken, args.platformConfig);
|
|
1716
|
+
assertProfileLoginUser(args, userInfo.email, userInfo.sub);
|
|
1679
1717
|
const pfConfig = await readPlatformConfig();
|
|
1680
1718
|
await saveUserTokens(pfConfig, userInfo.sub, {
|
|
1681
1719
|
accessToken: tokens.accessToken,
|
|
1682
1720
|
refreshToken: tokens.refreshToken ?? void 0
|
|
1683
|
-
}, new Date(assertDefined(tokens.expiresAt, "token response missing expiresAt")).toISOString(), {
|
|
1684
|
-
|
|
1685
|
-
|
|
1721
|
+
}, new Date(assertDefined(tokens.expiresAt, "token response missing expiresAt")).toISOString(), {
|
|
1722
|
+
platformConfig: args.platformConfig,
|
|
1723
|
+
email: userInfo.email
|
|
1724
|
+
});
|
|
1725
|
+
if (args.updateCurrentUser ?? true) pfConfig.current_user = userInfo.sub;
|
|
1726
|
+
await removeLegacyUserAlias(pfConfig, userInfo.email, userInfo.sub, args.platformConfig);
|
|
1686
1727
|
writePlatformConfig(pfConfig);
|
|
1687
1728
|
res.writeHead(200, { "Content-Type": "application/json" });
|
|
1688
1729
|
res.end(JSON.stringify({
|
|
@@ -1724,17 +1765,22 @@ const startAuthServer = async () => {
|
|
|
1724
1765
|
});
|
|
1725
1766
|
};
|
|
1726
1767
|
async function loginAsMachineUser(args) {
|
|
1768
|
+
assertProfileLoginUser(args, args.clientId);
|
|
1727
1769
|
const clientSecret = args.clientSecret ?? await prompt.password({ message: "Client secret" });
|
|
1728
|
-
const tokens = await fetchPlatformMachineUserToken(args.clientId, clientSecret);
|
|
1770
|
+
const tokens = await fetchPlatformMachineUserToken(args.clientId, clientSecret, args.platformConfig);
|
|
1729
1771
|
const pfConfig = await readPlatformConfig();
|
|
1730
|
-
await saveUserTokens(pfConfig, args.clientId, { accessToken: tokens.accessToken }, new Date(assertDefined(tokens.expiresAt, "token response missing expiresAt")).toISOString());
|
|
1731
|
-
pfConfig.current_user = args.clientId;
|
|
1772
|
+
await saveUserTokens(pfConfig, args.clientId, { accessToken: tokens.accessToken }, new Date(assertDefined(tokens.expiresAt, "token response missing expiresAt")).toISOString(), { platformConfig: args.platformConfig });
|
|
1773
|
+
if (args.updateCurrentUser ?? true) pfConfig.current_user = args.clientId;
|
|
1732
1774
|
writePlatformConfig(pfConfig);
|
|
1733
1775
|
}
|
|
1734
1776
|
const loginCommand = defineAppCommand({
|
|
1735
1777
|
name: "login",
|
|
1736
1778
|
description: "Login to Tailor Platform.",
|
|
1737
|
-
args: z.xor([z.
|
|
1779
|
+
args: z.xor([z.strictObject({ profile: arg(z.string().optional(), {
|
|
1780
|
+
alias: "p",
|
|
1781
|
+
description: "Workspace profile whose platform settings should be used for login.",
|
|
1782
|
+
env: "TAILOR_PLATFORM_PROFILE"
|
|
1783
|
+
}) }).describe("User Login"), z.strictObject({
|
|
1738
1784
|
"machine-user": arg(z.literal(true), {
|
|
1739
1785
|
description: "Login as a platform machine user.",
|
|
1740
1786
|
required: true
|
|
@@ -1747,14 +1793,37 @@ const loginCommand = defineAppCommand({
|
|
|
1747
1793
|
"client-secret": arg(z.string().optional(), {
|
|
1748
1794
|
description: "Client secret",
|
|
1749
1795
|
env: "TAILOR_PLATFORM_MACHINE_USER_CLIENT_SECRET"
|
|
1796
|
+
}),
|
|
1797
|
+
profile: arg(z.string().optional(), {
|
|
1798
|
+
alias: "p",
|
|
1799
|
+
description: "Workspace profile whose platform settings should be used for login.",
|
|
1800
|
+
env: "TAILOR_PLATFORM_PROFILE"
|
|
1750
1801
|
})
|
|
1751
|
-
}).
|
|
1802
|
+
}).describe("Machine User Login")]),
|
|
1752
1803
|
run: async (args) => {
|
|
1804
|
+
let platformConfig;
|
|
1805
|
+
let profileUser;
|
|
1806
|
+
if ("profile" in args && args.profile) {
|
|
1807
|
+
const profileEntry = (await readPlatformConfig()).profiles[args.profile];
|
|
1808
|
+
if (!profileEntry) throw new Error(`Profile "${args.profile}" not found`);
|
|
1809
|
+
platformConfig = platformConfigFromProfile(profileEntry);
|
|
1810
|
+
profileUser = profileEntry.user;
|
|
1811
|
+
}
|
|
1812
|
+
const updateCurrentUser = shouldUpdateCurrentUser(args.profile, platformConfig);
|
|
1753
1813
|
if ("machine-user" in args) await loginAsMachineUser({
|
|
1754
1814
|
clientId: args.clientId,
|
|
1755
|
-
clientSecret: args.clientSecret
|
|
1815
|
+
clientSecret: args.clientSecret,
|
|
1816
|
+
profile: args.profile,
|
|
1817
|
+
profileUser,
|
|
1818
|
+
platformConfig,
|
|
1819
|
+
updateCurrentUser
|
|
1820
|
+
});
|
|
1821
|
+
else await startAuthServer({
|
|
1822
|
+
profile: args.profile,
|
|
1823
|
+
profileUser,
|
|
1824
|
+
platformConfig,
|
|
1825
|
+
updateCurrentUser
|
|
1756
1826
|
});
|
|
1757
|
-
else await startAuthServer();
|
|
1758
1827
|
logger.success("Successfully logged in to Tailor Platform.");
|
|
1759
1828
|
await closeConnectionPool();
|
|
1760
1829
|
}
|
|
@@ -1765,30 +1834,53 @@ const loginCommand = defineAppCommand({
|
|
|
1765
1834
|
const logoutCommand = defineAppCommand({
|
|
1766
1835
|
name: "logout",
|
|
1767
1836
|
description: "Logout from Tailor Platform.",
|
|
1768
|
-
args: z.
|
|
1769
|
-
|
|
1837
|
+
args: z.strictObject({ profile: arg(z.string().optional(), {
|
|
1838
|
+
alias: "p",
|
|
1839
|
+
description: "Workspace profile whose platform settings should be used for logout.",
|
|
1840
|
+
env: "TAILOR_PLATFORM_PROFILE"
|
|
1841
|
+
}) }),
|
|
1842
|
+
run: async (args) => {
|
|
1843
|
+
const profile = args.profile || process.env.TAILOR_PLATFORM_PROFILE;
|
|
1770
1844
|
const pfConfig = await readPlatformConfig();
|
|
1771
|
-
const
|
|
1772
|
-
|
|
1773
|
-
|
|
1845
|
+
const profileEntry = profile ? pfConfig.profiles[profile] : void 0;
|
|
1846
|
+
if (profile && !profileEntry) throw new Error(`Profile "${profile}" not found`);
|
|
1847
|
+
const platformConfig = profileEntry ? platformConfigFromProfile(profileEntry) : void 0;
|
|
1848
|
+
const currentUser = profileEntry ? profileEntry.user : pfConfig.current_user;
|
|
1849
|
+
const deletesDefaultToken = isDefaultPlatform(platformConfig);
|
|
1850
|
+
const lookupOptions = profile ? { allowLegacyUserKey: true } : void 0;
|
|
1851
|
+
if (!currentUser) {
|
|
1774
1852
|
logger.info("You are not logged in.");
|
|
1775
1853
|
return;
|
|
1776
1854
|
}
|
|
1855
|
+
const hasDefaultUserToken = () => hasUserTokenEntry(pfConfig, currentUser, { platformUrl: defaultPlatformBaseUrl });
|
|
1856
|
+
const shouldClearCurrentUser = () => pfConfig.current_user === currentUser && (deletesDefaultToken ? !hasDefaultUserToken() : !hasAnyUserTokenEntry(pfConfig, currentUser) && !hasDefaultUserToken());
|
|
1857
|
+
let storedTokens;
|
|
1858
|
+
let tokenLoadFailed = false;
|
|
1777
1859
|
try {
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1860
|
+
storedTokens = await loadStoredUserTokens(pfConfig, currentUser, platformConfig, lookupOptions);
|
|
1861
|
+
} catch (error) {
|
|
1862
|
+
tokenLoadFailed = true;
|
|
1863
|
+
logger.warn(`Failed to revoke token: ${error instanceof Error ? error.message : error}`);
|
|
1864
|
+
}
|
|
1865
|
+
if (!storedTokens && !tokenLoadFailed) {
|
|
1866
|
+
logger.info("You are not logged in.");
|
|
1867
|
+
if (shouldClearCurrentUser()) pfConfig.current_user = null;
|
|
1868
|
+
writePlatformConfig(pfConfig);
|
|
1869
|
+
return;
|
|
1870
|
+
}
|
|
1871
|
+
if (storedTokens) try {
|
|
1872
|
+
const client = initOAuth2Client(platformConfig);
|
|
1873
|
+
const tokenTypeHint = storedTokens.refreshToken ? "refresh_token" : "access_token";
|
|
1781
1874
|
await client.revoke({
|
|
1782
|
-
accessToken,
|
|
1783
|
-
refreshToken: refreshToken ?? null,
|
|
1784
|
-
expiresAt: Date.parse(userEntry.token_expires_at)
|
|
1875
|
+
accessToken: storedTokens.accessToken,
|
|
1876
|
+
refreshToken: storedTokens.refreshToken ?? null,
|
|
1877
|
+
expiresAt: Date.parse(storedTokens.userEntry.token_expires_at)
|
|
1785
1878
|
}, tokenTypeHint);
|
|
1786
1879
|
} catch (error) {
|
|
1787
1880
|
logger.warn(`Failed to revoke token: ${error instanceof Error ? error.message : error}`);
|
|
1788
1881
|
}
|
|
1789
|
-
await deleteUserTokens(pfConfig, currentUser);
|
|
1790
|
-
|
|
1791
|
-
pfConfig.current_user = null;
|
|
1882
|
+
await deleteUserTokens(pfConfig, currentUser, platformConfig, lookupOptions);
|
|
1883
|
+
if (shouldClearCurrentUser()) pfConfig.current_user = null;
|
|
1792
1884
|
writePlatformConfig(pfConfig);
|
|
1793
1885
|
logger.success("Successfully logged out from Tailor Platform.");
|
|
1794
1886
|
}
|
|
@@ -1800,11 +1892,11 @@ const machineuserCommand = defineCommand({
|
|
|
1800
1892
|
name: "machineuser",
|
|
1801
1893
|
description: "Manage machine users in your Tailor Platform application.",
|
|
1802
1894
|
subCommands: {
|
|
1803
|
-
list: listCommand$
|
|
1804
|
-
token: tokenCommand
|
|
1895
|
+
list: listCommand$9,
|
|
1896
|
+
token: tokenCommand$1
|
|
1805
1897
|
},
|
|
1806
1898
|
async run() {
|
|
1807
|
-
await runCommand(listCommand$
|
|
1899
|
+
await runCommand(listCommand$9, []);
|
|
1808
1900
|
}
|
|
1809
1901
|
});
|
|
1810
1902
|
|
|
@@ -1814,21 +1906,20 @@ const oauth2clientCommand = defineCommand({
|
|
|
1814
1906
|
name: "oauth2client",
|
|
1815
1907
|
description: "Manage OAuth2 clients in your Tailor Platform application.",
|
|
1816
1908
|
subCommands: {
|
|
1817
|
-
list: listCommand$
|
|
1909
|
+
list: listCommand$10,
|
|
1818
1910
|
get: getCommand$3
|
|
1819
1911
|
},
|
|
1820
1912
|
async run() {
|
|
1821
|
-
await runCommand(listCommand$
|
|
1913
|
+
await runCommand(listCommand$10, []);
|
|
1822
1914
|
}
|
|
1823
1915
|
});
|
|
1824
1916
|
|
|
1825
1917
|
//#endregion
|
|
1826
1918
|
//#region src/cli/commands/open.ts
|
|
1827
|
-
const consoleBaseUrl = "https://console.tailor.tech";
|
|
1828
1919
|
const openCommand = defineAppCommand({
|
|
1829
1920
|
name: "open",
|
|
1830
1921
|
description: "Open Tailor Platform Console.",
|
|
1831
|
-
args: z.
|
|
1922
|
+
args: z.strictObject({ ...deploymentArgs }),
|
|
1832
1923
|
run: async (args) => {
|
|
1833
1924
|
const workspaceId = await loadWorkspaceId({
|
|
1834
1925
|
workspaceId: args["workspace-id"],
|
|
@@ -1837,6 +1928,10 @@ const openCommand = defineAppCommand({
|
|
|
1837
1928
|
const { config } = await loadConfig(args.config);
|
|
1838
1929
|
const applicationName = config.name;
|
|
1839
1930
|
const consolePath = `/workspaces/${workspaceId}/applications/${encodeURIComponent(applicationName)}/overview`;
|
|
1931
|
+
const consoleBaseUrl = await loadConsoleBaseUrl({
|
|
1932
|
+
profile: args.profile,
|
|
1933
|
+
...args["workspace-id"] !== void 0 ? { allowMissingProfile: true } : {}
|
|
1934
|
+
});
|
|
1840
1935
|
const consoleUrl = new URL(consolePath, consoleBaseUrl).toString();
|
|
1841
1936
|
const jsonOutput = logger.jsonMode;
|
|
1842
1937
|
logger.info("Opening Tailor Platform Console...");
|
|
@@ -1870,9 +1965,9 @@ const folderCommand = defineCommand({
|
|
|
1870
1965
|
description: "Manage organization folders.",
|
|
1871
1966
|
subCommands: {
|
|
1872
1967
|
create: createCommand$3,
|
|
1873
|
-
delete: deleteCommand$
|
|
1968
|
+
delete: deleteCommand$4,
|
|
1874
1969
|
get: getCommand$4,
|
|
1875
|
-
list: listCommand$
|
|
1970
|
+
list: listCommand$11,
|
|
1876
1971
|
update: updateCommand$2
|
|
1877
1972
|
}
|
|
1878
1973
|
});
|
|
@@ -1885,12 +1980,399 @@ const organizationCommand = defineCommand({
|
|
|
1885
1980
|
subCommands: {
|
|
1886
1981
|
folder: folderCommand,
|
|
1887
1982
|
get: getCommand$5,
|
|
1888
|
-
list: listCommand$
|
|
1983
|
+
list: listCommand$12,
|
|
1889
1984
|
tree: treeCommand,
|
|
1890
1985
|
update: updateCommand$3
|
|
1891
1986
|
},
|
|
1892
1987
|
async run() {
|
|
1893
|
-
await runCommand(listCommand$
|
|
1988
|
+
await runCommand(listCommand$12, []);
|
|
1989
|
+
}
|
|
1990
|
+
});
|
|
1991
|
+
|
|
1992
|
+
//#endregion
|
|
1993
|
+
//#region src/cli/shared/builtin-commands.ts
|
|
1994
|
+
/**
|
|
1995
|
+
* Top-level builtin command names. A plugin named the same as one of these is
|
|
1996
|
+
* shadowed by the builtin and can never be dispatched.
|
|
1997
|
+
*
|
|
1998
|
+
* This list is the single source of truth used by `plugin list` to flag
|
|
1999
|
+
* shadowed plugins without importing the (cyclic) command tree. It is kept in
|
|
2000
|
+
* sync with `main-command.ts` by a drift test in `options.test.ts`.
|
|
2001
|
+
*/
|
|
2002
|
+
const BUILTIN_COMMAND_NAMES = [
|
|
2003
|
+
"api",
|
|
2004
|
+
"auth",
|
|
2005
|
+
"authconnection",
|
|
2006
|
+
"crashreport",
|
|
2007
|
+
"deploy",
|
|
2008
|
+
"executor",
|
|
2009
|
+
"function",
|
|
2010
|
+
"generate",
|
|
2011
|
+
"init",
|
|
2012
|
+
"login",
|
|
2013
|
+
"logout",
|
|
2014
|
+
"machineuser",
|
|
2015
|
+
"oauth2client",
|
|
2016
|
+
"open",
|
|
2017
|
+
"organization",
|
|
2018
|
+
"plugin",
|
|
2019
|
+
"profile",
|
|
2020
|
+
"query",
|
|
2021
|
+
"remove",
|
|
2022
|
+
"secret",
|
|
2023
|
+
"setup",
|
|
2024
|
+
"show",
|
|
2025
|
+
"skills",
|
|
2026
|
+
"staticwebsite",
|
|
2027
|
+
"tailordb",
|
|
2028
|
+
"upgrade",
|
|
2029
|
+
"user",
|
|
2030
|
+
"workflow",
|
|
2031
|
+
"workspace"
|
|
2032
|
+
];
|
|
2033
|
+
|
|
2034
|
+
//#endregion
|
|
2035
|
+
//#region src/cli/shared/plugin.ts
|
|
2036
|
+
/** PATH entry separator (`;` on Windows, `:` elsewhere). */
|
|
2037
|
+
const pathDelimiter = process.platform === "win32" ? ";" : ":";
|
|
2038
|
+
/**
|
|
2039
|
+
* Yield `start` and each ancestor directory up to the filesystem root.
|
|
2040
|
+
* @param start - Directory to start from
|
|
2041
|
+
* @yields Each ancestor directory, starting with `start`
|
|
2042
|
+
*/
|
|
2043
|
+
function* ancestorDirs(start) {
|
|
2044
|
+
let dir = path.resolve(start);
|
|
2045
|
+
let parent = path.dirname(dir);
|
|
2046
|
+
while (parent !== dir) {
|
|
2047
|
+
yield dir;
|
|
2048
|
+
dir = parent;
|
|
2049
|
+
parent = path.dirname(dir);
|
|
2050
|
+
}
|
|
2051
|
+
yield dir;
|
|
2052
|
+
}
|
|
2053
|
+
/** Fallback executable extensions when `PATHEXT` is unset (Windows). */
|
|
2054
|
+
const DEFAULT_PATHEXT = ".COM;.EXE;.BAT;.CMD;.PS1";
|
|
2055
|
+
/**
|
|
2056
|
+
* Lowercased executable extensions from `PATHEXT` (Windows).
|
|
2057
|
+
* @returns Recognized executable extensions (e.g. `.exe`, `.cmd`)
|
|
2058
|
+
*/
|
|
2059
|
+
function windowsExecutableExts() {
|
|
2060
|
+
return (process.env.PATHEXT ?? DEFAULT_PATHEXT).split(";").map((e) => e.trim().toLowerCase()).filter(Boolean);
|
|
2061
|
+
}
|
|
2062
|
+
/**
|
|
2063
|
+
* Check whether a path exists and is runnable: X_OK on POSIX; on Windows, the
|
|
2064
|
+
* file must exist and either be extension-less (shebang script) or carry a
|
|
2065
|
+
* `PATHEXT` extension, so plain data files are not mistaken for plugins.
|
|
2066
|
+
* @param filePath - Path to check
|
|
2067
|
+
* @returns Whether the path is an executable file
|
|
2068
|
+
*/
|
|
2069
|
+
function isExecutable(filePath) {
|
|
2070
|
+
try {
|
|
2071
|
+
accessSync(filePath, process.platform === "win32" ? constants.F_OK : constants.X_OK);
|
|
2072
|
+
} catch {
|
|
2073
|
+
return false;
|
|
2074
|
+
}
|
|
2075
|
+
if (process.platform === "win32") {
|
|
2076
|
+
const ext = path.extname(filePath).toLowerCase();
|
|
2077
|
+
return ext === "" || windowsExecutableExts().includes(ext);
|
|
2078
|
+
}
|
|
2079
|
+
return true;
|
|
2080
|
+
}
|
|
2081
|
+
/**
|
|
2082
|
+
* Candidate file names for a plugin binary, accounting for Windows extensions.
|
|
2083
|
+
* @param binName - Base binary name (e.g. `tailor-foo`)
|
|
2084
|
+
* @returns Ordered candidate file names
|
|
2085
|
+
*/
|
|
2086
|
+
function binaryCandidates(binName) {
|
|
2087
|
+
if (process.platform !== "win32") return [binName];
|
|
2088
|
+
return [binName, ...windowsExecutableExts().map((ext) => `${binName}${ext}`)];
|
|
2089
|
+
}
|
|
2090
|
+
/**
|
|
2091
|
+
* Directories on the user's PATH.
|
|
2092
|
+
* @returns PATH entries
|
|
2093
|
+
*/
|
|
2094
|
+
function pathDirs() {
|
|
2095
|
+
return (process.env.PATH ?? "").split(pathDelimiter).filter(Boolean);
|
|
2096
|
+
}
|
|
2097
|
+
/**
|
|
2098
|
+
* Nearest `node_modules/.bin` directories, walking up from the current working
|
|
2099
|
+
* directory. Project-local bins take precedence over those higher in the tree.
|
|
2100
|
+
* @returns Ordered `node_modules/.bin` directories
|
|
2101
|
+
*/
|
|
2102
|
+
function nodeModulesBinDirs() {
|
|
2103
|
+
const dirs = [];
|
|
2104
|
+
for (const dir of ancestorDirs(process.cwd())) dirs.push(path.join(dir, "node_modules", ".bin"));
|
|
2105
|
+
return dirs;
|
|
2106
|
+
}
|
|
2107
|
+
/**
|
|
2108
|
+
* Find the first existing executable for the given base name across a set of
|
|
2109
|
+
* directories.
|
|
2110
|
+
* @param dirs - Directories to search
|
|
2111
|
+
* @param binName - Base binary name
|
|
2112
|
+
* @returns Absolute path, or undefined when not found
|
|
2113
|
+
*/
|
|
2114
|
+
function findExecutableIn(dirs, binName) {
|
|
2115
|
+
const candidates = binaryCandidates(binName);
|
|
2116
|
+
for (const dir of dirs) for (const candidate of candidates) {
|
|
2117
|
+
const full = path.join(dir, candidate);
|
|
2118
|
+
if (isExecutable(full)) return full;
|
|
2119
|
+
}
|
|
2120
|
+
}
|
|
2121
|
+
/**
|
|
2122
|
+
* Resolve a plugin executable by name.
|
|
2123
|
+
* Search order: project-local `node_modules/.bin` (nearest first), then PATH.
|
|
2124
|
+
* @param name - Plugin name (without the `<cli>-` prefix)
|
|
2125
|
+
* @param cliName - The host CLI name (e.g. `tailor`)
|
|
2126
|
+
* @returns The discovered plugin, or null when not found
|
|
2127
|
+
*/
|
|
2128
|
+
function resolvePlugin(name, cliName) {
|
|
2129
|
+
if (name.includes("/") || name.includes("\\") || name.includes("\0")) return null;
|
|
2130
|
+
const binName = `${cliName}-${name}`;
|
|
2131
|
+
const fromNodeModules = findExecutableIn(nodeModulesBinDirs(), binName);
|
|
2132
|
+
if (fromNodeModules) return {
|
|
2133
|
+
name,
|
|
2134
|
+
path: fromNodeModules,
|
|
2135
|
+
source: "node_modules"
|
|
2136
|
+
};
|
|
2137
|
+
const fromPath = findExecutableIn(pathDirs(), binName);
|
|
2138
|
+
if (fromPath) return {
|
|
2139
|
+
name,
|
|
2140
|
+
path: fromPath,
|
|
2141
|
+
source: "path"
|
|
2142
|
+
};
|
|
2143
|
+
return null;
|
|
2144
|
+
}
|
|
2145
|
+
/**
|
|
2146
|
+
* Strip a known binary extension from a file name (Windows).
|
|
2147
|
+
* @param fileName - File name
|
|
2148
|
+
* @returns File name without a recognized executable extension
|
|
2149
|
+
*/
|
|
2150
|
+
function stripBinExt(fileName) {
|
|
2151
|
+
if (process.platform !== "win32") return fileName;
|
|
2152
|
+
const ext = path.extname(fileName);
|
|
2153
|
+
return ext ? fileName.slice(0, -ext.length) : fileName;
|
|
2154
|
+
}
|
|
2155
|
+
/**
|
|
2156
|
+
* Discover all plugins reachable from `node_modules/.bin` and PATH.
|
|
2157
|
+
* Earlier entries win on name collisions (project-local over PATH).
|
|
2158
|
+
* @param cliName - The host CLI name (e.g. `tailor`)
|
|
2159
|
+
* @returns Discovered plugins, deduped by name
|
|
2160
|
+
*/
|
|
2161
|
+
function listPlugins(cliName) {
|
|
2162
|
+
const prefix = `${cliName}-`;
|
|
2163
|
+
const seen = /* @__PURE__ */ new Map();
|
|
2164
|
+
const scan = (dirs, source) => {
|
|
2165
|
+
for (const dir of dirs) {
|
|
2166
|
+
let entries;
|
|
2167
|
+
try {
|
|
2168
|
+
entries = readdirSync(dir);
|
|
2169
|
+
} catch {
|
|
2170
|
+
continue;
|
|
2171
|
+
}
|
|
2172
|
+
for (const entry of entries) {
|
|
2173
|
+
const base = stripBinExt(entry);
|
|
2174
|
+
if (!base.startsWith(prefix) || base.length === prefix.length) continue;
|
|
2175
|
+
const name = base.slice(prefix.length);
|
|
2176
|
+
if (seen.has(name)) continue;
|
|
2177
|
+
const full = path.join(dir, entry);
|
|
2178
|
+
if (!isExecutable(full)) continue;
|
|
2179
|
+
seen.set(name, {
|
|
2180
|
+
name,
|
|
2181
|
+
path: full,
|
|
2182
|
+
source
|
|
2183
|
+
});
|
|
2184
|
+
}
|
|
2185
|
+
}
|
|
2186
|
+
};
|
|
2187
|
+
scan(nodeModulesBinDirs(), "node_modules");
|
|
2188
|
+
scan(pathDirs(), "path");
|
|
2189
|
+
return [...seen.values()];
|
|
2190
|
+
}
|
|
2191
|
+
/**
|
|
2192
|
+
* Resolve the active user identifier, preferring the stored email over the
|
|
2193
|
+
* internal user key.
|
|
2194
|
+
* @param profile - Active profile name, if any
|
|
2195
|
+
* @returns The resolved user (email when known), or undefined when not logged in
|
|
2196
|
+
*/
|
|
2197
|
+
async function resolveActiveUser(profile) {
|
|
2198
|
+
const config = await readPlatformConfig();
|
|
2199
|
+
const user = profile ? config.profiles[profile]?.user : config.current_user;
|
|
2200
|
+
if (!user) return void 0;
|
|
2201
|
+
return config.users[user]?.email ?? user;
|
|
2202
|
+
}
|
|
2203
|
+
/**
|
|
2204
|
+
* Build the environment variables injected into a dispatched plugin.
|
|
2205
|
+
* Workspace, user, and token are resolved best-effort: whichever the current
|
|
2206
|
+
* context can provide is injected, and the rest are omitted so auth-free
|
|
2207
|
+
* plugins still run.
|
|
2208
|
+
* @param options - Plugin context options
|
|
2209
|
+
* @returns Environment variables to merge into the child process env
|
|
2210
|
+
*/
|
|
2211
|
+
async function buildPluginEnv(options = {}) {
|
|
2212
|
+
const { profile } = options;
|
|
2213
|
+
let platformConfig;
|
|
2214
|
+
try {
|
|
2215
|
+
platformConfig = await loadPlatformClientConfig({
|
|
2216
|
+
profile,
|
|
2217
|
+
allowMissingProfile: true
|
|
2218
|
+
});
|
|
2219
|
+
} catch {}
|
|
2220
|
+
const env = {
|
|
2221
|
+
TAILOR_PLATFORM_URL: getPlatformBaseUrl(platformConfig),
|
|
2222
|
+
TAILOR_PLATFORM_OAUTH2_CLIENT_ID: getOAuth2ClientId(platformConfig)
|
|
2223
|
+
};
|
|
2224
|
+
const binPath = process.argv[1];
|
|
2225
|
+
if (binPath) env.TAILOR_BIN = binPath;
|
|
2226
|
+
try {
|
|
2227
|
+
const { version } = await readPackageJson();
|
|
2228
|
+
if (version) env.TAILOR_VERSION = version;
|
|
2229
|
+
} catch {}
|
|
2230
|
+
const configPath = loadConfigPath();
|
|
2231
|
+
if (configPath) env.TAILOR_CONFIG_PATH = configPath;
|
|
2232
|
+
try {
|
|
2233
|
+
env.TAILOR_PLATFORM_WORKSPACE_ID = await loadWorkspaceId({ profile });
|
|
2234
|
+
} catch {}
|
|
2235
|
+
try {
|
|
2236
|
+
const user = await resolveActiveUser(profile);
|
|
2237
|
+
if (user) env.TAILOR_PLATFORM_USER = user;
|
|
2238
|
+
} catch {}
|
|
2239
|
+
try {
|
|
2240
|
+
const token = await loadAccessToken({ profile });
|
|
2241
|
+
if (token) env.TAILOR_PLATFORM_TOKEN = token;
|
|
2242
|
+
} catch {}
|
|
2243
|
+
return env;
|
|
2244
|
+
}
|
|
2245
|
+
/**
|
|
2246
|
+
* Map an exit signal to a conventional exit code (128 + signal number).
|
|
2247
|
+
* @param signal - Signal name
|
|
2248
|
+
* @returns Exit code
|
|
2249
|
+
*/
|
|
2250
|
+
function signalToExitCode(signal) {
|
|
2251
|
+
const num = os.constants.signals[signal];
|
|
2252
|
+
return typeof num === "number" ? 128 + num : 1;
|
|
2253
|
+
}
|
|
2254
|
+
/**
|
|
2255
|
+
* Build the command + args for spawning a plugin, handling Windows shebang
|
|
2256
|
+
* scripts via `sh` (following the `gh` extension dispatcher).
|
|
2257
|
+
* @param pluginPath - Resolved plugin executable path
|
|
2258
|
+
* @param args - Args to forward to the plugin
|
|
2259
|
+
* @returns Spawn command, args, and whether a shell is required
|
|
2260
|
+
*/
|
|
2261
|
+
function buildSpawnTarget(pluginPath, args) {
|
|
2262
|
+
if (process.platform !== "win32") return {
|
|
2263
|
+
command: pluginPath,
|
|
2264
|
+
args: [...args],
|
|
2265
|
+
shell: false
|
|
2266
|
+
};
|
|
2267
|
+
const ext = path.extname(pluginPath).toLowerCase();
|
|
2268
|
+
if (ext === ".exe" || ext === ".com") return {
|
|
2269
|
+
command: pluginPath,
|
|
2270
|
+
args: [...args],
|
|
2271
|
+
shell: false
|
|
2272
|
+
};
|
|
2273
|
+
if (ext === ".cmd" || ext === ".bat") return {
|
|
2274
|
+
command: pluginPath,
|
|
2275
|
+
args: [...args],
|
|
2276
|
+
shell: true
|
|
2277
|
+
};
|
|
2278
|
+
if (ext === ".ps1") return {
|
|
2279
|
+
command: findExecutableIn(pathDirs(), "pwsh") ?? findExecutableIn(pathDirs(), "powershell") ?? "powershell",
|
|
2280
|
+
args: [
|
|
2281
|
+
"-NoProfile",
|
|
2282
|
+
"-ExecutionPolicy",
|
|
2283
|
+
"Bypass",
|
|
2284
|
+
"-File",
|
|
2285
|
+
pluginPath,
|
|
2286
|
+
...args
|
|
2287
|
+
],
|
|
2288
|
+
shell: false
|
|
2289
|
+
};
|
|
2290
|
+
return {
|
|
2291
|
+
command: findExecutableIn(pathDirs(), "sh") ?? "sh",
|
|
2292
|
+
args: [
|
|
2293
|
+
"-c",
|
|
2294
|
+
"command \"$@\"",
|
|
2295
|
+
"--",
|
|
2296
|
+
pluginPath,
|
|
2297
|
+
...args
|
|
2298
|
+
],
|
|
2299
|
+
shell: false
|
|
2300
|
+
};
|
|
2301
|
+
}
|
|
2302
|
+
/**
|
|
2303
|
+
* Resolve and execute a plugin, forwarding stdio and propagating its exit code.
|
|
2304
|
+
* @param params - Dispatch parameters
|
|
2305
|
+
* @param params.name - Plugin name (without the `<cli>-` prefix)
|
|
2306
|
+
* @param params.args - Args to forward to the plugin
|
|
2307
|
+
* @param params.cliName - The host CLI name
|
|
2308
|
+
* @param params.profile - Active profile name, if any
|
|
2309
|
+
* @returns The plugin's exit code, or undefined when no matching plugin exists
|
|
2310
|
+
*/
|
|
2311
|
+
async function dispatchPlugin(params) {
|
|
2312
|
+
const slug = [...params.commandPath ?? [], params.name].join("-");
|
|
2313
|
+
const plugin = resolvePlugin(slug, params.cliName);
|
|
2314
|
+
if (!plugin) return;
|
|
2315
|
+
const env = {
|
|
2316
|
+
...process.env,
|
|
2317
|
+
...await buildPluginEnv({ profile: params.profile })
|
|
2318
|
+
};
|
|
2319
|
+
const { command, args, shell } = buildSpawnTarget(plugin.path, params.args);
|
|
2320
|
+
return await new Promise((resolve) => {
|
|
2321
|
+
const child = spawn(command, args, {
|
|
2322
|
+
stdio: "inherit",
|
|
2323
|
+
env,
|
|
2324
|
+
shell
|
|
2325
|
+
});
|
|
2326
|
+
child.on("error", (error) => {
|
|
2327
|
+
logger.error(`Failed to run plugin "${params.cliName}-${slug}": ${error.message}`);
|
|
2328
|
+
resolve(1);
|
|
2329
|
+
});
|
|
2330
|
+
child.on("close", (code, signal) => {
|
|
2331
|
+
if (signal) resolve(signalToExitCode(signal));
|
|
2332
|
+
else resolve(code ?? 0);
|
|
2333
|
+
});
|
|
2334
|
+
});
|
|
2335
|
+
}
|
|
2336
|
+
|
|
2337
|
+
//#endregion
|
|
2338
|
+
//#region src/cli/commands/plugin/list.ts
|
|
2339
|
+
const listCommand$5 = defineAppCommand({
|
|
2340
|
+
name: "list",
|
|
2341
|
+
description: "List discovered plugins (executables named `<cli>-<name>` on PATH or node_modules/.bin).",
|
|
2342
|
+
args: z.strictObject({}),
|
|
2343
|
+
run: async () => {
|
|
2344
|
+
const pkg = await readPackageJson();
|
|
2345
|
+
const cliName = Object.keys(pkg.bin ?? {})[0] || "tailor";
|
|
2346
|
+
const builtins = new Set(BUILTIN_COMMAND_NAMES);
|
|
2347
|
+
const plugins = listPlugins(cliName);
|
|
2348
|
+
if (plugins.length === 0) {
|
|
2349
|
+
logger.info(multiline`
|
|
2350
|
+
No plugins found.
|
|
2351
|
+
Install an executable named "${cliName}-<name>" on your PATH or in node_modules/.bin,
|
|
2352
|
+
then run it with "${cliName} <name>".
|
|
2353
|
+
`);
|
|
2354
|
+
if (logger.jsonMode) logger.out([]);
|
|
2355
|
+
return;
|
|
2356
|
+
}
|
|
2357
|
+
const items = plugins.map((plugin) => ({
|
|
2358
|
+
name: plugin.name,
|
|
2359
|
+
source: plugin.source,
|
|
2360
|
+
path: plugin.path,
|
|
2361
|
+
shadowed: builtins.has(plugin.name)
|
|
2362
|
+
}));
|
|
2363
|
+
logger.out(items);
|
|
2364
|
+
for (const item of items) if (item.shadowed) logger.warn(`Plugin "${cliName}-${item.name}" is shadowed by the builtin "${cliName} ${item.name}" command and will not be dispatched.`);
|
|
2365
|
+
}
|
|
2366
|
+
});
|
|
2367
|
+
|
|
2368
|
+
//#endregion
|
|
2369
|
+
//#region src/cli/commands/plugin/index.ts
|
|
2370
|
+
const pluginCommand = defineCommand({
|
|
2371
|
+
name: "plugin",
|
|
2372
|
+
description: "Manage and inspect CLI plugins (beta).",
|
|
2373
|
+
subCommands: { list: listCommand$5 },
|
|
2374
|
+
async run() {
|
|
2375
|
+
await runCommand(listCommand$5, []);
|
|
1894
2376
|
}
|
|
1895
2377
|
});
|
|
1896
2378
|
|
|
@@ -1899,7 +2381,7 @@ const organizationCommand = defineCommand({
|
|
|
1899
2381
|
const createCommand$2 = defineAppCommand({
|
|
1900
2382
|
name: "create",
|
|
1901
2383
|
description: "Create a new profile.",
|
|
1902
|
-
args: z.
|
|
2384
|
+
args: z.strictObject({
|
|
1903
2385
|
name: arg(z.string(), {
|
|
1904
2386
|
positional: true,
|
|
1905
2387
|
description: "Profile name"
|
|
@@ -1917,14 +2399,32 @@ const createCommand$2 = defineAppCommand({
|
|
|
1917
2399
|
alias: "m",
|
|
1918
2400
|
description: "Default machine user name for application-data commands (query, workflow start, function test-run, machineuser token)."
|
|
1919
2401
|
}),
|
|
1920
|
-
"machine-user-override": arg(z.enum(["allow", "deny"]).optional(), { description: "Whether the command line or TAILOR_PLATFORM_MACHINE_USER_NAME may override the profile's machine user. 'deny' requires --machine-user." })
|
|
1921
|
-
|
|
2402
|
+
"machine-user-override": arg(z.enum(["allow", "deny"]).optional(), { description: "Whether the command line or TAILOR_PLATFORM_MACHINE_USER_NAME may override the profile's machine user. 'deny' requires --machine-user." }),
|
|
2403
|
+
"platform-url": arg(z.url().optional(), {
|
|
2404
|
+
description: "Platform API base URL for this profile.",
|
|
2405
|
+
env: "TAILOR_PLATFORM_URL"
|
|
2406
|
+
}),
|
|
2407
|
+
"oauth2-client-id": arg(z.string().optional(), {
|
|
2408
|
+
description: "OAuth2 client ID for logging in to this profile's platform.",
|
|
2409
|
+
env: "TAILOR_PLATFORM_OAUTH2_CLIENT_ID"
|
|
2410
|
+
}),
|
|
2411
|
+
"console-url": arg(z.url().optional(), {
|
|
2412
|
+
description: "Console base URL for this profile.",
|
|
2413
|
+
env: "TAILOR_PLATFORM_CONSOLE_URL"
|
|
2414
|
+
})
|
|
2415
|
+
}),
|
|
1922
2416
|
run: async (args) => {
|
|
1923
2417
|
if (args["machine-user-override"] === "deny" && !args["machine-user"]) throw new Error("--machine-user-override deny requires --machine-user.");
|
|
1924
2418
|
const config = await readPlatformConfig();
|
|
1925
2419
|
if (config.profiles[args.name]) throw new Error(`Profile "${args.name}" already exists.`);
|
|
1926
|
-
const
|
|
1927
|
-
|
|
2420
|
+
const platformConfigInput = {
|
|
2421
|
+
...args["platform-url"] ? { platformUrl: args["platform-url"] } : {},
|
|
2422
|
+
...args["oauth2-client-id"] ? { oauth2ClientId: args["oauth2-client-id"] } : {},
|
|
2423
|
+
...args["console-url"] ? { consoleUrl: args["console-url"] } : {}
|
|
2424
|
+
};
|
|
2425
|
+
const platformConfig = Object.keys(platformConfigInput).length > 0 ? platformConfigInput : void 0;
|
|
2426
|
+
const { accessToken: token, user: resolvedUser } = await fetchLatestToken(config, args.user, platformConfig);
|
|
2427
|
+
const client = await initOperatorClient(token, platformConfig);
|
|
1928
2428
|
if (!(await fetchAll(async (pageToken, maxPageSize) => {
|
|
1929
2429
|
const { workspaces, nextPageToken } = await client.listWorkspaces({
|
|
1930
2430
|
pageToken,
|
|
@@ -1937,7 +2437,10 @@ const createCommand$2 = defineAppCommand({
|
|
|
1937
2437
|
workspace_id: args["workspace-id"],
|
|
1938
2438
|
...args.permission === "read" ? { readonly: true } : {},
|
|
1939
2439
|
...args["machine-user"] ? { machine_user: args["machine-user"] } : {},
|
|
1940
|
-
...args["machine-user-override"] === "deny" ? { machine_user_override: "deny" } : {}
|
|
2440
|
+
...args["machine-user-override"] === "deny" ? { machine_user_override: "deny" } : {},
|
|
2441
|
+
...args["platform-url"] ? { platform_url: args["platform-url"] } : {},
|
|
2442
|
+
...args["oauth2-client-id"] ? { oauth2_client_id: args["oauth2-client-id"] } : {},
|
|
2443
|
+
...args["console-url"] ? { console_url: args["console-url"] } : {}
|
|
1941
2444
|
};
|
|
1942
2445
|
writePlatformConfig(config);
|
|
1943
2446
|
if (!args.json) logger.success(`Profile "${args.name}" created successfully.`);
|
|
@@ -1949,7 +2452,10 @@ const createCommand$2 = defineAppCommand({
|
|
|
1949
2452
|
...args["machine-user"] ? {
|
|
1950
2453
|
machineUser: args["machine-user"],
|
|
1951
2454
|
machineUserOverride: args["machine-user-override"] ?? "allow"
|
|
1952
|
-
} : {}
|
|
2455
|
+
} : {},
|
|
2456
|
+
...args["platform-url"] ? { platformUrl: args["platform-url"] } : {},
|
|
2457
|
+
...args["oauth2-client-id"] ? { oauth2ClientId: args["oauth2-client-id"] } : {},
|
|
2458
|
+
...args["console-url"] ? { consoleUrl: args["console-url"] } : {}
|
|
1953
2459
|
};
|
|
1954
2460
|
logger.out(profileInfo);
|
|
1955
2461
|
}
|
|
@@ -1957,13 +2463,13 @@ const createCommand$2 = defineAppCommand({
|
|
|
1957
2463
|
|
|
1958
2464
|
//#endregion
|
|
1959
2465
|
//#region src/cli/commands/profile/delete.ts
|
|
1960
|
-
const deleteCommand$
|
|
2466
|
+
const deleteCommand$3 = defineAppCommand({
|
|
1961
2467
|
name: "delete",
|
|
1962
2468
|
description: "Delete a profile.",
|
|
1963
|
-
args: z.
|
|
2469
|
+
args: z.strictObject({ name: arg(z.string(), {
|
|
1964
2470
|
positional: true,
|
|
1965
2471
|
description: "Profile name"
|
|
1966
|
-
}) })
|
|
2472
|
+
}) }),
|
|
1967
2473
|
run: async (args) => {
|
|
1968
2474
|
const config = await readPlatformConfig();
|
|
1969
2475
|
if (!config.profiles[args.name]) throw new Error(`Profile "${args.name}" not found.`);
|
|
@@ -1978,7 +2484,7 @@ const deleteCommand$2 = defineAppCommand({
|
|
|
1978
2484
|
const listCommand$4 = defineAppCommand({
|
|
1979
2485
|
name: "list",
|
|
1980
2486
|
description: "List all profiles.",
|
|
1981
|
-
args: z.
|
|
2487
|
+
args: z.strictObject({}),
|
|
1982
2488
|
run: async () => {
|
|
1983
2489
|
const config = await readPlatformConfig();
|
|
1984
2490
|
const jsonOutput = logger.jsonMode;
|
|
@@ -1986,7 +2492,7 @@ const listCommand$4 = defineAppCommand({
|
|
|
1986
2492
|
if (profiles.length === 0) {
|
|
1987
2493
|
logger.info(multiline`
|
|
1988
2494
|
No profiles found.
|
|
1989
|
-
Please create a profile first using 'tailor
|
|
2495
|
+
Please create a profile first using 'tailor profile create' command.
|
|
1990
2496
|
`);
|
|
1991
2497
|
if (jsonOutput) logger.out([]);
|
|
1992
2498
|
return;
|
|
@@ -2001,7 +2507,10 @@ const listCommand$4 = defineAppCommand({
|
|
|
2001
2507
|
...p.machine_user ? {
|
|
2002
2508
|
machineUser: p.machine_user,
|
|
2003
2509
|
machineUserOverride: p.machine_user_override ?? "allow"
|
|
2004
|
-
} : {}
|
|
2510
|
+
} : {},
|
|
2511
|
+
...p.platform_url ? { platformUrl: p.platform_url } : {},
|
|
2512
|
+
...p.oauth2_client_id ? { oauth2ClientId: p.oauth2_client_id } : {},
|
|
2513
|
+
...p.console_url ? { consoleUrl: p.console_url } : {}
|
|
2005
2514
|
};
|
|
2006
2515
|
});
|
|
2007
2516
|
logger.out(profileInfos);
|
|
@@ -2013,7 +2522,7 @@ const listCommand$4 = defineAppCommand({
|
|
|
2013
2522
|
const updateCommand$1 = defineAppCommand({
|
|
2014
2523
|
name: "update",
|
|
2015
2524
|
description: "Update profile properties.",
|
|
2016
|
-
args: z.
|
|
2525
|
+
args: z.strictObject({
|
|
2017
2526
|
name: arg(z.string(), {
|
|
2018
2527
|
positional: true,
|
|
2019
2528
|
description: "Profile name"
|
|
@@ -2031,13 +2540,16 @@ const updateCommand$1 = defineAppCommand({
|
|
|
2031
2540
|
alias: "m",
|
|
2032
2541
|
description: "Default machine user name for application-data commands (query, workflow start, function test-run, machineuser token). Pass an empty string to clear."
|
|
2033
2542
|
}),
|
|
2034
|
-
"machine-user-override": arg(z.enum(["allow", "deny"]).optional(), { description: "Whether the command line or TAILOR_PLATFORM_MACHINE_USER_NAME may override the profile's machine user. 'deny' requires --machine-user; 'allow' lifts the restriction." })
|
|
2035
|
-
|
|
2543
|
+
"machine-user-override": arg(z.enum(["allow", "deny"]).optional(), { description: "Whether the command line or TAILOR_PLATFORM_MACHINE_USER_NAME may override the profile's machine user. 'deny' requires --machine-user; 'allow' lifts the restriction." }),
|
|
2544
|
+
"platform-url": arg(z.union([z.url(), z.literal("")]).optional(), { description: "Platform API base URL for this profile. Pass an empty string to clear." }),
|
|
2545
|
+
"oauth2-client-id": arg(z.string().optional(), { description: "OAuth2 client ID for logging in to this profile's platform. Pass an empty string to clear." }),
|
|
2546
|
+
"console-url": arg(z.union([z.url(), z.literal("")]).optional(), { description: "Console base URL for this profile. Pass an empty string to clear." })
|
|
2547
|
+
}),
|
|
2036
2548
|
run: async (args) => {
|
|
2037
2549
|
const config = await readPlatformConfig();
|
|
2038
2550
|
const profile = config.profiles[args.name];
|
|
2039
2551
|
if (!profile) throw new Error(`Profile "${args.name}" not found.`);
|
|
2040
|
-
if (!args.user && !args["workspace-id"] && args.permission === void 0 && args["machine-user"] === void 0 && args["machine-user-override"] === void 0) throw new Error("Please provide at least one property to update.");
|
|
2552
|
+
if (!args.user && !args["workspace-id"] && args.permission === void 0 && args["machine-user"] === void 0 && args["machine-user-override"] === void 0 && args["platform-url"] === void 0 && args["oauth2-client-id"] === void 0 && args["console-url"] === void 0) throw new Error("Please provide at least one property to update.");
|
|
2041
2553
|
const oldUser = profile.user;
|
|
2042
2554
|
const newUser = args.user || oldUser;
|
|
2043
2555
|
const oldWorkspaceId = profile.workspace_id;
|
|
@@ -2045,14 +2557,24 @@ const updateCommand$1 = defineAppCommand({
|
|
|
2045
2557
|
let resolvedUser = newUser;
|
|
2046
2558
|
const finalMachineUser = args["machine-user"] === "" ? void 0 : args["machine-user"] ?? profile.machine_user;
|
|
2047
2559
|
const finalOverride = args["machine-user-override"] === "allow" ? void 0 : args["machine-user-override"] ?? profile.machine_user_override;
|
|
2560
|
+
const finalPlatformUrl = args["platform-url"] === "" ? void 0 : args["platform-url"] ?? profile.platform_url;
|
|
2561
|
+
const finalOAuth2ClientId = args["oauth2-client-id"] === "" ? void 0 : args["oauth2-client-id"] ?? profile.oauth2_client_id;
|
|
2562
|
+
const finalConsoleUrl = args["console-url"] === "" ? void 0 : args["console-url"] ?? profile.console_url;
|
|
2563
|
+
const finalPlatformConfigInput = {
|
|
2564
|
+
...finalPlatformUrl ? { platformUrl: finalPlatformUrl } : {},
|
|
2565
|
+
...finalOAuth2ClientId ? { oauth2ClientId: finalOAuth2ClientId } : {},
|
|
2566
|
+
...finalConsoleUrl ? { consoleUrl: finalConsoleUrl } : {}
|
|
2567
|
+
};
|
|
2568
|
+
const finalPlatformConfig = Object.keys(finalPlatformConfigInput).length > 0 ? finalPlatformConfigInput : void 0;
|
|
2569
|
+
const tokenLookupPlatformConfig = args["platform-url"] === "" ? platformConfigFromProfile(profile) : finalPlatformConfig;
|
|
2048
2570
|
if ((args["machine-user"] !== void 0 || args["machine-user-override"] !== void 0) && finalOverride === "deny" && !finalMachineUser) {
|
|
2049
2571
|
if (args["machine-user-override"] === "deny") throw new Error("--machine-user-override deny requires --machine-user.");
|
|
2050
2572
|
throw new Error(`Cannot clear the machine user while machine-user-override is "deny". Also pass --machine-user-override allow.`);
|
|
2051
2573
|
}
|
|
2052
|
-
if (args.user !== void 0 || args["workspace-id"] !== void 0) {
|
|
2053
|
-
const refreshed = await fetchLatestToken(config, newUser);
|
|
2574
|
+
if (args.user !== void 0 || args["workspace-id"] !== void 0 || args["platform-url"] !== void 0) {
|
|
2575
|
+
const refreshed = await fetchLatestToken(config, newUser, tokenLookupPlatformConfig);
|
|
2054
2576
|
resolvedUser = refreshed.user;
|
|
2055
|
-
const client = await initOperatorClient(refreshed.accessToken);
|
|
2577
|
+
const client = await initOperatorClient(refreshed.accessToken, finalPlatformConfig);
|
|
2056
2578
|
if (!(await fetchAll(async (pageToken, maxPageSize) => {
|
|
2057
2579
|
const { workspaces, nextPageToken } = await client.listWorkspaces({
|
|
2058
2580
|
pageToken,
|
|
@@ -2069,6 +2591,12 @@ const updateCommand$1 = defineAppCommand({
|
|
|
2069
2591
|
else profile.machine_user = args["machine-user"];
|
|
2070
2592
|
if (args["machine-user-override"] === "deny") profile.machine_user_override = "deny";
|
|
2071
2593
|
else if (args["machine-user-override"] === "allow") delete profile.machine_user_override;
|
|
2594
|
+
if (args["platform-url"] !== void 0) if (args["platform-url"] === "") delete profile.platform_url;
|
|
2595
|
+
else profile.platform_url = args["platform-url"];
|
|
2596
|
+
if (args["oauth2-client-id"] !== void 0) if (args["oauth2-client-id"] === "") delete profile.oauth2_client_id;
|
|
2597
|
+
else profile.oauth2_client_id = args["oauth2-client-id"];
|
|
2598
|
+
if (args["console-url"] !== void 0) if (args["console-url"] === "") delete profile.console_url;
|
|
2599
|
+
else profile.console_url = args["console-url"];
|
|
2072
2600
|
writePlatformConfig(config);
|
|
2073
2601
|
if (!args.json) logger.success(`Profile "${args.name}" updated successfully`);
|
|
2074
2602
|
const profileInfo = {
|
|
@@ -2079,7 +2607,10 @@ const updateCommand$1 = defineAppCommand({
|
|
|
2079
2607
|
...profile.machine_user ? {
|
|
2080
2608
|
machineUser: profile.machine_user,
|
|
2081
2609
|
machineUserOverride: profile.machine_user_override ?? "allow"
|
|
2082
|
-
} : {}
|
|
2610
|
+
} : {},
|
|
2611
|
+
...profile.platform_url ? { platformUrl: profile.platform_url } : {},
|
|
2612
|
+
...profile.oauth2_client_id ? { oauth2ClientId: profile.oauth2_client_id } : {},
|
|
2613
|
+
...profile.console_url ? { consoleUrl: profile.console_url } : {}
|
|
2083
2614
|
};
|
|
2084
2615
|
logger.out(profileInfo);
|
|
2085
2616
|
}
|
|
@@ -2092,7 +2623,7 @@ const profileCommand = defineCommand({
|
|
|
2092
2623
|
description: "Manage workspace profiles (user + workspace combinations).",
|
|
2093
2624
|
subCommands: {
|
|
2094
2625
|
create: createCommand$2,
|
|
2095
|
-
delete: deleteCommand$
|
|
2626
|
+
delete: deleteCommand$3,
|
|
2096
2627
|
list: listCommand$4,
|
|
2097
2628
|
update: updateCommand$1
|
|
2098
2629
|
},
|
|
@@ -2187,11 +2718,11 @@ async function releaseVaultOwnership(params) {
|
|
|
2187
2718
|
const createSecretCommand = defineAppCommand({
|
|
2188
2719
|
name: "create",
|
|
2189
2720
|
description: "Create a secret in a vault.",
|
|
2190
|
-
args: z.
|
|
2721
|
+
args: z.strictObject({
|
|
2191
2722
|
...workspaceArgs,
|
|
2192
2723
|
...secretValueArgs,
|
|
2193
2724
|
...confirmationArgs
|
|
2194
|
-
})
|
|
2725
|
+
}),
|
|
2195
2726
|
run: async (args) => {
|
|
2196
2727
|
await assertWritable({ profile: args.profile });
|
|
2197
2728
|
const client = await initOperatorClient(await loadAccessToken({ profile: args.profile }));
|
|
@@ -2237,11 +2768,11 @@ const createSecretCommand = defineAppCommand({
|
|
|
2237
2768
|
const deleteSecretCommand = defineAppCommand({
|
|
2238
2769
|
name: "delete",
|
|
2239
2770
|
description: "Delete a secret in a vault.",
|
|
2240
|
-
args: z.
|
|
2771
|
+
args: z.strictObject({
|
|
2241
2772
|
...workspaceArgs,
|
|
2242
2773
|
...secretIdentifyArgs,
|
|
2243
2774
|
...confirmationArgs
|
|
2244
|
-
})
|
|
2775
|
+
}),
|
|
2245
2776
|
run: async (args) => {
|
|
2246
2777
|
await assertWritable({ profile: args.profile });
|
|
2247
2778
|
const client = await initOperatorClient(await loadAccessToken({ profile: args.profile }));
|
|
@@ -2313,11 +2844,11 @@ async function secretList(options) {
|
|
|
2313
2844
|
const listSecretCommand = defineAppCommand({
|
|
2314
2845
|
name: "list",
|
|
2315
2846
|
description: "List all secrets in a vault.",
|
|
2316
|
-
args: z.
|
|
2847
|
+
args: z.strictObject({
|
|
2317
2848
|
...workspaceArgs,
|
|
2318
2849
|
...vaultArgs,
|
|
2319
2850
|
...paginationArgs()
|
|
2320
|
-
})
|
|
2851
|
+
}),
|
|
2321
2852
|
run: async (args) => {
|
|
2322
2853
|
try {
|
|
2323
2854
|
const secrets = await secretList({
|
|
@@ -2340,11 +2871,11 @@ const listSecretCommand = defineAppCommand({
|
|
|
2340
2871
|
const updateSecretCommand = defineAppCommand({
|
|
2341
2872
|
name: "update",
|
|
2342
2873
|
description: "Update a secret in a vault.",
|
|
2343
|
-
args: z.
|
|
2874
|
+
args: z.strictObject({
|
|
2344
2875
|
...workspaceArgs,
|
|
2345
2876
|
...secretValueArgs,
|
|
2346
2877
|
...confirmationArgs
|
|
2347
|
-
})
|
|
2878
|
+
}),
|
|
2348
2879
|
run: async (args) => {
|
|
2349
2880
|
await assertWritable({ profile: args.profile });
|
|
2350
2881
|
const client = await initOperatorClient(await loadAccessToken({ profile: args.profile }));
|
|
@@ -2394,10 +2925,10 @@ const nameArgs = { name: arg(z.string(), {
|
|
|
2394
2925
|
const createCommand$1 = defineAppCommand({
|
|
2395
2926
|
name: "create",
|
|
2396
2927
|
description: "Create a new Secret Manager vault.",
|
|
2397
|
-
args: z.
|
|
2928
|
+
args: z.strictObject({
|
|
2398
2929
|
...workspaceArgs,
|
|
2399
2930
|
...nameArgs
|
|
2400
|
-
})
|
|
2931
|
+
}),
|
|
2401
2932
|
run: async (args) => {
|
|
2402
2933
|
await assertWritable({ profile: args.profile });
|
|
2403
2934
|
const client = await initOperatorClient(await loadAccessToken({ profile: args.profile }));
|
|
@@ -2420,14 +2951,14 @@ const createCommand$1 = defineAppCommand({
|
|
|
2420
2951
|
|
|
2421
2952
|
//#endregion
|
|
2422
2953
|
//#region src/cli/commands/secret/vault/delete.ts
|
|
2423
|
-
const deleteCommand$
|
|
2954
|
+
const deleteCommand$2 = defineAppCommand({
|
|
2424
2955
|
name: "delete",
|
|
2425
2956
|
description: "Delete a Secret Manager vault.",
|
|
2426
|
-
args: z.
|
|
2957
|
+
args: z.strictObject({
|
|
2427
2958
|
...workspaceArgs,
|
|
2428
2959
|
...nameArgs,
|
|
2429
2960
|
...confirmationArgs
|
|
2430
|
-
})
|
|
2961
|
+
}),
|
|
2431
2962
|
run: async (args) => {
|
|
2432
2963
|
await assertWritable({ profile: args.profile });
|
|
2433
2964
|
const client = await initOperatorClient(await loadAccessToken({ profile: args.profile }));
|
|
@@ -2494,10 +3025,10 @@ async function vaultList(options) {
|
|
|
2494
3025
|
const listCommand$3 = defineAppCommand({
|
|
2495
3026
|
name: "list",
|
|
2496
3027
|
description: "List all Secret Manager vaults in the workspace.",
|
|
2497
|
-
args: z.
|
|
3028
|
+
args: z.strictObject({
|
|
2498
3029
|
...workspaceArgs,
|
|
2499
3030
|
...paginationArgs()
|
|
2500
|
-
})
|
|
3031
|
+
}),
|
|
2501
3032
|
run: async (args) => {
|
|
2502
3033
|
const vaults = await vaultList({
|
|
2503
3034
|
workspaceId: args["workspace-id"],
|
|
@@ -2516,7 +3047,7 @@ const vaultCommand = defineCommand({
|
|
|
2516
3047
|
description: "Manage Secret Manager vaults.",
|
|
2517
3048
|
subCommands: {
|
|
2518
3049
|
create: createCommand$1,
|
|
2519
|
-
delete: deleteCommand$
|
|
3050
|
+
delete: deleteCommand$2,
|
|
2520
3051
|
list: listCommand$3
|
|
2521
3052
|
},
|
|
2522
3053
|
async run() {
|
|
@@ -2577,7 +3108,7 @@ function detectDefaultBranch(cwd, run = defaultGitRunner) {
|
|
|
2577
3108
|
/** Current lock schema version. Bumped only on breaking lock-format changes. */
|
|
2578
3109
|
const LOCK_VERSION = 1;
|
|
2579
3110
|
/** Lock file path, relative to the repository root. */
|
|
2580
|
-
const LOCK_FILENAME = ".github/tailor
|
|
3111
|
+
const LOCK_FILENAME = ".github/tailor.lock";
|
|
2581
3112
|
/**
|
|
2582
3113
|
* Compute the lock content hash for a rendered workflow file.
|
|
2583
3114
|
* @param content - File content to hash
|
|
@@ -2609,11 +3140,11 @@ function readLock(outputDir) {
|
|
|
2609
3140
|
try {
|
|
2610
3141
|
parsed = JSON.parse(fs$1.readFileSync(file, "utf-8"));
|
|
2611
3142
|
} catch (cause) {
|
|
2612
|
-
throw new Error(`${LOCK_FILENAME} is not valid JSON. The lock file is machine-owned; restore it from git (git checkout -- .github/tailor
|
|
3143
|
+
throw new Error(`${LOCK_FILENAME} is not valid JSON. The lock file is machine-owned; restore it from git (git checkout -- .github/tailor.lock) and re-run setup.`, { cause });
|
|
2613
3144
|
}
|
|
2614
|
-
if (typeof parsed.version !== "number") throw new Error(`${LOCK_FILENAME} has no valid 'version' field. The lock file is machine-owned; restore it from git (git checkout -- .github/tailor
|
|
3145
|
+
if (typeof parsed.version !== "number") throw new Error(`${LOCK_FILENAME} has no valid 'version' field. The lock file is machine-owned; restore it from git (git checkout -- .github/tailor.lock) and re-run setup.`);
|
|
2615
3146
|
if (parsed.version > 1) throw new Error(`${LOCK_FILENAME} was written by a newer SDK (lock version ${String(parsed.version)}). Update @tailor-platform/sdk to continue (e.g. pnpm update @tailor-platform/sdk).`);
|
|
2616
|
-
if (!Array.isArray(parsed.targets)) throw new Error(`${LOCK_FILENAME} has no valid 'targets' array. The lock file is machine-owned; restore it from git (git checkout -- .github/tailor
|
|
3147
|
+
if (!Array.isArray(parsed.targets)) throw new Error(`${LOCK_FILENAME} has no valid 'targets' array. The lock file is machine-owned; restore it from git (git checkout -- .github/tailor.lock) and re-run setup.`);
|
|
2617
3148
|
return parsed;
|
|
2618
3149
|
}
|
|
2619
3150
|
/**
|
|
@@ -2638,22 +3169,34 @@ function findTarget(lock, kind, workspaceName) {
|
|
|
2638
3169
|
return lock?.targets.find((t) => t.kind === kind && t.workspaceName === workspaceName);
|
|
2639
3170
|
}
|
|
2640
3171
|
|
|
3172
|
+
//#endregion
|
|
3173
|
+
//#region src/cli/commands/setup/action.yml
|
|
3174
|
+
var action_default = "# __HEADER__\nname: __WORKSPACE_NAME__\ndescription: >-\n Deploy __WORKSPACE_NAME__ to Tailor Platform.\n Checkout and dependency installation are expected to be handled by the caller.\n\ninputs:\n workspace-id:\n description: Tailor Platform workspace id\n required: true\n name:\n description: Workspace name (used in Slack notifications)\n required: true\n working-directory:\n description: App directory relative to the repository root\n required: false\n default: \".\"\n config:\n description: SDK config path, or comma-separated config paths for multi-config deploys\n required: false\n default: \"tailor.config.ts\"\n deploy:\n description: Whether to run the Tailor deploy step after build steps\n required: false\n default: \"true\"\n build-site:\n description: Whether to run the static website build slot when present\n required: false\n default: \"true\"\n platform-client-id:\n description: Machine-user client id\n required: true\n platform-client-secret:\n description: Machine-user client secret\n required: true\n slack-token:\n description: Slack bot token\n required: false\n slack-channel-id:\n description: Slack channel id\n required: false\n user-mapping:\n description: JSON string mapping GitHub usernames to Slack user IDs\n required: false\n package-manager:\n description: Package manager used for dependency installation (pnpm, npm, yarn, bun)\n required: false\n default: pnpm\n\noutputs:\n app-url:\n description: Deployed application URL\n value: ${{ steps.tailor-apply.outputs.app-url }}\n\nruns:\n using: composite\n steps:\n # __STATIC_WEBSITE_BUILD_START__\n - id: build-site\n if: inputs.build-site == 'true'\n shell: bash\n run: |\n # Replace with your static website build command.\n # Example: pnpm --filter <package-name> run build:static\n # Note: this run: command is user-editable. Custom build commands are preserved across\n # reruns and are not treated as drift by setup check. Other edits to this file are flagged\n # as drift and will be overwritten if you re-run setup action with --force.\n true\n # __STATIC_WEBSITE_BUILD_END__\n - id: tailor-apply\n if: inputs.deploy == 'true'\n uses: tailor-platform/actions/deploy@e74173d4bdd931d036ad359cf5f7729fc6aa7067 # v1.5.1\n env:\n TAILOR_PLATFORM_SDK_CONFIG_PATH: ${{ inputs.config }}\n with:\n workspace-id: ${{ inputs.workspace-id }}\n working-directory: ${{ inputs.working-directory }}\n package-manager: ${{ inputs.package-manager }}\n platform-client-id: ${{ inputs.platform-client-id }}\n platform-client-secret: ${{ inputs.platform-client-secret }}\n - id: tailor-notify\n if: failure() && inputs.slack-token != '' && inputs.slack-channel-id != ''\n uses: tailor-platform/actions/notify@e74173d4bdd931d036ad359cf5f7729fc6aa7067 # v1.5.1\n with:\n provider: slack\n status: failure\n workspace-name: ${{ inputs.name }}\n slack-token: ${{ inputs.slack-token }}\n slack-channel-id: ${{ inputs.slack-channel-id }}\n user-mapping: ${{ inputs.user-mapping }}\n";
|
|
3175
|
+
|
|
2641
3176
|
//#endregion
|
|
2642
3177
|
//#region src/cli/commands/setup/branch.workflow.yml
|
|
2643
|
-
var branch_workflow_default = "# __HEADER__\nname: Tailor (__WORKSPACE_NAME__)\n\non:\n # __PULL_REQUEST_START__\n pull_request:\n branches: [\"__BRANCH__\"]\n # __PATHS__\n # __PULL_REQUEST_END__\n push:\n branches: [\"__BRANCH__\"]\n # __PATHS__\n workflow_dispatch:\n # __DISPATCH_INPUTS_START__\n inputs:\n dry-run:\n description: Preview changes without deploying\n type: boolean\n default: false\n # __DISPATCH_INPUTS_END__\n\npermissions:\n contents: read\n\njobs:\n # __PLAN_JOB_START__\n tailor-plan:\n if: >-\n github.event_name == 'pull_request' ||\n (github.event_name == 'workflow_dispatch' && inputs['dry-run'])\n runs-on: ubuntu-latest\n timeout-minutes: 30\n environment: __ENVIRONMENT__\n permissions:\n contents: read\n pull-requests: write\n concurrency:\n group: tailor-plan-__WORKSPACE_NAME__-${{ github.event.pull_request.number || github.run_id }}\n cancel-in-progress: true\n steps:\n - id: tailor-checkout\n uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3\n - id: tailor-setup\n uses: tailor-platform/actions/setup@7817f37adf2891fbb2ebbe0a3b222a5fa2ed0c1f # v1.3.0\n with:\n package-manager: __PACKAGE_MANAGER__\n - id: tailor-generate-check\n uses: tailor-platform/actions/generate-check@7817f37adf2891fbb2ebbe0a3b222a5fa2ed0c1f # v1.3.0\n with:\n package-manager: __PACKAGE_MANAGER__\n # __WORKING_DIRECTORY__\n - id: tailor-plan\n # Fork PRs cannot read secrets; the checks above still run for them.\n if: github.event_name != 'pull_request' || !github.event.pull_request.head.repo.fork\n uses: tailor-platform/actions/plan@7817f37adf2891fbb2ebbe0a3b222a5fa2ed0c1f # v1.3.0\n with:\n workspace-id: ${{ vars.TAILOR_PLATFORM_WORKSPACE_ID }}\n package-manager: __PACKAGE_MANAGER__\n label: __WORKSPACE_NAME__\n # __WORKING_DIRECTORY__\n platform-client-id: ${{ secrets.TAILOR_PLATFORM_MACHINE_USER_CLIENT_ID }}\n platform-client-secret: ${{ secrets.TAILOR_PLATFORM_MACHINE_USER_CLIENT_SECRET }}\n github-token: ${{ secrets.GITHUB_TOKEN }}\n # __PLAN_JOB_END__\n tailor-deploy:\n # __DEPLOY_IF__\n runs-on: ubuntu-latest\n timeout-minutes: 30\n permissions:\n contents: read\n environment: __ENVIRONMENT__\n concurrency:\n group: tailor-deploy-__WORKSPACE_NAME__\n cancel-in-progress: false\n steps:\n - id: tailor-checkout\n uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3\n - id: tailor-setup\n uses: tailor-platform/actions/setup@7817f37adf2891fbb2ebbe0a3b222a5fa2ed0c1f # v1.3.0\n with:\n package-manager: __PACKAGE_MANAGER__\n - id: tailor-apply\n uses: tailor-platform/actions/deploy@7817f37adf2891fbb2ebbe0a3b222a5fa2ed0c1f # v1.3.0\n with:\n workspace-id: ${{ vars.TAILOR_PLATFORM_WORKSPACE_ID }}\n package-manager: __PACKAGE_MANAGER__\n # __WORKING_DIRECTORY__\n platform-client-id: ${{ secrets.TAILOR_PLATFORM_MACHINE_USER_CLIENT_ID }}\n platform-client-secret: ${{ secrets.TAILOR_PLATFORM_MACHINE_USER_CLIENT_SECRET }}\n";
|
|
3178
|
+
var branch_workflow_default = "# __HEADER__\nname: Tailor (__WORKSPACE_NAME__)\n\non:\n # __PULL_REQUEST_START__\n pull_request:\n branches: [\"__BRANCH__\"]\n # __PATHS__\n # __PULL_REQUEST_END__\n push:\n branches: [\"__BRANCH__\"]\n # __PATHS__\n workflow_dispatch:\n # __DISPATCH_INPUTS_START__\n inputs:\n dry-run:\n description: Preview changes without deploying\n type: boolean\n default: false\n # __DISPATCH_INPUTS_END__\n\npermissions:\n contents: read\n\njobs:\n # __PLAN_JOB_START__\n tailor-plan:\n if: >-\n github.event_name == 'pull_request' ||\n (github.event_name == 'workflow_dispatch' && inputs['dry-run'])\n runs-on: ubuntu-latest\n timeout-minutes: 30\n environment: __ENVIRONMENT__\n permissions:\n contents: read\n pull-requests: write\n concurrency:\n group: tailor-plan-__WORKSPACE_NAME__-${{ github.event.pull_request.number || github.run_id }}\n cancel-in-progress: true\n steps:\n - id: tailor-checkout\n uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0\n - id: tailor-setup\n uses: tailor-platform/actions/setup@e74173d4bdd931d036ad359cf5f7729fc6aa7067 # v1.5.1\n with:\n package-manager: __PACKAGE_MANAGER__\n - id: tailor-install\n uses: tailor-platform/actions/install@e74173d4bdd931d036ad359cf5f7729fc6aa7067 # v1.5.1\n with:\n package-manager: __PACKAGE_MANAGER__\n - id: tailor-generate-check\n uses: tailor-platform/actions/generate-check@e74173d4bdd931d036ad359cf5f7729fc6aa7067 # v1.5.1\n with:\n package-manager: __PACKAGE_MANAGER__\n # __WORKING_DIRECTORY__\n # __SEED_VALIDATE_START__\n - id: tailor-seed-validate\n uses: tailor-platform/actions/seed-validate@e74173d4bdd931d036ad359cf5f7729fc6aa7067 # v1.5.1\n with:\n package-manager: __PACKAGE_MANAGER__\n # __WORKING_DIRECTORY__\n # __SEED_VALIDATE_END__\n - id: tailor-drift-check\n uses: tailor-platform/actions/drift-check@e74173d4bdd931d036ad359cf5f7729fc6aa7067 # v1.5.1\n with:\n package-manager: __PACKAGE_MANAGER__\n # __MIGRATION_DRIFT_CHECK_START__\n - id: tailor-migration-drift-check\n uses: tailor-platform/actions/migration-drift-check@e74173d4bdd931d036ad359cf5f7729fc6aa7067 # v1.5.1\n with:\n package-manager: __PACKAGE_MANAGER__\n # __WORKING_DIRECTORY__\n # __MIGRATION_DRIFT_CHECK_END__\n - id: tailor-plan\n # Fork PRs cannot read secrets; the checks above still run for them.\n if: github.event_name != 'pull_request' || !github.event.pull_request.head.repo.fork\n uses: tailor-platform/actions/plan@e74173d4bdd931d036ad359cf5f7729fc6aa7067 # v1.5.1\n with:\n workspace-id: ${{ vars.TAILOR_PLATFORM_WORKSPACE_ID }}\n package-manager: __PACKAGE_MANAGER__\n label: __WORKSPACE_NAME__\n # __WORKING_DIRECTORY__\n platform-client-id: ${{ secrets.TAILOR_PLATFORM_MACHINE_USER_CLIENT_ID }}\n platform-client-secret: ${{ secrets.TAILOR_PLATFORM_MACHINE_USER_CLIENT_SECRET }}\n github-token: ${{ secrets.GITHUB_TOKEN }}\n # __PLAN_JOB_END__\n # __ERD_PREVIEW_JOB_START__\n tailor-erd-preview-matrix:\n if: github.event_name == 'pull_request'\n runs-on: ubuntu-latest\n timeout-minutes: 5\n permissions:\n contents: read\n outputs:\n namespaces: ${{ steps.tailor-erd-preview-matrix.outputs.namespaces }}\n base-app-dir: ${{ steps.tailor-erd-preview-matrix.outputs['base-app-dir'] }}\n steps:\n - id: tailor-checkout\n uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0\n with:\n persist-credentials: false\n - id: tailor-checkout-base\n uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0\n with:\n repository: ${{ github.event.pull_request.base.repo.full_name }}\n ref: ${{ github.event.pull_request.base.sha }}\n path: .tailor-erd-base\n persist-credentials: false\n - id: tailor-erd-preview-matrix\n shell: bash\n env:\n APP_DIR: \"__APP_DIR__\"\n WORKSPACE_NAME: \"__WORKSPACE_NAME__\"\n run: |\n set -euo pipefail\n node <<'NODE'\n const fs = require(\"node:fs\");\n\n const appDir = process.env.APP_DIR || \".\";\n const workspaceName = process.env.WORKSPACE_NAME;\n const output = process.env.GITHUB_OUTPUT;\n const dirPattern = /^[A-Za-z0-9._/-]+$/;\n const namespacePattern = /^[A-Za-z0-9][A-Za-z0-9._-]*$/;\n\n function lockTarget(file) {\n try {\n const lock = JSON.parse(fs.readFileSync(file, \"utf8\"));\n return lock.targets?.find(\n (candidate) =>\n candidate.kind === \"branch\" &&\n candidate.workspaceName === workspaceName,\n );\n } catch (error) {\n if (error && error.code === \"ENOENT\") return undefined;\n throw error;\n }\n }\n\n function namespacesFromTarget(target, label) {\n const namespaces =\n target?.inputs?.erdPreview === true && Array.isArray(target.inputs?.erdNamespaces)\n ? target.inputs.erdNamespaces\n : [];\n for (const namespace of namespaces) {\n if (typeof namespace !== \"string\" || !namespacePattern.test(namespace)) {\n throw new Error(`Invalid ERD namespace in ${label}: ${String(namespace)}`);\n }\n }\n return namespaces;\n }\n\n function normalizeDir(value, label) {\n const raw = typeof value === \"string\" && value.length > 0 ? value : appDir;\n const normalized =\n raw.replaceAll(\"\\\\\", \"/\").replace(/\\/{2,}/g, \"/\").replace(/^\\.\\//, \"\").replace(/\\/$/, \"\") ||\n \".\";\n if (\n !dirPattern.test(normalized) ||\n normalized.startsWith(\"/\") ||\n normalized.split(\"/\").includes(\"..\")\n ) {\n throw new Error(`Invalid ERD app dir in ${label}: ${raw}`);\n }\n return normalized;\n }\n\n const headTarget = lockTarget(\".github/tailor.lock\");\n const baseTarget = lockTarget(\".tailor-erd-base/.github/tailor.lock\");\n const baseAppDir = normalizeDir(baseTarget?.inputs?.dir, \"base setup lock\");\n const namespaces = [\n ...new Set([\n ...namespacesFromTarget(headTarget, \"head setup lock\"),\n ...namespacesFromTarget(baseTarget, \"base setup lock\"),\n ]),\n ].sort((a, b) => a.localeCompare(b));\n\n if (namespaces.length === 0) {\n console.error(\"No ERD preview namespaces found in the head or base setup lock.\");\n process.exit(1);\n }\n\n fs.appendFileSync(output, `namespaces=${JSON.stringify(namespaces)}\\n`);\n fs.appendFileSync(output, `base-app-dir=${baseAppDir}\\n`);\n NODE\n\n tailor-erd-preview:\n needs: tailor-erd-preview-matrix\n if: github.event_name == 'pull_request'\n runs-on: ubuntu-latest\n timeout-minutes: 15\n permissions:\n contents: read\n strategy:\n fail-fast: false\n matrix:\n namespace: ${{ fromJSON(needs.tailor-erd-preview-matrix.outputs.namespaces) }}\n steps:\n - id: tailor-checkout\n uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0\n with:\n persist-credentials: false\n - id: tailor-setup\n uses: tailor-platform/actions/setup@7817f37adf2891fbb2ebbe0a3b222a5fa2ed0c1f # v1.3.0\n with:\n package-manager: __PACKAGE_MANAGER__\n - id: tailor-checkout-base\n uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0\n with:\n repository: ${{ github.event.pull_request.base.repo.full_name }}\n ref: ${{ github.event.pull_request.base.sha }}\n path: .tailor-erd-base\n persist-credentials: false\n - id: tailor-detect-base-package-manager\n shell: bash\n run: |\n set -euo pipefail\n cd .tailor-erd-base\n base_package_manager=\"$(\n if [ -f pnpm-lock.yaml ]; then\n echo pnpm\n elif [ -f yarn.lock ]; then\n echo yarn\n elif [ -f bun.lockb ] || [ -f bun.lock ]; then\n echo bun\n elif [ -f package-lock.json ]; then\n echo npm\n else\n echo \"__PACKAGE_MANAGER__\"\n fi\n )\"\n echo \"package-manager=$base_package_manager\" >> \"$GITHUB_OUTPUT\"\n if [ \"$base_package_manager\" = \"pnpm\" ]; then\n base_pnpm_version=\"$(node <<'NODE'\n const fs = require(\"node:fs\");\n\n const fallback = \"10\";\n\n function readManifest(file) {\n try {\n return JSON.parse(fs.readFileSync(file, \"utf8\"));\n } catch {\n return {};\n }\n }\n\n function pnpmVersion(manifest) {\n const packageManager = manifest.packageManager;\n if (typeof packageManager === \"string\" && packageManager.startsWith(\"pnpm@\")) {\n return packageManager.slice(\"pnpm@\".length).split(\"+\")[0];\n }\n\n const devPackageManager = manifest.devEngines?.packageManager;\n if (devPackageManager?.name === \"pnpm\" && devPackageManager.version) {\n return devPackageManager.version;\n }\n\n return undefined;\n }\n\n process.stdout.write(\n pnpmVersion(readManifest(\"package.json\")) ?? fallback,\n );\n NODE\n )\"\n echo \"pnpm-version=$base_pnpm_version\" >> \"$GITHUB_OUTPUT\"\n fi\n - id: tailor-setup-base-pnpm\n if: steps.tailor-detect-base-package-manager.outputs['package-manager'] == 'pnpm'\n uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9\n with:\n version: ${{ steps.tailor-detect-base-package-manager.outputs['pnpm-version'] }}\n package_json_file: .tailor-erd-base/package.json\n run_install: false\n - id: tailor-setup-base-node\n if: steps.tailor-detect-base-package-manager.outputs['package-manager'] != 'bun'\n uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0\n with:\n node-version-file: .tailor-erd-base/package.json\n - id: tailor-setup-base-yarn\n if: steps.tailor-detect-base-package-manager.outputs['package-manager'] == 'yarn'\n shell: bash\n working-directory: .tailor-erd-base\n run: |\n set -euo pipefail\n corepack enable\n - id: tailor-setup-base-bun\n if: steps.tailor-detect-base-package-manager.outputs['package-manager'] == 'bun'\n uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0\n - id: tailor-install-base\n shell: bash\n working-directory: .tailor-erd-base\n env:\n BASE_PACKAGE_MANAGER: ${{ steps.tailor-detect-base-package-manager.outputs['package-manager'] }}\n run: |\n set -euo pipefail\n case \"$BASE_PACKAGE_MANAGER\" in\n pnpm) pnpm install --frozen-lockfile ;;\n npm) npm ci ;;\n yarn)\n if grep -q '^__metadata:' yarn.lock; then\n yarn install --immutable\n else\n yarn install --frozen-lockfile\n fi\n ;;\n bun) bun install --frozen-lockfile ;;\n *)\n echo \"::error::Unsupported base package-manager '$BASE_PACKAGE_MANAGER' (expected pnpm, npm, yarn, or bun)\"\n exit 1\n ;;\n esac\n - id: tailor-restore-head-setup\n uses: tailor-platform/actions/setup@7817f37adf2891fbb2ebbe0a3b222a5fa2ed0c1f # v1.3.0\n with:\n package-manager: __PACKAGE_MANAGER__\n - id: tailor-build-erd-preview\n shell: bash\n env:\n APP_DIR: \"__APP_DIR__\"\n BASE_APP_DIR: ${{ needs.tailor-erd-preview-matrix.outputs['base-app-dir'] }}\n BASE_PACKAGE_MANAGER: ${{ steps.tailor-detect-base-package-manager.outputs['package-manager'] }}\n NAMESPACE: ${{ matrix.namespace }}\n run: |\n set -euo pipefail\n\n run_tailor_cli() {\n case \"__PACKAGE_MANAGER__\" in\n pnpm) pnpm exec tailor \"$@\" ;;\n npm) npx tailor \"$@\" ;;\n yarn) yarn tailor \"$@\" ;;\n bun) bunx tailor \"$@\" ;;\n *)\n echo \"::error::Unsupported package-manager '__PACKAGE_MANAGER__' (expected pnpm, npm, yarn, or bun)\"\n exit 1\n ;;\n esac\n }\n\n run_head_node() {\n case \"__PACKAGE_MANAGER__\" in\n pnpm) pnpm exec node \"$@\" ;;\n npm) node \"$@\" ;;\n yarn) yarn node \"$@\" ;;\n bun) node \"$@\" ;;\n *)\n echo \"::error::Unsupported package-manager '__PACKAGE_MANAGER__' (expected pnpm, npm, yarn, or bun)\"\n exit 1\n ;;\n esac\n }\n\n tailor_cli_bin=\"$(\n run_head_node - <<'NODE'\n const path = require(\"node:path\");\n const { createRequire } = require(\"node:module\");\n const appDir = process.env.APP_DIR || \".\";\n const githubWorkspace = process.env.GITHUB_WORKSPACE;\n if (!githubWorkspace) {\n throw new Error(\"GITHUB_WORKSPACE is required.\");\n }\n\n let lastError;\n for (const manifestPath of [\n path.join(githubWorkspace, appDir, \"package.json\"),\n path.join(githubWorkspace, \"package.json\"),\n ]) {\n try {\n const requireFromManifest = createRequire(manifestPath);\n process.stdout.write(\n path.join(\n path.dirname(requireFromManifest.resolve(\"@tailor-platform/sdk/cli\")),\n \"index.mjs\",\n ),\n );\n process.exit(0);\n } catch (error) {\n lastError = error;\n }\n }\n throw lastError || new Error(\"Failed to resolve @tailor-platform/sdk/cli.\");\n NODE\n )\"\n\n run_head_tailor_cli_bin() {\n local command_cwd=\"$1\"\n shift\n\n run_head_cli_env() {\n env TAILOR_CLI_BIN=\"$tailor_cli_bin\" TAILOR_CLI_CWD=\"$command_cwd\" \"$@\"\n }\n\n (\n cd \"$GITHUB_WORKSPACE\" || exit 1\n case \"__PACKAGE_MANAGER__\" in\n pnpm) run_head_cli_env pnpm exec node \"$head_cli_runner\" \"$@\" ;;\n npm) run_head_cli_env node \"$head_cli_runner\" \"$@\" ;;\n yarn) run_head_cli_env yarn node \"$head_cli_runner\" \"$@\" ;;\n bun) run_head_cli_env bun \"$head_cli_runner\" \"$@\" ;;\n *)\n echo \"::error::Unsupported package-manager '__PACKAGE_MANAGER__' (expected pnpm, npm, yarn, or bun)\"\n exit 1\n ;;\n esac\n )\n }\n\n run_base_tailor_cli_bin() {\n local command_cwd=\"$1\"\n shift\n\n run_head_cli_env() {\n env TAILOR_CLI_BIN=\"$tailor_cli_bin\" TAILOR_CLI_CWD=\"$command_cwd\" \"$@\"\n }\n\n (\n cd \"$command_cwd\" || exit 1\n case \"$BASE_PACKAGE_MANAGER\" in\n pnpm) run_head_cli_env pnpm exec node \"$head_cli_runner\" \"$@\" ;;\n npm) run_head_cli_env node \"$head_cli_runner\" \"$@\" ;;\n yarn) run_head_cli_env yarn node \"$head_cli_runner\" \"$@\" ;;\n bun) run_head_cli_env bun \"$head_cli_runner\" \"$@\" ;;\n *)\n echo \"::error::Unsupported base package-manager '$BASE_PACKAGE_MANAGER' (expected pnpm, npm, yarn, or bun)\"\n exit 1\n ;;\n esac\n )\n }\n\n head_output=\"$RUNNER_TEMP/tailor-erd/head\"\n base_output=\"$RUNNER_TEMP/tailor-erd/base\"\n preview_output=\"$RUNNER_TEMP/tailor-erd/preview\"\n dts_output=\"$RUNNER_TEMP/tailor-erd/dts\"\n head_cli_runner=\"$RUNNER_TEMP/tailor-erd/run-head-cli.mjs\"\n head_log=\"$RUNNER_TEMP/tailor-erd/head-$NAMESPACE.log\"\n base_log=\"$RUNNER_TEMP/tailor-erd/base-$NAMESPACE.log\"\n rm -rf \"$head_output\" \"$base_output\" \"$preview_output\" \"$dts_output\"\n mkdir -p \"$head_output\" \"$base_output\" \"$preview_output\" \"$dts_output\"\n cat > \"$head_cli_runner\" <<'NODE'\n import { pathToFileURL } from \"node:url\";\n\n const cliBin = process.env.TAILOR_CLI_BIN;\n const commandCwd = process.env.TAILOR_CLI_CWD;\n if (!cliBin || !commandCwd) {\n throw new Error(\"TAILOR_CLI_BIN and TAILOR_CLI_CWD are required.\");\n }\n\n process.chdir(commandCwd);\n process.argv = [process.argv[0], cliBin, ...process.argv.slice(2)];\n await import(pathToFileURL(cliBin).href);\n NODE\n\n head_config=\"$GITHUB_WORKSPACE/$APP_DIR/tailor.config.ts\"\n base_config=\"$GITHUB_WORKSPACE/.tailor-erd-base/$BASE_APP_DIR/tailor.config.ts\"\n head_html=\"$head_output/$NAMESPACE/dist/index.html\"\n base_html=\"$base_output/$NAMESPACE/dist/index.html\"\n preview_html=\"$preview_output/$NAMESPACE-viewer.html\"\n\n head_missing=\"false\"\n if [ ! -f \"$head_config\" ]; then\n echo \"Head ERD config not found; rendering base objects as removed.\"\n head_missing=\"true\"\n elif ! (\n cd \"$GITHUB_WORKSPACE/$APP_DIR\" || exit 1\n TAILOR_PLATFORM_SDK_DTS_PATH=\"$dts_output/head-$NAMESPACE.d.ts\" \\\n run_tailor_cli tailordb erd export --config \"$head_config\" --namespace \"$NAMESPACE\" --output \"$head_output\"\n ) >\"$head_log\" 2>&1; then\n if grep -q 'not found in local config.db' \"$head_log\"; then\n cat \"$head_log\"\n echo \"Head ERD namespace '$NAMESPACE' not found; rendering base objects as removed.\"\n head_missing=\"true\"\n else\n cat \"$head_log\"\n exit 1\n fi\n elif [ -s \"$head_log\" ]; then\n cat \"$head_log\"\n fi\n\n base_missing=\"false\"\n if [ ! -f \"$base_config\" ]; then\n echo \"Base ERD config not found; rendering current objects as added.\"\n base_missing=\"true\"\n elif ! (\n TAILOR_PLATFORM_SDK_DTS_PATH=\"$dts_output/base-$NAMESPACE.d.ts\" \\\n run_base_tailor_cli_bin \"$GITHUB_WORKSPACE/.tailor-erd-base/$BASE_APP_DIR\" tailordb erd export --config \"$base_config\" --namespace \"$NAMESPACE\" --output \"$base_output\"\n ) >\"$base_log\" 2>&1; then\n if grep -q 'not found in local config.db' \"$base_log\"; then\n cat \"$base_log\"\n echo \"Base ERD namespace '$NAMESPACE' not found; rendering current objects as added.\"\n base_missing=\"true\"\n else\n cat \"$base_log\"\n exit 1\n fi\n elif [ -s \"$base_log\" ]; then\n cat \"$base_log\"\n fi\n\n if [ \"$head_missing\" = \"true\" ] && [ \"$base_missing\" = \"true\" ]; then\n echo \"::error::ERD namespace '$NAMESPACE' was not found in head or base.\"\n exit 1\n fi\n if [ \"$head_missing\" = \"false\" ] && [ ! -f \"$head_html\" ]; then\n echo \"::error::Head ERD viewer was not generated at $head_html\"\n exit 1\n fi\n if [ \"$base_missing\" = \"false\" ] && [ ! -f \"$base_html\" ]; then\n echo \"::error::Base ERD viewer was not generated at $base_html\"\n exit 1\n fi\n\n diff_args=(--namespace \"$NAMESPACE\" --output \"$preview_html\")\n if [ \"$head_missing\" = \"false\" ]; then\n diff_args+=(--head-html \"$head_html\")\n fi\n if [ \"$base_missing\" = \"false\" ]; then\n diff_args+=(--base-html \"$base_html\")\n fi\n run_head_tailor_cli_bin \"$GITHUB_WORKSPACE\" tailordb erd diff \"${diff_args[@]}\"\n - id: tailor-upload-erd-viewer\n uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1\n with:\n name: ${{ matrix.namespace }}-viewer.html\n path: ${{ runner.temp }}/tailor-erd/preview/${{ matrix.namespace }}-viewer.html\n if-no-files-found: error\n archive: false\n retention-days: 7\n\n # __ERD_PREVIEW_JOB_END__\n # __ERD_PREVIEW_COMMENT_JOB_START__\n tailor-erd-preview-comment:\n needs: tailor-erd-preview\n if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository\n runs-on: ubuntu-latest\n timeout-minutes: 5\n permissions:\n actions: read\n pull-requests: write\n steps:\n - id: tailor-comment-erd-preview\n env:\n GH_TOKEN: ${{ github.token }}\n REPO: ${{ github.repository }}\n RUN_ID: ${{ github.run_id }}\n SERVER: ${{ github.server_url }}\n PR_NUMBER: ${{ github.event.pull_request.number }}\n BASE_REF: ${{ github.event.pull_request.base.ref }}\n HEAD_SHA: ${{ github.event.pull_request.head.sha }}\n MARKER: \"<!-- tailor-erd-preview: __WORKSPACE_NAME__ -->\"\n run: |\n set -euo pipefail\n\n current_head=$(gh api \"repos/$REPO/pulls/$PR_NUMBER\" --jq '.head.sha')\n if [ \"$current_head\" != \"$HEAD_SHA\" ]; then\n echo \"Skipping stale ERD preview comment for $HEAD_SHA; current head is $current_head.\"\n exit 0\n fi\n\n body=\"$MARKER\"$'\\n'\"### TailorDB ERD preview (__WORKSPACE_NAME__)\"$'\\n\\n'\n body+=\"Self-contained ERD viewer HTML artifacts for this pull request. Each viewer can switch between the current schema and a diff against base branch \\`$BASE_REF\\`.\"$'\\n'\n\n while IFS=$'\\t' read -r name id; do\n [ -z \"$name\" ] && continue\n if [[ \"$name\" != *-viewer.html ]]; then\n continue\n fi\n ns=${name%-viewer.html}\n label=\"$ns viewer\"\n body+=$'\\n'\"- **$label**: [$name]($SERVER/$REPO/actions/runs/$RUN_ID/artifacts/$id)\"\n done < <(gh api \"repos/$REPO/actions/runs/$RUN_ID/artifacts\" --paginate \\\n --jq '.artifacts[] | select(.name | endswith(\"-viewer.html\")) | [.name, (.id|tostring)] | @tsv' | sort)\n\n existing=$(gh api \"repos/$REPO/issues/$PR_NUMBER/comments\" --paginate \\\n --jq 'map(select(.body | contains(env.MARKER))) | .[0].id // empty')\n if [ -n \"$existing\" ]; then\n gh api --method PATCH \"repos/$REPO/issues/comments/$existing\" -f body=\"$body\" >/dev/null\n else\n gh api --method POST \"repos/$REPO/issues/$PR_NUMBER/comments\" -f body=\"$body\" >/dev/null\n fi\n\n # __ERD_PREVIEW_COMMENT_JOB_END__\n tailor-deploy:\n # __DEPLOY_IF__\n runs-on: ubuntu-latest\n timeout-minutes: 30\n permissions:\n contents: read\n environment: __ENVIRONMENT__\n concurrency:\n group: tailor-deploy-__WORKSPACE_NAME__\n cancel-in-progress: false\n steps:\n - id: tailor-checkout\n uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0\n - id: tailor-setup\n uses: tailor-platform/actions/setup@e74173d4bdd931d036ad359cf5f7729fc6aa7067 # v1.5.1\n with:\n package-manager: __PACKAGE_MANAGER__\n - id: tailor-install\n uses: tailor-platform/actions/install@e74173d4bdd931d036ad359cf5f7729fc6aa7067 # v1.5.1\n with:\n package-manager: __PACKAGE_MANAGER__\n - id: tailor-apply\n uses: tailor-platform/actions/deploy@e74173d4bdd931d036ad359cf5f7729fc6aa7067 # v1.5.1\n with:\n workspace-id: ${{ vars.TAILOR_PLATFORM_WORKSPACE_ID }}\n package-manager: __PACKAGE_MANAGER__\n # __WORKING_DIRECTORY__\n platform-client-id: ${{ secrets.TAILOR_PLATFORM_MACHINE_USER_CLIENT_ID }}\n platform-client-secret: ${{ secrets.TAILOR_PLATFORM_MACHINE_USER_CLIENT_SECRET }}\n # __SEED_DATA_START__\n - id: seed-data\n if: false\n shell: bash\n # __WORKING_DIRECTORY__\n run: |\n # Uncomment and implement to seed data on deploy.\n # Run after apply so the schema is ready.\n # Example: node scripts/seed.mjs\n true\n # __SEED_DATA_END__\n - id: slack-prereq\n if: always()\n shell: bash\n env:\n SLACK_TOKEN: ${{ secrets.TAILOR_SLACK_BOT_TOKEN }}\n SLACK_CHANNEL_ID: ${{ vars.TAILOR_SLACK_CHANNEL_ID }}\n run: |\n if [[ -n \"$SLACK_TOKEN\" && -n \"$SLACK_CHANNEL_ID\" ]]; then\n echo \"ready=true\" >> \"$GITHUB_OUTPUT\"\n fi\n - id: tailor-notify\n if: always() && steps.slack-prereq.outputs.ready == 'true'\n uses: tailor-platform/actions/notify@e74173d4bdd931d036ad359cf5f7729fc6aa7067 # v1.5.1\n with:\n provider: slack\n status: ${{ job.status }}\n workspace-name: __WORKSPACE_NAME__\n slack-token: ${{ secrets.TAILOR_SLACK_BOT_TOKEN }}\n slack-channel-id: ${{ vars.TAILOR_SLACK_CHANNEL_ID }}\n # user-mapping: ${{ vars.TAILOR_SLACK_USER_MAPPING }}\n";
|
|
3179
|
+
|
|
3180
|
+
//#endregion
|
|
3181
|
+
//#region src/cli/commands/setup/coordinate.workflow.yml
|
|
3182
|
+
var coordinate_workflow_default = "# __HEADER__\nname: Tailor Coordinator (__WORKSPACE_NAME__)\n\non:\n # __PULL_REQUEST_START__\n pull_request:\n branches: [\"__BRANCH__\"]\n # __PULL_REQUEST_END__\n push:\n # __PUSH_BRANCHES_START__\n branches: [\"__BRANCH__\"]\n # __PUSH_BRANCHES_END__\n # __PUSH_TAGS_START__\n tags: [__TAG_PATTERN__]\n # __PUSH_TAGS_END__\n workflow_dispatch:\n inputs:\n dry-run:\n description: Preview changes without deploying\n type: boolean\n default: false\n\npermissions:\n contents: read\n\njobs:\n # __TAG_GUARD_JOB_START__\n tailor-tag-guard:\n runs-on: ubuntu-latest\n timeout-minutes: 5\n permissions:\n contents: read\n outputs:\n on-branch: ${{ steps.tailor-tag-guard.outputs.on-branch }}\n steps:\n - id: tailor-checkout\n uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0\n with:\n fetch-depth: 0\n - id: tailor-tag-guard\n uses: tailor-platform/actions/tag-guard@e74173d4bdd931d036ad359cf5f7729fc6aa7067 # v1.5.1\n with:\n target-branch: __BRANCH__\n # __TAG_GUARD_JOB_END__\n\n tailor-plan:\n # __PLAN_NEEDS__\n # __PLAN_IF__\n runs-on: ubuntu-latest\n timeout-minutes: 60\n environment: __ENVIRONMENT__\n permissions:\n contents: read\n pull-requests: write\n concurrency:\n group: tailor-plan-__WORKSPACE_NAME__-${{ github.event.pull_request.number || github.run_id }}\n cancel-in-progress: true\n steps:\n - id: tailor-checkout\n uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0\n - id: tailor-setup\n uses: ./.github/actions/tailor-setup\n # __PER_APP_PLAN_STEPS__\n\n tailor-deploy:\n # __DEPLOY_NEEDS__\n # __DEPLOY_IF__\n # __DEPLOY_ENVIRONMENT__\n runs-on: ubuntu-latest\n timeout-minutes: 60\n permissions:\n contents: read\n concurrency:\n group: tailor-deploy-__WORKSPACE_NAME__\n cancel-in-progress: false\n steps:\n - id: tailor-checkout\n uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0\n - id: tailor-setup\n uses: ./.github/actions/tailor-setup\n # __PER_APP_DEPLOY_STEPS__\n";
|
|
3183
|
+
|
|
3184
|
+
//#endregion
|
|
3185
|
+
//#region src/cli/commands/setup/preview.workflow.yml
|
|
3186
|
+
var preview_workflow_default = "# __HEADER__\nname: Tailor Preview (__WORKSPACE_NAME__)\n\non:\n pull_request:\n branches: [\"__BRANCH__\"]\n # __PR_TYPES__\n # To deploy only for PRs that carry the `tailor:preview` label instead of all PRs,\n # re-run: tailor setup preview --require-preview-label\n # __PATHS__\n\npermissions:\n contents: read\n pull-requests: write\n\njobs:\n tailor-preview-deploy:\n # __DEPLOY_IF__\n runs-on: ubuntu-latest\n timeout-minutes: 30\n environment: __ENVIRONMENT__\n permissions:\n contents: read\n pull-requests: write\n concurrency:\n group: tailor-preview-__WORKSPACE_NAME__-${{ github.event.pull_request.number }}\n cancel-in-progress: true\n steps:\n - id: tailor-checkout\n uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0\n - id: tailor-setup\n uses: tailor-platform/actions/setup@e74173d4bdd931d036ad359cf5f7729fc6aa7067 # v1.5.1\n with:\n package-manager: __PACKAGE_MANAGER__\n - id: tailor-install\n uses: tailor-platform/actions/install@e74173d4bdd931d036ad359cf5f7729fc6aa7067 # v1.5.1\n with:\n package-manager: __PACKAGE_MANAGER__\n - id: tailor-generate-check\n uses: tailor-platform/actions/generate-check@e74173d4bdd931d036ad359cf5f7729fc6aa7067 # v1.5.1\n with:\n package-manager: __PACKAGE_MANAGER__\n # __WORKING_DIRECTORY__\n - id: tailor-preview-deploy\n uses: tailor-platform/actions/preview-deploy@e74173d4bdd931d036ad359cf5f7729fc6aa7067 # v1.5.1\n with:\n workspace-name-prefix: __WORKSPACE_NAME__\n region: \"__REGION__\"\n organization-id: ${{ vars.TAILOR_PLATFORM_ORGANIZATION_ID }}\n folder-id: ${{ vars.TAILOR_PLATFORM_FOLDER_ID }}\n package-manager: __PACKAGE_MANAGER__\n # __WORKING_DIRECTORY__\n platform-client-id: ${{ secrets.TAILOR_PLATFORM_MACHINE_USER_CLIENT_ID }}\n platform-client-secret: ${{ secrets.TAILOR_PLATFORM_MACHINE_USER_CLIENT_SECRET }}\n github-token: ${{ secrets.GITHUB_TOKEN }}\n - id: tailor-preview-comment\n if: always()\n uses: tailor-platform/actions/preview-comment@e74173d4bdd931d036ad359cf5f7729fc6aa7067 # v1.5.1\n with:\n workspace-id: ${{ steps.tailor-preview-deploy.outputs.workspace-id }}\n workspace-name: ${{ steps.tailor-preview-deploy.outputs.workspace-name }}\n status: ${{ job.status == 'success' && 'success' || 'failure' }}\n app-url: ${{ steps.tailor-preview-deploy.outputs.app-url }}\n github-token: ${{ secrets.GITHUB_TOKEN }}\n\n tailor-preview-cleanup:\n # __CLEANUP_IF__\n runs-on: ubuntu-latest\n timeout-minutes: 15\n environment: __ENVIRONMENT__\n permissions:\n contents: read\n pull-requests: write\n steps:\n - id: tailor-checkout\n uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0\n - id: tailor-setup\n uses: tailor-platform/actions/setup@e74173d4bdd931d036ad359cf5f7729fc6aa7067 # v1.5.1\n with:\n package-manager: __PACKAGE_MANAGER__\n - id: tailor-install\n uses: tailor-platform/actions/install@e74173d4bdd931d036ad359cf5f7729fc6aa7067 # v1.5.1\n with:\n package-manager: __PACKAGE_MANAGER__\n - id: tailor-preview-cleanup\n uses: tailor-platform/actions/preview-cleanup@e74173d4bdd931d036ad359cf5f7729fc6aa7067 # v1.5.1\n with:\n workspace-name-prefix: __WORKSPACE_NAME__\n package-manager: __PACKAGE_MANAGER__\n # __WORKING_DIRECTORY__\n platform-client-id: ${{ secrets.TAILOR_PLATFORM_MACHINE_USER_CLIENT_ID }}\n platform-client-secret: ${{ secrets.TAILOR_PLATFORM_MACHINE_USER_CLIENT_SECRET }}\n github-token: ${{ secrets.GITHUB_TOKEN }}\n";
|
|
2644
3187
|
|
|
2645
3188
|
//#endregion
|
|
2646
3189
|
//#region src/cli/commands/setup/tag.workflow.yml
|
|
2647
|
-
var tag_workflow_default = "# __HEADER__\nname: Tailor (__WORKSPACE_NAME__)\n\non:\n push:\n tags: [\"__TAG_PATTERN__\"]\n workflow_dispatch:\n inputs:\n dry-run:\n description: Preview changes without deploying\n type: boolean\n default: false\n\npermissions:\n contents: read\n\njobs:\n # __TAG_GUARD_JOB_START__\n tailor-tag-guard:\n runs-on: ubuntu-latest\n timeout-minutes: 10\n permissions:\n contents: read\n outputs:\n on-branch: ${{ steps.tailor-tag-guard.outputs.on-branch }}\n steps:\n - id: tailor-checkout\n uses: actions/checkout@
|
|
3190
|
+
var tag_workflow_default = "# __HEADER__\nname: Tailor (__WORKSPACE_NAME__)\n\non:\n push:\n tags: [\"__TAG_PATTERN__\"]\n workflow_dispatch:\n inputs:\n dry-run:\n description: Preview changes without deploying\n type: boolean\n default: false\n\npermissions:\n contents: read\n\njobs:\n # __TAG_GUARD_JOB_START__\n tailor-tag-guard:\n runs-on: ubuntu-latest\n timeout-minutes: 10\n permissions:\n contents: read\n outputs:\n on-branch: ${{ steps.tailor-tag-guard.outputs.on-branch }}\n steps:\n - id: tailor-checkout\n uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0\n with:\n fetch-depth: 0\n - id: tailor-tag-guard\n uses: tailor-platform/actions/tag-guard@e74173d4bdd931d036ad359cf5f7729fc6aa7067 # v1.5.1\n with:\n target-branch: \"__BRANCH__\"\n # __TAG_GUARD_JOB_END__\n tailor-plan:\n # __PLAN_NEEDS__\n # __PLAN_IF__\n runs-on: ubuntu-latest\n timeout-minutes: 30\n environment: __ENVIRONMENT__\n permissions:\n contents: read\n steps:\n - id: tailor-checkout\n uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0\n - id: tailor-setup\n uses: tailor-platform/actions/setup@e74173d4bdd931d036ad359cf5f7729fc6aa7067 # v1.5.1\n with:\n package-manager: __PACKAGE_MANAGER__\n - id: tailor-install\n uses: tailor-platform/actions/install@e74173d4bdd931d036ad359cf5f7729fc6aa7067 # v1.5.1\n with:\n package-manager: __PACKAGE_MANAGER__\n - id: tailor-generate-check\n uses: tailor-platform/actions/generate-check@e74173d4bdd931d036ad359cf5f7729fc6aa7067 # v1.5.1\n with:\n package-manager: __PACKAGE_MANAGER__\n # __WORKING_DIRECTORY__\n # __SEED_VALIDATE_START__\n - id: tailor-seed-validate\n uses: tailor-platform/actions/seed-validate@e74173d4bdd931d036ad359cf5f7729fc6aa7067 # v1.5.1\n with:\n package-manager: __PACKAGE_MANAGER__\n # __WORKING_DIRECTORY__\n # __SEED_VALIDATE_END__\n - id: tailor-drift-check\n uses: tailor-platform/actions/drift-check@e74173d4bdd931d036ad359cf5f7729fc6aa7067 # v1.5.1\n with:\n package-manager: __PACKAGE_MANAGER__\n # __MIGRATION_DRIFT_CHECK_START__\n - id: tailor-migration-drift-check\n uses: tailor-platform/actions/migration-drift-check@e74173d4bdd931d036ad359cf5f7729fc6aa7067 # v1.5.1\n with:\n package-manager: __PACKAGE_MANAGER__\n # __WORKING_DIRECTORY__\n # __MIGRATION_DRIFT_CHECK_END__\n - id: tailor-plan\n uses: tailor-platform/actions/plan@e74173d4bdd931d036ad359cf5f7729fc6aa7067 # v1.5.1\n with:\n workspace-id: ${{ vars.TAILOR_PLATFORM_WORKSPACE_ID }}\n package-manager: __PACKAGE_MANAGER__\n label: __WORKSPACE_NAME__\n # __WORKING_DIRECTORY__\n platform-client-id: ${{ secrets.TAILOR_PLATFORM_MACHINE_USER_CLIENT_ID }}\n platform-client-secret: ${{ secrets.TAILOR_PLATFORM_MACHINE_USER_CLIENT_SECRET }}\n\n tailor-deploy:\n needs: tailor-plan\n if: ${{ !(github.event_name == 'workflow_dispatch' && inputs['dry-run']) }}\n environment: __ENVIRONMENT__\n runs-on: ubuntu-latest\n timeout-minutes: 30\n permissions:\n contents: read\n concurrency:\n group: tailor-deploy-__WORKSPACE_NAME__\n cancel-in-progress: false\n steps:\n - id: tailor-checkout\n uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0\n - id: tailor-setup\n uses: tailor-platform/actions/setup@e74173d4bdd931d036ad359cf5f7729fc6aa7067 # v1.5.1\n with:\n package-manager: __PACKAGE_MANAGER__\n - id: tailor-install\n uses: tailor-platform/actions/install@e74173d4bdd931d036ad359cf5f7729fc6aa7067 # v1.5.1\n with:\n package-manager: __PACKAGE_MANAGER__\n - id: tailor-apply\n uses: tailor-platform/actions/deploy@e74173d4bdd931d036ad359cf5f7729fc6aa7067 # v1.5.1\n with:\n workspace-id: ${{ vars.TAILOR_PLATFORM_WORKSPACE_ID }}\n package-manager: __PACKAGE_MANAGER__\n # __WORKING_DIRECTORY__\n platform-client-id: ${{ secrets.TAILOR_PLATFORM_MACHINE_USER_CLIENT_ID }}\n platform-client-secret: ${{ secrets.TAILOR_PLATFORM_MACHINE_USER_CLIENT_SECRET }}\n # __SEED_DATA_START__\n - id: seed-data\n if: false\n shell: bash\n # __WORKING_DIRECTORY__\n run: |\n # Uncomment and implement to seed data on deploy.\n # Run after apply so the schema is ready.\n # Example: node scripts/seed.mjs\n true\n # __SEED_DATA_END__\n - id: slack-prereq\n if: always()\n shell: bash\n env:\n SLACK_TOKEN: ${{ secrets.TAILOR_SLACK_BOT_TOKEN }}\n SLACK_CHANNEL_ID: ${{ vars.TAILOR_SLACK_CHANNEL_ID }}\n run: |\n if [[ -n \"$SLACK_TOKEN\" && -n \"$SLACK_CHANNEL_ID\" ]]; then\n echo \"ready=true\" >> \"$GITHUB_OUTPUT\"\n fi\n - id: tailor-notify\n if: always() && steps.slack-prereq.outputs.ready == 'true'\n uses: tailor-platform/actions/notify@e74173d4bdd931d036ad359cf5f7729fc6aa7067 # v1.5.1\n with:\n provider: slack\n status: ${{ job.status }}\n workspace-name: __WORKSPACE_NAME__\n slack-token: ${{ secrets.TAILOR_SLACK_BOT_TOKEN }}\n slack-channel-id: ${{ vars.TAILOR_SLACK_CHANNEL_ID }}\n # user-mapping: ${{ vars.TAILOR_SLACK_USER_MAPPING }}\n";
|
|
2648
3191
|
|
|
2649
3192
|
//#endregion
|
|
2650
3193
|
//#region src/cli/commands/setup/templates.ts
|
|
2651
|
-
const HEADER = `# Generated by \`tailor
|
|
3194
|
+
const HEADER = `# Generated by \`tailor setup\` — managed by the Tailor SDK.
|
|
2652
3195
|
#
|
|
2653
3196
|
# - Jobs and steps whose id starts with \`tailor-\` are managed by the SDK.
|
|
2654
3197
|
# Do not edit or rename them.
|
|
2655
|
-
# - State is tracked in .github/tailor
|
|
2656
|
-
# - Re-running \`tailor
|
|
3198
|
+
# - State is tracked in .github/tailor.lock (machine-owned: commit it, never edit it).
|
|
3199
|
+
# - Re-running \`tailor setup\` regenerates this file. If you have
|
|
2657
3200
|
# edited it by hand, regeneration stops and asks for --force (which discards
|
|
2658
3201
|
# your edits), so prefer keeping customizations in your own jobs/steps and
|
|
2659
3202
|
# re-running setup after SDK updates.`;
|
|
@@ -2684,29 +3227,43 @@ function applyCommon(content, params) {
|
|
|
2684
3227
|
const { workingDirectory, environment, packageManager } = params;
|
|
2685
3228
|
let out = line(content, "HEADER", HEADER);
|
|
2686
3229
|
out = line(out, "WORKING_DIRECTORY", workingDirectory ? `working-directory: ${workingDirectory}` : void 0);
|
|
2687
|
-
return out.replaceAll("__WORKSPACE_NAME__", () => params.workspaceName).replaceAll("__ENVIRONMENT__", () => environment).replaceAll("__PACKAGE_MANAGER__", () => packageManager);
|
|
3230
|
+
return out.replaceAll("__WORKSPACE_NAME__", () => params.workspaceName).replaceAll("__ENVIRONMENT__", () => environment).replaceAll("__PACKAGE_MANAGER__", () => packageManager).replaceAll("__APP_DIR__", () => workingDirectory ?? ".");
|
|
2688
3231
|
}
|
|
2689
3232
|
/**
|
|
2690
3233
|
* Render the branch-target deploy workflow.
|
|
2691
|
-
*
|
|
2692
|
-
* When `plan` is false the plan job, the pull_request trigger, and the
|
|
2693
|
-
* workflow_dispatch dry-run input are all removed (a plan-less workflow has no
|
|
2694
|
-
* meaningful dry-run), and the deploy job runs unconditionally.
|
|
2695
3234
|
* @param params - Workspace and rendering configuration
|
|
2696
3235
|
* @returns Rendered YAML and the list of managed job/step ids
|
|
2697
3236
|
*/
|
|
2698
3237
|
function renderBranchWorkflow(params) {
|
|
2699
|
-
const { branch
|
|
3238
|
+
const { branch } = params;
|
|
3239
|
+
const seedValidate = params.seedValidate ?? false;
|
|
3240
|
+
const migrationDriftCheck = params.migrationDriftCheck ?? false;
|
|
3241
|
+
const erdPreview = params.erdPreview;
|
|
2700
3242
|
let out = branch_workflow_default;
|
|
2701
|
-
out = block(out, "PLAN_JOB",
|
|
2702
|
-
out = block(out, "
|
|
2703
|
-
out = block(out, "
|
|
2704
|
-
out =
|
|
3243
|
+
out = block(out, "PLAN_JOB", true);
|
|
3244
|
+
out = block(out, "ERD_PREVIEW_JOB", erdPreview !== null);
|
|
3245
|
+
out = block(out, "ERD_PREVIEW_COMMENT_JOB", erdPreview !== null);
|
|
3246
|
+
out = block(out, "PULL_REQUEST", true);
|
|
3247
|
+
out = block(out, "DISPATCH_INPUTS", true);
|
|
3248
|
+
out = block(out, "SEED_VALIDATE", seedValidate);
|
|
3249
|
+
out = block(out, "MIGRATION_DRIFT_CHECK", migrationDriftCheck);
|
|
3250
|
+
out = block(out, "SEED_DATA", false);
|
|
3251
|
+
out = line(out, "DEPLOY_IF", `if: >-\n github.event_name == 'push' ||\n (github.event_name == 'workflow_dispatch' && !inputs['dry-run'])`);
|
|
2705
3252
|
out = line(out, "PATHS", params.workingDirectory ? `paths: ["${params.workingDirectory}/**"]` : void 0);
|
|
2706
3253
|
out = applyCommon(out, params).replaceAll("__BRANCH__", () => branch);
|
|
2707
|
-
const generatedIds = [
|
|
2708
|
-
|
|
2709
|
-
|
|
3254
|
+
const generatedIds = [
|
|
3255
|
+
"tailor-plan",
|
|
3256
|
+
"tailor-plan/tailor-checkout",
|
|
3257
|
+
"tailor-plan/tailor-setup",
|
|
3258
|
+
"tailor-plan/tailor-install",
|
|
3259
|
+
"tailor-plan/tailor-generate-check"
|
|
3260
|
+
];
|
|
3261
|
+
if (seedValidate) generatedIds.push("tailor-plan/tailor-seed-validate");
|
|
3262
|
+
generatedIds.push("tailor-plan/tailor-drift-check");
|
|
3263
|
+
if (migrationDriftCheck) generatedIds.push("tailor-plan/tailor-migration-drift-check");
|
|
3264
|
+
generatedIds.push("tailor-plan/tailor-plan");
|
|
3265
|
+
if (erdPreview) generatedIds.push("tailor-erd-preview-matrix", "tailor-erd-preview-matrix/tailor-checkout", "tailor-erd-preview-matrix/tailor-checkout-base", "tailor-erd-preview-matrix/tailor-erd-preview-matrix", "tailor-erd-preview", "tailor-erd-preview/tailor-checkout", "tailor-erd-preview/tailor-setup", "tailor-erd-preview/tailor-checkout-base", "tailor-erd-preview/tailor-detect-base-package-manager", "tailor-erd-preview/tailor-setup-base-pnpm", "tailor-erd-preview/tailor-setup-base-node", "tailor-erd-preview/tailor-setup-base-yarn", "tailor-erd-preview/tailor-setup-base-bun", "tailor-erd-preview/tailor-install-base", "tailor-erd-preview/tailor-restore-head-setup", "tailor-erd-preview/tailor-build-erd-preview", "tailor-erd-preview/tailor-upload-erd-viewer", "tailor-erd-preview-comment", "tailor-erd-preview-comment/tailor-comment-erd-preview");
|
|
3266
|
+
generatedIds.push("tailor-deploy", "tailor-deploy/tailor-checkout", "tailor-deploy/tailor-setup", "tailor-deploy/tailor-install", "tailor-deploy/tailor-apply", "tailor-deploy/tailor-notify");
|
|
2710
3267
|
return {
|
|
2711
3268
|
content: out,
|
|
2712
3269
|
generatedIds
|
|
@@ -2723,133 +3280,323 @@ function renderBranchWorkflow(params) {
|
|
|
2723
3280
|
function renderTagWorkflow(params) {
|
|
2724
3281
|
const { tagPattern, branch } = params;
|
|
2725
3282
|
const hasGuard = branch !== void 0;
|
|
3283
|
+
const seedValidate = params.seedValidate ?? false;
|
|
3284
|
+
const migrationDriftCheck = params.migrationDriftCheck ?? false;
|
|
2726
3285
|
let out = tag_workflow_default;
|
|
2727
3286
|
out = block(out, "TAG_GUARD_JOB", hasGuard);
|
|
3287
|
+
out = block(out, "SEED_VALIDATE", seedValidate);
|
|
3288
|
+
out = block(out, "MIGRATION_DRIFT_CHECK", migrationDriftCheck);
|
|
3289
|
+
out = block(out, "SEED_DATA", false);
|
|
2728
3290
|
out = line(out, "PLAN_NEEDS", hasGuard ? "needs: tailor-tag-guard" : void 0);
|
|
2729
3291
|
out = line(out, "PLAN_IF", hasGuard ? `if: >-\n github.event_name == 'workflow_dispatch' ||\n needs.tailor-tag-guard.outputs.on-branch == 'true'` : void 0);
|
|
2730
3292
|
out = applyCommon(out, params).replaceAll("__TAG_PATTERN__", () => tagPattern);
|
|
2731
3293
|
if (hasGuard) out = out.replaceAll("__BRANCH__", () => branch);
|
|
2732
3294
|
const generatedIds = [];
|
|
2733
3295
|
if (hasGuard) generatedIds.push("tailor-tag-guard", "tailor-tag-guard/tailor-checkout", "tailor-tag-guard/tailor-tag-guard");
|
|
2734
|
-
generatedIds.push("tailor-plan", "tailor-plan/tailor-checkout", "tailor-plan/tailor-setup", "tailor-plan/tailor-
|
|
3296
|
+
generatedIds.push("tailor-plan", "tailor-plan/tailor-checkout", "tailor-plan/tailor-setup", "tailor-plan/tailor-install", "tailor-plan/tailor-generate-check");
|
|
3297
|
+
if (seedValidate) generatedIds.push("tailor-plan/tailor-seed-validate");
|
|
3298
|
+
generatedIds.push("tailor-plan/tailor-drift-check");
|
|
3299
|
+
if (migrationDriftCheck) generatedIds.push("tailor-plan/tailor-migration-drift-check");
|
|
3300
|
+
generatedIds.push("tailor-plan/tailor-plan", "tailor-deploy", "tailor-deploy/tailor-checkout", "tailor-deploy/tailor-setup", "tailor-deploy/tailor-install", "tailor-deploy/tailor-apply", "tailor-deploy/tailor-notify");
|
|
2735
3301
|
return {
|
|
2736
3302
|
content: out,
|
|
2737
3303
|
generatedIds
|
|
2738
3304
|
};
|
|
2739
3305
|
}
|
|
2740
|
-
|
|
2741
|
-
//#endregion
|
|
2742
|
-
//#region src/cli/commands/setup/check.ts
|
|
2743
3306
|
/**
|
|
2744
|
-
*
|
|
2745
|
-
*
|
|
2746
|
-
*
|
|
2747
|
-
*
|
|
2748
|
-
*
|
|
3307
|
+
* Render the preview workflow (PR open/sync/close triggers).
|
|
3308
|
+
*
|
|
3309
|
+
* The deploy job runs on opened/synchronize/reopened events; the cleanup job
|
|
3310
|
+
* runs on the closed event. Both jobs run in the same workflow so PR number
|
|
3311
|
+
* context is always available.
|
|
3312
|
+
* @param params - Workspace and rendering configuration
|
|
3313
|
+
* @returns Rendered YAML and the list of managed job/step ids
|
|
2749
3314
|
*/
|
|
2750
|
-
function
|
|
2751
|
-
const
|
|
2752
|
-
const
|
|
2753
|
-
|
|
2754
|
-
|
|
2755
|
-
|
|
2756
|
-
|
|
2757
|
-
|
|
2758
|
-
|
|
2759
|
-
|
|
2760
|
-
|
|
2761
|
-
|
|
2762
|
-
|
|
2763
|
-
|
|
2764
|
-
|
|
2765
|
-
|
|
2766
|
-
|
|
2767
|
-
|
|
2768
|
-
|
|
2769
|
-
|
|
2770
|
-
|
|
2771
|
-
|
|
2772
|
-
|
|
2773
|
-
|
|
2774
|
-
|
|
2775
|
-
|
|
2776
|
-
message: `The workflow triggers on "${target.inputs.branch}" but the repository default branch is now "${state.defaultBranch}". If this is intentional, ignore this; otherwise re-run setup so the trigger matches the default branch.`
|
|
2777
|
-
});
|
|
2778
|
-
return findings;
|
|
2779
|
-
}
|
|
2780
|
-
function detectDefaultBranchSafe(cwd, run) {
|
|
2781
|
-
try {
|
|
2782
|
-
return detectDefaultBranch(cwd, run);
|
|
2783
|
-
} catch {
|
|
2784
|
-
return null;
|
|
2785
|
-
}
|
|
2786
|
-
}
|
|
2787
|
-
function escapesRoot$1(rel) {
|
|
2788
|
-
return path.isAbsolute(rel) || rel === ".." || rel.startsWith(`..${path.sep}`) || rel.startsWith("../");
|
|
2789
|
-
}
|
|
2790
|
-
function resolveWithinRoot(outputDir, relPath) {
|
|
2791
|
-
if (path.isAbsolute(relPath)) return null;
|
|
2792
|
-
const abs = path.join(outputDir, relPath);
|
|
2793
|
-
if (escapesRoot$1(path.relative(outputDir, abs))) return null;
|
|
2794
|
-
try {
|
|
2795
|
-
if (escapesRoot$1(path.relative(fs$1.realpathSync(outputDir), fs$1.realpathSync(abs)))) return null;
|
|
2796
|
-
} catch {}
|
|
2797
|
-
return abs;
|
|
3315
|
+
function renderPreviewWorkflow(params) {
|
|
3316
|
+
const { branch } = params;
|
|
3317
|
+
const requirePreviewLabel = params.requirePreviewLabel ?? false;
|
|
3318
|
+
let out = preview_workflow_default;
|
|
3319
|
+
out = line(out, "PATHS", params.workingDirectory ? `paths: ["${params.workingDirectory}/**"]` : void 0);
|
|
3320
|
+
out = line(out, "PR_TYPES", requirePreviewLabel ? "types: [labeled, synchronize, reopened, closed]" : "types: [opened, synchronize, reopened, closed]");
|
|
3321
|
+
out = line(out, "DEPLOY_IF", requirePreviewLabel ? `if: >-\n contains(github.event.pull_request.labels.*.name, 'tailor:preview') &&\n github.event.action != 'closed' &&\n !github.event.pull_request.draft &&\n !github.event.pull_request.head.repo.fork` : `if: >-\n github.event.action != 'closed' &&\n !github.event.pull_request.draft &&\n !github.event.pull_request.head.repo.fork`);
|
|
3322
|
+
out = line(out, "CLEANUP_IF", `if: >-\n github.event.action == 'closed' &&\n !github.event.pull_request.head.repo.fork`);
|
|
3323
|
+
out = applyCommon(out, params).replaceAll("__BRANCH__", () => branch).replaceAll("__REGION__", () => params.region);
|
|
3324
|
+
return {
|
|
3325
|
+
content: out,
|
|
3326
|
+
generatedIds: [
|
|
3327
|
+
"tailor-preview-deploy",
|
|
3328
|
+
"tailor-preview-deploy/tailor-checkout",
|
|
3329
|
+
"tailor-preview-deploy/tailor-setup",
|
|
3330
|
+
"tailor-preview-deploy/tailor-install",
|
|
3331
|
+
"tailor-preview-deploy/tailor-generate-check",
|
|
3332
|
+
"tailor-preview-deploy/tailor-preview-deploy",
|
|
3333
|
+
"tailor-preview-deploy/tailor-preview-comment",
|
|
3334
|
+
"tailor-preview-cleanup",
|
|
3335
|
+
"tailor-preview-cleanup/tailor-checkout",
|
|
3336
|
+
"tailor-preview-cleanup/tailor-setup",
|
|
3337
|
+
"tailor-preview-cleanup/tailor-install",
|
|
3338
|
+
"tailor-preview-cleanup/tailor-preview-cleanup"
|
|
3339
|
+
]
|
|
3340
|
+
};
|
|
2798
3341
|
}
|
|
2799
|
-
|
|
2800
|
-
|
|
2801
|
-
|
|
2802
|
-
|
|
2803
|
-
|
|
2804
|
-
|
|
3342
|
+
/**
|
|
3343
|
+
* Render the per-app composite action.
|
|
3344
|
+
*
|
|
3345
|
+
* The action contains only the deploy step and a failure notification.
|
|
3346
|
+
* Checkout, setup, and install are the caller's responsibility (coordinator workflow).
|
|
3347
|
+
* @param params - Workspace and rendering configuration
|
|
3348
|
+
* @returns Rendered YAML and the list of managed step ids
|
|
3349
|
+
*/
|
|
3350
|
+
function renderActionWorkflow(params) {
|
|
3351
|
+
let out = action_default;
|
|
3352
|
+
out = line(out, "HEADER", HEADER);
|
|
3353
|
+
out = block(out, "STATIC_WEBSITE_BUILD", params.hasStaticWebsites ?? false);
|
|
3354
|
+
out = out.replaceAll("__WORKSPACE_NAME__", () => params.workspaceName);
|
|
3355
|
+
return {
|
|
3356
|
+
content: out,
|
|
3357
|
+
generatedIds: ["tailor-apply", "tailor-notify"]
|
|
3358
|
+
};
|
|
2805
3359
|
}
|
|
3360
|
+
const ACTIONS_SHA = "e74173d4bdd931d036ad359cf5f7729fc6aa7067";
|
|
2806
3361
|
/**
|
|
2807
|
-
*
|
|
2808
|
-
* state. Read-only: never writes files, the lock, or the config.
|
|
3362
|
+
* Render the coordinator workflow that orchestrates per-app composite actions.
|
|
2809
3363
|
*
|
|
2810
|
-
*
|
|
2811
|
-
*
|
|
2812
|
-
*
|
|
2813
|
-
* @param
|
|
3364
|
+
* Generates per-app plan steps (generate-check, drift-check, plan) and per-app
|
|
3365
|
+
* deploy steps (calling each app's composite action at .github/actions/tailor-<name>).
|
|
3366
|
+
* Checkout, setup, and install are done once per job using the local tailor-setup action.
|
|
3367
|
+
* @param params - Coordinator and app configuration
|
|
3368
|
+
* @returns Rendered YAML and the list of managed job/step ids
|
|
2814
3369
|
*/
|
|
2815
|
-
function
|
|
2816
|
-
|
|
2817
|
-
const
|
|
2818
|
-
const
|
|
2819
|
-
|
|
2820
|
-
|
|
2821
|
-
|
|
2822
|
-
|
|
2823
|
-
|
|
2824
|
-
|
|
2825
|
-
|
|
2826
|
-
|
|
2827
|
-
|
|
2828
|
-
|
|
2829
|
-
|
|
2830
|
-
|
|
2831
|
-
|
|
2832
|
-
|
|
2833
|
-
|
|
3370
|
+
function renderCoordinateWorkflow(params) {
|
|
3371
|
+
const { coordinatorName, kind, actionGroups, environment, packageManager } = params;
|
|
3372
|
+
const isTag = kind === "tag";
|
|
3373
|
+
const branch = params.branch;
|
|
3374
|
+
const tagPattern = params.tagPattern ?? "v*";
|
|
3375
|
+
let out = coordinate_workflow_default;
|
|
3376
|
+
out = line(out, "HEADER", HEADER);
|
|
3377
|
+
out = block(out, "PULL_REQUEST", !isTag);
|
|
3378
|
+
out = block(out, "PUSH_BRANCHES", !isTag);
|
|
3379
|
+
out = block(out, "PUSH_TAGS", isTag);
|
|
3380
|
+
out = block(out, "TAG_GUARD_JOB", isTag && branch !== void 0);
|
|
3381
|
+
if (isTag && branch !== void 0) {
|
|
3382
|
+
out = line(out, "PLAN_NEEDS", "needs: tailor-tag-guard");
|
|
3383
|
+
out = line(out, "PLAN_IF", `if: >-\n github.event_name == 'workflow_dispatch' ||\n needs.tailor-tag-guard.outputs.on-branch == 'true'`);
|
|
3384
|
+
} else if (isTag) {
|
|
3385
|
+
out = line(out, "PLAN_NEEDS", void 0);
|
|
3386
|
+
out = line(out, "PLAN_IF", void 0);
|
|
3387
|
+
} else {
|
|
3388
|
+
out = line(out, "PLAN_NEEDS", void 0);
|
|
3389
|
+
out = line(out, "PLAN_IF", `if: >-\n github.event_name == 'pull_request' ||\n (github.event_name == 'workflow_dispatch' && inputs['dry-run'])`);
|
|
2834
3390
|
}
|
|
2835
|
-
|
|
2836
|
-
|
|
2837
|
-
|
|
2838
|
-
|
|
3391
|
+
if (isTag) {
|
|
3392
|
+
out = line(out, "DEPLOY_NEEDS", "needs: tailor-plan");
|
|
3393
|
+
out = line(out, "DEPLOY_IF", `if: \${{ !(github.event_name == 'workflow_dispatch' && inputs['dry-run']) }}`);
|
|
3394
|
+
out = line(out, "DEPLOY_ENVIRONMENT", `environment: ${environment}`);
|
|
3395
|
+
} else {
|
|
3396
|
+
out = line(out, "DEPLOY_NEEDS", void 0);
|
|
3397
|
+
out = line(out, "DEPLOY_IF", `if: >-\n github.event_name == 'push' ||\n (github.event_name == 'workflow_dispatch' && !inputs['dry-run'])`);
|
|
3398
|
+
out = line(out, "DEPLOY_ENVIRONMENT", `environment: ${environment}`);
|
|
3399
|
+
}
|
|
3400
|
+
const driftCheckStep = [
|
|
3401
|
+
`- id: tailor-drift-check`,
|
|
3402
|
+
` uses: tailor-platform/actions/drift-check@${ACTIONS_SHA} # v1.5.1`,
|
|
3403
|
+
` with:`,
|
|
3404
|
+
` package-manager: ${packageManager}`
|
|
3405
|
+
].join("\n");
|
|
3406
|
+
const configPath = (app) => app.dir === "." ? "tailor.config.ts" : `${app.dir}/tailor.config.ts`;
|
|
3407
|
+
const groupConfigPath = (group) => group.apps.map(configPath).join(",");
|
|
3408
|
+
const firstApp = (group) => {
|
|
3409
|
+
const [app] = group.apps;
|
|
3410
|
+
if (!app) throw new Error("Coordinate action groups must contain at least one app.");
|
|
3411
|
+
return app;
|
|
3412
|
+
};
|
|
3413
|
+
const singleAppPlanStep = (group, app) => {
|
|
3414
|
+
const wd = app.dir !== "." ? app.dir : void 0;
|
|
3415
|
+
const wdLine = wd ? `\n working-directory: ${wd}` : "";
|
|
3416
|
+
return [
|
|
3417
|
+
`- id: tailor-plan-${group.id}`,
|
|
3418
|
+
` if: github.event_name != 'pull_request' || !github.event.pull_request.head.repo.fork`,
|
|
3419
|
+
` uses: tailor-platform/actions/plan@${ACTIONS_SHA} # v1.5.1`,
|
|
3420
|
+
` with:`,
|
|
3421
|
+
` workspace-id: \${{ vars.TAILOR_PLATFORM_WORKSPACE_ID }}`,
|
|
3422
|
+
` package-manager: ${packageManager}${wdLine}`,
|
|
3423
|
+
` label: ${coordinatorName}/${group.id}`,
|
|
3424
|
+
` platform-client-id: \${{ secrets.TAILOR_PLATFORM_MACHINE_USER_CLIENT_ID }}`,
|
|
3425
|
+
` platform-client-secret: \${{ secrets.TAILOR_PLATFORM_MACHINE_USER_CLIENT_SECRET }}`,
|
|
3426
|
+
` github-token: \${{ secrets.GITHUB_TOKEN }}`
|
|
3427
|
+
];
|
|
3428
|
+
};
|
|
3429
|
+
const multiConfigPlanStep = (group) => [
|
|
3430
|
+
`- id: tailor-plan-${group.id}`,
|
|
3431
|
+
` if: github.event_name != 'pull_request' || !github.event.pull_request.head.repo.fork`,
|
|
3432
|
+
` uses: tailor-platform/actions/plan@${ACTIONS_SHA} # v1.5.1`,
|
|
3433
|
+
` env:`,
|
|
3434
|
+
` TAILOR_PLATFORM_SDK_CONFIG_PATH: ${groupConfigPath(group)}`,
|
|
3435
|
+
` with:`,
|
|
3436
|
+
` workspace-id: \${{ vars.TAILOR_PLATFORM_WORKSPACE_ID }}`,
|
|
3437
|
+
` package-manager: ${packageManager}`,
|
|
3438
|
+
` label: ${coordinatorName}/${group.id}`,
|
|
3439
|
+
` platform-client-id: \${{ secrets.TAILOR_PLATFORM_MACHINE_USER_CLIENT_ID }}`,
|
|
3440
|
+
` platform-client-secret: \${{ secrets.TAILOR_PLATFORM_MACHINE_USER_CLIENT_SECRET }}`,
|
|
3441
|
+
` github-token: \${{ secrets.GITHUB_TOKEN }}`
|
|
3442
|
+
];
|
|
3443
|
+
const perAppPlanSteps = actionGroups.flatMap((group) => {
|
|
3444
|
+
const generateCheckSteps = group.apps.flatMap((app) => {
|
|
3445
|
+
const wd = app.dir !== "." ? app.dir : void 0;
|
|
3446
|
+
const wdLine = wd ? `\n working-directory: ${wd}` : "";
|
|
3447
|
+
return [
|
|
3448
|
+
`- id: tailor-generate-check-${app.name}`,
|
|
3449
|
+
` uses: tailor-platform/actions/generate-check@${ACTIONS_SHA} # v1.5.1`,
|
|
3450
|
+
` with:`,
|
|
3451
|
+
` package-manager: ${packageManager}${wdLine}`
|
|
3452
|
+
];
|
|
3453
|
+
});
|
|
3454
|
+
const planStep = group.apps.length === 1 ? singleAppPlanStep(group, firstApp(group)) : multiConfigPlanStep(group);
|
|
3455
|
+
return [...generateCheckSteps, ...planStep];
|
|
3456
|
+
}).join("\n");
|
|
3457
|
+
out = line(out, "PER_APP_PLAN_STEPS", `${driftCheckStep}\n${perAppPlanSteps}`);
|
|
3458
|
+
const singleAppDeployStep = (group, app) => [
|
|
3459
|
+
`- id: tailor-deploy-${group.id}`,
|
|
3460
|
+
` uses: ./.github/actions/tailor-${app.name}`,
|
|
3461
|
+
` with:`,
|
|
3462
|
+
` workspace-id: \${{ vars.TAILOR_PLATFORM_WORKSPACE_ID }}`,
|
|
3463
|
+
` name: ${app.name}`,
|
|
3464
|
+
` working-directory: ${app.dir}`,
|
|
3465
|
+
` package-manager: ${packageManager}`,
|
|
3466
|
+
` platform-client-id: \${{ secrets.TAILOR_PLATFORM_MACHINE_USER_CLIENT_ID }}`,
|
|
3467
|
+
` platform-client-secret: \${{ secrets.TAILOR_PLATFORM_MACHINE_USER_CLIENT_SECRET }}`,
|
|
3468
|
+
` slack-token: \${{ secrets.TAILOR_SLACK_BOT_TOKEN }}`,
|
|
3469
|
+
` slack-channel-id: \${{ vars.TAILOR_SLACK_CHANNEL_ID }}`,
|
|
3470
|
+
` # editable: api-url:`
|
|
3471
|
+
];
|
|
3472
|
+
const buildSiteStep = (app) => [
|
|
3473
|
+
`- id: tailor-build-site-${app.name}`,
|
|
3474
|
+
` uses: ./.github/actions/tailor-${app.name}`,
|
|
3475
|
+
` with:`,
|
|
3476
|
+
` workspace-id: \${{ vars.TAILOR_PLATFORM_WORKSPACE_ID }}`,
|
|
3477
|
+
` name: ${app.name}`,
|
|
3478
|
+
` working-directory: ${app.dir}`,
|
|
3479
|
+
` deploy: "false"`,
|
|
3480
|
+
` package-manager: ${packageManager}`,
|
|
3481
|
+
` platform-client-id: \${{ secrets.TAILOR_PLATFORM_MACHINE_USER_CLIENT_ID }}`,
|
|
3482
|
+
` platform-client-secret: \${{ secrets.TAILOR_PLATFORM_MACHINE_USER_CLIENT_SECRET }}`,
|
|
3483
|
+
` slack-token: \${{ secrets.TAILOR_SLACK_BOT_TOKEN }}`,
|
|
3484
|
+
` slack-channel-id: \${{ vars.TAILOR_SLACK_CHANNEL_ID }}`
|
|
3485
|
+
];
|
|
3486
|
+
const multiConfigDeployStep = (group) => [
|
|
3487
|
+
...group.apps.filter((app) => app.hasStaticWebsites).flatMap(buildSiteStep),
|
|
3488
|
+
`- id: tailor-deploy-${group.id}`,
|
|
3489
|
+
` uses: ./.github/actions/tailor-${firstApp(group).name}`,
|
|
3490
|
+
` with:`,
|
|
3491
|
+
` workspace-id: \${{ vars.TAILOR_PLATFORM_WORKSPACE_ID }}`,
|
|
3492
|
+
` name: ${coordinatorName}/${group.id}`,
|
|
3493
|
+
` working-directory: .`,
|
|
3494
|
+
` config: ${groupConfigPath(group)}`,
|
|
3495
|
+
` build-site: "false"`,
|
|
3496
|
+
` package-manager: ${packageManager}`,
|
|
3497
|
+
` platform-client-id: \${{ secrets.TAILOR_PLATFORM_MACHINE_USER_CLIENT_ID }}`,
|
|
3498
|
+
` platform-client-secret: \${{ secrets.TAILOR_PLATFORM_MACHINE_USER_CLIENT_SECRET }}`,
|
|
3499
|
+
` slack-token: \${{ secrets.TAILOR_SLACK_BOT_TOKEN }}`,
|
|
3500
|
+
` slack-channel-id: \${{ vars.TAILOR_SLACK_CHANNEL_ID }}`,
|
|
3501
|
+
` # editable: api-url:`
|
|
3502
|
+
];
|
|
3503
|
+
const deploySteps = actionGroups.flatMap((group) => group.apps.length === 1 ? singleAppDeployStep(group, firstApp(group)) : multiConfigDeployStep(group)).join("\n");
|
|
3504
|
+
out = line(out, "PER_APP_DEPLOY_STEPS", deploySteps);
|
|
3505
|
+
out = out.replaceAll("__WORKSPACE_NAME__", () => coordinatorName);
|
|
3506
|
+
out = out.replaceAll("__ENVIRONMENT__", () => environment);
|
|
3507
|
+
if (branch !== void 0) out = out.replaceAll("__BRANCH__", () => branch);
|
|
3508
|
+
if (isTag) out = out.replaceAll("__TAG_PATTERN__", () => tagPattern);
|
|
3509
|
+
const generatedIds = [];
|
|
3510
|
+
if (isTag && branch !== void 0) generatedIds.push("tailor-tag-guard", "tailor-tag-guard/tailor-checkout", "tailor-tag-guard/tailor-tag-guard");
|
|
3511
|
+
generatedIds.push("tailor-plan", "tailor-plan/tailor-checkout", "tailor-plan/tailor-setup", "tailor-plan/tailor-drift-check");
|
|
3512
|
+
for (const group of actionGroups) {
|
|
3513
|
+
for (const app of group.apps) generatedIds.push(`tailor-plan/tailor-generate-check-${app.name}`);
|
|
3514
|
+
generatedIds.push(`tailor-plan/tailor-plan-${group.id}`);
|
|
2839
3515
|
}
|
|
2840
|
-
|
|
2841
|
-
|
|
3516
|
+
generatedIds.push("tailor-deploy", "tailor-deploy/tailor-checkout", "tailor-deploy/tailor-setup");
|
|
3517
|
+
for (const group of actionGroups) {
|
|
3518
|
+
for (const app of group.apps) if (group.apps.length > 1 && app.hasStaticWebsites) generatedIds.push(`tailor-deploy/tailor-build-site-${app.name}`);
|
|
3519
|
+
generatedIds.push(`tailor-deploy/tailor-deploy-${group.id}`);
|
|
3520
|
+
}
|
|
3521
|
+
return {
|
|
3522
|
+
content: out,
|
|
3523
|
+
generatedIds
|
|
3524
|
+
};
|
|
3525
|
+
}
|
|
3526
|
+
/**
|
|
3527
|
+
* Render the content of the user-owned .github/actions/tailor-setup/action.yml local action.
|
|
3528
|
+
*
|
|
3529
|
+
* This file is generated once by `setup coordinate` and never overwritten.
|
|
3530
|
+
* It wraps the setup and install actions with the package manager baked in.
|
|
3531
|
+
* @param params - Package manager configuration
|
|
3532
|
+
* @returns File content string
|
|
3533
|
+
*/
|
|
3534
|
+
function renderTailorSetupAction(params) {
|
|
3535
|
+
const { packageManager } = params;
|
|
3536
|
+
return [
|
|
3537
|
+
`# This file is user-owned. Generated once by \`tailor setup coordinate\` and never overwritten.`,
|
|
3538
|
+
`# Customize freely: add pre/post steps, change install flags, etc.`,
|
|
3539
|
+
`name: Tailor Setup`,
|
|
3540
|
+
`description: Install dependencies and run tailor setup for composite action callers.`,
|
|
3541
|
+
`runs:`,
|
|
3542
|
+
` using: composite`,
|
|
3543
|
+
` steps:`,
|
|
3544
|
+
` - uses: tailor-platform/actions/setup@${ACTIONS_SHA} # v1.5.1`,
|
|
3545
|
+
` with:`,
|
|
3546
|
+
` package-manager: ${packageManager}`,
|
|
3547
|
+
` # Add custom steps here (e.g. private registry authentication)`,
|
|
3548
|
+
` - uses: tailor-platform/actions/install@${ACTIONS_SHA} # v1.5.1`,
|
|
3549
|
+
` with:`,
|
|
3550
|
+
` package-manager: ${packageManager}`,
|
|
3551
|
+
` # editable: install-command:`,
|
|
3552
|
+
``
|
|
3553
|
+
].join("\n");
|
|
2842
3554
|
}
|
|
2843
3555
|
|
|
2844
3556
|
//#endregion
|
|
2845
3557
|
//#region src/cli/commands/setup/generate.ts
|
|
3558
|
+
function actionName(input) {
|
|
3559
|
+
return input.startsWith("tailor-") ? input.slice(7) : input;
|
|
3560
|
+
}
|
|
3561
|
+
function splitActionGroup(input) {
|
|
3562
|
+
const names = input.split(",").map((entry) => actionName(entry.trim()));
|
|
3563
|
+
if (names.some((name) => name.length === 0)) throw new Error("--action must contain one or more non-empty action names.");
|
|
3564
|
+
return names;
|
|
3565
|
+
}
|
|
3566
|
+
function uniqueGroupId(names, usedIds) {
|
|
3567
|
+
const base = names.join("-");
|
|
3568
|
+
let id = base;
|
|
3569
|
+
let suffix = 2;
|
|
3570
|
+
while (usedIds.has(id)) {
|
|
3571
|
+
id = `${base}-${suffix}`;
|
|
3572
|
+
suffix += 1;
|
|
3573
|
+
}
|
|
3574
|
+
usedIds.add(id);
|
|
3575
|
+
return id;
|
|
3576
|
+
}
|
|
2846
3577
|
async function defaultLoadConfigName(configPath) {
|
|
2847
3578
|
const { config } = await loadConfig(configPath);
|
|
2848
3579
|
return config.name;
|
|
2849
3580
|
}
|
|
3581
|
+
async function defaultLoadErdNamespaces$1(configPath) {
|
|
3582
|
+
const { config } = await loadConfig(configPath);
|
|
3583
|
+
return extractOwnedNamespaces(config);
|
|
3584
|
+
}
|
|
3585
|
+
async function defaultLoadHasMigrations$1(configPath) {
|
|
3586
|
+
const { config } = await loadConfig(configPath);
|
|
3587
|
+
return getNamespacesWithMigrations(config, path.dirname(configPath)).length > 0;
|
|
3588
|
+
}
|
|
3589
|
+
async function defaultLoadHasSeeds$1(configPath) {
|
|
3590
|
+
const { plugins } = await loadConfig(configPath);
|
|
3591
|
+
return plugins.some((p) => p.id === "@tailor-platform/seed");
|
|
3592
|
+
}
|
|
3593
|
+
async function defaultLoadHasStaticWebsites$1(configPath) {
|
|
3594
|
+
const { config } = await loadConfig(configPath);
|
|
3595
|
+
return (config.staticWebsites?.length ?? 0) > 0;
|
|
3596
|
+
}
|
|
2850
3597
|
const WORKSPACE_NAME_RE = /^[a-z0-9-]+$/;
|
|
2851
3598
|
function validateWorkspaceName(name) {
|
|
2852
|
-
if (name.length < 3 || name.length > 63 || !WORKSPACE_NAME_RE.test(name) || name.startsWith("-") || name.endsWith("-")) throw new Error(`Invalid workspace name "${name}". Names must be 3-63 characters of lowercase letters, numbers, and hyphens, and cannot start or end with a hyphen. Pass a valid name with --
|
|
3599
|
+
if (name.length < 3 || name.length > 63 || !WORKSPACE_NAME_RE.test(name) || name.startsWith("-") || name.endsWith("-")) throw new Error(`Invalid workspace name "${name}". Names must be 3-63 characters of lowercase letters, numbers, and hyphens, and cannot start or end with a hyphen. Pass a valid name with --name.`);
|
|
2853
3600
|
}
|
|
2854
3601
|
const BRANCH_RE = /^[A-Za-z0-9._/-]+$/;
|
|
2855
3602
|
const TAG_PATTERN_RE = /^[A-Za-z0-9._/*?![\]-]+$/;
|
|
@@ -2863,11 +3610,19 @@ const ENVIRONMENT_RE = /^[A-Za-z0-9._/-]+$/;
|
|
|
2863
3610
|
function validateEnvironment(environment) {
|
|
2864
3611
|
if (!ENVIRONMENT_RE.test(environment)) throw new Error(`Invalid environment name "${environment}". Only letters, numbers, ".", "_", "/", and "-" are supported.`);
|
|
2865
3612
|
}
|
|
3613
|
+
const REGION_RE = /^[A-Za-z0-9._-]+$/;
|
|
3614
|
+
function validateRegion(region) {
|
|
3615
|
+
if (!REGION_RE.test(region)) throw new Error(`Invalid region "${region}". Only letters, numbers, ".", "_", and "-" are supported.`);
|
|
3616
|
+
}
|
|
2866
3617
|
const DIR_RE = /^[A-Za-z0-9._/-]+$/;
|
|
2867
3618
|
function validateDir(dir) {
|
|
2868
3619
|
if (!DIR_RE.test(dir)) throw new Error(`Invalid --dir "${dir}". Only letters, numbers, ".", "_", "/", and "-" are supported.`);
|
|
2869
3620
|
}
|
|
2870
|
-
|
|
3621
|
+
const ERD_NAMESPACE_RE = /^[A-Za-z0-9][A-Za-z0-9._-]*$/;
|
|
3622
|
+
function validateErdNamespaces(namespaces) {
|
|
3623
|
+
for (const namespace of namespaces) if (!ERD_NAMESPACE_RE.test(namespace)) throw new Error(`TailorDB namespace "${namespace}" cannot be used in --erd-preview. Only letters, numbers, '.', '_', and '-' are supported, and the name must start with a letter or number.`);
|
|
3624
|
+
}
|
|
3625
|
+
function escapesRoot$1(rel) {
|
|
2871
3626
|
return rel === ".." || rel.startsWith(`..${path.sep}`) || rel.startsWith("../") || path.isAbsolute(rel);
|
|
2872
3627
|
}
|
|
2873
3628
|
/**
|
|
@@ -2883,11 +3638,11 @@ function escapesRoot(rel) {
|
|
|
2883
3638
|
function resolveConfigPath(outputDir, dir) {
|
|
2884
3639
|
const appDir = path.resolve(outputDir, dir);
|
|
2885
3640
|
const rel = path.relative(outputDir, appDir);
|
|
2886
|
-
if (path.isAbsolute(dir) || escapesRoot(rel)) throw new Error(`--dir must be a relative path inside the repository (got "${dir}").`);
|
|
3641
|
+
if (path.isAbsolute(dir) || escapesRoot$1(rel)) throw new Error(`--dir must be a relative path inside the repository (got "${dir}").`);
|
|
2887
3642
|
if (fs$1.existsSync(appDir)) {
|
|
2888
3643
|
const realAppDir = path.normalize(fs$1.realpathSync(appDir));
|
|
2889
3644
|
const realOutputDir = path.normalize(fs$1.realpathSync(outputDir));
|
|
2890
|
-
if (escapesRoot(path.relative(realOutputDir, realAppDir))) throw new Error(`--dir must resolve to a directory inside the repository (got "${dir}", which links outside it).`);
|
|
3645
|
+
if (escapesRoot$1(path.relative(realOutputDir, realAppDir))) throw new Error(`--dir must resolve to a directory inside the repository (got "${dir}", which links outside it).`);
|
|
2891
3646
|
}
|
|
2892
3647
|
const configPath = path.join(appDir, "tailor.config.ts");
|
|
2893
3648
|
if (!fs$1.existsSync(configPath)) throw new Error(`tailor.config.ts not found at ${configPath}. Run this from your SDK project root, or pass the app directory with --dir.`);
|
|
@@ -2899,16 +3654,15 @@ function resolveConfigPath(outputDir, dir) {
|
|
|
2899
3654
|
* @returns Resolved target metadata and rendered content
|
|
2900
3655
|
*/
|
|
2901
3656
|
async function resolve$1(options) {
|
|
2902
|
-
if (options.tag && !options.plan) throw new Error("--no-plan cannot be combined with --tag (tag targets always run plan before deploy). Drop --no-plan or use a branch target.");
|
|
2903
3657
|
const dir = options.dir.replaceAll("\\", "/").replace(/\/{2,}/g, "/").replace(/^\.\//, "").replace(/\/$/, "") || ".";
|
|
2904
3658
|
validateDir(dir);
|
|
2905
3659
|
const workingDirectory = dir !== "." ? dir : void 0;
|
|
2906
3660
|
const configPath = resolveConfigPath(options.outputDir, dir);
|
|
2907
3661
|
const loadName = options.loadConfigName ?? defaultLoadConfigName;
|
|
2908
3662
|
const workspaceName = options.workspaceName ?? await loadName(configPath);
|
|
2909
|
-
if (!workspaceName) throw new Error("Could not determine the workspace name. Pass --
|
|
3663
|
+
if (!workspaceName) throw new Error("Could not determine the workspace name. Pass --name, or set 'name' in tailor.config.ts.");
|
|
2910
3664
|
validateWorkspaceName(workspaceName);
|
|
2911
|
-
const kind = options
|
|
3665
|
+
const { kind } = options;
|
|
2912
3666
|
const packageManager = detectPackageManager(options.outputDir);
|
|
2913
3667
|
const environment = options.environment ?? workspaceName;
|
|
2914
3668
|
validateEnvironment(environment);
|
|
@@ -2916,39 +3670,86 @@ async function resolve$1(options) {
|
|
|
2916
3670
|
let branch = null;
|
|
2917
3671
|
let branchAutoDetected = false;
|
|
2918
3672
|
let render;
|
|
3673
|
+
let erdNamespaces = [];
|
|
3674
|
+
let hasMigrations = false;
|
|
3675
|
+
let hasSeeds = false;
|
|
3676
|
+
let hasStaticWebsites = false;
|
|
3677
|
+
const loadHasMigrations = options.loadHasMigrations ?? defaultLoadHasMigrations$1;
|
|
3678
|
+
const loadHasSeeds = options.loadHasSeeds ?? defaultLoadHasSeeds$1;
|
|
3679
|
+
const loadHasStaticWebsites = options.loadHasStaticWebsites ?? defaultLoadHasStaticWebsites$1;
|
|
2919
3680
|
if (kind === "branch") {
|
|
3681
|
+
if (options.erdPreview) {
|
|
3682
|
+
erdNamespaces = await (options.loadErdNamespaces ?? defaultLoadErdNamespaces$1)(configPath);
|
|
3683
|
+
if (erdNamespaces.length === 0) throw new Error("No TailorDB namespaces found for --erd-preview. Define owned db namespaces in tailor.config.ts.");
|
|
3684
|
+
validateErdNamespaces(erdNamespaces);
|
|
3685
|
+
}
|
|
2920
3686
|
branchAutoDetected = options.branch === void 0;
|
|
2921
3687
|
branch = options.branch ?? detectDefaultBranch(options.outputDir, options.gitRunner);
|
|
2922
3688
|
validateBranch(branch);
|
|
3689
|
+
hasMigrations = await loadHasMigrations(configPath);
|
|
3690
|
+
hasSeeds = await loadHasSeeds(configPath);
|
|
2923
3691
|
render = renderBranchWorkflow({
|
|
2924
3692
|
workspaceName,
|
|
2925
3693
|
branch,
|
|
2926
3694
|
workingDirectory,
|
|
2927
3695
|
environment,
|
|
2928
3696
|
packageManager,
|
|
2929
|
-
|
|
3697
|
+
erdPreview: options.erdPreview ? { namespaces: erdNamespaces } : null,
|
|
3698
|
+
migrationDriftCheck: hasMigrations,
|
|
3699
|
+
seedValidate: hasSeeds
|
|
2930
3700
|
});
|
|
2931
|
-
} else {
|
|
3701
|
+
} else if (kind === "tag") {
|
|
2932
3702
|
branch = options.branch ?? null;
|
|
2933
3703
|
if (branch !== null) validateBranch(branch);
|
|
3704
|
+
hasMigrations = await loadHasMigrations(configPath);
|
|
3705
|
+
hasSeeds = await loadHasSeeds(configPath);
|
|
2934
3706
|
render = renderTagWorkflow({
|
|
2935
3707
|
workspaceName,
|
|
2936
3708
|
tagPattern: options.tagPattern,
|
|
2937
3709
|
branch: options.branch,
|
|
2938
3710
|
workingDirectory,
|
|
2939
3711
|
environment,
|
|
2940
|
-
packageManager
|
|
3712
|
+
packageManager,
|
|
3713
|
+
migrationDriftCheck: hasMigrations,
|
|
3714
|
+
seedValidate: hasSeeds
|
|
3715
|
+
});
|
|
3716
|
+
} else if (kind === "preview") {
|
|
3717
|
+
branchAutoDetected = options.branch === void 0;
|
|
3718
|
+
branch = options.branch ?? detectDefaultBranch(options.outputDir, options.gitRunner);
|
|
3719
|
+
validateBranch(branch);
|
|
3720
|
+
validateRegion(options.region);
|
|
3721
|
+
render = renderPreviewWorkflow({
|
|
3722
|
+
workspaceName,
|
|
3723
|
+
branch,
|
|
3724
|
+
workingDirectory,
|
|
3725
|
+
environment,
|
|
3726
|
+
packageManager,
|
|
3727
|
+
region: options.region,
|
|
3728
|
+
requirePreviewLabel: options.requirePreviewLabel ?? false
|
|
3729
|
+
});
|
|
3730
|
+
} else {
|
|
3731
|
+
hasStaticWebsites = await loadHasStaticWebsites(configPath);
|
|
3732
|
+
render = renderActionWorkflow({
|
|
3733
|
+
workspaceName,
|
|
3734
|
+
workingDirectory,
|
|
3735
|
+
hasStaticWebsites
|
|
2941
3736
|
});
|
|
2942
3737
|
}
|
|
2943
|
-
const file = `.github/workflows/tailor-${workspaceName}.yml`;
|
|
3738
|
+
const file = kind === "action" ? `.github/actions/tailor-${workspaceName}/action.yml` : `.github/workflows/tailor-${workspaceName}${kind === "tag" ? "-tag" : kind === "preview" ? "-preview" : ""}.yml`;
|
|
2944
3739
|
const inputs = {
|
|
2945
|
-
branch,
|
|
2946
|
-
branchAutoDetected: kind === "branch" ? branchAutoDetected : void 0,
|
|
3740
|
+
branch: kind === "action" ? null : branch,
|
|
3741
|
+
branchAutoDetected: kind === "branch" || kind === "preview" ? branchAutoDetected : void 0,
|
|
2947
3742
|
tagPattern: kind === "tag" ? options.tagPattern : null,
|
|
2948
3743
|
environment,
|
|
2949
3744
|
dir,
|
|
2950
3745
|
packageManager,
|
|
2951
|
-
|
|
3746
|
+
region: kind === "preview" ? options.region : void 0,
|
|
3747
|
+
requirePreviewLabel: kind === "preview" ? options.requirePreviewLabel ?? false : void 0,
|
|
3748
|
+
erdPreview: kind === "branch" ? options.erdPreview : false,
|
|
3749
|
+
erdNamespaces: kind === "branch" && options.erdPreview ? erdNamespaces : void 0,
|
|
3750
|
+
migrationDriftCheck: kind === "branch" || kind === "tag" ? hasMigrations : void 0,
|
|
3751
|
+
seedValidate: kind === "branch" || kind === "tag" ? hasSeeds : void 0,
|
|
3752
|
+
hasStaticWebsites: kind === "action" ? hasStaticWebsites : void 0
|
|
2952
3753
|
};
|
|
2953
3754
|
return {
|
|
2954
3755
|
kind,
|
|
@@ -2956,12 +3757,47 @@ async function resolve$1(options) {
|
|
|
2956
3757
|
branch,
|
|
2957
3758
|
environment,
|
|
2958
3759
|
packageManager,
|
|
3760
|
+
erdNamespaces,
|
|
2959
3761
|
render,
|
|
2960
3762
|
inputs,
|
|
2961
3763
|
file,
|
|
2962
3764
|
configPath
|
|
2963
3765
|
};
|
|
2964
3766
|
}
|
|
3767
|
+
const BUILD_SITE_RUN_RE = /([ \t]*- id: build-site\n(?:[ \t]+if: [^\n]+\n)?[ \t]+shell: bash\n[ \t]+run: \|)([\s\S]*?)(\n[ \t]*- |\n*$)/;
|
|
3768
|
+
/**
|
|
3769
|
+
* Strip the mutable run: body of the build-site step before hashing.
|
|
3770
|
+
* When hasStaticWebsites is true the step is present; users are expected to
|
|
3771
|
+
* replace the placeholder command with their actual build command. We only
|
|
3772
|
+
* hash the structural parts (step id, shell declaration, run: key) so that
|
|
3773
|
+
* editing the build command does not look like unintended drift.
|
|
3774
|
+
* When hasStaticWebsites is false the step is absent, so the regex is a no-op.
|
|
3775
|
+
* @param content - Raw action workflow content
|
|
3776
|
+
* @returns Normalised content with the build-site run body replaced by a placeholder
|
|
3777
|
+
*/
|
|
3778
|
+
function normalizeActionContent(content) {
|
|
3779
|
+
return content.replace(BUILD_SITE_RUN_RE, (_, header, _body, tail) => `${header}\n true${tail}`);
|
|
3780
|
+
}
|
|
3781
|
+
/**
|
|
3782
|
+
* Extract the run: body of the build-site step from action content.
|
|
3783
|
+
* Returns null when the step is absent (hasStaticWebsites is false).
|
|
3784
|
+
* @param content - Action workflow content
|
|
3785
|
+
* @returns The captured run body string (includes leading newline), or null
|
|
3786
|
+
*/
|
|
3787
|
+
function extractBuildSiteRunBody(content) {
|
|
3788
|
+
const m = BUILD_SITE_RUN_RE.exec(content);
|
|
3789
|
+
return m ? m[2] ?? null : null;
|
|
3790
|
+
}
|
|
3791
|
+
/**
|
|
3792
|
+
* Replace the run: body of the build-site step in action content.
|
|
3793
|
+
* No-op when the step is absent.
|
|
3794
|
+
* @param content - Target action workflow content
|
|
3795
|
+
* @param body - Run body to inject (includes leading newline and indentation)
|
|
3796
|
+
* @returns Content with the run body replaced
|
|
3797
|
+
*/
|
|
3798
|
+
function injectBuildSiteRunBody(content, body) {
|
|
3799
|
+
return content.replace(BUILD_SITE_RUN_RE, (_, header, _placeholder, tail) => `${header}${body}${tail}`);
|
|
3800
|
+
}
|
|
2965
3801
|
/**
|
|
2966
3802
|
* Decide how to reconcile a target with the on-disk file and lock state.
|
|
2967
3803
|
* @param obj - Decision inputs
|
|
@@ -2969,10 +3805,11 @@ async function resolve$1(options) {
|
|
|
2969
3805
|
* @param obj.fileExists - Whether the workflow file is present on disk
|
|
2970
3806
|
* @param obj.currentContent - On-disk content when present
|
|
2971
3807
|
* @param obj.force - Whether --force was passed
|
|
3808
|
+
* @param obj.normalize - Optional content normaliser applied before hashing
|
|
2972
3809
|
* @returns The reconciliation action
|
|
2973
3810
|
*/
|
|
2974
3811
|
function decideAction(obj) {
|
|
2975
|
-
const { existing, fileExists, currentContent, force } = obj;
|
|
3812
|
+
const { existing, fileExists, currentContent, force, normalize } = obj;
|
|
2976
3813
|
if (!existing) {
|
|
2977
3814
|
if (!fileExists) return { action: "create" };
|
|
2978
3815
|
if (force) return { action: "regenerate" };
|
|
@@ -2982,7 +3819,9 @@ function decideAction(obj) {
|
|
|
2982
3819
|
};
|
|
2983
3820
|
}
|
|
2984
3821
|
if (!fileExists) return { action: "restore" };
|
|
2985
|
-
if (currentContent !== null
|
|
3822
|
+
if (currentContent !== null) {
|
|
3823
|
+
if (hashContent(normalize ? normalize(currentContent) : currentContent) === existing.contentHash) return { action: "regenerate" };
|
|
3824
|
+
}
|
|
2986
3825
|
if (force) return { action: "regenerate" };
|
|
2987
3826
|
return {
|
|
2988
3827
|
action: "conflict",
|
|
@@ -3000,7 +3839,7 @@ function decideAction(obj) {
|
|
|
3000
3839
|
function assertNoKindCollision(obj) {
|
|
3001
3840
|
const { lock, kind, workspaceName, file } = obj;
|
|
3002
3841
|
const collision = lock?.targets.find((t) => t.file === file && !(t.kind === kind && t.workspaceName === workspaceName));
|
|
3003
|
-
if (collision) throw new Error(`A ${collision.kind} target already owns ${file}, which conflicts with this ${kind} target. Pass a different name with --
|
|
3842
|
+
if (collision) throw new Error(`A ${collision.kind} target already owns ${file}, which conflicts with this ${kind} target. Pass a different name with --name to generate a separate workflow.`);
|
|
3004
3843
|
}
|
|
3005
3844
|
/**
|
|
3006
3845
|
* Print next-step guidance after generating workflow files.
|
|
@@ -3018,20 +3857,19 @@ function printNextSteps(obj) {
|
|
|
3018
3857
|
logger.log(` gh secret set TAILOR_PLATFORM_MACHINE_USER_CLIENT_SECRET --env ${environment}`);
|
|
3019
3858
|
logger.newline();
|
|
3020
3859
|
logger.log(`2. Provision the workspace and set its id as the TAILOR_PLATFORM_WORKSPACE_ID variable on the "${environment}" environment:`);
|
|
3021
|
-
logger.log(" tailor
|
|
3860
|
+
logger.log(" tailor workspace create # if it does not exist yet; copy the id");
|
|
3022
3861
|
logger.log(` gh variable set TAILOR_PLATFORM_WORKSPACE_ID --env ${environment}`);
|
|
3023
3862
|
logger.newline();
|
|
3024
3863
|
logger.log("3. Commit the generated files:");
|
|
3025
3864
|
logger.log(" - .github/workflows/tailor-*.yml");
|
|
3026
|
-
logger.log(" - .github/tailor
|
|
3865
|
+
logger.log(" - .github/tailor.lock");
|
|
3027
3866
|
if (idInjected) logger.log(" - tailor.config.ts (app id was added)");
|
|
3028
3867
|
}
|
|
3029
3868
|
/**
|
|
3030
|
-
* Generate
|
|
3031
|
-
* with the lock file.
|
|
3869
|
+
* Generate a deploy target workflow and reconcile it with the lock file.
|
|
3032
3870
|
* @param options - Setup options
|
|
3033
3871
|
*/
|
|
3034
|
-
async function
|
|
3872
|
+
async function setupTarget(options) {
|
|
3035
3873
|
logBetaWarning("setup");
|
|
3036
3874
|
const resolved = await resolve$1(options);
|
|
3037
3875
|
const lock = readLock(options.outputDir);
|
|
@@ -3044,27 +3882,35 @@ async function setupGitHub(options) {
|
|
|
3044
3882
|
});
|
|
3045
3883
|
const existing = findTarget(lock, resolved.kind, resolved.workspaceName);
|
|
3046
3884
|
const fileExists = fs$1.existsSync(absFile);
|
|
3885
|
+
const currentContent = fileExists ? fs$1.readFileSync(absFile, "utf-8") : null;
|
|
3886
|
+
const normalize = resolved.kind === "action" ? normalizeActionContent : void 0;
|
|
3047
3887
|
const decision = decideAction({
|
|
3048
3888
|
existing,
|
|
3049
3889
|
fileExists,
|
|
3050
|
-
currentContent
|
|
3051
|
-
force: options.force
|
|
3890
|
+
currentContent,
|
|
3891
|
+
force: options.force,
|
|
3892
|
+
normalize
|
|
3052
3893
|
});
|
|
3053
3894
|
if (decision.action === "conflict") throw new Error(`${resolved.file}: ${decision.reason}`);
|
|
3054
3895
|
const idResult = await ensureConfigId(resolved.configPath);
|
|
3055
3896
|
if (idResult === null) logger.warn("Could not find a defineConfig() call to confirm an app id. The CI deploy will fail unless your config resolves to one with an 'id'.");
|
|
3056
3897
|
const idInjected = idResult?.injected ?? false;
|
|
3898
|
+
let contentToWrite = resolved.render.content;
|
|
3899
|
+
if (decision.action === "regenerate" && normalize && currentContent !== null) {
|
|
3900
|
+
const existingBody = extractBuildSiteRunBody(currentContent);
|
|
3901
|
+
if (existingBody !== null) contentToWrite = injectBuildSiteRunBody(contentToWrite, existingBody);
|
|
3902
|
+
}
|
|
3057
3903
|
fs$1.mkdirSync(path.dirname(absFile), { recursive: true });
|
|
3058
|
-
fs$1.writeFileSync(absFile,
|
|
3904
|
+
fs$1.writeFileSync(absFile, contentToWrite, "utf-8");
|
|
3059
3905
|
const newTarget = {
|
|
3060
3906
|
kind: resolved.kind,
|
|
3061
3907
|
workspaceName: resolved.workspaceName,
|
|
3062
3908
|
file: resolved.file,
|
|
3063
|
-
templateVersion:
|
|
3909
|
+
templateVersion: 7,
|
|
3064
3910
|
inputs: resolved.inputs,
|
|
3065
3911
|
generatedIds: resolved.render.generatedIds,
|
|
3066
3912
|
ejectedIds: existing?.ejectedIds ?? [],
|
|
3067
|
-
contentHash: hashContent(resolved.render.content)
|
|
3913
|
+
contentHash: hashContent(normalize ? normalize(resolved.render.content) : resolved.render.content)
|
|
3068
3914
|
};
|
|
3069
3915
|
const targets = [...lock?.targets ?? []];
|
|
3070
3916
|
const index = targets.findIndex((t) => t.kind === newTarget.kind && t.workspaceName === newTarget.workspaceName);
|
|
@@ -3077,150 +3923,569 @@ async function setupGitHub(options) {
|
|
|
3077
3923
|
if (decision.action === "restore") logger.success(`Regenerated ${styles.path(resolved.file)} (was missing on disk)`);
|
|
3078
3924
|
else if (decision.action === "regenerate") logger.success(`Regenerated ${styles.path(resolved.file)}`);
|
|
3079
3925
|
else logger.success(`Generated ${styles.path(resolved.file)}`);
|
|
3080
|
-
|
|
3926
|
+
if (resolved.kind === "action") {
|
|
3927
|
+
logger.newline();
|
|
3928
|
+
logger.info("Next steps:");
|
|
3929
|
+
logger.newline();
|
|
3930
|
+
logger.log(`The composite action has been generated at ${styles.path(resolved.file)}.`);
|
|
3931
|
+
logger.log("Use `tailor setup coordinate` to generate a coordinator workflow that orchestrates this action.");
|
|
3932
|
+
} else printNextSteps({
|
|
3081
3933
|
environment: resolved.environment,
|
|
3082
3934
|
idInjected
|
|
3083
3935
|
});
|
|
3084
3936
|
}
|
|
3937
|
+
/**
|
|
3938
|
+
* Generate the coordinator workflow that orchestrates per-app composite actions.
|
|
3939
|
+
*
|
|
3940
|
+
* Unlike `setupTarget`, this function does not read a Tailor config. The coordinator
|
|
3941
|
+
* name is required via `--name`. App working directories are resolved from
|
|
3942
|
+
* the lock file entries created by `setup action`.
|
|
3943
|
+
* @param options - Coordinate setup options
|
|
3944
|
+
*/
|
|
3945
|
+
async function setupCoordinate(options) {
|
|
3946
|
+
logBetaWarning("setup");
|
|
3947
|
+
const { coordinatorName, coordinateKind, actions, force, outputDir } = options;
|
|
3948
|
+
validateWorkspaceName(coordinatorName);
|
|
3949
|
+
if (actions.length === 0) throw new Error("At least one --action is required. Run `tailor setup action --dir <app-dir>` for each app first.");
|
|
3950
|
+
const environment = options.environment ?? coordinatorName;
|
|
3951
|
+
validateEnvironment(environment);
|
|
3952
|
+
let branch = null;
|
|
3953
|
+
let branchAutoDetected = false;
|
|
3954
|
+
if (coordinateKind === "branch") {
|
|
3955
|
+
branchAutoDetected = options.branch === void 0;
|
|
3956
|
+
branch = options.branch ?? detectDefaultBranch(outputDir, options.gitRunner);
|
|
3957
|
+
validateBranch(branch);
|
|
3958
|
+
} else if (options.branch !== void 0) {
|
|
3959
|
+
validateBranch(options.branch);
|
|
3960
|
+
branch = options.branch;
|
|
3961
|
+
}
|
|
3962
|
+
const tagPattern = options.tagPattern ?? "v*";
|
|
3963
|
+
if (coordinateKind === "tag") validateTagPattern(tagPattern);
|
|
3964
|
+
const packageManager = detectPackageManager(outputDir);
|
|
3965
|
+
const lock = readLock(outputDir);
|
|
3966
|
+
if (!lock) throw new Error(".github/tailor.lock not found. Run `tailor setup action --name <name>` for each app before running setup coordinate.");
|
|
3967
|
+
const actionTargets = new Map(lock.targets.filter((t) => t.kind === "action").map((target) => [target.workspaceName, target]));
|
|
3968
|
+
const seenNames = /* @__PURE__ */ new Set();
|
|
3969
|
+
const usedGroupIds = /* @__PURE__ */ new Set();
|
|
3970
|
+
const actionGroups = actions.map((actionGroup) => {
|
|
3971
|
+
const names = splitActionGroup(actionGroup);
|
|
3972
|
+
const apps = names.map((name) => {
|
|
3973
|
+
if (seenNames.has(name)) throw new Error(`Duplicate --action "${name}". Each composite action can only appear once in a coordinator.`);
|
|
3974
|
+
seenNames.add(name);
|
|
3975
|
+
const entry = actionTargets.get(name);
|
|
3976
|
+
if (!entry) throw new Error(`Action target "${name}" not found in .github/tailor.lock. Run \`tailor setup action --name ${name}\` first.`);
|
|
3977
|
+
if (names.length > 1 && entry.templateVersion < 7) throw new Error(`Action target "${name}" was generated with an older setup template. Run \`tailor setup action --name ${name} --force\` before grouping it in setup coordinate.`);
|
|
3978
|
+
validateDir(entry.inputs.dir);
|
|
3979
|
+
return {
|
|
3980
|
+
name,
|
|
3981
|
+
dir: entry.inputs.dir,
|
|
3982
|
+
hasStaticWebsites: entry.inputs.hasStaticWebsites
|
|
3983
|
+
};
|
|
3984
|
+
});
|
|
3985
|
+
return {
|
|
3986
|
+
id: uniqueGroupId(names, usedGroupIds),
|
|
3987
|
+
apps
|
|
3988
|
+
};
|
|
3989
|
+
});
|
|
3990
|
+
const render = renderCoordinateWorkflow({
|
|
3991
|
+
coordinatorName,
|
|
3992
|
+
kind: coordinateKind,
|
|
3993
|
+
actionGroups,
|
|
3994
|
+
branch: branch ?? void 0,
|
|
3995
|
+
tagPattern: coordinateKind === "tag" ? tagPattern : void 0,
|
|
3996
|
+
environment,
|
|
3997
|
+
packageManager
|
|
3998
|
+
});
|
|
3999
|
+
const file = `.github/workflows/tailor-coordinate-${coordinatorName}${coordinateKind === "tag" ? "-tag" : ""}.yml`;
|
|
4000
|
+
const absFile = path.join(outputDir, file);
|
|
4001
|
+
assertNoKindCollision({
|
|
4002
|
+
lock,
|
|
4003
|
+
kind: "coordinate",
|
|
4004
|
+
workspaceName: coordinatorName,
|
|
4005
|
+
file
|
|
4006
|
+
});
|
|
4007
|
+
const existing = findTarget(lock, "coordinate", coordinatorName);
|
|
4008
|
+
const fileExists = fs$1.existsSync(absFile);
|
|
4009
|
+
const decision = decideAction({
|
|
4010
|
+
existing,
|
|
4011
|
+
fileExists,
|
|
4012
|
+
currentContent: fileExists ? fs$1.readFileSync(absFile, "utf-8") : null,
|
|
4013
|
+
force
|
|
4014
|
+
});
|
|
4015
|
+
if (decision.action === "conflict") throw new Error(`${file}: ${decision.reason}`);
|
|
4016
|
+
fs$1.mkdirSync(path.dirname(absFile), { recursive: true });
|
|
4017
|
+
fs$1.writeFileSync(absFile, render.content, "utf-8");
|
|
4018
|
+
const tailorSetupFile = ".github/actions/tailor-setup/action.yml";
|
|
4019
|
+
const absTailorSetupFile = path.join(outputDir, tailorSetupFile);
|
|
4020
|
+
if (!fs$1.existsSync(absTailorSetupFile)) {
|
|
4021
|
+
fs$1.mkdirSync(path.dirname(absTailorSetupFile), { recursive: true });
|
|
4022
|
+
fs$1.writeFileSync(absTailorSetupFile, renderTailorSetupAction({ packageManager }), "utf-8");
|
|
4023
|
+
logger.success(`Generated ${styles.path(tailorSetupFile)}`);
|
|
4024
|
+
}
|
|
4025
|
+
const newTarget = {
|
|
4026
|
+
kind: "coordinate",
|
|
4027
|
+
workspaceName: coordinatorName,
|
|
4028
|
+
file,
|
|
4029
|
+
templateVersion: 7,
|
|
4030
|
+
inputs: {
|
|
4031
|
+
branch,
|
|
4032
|
+
branchAutoDetected: coordinateKind === "branch" ? branchAutoDetected : branch !== null ? false : void 0,
|
|
4033
|
+
tagPattern: coordinateKind === "tag" ? tagPattern : null,
|
|
4034
|
+
environment,
|
|
4035
|
+
dir: ".",
|
|
4036
|
+
packageManager,
|
|
4037
|
+
actionDirs: actionGroups.flatMap((group) => group.apps.map((a) => a.dir))
|
|
4038
|
+
},
|
|
4039
|
+
generatedIds: render.generatedIds,
|
|
4040
|
+
ejectedIds: existing?.ejectedIds ?? [],
|
|
4041
|
+
contentHash: hashContent(render.content)
|
|
4042
|
+
};
|
|
4043
|
+
const targets = [...lock.targets];
|
|
4044
|
+
const idx = targets.findIndex((t) => t.kind === newTarget.kind && t.workspaceName === newTarget.workspaceName);
|
|
4045
|
+
if (idx === -1) targets.push(newTarget);
|
|
4046
|
+
else targets[idx] = newTarget;
|
|
4047
|
+
writeLock(outputDir, {
|
|
4048
|
+
version: 1,
|
|
4049
|
+
targets
|
|
4050
|
+
});
|
|
4051
|
+
if (decision.action === "restore") logger.success(`Regenerated ${styles.path(file)} (was missing on disk)`);
|
|
4052
|
+
else if (decision.action === "regenerate") logger.success(`Regenerated ${styles.path(file)}`);
|
|
4053
|
+
else logger.success(`Generated ${styles.path(file)}`);
|
|
4054
|
+
logger.newline();
|
|
4055
|
+
logger.info("Next steps:");
|
|
4056
|
+
logger.newline();
|
|
4057
|
+
logger.log(`1. Set the machine-user credentials as secrets on the "${environment}" environment:`);
|
|
4058
|
+
logger.log(` gh secret set TAILOR_PLATFORM_MACHINE_USER_CLIENT_ID --env ${environment}`);
|
|
4059
|
+
logger.log(` gh secret set TAILOR_PLATFORM_MACHINE_USER_CLIENT_SECRET --env ${environment}`);
|
|
4060
|
+
logger.newline();
|
|
4061
|
+
logger.log(`2. Provision the target workspace and set TAILOR_PLATFORM_WORKSPACE_ID on the "${environment}" environment:`);
|
|
4062
|
+
logger.log(" tailor workspace create # if it does not exist yet; copy the id");
|
|
4063
|
+
logger.log(` gh variable set TAILOR_PLATFORM_WORKSPACE_ID --env ${environment}`);
|
|
4064
|
+
logger.newline();
|
|
4065
|
+
logger.log("3. Commit the generated files:");
|
|
4066
|
+
logger.log(` - ${file}`);
|
|
4067
|
+
logger.log(` - ${tailorSetupFile} (if newly created)`);
|
|
4068
|
+
logger.log(" - .github/tailor.lock");
|
|
4069
|
+
}
|
|
4070
|
+
|
|
4071
|
+
//#endregion
|
|
4072
|
+
//#region src/cli/commands/setup/check.ts
|
|
4073
|
+
/**
|
|
4074
|
+
* Compute drift findings for one target by comparing its recorded lock state
|
|
4075
|
+
* against the current repository/config state.
|
|
4076
|
+
* @param target - The lock target being audited
|
|
4077
|
+
* @param state - Current repository/config state for this target
|
|
4078
|
+
* @returns Drift findings (empty when the target is in sync)
|
|
4079
|
+
*/
|
|
4080
|
+
function findTargetDrift(target, state) {
|
|
4081
|
+
const id = `${target.kind} ${target.workspaceName}`;
|
|
4082
|
+
const findings = [];
|
|
4083
|
+
if (!state.fileExists) findings.push({
|
|
4084
|
+
target: id,
|
|
4085
|
+
rule: "missing-file",
|
|
4086
|
+
message: `${target.file} is missing or unreadable. Re-run setup to restore it.`
|
|
4087
|
+
});
|
|
4088
|
+
else if (state.currentHash !== null && state.currentHash !== target.contentHash) findings.push({
|
|
4089
|
+
target: id,
|
|
4090
|
+
rule: "hand-edit",
|
|
4091
|
+
message: `${target.file} was edited by hand since it was generated. Re-run setup with --force to regenerate, or keep customizations in your own jobs/steps.`
|
|
4092
|
+
});
|
|
4093
|
+
if (target.templateVersion < state.templateVersion) findings.push({
|
|
4094
|
+
target: id,
|
|
4095
|
+
rule: "template-version",
|
|
4096
|
+
message: `A newer workflow template is available (generated with v${String(target.templateVersion)}, current v${String(state.templateVersion)}). Re-run setup to update.`
|
|
4097
|
+
});
|
|
4098
|
+
if (!state.configExists) findings.push({
|
|
4099
|
+
target: id,
|
|
4100
|
+
rule: "config-dir",
|
|
4101
|
+
message: `tailor.config.ts not found under "${target.inputs.dir}". The app directory may have moved; re-run setup with the correct --dir.`
|
|
4102
|
+
});
|
|
4103
|
+
if ((target.kind === "branch" || target.kind === "coordinate" || target.kind === "preview") && target.inputs.branchAutoDetected !== false && state.defaultBranch !== null && target.inputs.branch !== null && target.inputs.branch !== state.defaultBranch) findings.push({
|
|
4104
|
+
target: id,
|
|
4105
|
+
rule: "default-branch",
|
|
4106
|
+
message: `The workflow triggers on "${target.inputs.branch}" but the repository default branch is now "${state.defaultBranch}". If this is intentional, ignore this; otherwise re-run setup so the trigger matches the default branch.`
|
|
4107
|
+
});
|
|
4108
|
+
if (target.kind === "branch" && target.inputs.erdPreview && state.configExists) {
|
|
4109
|
+
const recorded = [...target.inputs.erdNamespaces ?? []].toSorted((a, b) => a.localeCompare(b));
|
|
4110
|
+
const current = state.erdNamespaces?.toSorted((a, b) => a.localeCompare(b)) ?? null;
|
|
4111
|
+
if (current === null || recorded.length !== current.length || recorded.some((namespace, index) => namespace !== current[index])) findings.push({
|
|
4112
|
+
target: id,
|
|
4113
|
+
rule: "erd-namespaces",
|
|
4114
|
+
message: "TailorDB namespaces for ERD preview changed. Re-run setup so the ERD preview matrix is regenerated."
|
|
4115
|
+
});
|
|
4116
|
+
}
|
|
4117
|
+
if ((target.kind === "branch" || target.kind === "tag") && target.inputs.migrationDriftCheck === false && state.hasMigrations === true) findings.push({
|
|
4118
|
+
target: id,
|
|
4119
|
+
rule: "migration-drift",
|
|
4120
|
+
message: "TailorDB namespaces with migrations were added to the config. Re-run setup so the tailor-migration-drift-check step is included in the plan job."
|
|
4121
|
+
});
|
|
4122
|
+
if ((target.kind === "branch" || target.kind === "tag") && target.inputs.seedValidate === false && state.hasSeeds === true) findings.push({
|
|
4123
|
+
target: id,
|
|
4124
|
+
rule: "seed-validate",
|
|
4125
|
+
message: "Seed plugin detected in the current config. Re-run setup so the tailor-seed-validate step is included in the plan job."
|
|
4126
|
+
});
|
|
4127
|
+
if (target.kind === "action" && target.inputs.hasStaticWebsites === false && state.hasStaticWebsites === true) findings.push({
|
|
4128
|
+
target: id,
|
|
4129
|
+
rule: "static-websites",
|
|
4130
|
+
message: "Static websites were added to the config. Re-run setup so the build-site slot is included in the composite action."
|
|
4131
|
+
});
|
|
4132
|
+
return findings;
|
|
4133
|
+
}
|
|
4134
|
+
function detectDefaultBranchSafe(cwd, run) {
|
|
4135
|
+
try {
|
|
4136
|
+
return detectDefaultBranch(cwd, run);
|
|
4137
|
+
} catch {
|
|
4138
|
+
return null;
|
|
4139
|
+
}
|
|
4140
|
+
}
|
|
4141
|
+
function escapesRoot(rel) {
|
|
4142
|
+
return path.isAbsolute(rel) || rel === ".." || rel.startsWith(`..${path.sep}`) || rel.startsWith("../");
|
|
4143
|
+
}
|
|
4144
|
+
function resolveWithinRoot(outputDir, relPath) {
|
|
4145
|
+
if (path.isAbsolute(relPath)) return null;
|
|
4146
|
+
const abs = path.join(outputDir, relPath);
|
|
4147
|
+
if (escapesRoot(path.relative(outputDir, abs))) return null;
|
|
4148
|
+
try {
|
|
4149
|
+
if (escapesRoot(path.relative(fs$1.realpathSync(outputDir), fs$1.realpathSync(abs)))) return null;
|
|
4150
|
+
} catch {}
|
|
4151
|
+
return abs;
|
|
4152
|
+
}
|
|
4153
|
+
function readHash(absFile, normalize) {
|
|
4154
|
+
try {
|
|
4155
|
+
const content = fs$1.readFileSync(absFile, "utf-8");
|
|
4156
|
+
return hashContent(normalize ? normalize(content) : content);
|
|
4157
|
+
} catch {
|
|
4158
|
+
return null;
|
|
4159
|
+
}
|
|
4160
|
+
}
|
|
4161
|
+
async function defaultLoadErdNamespaces(configPath) {
|
|
4162
|
+
const { config } = await loadConfig(configPath);
|
|
4163
|
+
return extractOwnedNamespaces(config);
|
|
4164
|
+
}
|
|
4165
|
+
async function defaultLoadHasMigrations(configPath) {
|
|
4166
|
+
const { config } = await loadConfig(configPath);
|
|
4167
|
+
return getNamespacesWithMigrations(config, path.dirname(configPath)).length > 0;
|
|
4168
|
+
}
|
|
4169
|
+
async function defaultLoadHasSeeds(configPath) {
|
|
4170
|
+
const { plugins } = await loadConfig(configPath);
|
|
4171
|
+
return plugins.some((p) => p.id === "@tailor-platform/seed");
|
|
4172
|
+
}
|
|
4173
|
+
async function defaultLoadHasStaticWebsites(configPath) {
|
|
4174
|
+
const { config } = await loadConfig(configPath);
|
|
4175
|
+
return (config.staticWebsites?.length ?? 0) > 0;
|
|
4176
|
+
}
|
|
4177
|
+
/**
|
|
4178
|
+
* Audit the generated workflows for drift against the current config/repo
|
|
4179
|
+
* state. Read-only: never writes files, the lock, or the config.
|
|
4180
|
+
*
|
|
4181
|
+
* Throws when drift is found (so it composes like the other `:check`
|
|
4182
|
+
* commands). The workflow drift-check step layers advisory behaviour on top
|
|
4183
|
+
* (per-rule ignore / continue-on-error); the CLI itself reports via exit code.
|
|
4184
|
+
* @param options - Check options
|
|
4185
|
+
*/
|
|
4186
|
+
async function checkGitHub(options) {
|
|
4187
|
+
logBetaWarning("setup");
|
|
4188
|
+
const { outputDir } = options;
|
|
4189
|
+
const lock = readLock(outputDir);
|
|
4190
|
+
if (!lock || lock.targets.length === 0) throw new Error("No managed workflows found (.github/tailor.lock is missing or empty). Run `tailor setup branch` (or another setup subcommand) first.");
|
|
4191
|
+
if (!options.ci) {
|
|
4192
|
+
if (lock.targets.some((t) => t.kind === "branch" || t.kind === "tag" || t.kind === "coordinate") && !process.env["TAILOR_PLATFORM_WORKSPACE_ID"]) throw new Error("TAILOR_PLATFORM_WORKSPACE_ID is not set. Provision the workspace and set the variable:\n tailor workspace create # if it does not exist yet; copy the id\n gh variable set TAILOR_PLATFORM_WORKSPACE_ID --env <environment>");
|
|
4193
|
+
}
|
|
4194
|
+
const slackToken = Boolean(process.env["TAILOR_SLACK_BOT_TOKEN"]);
|
|
4195
|
+
if (slackToken !== Boolean(process.env["TAILOR_SLACK_CHANNEL_ID"])) throw new Error(`Slack is partially configured: ${slackToken ? "TAILOR_SLACK_BOT_TOKEN is set" : "TAILOR_SLACK_CHANNEL_ID is set"} but not both. TAILOR_SLACK_BOT_TOKEN (secret) and TAILOR_SLACK_CHANNEL_ID (variable) must be set together, or neither should be set.`);
|
|
4196
|
+
const exists = options.configExistsAt ?? ((p) => fs$1.existsSync(p));
|
|
4197
|
+
const defaultBranch = detectDefaultBranchSafe(outputDir, options.gitRunner);
|
|
4198
|
+
const loadErdNamespaces = options.loadErdNamespaces ?? defaultLoadErdNamespaces;
|
|
4199
|
+
const loadHasMigrations = options.loadHasMigrations ?? defaultLoadHasMigrations;
|
|
4200
|
+
const loadHasSeeds = options.loadHasSeeds ?? defaultLoadHasSeeds;
|
|
4201
|
+
const loadHasStaticWebsites = options.loadHasStaticWebsites ?? defaultLoadHasStaticWebsites;
|
|
4202
|
+
const findings = [];
|
|
4203
|
+
for (const target of lock.targets) {
|
|
4204
|
+
const absFile = resolveWithinRoot(outputDir, target.file);
|
|
4205
|
+
const currentHash = absFile === null ? null : readHash(absFile, target.kind === "action" ? normalizeActionContent : void 0);
|
|
4206
|
+
const configAbs = target.kind === "coordinate" ? null : resolveWithinRoot(outputDir, path.join(target.inputs.dir, "tailor.config.ts"));
|
|
4207
|
+
const configExists = target.kind === "coordinate" || configAbs !== null && exists(configAbs);
|
|
4208
|
+
const erdNamespaces = target.kind === "branch" && target.inputs.erdPreview && configAbs !== null && configExists ? await loadErdNamespaces(configAbs) : null;
|
|
4209
|
+
const hasMigrations = (target.kind === "branch" || target.kind === "tag") && target.inputs.migrationDriftCheck !== void 0 && configAbs !== null && configExists ? await loadHasMigrations(configAbs) : void 0;
|
|
4210
|
+
const hasSeeds = (target.kind === "branch" || target.kind === "tag") && target.inputs.seedValidate !== void 0 && configAbs !== null && configExists ? await loadHasSeeds(configAbs) : void 0;
|
|
4211
|
+
const hasStaticWebsites = target.kind === "action" && target.inputs.hasStaticWebsites !== void 0 && configAbs !== null && configExists ? await loadHasStaticWebsites(configAbs) : void 0;
|
|
4212
|
+
findings.push(...findTargetDrift(target, {
|
|
4213
|
+
fileExists: currentHash !== null,
|
|
4214
|
+
currentHash,
|
|
4215
|
+
configExists,
|
|
4216
|
+
defaultBranch,
|
|
4217
|
+
templateVersion: 7,
|
|
4218
|
+
erdNamespaces,
|
|
4219
|
+
hasMigrations,
|
|
4220
|
+
hasSeeds,
|
|
4221
|
+
hasStaticWebsites
|
|
4222
|
+
}));
|
|
4223
|
+
}
|
|
4224
|
+
const count = lock.targets.length;
|
|
4225
|
+
if (findings.length === 0) {
|
|
4226
|
+
logger.success(`No drift detected across ${String(count)} target(s).`);
|
|
4227
|
+
return;
|
|
4228
|
+
}
|
|
4229
|
+
for (const finding of findings) logger.warn(`[${finding.target}] ${finding.message} (ignore key: ${finding.rule})`);
|
|
4230
|
+
throw new Error(`Detected ${String(findings.length)} drift finding(s) across ${String(count)} target(s). Re-run \`tailor setup\` to regenerate, or address each finding above.`);
|
|
4231
|
+
}
|
|
4232
|
+
|
|
4233
|
+
//#endregion
|
|
4234
|
+
//#region src/cli/commands/setup/delete.ts
|
|
4235
|
+
function normalizeRelPath(input) {
|
|
4236
|
+
return input.replaceAll("\\", "/").replace(/\/{2,}/g, "/").replace(/^\.\//, "").replace(/\/$/, "");
|
|
4237
|
+
}
|
|
4238
|
+
function targetKey(target) {
|
|
4239
|
+
return `${target.kind}:${target.workspaceName}`;
|
|
4240
|
+
}
|
|
4241
|
+
function resolveMatch(outputDir, targets, input) {
|
|
4242
|
+
const relPath = normalizeRelPath(input);
|
|
4243
|
+
const absPath = resolveWithinRoot(outputDir, relPath);
|
|
4244
|
+
if (absPath === null) throw new Error(`"${input}" must be a path inside the repository.`);
|
|
4245
|
+
const target = targets.find((t) => t.file === relPath);
|
|
4246
|
+
if (!target) throw new Error(`"${relPath}" is not a managed workflow/action file (not recorded in .github/tailor-sdk.lock). Only files generated by \`tailor-sdk setup\` can be deleted with this command.`);
|
|
4247
|
+
return {
|
|
4248
|
+
relPath,
|
|
4249
|
+
absPath,
|
|
4250
|
+
target
|
|
4251
|
+
};
|
|
4252
|
+
}
|
|
4253
|
+
function warnCoordinatorReferences(allTargets, deletedKeys, match) {
|
|
4254
|
+
if (match.target.kind !== "action") return;
|
|
4255
|
+
for (const coordinator of allTargets) if (coordinator.kind === "coordinate" && coordinator.inputs.actionDirs?.includes(match.target.inputs.dir) && !deletedKeys.has(targetKey(coordinator))) logger.warn(`Coordinator "${coordinator.workspaceName}" still references this action's directory (${match.target.inputs.dir}). Remove \`${match.target.workspaceName}\` from the relevant \`--action\` value and re-run \`tailor-sdk setup coordinate\` to update it.`);
|
|
4256
|
+
}
|
|
4257
|
+
/**
|
|
4258
|
+
* Delete managed workflow/action files and their `.github/tailor-sdk.lock` entries.
|
|
4259
|
+
*
|
|
4260
|
+
* Refuses to touch files that are not recorded in the lock, so it never
|
|
4261
|
+
* deletes hand-written workflows or the user-owned tailor-setup action.
|
|
4262
|
+
* @param options - Delete options
|
|
4263
|
+
*/
|
|
4264
|
+
async function setupDelete(options) {
|
|
4265
|
+
logBetaWarning("setup");
|
|
4266
|
+
const { outputDir, yes } = options;
|
|
4267
|
+
const lock = readLock(outputDir);
|
|
4268
|
+
if (!lock || lock.targets.length === 0) throw new Error("No managed workflows found (.github/tailor-sdk.lock is missing or empty). Run `tailor-sdk setup branch` (or another setup subcommand) first.");
|
|
4269
|
+
const matches = [...new Set(options.files.map(normalizeRelPath))].map((relPath) => resolveMatch(outputDir, lock.targets, relPath));
|
|
4270
|
+
const deletedKeys = new Set(matches.map((m) => targetKey(m.target)));
|
|
4271
|
+
for (const match of matches) warnCoordinatorReferences(lock.targets, deletedKeys, match);
|
|
4272
|
+
if (!yes) {
|
|
4273
|
+
const list = matches.map((m) => m.relPath).join("\n ");
|
|
4274
|
+
if (!await prompt.confirm({
|
|
4275
|
+
message: `This will delete the following file(s) and their .github/tailor-sdk.lock entries:\n ${list}\nContinue?`,
|
|
4276
|
+
default: false
|
|
4277
|
+
})) {
|
|
4278
|
+
logger.info("Delete cancelled.");
|
|
4279
|
+
return;
|
|
4280
|
+
}
|
|
4281
|
+
}
|
|
4282
|
+
const remaining = [...lock.targets];
|
|
4283
|
+
try {
|
|
4284
|
+
for (const { relPath, absPath, target } of matches) {
|
|
4285
|
+
fs$1.rmSync(absPath, { force: true });
|
|
4286
|
+
if (target.kind === "action") {
|
|
4287
|
+
const dir = path.dirname(absPath);
|
|
4288
|
+
if (fs$1.existsSync(dir) && fs$1.readdirSync(dir).length === 0) fs$1.rmdirSync(dir);
|
|
4289
|
+
}
|
|
4290
|
+
const index = remaining.findIndex((t) => targetKey(t) === targetKey(target));
|
|
4291
|
+
if (index !== -1) remaining.splice(index, 1);
|
|
4292
|
+
logger.success(`Deleted ${styles.path(relPath)} (${target.kind} ${target.workspaceName})`);
|
|
4293
|
+
}
|
|
4294
|
+
} finally {
|
|
4295
|
+
if (remaining.length !== lock.targets.length) writeLock(outputDir, {
|
|
4296
|
+
version: 1,
|
|
4297
|
+
targets: remaining
|
|
4298
|
+
});
|
|
4299
|
+
}
|
|
4300
|
+
}
|
|
3085
4301
|
|
|
3086
4302
|
//#endregion
|
|
3087
4303
|
//#region src/cli/commands/setup/index.ts
|
|
3088
4304
|
const checkCommand = defineAppCommand({
|
|
3089
4305
|
name: "check",
|
|
3090
4306
|
description: "Audit generated workflows for drift against the current config/repo (read-only).",
|
|
3091
|
-
args: z.
|
|
3092
|
-
run: () => {
|
|
3093
|
-
checkGitHub({
|
|
4307
|
+
args: z.strictObject({ ci: arg(z.boolean().default(false), { description: "Run in CI mode: skip checks that are handled by the runtime (e.g. TAILOR_PLATFORM_WORKSPACE_ID)." }) }),
|
|
4308
|
+
run: async (args) => {
|
|
4309
|
+
await checkGitHub({
|
|
4310
|
+
outputDir: process.cwd(),
|
|
4311
|
+
ci: args.ci
|
|
4312
|
+
});
|
|
4313
|
+
}
|
|
4314
|
+
});
|
|
4315
|
+
const coordinateCommand = defineAppCommand({
|
|
4316
|
+
name: "coordinate",
|
|
4317
|
+
description: "Generate a coordinator workflow that orchestrates multiple --action-generated composite actions.",
|
|
4318
|
+
args: z.strictObject({
|
|
4319
|
+
name: arg(z.string().min(1), {
|
|
4320
|
+
alias: "n",
|
|
4321
|
+
description: "Coordinator name (used in the generated workflow file name and job names)"
|
|
4322
|
+
}),
|
|
4323
|
+
action: arg(z.array(z.string().min(1)).min(1), { description: "Composite action to include. Repeat for separate deploy steps, or use commas to deploy actions as one multi-config group. tailor- prefix optional." }),
|
|
4324
|
+
branch: arg(z.string().min(1).optional(), { description: "Branch target: deploy trigger branch (defaults to the detected default branch)" }),
|
|
4325
|
+
tag: arg(z.boolean().default(false), { description: "Generate a tag target coordinator" }),
|
|
4326
|
+
environment: arg(z.string().min(1).optional(), { description: "GitHub Environment for the plan/deploy jobs" }),
|
|
4327
|
+
force: arg(z.boolean().default(false), { description: "Discard hand edits and regenerate" })
|
|
4328
|
+
}),
|
|
4329
|
+
run: async (args) => {
|
|
4330
|
+
const coordinateKind = args.tag ? "tag" : "branch";
|
|
4331
|
+
await setupCoordinate({
|
|
4332
|
+
coordinatorName: args.name,
|
|
4333
|
+
coordinateKind,
|
|
4334
|
+
actions: args.action,
|
|
4335
|
+
branch: args.branch,
|
|
4336
|
+
environment: args.environment,
|
|
4337
|
+
force: args.force,
|
|
4338
|
+
outputDir: process.cwd()
|
|
4339
|
+
});
|
|
4340
|
+
}
|
|
4341
|
+
});
|
|
4342
|
+
const actionCommand = defineAppCommand({
|
|
4343
|
+
name: "action",
|
|
4344
|
+
description: "Generate a per-app composite action for use with setup coordinate (monorepo multi-app deploys).",
|
|
4345
|
+
args: z.strictObject({
|
|
4346
|
+
name: arg(z.string().min(1).optional(), {
|
|
4347
|
+
alias: "n",
|
|
4348
|
+
description: "Name (defaults to the config 'name')"
|
|
4349
|
+
}),
|
|
4350
|
+
dir: arg(z.string().min(1).default("."), {
|
|
4351
|
+
alias: "d",
|
|
4352
|
+
description: "App directory"
|
|
4353
|
+
}),
|
|
4354
|
+
environment: arg(z.string().min(1).optional(), { description: "GitHub Environment (defaults to the workspace name)" }),
|
|
4355
|
+
force: arg(z.boolean().default(false), { description: "Discard hand edits and regenerate" })
|
|
4356
|
+
}),
|
|
4357
|
+
run: async (args) => {
|
|
4358
|
+
await setupTarget({
|
|
4359
|
+
kind: "action",
|
|
4360
|
+
workspaceName: args.name,
|
|
4361
|
+
dir: args.dir,
|
|
4362
|
+
environment: args.environment,
|
|
4363
|
+
force: args.force,
|
|
4364
|
+
outputDir: process.cwd()
|
|
4365
|
+
});
|
|
3094
4366
|
}
|
|
3095
4367
|
});
|
|
3096
|
-
const
|
|
3097
|
-
name: "
|
|
3098
|
-
description: "Generate a
|
|
3099
|
-
args: z.
|
|
3100
|
-
|
|
3101
|
-
alias: "
|
|
3102
|
-
description: "
|
|
4368
|
+
const branchCommand = defineAppCommand({
|
|
4369
|
+
name: "branch",
|
|
4370
|
+
description: "Generate a branch-target deploy workflow (push to branch triggers deploy).",
|
|
4371
|
+
args: z.strictObject({
|
|
4372
|
+
name: arg(z.string().min(1).optional(), {
|
|
4373
|
+
alias: "n",
|
|
4374
|
+
description: "Name (defaults to the config 'name')"
|
|
4375
|
+
}),
|
|
4376
|
+
branch: arg(z.string().min(1).optional(), { description: "Deploy trigger branch (defaults to the detected default branch)" }),
|
|
4377
|
+
environment: arg(z.string().min(1).optional(), { description: "GitHub Environment for the plan/deploy jobs (defaults to the workspace name)" }),
|
|
4378
|
+
"erd-preview": arg(z.boolean().default(false), { description: "Add PR ERD viewer artifacts with current/diff previews for TailorDB namespaces" }),
|
|
4379
|
+
dir: arg(z.string().min(1).default("."), {
|
|
4380
|
+
alias: "d",
|
|
4381
|
+
description: "App directory (for monorepo setups)"
|
|
3103
4382
|
}),
|
|
3104
|
-
|
|
4383
|
+
force: arg(z.boolean().default(false), { description: "Discard hand edits / take over unmanaged files and regenerate" })
|
|
4384
|
+
}),
|
|
4385
|
+
run: async (args) => {
|
|
4386
|
+
await setupTarget({
|
|
4387
|
+
kind: "branch",
|
|
4388
|
+
workspaceName: args.name,
|
|
4389
|
+
branch: args.branch,
|
|
4390
|
+
environment: args.environment,
|
|
4391
|
+
erdPreview: args["erd-preview"],
|
|
4392
|
+
dir: args.dir,
|
|
4393
|
+
force: args.force,
|
|
4394
|
+
outputDir: process.cwd()
|
|
4395
|
+
});
|
|
4396
|
+
}
|
|
4397
|
+
});
|
|
4398
|
+
const tagCommand = defineAppCommand({
|
|
4399
|
+
name: "tag",
|
|
4400
|
+
description: "Generate a tag-target deploy workflow (tag push triggers deploy).",
|
|
4401
|
+
args: z.strictObject({
|
|
4402
|
+
name: arg(z.string().min(1).optional(), {
|
|
3105
4403
|
alias: "n",
|
|
3106
|
-
description: "
|
|
4404
|
+
description: "Name (defaults to the config 'name')"
|
|
3107
4405
|
}),
|
|
3108
|
-
|
|
3109
|
-
|
|
3110
|
-
"tag-pattern": arg(z.string().min(1).optional(), { description: "Tag glob to match (requires --tag; defaults to v*)" }),
|
|
4406
|
+
"tag-pattern": arg(z.string().min(1).default("v*"), { description: "Tag glob to match (defaults to v*)" }),
|
|
4407
|
+
branch: arg(z.string().min(1).optional(), { description: "Tag-reachability guard branch (no guard when omitted)" }),
|
|
3111
4408
|
environment: arg(z.string().min(1).optional(), { description: "GitHub Environment for the plan/deploy jobs (defaults to the workspace name)" }),
|
|
3112
|
-
"no-plan": arg(z.boolean().default(false), { description: "Disable the plan job for a branch target (cannot be combined with --tag)" }),
|
|
3113
4409
|
dir: arg(z.string().min(1).default("."), {
|
|
3114
4410
|
alias: "d",
|
|
3115
4411
|
description: "App directory (for monorepo setups)"
|
|
3116
4412
|
}),
|
|
3117
4413
|
force: arg(z.boolean().default(false), { description: "Discard hand edits / take over unmanaged files and regenerate" })
|
|
3118
|
-
})
|
|
3119
|
-
subCommands: { check: checkCommand },
|
|
4414
|
+
}),
|
|
3120
4415
|
run: async (args) => {
|
|
3121
|
-
|
|
3122
|
-
|
|
3123
|
-
|
|
3124
|
-
|
|
4416
|
+
await setupTarget({
|
|
4417
|
+
kind: "tag",
|
|
4418
|
+
workspaceName: args.name,
|
|
4419
|
+
tagPattern: args["tag-pattern"],
|
|
3125
4420
|
branch: args.branch,
|
|
3126
|
-
tag: args.tag,
|
|
3127
|
-
tagPattern: args["tag-pattern"] ?? "v*",
|
|
3128
4421
|
environment: args.environment,
|
|
3129
|
-
plan: !args["no-plan"],
|
|
3130
4422
|
dir: args.dir,
|
|
3131
4423
|
force: args.force,
|
|
3132
4424
|
outputDir: process.cwd()
|
|
3133
4425
|
});
|
|
3134
4426
|
}
|
|
3135
4427
|
});
|
|
3136
|
-
|
|
3137
|
-
|
|
3138
|
-
|
|
3139
|
-
|
|
3140
|
-
|
|
3141
|
-
|
|
3142
|
-
|
|
3143
|
-
}
|
|
3144
|
-
|
|
3145
|
-
|
|
3146
|
-
}
|
|
3147
|
-
|
|
3148
|
-
|
|
3149
|
-
|
|
3150
|
-
|
|
3151
|
-
* @param options.source
|
|
3152
|
-
* @param options.agent
|
|
3153
|
-
* @param options.yes
|
|
3154
|
-
* @returns CLI arguments for `npx skills add`
|
|
3155
|
-
*/
|
|
3156
|
-
function buildSkillsAddArgs(options) {
|
|
3157
|
-
const args = [
|
|
3158
|
-
"skills",
|
|
3159
|
-
"add",
|
|
3160
|
-
resolveSkillsSource(options.source),
|
|
3161
|
-
"--skill",
|
|
3162
|
-
SKILL_NAME,
|
|
3163
|
-
"--copy"
|
|
3164
|
-
];
|
|
3165
|
-
if (options.agent) args.push("--agent", options.agent);
|
|
3166
|
-
if (options.yes) args.push("--yes");
|
|
3167
|
-
return args;
|
|
3168
|
-
}
|
|
3169
|
-
/**
|
|
3170
|
-
* Run `npx skills add` to install the tailor-sdk skill.
|
|
3171
|
-
* @param options - Runtime options for skill installation
|
|
3172
|
-
* @returns Process exit code from the spawned `npx` command
|
|
3173
|
-
*/
|
|
3174
|
-
async function runSkillsInstaller(options) {
|
|
3175
|
-
const args = buildSkillsAddArgs({
|
|
3176
|
-
source: options.source,
|
|
3177
|
-
agent: options.agent,
|
|
3178
|
-
yes: options.yes
|
|
3179
|
-
});
|
|
3180
|
-
const childProcess = (options.spawnFn ?? ((command, spawnArgs, spawnOptions) => spawn(command, spawnArgs, spawnOptions)))(resolveNpxCommand(), args, { stdio: "inherit" });
|
|
3181
|
-
return await new Promise((resolve, reject) => {
|
|
3182
|
-
childProcess.on("error", (error) => reject(error));
|
|
3183
|
-
childProcess.on("close", (code) => resolve(code ?? 1));
|
|
3184
|
-
});
|
|
3185
|
-
}
|
|
3186
|
-
|
|
3187
|
-
//#endregion
|
|
3188
|
-
//#region src/cli/commands/skills/install.ts
|
|
3189
|
-
async function resolveBundledSkillsDir() {
|
|
3190
|
-
return resolve(dirname(await resolvePackageJSON(import.meta.url)), "agent-skills");
|
|
3191
|
-
}
|
|
3192
|
-
const DEFAULT_AGENT = "claude-code";
|
|
3193
|
-
const installCommand = defineAppCommand({
|
|
3194
|
-
name: "install",
|
|
3195
|
-
description: "Install the tailor-sdk agent skill from the installed SDK package.",
|
|
3196
|
-
args: z.object({
|
|
3197
|
-
agent: arg(z.string().default(DEFAULT_AGENT), {
|
|
3198
|
-
alias: "a",
|
|
3199
|
-
description: `vercel/skills agent name (e.g. ${DEFAULT_AGENT}, codex). Defaults to ${DEFAULT_AGENT}.`
|
|
4428
|
+
const previewCommand = defineAppCommand({
|
|
4429
|
+
name: "preview",
|
|
4430
|
+
description: "Generate a preview workflow (PR open/sync triggers deploy to a per-PR workspace).",
|
|
4431
|
+
args: z.strictObject({
|
|
4432
|
+
name: arg(z.string().min(1).optional(), {
|
|
4433
|
+
alias: "n",
|
|
4434
|
+
description: "Name (defaults to the config 'name')"
|
|
4435
|
+
}),
|
|
4436
|
+
branch: arg(z.string().min(1).optional(), { description: "Branch to filter PRs by (defaults to the detected default branch)" }),
|
|
4437
|
+
region: arg(z.string().min(1), { description: "Workspace region for preview workspace creation (e.g. us-west). Required." }),
|
|
4438
|
+
"require-preview-label": arg(z.boolean().default(false), { description: "Deploy preview only for PRs labeled `tailor:preview` instead of all PRs." }),
|
|
4439
|
+
environment: arg(z.string().min(1).optional(), { description: "GitHub Environment for the preview jobs (defaults to the workspace name)" }),
|
|
4440
|
+
dir: arg(z.string().min(1).default("."), {
|
|
4441
|
+
alias: "d",
|
|
4442
|
+
description: "App directory (for monorepo setups)"
|
|
3200
4443
|
}),
|
|
3201
|
-
|
|
3202
|
-
|
|
3203
|
-
|
|
4444
|
+
force: arg(z.boolean().default(false), { description: "Discard hand edits / take over unmanaged files and regenerate" })
|
|
4445
|
+
}),
|
|
4446
|
+
run: async (args) => {
|
|
4447
|
+
await setupTarget({
|
|
4448
|
+
kind: "preview",
|
|
4449
|
+
workspaceName: args.name,
|
|
4450
|
+
branch: args.branch,
|
|
4451
|
+
region: args.region,
|
|
4452
|
+
requirePreviewLabel: args["require-preview-label"],
|
|
4453
|
+
environment: args.environment,
|
|
4454
|
+
dir: args.dir,
|
|
4455
|
+
force: args.force,
|
|
4456
|
+
outputDir: process.cwd()
|
|
4457
|
+
});
|
|
4458
|
+
}
|
|
4459
|
+
});
|
|
4460
|
+
const deleteCommand$1 = defineAppCommand({
|
|
4461
|
+
name: "delete",
|
|
4462
|
+
description: "Delete managed workflow/action file(s) and their .github/tailor.lock entries.",
|
|
4463
|
+
args: z.strictObject({
|
|
4464
|
+
...confirmationArgs,
|
|
4465
|
+
files: arg(z.string().array().min(1), {
|
|
4466
|
+
positional: true,
|
|
4467
|
+
description: "Workflow/action file(s) to delete, as generated under .github/workflows or .github/actions"
|
|
3204
4468
|
})
|
|
3205
|
-
})
|
|
4469
|
+
}),
|
|
3206
4470
|
run: async (args) => {
|
|
3207
|
-
|
|
3208
|
-
|
|
3209
|
-
|
|
3210
|
-
|
|
4471
|
+
await setupDelete({
|
|
4472
|
+
files: args.files,
|
|
4473
|
+
yes: args.yes,
|
|
4474
|
+
outputDir: process.cwd()
|
|
3211
4475
|
});
|
|
3212
|
-
if (exitCode !== 0) process.exit(exitCode);
|
|
3213
4476
|
}
|
|
3214
4477
|
});
|
|
3215
|
-
|
|
3216
|
-
|
|
3217
|
-
|
|
3218
|
-
|
|
3219
|
-
|
|
3220
|
-
|
|
3221
|
-
|
|
3222
|
-
|
|
3223
|
-
|
|
4478
|
+
const setupCommand = defineCommand({
|
|
4479
|
+
name: "setup",
|
|
4480
|
+
description: "Generate CI deploy workflows for your project. (beta)",
|
|
4481
|
+
subCommands: {
|
|
4482
|
+
branch: branchCommand,
|
|
4483
|
+
tag: tagCommand,
|
|
4484
|
+
preview: previewCommand,
|
|
4485
|
+
action: actionCommand,
|
|
4486
|
+
coordinate: coordinateCommand,
|
|
4487
|
+
check: checkCommand,
|
|
4488
|
+
delete: deleteCommand$1
|
|
3224
4489
|
}
|
|
3225
4490
|
});
|
|
3226
4491
|
|
|
@@ -3264,7 +4529,7 @@ async function withTimeout(p, ms, message) {
|
|
|
3264
4529
|
//#endregion
|
|
3265
4530
|
//#region src/cli/commands/staticwebsite/deploy.ts
|
|
3266
4531
|
const CHUNK_SIZE = 64 * 1024;
|
|
3267
|
-
const IGNORED_FILES = new Set([
|
|
4532
|
+
const IGNORED_FILES = /* @__PURE__ */ new Set([
|
|
3268
4533
|
".DS_Store",
|
|
3269
4534
|
"thumbs.db",
|
|
3270
4535
|
"desktop.ini"
|
|
@@ -3384,7 +4649,7 @@ function logSkippedFiles(skippedFiles) {
|
|
|
3384
4649
|
const deployCommand = defineAppCommand({
|
|
3385
4650
|
name: "deploy",
|
|
3386
4651
|
description: "Deploy a static website from a local build directory.",
|
|
3387
|
-
args: z.
|
|
4652
|
+
args: z.strictObject({
|
|
3388
4653
|
...workspaceArgs,
|
|
3389
4654
|
name: arg(z.string(), {
|
|
3390
4655
|
alias: "n",
|
|
@@ -3395,7 +4660,7 @@ const deployCommand = defineAppCommand({
|
|
|
3395
4660
|
description: "Path to the static website files",
|
|
3396
4661
|
completion: { type: "directory" }
|
|
3397
4662
|
})
|
|
3398
|
-
})
|
|
4663
|
+
}),
|
|
3399
4664
|
run: async (args) => {
|
|
3400
4665
|
await assertWritable({ profile: args.profile });
|
|
3401
4666
|
logger.info(`Deploying static website "${args.name}" from directory: ${args.dir}`);
|
|
@@ -3437,13 +4702,13 @@ const statusLabels = {
|
|
|
3437
4702
|
const domainGetCommand = defineAppCommand({
|
|
3438
4703
|
name: "get",
|
|
3439
4704
|
description: "Get details of a custom domain.",
|
|
3440
|
-
args: z.
|
|
4705
|
+
args: z.strictObject({
|
|
3441
4706
|
...workspaceArgs,
|
|
3442
4707
|
domain: arg(z.string(), {
|
|
3443
4708
|
positional: true,
|
|
3444
4709
|
description: "Custom domain name"
|
|
3445
4710
|
})
|
|
3446
|
-
})
|
|
4711
|
+
}),
|
|
3447
4712
|
run: async (args) => {
|
|
3448
4713
|
const client = await initOperatorClient(await loadAccessToken({ profile: args.profile }));
|
|
3449
4714
|
const workspaceId = await loadWorkspaceId({
|
|
@@ -3477,13 +4742,13 @@ const domainGetCommand = defineAppCommand({
|
|
|
3477
4742
|
const domainListCommand = defineAppCommand({
|
|
3478
4743
|
name: "list",
|
|
3479
4744
|
description: "List custom domains for a static website.",
|
|
3480
|
-
args: z.
|
|
4745
|
+
args: z.strictObject({
|
|
3481
4746
|
...workspaceArgs,
|
|
3482
4747
|
name: arg(z.string(), {
|
|
3483
4748
|
positional: true,
|
|
3484
4749
|
description: "Static website name"
|
|
3485
4750
|
})
|
|
3486
|
-
})
|
|
4751
|
+
}),
|
|
3487
4752
|
run: async (args) => {
|
|
3488
4753
|
const client = await initOperatorClient(await loadAccessToken({ profile: args.profile }));
|
|
3489
4754
|
const workspaceId = await loadWorkspaceId({
|
|
@@ -3529,13 +4794,13 @@ const domainCommand = defineCommand({
|
|
|
3529
4794
|
const getCommand = defineAppCommand({
|
|
3530
4795
|
name: "get",
|
|
3531
4796
|
description: "Get details of a specific static website.",
|
|
3532
|
-
args: z.
|
|
4797
|
+
args: z.strictObject({
|
|
3533
4798
|
...workspaceArgs,
|
|
3534
4799
|
name: arg(z.string(), {
|
|
3535
4800
|
positional: true,
|
|
3536
4801
|
description: "Static website name"
|
|
3537
4802
|
})
|
|
3538
|
-
})
|
|
4803
|
+
}),
|
|
3539
4804
|
run: async (args) => {
|
|
3540
4805
|
const client = await initOperatorClient(await loadAccessToken({ profile: args.profile }));
|
|
3541
4806
|
const workspaceId = await loadWorkspaceId({
|
|
@@ -3597,10 +4862,10 @@ async function listStaticWebsites(options) {
|
|
|
3597
4862
|
const listCommand$2 = defineAppCommand({
|
|
3598
4863
|
name: "list",
|
|
3599
4864
|
description: "List all static websites in a workspace.",
|
|
3600
|
-
args: z.
|
|
4865
|
+
args: z.strictObject({
|
|
3601
4866
|
...workspaceArgs,
|
|
3602
4867
|
...paginationArgs()
|
|
3603
|
-
})
|
|
4868
|
+
}),
|
|
3604
4869
|
run: async (args) => {
|
|
3605
4870
|
const jsonOutput = logger.jsonMode;
|
|
3606
4871
|
const websites = await listStaticWebsites({
|
|
@@ -3862,7 +5127,7 @@ function buildTailorDbErdSchema(options) {
|
|
|
3862
5127
|
namespace: namespaceData.namespace,
|
|
3863
5128
|
source: options.source ?? "local",
|
|
3864
5129
|
cleanRoom: {
|
|
3865
|
-
implementation: "tailor
|
|
5130
|
+
implementation: "tailor",
|
|
3866
5131
|
notes: CLEAN_ROOM_NOTES
|
|
3867
5132
|
},
|
|
3868
5133
|
tables,
|
|
@@ -3904,6 +5169,9 @@ async function initErdDeployContext(args) {
|
|
|
3904
5169
|
const VIEWER_ASSETS_DIR = "erd-viewer-assets";
|
|
3905
5170
|
const STYLES_LINK = "<link rel=\"stylesheet\" href=\"./styles.css\" />";
|
|
3906
5171
|
const APP_SCRIPT = "<script src=\"./app.js\" type=\"module\"><\/script>";
|
|
5172
|
+
function escapeHtml(value) {
|
|
5173
|
+
return value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll("\"", """);
|
|
5174
|
+
}
|
|
3907
5175
|
function assetDirCandidates() {
|
|
3908
5176
|
const currentDir = path.dirname(fileURLToPath(import.meta.url));
|
|
3909
5177
|
return [
|
|
@@ -3926,8 +5194,8 @@ function resolveViewerAssetsDir() {
|
|
|
3926
5194
|
* are inlined as separately extractable blocks: a `<style>` element, a
|
|
3927
5195
|
* `<script type="module">`, and a `<script type="application/json"
|
|
3928
5196
|
* id="erd-schema">` data block. This renders without any sibling asset files
|
|
3929
|
-
* and lets external tooling (
|
|
3930
|
-
* `JSON.parse`.
|
|
5197
|
+
* and lets external tooling (including the ERD diff command) pull out the
|
|
5198
|
+
* schema via `JSON.parse`.
|
|
3931
5199
|
* @param options - Viewer build options.
|
|
3932
5200
|
* @returns The self-contained HTML document.
|
|
3933
5201
|
*/
|
|
@@ -3938,8 +5206,10 @@ function buildViewerHtml(options) {
|
|
|
3938
5206
|
const appJs = fs$1.readFileSync(path.join(assetsDir, "app.js"), "utf8");
|
|
3939
5207
|
if (!html.includes(STYLES_LINK) || !html.includes(APP_SCRIPT)) throw new Error("ERD viewer index.html is missing expected asset references for inlining.");
|
|
3940
5208
|
const embedScript = `<script type="application/json" id="erd-schema">${JSON.stringify(options.schema).replaceAll("<", "\\u003c")}<\/script>`;
|
|
5209
|
+
const currentSchemaScript = options.currentSchema === void 0 ? "" : `\n <script type="application/json" id="erd-current-schema">${JSON.stringify(options.currentSchema).replaceAll("<", "\\u003c")}<\/script>`;
|
|
5210
|
+
const diffScript = options.diff === void 0 ? "" : `\n <script type="application/json" id="erd-diff">${JSON.stringify(options.diff).replaceAll("<", "\\u003c")}<\/script>`;
|
|
3941
5211
|
const inlineScript = `<script type="module">\n${appJs.replace(/<\/script/gi, "<\\/script")}\n<\/script>`;
|
|
3942
|
-
return html.replace(STYLES_LINK, `<style>\n${css}\n</style>`).replace(APP_SCRIPT, `${embedScript}\n ${inlineScript}`);
|
|
5212
|
+
return html.replace("<title>TailorDB ERD</title>", () => `<title>${escapeHtml(options.title ?? "TailorDB ERD")}</title>`).replace(STYLES_LINK, () => `<style>\n${css}\n</style>`).replace(APP_SCRIPT, () => `${embedScript}${currentSchemaScript}${diffScript}\n ${inlineScript}`);
|
|
3943
5213
|
}
|
|
3944
5214
|
/**
|
|
3945
5215
|
* Write the self-contained TailorDB ERD viewer to `<distDir>/index.html`.
|
|
@@ -3956,7 +5226,7 @@ function writeViewerDist(options) {
|
|
|
3956
5226
|
|
|
3957
5227
|
//#endregion
|
|
3958
5228
|
//#region src/cli/commands/tailordb/erd/export.ts
|
|
3959
|
-
const DEFAULT_ERD_BASE_DIR$1 = ".tailor
|
|
5229
|
+
const DEFAULT_ERD_BASE_DIR$1 = ".tailor/erd";
|
|
3960
5230
|
function getErdSite(context, namespace) {
|
|
3961
5231
|
const dbConfig = context.config.db?.[namespace];
|
|
3962
5232
|
if (!dbConfig || "external" in dbConfig) return;
|
|
@@ -4036,7 +5306,7 @@ function prepareErdBuildsFromContext(options) {
|
|
|
4036
5306
|
const erdExportCommand = defineAppCommand({
|
|
4037
5307
|
name: "export",
|
|
4038
5308
|
description: "Export TailorDB ERD static viewer from local TailorDB schema.",
|
|
4039
|
-
args: z.
|
|
5309
|
+
args: z.strictObject({
|
|
4040
5310
|
...configArg,
|
|
4041
5311
|
namespace: arg(z.string().optional(), {
|
|
4042
5312
|
alias: "n",
|
|
@@ -4047,7 +5317,7 @@ const erdExportCommand = defineAppCommand({
|
|
|
4047
5317
|
description: "Output directory path for TailorDB ERD viewer files (writes to `<outputDir>/<namespace>/dist`)",
|
|
4048
5318
|
completion: { type: "directory" }
|
|
4049
5319
|
})
|
|
4050
|
-
})
|
|
5320
|
+
}),
|
|
4051
5321
|
run: async (args) => {
|
|
4052
5322
|
initErdCommand();
|
|
4053
5323
|
const results = await prepareErdBuilds({
|
|
@@ -4072,13 +5342,13 @@ const erdExportCommand = defineAppCommand({
|
|
|
4072
5342
|
const erdDeployCommand = defineAppCommand({
|
|
4073
5343
|
name: "deploy",
|
|
4074
5344
|
description: "Deploy ERD static website for TailorDB namespace(s).",
|
|
4075
|
-
args: z.
|
|
5345
|
+
args: z.strictObject({
|
|
4076
5346
|
...deploymentArgs,
|
|
4077
5347
|
namespace: arg(z.string().optional(), {
|
|
4078
5348
|
alias: "n",
|
|
4079
5349
|
description: "TailorDB namespace name (optional - deploys all namespaces with erdSite if omitted)"
|
|
4080
5350
|
})
|
|
4081
|
-
})
|
|
5351
|
+
}),
|
|
4082
5352
|
run: async (args) => {
|
|
4083
5353
|
await assertWritable({ profile: args.profile });
|
|
4084
5354
|
const { client, workspaceId } = await initErdDeployContext(args);
|
|
@@ -4109,13 +5379,369 @@ const erdDeployCommand = defineAppCommand({
|
|
|
4109
5379
|
}
|
|
4110
5380
|
});
|
|
4111
5381
|
|
|
5382
|
+
//#endregion
|
|
5383
|
+
//#region src/cli/commands/tailordb/erd/diff.ts
|
|
5384
|
+
const SCHEMA_BLOCK_PATTERN = /<script type="application\/json" id="erd-schema">([\s\S]*?)<\/script>/;
|
|
5385
|
+
function stableValue(value) {
|
|
5386
|
+
if (Array.isArray(value)) return value.map((item) => stableValue(item));
|
|
5387
|
+
if (value && typeof value === "object") return Object.fromEntries(Object.entries(value).toSorted(([a], [b]) => a.localeCompare(b)).map(([key, item]) => [key, stableValue(item)]));
|
|
5388
|
+
return value;
|
|
5389
|
+
}
|
|
5390
|
+
function stableJson(value) {
|
|
5391
|
+
return JSON.stringify(stableValue(value));
|
|
5392
|
+
}
|
|
5393
|
+
function changedFields(base, head) {
|
|
5394
|
+
return [.../* @__PURE__ */ new Set([...Object.keys(base), ...Object.keys(head)])].toSorted((a, b) => a.localeCompare(b)).filter((key) => stableJson(base[key]) !== stableJson(head[key]));
|
|
5395
|
+
}
|
|
5396
|
+
function detailForChangedFields(fields) {
|
|
5397
|
+
return `Changed fields: ${fields.join(", ")}`;
|
|
5398
|
+
}
|
|
5399
|
+
function mapByName(items) {
|
|
5400
|
+
return new Map(items.map((item) => [item.name, item]));
|
|
5401
|
+
}
|
|
5402
|
+
function compareCommonNamed(baseItems, headItems, addChange) {
|
|
5403
|
+
const baseByName = mapByName(baseItems);
|
|
5404
|
+
const headByName = mapByName(headItems);
|
|
5405
|
+
const commonNames = [...baseByName.keys()].filter((name) => headByName.has(name)).toSorted((a, b) => a.localeCompare(b));
|
|
5406
|
+
for (const name of commonNames) {
|
|
5407
|
+
const base = baseByName.get(name);
|
|
5408
|
+
const head = headByName.get(name);
|
|
5409
|
+
if (!base || !head) continue;
|
|
5410
|
+
const fields = changedFields(base, head);
|
|
5411
|
+
if (fields.length > 0) addChange(name, fields);
|
|
5412
|
+
}
|
|
5413
|
+
}
|
|
5414
|
+
function removedNames(baseItems, headItems) {
|
|
5415
|
+
const headByName = mapByName(headItems);
|
|
5416
|
+
return baseItems.map((item) => item.name).filter((name) => !headByName.has(name)).toSorted((a, b) => a.localeCompare(b));
|
|
5417
|
+
}
|
|
5418
|
+
function addedNames(baseItems, headItems) {
|
|
5419
|
+
const baseByName = mapByName(baseItems);
|
|
5420
|
+
return headItems.map((item) => item.name).filter((name) => !baseByName.has(name)).toSorted((a, b) => a.localeCompare(b));
|
|
5421
|
+
}
|
|
5422
|
+
function tableMetadata(table) {
|
|
5423
|
+
return {
|
|
5424
|
+
backwardRelationships: table.backwardRelationships,
|
|
5425
|
+
description: table.description,
|
|
5426
|
+
forwardRelationships: table.forwardRelationships,
|
|
5427
|
+
pluralForm: table.pluralForm,
|
|
5428
|
+
source: table.source
|
|
5429
|
+
};
|
|
5430
|
+
}
|
|
5431
|
+
function pushChange(changes, change) {
|
|
5432
|
+
changes.push(change);
|
|
5433
|
+
}
|
|
5434
|
+
function diffColumns(changes, tableName, baseColumns, headColumns) {
|
|
5435
|
+
compareCommonNamed(baseColumns, headColumns, (name, fields) => {
|
|
5436
|
+
pushChange(changes, {
|
|
5437
|
+
action: "changed",
|
|
5438
|
+
entity: "column",
|
|
5439
|
+
path: `${tableName}.${name}`,
|
|
5440
|
+
detail: detailForChangedFields(fields)
|
|
5441
|
+
});
|
|
5442
|
+
});
|
|
5443
|
+
for (const name of removedNames(baseColumns, headColumns)) pushChange(changes, {
|
|
5444
|
+
action: "removed",
|
|
5445
|
+
entity: "column",
|
|
5446
|
+
path: `${tableName}.${name}`,
|
|
5447
|
+
detail: "Column removed"
|
|
5448
|
+
});
|
|
5449
|
+
for (const name of addedNames(baseColumns, headColumns)) pushChange(changes, {
|
|
5450
|
+
action: "added",
|
|
5451
|
+
entity: "column",
|
|
5452
|
+
path: `${tableName}.${name}`,
|
|
5453
|
+
detail: "Column added"
|
|
5454
|
+
});
|
|
5455
|
+
}
|
|
5456
|
+
function diffIndexes(changes, tableName, baseIndexes, headIndexes) {
|
|
5457
|
+
compareCommonNamed(baseIndexes, headIndexes, (name, fields) => {
|
|
5458
|
+
pushChange(changes, {
|
|
5459
|
+
action: "changed",
|
|
5460
|
+
entity: "index",
|
|
5461
|
+
path: `${tableName}.${name}`,
|
|
5462
|
+
detail: detailForChangedFields(fields)
|
|
5463
|
+
});
|
|
5464
|
+
});
|
|
5465
|
+
for (const name of removedNames(baseIndexes, headIndexes)) pushChange(changes, {
|
|
5466
|
+
action: "removed",
|
|
5467
|
+
entity: "index",
|
|
5468
|
+
path: `${tableName}.${name}`,
|
|
5469
|
+
detail: "Index removed"
|
|
5470
|
+
});
|
|
5471
|
+
for (const name of addedNames(baseIndexes, headIndexes)) pushChange(changes, {
|
|
5472
|
+
action: "added",
|
|
5473
|
+
entity: "index",
|
|
5474
|
+
path: `${tableName}.${name}`,
|
|
5475
|
+
detail: "Index added"
|
|
5476
|
+
});
|
|
5477
|
+
}
|
|
5478
|
+
function diffTables(changes, baseTables, headTables) {
|
|
5479
|
+
const baseByName = mapByName(baseTables);
|
|
5480
|
+
const headByName = mapByName(headTables);
|
|
5481
|
+
for (const name of removedNames(baseTables, headTables)) pushChange(changes, {
|
|
5482
|
+
action: "removed",
|
|
5483
|
+
entity: "table",
|
|
5484
|
+
path: name,
|
|
5485
|
+
detail: "Table removed"
|
|
5486
|
+
});
|
|
5487
|
+
for (const name of addedNames(baseTables, headTables)) pushChange(changes, {
|
|
5488
|
+
action: "added",
|
|
5489
|
+
entity: "table",
|
|
5490
|
+
path: name,
|
|
5491
|
+
detail: "Table added"
|
|
5492
|
+
});
|
|
5493
|
+
const commonNames = [...baseByName.keys()].filter((name) => headByName.has(name)).toSorted((a, b) => a.localeCompare(b));
|
|
5494
|
+
for (const name of commonNames) {
|
|
5495
|
+
const base = baseByName.get(name);
|
|
5496
|
+
const head = headByName.get(name);
|
|
5497
|
+
if (!base || !head) continue;
|
|
5498
|
+
const tableFields = changedFields(tableMetadata(base), tableMetadata(head));
|
|
5499
|
+
if (tableFields.length > 0) pushChange(changes, {
|
|
5500
|
+
action: "changed",
|
|
5501
|
+
entity: "table",
|
|
5502
|
+
path: name,
|
|
5503
|
+
detail: detailForChangedFields(tableFields)
|
|
5504
|
+
});
|
|
5505
|
+
diffColumns(changes, name, base.columns, head.columns);
|
|
5506
|
+
diffIndexes(changes, name, base.indexes, head.indexes);
|
|
5507
|
+
}
|
|
5508
|
+
}
|
|
5509
|
+
function diffRelations(changes, baseRelations, headRelations) {
|
|
5510
|
+
compareCommonNamed(baseRelations, headRelations, (name, fields) => {
|
|
5511
|
+
pushChange(changes, {
|
|
5512
|
+
action: "changed",
|
|
5513
|
+
entity: "relation",
|
|
5514
|
+
path: name,
|
|
5515
|
+
detail: detailForChangedFields(fields)
|
|
5516
|
+
});
|
|
5517
|
+
});
|
|
5518
|
+
for (const name of removedNames(baseRelations, headRelations)) pushChange(changes, {
|
|
5519
|
+
action: "removed",
|
|
5520
|
+
entity: "relation",
|
|
5521
|
+
path: name,
|
|
5522
|
+
detail: "Relation removed"
|
|
5523
|
+
});
|
|
5524
|
+
for (const name of addedNames(baseRelations, headRelations)) pushChange(changes, {
|
|
5525
|
+
action: "added",
|
|
5526
|
+
entity: "relation",
|
|
5527
|
+
path: name,
|
|
5528
|
+
detail: "Relation added"
|
|
5529
|
+
});
|
|
5530
|
+
}
|
|
5531
|
+
function summarize(changes) {
|
|
5532
|
+
return {
|
|
5533
|
+
added: changes.filter((change) => change.action === "added").length,
|
|
5534
|
+
changed: changes.filter((change) => change.action === "changed").length,
|
|
5535
|
+
removed: changes.filter((change) => change.action === "removed").length
|
|
5536
|
+
};
|
|
5537
|
+
}
|
|
5538
|
+
function extractEmbeddedErdSchema(html) {
|
|
5539
|
+
const match = SCHEMA_BLOCK_PATTERN.exec(html);
|
|
5540
|
+
if (!match?.[1]) throw new Error("ERD schema block not found.");
|
|
5541
|
+
try {
|
|
5542
|
+
return JSON.parse(match[1]);
|
|
5543
|
+
} catch (error) {
|
|
5544
|
+
throw new Error(`Failed to parse ERD schema block: ${String(error)}`, { cause: error });
|
|
5545
|
+
}
|
|
5546
|
+
}
|
|
5547
|
+
function createEmptyErdSchema(options) {
|
|
5548
|
+
return {
|
|
5549
|
+
version: 1,
|
|
5550
|
+
namespace: options.namespace,
|
|
5551
|
+
generatedAt: (/* @__PURE__ */ new Date(0)).toISOString(),
|
|
5552
|
+
revision: options.revision,
|
|
5553
|
+
source: "local",
|
|
5554
|
+
cleanRoom: {
|
|
5555
|
+
implementation: "tailor",
|
|
5556
|
+
notes: ["Synthetic empty schema used for ERD diff generation."]
|
|
5557
|
+
},
|
|
5558
|
+
tables: [],
|
|
5559
|
+
relations: []
|
|
5560
|
+
};
|
|
5561
|
+
}
|
|
5562
|
+
function buildErdSchemaDiff(options) {
|
|
5563
|
+
const { base, head } = options;
|
|
5564
|
+
if (base.namespace !== head.namespace) throw new Error(`Cannot diff ERD schemas from different namespaces: ${base.namespace}, ${head.namespace}`);
|
|
5565
|
+
const changes = [];
|
|
5566
|
+
diffTables(changes, base.tables, head.tables);
|
|
5567
|
+
diffRelations(changes, base.relations, head.relations);
|
|
5568
|
+
const summary = summarize(changes);
|
|
5569
|
+
return {
|
|
5570
|
+
namespace: head.namespace,
|
|
5571
|
+
baseRevision: base.revision,
|
|
5572
|
+
headRevision: head.revision,
|
|
5573
|
+
changed: changes.length > 0,
|
|
5574
|
+
summary,
|
|
5575
|
+
changes
|
|
5576
|
+
};
|
|
5577
|
+
}
|
|
5578
|
+
function mergeNamedByHead(baseItems, headItems) {
|
|
5579
|
+
const headByName = mapByName(headItems);
|
|
5580
|
+
const removedItems = baseItems.filter((item) => !headByName.has(item.name)).toSorted((a, b) => a.name.localeCompare(b.name));
|
|
5581
|
+
return [...headItems, ...removedItems];
|
|
5582
|
+
}
|
|
5583
|
+
function mergeDiffViewerTable(baseTable, headTable) {
|
|
5584
|
+
return {
|
|
5585
|
+
...headTable,
|
|
5586
|
+
columns: mergeNamedByHead(baseTable.columns, headTable.columns),
|
|
5587
|
+
indexes: mergeNamedByHead(baseTable.indexes, headTable.indexes)
|
|
5588
|
+
};
|
|
5589
|
+
}
|
|
5590
|
+
function mergeDiffViewerTables(baseTables, headTables) {
|
|
5591
|
+
const baseByName = mapByName(baseTables);
|
|
5592
|
+
const headByName = mapByName(headTables);
|
|
5593
|
+
const merged = headTables.map((headTable) => {
|
|
5594
|
+
const baseTable = baseByName.get(headTable.name);
|
|
5595
|
+
return baseTable ? mergeDiffViewerTable(baseTable, headTable) : headTable;
|
|
5596
|
+
});
|
|
5597
|
+
const removedTables = baseTables.filter((table) => !headByName.has(table.name)).toSorted((a, b) => a.name.localeCompare(b.name));
|
|
5598
|
+
return [...merged, ...removedTables];
|
|
5599
|
+
}
|
|
5600
|
+
/**
|
|
5601
|
+
* Build the schema rendered by the diff viewer. The ordinary ERD viewer only
|
|
5602
|
+
* knows how to draw objects present in its schema, so the diff schema keeps
|
|
5603
|
+
* base-only tables, columns, indexes, and relations visible for removal
|
|
5604
|
+
* highlighting while preferring head-side metadata for unchanged objects.
|
|
5605
|
+
* @param options - Base and head schemas to merge for diff rendering.
|
|
5606
|
+
* @returns A TailorDB ERD schema suitable for the visual diff viewer.
|
|
5607
|
+
*/
|
|
5608
|
+
function buildErdDiffViewerSchema(options) {
|
|
5609
|
+
const { base, head } = options;
|
|
5610
|
+
if (base.namespace !== head.namespace) throw new Error(`Cannot diff ERD schemas from different namespaces: ${base.namespace}, ${head.namespace}`);
|
|
5611
|
+
return {
|
|
5612
|
+
...head,
|
|
5613
|
+
tables: mergeDiffViewerTables(base.tables, head.tables),
|
|
5614
|
+
relations: mergeNamedByHead(base.relations, head.relations)
|
|
5615
|
+
};
|
|
5616
|
+
}
|
|
5617
|
+
function renderErdDiffHtml(options) {
|
|
5618
|
+
return buildViewerHtml({
|
|
5619
|
+
schema: options.schema,
|
|
5620
|
+
currentSchema: options.currentSchema,
|
|
5621
|
+
diff: options.diff,
|
|
5622
|
+
title: `TailorDB ERD diff - ${options.diff.namespace}`
|
|
5623
|
+
});
|
|
5624
|
+
}
|
|
5625
|
+
|
|
5626
|
+
//#endregion
|
|
5627
|
+
//#region src/cli/commands/tailordb/erd/diff-command.ts
|
|
5628
|
+
function readSchema(filePath) {
|
|
5629
|
+
if (!filePath) return void 0;
|
|
5630
|
+
return extractEmbeddedErdSchema(fs$1.readFileSync(filePath, "utf8"));
|
|
5631
|
+
}
|
|
5632
|
+
function resolveNamespace(options) {
|
|
5633
|
+
const namespace = options.namespace ?? options.head?.namespace ?? options.base?.namespace;
|
|
5634
|
+
if (!namespace) throw new Error("Missing --namespace when one side of the ERD diff is omitted.");
|
|
5635
|
+
if (options.base && options.base.namespace !== namespace) throw new Error(`Base ERD namespace "${options.base.namespace}" does not match "${namespace}".`);
|
|
5636
|
+
if (options.head && options.head.namespace !== namespace) throw new Error(`Head ERD namespace "${options.head.namespace}" does not match "${namespace}".`);
|
|
5637
|
+
return namespace;
|
|
5638
|
+
}
|
|
5639
|
+
function writeFile(filePath, content) {
|
|
5640
|
+
fs$1.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
5641
|
+
fs$1.writeFileSync(filePath, content, "utf8");
|
|
5642
|
+
}
|
|
5643
|
+
function writeErdDiff(options) {
|
|
5644
|
+
const base = readSchema(options.baseHtml);
|
|
5645
|
+
const head = readSchema(options.headHtml);
|
|
5646
|
+
if (!base && !head) throw new Error("At least one of --base-html or --head-html is required.");
|
|
5647
|
+
const namespace = resolveNamespace({
|
|
5648
|
+
namespace: options.namespace,
|
|
5649
|
+
base,
|
|
5650
|
+
head
|
|
5651
|
+
});
|
|
5652
|
+
const baseSchema = base ?? createEmptyErdSchema({
|
|
5653
|
+
namespace,
|
|
5654
|
+
revision: "missing-base"
|
|
5655
|
+
});
|
|
5656
|
+
const headSchema = head ?? createEmptyErdSchema({
|
|
5657
|
+
namespace,
|
|
5658
|
+
revision: "missing-head"
|
|
5659
|
+
});
|
|
5660
|
+
const diff = buildErdSchemaDiff({
|
|
5661
|
+
base: baseSchema,
|
|
5662
|
+
head: headSchema
|
|
5663
|
+
});
|
|
5664
|
+
const viewerSchema = buildErdDiffViewerSchema({
|
|
5665
|
+
base: baseSchema,
|
|
5666
|
+
head: headSchema
|
|
5667
|
+
});
|
|
5668
|
+
writeFile(options.outputHtml, renderErdDiffHtml({
|
|
5669
|
+
schema: viewerSchema,
|
|
5670
|
+
currentSchema: headSchema,
|
|
5671
|
+
diff
|
|
5672
|
+
}));
|
|
5673
|
+
if (options.outputJson) writeFile(options.outputJson, `${JSON.stringify(diff, null, 2)}\n`);
|
|
5674
|
+
return {
|
|
5675
|
+
namespace,
|
|
5676
|
+
outputHtml: options.outputHtml,
|
|
5677
|
+
outputJson: options.outputJson,
|
|
5678
|
+
diff
|
|
5679
|
+
};
|
|
5680
|
+
}
|
|
5681
|
+
const erdDiffCommand = defineAppCommand({
|
|
5682
|
+
name: "diff",
|
|
5683
|
+
description: "Render TailorDB ERD schema diff HTML from exported ERD viewers.",
|
|
5684
|
+
args: z.strictObject({
|
|
5685
|
+
"base-html": arg(z.string().optional(), {
|
|
5686
|
+
description: "Base ERD viewer HTML file",
|
|
5687
|
+
completion: {
|
|
5688
|
+
type: "file",
|
|
5689
|
+
matcher: [".html"]
|
|
5690
|
+
}
|
|
5691
|
+
}),
|
|
5692
|
+
"head-html": arg(z.string().optional(), {
|
|
5693
|
+
description: "Head ERD viewer HTML file",
|
|
5694
|
+
completion: {
|
|
5695
|
+
type: "file",
|
|
5696
|
+
matcher: [".html"]
|
|
5697
|
+
}
|
|
5698
|
+
}),
|
|
5699
|
+
namespace: arg(z.string().optional(), {
|
|
5700
|
+
alias: "n",
|
|
5701
|
+
description: "TailorDB namespace name (defaults to the provided ERD schema namespace)"
|
|
5702
|
+
}),
|
|
5703
|
+
output: arg(z.string().min(1), {
|
|
5704
|
+
alias: "o",
|
|
5705
|
+
description: "Output ERD diff HTML file",
|
|
5706
|
+
completion: {
|
|
5707
|
+
type: "file",
|
|
5708
|
+
matcher: [".html"]
|
|
5709
|
+
}
|
|
5710
|
+
}),
|
|
5711
|
+
"output-json": arg(z.string().optional(), {
|
|
5712
|
+
description: "Optional output JSON file for the computed diff",
|
|
5713
|
+
completion: {
|
|
5714
|
+
type: "file",
|
|
5715
|
+
matcher: [".json"]
|
|
5716
|
+
}
|
|
5717
|
+
})
|
|
5718
|
+
}),
|
|
5719
|
+
run: (args) => {
|
|
5720
|
+
initErdCommand();
|
|
5721
|
+
const result = writeErdDiff({
|
|
5722
|
+
baseHtml: args["base-html"],
|
|
5723
|
+
headHtml: args["head-html"],
|
|
5724
|
+
namespace: args.namespace,
|
|
5725
|
+
outputHtml: args.output,
|
|
5726
|
+
outputJson: args["output-json"]
|
|
5727
|
+
});
|
|
5728
|
+
if (args.json) logger.out({
|
|
5729
|
+
namespace: result.namespace,
|
|
5730
|
+
outputHtml: result.outputHtml,
|
|
5731
|
+
outputJson: result.outputJson,
|
|
5732
|
+
summary: result.diff.summary
|
|
5733
|
+
});
|
|
5734
|
+
else logger.success(`Wrote ERD diff to ${path.relative(process.cwd(), result.outputHtml)}`);
|
|
5735
|
+
}
|
|
5736
|
+
});
|
|
5737
|
+
|
|
4112
5738
|
//#endregion
|
|
4113
5739
|
//#region src/cli/commands/tailordb/erd/serve.ts
|
|
4114
|
-
const DEFAULT_ERD_BASE_DIR = ".tailor
|
|
5740
|
+
const DEFAULT_ERD_BASE_DIR = ".tailor/erd";
|
|
4115
5741
|
const LOCAL_HOST = "127.0.0.1";
|
|
4116
5742
|
const GLOB_CHARS = /[*?[\]{}()!+@]/;
|
|
4117
5743
|
function formatServeCommand(namespace) {
|
|
4118
|
-
return `tailor
|
|
5744
|
+
return `tailor tailordb erd serve --namespace ${namespace}`;
|
|
4119
5745
|
}
|
|
4120
5746
|
function getCacheControl(filePath) {
|
|
4121
5747
|
return filePath.endsWith(".html") || filePath.endsWith(".json") ? "no-cache" : "public, max-age=3600";
|
|
@@ -4409,7 +6035,7 @@ async function waitForShutdown(server, watcher) {
|
|
|
4409
6035
|
const erdServeCommand = defineAppCommand({
|
|
4410
6036
|
name: "serve",
|
|
4411
6037
|
description: "Generate and serve TailorDB ERD locally with watch reload. (beta)",
|
|
4412
|
-
args: z.
|
|
6038
|
+
args: z.strictObject({
|
|
4413
6039
|
...configArg,
|
|
4414
6040
|
namespace: arg(z.string().optional(), {
|
|
4415
6041
|
alias: "n",
|
|
@@ -4417,7 +6043,7 @@ const erdServeCommand = defineAppCommand({
|
|
|
4417
6043
|
}),
|
|
4418
6044
|
port: arg(z.coerce.number().int().min(0).max(65535).default(0), { description: "Local server port (0 selects a free port)" }),
|
|
4419
6045
|
open: arg(z.boolean().default(false), { description: "Open the ERD viewer in the default browser" })
|
|
4420
|
-
})
|
|
6046
|
+
}),
|
|
4421
6047
|
run: async (args) => {
|
|
4422
6048
|
initErdCommand();
|
|
4423
6049
|
const outputDir = path.resolve(process.cwd(), DEFAULT_ERD_BASE_DIR);
|
|
@@ -4469,6 +6095,7 @@ const erdCommand = defineCommand({
|
|
|
4469
6095
|
description: "Generate TailorDB ERD viewer artifacts from local TailorDB schema. (beta)",
|
|
4470
6096
|
subCommands: {
|
|
4471
6097
|
export: erdExportCommand,
|
|
6098
|
+
diff: erdDiffCommand,
|
|
4472
6099
|
serve: erdServeCommand,
|
|
4473
6100
|
deploy: erdDeployCommand
|
|
4474
6101
|
}
|
|
@@ -4512,7 +6139,7 @@ async function script(options) {
|
|
|
4512
6139
|
logger.info(` DB types: ${getMigrationFilePath(migrationsDir, migrationNumber, "db")}`);
|
|
4513
6140
|
logger.newline();
|
|
4514
6141
|
logger.log("Edit the script to implement your data migration logic.");
|
|
4515
|
-
logger.log("It will be executed by 'tailor
|
|
6142
|
+
logger.log("It will be executed by 'tailor deploy' between Pre and Post phases.");
|
|
4516
6143
|
const editor = getConfiguredEditorCommand();
|
|
4517
6144
|
if (!editor) return;
|
|
4518
6145
|
logger.newline();
|
|
@@ -4537,7 +6164,7 @@ function resolveTargetNamespace(namespacesWithMigrations, requested) {
|
|
|
4537
6164
|
const scriptCommand = defineAppCommand({
|
|
4538
6165
|
name: "script",
|
|
4539
6166
|
description: "Add a migration script (migrate.ts) template to an existing migration directory.",
|
|
4540
|
-
args: z.
|
|
6167
|
+
args: z.strictObject({
|
|
4541
6168
|
...configArg,
|
|
4542
6169
|
number: arg(z.string(), {
|
|
4543
6170
|
positional: true,
|
|
@@ -4547,7 +6174,7 @@ const scriptCommand = defineAppCommand({
|
|
|
4547
6174
|
alias: "n",
|
|
4548
6175
|
description: "Target TailorDB namespace (required if multiple namespaces exist)"
|
|
4549
6176
|
})
|
|
4550
|
-
})
|
|
6177
|
+
}),
|
|
4551
6178
|
run: async (args) => {
|
|
4552
6179
|
await script({
|
|
4553
6180
|
configPath: args.config,
|
|
@@ -4633,7 +6260,7 @@ async function set(options) {
|
|
|
4633
6260
|
const setCommand = defineAppCommand({
|
|
4634
6261
|
name: "set",
|
|
4635
6262
|
description: "Set migration checkpoint to a specific number.",
|
|
4636
|
-
args: z.
|
|
6263
|
+
args: z.strictObject({
|
|
4637
6264
|
...deploymentArgs,
|
|
4638
6265
|
...confirmationArgs,
|
|
4639
6266
|
number: arg(z.string(), {
|
|
@@ -4644,7 +6271,7 @@ const setCommand = defineAppCommand({
|
|
|
4644
6271
|
alias: "n",
|
|
4645
6272
|
description: "Target TailorDB namespace (required if multiple namespaces exist)"
|
|
4646
6273
|
})
|
|
4647
|
-
})
|
|
6274
|
+
}),
|
|
4648
6275
|
run: async (args) => {
|
|
4649
6276
|
await assertWritable({ profile: args.profile });
|
|
4650
6277
|
await set({
|
|
@@ -4732,13 +6359,13 @@ async function status(options) {
|
|
|
4732
6359
|
const statusCommand = defineAppCommand({
|
|
4733
6360
|
name: "status",
|
|
4734
6361
|
description: "Show the current migration status for TailorDB namespaces, including applied and pending migrations.",
|
|
4735
|
-
args: z.
|
|
6362
|
+
args: z.strictObject({
|
|
4736
6363
|
...deploymentArgs,
|
|
4737
6364
|
namespace: arg(z.string().optional(), {
|
|
4738
6365
|
alias: "n",
|
|
4739
6366
|
description: "Target TailorDB namespace (shows all namespaces if not specified)"
|
|
4740
6367
|
})
|
|
4741
|
-
})
|
|
6368
|
+
}),
|
|
4742
6369
|
run: async (args) => {
|
|
4743
6370
|
await status({
|
|
4744
6371
|
configPath: args.config,
|
|
@@ -4753,19 +6380,14 @@ const statusCommand = defineAppCommand({
|
|
|
4753
6380
|
//#endregion
|
|
4754
6381
|
//#region src/cli/commands/tailordb/migrate/sync.ts
|
|
4755
6382
|
async function fetchRemoteGqlPermissions(client, workspaceId, namespace) {
|
|
4756
|
-
return
|
|
4757
|
-
|
|
4758
|
-
|
|
4759
|
-
|
|
4760
|
-
|
|
4761
|
-
|
|
4762
|
-
|
|
4763
|
-
|
|
4764
|
-
return [permissions, nextPageToken];
|
|
4765
|
-
} catch (error) {
|
|
4766
|
-
if (error instanceof ConnectError && error.code === Code.NotFound) return [[], ""];
|
|
4767
|
-
throw error;
|
|
4768
|
-
}
|
|
6383
|
+
return fetchAllTolerant(async (pageToken, maxPageSize) => {
|
|
6384
|
+
const { permissions, nextPageToken } = await client.listTailorDBGQLPermissions({
|
|
6385
|
+
workspaceId,
|
|
6386
|
+
namespaceName: namespace,
|
|
6387
|
+
pageToken,
|
|
6388
|
+
pageSize: maxPageSize
|
|
6389
|
+
});
|
|
6390
|
+
return [permissions, nextPageToken];
|
|
4769
6391
|
});
|
|
4770
6392
|
}
|
|
4771
6393
|
async function fetchRemoteTypes(client, workspaceId, namespace) {
|
|
@@ -4805,7 +6427,7 @@ async function fetchRemoteTypes(client, workspaceId, namespace) {
|
|
|
4805
6427
|
async function assertMigrationsReproduceLocalTypes(loaded, target) {
|
|
4806
6428
|
const { config, plugins } = loaded;
|
|
4807
6429
|
const pluginManager = plugins.length > 0 ? new PluginManager(plugins) : void 0;
|
|
4808
|
-
const { defineApplication, generatePluginFilesIfNeeded } = await import("../application-
|
|
6430
|
+
const { defineApplication, generatePluginFilesIfNeeded } = await import("../application-BO4ZF8wP.mjs");
|
|
4809
6431
|
const application = defineApplication({
|
|
4810
6432
|
config,
|
|
4811
6433
|
pluginManager
|
|
@@ -4817,7 +6439,7 @@ async function assertMigrationsReproduceLocalTypes(loaded, target) {
|
|
|
4817
6439
|
await service.processNamespacePlugins();
|
|
4818
6440
|
}
|
|
4819
6441
|
const pluginExecutorFiles = generatePluginFilesIfNeeded(pluginManager, application.tailorDBServices, config.path);
|
|
4820
|
-
const executorService = application.executorService ?? (pluginExecutorFiles.length > 0 ? (await import("../service-
|
|
6442
|
+
const executorService = application.executorService ?? (pluginExecutorFiles.length > 0 ? (await import("../service-CkIwEctX.mjs")).createExecutorService({ config: { files: [] } }) : void 0);
|
|
4821
6443
|
await executorService?.loadExecutors();
|
|
4822
6444
|
if (pluginExecutorFiles.length > 0) await executorService?.loadPluginExecutorFiles([...pluginExecutorFiles]);
|
|
4823
6445
|
const executorUsedTypes = /* @__PURE__ */ new Set();
|
|
@@ -4835,7 +6457,7 @@ async function assertMigrationsReproduceLocalTypes(loaded, target) {
|
|
|
4835
6457
|
logger.newline();
|
|
4836
6458
|
logger.info("This usually means one of the following:");
|
|
4837
6459
|
logger.info(" - Migration files were edited and replaying them no longer matches the type definitions — fix the migration files.", { mode: "plain" });
|
|
4838
|
-
logger.info(" - Type definitions changed without a new migration — run 'tailor
|
|
6460
|
+
logger.info(" - Type definitions changed without a new migration — run 'tailor tailordb migration generate' first.", { mode: "plain" });
|
|
4839
6461
|
logger.newline();
|
|
4840
6462
|
throw new Error("Refusing to sync: the migration history must reproduce the current local schema before it can be applied to the remote.");
|
|
4841
6463
|
}
|
|
@@ -4852,21 +6474,15 @@ async function assertMigrationsReproduceLocalTypes(loaded, target) {
|
|
|
4852
6474
|
* @returns Existing labels and the parsed current migration number
|
|
4853
6475
|
*/
|
|
4854
6476
|
async function fetchRemoteMigrationState(client, trn) {
|
|
4855
|
-
|
|
6477
|
+
const labels = (await getOrNull(async () => {
|
|
4856
6478
|
const { metadata } = await client.getMetadata({ trn });
|
|
4857
|
-
|
|
4858
|
-
|
|
4859
|
-
|
|
4860
|
-
|
|
4861
|
-
|
|
4862
|
-
|
|
4863
|
-
}
|
|
4864
|
-
if (error instanceof ConnectError && error.code === Code.NotFound) return {
|
|
4865
|
-
labels: {},
|
|
4866
|
-
current: null
|
|
4867
|
-
};
|
|
4868
|
-
throw error;
|
|
4869
|
-
}
|
|
6479
|
+
return metadata;
|
|
6480
|
+
}))?.labels ?? {};
|
|
6481
|
+
const label = labels[MIGRATION_LABEL_KEY];
|
|
6482
|
+
return {
|
|
6483
|
+
labels,
|
|
6484
|
+
current: label ? parseMigrationLabelNumber(label) : null
|
|
6485
|
+
};
|
|
4870
6486
|
}
|
|
4871
6487
|
function selectTargetNamespace(namespacesWithMigrations, requested) {
|
|
4872
6488
|
if (namespacesWithMigrations.length === 0) throw new Error("No TailorDB services with migrations configuration found");
|
|
@@ -4977,7 +6593,7 @@ async function sync(options) {
|
|
|
4977
6593
|
tailordbType
|
|
4978
6594
|
}))]);
|
|
4979
6595
|
} catch (error) {
|
|
4980
|
-
handleOptionalToRequiredError(error, ["The target snapshot marks a field as required, but existing remote records have no value for it.", "Populate those records first (e.g. with a migration script applied via 'tailor
|
|
6596
|
+
handleOptionalToRequiredError(error, ["The target snapshot marks a field as required, but existing remote records have no value for it.", "Populate those records first (e.g. with a migration script applied via 'tailor deploy'), then re-run the sync."]);
|
|
4981
6597
|
}
|
|
4982
6598
|
await Promise.all(desiredGqlPermissions.map(({ typeName, permission }) => {
|
|
4983
6599
|
const request = {
|
|
@@ -5008,13 +6624,13 @@ async function sync(options) {
|
|
|
5008
6624
|
logger.success(`Synced namespace ${styles.bold(target.namespace)} to migration ${styles.bold(formatMigrationNumber(targetVersion))}.`);
|
|
5009
6625
|
if (targetVersion < latest) {
|
|
5010
6626
|
logger.newline();
|
|
5011
|
-
logger.info(`Run 'tailor
|
|
6627
|
+
logger.info(`Run 'tailor deploy' to apply migrations ${formatMigrationNumber(targetVersion + 1)}–${formatMigrationNumber(latest)} from the working tree.`);
|
|
5012
6628
|
}
|
|
5013
6629
|
}
|
|
5014
6630
|
const syncCommand = defineAppCommand({
|
|
5015
6631
|
name: "sync",
|
|
5016
6632
|
description: "Sync remote TailorDB schema to a specific migration snapshot (recovery from --no-schema-check drift).",
|
|
5017
|
-
args: z.
|
|
6633
|
+
args: z.strictObject({
|
|
5018
6634
|
...deploymentArgs,
|
|
5019
6635
|
...confirmationArgs,
|
|
5020
6636
|
number: arg(z.string(), {
|
|
@@ -5025,7 +6641,7 @@ const syncCommand = defineAppCommand({
|
|
|
5025
6641
|
alias: "n",
|
|
5026
6642
|
description: "Target TailorDB namespace (required if multiple namespaces exist)"
|
|
5027
6643
|
})
|
|
5028
|
-
})
|
|
6644
|
+
}),
|
|
5029
6645
|
run: async (args) => {
|
|
5030
6646
|
await assertWritable({ profile: args.profile });
|
|
5031
6647
|
await sync({
|
|
@@ -5080,7 +6696,7 @@ const tailordbCommand = defineCommand({
|
|
|
5080
6696
|
const upgradeCommand = defineAppCommand({
|
|
5081
6697
|
name: "upgrade",
|
|
5082
6698
|
description: "Run codemods to upgrade your project to a newer SDK version.",
|
|
5083
|
-
args: z.
|
|
6699
|
+
args: z.strictObject({
|
|
5084
6700
|
from: arg(z.string(), { description: "SDK version before the upgrade (e.g., 1.33.0)" }),
|
|
5085
6701
|
"dry-run": arg(z.boolean().default(false), {
|
|
5086
6702
|
alias: "d",
|
|
@@ -5090,11 +6706,11 @@ const upgradeCommand = defineAppCommand({
|
|
|
5090
6706
|
description: "Project directory to upgrade",
|
|
5091
6707
|
completion: { type: "directory" }
|
|
5092
6708
|
})
|
|
5093
|
-
})
|
|
6709
|
+
}),
|
|
5094
6710
|
run: async (args) => {
|
|
5095
|
-
const { initTelemetry } = await import("../telemetry-
|
|
6711
|
+
const { initTelemetry } = await import("../telemetry-CkbkeJxl.mjs");
|
|
5096
6712
|
await initTelemetry();
|
|
5097
|
-
const { upgrade } = await import("../service-
|
|
6713
|
+
const { upgrade } = await import("../service-DeZeYa06.mjs");
|
|
5098
6714
|
await upgrade({
|
|
5099
6715
|
from: args.from,
|
|
5100
6716
|
dryRun: args["dry-run"],
|
|
@@ -5108,32 +6724,59 @@ const upgradeCommand = defineAppCommand({
|
|
|
5108
6724
|
const currentCommand = defineAppCommand({
|
|
5109
6725
|
name: "current",
|
|
5110
6726
|
description: "Show current user.",
|
|
5111
|
-
args: z.
|
|
6727
|
+
args: z.strictObject({}),
|
|
5112
6728
|
run: async () => {
|
|
5113
6729
|
const config = await readPlatformConfig();
|
|
6730
|
+
const profile = process.env.TAILOR_PLATFORM_PROFILE;
|
|
6731
|
+
const profileEntry = profile ? config.profiles[profile] : void 0;
|
|
6732
|
+
if (profile && !profileEntry) throw new Error(`Profile "${profile}" not found`);
|
|
6733
|
+
const platformConfig = profileEntry ? platformConfigFromProfile(profileEntry) : void 0;
|
|
6734
|
+
const currentUser = profile ? profileEntry?.user ?? null : config.current_user;
|
|
5114
6735
|
const jsonOutput = logger.jsonMode;
|
|
5115
|
-
if (!
|
|
6736
|
+
if (!currentUser) throw new Error(multiline`
|
|
5116
6737
|
Current user not set.
|
|
5117
|
-
Please login first using 'tailor
|
|
6738
|
+
Please login first using 'tailor login' command to register a user.
|
|
5118
6739
|
`);
|
|
5119
|
-
if (!config
|
|
5120
|
-
Current user '${
|
|
5121
|
-
Please login again using 'tailor
|
|
6740
|
+
if (!hasUserTokenEntry(config, currentUser, platformConfig)) throw new Error(multiline`
|
|
6741
|
+
Current user '${currentUser}' not found in registered users.
|
|
6742
|
+
Please login again using 'tailor login' command to register the user.
|
|
5122
6743
|
`);
|
|
5123
6744
|
if (jsonOutput) {
|
|
5124
|
-
logger.out({ user:
|
|
6745
|
+
logger.out({ user: currentUser });
|
|
5125
6746
|
return;
|
|
5126
6747
|
}
|
|
5127
|
-
logger.out(
|
|
6748
|
+
logger.out(currentUser);
|
|
5128
6749
|
}
|
|
5129
6750
|
});
|
|
5130
6751
|
|
|
5131
6752
|
//#endregion
|
|
5132
6753
|
//#region src/cli/commands/user/list.ts
|
|
6754
|
+
function activeCurrentUserKey(config) {
|
|
6755
|
+
const activeProfile = process.env.TAILOR_PLATFORM_PROFILE;
|
|
6756
|
+
if (!activeProfile) {
|
|
6757
|
+
if (!config.current_user) return null;
|
|
6758
|
+
return resolveUserTokenKey(config, config.current_user);
|
|
6759
|
+
}
|
|
6760
|
+
const profile = config.profiles[activeProfile];
|
|
6761
|
+
if (!profile) return null;
|
|
6762
|
+
return resolveUserTokenKey(config, profile.user, platformConfigFromProfile(profile));
|
|
6763
|
+
}
|
|
6764
|
+
function toUserListInfo(userKey, currentUserKey) {
|
|
6765
|
+
const separatorIndex = userKey.indexOf("|");
|
|
6766
|
+
const platformUrl = separatorIndex === -1 ? null : userKey.slice(0, separatorIndex);
|
|
6767
|
+
return {
|
|
6768
|
+
user: separatorIndex === -1 ? userKey : userKey.slice(separatorIndex + 1),
|
|
6769
|
+
platformUrl,
|
|
6770
|
+
current: currentUserKey === userKey
|
|
6771
|
+
};
|
|
6772
|
+
}
|
|
6773
|
+
function formatUserListInfo(info) {
|
|
6774
|
+
return `${info.user}${info.platformUrl ? ` [${info.platformUrl}]` : ""}${info.current ? " (current)" : ""}`;
|
|
6775
|
+
}
|
|
5133
6776
|
const listCommand$1 = defineAppCommand({
|
|
5134
6777
|
name: "list",
|
|
5135
6778
|
description: "List all users.",
|
|
5136
|
-
args: z.
|
|
6779
|
+
args: z.strictObject({}),
|
|
5137
6780
|
run: async () => {
|
|
5138
6781
|
const config = await readPlatformConfig();
|
|
5139
6782
|
const jsonOutput = logger.jsonMode;
|
|
@@ -5141,18 +6784,20 @@ const listCommand$1 = defineAppCommand({
|
|
|
5141
6784
|
if (users.length === 0) {
|
|
5142
6785
|
logger.info(multiline`
|
|
5143
6786
|
No users found.
|
|
5144
|
-
Please login first using 'tailor
|
|
6787
|
+
Please login first using 'tailor login' command to register a user.
|
|
5145
6788
|
`);
|
|
5146
6789
|
if (jsonOutput) logger.out([]);
|
|
5147
6790
|
return;
|
|
5148
6791
|
}
|
|
6792
|
+
const currentUserKey = activeCurrentUserKey(config);
|
|
6793
|
+
const userInfos = users.map((user) => toUserListInfo(user, currentUserKey));
|
|
5149
6794
|
if (jsonOutput) {
|
|
5150
|
-
logger.out(
|
|
6795
|
+
logger.out([...new Set(userInfos.map((userInfo) => userInfo.user))]);
|
|
5151
6796
|
return;
|
|
5152
6797
|
}
|
|
5153
|
-
|
|
5154
|
-
if (
|
|
5155
|
-
else logger.log(
|
|
6798
|
+
userInfos.forEach((userInfo) => {
|
|
6799
|
+
if (userInfo.current) logger.success(formatUserListInfo(userInfo), { mode: "plain" });
|
|
6800
|
+
else logger.log(formatUserListInfo(userInfo));
|
|
5156
6801
|
});
|
|
5157
6802
|
}
|
|
5158
6803
|
});
|
|
@@ -5213,12 +6858,31 @@ function printCreatedToken(name, token, write, action) {
|
|
|
5213
6858
|
`);
|
|
5214
6859
|
}
|
|
5215
6860
|
|
|
6861
|
+
//#endregion
|
|
6862
|
+
//#region src/cli/commands/user/pat/user.ts
|
|
6863
|
+
function resolvePatUser(config) {
|
|
6864
|
+
const activeProfile = process.env.TAILOR_PLATFORM_PROFILE;
|
|
6865
|
+
if (activeProfile) return config.profiles[activeProfile]?.user ?? null;
|
|
6866
|
+
return config.current_user;
|
|
6867
|
+
}
|
|
6868
|
+
async function createPatOperatorClient() {
|
|
6869
|
+
const config = await readPlatformConfig();
|
|
6870
|
+
const activeProfile = process.env.TAILOR_PLATFORM_PROFILE;
|
|
6871
|
+
const profileEntry = activeProfile ? config.profiles[activeProfile] : void 0;
|
|
6872
|
+
if (activeProfile && !profileEntry) throw new Error(`Profile "${activeProfile}" not found`);
|
|
6873
|
+
const platformConfig = profileEntry ? platformConfigFromProfile(profileEntry) : void 0;
|
|
6874
|
+
const user = resolvePatUser(config);
|
|
6875
|
+
if (!user) throw new Error("No user logged in.\nPlease login first using 'tailor login' command.");
|
|
6876
|
+
const { accessToken } = await fetchLatestToken(config, user, platformConfig);
|
|
6877
|
+
return await initOperatorClient(accessToken, platformConfig);
|
|
6878
|
+
}
|
|
6879
|
+
|
|
5216
6880
|
//#endregion
|
|
5217
6881
|
//#region src/cli/commands/user/pat/create.ts
|
|
5218
6882
|
const createCommand = defineAppCommand({
|
|
5219
6883
|
name: "create",
|
|
5220
6884
|
description: "Create a new personal access token.",
|
|
5221
|
-
args: z.
|
|
6885
|
+
args: z.strictObject({
|
|
5222
6886
|
name: arg(z.string(), {
|
|
5223
6887
|
positional: true,
|
|
5224
6888
|
description: "Token name"
|
|
@@ -5227,16 +6891,10 @@ const createCommand = defineAppCommand({
|
|
|
5227
6891
|
alias: "W",
|
|
5228
6892
|
description: "Grant write permission (default: read-only)"
|
|
5229
6893
|
})
|
|
5230
|
-
})
|
|
6894
|
+
}),
|
|
5231
6895
|
run: async (args) => {
|
|
5232
6896
|
await assertWritable();
|
|
5233
|
-
const
|
|
5234
|
-
if (!config.current_user) throw new Error(multiline`
|
|
5235
|
-
No user logged in.
|
|
5236
|
-
Please login first using 'tailor-sdk login' command.
|
|
5237
|
-
`);
|
|
5238
|
-
const { accessToken: token } = await fetchLatestToken(config, config.current_user);
|
|
5239
|
-
const client = await initOperatorClient(token);
|
|
6897
|
+
const client = await createPatOperatorClient();
|
|
5240
6898
|
const scopes = getScopesFromWriteFlag(args.write);
|
|
5241
6899
|
const result = await client.createPersonalAccessToken({
|
|
5242
6900
|
name: args.name,
|
|
@@ -5252,19 +6910,13 @@ const createCommand = defineAppCommand({
|
|
|
5252
6910
|
const deleteCommand = defineAppCommand({
|
|
5253
6911
|
name: "delete",
|
|
5254
6912
|
description: "Delete a personal access token.",
|
|
5255
|
-
args: z.
|
|
6913
|
+
args: z.strictObject({ name: arg(z.string(), {
|
|
5256
6914
|
positional: true,
|
|
5257
6915
|
description: "Token name"
|
|
5258
|
-
}) })
|
|
6916
|
+
}) }),
|
|
5259
6917
|
run: async (args) => {
|
|
5260
6918
|
await assertWritable();
|
|
5261
|
-
|
|
5262
|
-
if (!config.current_user) throw new Error(multiline`
|
|
5263
|
-
No user logged in.
|
|
5264
|
-
Please login first using 'tailor-sdk login' command.
|
|
5265
|
-
`);
|
|
5266
|
-
const { accessToken: token } = await fetchLatestToken(config, config.current_user);
|
|
5267
|
-
await (await initOperatorClient(token)).deletePersonalAccessToken({ name: args.name });
|
|
6919
|
+
await (await createPatOperatorClient()).deletePersonalAccessToken({ name: args.name });
|
|
5268
6920
|
logger.success(`Personal access token "${args.name}" deleted successfully.`);
|
|
5269
6921
|
}
|
|
5270
6922
|
});
|
|
@@ -5274,16 +6926,10 @@ const deleteCommand = defineAppCommand({
|
|
|
5274
6926
|
const listCommand = defineAppCommand({
|
|
5275
6927
|
name: "list",
|
|
5276
6928
|
description: "List all personal access tokens.",
|
|
5277
|
-
args: z.
|
|
6929
|
+
args: z.strictObject({ ...paginationArgs() }),
|
|
5278
6930
|
run: async (args) => {
|
|
5279
6931
|
const jsonOutput = logger.jsonMode;
|
|
5280
|
-
const
|
|
5281
|
-
if (!config.current_user) throw new Error(multiline`
|
|
5282
|
-
No user logged in.
|
|
5283
|
-
Please login first using 'tailor-sdk login' command.
|
|
5284
|
-
`);
|
|
5285
|
-
const { accessToken: token } = await fetchLatestToken(config, config.current_user);
|
|
5286
|
-
const client = await initOperatorClient(token);
|
|
6932
|
+
const client = await createPatOperatorClient();
|
|
5287
6933
|
const pageDirection = toPageDirection(args.order);
|
|
5288
6934
|
const pats = await fetchPaged(async (pageToken, pageSize) => {
|
|
5289
6935
|
const { personalAccessTokens, nextPageToken } = await client.listPersonalAccessTokens({
|
|
@@ -5296,7 +6942,7 @@ const listCommand = defineAppCommand({
|
|
|
5296
6942
|
if (pats.length === 0) {
|
|
5297
6943
|
logger.info(multiline`
|
|
5298
6944
|
No personal access tokens found.
|
|
5299
|
-
Please create a token using 'tailor
|
|
6945
|
+
Please create a token using 'tailor user pat create' command.
|
|
5300
6946
|
`);
|
|
5301
6947
|
if (!jsonOutput) return;
|
|
5302
6948
|
}
|
|
@@ -5319,7 +6965,7 @@ const listCommand = defineAppCommand({
|
|
|
5319
6965
|
const updateCommand = defineAppCommand({
|
|
5320
6966
|
name: "update",
|
|
5321
6967
|
description: "Update a personal access token (delete and recreate).",
|
|
5322
|
-
args: z.
|
|
6968
|
+
args: z.strictObject({
|
|
5323
6969
|
name: arg(z.string(), {
|
|
5324
6970
|
positional: true,
|
|
5325
6971
|
description: "Token name"
|
|
@@ -5328,16 +6974,10 @@ const updateCommand = defineAppCommand({
|
|
|
5328
6974
|
alias: "W",
|
|
5329
6975
|
description: "Grant write permission (if not specified, keeps read-only)"
|
|
5330
6976
|
})
|
|
5331
|
-
})
|
|
6977
|
+
}),
|
|
5332
6978
|
run: async (args) => {
|
|
5333
6979
|
await assertWritable();
|
|
5334
|
-
const
|
|
5335
|
-
if (!config.current_user) throw new Error(multiline`
|
|
5336
|
-
No user logged in.
|
|
5337
|
-
Please login first using 'tailor-sdk login' command.
|
|
5338
|
-
`);
|
|
5339
|
-
const { accessToken: token } = await fetchLatestToken(config, config.current_user);
|
|
5340
|
-
const client = await initOperatorClient(token);
|
|
6980
|
+
const client = await createPatOperatorClient();
|
|
5341
6981
|
await client.deletePersonalAccessToken({ name: args.name });
|
|
5342
6982
|
const scopes = getScopesFromWriteFlag(args.write);
|
|
5343
6983
|
const result = await client.createPersonalAccessToken({
|
|
@@ -5370,20 +7010,26 @@ const patCommand = defineCommand({
|
|
|
5370
7010
|
const switchCommand = defineAppCommand({
|
|
5371
7011
|
name: "switch",
|
|
5372
7012
|
description: "Set current user.",
|
|
5373
|
-
args: z.
|
|
7013
|
+
args: z.strictObject({ user: arg(z.string(), {
|
|
5374
7014
|
positional: true,
|
|
5375
7015
|
description: "User email address or machine user client ID"
|
|
5376
|
-
}) })
|
|
7016
|
+
}) }),
|
|
5377
7017
|
run: async (args) => {
|
|
5378
7018
|
const config = await readPlatformConfig();
|
|
5379
|
-
const
|
|
7019
|
+
const activeProfileName = process.env.TAILOR_PLATFORM_PROFILE;
|
|
7020
|
+
const activeProfileEntry = activeProfileName ? config.profiles[activeProfileName] : void 0;
|
|
7021
|
+
if (activeProfileName && !activeProfileEntry) throw new Error(`Profile "${activeProfileName}" not found`);
|
|
7022
|
+
const platformConfig = activeProfileEntry ? platformConfigFromProfile(activeProfileEntry) : void 0;
|
|
7023
|
+
if (args.user.includes("|")) throw new Error(`User "${args.user}" looks like a platform-scoped token key. Pass the user name without the platform URL and select the platform with TAILOR_PLATFORM_URL or a profile.`);
|
|
7024
|
+
const user = resolveConfigUser(config, args.user, platformConfig);
|
|
5380
7025
|
if (!user) throw new Error(multiline`
|
|
5381
7026
|
User "${args.user}" not found.
|
|
5382
|
-
Please login first using 'tailor
|
|
7027
|
+
Please login first using 'tailor login' command to register this user.
|
|
5383
7028
|
`);
|
|
5384
|
-
|
|
7029
|
+
if (activeProfileEntry) activeProfileEntry.user = user;
|
|
7030
|
+
else config.current_user = user;
|
|
5385
7031
|
writePlatformConfig(config);
|
|
5386
|
-
logger.success(`Current user set to "${
|
|
7032
|
+
logger.success(`Current user set to "${user}" successfully.`);
|
|
5387
7033
|
}
|
|
5388
7034
|
});
|
|
5389
7035
|
|
|
@@ -5409,7 +7055,7 @@ const workflowCommand = defineCommand({
|
|
|
5409
7055
|
name: "workflow",
|
|
5410
7056
|
description: "Manage workflows and workflow executions.",
|
|
5411
7057
|
subCommands: {
|
|
5412
|
-
list: listCommand$
|
|
7058
|
+
list: listCommand$13,
|
|
5413
7059
|
get: getCommand$6,
|
|
5414
7060
|
start: startCommand,
|
|
5415
7061
|
wait: waitCommand,
|
|
@@ -5417,7 +7063,7 @@ const workflowCommand = defineCommand({
|
|
|
5417
7063
|
resume: resumeCommand
|
|
5418
7064
|
},
|
|
5419
7065
|
async run() {
|
|
5420
|
-
await runCommand(listCommand$
|
|
7066
|
+
await runCommand(listCommand$13, []);
|
|
5421
7067
|
}
|
|
5422
7068
|
});
|
|
5423
7069
|
|
|
@@ -5428,10 +7074,10 @@ const appCommand = defineCommand({
|
|
|
5428
7074
|
description: "Manage workspace applications",
|
|
5429
7075
|
subCommands: {
|
|
5430
7076
|
health: healthCommand,
|
|
5431
|
-
list: listCommand$
|
|
7077
|
+
list: listCommand$14
|
|
5432
7078
|
},
|
|
5433
7079
|
async run() {
|
|
5434
|
-
await runCommand(listCommand$
|
|
7080
|
+
await runCommand(listCommand$14, []);
|
|
5435
7081
|
}
|
|
5436
7082
|
});
|
|
5437
7083
|
|
|
@@ -5442,12 +7088,12 @@ const userCommand = defineCommand({
|
|
|
5442
7088
|
description: "Manage workspace users",
|
|
5443
7089
|
subCommands: {
|
|
5444
7090
|
invite: inviteCommand,
|
|
5445
|
-
list: listCommand$
|
|
7091
|
+
list: listCommand$15,
|
|
5446
7092
|
remove: removeCommand,
|
|
5447
7093
|
update: updateCommand$4
|
|
5448
7094
|
},
|
|
5449
7095
|
async run() {
|
|
5450
|
-
await runCommand(listCommand$
|
|
7096
|
+
await runCommand(listCommand$15, []);
|
|
5451
7097
|
}
|
|
5452
7098
|
});
|
|
5453
7099
|
|
|
@@ -5459,31 +7105,82 @@ const workspaceCommand = defineCommand({
|
|
|
5459
7105
|
subCommands: {
|
|
5460
7106
|
app: appCommand,
|
|
5461
7107
|
create: createCommand$4,
|
|
5462
|
-
delete: deleteCommand$
|
|
7108
|
+
delete: deleteCommand$5,
|
|
5463
7109
|
get: getCommand$7,
|
|
5464
|
-
list: listCommand$
|
|
7110
|
+
list: listCommand$16,
|
|
5465
7111
|
restore: restoreCommand,
|
|
5466
7112
|
user: userCommand
|
|
5467
7113
|
},
|
|
5468
7114
|
async run() {
|
|
5469
|
-
await runCommand(listCommand$
|
|
7115
|
+
await runCommand(listCommand$16, []);
|
|
5470
7116
|
}
|
|
5471
7117
|
});
|
|
5472
7118
|
|
|
5473
7119
|
//#endregion
|
|
5474
7120
|
//#region src/cli/index.ts
|
|
5475
|
-
|
|
5476
|
-
const { register } = await import("tsx/esm/api");
|
|
5477
|
-
register();
|
|
5478
|
-
}
|
|
7121
|
+
await registerTsHook(new URL("./ts-hook.mjs", import.meta.url));
|
|
5479
7122
|
initCrashReporting();
|
|
5480
7123
|
const packageJson = await readPackageJson();
|
|
5481
|
-
const cliName = Object.keys(packageJson.bin ?? {})[0] || "tailor
|
|
5482
|
-
const
|
|
7124
|
+
const cliName = Object.keys(packageJson.bin ?? {})[0] || "tailor";
|
|
7125
|
+
const packageName = packageJson.name ?? "@tailor-platform/sdk";
|
|
7126
|
+
const bundledSkillsDir = resolve(dirname(await resolvePackageJSON(import.meta.url)), "agent-skills");
|
|
7127
|
+
function replaceCommandArgs(command, removals = []) {
|
|
7128
|
+
const args = command.args;
|
|
7129
|
+
if (!args?.shape) return command;
|
|
7130
|
+
const shape = { ...args.shape };
|
|
7131
|
+
for (const name of removals) delete shape[name];
|
|
7132
|
+
return {
|
|
7133
|
+
...command,
|
|
7134
|
+
args: z.strictObject({ ...shape })
|
|
7135
|
+
};
|
|
7136
|
+
}
|
|
7137
|
+
function removeCommandAliases(command) {
|
|
7138
|
+
const next = { ...command };
|
|
7139
|
+
delete next.aliases;
|
|
7140
|
+
return next;
|
|
7141
|
+
}
|
|
7142
|
+
function alignSkillCommand(command) {
|
|
7143
|
+
const subCommands = command.subCommands ?? {};
|
|
7144
|
+
const add = {
|
|
7145
|
+
...removeCommandAliases(replaceCommandArgs(subCommands.add, ["verbose"])),
|
|
7146
|
+
description: "Install Tailor SDK agent skills."
|
|
7147
|
+
};
|
|
7148
|
+
const list = {
|
|
7149
|
+
...replaceCommandArgs(subCommands.list, ["json"]),
|
|
7150
|
+
description: "List Tailor SDK agent skills."
|
|
7151
|
+
};
|
|
7152
|
+
const remove = {
|
|
7153
|
+
...removeCommandAliases(replaceCommandArgs(subCommands.remove)),
|
|
7154
|
+
description: "Remove installed Tailor SDK agent skills."
|
|
7155
|
+
};
|
|
7156
|
+
const sync = {
|
|
7157
|
+
...replaceCommandArgs(subCommands.sync, ["verbose"]),
|
|
7158
|
+
description: "Remove and reinstall Tailor SDK agent skills."
|
|
7159
|
+
};
|
|
7160
|
+
return {
|
|
7161
|
+
...command,
|
|
7162
|
+
description: "Manage Tailor SDK agent skills.",
|
|
7163
|
+
async run() {
|
|
7164
|
+
const result = await runCommand(add, []);
|
|
7165
|
+
if (!result.success) throw result.error;
|
|
7166
|
+
},
|
|
7167
|
+
subCommands: {
|
|
7168
|
+
...subCommands,
|
|
7169
|
+
add,
|
|
7170
|
+
list,
|
|
7171
|
+
remove,
|
|
7172
|
+
sync
|
|
7173
|
+
}
|
|
7174
|
+
};
|
|
7175
|
+
}
|
|
7176
|
+
const commandWithSkills = withSkillCommand(defineCommand({
|
|
5483
7177
|
name: cliName,
|
|
5484
7178
|
description: packageJson.description || "Tailor CLI for managing Tailor Platform SDK applications",
|
|
7179
|
+
notes: `CLI plugins (beta): an unknown subcommand is dispatched to an external plugin executable named \`${cliName}-<name>\` (found on your PATH or in node_modules/.bin), similar to \`gh\` extensions.
|
|
7180
|
+
Run \`${cliName} plugin list\` to see which plugins are installed and where they resolve from.`,
|
|
5485
7181
|
subCommands: {
|
|
5486
7182
|
api: apiCommand,
|
|
7183
|
+
auth: authCommand,
|
|
5487
7184
|
authconnection: authconnectionCommand,
|
|
5488
7185
|
crashreport: crashReportCommand,
|
|
5489
7186
|
deploy: deployCommand$1,
|
|
@@ -5497,13 +7194,13 @@ const mainCommand = withCompletionCommand(defineCommand({
|
|
|
5497
7194
|
oauth2client: oauth2clientCommand,
|
|
5498
7195
|
open: openCommand,
|
|
5499
7196
|
organization: organizationCommand,
|
|
7197
|
+
plugin: pluginCommand,
|
|
5500
7198
|
profile: profileCommand,
|
|
5501
7199
|
query: queryCommand,
|
|
5502
7200
|
remove: removeCommand$1,
|
|
5503
7201
|
secret: secretCommand,
|
|
5504
7202
|
setup: setupCommand,
|
|
5505
7203
|
show: showCommand,
|
|
5506
|
-
skills: skillsCommand,
|
|
5507
7204
|
staticwebsite: staticwebsiteCommand,
|
|
5508
7205
|
tailordb: tailordbCommand,
|
|
5509
7206
|
upgrade: upgradeCommand,
|
|
@@ -5511,11 +7208,31 @@ const mainCommand = withCompletionCommand(defineCommand({
|
|
|
5511
7208
|
workflow: workflowCommand,
|
|
5512
7209
|
workspace: workspaceCommand
|
|
5513
7210
|
}
|
|
5514
|
-
})
|
|
7211
|
+
}), {
|
|
7212
|
+
sourceDir: bundledSkillsDir,
|
|
7213
|
+
package: packageName,
|
|
7214
|
+
mode: "copy",
|
|
7215
|
+
descriptionAppend: false
|
|
7216
|
+
});
|
|
7217
|
+
const commandWithSkillsSubCommands = commandWithSkills.subCommands ?? {};
|
|
7218
|
+
const mainCommand = withCompletionCommand({
|
|
7219
|
+
...commandWithSkills,
|
|
7220
|
+
subCommands: {
|
|
7221
|
+
...commandWithSkillsSubCommands,
|
|
7222
|
+
skills: alignSkillCommand(commandWithSkillsSubCommands.skills)
|
|
7223
|
+
}
|
|
7224
|
+
});
|
|
5515
7225
|
runMain(mainCommand, {
|
|
5516
7226
|
version: packageJson.version,
|
|
5517
7227
|
globalArgs: z.object(commonArgs),
|
|
5518
7228
|
displayErrors: false,
|
|
7229
|
+
onUnknownSubcommand: ({ commandPath, name, args }) => dispatchPlugin({
|
|
7230
|
+
commandPath,
|
|
7231
|
+
name,
|
|
7232
|
+
args,
|
|
7233
|
+
cliName,
|
|
7234
|
+
profile: process.env.TAILOR_PLATFORM_PROFILE
|
|
7235
|
+
}),
|
|
5519
7236
|
cleanup: async ({ error }) => {
|
|
5520
7237
|
if (error) {
|
|
5521
7238
|
if (isCLIError(error)) {
|
|
@@ -5526,11 +7243,11 @@ runMain(mainCommand, {
|
|
|
5526
7243
|
if (isVerbose() && error.stack) logger.debug(`\nStack trace:\n${error.stack}`);
|
|
5527
7244
|
} else logger.error(`Unknown error: ${error}`);
|
|
5528
7245
|
if (!isCLIError(error) && (!(error instanceof Error) || error instanceof TypeError || error instanceof RangeError)) {
|
|
5529
|
-
const { reportCrash } = await import("../crashreport-
|
|
7246
|
+
const { reportCrash } = await import("../crashreport-D3RjuSYb.mjs");
|
|
5530
7247
|
await reportCrash(error, "handledError");
|
|
5531
7248
|
}
|
|
5532
7249
|
}
|
|
5533
|
-
const { shutdownTelemetry } = await import("../telemetry-
|
|
7250
|
+
const { shutdownTelemetry } = await import("../telemetry-CkbkeJxl.mjs");
|
|
5534
7251
|
await shutdownTelemetry();
|
|
5535
7252
|
}
|
|
5536
7253
|
});
|