Skip to content

breaking!: merge the v2 release line into main#1808

Draft
toiroakr wants to merge 862 commits into
mainfrom
v2
Draft

breaking!: merge the v2 release line into main#1808
toiroakr wants to merge 862 commits into
mainfrom
v2

Conversation

@toiroakr

@toiroakr toiroakr commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Merges the v2 release line (SDK 2.0.0-next.x prereleases, published to the next dist-tag) into main, so the stable 2.0.0 ships from main.
  • This PR body tracks everything that must be done before (and immediately after) the merge — mostly cleanup of interim measures that only existed because v2 was a long-lived branch.

Pre-merge checklist

Branch sync & CI

Release & versioning

  • Correct sequencing: main stays the 1.x release line as normal (merging its Version Packages PR, currently Version Packages #1878 → 1.84.0, is fine at any time before this PR merges — latest is still legitimately 1.x pre-merge). The maintenance/v1 cutover happens at/after this PR merges: fast-forward/merge main's pre-merge tip into maintenance/v1, and from that point on 1.x patches release from there instead. The duplicate 1.84.0 Version Packages PR on maintenance/v1 (Version Packages #1880, created by activating the release channel ahead of the actual cutover) has been closed — Version Packages #1878 is the sole live 1.x release PR again, so 1.84.0 will ship from main as normal.
  • Land or close the v2 prerelease PR (Version Packages (next) #1804 already merged; a new one reopened as Version Packages (next) #1845, changeset-release/v2v2, currently mergeable) so no release PR is in flight when the merge happens.
  • Create a v1 maintenance branch, kept in sync with main's tip until the real cutover — created as maintenance/v1 @ bfad6ccde (2026-07-23), since re-synced to main @ bd0b02e0c (2026-07-24). Needs one more sync to main's actual pre-merge tip right at/after this PR merges (see the sequencing item above) — this is not a one-time setup, it must track main until the cutover moment.
  • Set up the 1.x release channel on maintenance/v1 (fc4f57d07): release.yml triggers on maintenance/v1 and publishes with --tag v1, changeset-check.yml skips changeset-release/maintenance/v1, and .changeset/config.json baseBranch follows. The workflow itself is verified working (it opened Version Packages #1880, since closed as a premature duplicate of Version Packages #1878 — see the sequencing item above). The configuration stays in place ready for the real cutover; no further action needed here.
  • Exit changesets pre mode (pnpm changeset pre exit, which removes .changeset/pre.json) so the first release from main publishes the stable 2.0.0 to the latest dist-tag. Decide whether to run it on v2 right before merging or on main immediately after.
  • Verify the release workflow's "Resolve pending codemod version boundaries" step (pnpm codemod:resolve-pending) behaves correctly for the first stable (non-next) release. Verified in resolve-pending-boundaries.ts: a stable 2.0.0 with pending markers is an explicit no-op (reachesCodemodBoundary() treats pending as reached at the stable until boundary), any other stable version with a pending marker fails the release loudly, and no codemod currently uses V2_NEXT_PENDING (usage count 0 in registry.ts).
  • Reword .changeset/v2-baseline.md — now describes the stable upgrade path (codemod command + migration guide link + v1 dist-tag note) instead of the next prerelease instructions (111518052).
  • Confirm @tailor-platform/sdk-plugin-tailordb-erd (now 0.1.0-next.2) is ready to leave prerelease — the one known blocker (bin not linking on clean installs) is fixed via the committed launcher; no other open PRs target it. Exits pre mode together with the SDK at 0.1.0.
  • Resolve stable-exit version collisions with versions already published from main (111518052): bumped sdk-codemod to 0.4.0-next.8 and eslint-plugin-sdk to 0.2.0-next.0 (package.json only — pre.json has no initialVersions). Dry-run verified: pre exit + changeset version now lands on 0.4.0 / 0.2.0 (previously 0.3.0 / 0.1.0, colliding with 0.3.00.3.8 / 0.1.0 already on npm). Also confirmed no collision for the others: sdk/create-sdk exit at 2.0.0, tailordb-erd plugin at 0.1.0 (stable not yet published).

CI workflow cleanup (in this PR or immediately after merge)

  • Drop v2 from branch triggers: test.yml, ci.yml, release.yml, pkg-pr-new.yml, erd-schema.yml, lockfile-audit.yml, check-license.yml, template-tests.yml, skills-sync-check.yml, sdk-metrics.yml.
  • Restore the branches: [main] filter on sdk-metrics.yml's pull_request trigger (removed so it ran for v2-based PRs).
  • Optionally tighten changeset-check.yml's skip condition back from startsWith(head.ref, 'changeset-release/') to the exact changeset-release/main (it was broadened for the v2 release branch).
  • Remove the link-check.yml exclusion for example/resolvers/startWorkflow.ts — the file exists on main once this merges.

tailor-platform/actions

  • Cut a v1 maintenance branch in tailor-platform/actions (maintenance/v1) so the v1.x line can still receive patches.
  • Merge tailor-platform/actions' v2 branch into main and publish v2.0.0 (released 2026-07-21; the v2 major tag tracks it).
  • Repin erd-schema-export / erd-schema-preview / erd-schema-comment in erd-schema.yml to the v2.0.0 release and remove the # pinact:skip-file marker (also bumped check-licenses).
  • Bump ACTIONS_SHA / ACTIONS_VERSION in packages/sdk/src/cli/commands/setup/templates.ts (and the setup workflow templates) to v2.0.0, so tailor setup scaffolds CI that invokes the renamed tailor CLI. TEMPLATE_VERSION unchanged — pin-only update.
  • The v2 migration guide now tells tailor setup users to re-run setup so scaffolded workflows move off the v1 actions.
  • Re-evaluate the "Stub ERD plugin bin target" workaround in erd-schema.yml — resolved (884d6a08e): the plugin bin now resolves through a committed bin/cli.mjs launcher (the pattern used by vite/vitest/astro/nuxt/changesets), so the bin links on clean installs and both stub steps were removed. Verified with a clean-checkout install (bin links with no dist/ present, even under --ignore-scripts).

Docs (after the stable publish)

  • Replace the @next install instructions with the stable tag: packages/sdk-plugin-tailordb-erd/README.md, packages/sdk/docs/cli/tailordb.md + tailordb.template.md, packages/sdk/docs/github-actions.md (then run pnpm docs:update).
  • Regenerate packages/sdk/docs/migration/v2.md (pnpm codemod:docs:update) once all codemod version boundaries are final.

Repo housekeeping (after merge)

Notes

  • Merge this PR with a merge commit (not squash): the branch contains merges from main, and the release workflow relies on the consumed-changeset history staying intact.

dqn and others added 30 commits July 9, 2026 22:49
fix(sdk): restore Timestamp Kysely type for date fields
collectPathsInto only applied `paths` when `baseUrl` was also set,
silently dropping alias resolution for tsconfig.json files that omit
baseUrl (the standard TS 5.0+ style, resolved relative to the
tsconfig directory).
…olution

`baseUrl ?? "."` left non-string values (e.g. a boolean) from a
malformed tsconfig.json passed straight to path.resolve(), which
throws a TypeError. Fall back to "." unless baseUrl is actually a
string.

Also add a matching resolveSync regression test for the baseUrl-
omitted case, and a changeset for the fix.
collectPathsInto resolved a child config's paths against its own
directory even when baseUrl was only defined in an extended parent
config, and merged inherited paths instead of replacing them the way
TypeScript does when a child config defines its own paths.

Verified both behaviors against tsc directly before fixing:
baseUrl resolves relative to the config file that defines it, and a
child's own paths replaces (not merges with) inherited paths.

Propagate the effective baseUrl up through the extends recursion so
a child without its own baseUrl can still use one inherited from an
extended config, and clear previously collected paths before writing
a config's own paths.
…exports-v2

feat(runtime): replace flat runtime subpath exports
The CI format check failed on a line oxfmt would have wrapped
differently; the local pre-commit hook did not catch it. No logic
change.
…verage in ts-hook

collectPathsInto called targets.map() unconditionally, which throws a
TypeError when a malformed tsconfig defines a paths target that is
not an array (e.g. a bare string instead of a string[]). Skip such
entries instead of crashing.

Also add resolveSync regression tests mirroring the existing resolve
tests for the extends inheritance fixes (inherited baseUrl, paths
replacement), since both entry points share the same
collectPathsInto/loadTsconfigPaths implementation.
…lformed-config coverage in ts-hook

collectPathsInto called t.endsWith() on each paths target entry
unconditionally, which throws a TypeError when a malformed tsconfig
has a non-string entry in a target array (e.g. [123, "./*"]). Filter
out non-string entries before mapping.

Also add resolveSync tests mirroring the existing resolve tests for
the malformed baseUrl/paths cases, matching the PR description's
stated coverage.
…ject in ts-hook

collectPathsInto cleared previously collected paths whenever
compilerOptions.paths was truthy, even when it was malformed (e.g. a
boolean). In that case no usable aliases could be collected either,
so a valid paths object inherited from an extended config was
unintentionally dropped. Only apply the replace-on-own-paths
behavior when paths is a non-array object.
When a paths target array normalized to an empty list (all entries
were non-string), collectPathsInto still wrote that empty array to
the alias. matchTsconfigPaths() returns the first prefix-matching
wildcard alias's targets unconditionally, so an empty result short-
circuited resolution instead of falling through to a less specific
alias with valid targets. Skip writing the alias entirely when its
normalized targets are empty.
… in ts-hook

TypeScript resolves inherited compilerOptions.paths against the
nearest-defined baseUrl in the extends chain, independent of which
config level defines paths -- verified directly against tsc: a child
overriding only baseUrl (paths inherited) resolves those paths
against its own baseUrl, and when no baseUrl is defined anywhere the
directory of the config that defines paths is used as the fallback.

collectPathsInto previously resolved each config's paths eagerly
while still walking the extends chain, using only the baseUrl known
at that point, so a baseUrl override at an outer (more child) level
was never applied to paths inherited from an extended config.

Restructure the walk: resolveEffectiveConfig now walks the chain and
returns the raw (unresolved) nearest-defined baseUrl, paths, and the
directory that defines those paths, without resolving any target
paths. collectPathsInto resolves targets once, after the full chain
has been walked, using the final effective baseUrl.
- Strip typeHookExpr/typeValidateExpr in remote snapshot comparison
- Add default field comparison to areFieldsDifferent
- Fix nested field validators being skipped by early continue
- Update orderItem template to use oldRecord fallback in update hook
- Bump codemod prereleaseUntil to V2_NEXT_4
- Add sdk-codemod patch to changeset
Use a generic parameter on the issues callback instead of (string & {})
in DottedPaths. The generic <P extends DottedPaths<...>> preserves strict
compile-time checking for invalid field names while maintaining structural
compatibility between concrete and generic TailorDBType instantiations.
fix(cli): resolve tsconfig path aliases without baseUrl in ts-hook
@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

Bump sdk-codemod to 0.4.0-next.8 and eslint-plugin-sdk to 0.2.0-next.0 so
exiting changesets pre mode lands on 0.4.0 / 0.2.0 instead of colliding
with 0.3.0-0.3.8 / 0.1.0 already published from main. Reword the v2
baseline changeset for the stable 2.0.0 changelog.
@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

pnpm only links a package bin whose target file exists at install time,
so pointing bin at dist/cli.js left it unlinked on clean checkouts and
required stubbing the dist entry before install in erd-schema.yml.
Point bin at a committed bin/cli.mjs launcher that imports dist/cli.js,
matching the pattern used across major pnpm monorepos, and drop the
stub steps.
tailor-platform-pr-trigger Bot and others added 3 commits July 23, 2026 13:52
changesets/action does not support executable files; pnpm sets the bit
on the linked bin itself.
@github-actions

This comment has been minimized.

toiroakr added 3 commits July 23, 2026 23:01
pnpm chmods a package's bin target +x at install time, so a committed
launcher trips the working-tree -x check and dirties checkouts when
tracked as 644. Track the launcher as 755 (matching what pnpm enforces)
and make the guard inspect committed git object modes, exempting
declared workspace bin targets, which changesets/action never commits.
@github-actions

This comment has been minimized.

toiroakr added 4 commits July 24, 2026 15:07
The tailor bin (dist/cli/index.mjs) is now a minimal shim that enables
Node's on-disk compile cache before dynamically importing the real
CLI entry (renamed from index.ts to main.ts), so its full module graph
is cached across invocations instead of only the lazily-loaded parts.

Hand-rolled via node:module's enableCompileCache since this package
is still on politty 0.11.3, which predates politty's own
compile-cache helpers.
Upgrade politty to 0.11.5, which ships `politty/compile-cache` and the
`politty generate-shim` CLI command. Replace the hand-rolled
src/cli/index.mjs shim with a shim generated at build time by
`politty generate-shim --entry ./main.mjs --program tailor`, matching
the generated content's own reasoning and validation instead of a
duplicated, hand-maintained implementation.
@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

dqn and others added 2 commits July 24, 2026 16:56
perf(cli): enable Node's on-disk compile cache for the tailor binary
@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

toiroakr and others added 5 commits July 24, 2026 20:23
`executeScript()'s `arg` option is serialized internally, so a
pre-stringified value (`arg: JSON.stringify(x)`, a leftover v1
pattern) double-encodes silently at runtime. A type-level fix was
explored first (narrowing `arg` to reject a bare string) but any such
constraint is asymmetric with createWorkflowJob's input contract,
which allows a top-level string just as legitimately as
number/boolean/array/object -- there is no way to distinguish an
intentional string arg from an accidental JSON.stringify(x) result by
type alone.

Add an oxlint rule instead: it flags the exact `arg: JSON.stringify(...)`
syntactic pattern (including the multi-argument pretty-print form and
values held in a `const` variable) without touching executeScript's
type at all, so no legitimate arg shape is ever rejected. Enable it in
newly scaffolded projects alongside the existing rules.

Also share `resolveValue` (previously local to no-unconditional-permit)
via lib/sdk-bindings.js so both rules can follow an identifier through
its const initializer.
Now that the plugin has 3 rules, splitting the 350+ line index.test.js
by rule keeps each rule's coverage easier to navigate. The shared
`lint`/`expectViolation`/`expectClean` helpers move to
rules/test-helpers.js; index.test.js keeps only the plugin-level tests
(recommended config, scaffolded template alignment).

package.json's `files` previously listed the whole `rules`/`lib`
directories, which would have silently shipped the new colocated test
files (and the test-only helper) to npm consumers. Enumerate the actual
source files instead of the directories, so what ships is explicit
rather than relying on exclusion patterns.
…onst chains

Address Copilot review feedback:

- test-helpers.js spawned the platform `.bin/oxlint` shim directly,
  which is `oxlint.cmd`/`oxlint.ps1` on Windows and would fail with
  ENOENT there (the reviews claim that CI already exercises this on
  Windows is incorrect -- "Plugin tests (Windows)" runs the unrelated
  `packages/sdk` `unit-plugin` project, not this package -- but the
  underlying platform gap is real for anyone running these tests on
  Windows directly). Resolve the actual `bin/oxlint` script (a plain JS
  file with a shebang) from the oxlint package and run it via `node`
  instead, which works identically on every platform.
- Both no-execute-script-arg-stringify and no-api-prefix-in-path-pattern
  resolved their options object through only one identifier hop, unlike
  the arg/pathPattern values they read from it, which already follow a
  chain of `const` aliases via resolveValue. Use resolveValue for the
  options object too, in both rules, so a transitive alias like
  `const base = {...}; const options = base; executeScript(options)`
  is caught consistently.
feat(lint): add no-execute-script-arg-stringify rule
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants