@tailor-platform/sdk 2.23.0 → 2.25.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/CHANGELOG.md +89 -0
  2. package/dist/application-ChqHuhZW.mjs +1 -0
  3. package/dist/application-DS0XKBtK.mjs +200 -0
  4. package/dist/application-DS0XKBtK.mjs.map +1 -0
  5. package/dist/cli/cache/bundle-cache.d.mts +1 -0
  6. package/dist/cli/commands/deploy/deployment-target.d.mts +1 -0
  7. package/dist/cli/commands/machineuser/list.d.mts +1 -0
  8. package/dist/cli/commands/show.d.mts +13 -1
  9. package/dist/cli/lib.d.mts +2 -2
  10. package/dist/cli/lib.mjs +1 -1
  11. package/dist/cli/lib.mjs.map +1 -1
  12. package/dist/cli/main.mjs +43 -43
  13. package/dist/cli/main.mjs.map +1 -1
  14. package/dist/cli/services/application.d.mts +1 -0
  15. package/dist/cli/services/workflow/bundler.d.mts +2 -1
  16. package/dist/cli/shared/forbidden-runtime-globals.d.mts +1 -0
  17. package/dist/cli/shared/start-context.d.mts +1 -0
  18. package/dist/cli/ts-hook.mjs +3 -3
  19. package/dist/completion/zsh-worker.zsh +3 -3
  20. package/dist/configure/config/types.d.mts +42 -2
  21. package/dist/configure/index.d.mts +2 -2
  22. package/dist/configure/index.mjs +1 -1
  23. package/dist/configure/index.mjs.map +1 -1
  24. package/dist/plugin/index.mjs +1 -1
  25. package/dist/plugin/index.mjs.map +1 -1
  26. package/dist/plugin/types.d.mts +97 -0
  27. package/dist/register-ts-hook-DPAW0Z4M.mjs +924 -0
  28. package/dist/register-ts-hook-DPAW0Z4M.mjs.map +1 -0
  29. package/dist/vitest/mocks/file.d.mts +1 -1
  30. package/docs/cli/application.md +23 -0
  31. package/docs/cli/secret.md +24 -16
  32. package/docs/cli-reference.md +25 -13
  33. package/docs/configuration.md +28 -3
  34. package/docs/github-actions.md +236 -56
  35. package/docs/migration/v3.md +46 -0
  36. package/docs/multi-environment.md +3 -1
  37. package/docs/plugin/custom.md +76 -1
  38. package/docs/plugin/frontend.md +124 -0
  39. package/docs/plugin/index.md +24 -2
  40. package/docs/services/auth.md +2 -0
  41. package/docs/services/secret.md +5 -4
  42. package/docs/services/staticwebsite.md +2 -0
  43. package/docs/services/tailordb-migration.md +1 -1
  44. package/docs/services/workflow.md +3 -0
  45. package/package.json +8 -8
  46. package/dist/application-BtZ8hmx9.mjs +0 -1
  47. package/dist/application-m2G91kKI.mjs +0 -199
  48. package/dist/application-m2G91kKI.mjs.map +0 -1
  49. package/dist/register-ts-hook-ztnEFW6n.mjs +0 -922
  50. package/dist/register-ts-hook-ztnEFW6n.mjs.map +0 -1
@@ -33,9 +33,11 @@ tailor setup ci tag --name my-app-prod \
33
33
  --branch main --environment production
34
34
  ```
35
35
 
36
- After running the command, follow the **Next steps** printed to the terminal to
37
- set the required secrets, set the `TAILOR_PLATFORM_WORKSPACE_ID` variable, and
38
- commit the generated files.
36
+ After running the command, follow the **Next steps** printed to the terminal:
37
+ run `tailor setup ci env` to get the commands that set the secrets and
38
+ variables each GitHub Environment needs (see
39
+ [Setting secrets and variables](#setting-secrets-and-variables)), then commit
40
+ the generated files.
39
41
 
40
42
  The generated workflow deploys to whichever workspace its
41
43
  `TAILOR_PLATFORM_WORKSPACE_ID` Environment variable points at — it never
@@ -170,10 +172,13 @@ Because the variable is scoped to a GitHub Environment, both the `plan` and
170
172
  ```
171
173
 
172
174
  2. Set the id as the Environment variable (the environment name is your
173
- `--environment` value, or the workspace name when omitted):
175
+ `--environment` value, or the workspace name when omitted).
176
+ `tailor setup ci env` prints this command together with the other secrets
177
+ and variables each environment needs (see
178
+ [Setting secrets and variables](#setting-secrets-and-variables)):
174
179
 
175
180
  ```bash
176
- gh variable set TAILOR_PLATFORM_WORKSPACE_ID --env my-app-stg
181
+ gh variable set TAILOR_PLATFORM_WORKSPACE_ID --env=my-app-stg
177
182
  ```
178
183
 
179
184
  If `TAILOR_PLATFORM_WORKSPACE_ID` is unset, `deploy` fails because the target
@@ -194,6 +199,14 @@ Running a workflow setup subcommand creates or updates:
194
199
  The workflow file. The `name:` field is set to `Tailor (<workspace-name>)` so
195
200
  you can distinguish multiple workspaces in the Actions UI.
196
201
 
202
+ Every checkout in the workflow sets `persist-credentials: false`, so the GitHub
203
+ token is not left in `.git/config` while the job installs and runs your
204
+ project's code. The tag guard step passes the job token only to its
205
+ `git fetch`, and the plan step passes it to its `git fetch` and to the step that
206
+ posts the plan comment on the pull request; neither writes it to `.git/config`.
207
+ If installing your dependencies fetches git repositories
208
+ that require authentication, configure the credentials in your own step.
209
+
197
210
  See [Customizing the generated workflow](#customizing-the-generated-workflow)
198
211
  for what you can edit.
199
212
 
@@ -204,39 +217,83 @@ top-level keys it writes (`name:`, `on:`, and `permissions:` in a workflow; the
204
217
  metadata, inputs, and outputs of a composite action). Do not edit or rename
205
218
  them. Everything else is yours, and re-running `setup` keeps it:
206
219
 
207
- - **Your own jobs and steps.** Add them anywhere, without the `tailor-` prefix.
208
- A step you add inside a managed job stays right after the managed step it
220
+ - **Your own jobs and steps.** Add them anywhere, with an `id` (if any) that
221
+ does not start with `tailor-`: the prefix is reserved for the SDK, even in a
222
+ job of your own. A step you add inside a managed job stays right after the managed step it
209
223
  followed. For example, add private registry authentication or a system
210
224
  dependency _before_ the managed `tailor-setup` step, and post-install extras
211
225
  (such as `playwright install`) _after_ it. A job of your own can depend on a
212
226
  managed job with `needs: tailor-deploy`.
213
227
  - **Your own top-level keys**, such as a workflow-level `env:` or `defaults:`.
214
228
  - **Runtime settings of managed jobs:** `runs-on`, `timeout-minutes`,
215
- `container`, and `env`.
229
+ `container`, and `env`. On a managed job generated without an
230
+ `environment:` (such as `tailor-tag-guard` or `tailor-erd-preview`), you can
231
+ also add one, for example so a step you added there can read that GitHub
232
+ Environment's secrets. The `environment:` of `tailor-plan`, `tailor-deploy`,
233
+ and the preview jobs comes from `--environment`, so change it with that
234
+ option instead.
216
235
  - **These inputs of managed steps:** `ignore` on `tailor-generate-check` and
217
236
  `tailor-drift-check`, `fail-on-drift` on `tailor-drift-check`,
218
237
  `install-command` on `tailor-install`, `node-version-file` on
219
238
  `tailor-setup`, `label` on `tailor-plan`, and `user-mapping` on
220
239
  `tailor-notify` and on a coordinator's steps that call an app action.
221
- - **The `run:` command of the `build-site` step** in a composite action.
240
+ - **The `run:` command of the `tailor-build-site` step** in a composite action.
241
+
242
+ In a preview workflow, the `tailor-preview-deploy` job exposes the per-PR
243
+ workspace as the outputs `workspace-id`, `workspace-name`, and `app-url`, so a
244
+ job of your own can run tests or deploy extra assets against it:
245
+
246
+ ```yaml
247
+ deploy-assets:
248
+ needs: tailor-preview-deploy
249
+ runs-on: ubuntu-latest
250
+ environment: my-app
251
+ env:
252
+ TAILOR_PLATFORM_WORKSPACE_ID: ${{ needs.tailor-preview-deploy.outputs.workspace-id }}
253
+ TAILOR_PLATFORM_MACHINE_USER_CLIENT_ID: ${{ secrets.TAILOR_PLATFORM_MACHINE_USER_CLIENT_ID }}
254
+ TAILOR_PLATFORM_MACHINE_USER_CLIENT_SECRET: ${{ secrets.TAILOR_PLATFORM_MACHINE_USER_CLIENT_SECRET }}
255
+ steps:
256
+ # checkout, dependency installation, and your tailor commands
257
+ ```
258
+
259
+ A job that runs `tailor` against the workspace needs the machine-user
260
+ [secrets](#secrets), so it declares the preview target's GitHub Environment
261
+ (the `--environment` value, or the workspace name when omitted) as above. A job
262
+ that only uses `app-url`, such as end-to-end tests, does not.
263
+
264
+ The job is skipped whenever `tailor-preview-deploy` is skipped: when a pull
265
+ request is closed, for draft and fork pull requests, and for unlabeled ones with
266
+ `--require-preview-label`.
267
+
268
+ Likewise, the `tailor-deploy` job of a branch or tag workflow exposes the
269
+ deployed workspace as the outputs `workspace-id` and `app-url` to a job with
270
+ `needs: tailor-deploy`. Reading them does not require the target's GitHub
271
+ Environment; a job that runs `tailor` against the workspace declares it for the
272
+ machine-user secrets, as in the preview example above.
222
273
 
223
274
  Comments above your own jobs and steps and at the end of the file are kept too.
224
275
  Comments inside managed jobs and steps, and edits to the header comment, are
225
276
  not kept.
226
277
 
227
278
  `setup check` reports an edit to a managed part, and re-running `setup` stops
228
- on it. Revert the edit, or pass `--force` to reset the managed parts to the
229
- current template; `--force` still keeps your own jobs, steps, and settings.
230
- A managed job or step you renamed counts as your own, so `--force` adds the
231
- managed one back next to it; rename it back instead of forcing. To start over
232
- from a clean template, delete the file and re-run `setup`.
279
+ on it; both name the edited job, step, or top-level key. Revert the edit, or
280
+ pass `--force` to reset the managed parts to the current template; `--force`
281
+ still keeps your own jobs, steps, and settings. A managed step you renamed to
282
+ an id without the `tailor-` prefix counts as your own, so `--force` adds the
283
+ managed one back next to it; rename it back instead of forcing. A managed job
284
+ you renamed still holds the SDK's `tailor-` steps, so `setup check` reports
285
+ them as reserved ids and `setup` stops on them, even with `--force`; rename the
286
+ job back. To start over from a clean template, delete the file and re-run
287
+ `setup`.
233
288
 
234
289
  When a template update removes a managed job that contains steps of yours, or
235
290
  a job of yours `needs` a removed job, `setup` stops and names them. Move those
236
291
  steps into a job of your own (or update the `needs`) and re-run. `--force` drops
237
- steps left inside a removed job. It also stops when a new template version adds
238
- a managed job or step whose id you already use; rename yours, or pass `--force`
239
- to let the managed one replace it.
292
+ steps left inside a removed job.
293
+
294
+ `setup check` reports a job or step of yours whose `id` starts with `tailor-`,
295
+ and re-running `setup` stops on it, naming the id. Rename it; `--force` does
296
+ not rename or remove it.
240
297
 
241
298
  Files generated by an older SDK version are compared as a whole, so the first
242
299
  re-run after upgrading stops if you edited the file in any way. Re-run once with
@@ -296,7 +353,8 @@ the move.
296
353
 
297
354
  ## Secrets
298
355
 
299
- The generated workflow reads two secrets:
356
+ The generated workflow requires two secrets (the optional Slack token is listed in
357
+ [Setting secrets and variables](#setting-secrets-and-variables)):
300
358
 
301
359
  | Secret | Description |
302
360
  | -------------------------------------------- | -------------------------- |
@@ -304,12 +362,8 @@ The generated workflow reads two secrets:
304
362
  | `TAILOR_PLATFORM_MACHINE_USER_CLIENT_SECRET` | Machine user client secret |
305
363
 
306
364
  Set them on the target GitHub Environment (the `--environment` value, or the
307
- workspace name when omitted) with the GitHub CLI:
308
-
309
- ```bash
310
- gh secret set TAILOR_PLATFORM_MACHINE_USER_CLIENT_ID --env my-app-stg
311
- gh secret set TAILOR_PLATFORM_MACHINE_USER_CLIENT_SECRET --env my-app-stg
312
- ```
365
+ workspace name when omitted); `tailor setup ci env` prints the commands (see
366
+ [Setting secrets and variables](#setting-secrets-and-variables)).
313
367
 
314
368
  Setting them at the environment level isolates each target's credentials and
315
369
  keeps them alongside that environment's `TAILOR_PLATFORM_WORKSPACE_ID`
@@ -317,6 +371,62 @@ variable. You can also set them as repository-level secrets if every target
317
371
  shares one machine user, but then any workflow on any branch can read them, so
318
372
  the environment's protection rules no longer guard your deploys.
319
373
 
374
+ ### Setting secrets and variables
375
+
376
+ `tailor setup ci env` reads `.github/tailor.lock` and prints, for every GitHub
377
+ Environment the generated workflows use, the commands that create the
378
+ environment (only when it does not exist yet) and set its secrets and
379
+ variables. It is read-only, so re-run it whenever you add a target. When the
380
+ `origin` remote points at github.com, the output names that repository, so the
381
+ commands work from any directory; otherwise `gh` resolves the repository from
382
+ the current directory and the Terraform output leaves it as a placeholder.
383
+
384
+ ```bash
385
+ tailor setup ci env # gh CLI commands (default)
386
+ tailor setup ci env --format terraform # Terraform for the integrations/github provider
387
+ tailor setup ci env --environment my-app-stg # only one environment (repeat for several)
388
+ ```
389
+
390
+ The list follows what each generated workflow actually reads:
391
+
392
+ | Name | Kind | Targets | Required | Where the value comes from |
393
+ | -------------------------------------------- | -------- | ----------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
394
+ | `TAILOR_PLATFORM_MACHINE_USER_CLIENT_ID` | secret | all | yes | Client ID of the platform machine user CI signs in as; it needs an editor or admin role on the organization or folder that holds the workspace. Contact [Tailor support](https://docs.tailor.tech/administration/support) to get one |
395
+ | `TAILOR_PLATFORM_MACHINE_USER_CLIENT_SECRET` | secret | all | yes | Client secret of the same platform machine user |
396
+ | `TAILOR_PLATFORM_WORKSPACE_ID` | variable | branch, tag, coordinate | yes | `id` printed by `tailor workspace create`, or listed by `tailor workspace list` |
397
+ | `TAILOR_PLATFORM_ORGANIZATION_ID` | variable | preview | yes | Organization to create the per-PR workspaces in (a machine user cannot create a workspace without one): `organizationId` listed by `tailor organization list` |
398
+ | `TAILOR_PLATFORM_FOLDER_ID` | variable | preview | no | Folder to create the per-PR workspaces in: `id` listed by `tailor organization folder list -o <organization id>`. When unset they go directly under the organization, which needs the machine user's role on the organization itself |
399
+ | `TAILOR_PLATFORM_FAIL_ON_DRIFT` | variable | all | no | `true` to fail the drift check when it finds drift |
400
+ | `TAILOR_SLACK_BOT_TOKEN` | secret | branch, tag, coordinate | no | Bot User OAuth Token (`xoxb-...`) of a Slack app with the `chat:write` scope |
401
+ | `TAILOR_SLACK_CHANNEL_ID` | variable | branch, tag, coordinate | no | Channel ID (`C...`) from the channel details in Slack; invite the bot to the channel |
402
+ | `TAILOR_SLACK_USER_MAPPING` | variable | branch, tag | no | JSON object mapping GitHub usernames to Slack member IDs (for example `{"alice":"U0123456"}`) so notifications mention the actor; read only after you uncomment the `user-mapping` input of the `tailor-notify` step |
403
+
404
+ See [Account management](https://docs.tailor.tech/administration/account-management)
405
+ for how organizations, folders, workspaces, and machine users relate.
406
+
407
+ Composite actions (`setup ci action`) read nothing themselves; the coordinator
408
+ that calls them does. Set `TAILOR_SLACK_BOT_TOKEN` and `TAILOR_SLACK_CHANNEL_ID`
409
+ together to enable Slack deploy notifications.
410
+
411
+ The `gh` output creates an environment only when GitHub reports it missing and
412
+ leaves optional entries commented out. Run the commands one at a time: each
413
+ `gh secret set` / `gh variable set` prompts for its value.
414
+
415
+ The Terraform output takes every value from an input variable (secrets are
416
+ `sensitive`) and creates an optional entry only when its variable is set, so no
417
+ value is written to the output. Terraform still stores the secret values in its
418
+ state in plain text, so keep the state encrypted and access-restricted, or set
419
+ the secrets with the `gh` output instead. Its header lists the steps with the variable
420
+ names for your environments: authenticate the provider, put non-secret values in
421
+ `terraform.tfvars`, pass secrets as `TF_VAR_<name>` environment variables, and
422
+ import each environment and variable that already exists (for example
423
+ `terraform import github_repository_environment.production my-repo:production`)
424
+ before `terraform apply`: creating a variable that already exists fails, while
425
+ secrets are overwritten. The generated environments ignore changes to their
426
+ protection settings (reviewers, wait timer, branch policy), so importing an
427
+ environment keeps the approval gate you configured; remove the `lifecycle` block
428
+ to manage those settings in Terraform instead.
429
+
320
430
  ## GitHub Environments (approval gate)
321
431
 
322
432
  Both the `plan` and `deploy` jobs are associated with a GitHub Environment — the
@@ -382,9 +492,61 @@ For a monorepo where your SDK app lives in a subdirectory, pass `--dir`:
382
492
  tailor setup ci branch --name my-app --dir apps/backend
383
493
  ```
384
494
 
385
- The generated workflow adds a `paths` filter on `apps/backend/**` so the
386
- workflow only runs when that subdirectory changes. The `working-directory` for
387
- SDK commands is set accordingly.
495
+ The `working-directory` for SDK commands is set accordingly, and the workflow
496
+ only plans and deploys when that subdirectory changes. The workflow itself
497
+ starts on every pull request and push: a `tailor-changes` job checks whether
498
+ the change touches `apps/backend/**`, and the plan, deploy, and ERD preview jobs
499
+ are skipped when it does not. A skipped job reports success, so you can make
500
+ these checks required in branch protection; a workflow that a `paths` trigger
501
+ filter never started would leave them pending instead. If the `tailor-changes` job
502
+ itself fails (for example, on a GitHub API error), the plan, deploy, and ERD
503
+ preview jobs fail too instead of being skipped, so a required check blocks
504
+ merging and nothing is deployed. Re-run the failed jobs once the cause is gone.
505
+
506
+ ### Deploying several apps together
507
+
508
+ To deploy several apps to the same workspace from one workflow, repeat `--dir`
509
+ and pass `--name` for the workflow:
510
+
511
+ ```bash
512
+ tailor setup ci branch --name erp --dir apps/erp/backend --dir apps/users/backend
513
+ ```
514
+
515
+ `setup ci branch`, `setup ci tag`, and `setup ci preview` accept repeated
516
+ `--dir`. The workflow plans and deploys every app's `tailor.config.ts` in one
517
+ [multi-config deploy](./cli/application.md#deploy) from the repository root, so
518
+ an app can reference resources of another app with `external: true`. Add
519
+ `@tailor-platform/sdk` to the root `package.json` so the `tailor` CLI resolves
520
+ there; setup stops until it is declared. The generate check runs for each app
521
+ directory, as do seed validation and the migration drift check on branch and tag
522
+ workflows, and a change under any app directory runs the workflow's jobs.
523
+
524
+ With `--erd-preview`, each TailorDB namespace is previewed from the app that
525
+ owns it. A namespace may be owned by only one app; the others reference it with
526
+ `external: true`.
527
+
528
+ With more than one app, the preview comment does not link an application URL.
529
+
530
+ ### Running on changes outside the app directories
531
+
532
+ When the apps depend on code outside their directories, such as a frontend or
533
+ shared packages, add those paths with `--paths` on `setup ci branch` or
534
+ `setup ci preview`. Repeat it for each pattern:
535
+
536
+ ```bash
537
+ tailor setup ci preview --name erp --region asia-northeast \
538
+ --dir apps/erp/backend --dir apps/users/backend \
539
+ --paths "apps/*/frontend/**" --paths "modules/**" --paths pnpm-lock.yaml
540
+ ```
541
+
542
+ The patterns are checked after the app directories, in order, and the last
543
+ pattern that matches a changed file decides whether it counts. `*` matches
544
+ within one path segment, `**` matches any number of segments, and a pattern
545
+ starting with `!` excludes matching paths, so it can also exclude files inside
546
+ an app directory, for example `--paths '!apps/erp/backend/**/*.md'`. Other glob
547
+ characters (`?`, `+`, `[ ]`, `{ }`, `( )`, and `\`) are not supported, and
548
+ `setup ci` rejects a pattern that contains them. An app at the repository root already
549
+ runs on every change, so `--paths` is not accepted with `--dir .`.
388
550
 
389
551
  ## Rollback
390
552
 
@@ -453,18 +615,13 @@ tailor setup ci tag --name my-app-prod \
453
615
  --branch main --environment production
454
616
  ```
455
617
 
456
- Then provision each workspace and set its id on the matching environment (the
457
- staging target's environment defaults to `my-app-stg`; production uses
458
- `production`):
618
+ Then provision each workspace and set its id and the machine-user credentials
619
+ on the matching environment (the staging target's environment defaults to
620
+ `my-app-stg`; production uses `production`). `tailor setup ci env` prints the
621
+ commands for both environments:
459
622
 
460
623
  ```bash
461
- gh variable set TAILOR_PLATFORM_WORKSPACE_ID --env my-app-stg
462
- gh secret set TAILOR_PLATFORM_MACHINE_USER_CLIENT_ID --env my-app-stg
463
- gh secret set TAILOR_PLATFORM_MACHINE_USER_CLIENT_SECRET --env my-app-stg
464
-
465
- gh variable set TAILOR_PLATFORM_WORKSPACE_ID --env production
466
- gh secret set TAILOR_PLATFORM_MACHINE_USER_CLIENT_ID --env production
467
- gh secret set TAILOR_PLATFORM_MACHINE_USER_CLIENT_SECRET --env production
624
+ tailor setup ci env
468
625
  ```
469
626
 
470
627
  Commit both workflow files and `.github/tailor.lock`.
@@ -502,19 +659,21 @@ them. To remove it, delete the file.
502
659
 
503
660
  Renovate updates the SDK dependency and action pins, but it does not regenerate
504
661
  the workflow template. After an SDK update, `tailor setup check` reports a
505
- template-version warning until you re-run the relevant workflow setup
506
- subcommand.
662
+ template-version warning until you run
663
+ [`tailor setup update`](#updating-the-generated-workflow).
507
664
 
508
665
  ## Checking for drift
509
666
 
510
667
  `tailor setup check` audits the workflows recorded in
511
668
  `.github/tailor.lock` against your current config and repository, without
512
669
  writing anything. It reports when a workflow file is missing or its SDK-managed
513
- parts were edited by hand, a
514
- newer template is available, `tailor.config.ts` is no longer under the recorded
670
+ parts were edited by hand, a job or step of yours uses the reserved `tailor-`
671
+ prefix, a newer template is available, `tailor.config.ts` is no longer under the recorded
515
672
  `--dir`, or the repository default branch no longer matches a branch target's
516
673
  trigger. It exits non-zero when it finds drift, so you can run it in CI. Each
517
- finding names a stable rule key for future suppression.
674
+ finding names a stable rule key for future suppression. Run
675
+ [`tailor setup update`](#updating-the-generated-workflow) to regenerate the
676
+ targets it reports.
518
677
 
519
678
  Workflows generated by `setup ci branch`, `setup ci tag`, `setup ci preview`, and
520
679
  `setup ci coordinate` self-audit: each contains a `tailor-drift-check` step that
@@ -529,21 +688,42 @@ Drift findings are advisory by default. Set the repository variable
529
688
  `TAILOR_PLATFORM_FAIL_ON_DRIFT` to `true` to make unsuppressed findings fail
530
689
  the job. Execution and configuration errors fail regardless of this variable.
531
690
 
532
- Running `check` on your own machine also verifies that
533
- `TAILOR_PLATFORM_WORKSPACE_ID` is set locally for any branch, tag, or
534
- coordinate target, since those workflows read it directly. `check` detects on
535
- its own when it is running in CI (no flag needed) and skips that local-only
536
- verification there, since the deploy job resolves the Environment variable
537
- itself at runtime.
691
+ `check` compares only the generated files, `.github/tailor.lock`, and the
692
+ config; it does not read the GitHub Environment secrets and variables, so it
693
+ runs the same on your own machine and in CI. Run `tailor setup ci env` to list
694
+ what each environment needs.
538
695
 
539
696
  ## Updating the generated workflow
540
697
 
541
- When you upgrade the SDK, re-run the relevant workflow setup subcommand with
542
- the same flags to pick up template improvements. Your own jobs, steps, and
543
- settings are kept (see
544
- [Customizing the generated workflow](#customizing-the-generated-workflow)). If
545
- you edited a managed part, the command stops; revert the edit or pass `--force`
546
- to reset it.
698
+ When you upgrade the SDK, run `tailor setup update` from the repository root to
699
+ pick up template improvements:
700
+
701
+ ```bash
702
+ tailor setup update
703
+ ```
547
704
 
548
- The `.github/tailor.lock` file records the flags used at generation time,
549
- so you can check what arguments were used previously.
705
+ It regenerates every workflow and composite action recorded in
706
+ `.github/tailor.lock` with the flags each one was generated with, so you do not
707
+ have to re-type `setup ci branch`, `setup ci tag`, and the rest one by one.
708
+ Your own jobs, steps, and settings are kept (see
709
+ [Customizing the generated workflow](#customizing-the-generated-workflow)). A
710
+ branch that was detected from the repository default branch is detected again,
711
+ and the parts that follow your config — the migration drift check, seed
712
+ validation, static website builds, and ERD preview namespaces — are derived
713
+ from the current `tailor.config.ts`.
714
+
715
+ A target that cannot be regenerated, for example because you edited a managed
716
+ part, does not stop the others: `update` regenerates the rest, then lists the
717
+ targets it could not update and exits non-zero. Revert the edit, or run
718
+ `tailor setup update --force` to reset the managed parts of every target.
719
+
720
+ Coordinators generated by an older plugin version did not record how their
721
+ `--action` values were grouped, so `update` lists them instead of guessing
722
+ which apps deploy together. Re-run `tailor setup ci coordinate` once with all
723
+ of its original flags and its original `--action` groups. The message `update`
724
+ prints fills in the recorded flags (`--tag`, `--branch`, `--environment`,
725
+ `--restrict-dispatch`), so you only add the `--action` values. Later updates
726
+ pick the grouping up from the lock.
727
+
728
+ To change a flag for one target, re-run its `setup ci` subcommand with the new
729
+ flags. `.github/tailor.lock` records them, and later updates reuse them.
@@ -128,6 +128,52 @@ property, which is the relation's cardinality (e.g. "n-1", "1-1",
128
128
 
129
129
  </details>
130
130
 
131
+ ## defineConfig inlineSourcemap / logLevel → buildOptions
132
+
133
+ **Migration:** Partially automatic
134
+
135
+ Move the top-level `inlineSourcemap` and `logLevel` of `defineConfig()` into `buildOptions`, which groups the settings that control how functions are bundled. The top-level fields keep working until they are removed in v3; setting the same option in both places is rejected.
136
+
137
+ Before:
138
+
139
+ ```ts
140
+ export default defineConfig({
141
+ name: "my-app",
142
+ inlineSourcemap: false,
143
+ logLevel: "WARN",
144
+ });
145
+ ```
146
+
147
+ After:
148
+
149
+ ```ts
150
+ export default defineConfig({
151
+ name: "my-app",
152
+ buildOptions: {
153
+ inlineSourcemap: false,
154
+ logLevel: "WARN",
155
+ },
156
+ });
157
+ ```
158
+
159
+ <details>
160
+ <summary>Prompt for an AI agent (to finish the cases the codemod could not migrate)</summary>
161
+
162
+ ```text
163
+ In Tailor SDK v3, the top-level `inlineSourcemap` and `logLevel` options of
164
+ `defineConfig()` from `@tailor-platform/sdk` are removed in favor of
165
+ `buildOptions.inlineSourcemap` and `buildOptions.logLevel`. For each flagged
166
+ config, move those two properties into a `buildOptions` object (create it if
167
+ it does not exist) without changing their values. When the config is built
168
+ from a variable or a spread, move them in the object that actually defines
169
+ them. If an option is set both at the top level and in `buildOptions`, keep
170
+ the value that the deployed app should use and delete the other; the build
171
+ rejects a config that sets both. Leave `logLevel` options of other tools
172
+ (for example a Vite or Vitest config) unchanged.
173
+ ```
174
+
175
+ </details>
176
+
131
177
  ## String file uploads → explicit encoding
132
178
 
133
179
  **Migration:** Manual
@@ -64,7 +64,9 @@ tailor deploy -w <production-workspace-id> --env-file .env.production
64
64
  ```typescript
65
65
  export default defineConfig({
66
66
  name: "my-app",
67
- logLevel: process.env.TAILOR_APP_LOG_LEVEL ?? "DEBUG",
67
+ buildOptions: {
68
+ logLevel: process.env.TAILOR_APP_LOG_LEVEL ?? "DEBUG",
69
+ },
68
70
  });
69
71
  ```
70
72
 
@@ -6,7 +6,7 @@ Create your own plugins by implementing the `Plugin` interface.
6
6
 
7
7
  ## Requirements
8
8
 
9
- **Plugins must use default export**:
9
+ **Plugins with definition-time hooks must use default export**:
10
10
 
11
11
  ```typescript
12
12
  // plugin.ts
@@ -48,6 +48,9 @@ interface Plugin<TableConfig = unknown, PluginConfig = unknown> {
48
48
  onExecutorReady?(
49
49
  context: ExecutorReadyContext<PluginConfig>,
50
50
  ): GeneratorResult | Promise<GeneratorResult>;
51
+ onDeployed?(
52
+ context: DeployedContext<PluginConfig>,
53
+ ): void | DeployedHookResult | Promise<void | DeployedHookResult>;
51
54
  }
52
55
  ```
53
56
 
@@ -254,6 +257,78 @@ onExecutorReady(ctx) {
254
257
  },
255
258
  ```
256
259
 
260
+ ### onDeployed
261
+
262
+ Runs once after every application in the deploy has been applied, including when
263
+ there are no resource changes. Hooks run sequentially in config order, then in
264
+ `definePlugins()` registration order. No `importPath` or table attachment is required.
265
+
266
+ Use `ctx.application` for the registering application's ID, URL, domain, AI
267
+ Gateway URLs, static websites, and public OAuth client IDs. `ctx.application.id`
268
+ is the `id` from `defineConfig()` and is `undefined` when the config has none.
269
+ `ctx.applications` contains all applications in a multi-config deploy with the
270
+ same fields. OAuth client secrets are never included.
271
+
272
+ `ctx.configPath` is the absolute path of the registering config. `ctx.workspaceId`
273
+ and `ctx.pluginConfig` identify the deployment workspace and the plugin's options.
274
+ Use `ctx.logger.info`, `warn`, and `success` for diagnostics; they write to stderr
275
+ and preserve deploy's JSON stdout.
276
+
277
+ ```typescript
278
+ import { definePlugins, type Plugin } from "@tailor-platform/sdk";
279
+
280
+ const deployedInfo: Plugin = {
281
+ id: "@example/deployed-info",
282
+ description: "Reports the deployed application URL",
283
+ onDeployed(ctx) {
284
+ const url = ctx.application.url;
285
+ if (!url) return;
286
+ ctx.logger.success(`Application ready: ${url}`);
287
+ return { outputs: { url } };
288
+ },
289
+ };
290
+
291
+ export const plugins = definePlugins(deployedInfo);
292
+ ```
293
+
294
+ To publish assets, look up the site with `ctx.application.staticWebsites[name]`
295
+ and await its `publish(dir)` with an absolute directory path. Only websites
296
+ declared in the registering config appear there; any other name is `undefined`,
297
+ so check the site before publishing. Websites of other configs are listed under
298
+ `ctx.applications` with their `name` and `url` only, so a plugin can read their
299
+ URLs but cannot publish to them. The result contains `url` and `skippedFiles`; report skipped
300
+ files so users can correct files that could not be uploaded.
301
+
302
+ To run a shell command such as a build, await `ctx.exec(command, { workingDir, env })`.
303
+ It rejects when the command exits with a non-zero code. By default the command's
304
+ stdout and stderr both go to stderr so `tailor deploy --json` stdout stays parseable;
305
+ pass `output: "capture"` to receive them as `stdout` and `stderr` instead, or
306
+ `output: "ignore"` to discard them. Use `ctx.exec` rather than importing
307
+ `node:child_process` in the plugin: `tailor.config.ts` is also bundled into
308
+ functions that run on the Tailor Platform, such as auth hooks, and a plugin module
309
+ that imports Node.js-only modules makes that bundle fail.
310
+
311
+ Return JSON-serializable `outputs` to include a result in `deployedHooks` under
312
+ `tailor deploy --json`. Each entry identifies the application and plugin. This
313
+ key is omitted when no hook supplies outputs. `outputs` must be a plain object whose
314
+ values are only strings, finite numbers, booleans, `null`, arrays, and plain objects;
315
+ any other value, such as `undefined`, a function, or a `Date`, fails the hook.
316
+ Output values may be typed with an `interface`. The type checker rejects a
317
+ `Date`, function, `Map`, or other non-JSON value placed directly in `outputs` or in
318
+ an object or array literal, but a `Date` or class instance nested inside an
319
+ interface-typed value is caught only when `tailor deploy` runs the hook.
320
+
321
+ A failed hook stops later hooks with `DEPLOYED_HOOK_FAILED`. The same error is
322
+ reported when the deployed information passed to hooks cannot be loaded. Platform
323
+ resources have already been applied and are not rolled back. The error lists any hooks
324
+ that did not run, with the application each belongs to. Fix the hook and run `tailor deploy` again; hooks run again even
325
+ if there are no resource changes.
326
+
327
+ Hooks do not run during dry-run, build-only, or migration test deployments.
328
+ Dry-run lists the pending hooks; with `--json`, they appear under
329
+ `pendingDeployedHooks` as `{ application, pluginId }` entries. Calling `deploy()` from the programmatic CLI
330
+ API runs hooks under the same conditions as `tailor deploy`.
331
+
257
332
  ## Hook Scheduling Rules
258
333
 
259
334
  Each generation-time hook runs at its own pipeline phase, regardless of what other hooks the same plugin implements: