generatesaas 3.4.0 → 3.4.2
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.
|
@@ -122,8 +122,8 @@ Creates:
|
|
|
122
122
|
`added` files; `unmodified`, `deleted`, and `removed` files have none.
|
|
123
123
|
- `references/update-manifest.json` - lists of added, modified, and removed files,
|
|
124
124
|
plus `renamed` (upstream renames detected by identical content: `[{from, to}]`)
|
|
125
|
-
and `sensitive` (database schema
|
|
126
|
-
|
|
125
|
+
and `sensitive` (database schema, `packages/database/drizzle/`, and
|
|
126
|
+
`.env.example` changes that carry follow-up work: migrations, new env vars)
|
|
127
127
|
|
|
128
128
|
### Step 2: Present Changelog
|
|
129
129
|
|
|
@@ -134,7 +134,7 @@ Each release's notes may be one of two shapes:
|
|
|
134
134
|
- **Curated** - contains explicit `## Breaking`, `## Migration`, `## Features`, `## Fixes` (or similar) sections. Use those sections verbatim; they are authoritative.
|
|
135
135
|
- **Raw** - an auto-generated flat list of change titles (one line per commit or PR, no sections). This is the common case. You can group the titles into features/fixes by reading them, but you **cannot** reliably infer breaking changes or migration steps from titles alone.
|
|
136
136
|
|
|
137
|
-
Also read `sensitive` in `references/update-manifest.json`: database schema changes mean
|
|
137
|
+
Also read `sensitive` in `references/update-manifest.json`: database schema changes mean a migration is likely required, anything under `packages/database/drizzle/` means upstream migrations that must be regenerated into the project's own history before they will run (see [Database](#database-schema-migrations)), and `.env.example` changes mean new or changed environment variables. Mention all three in the summary and again in the final post-update steps.
|
|
138
138
|
|
|
139
139
|
Present a clear, organized summary:
|
|
140
140
|
|
|
@@ -143,9 +143,9 @@ Present a clear, organized summary:
|
|
|
143
143
|
> **Breaking changes:** {from a Breaking section, or "⚠️ Not specified in the changelog - review the diffs in Step 7 carefully, especially database/schema and config files"}
|
|
144
144
|
> **New features:** {list}
|
|
145
145
|
> **Bug fixes:** {list}
|
|
146
|
-
> **
|
|
146
|
+
> **Migration steps required:** {from a Migration section, or "⚠️ None listed - if this update changes the database schema or required env vars, you may still need to migrate. I'll flag schema/config changes as we review them."}
|
|
147
147
|
|
|
148
|
-
**Be honest about uncertainty.** Never present an inferred "Breaking changes: None" or "
|
|
148
|
+
**Be honest about uncertainty.** Never present an inferred "Breaking changes: None" or "Migration steps: None" as if it were authoritative when the changelog is raw PR titles - say it's not specified and that you'll watch for schema/config/env changes during the file review. A silently-missed migration (e.g. a new DB column) is the worst failure mode.
|
|
149
149
|
|
|
150
150
|
Then ask: *"Ready to proceed with the file analysis?"*
|
|
151
151
|
|
|
@@ -341,13 +341,15 @@ Preview first (especially useful in Careful mode or when unsure), then apply:
|
|
|
341
341
|
node __SKILL_ROOT__/generatesaas-update/scripts/apply-auto.js --dry-run # lists every file it would update/create, changes nothing
|
|
342
342
|
node __SKILL_ROOT__/generatesaas-update/scripts/apply-auto.js
|
|
343
343
|
```
|
|
344
|
-
`apply-auto.js` copies only `unmodified` (safe replace) + `new` (any upstream-added file whose tree the buyer has not deleted). It will **never** touch `newInDeletedTree` files, and it aborts without writing anything if any source is missing from staging (no half-applied tree). Report what was applied
|
|
344
|
+
`apply-auto.js` copies only `unmodified` (safe replace) + `new` (any upstream-added file whose tree the buyer has not deleted). It will **never** touch `newInDeletedTree` files, and it aborts without writing anything if any source is missing from staging (no half-applied tree). Report what was applied. Mark all auto-update and new file items as `[x]` in the plan - **except any file the script reports as held back**, which was never applied and moves to **Skipped** with the regenerate reason. Marking a held-back migration `[x]` records a table that does not exist as delivered, in the one artifact that outlives the session.
|
|
345
|
+
|
|
346
|
+
It also holds back everything under `packages/database/drizzle/` and lists what it skipped. Those are NOT pending work you can copy in afterwards - migrations must be regenerated into the project's own history or they never run. See [Database](#database-schema-migrations).
|
|
345
347
|
|
|
346
348
|
**Careful mode:**
|
|
347
349
|
Present the list of safe auto-updates and new files to the user:
|
|
348
350
|
> These {N} files haven't been customized and can be safely replaced with the new version. {N} new files will be created. Want me to apply all of these, or would you like to review any specific ones first?
|
|
349
351
|
|
|
350
|
-
If the user wants to review specific files, show them the diff for each requested file. Apply only after approval.
|
|
352
|
+
If the user wants to review specific files, show them the diff for each requested file. Apply only after approval. Do not count held-back migrations in either number - they are not being created, and presenting them as applied is the same error as marking them `[x]`.
|
|
351
353
|
|
|
352
354
|
### Step 7: Review Modified Files
|
|
353
355
|
|
|
@@ -537,7 +539,7 @@ This updates `.generatesaas/manifest.json` to the new version, regenerates file
|
|
|
537
539
|
|
|
538
540
|
### Post-update steps
|
|
539
541
|
- Run `pnpm install` if dependencies changed
|
|
540
|
-
-
|
|
542
|
+
- Run database migrations if schema was updated
|
|
541
543
|
- {any other steps from the changelog}
|
|
542
544
|
|
|
543
545
|
### Skipped changes
|
|
@@ -574,44 +576,32 @@ Config files are the most commonly customized.
|
|
|
574
576
|
- Keep user-added business logic intact
|
|
575
577
|
- Watch for new imports or dependency changes
|
|
576
578
|
|
|
577
|
-
### Database (schema)
|
|
579
|
+
### Database (schema, migrations)
|
|
578
580
|
|
|
579
581
|
- NEVER auto-merge schema files - always present for user review
|
|
580
|
-
- Warn about schema changes
|
|
581
|
-
|
|
582
|
-
**
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
**Push can report success without applying anything.** `drizzle-kit push` prints its error and
|
|
605
|
-
still exits 0 when a change cannot be applied. `db:deploy` is why that no longer matters in
|
|
606
|
-
production: it re-reads the live schema afterwards and fails the boot naming the tables and columns
|
|
607
|
-
that differ. If a user applies a change by hand with `push` instead, have them run `db:deploy`
|
|
608
|
-
after it, or check the column themselves - a green push is not evidence on its own.
|
|
609
|
-
|
|
610
|
-
**A project generated before push-only** may still have `packages/database/drizzle/` and `generate`
|
|
611
|
-
/ `migrate` scripts. Those are vestigial: the update removes the scripts, and the directory can be
|
|
612
|
-
deleted whenever the user is comfortable. Their database is already at whatever state the old
|
|
613
|
-
migrations left it, so the first push simply diffs from there - usually a no-op if the schema
|
|
614
|
-
matched. Review that first push on a production database rather than letting it ride a deploy.
|
|
582
|
+
- Warn about schema changes that may require new migrations
|
|
583
|
+
|
|
584
|
+
**Never copy an upstream migration into `packages/database/drizzle/` as-is, and never overwrite the project's `drizzle/meta/_journal.json` or `drizzle/meta/*_snapshot.json` with the upstream versions.** A project's migration history is its own: upstream changes get re-derived into it, never pasted over it. Regenerate instead:
|
|
585
|
+
|
|
586
|
+
1. Merge the upstream schema change into `packages/database/src/db/*.ts` (user review first, as above).
|
|
587
|
+
2. Run `pnpm --filter @repo/database generate`. Drizzle diffs the project's own latest snapshot against the merged schema and writes a fresh migration carrying a current timestamp, the next `idx`, a correct `prevId`, and a snapshot that describes the project's real schema.
|
|
588
|
+
3. Open the upstream `.sql` under `.generatesaas/staging/packages/database/drizzle/` and copy any hand-written statements into the generated file: seeds, backfills, data migrations, `ON CONFLICT` guards. `generate` infers structure only, so those are lost otherwise.
|
|
589
|
+
4. Run `pnpm --filter @repo/database migrate`.
|
|
590
|
+
|
|
591
|
+
**Why the upstream file cannot just be added.** Drizzle applies a migration only when its journal `when` value is greater than the `created_at` of the last migration already applied to the database. A project that generated its own migration after the upstream one was authored sits above that mark, so a copied-in upstream file is **skipped silently and permanently**: no error, no warning, no log line, and `migrate` still exits 0. The table or column it was supposed to create never exists, and the miss surfaces later as runtime failures on whatever code path needs it. Taking the upstream `_journal.json` and snapshots instead of the project's own is the second half of the same trap: the final snapshot then knows nothing about the project's own tables, so the next `generate` re-emits `CREATE TABLE` for tables that already exist.
|
|
592
|
+
|
|
593
|
+
**If step 2 reports no schema changes**, the upstream migration is data-only and there is nothing to regenerate. This is the ONLY case where the `.sql` is copied in by hand. Copying the file is not enough on its own: drizzle reads `_journal.json` and runs only the entries listed there, so a `.sql` sitting in the folder with no entry is never opened. Do all four, in this order:
|
|
594
|
+
|
|
595
|
+
1. **Append an entry** to the project's own `_journal.json`, copied from the upstream entry. This is the load-bearing step - without it nothing else matters.
|
|
596
|
+
2. Set that entry's `when` **greater than the largest `when` already applied** to the database, never the upstream value. Drizzle takes the highest `created_at` in `drizzle.__drizzle_migrations` as its floor and skips everything at or below it.
|
|
597
|
+
3. Set that entry's `idx` to continue the project's own sequence (last `idx` + 1).
|
|
598
|
+
4. Rename the `.sql` file so its name matches the entry's `tag`, or set the `tag` to the file's existing name. Drizzle loads the file **by tag** and throws if the two disagree.
|
|
599
|
+
|
|
600
|
+
Still do not bring the upstream `meta/*_snapshot.json`, here least of all. Nothing needs it: `migrate` never reads snapshots, and `generate` needs only ONE previous snapshot, which must be the project's own. Adding an upstream one breaks `generate` whichever way it sorts - `meta/` is read in filename order, so an upstream snapshot that sorts LAST silently becomes the diff base (re-emitting `CREATE TABLE` for the project's own tables, and `DROP TABLE` for anything upstream carries that the project removed), while one that sorts earlier collides on `prevId` with the project's own, which prints a warning, writes no migration, and still exits 0.
|
|
601
|
+
|
|
602
|
+
**A project generated on 3.3.0-3.4.1 has no `packages/database/drizzle/` directory** - those releases were push-only, so a push shaped its database and there is no history to regenerate into. Run `pnpm --filter @repo/database generate` once to write its baseline migration and commit it; until then `db:deploy` prints a warning and falls back to a push for that deploy. **Generate that baseline BEFORE merging any schema change from this update**, so it describes the schema the database actually has: `db:deploy` records a baseline as applied rather than executing it only when every table it creates already exists. A baseline generated after the merge creates tables the database lacks, and `db:deploy` then aborts with a message that says how to regenerate it - it never runs a baseline against a half-shaped database. Once the baseline exists, merge the upstream schema change and run `generate` again for the difference, as above.
|
|
603
|
+
|
|
604
|
+
Whichever path applies, `db:deploy` re-reads the live schema afterwards and fails the boot naming any table or column that differs - so a migrate (or a push) that quietly changed nothing is caught at boot rather than at runtime.
|
|
615
605
|
|
|
616
606
|
### Package Dependencies (`package.json`)
|
|
617
607
|
|
|
@@ -10,6 +10,9 @@ const fs = require("node:fs");
|
|
|
10
10
|
const path = require("node:path");
|
|
11
11
|
const { findProjectRoot, ensureDir, STAGING_DIR } = require("./_helpers.js");
|
|
12
12
|
|
|
13
|
+
/** Drizzle migration history. Held back from every auto-copy - see filesToCopy. */
|
|
14
|
+
const MIGRATION_DIR = "packages/database/drizzle/";
|
|
15
|
+
|
|
13
16
|
function main() {
|
|
14
17
|
const root = findProjectRoot();
|
|
15
18
|
if (!root) {
|
|
@@ -41,15 +44,19 @@ function main() {
|
|
|
41
44
|
// handles by prompt (never here).
|
|
42
45
|
const candidates = [...classification.unmodified, ...classification.new];
|
|
43
46
|
|
|
44
|
-
//
|
|
45
|
-
//
|
|
46
|
-
//
|
|
47
|
-
//
|
|
48
|
-
//
|
|
49
|
-
|
|
47
|
+
// Migrations are NEVER auto-applied, and both failure modes are silent.
|
|
48
|
+
// Drizzle runs a migration only when its journal `when` beats the newest one
|
|
49
|
+
// already applied, so an upstream .sql copied in below that mark never runs
|
|
50
|
+
// and `migrate` still exits 0; and an upstream meta snapshot becomes the diff
|
|
51
|
+
// base for the next `generate`, which then re-emits the buyer's own tables.
|
|
52
|
+
// The skill's Database section regenerates them into the project's own
|
|
53
|
+
// history instead, which is only possible if this step leaves them alone.
|
|
54
|
+
const heldMigrations = candidates.filter((f) => f.startsWith(MIGRATION_DIR));
|
|
55
|
+
const filesToCopy = candidates.filter((f) => !f.startsWith(MIGRATION_DIR));
|
|
50
56
|
|
|
51
57
|
if (filesToCopy.length === 0) {
|
|
52
58
|
console.log("No files to auto-update. All changed files need manual merge.");
|
|
59
|
+
reportHeldMigrations(heldMigrations);
|
|
53
60
|
return;
|
|
54
61
|
}
|
|
55
62
|
|
|
@@ -77,6 +84,7 @@ function main() {
|
|
|
77
84
|
console.log(` ${unmodifiedSet.has(filePath) ? "update" : "create"} ${filePath}`);
|
|
78
85
|
}
|
|
79
86
|
console.log("\n[dry run] No files were modified.");
|
|
87
|
+
reportHeldMigrations(heldMigrations);
|
|
80
88
|
return;
|
|
81
89
|
}
|
|
82
90
|
|
|
@@ -110,10 +118,36 @@ function main() {
|
|
|
110
118
|
}
|
|
111
119
|
}
|
|
112
120
|
|
|
121
|
+
reportHeldMigrations(heldMigrations);
|
|
113
122
|
|
|
114
123
|
console.log("\nAfter merging all files, run complete-update.js to finalize.");
|
|
115
124
|
}
|
|
116
125
|
|
|
126
|
+
/**
|
|
127
|
+
* Report migration files deliberately NOT copied, so the AI knows they are
|
|
128
|
+
* waiting rather than assuming the tree is settled. Silent when there are none.
|
|
129
|
+
*
|
|
130
|
+
* States the whole procedure rather than pointing at SKILL.md. The AI loads
|
|
131
|
+
* SKILL.md at the start of the update, BEFORE step 1a refreshes the bundle, so
|
|
132
|
+
* for one release after this ships it is following the previous release's prose
|
|
133
|
+
* - which says migrations can be added directly. This output has to stand alone
|
|
134
|
+
* against that reader, or the fix reads as an obstacle to work around.
|
|
135
|
+
*/
|
|
136
|
+
function reportHeldMigrations(heldMigrations) {
|
|
137
|
+
if (heldMigrations.length === 0) return;
|
|
138
|
+
console.log(`\n${heldMigrations.length} migration file(s) held back - NOT copied into the project:`);
|
|
139
|
+
for (const f of heldMigrations) console.log(` - ${f}`);
|
|
140
|
+
console.log("\nDo NOT copy these in by hand, and do not take the upstream meta/ snapshots either.");
|
|
141
|
+
console.log("Drizzle runs a migration only when its journal entry is newer than the newest one");
|
|
142
|
+
console.log("already applied, so a pasted-in file never runs, `migrate` still exits 0, and the");
|
|
143
|
+
console.log("table it should have created silently does not exist. Regenerate instead:");
|
|
144
|
+
console.log(" 1. Merge the upstream schema change into packages/database/src/db/");
|
|
145
|
+
console.log(" 2. Run: pnpm --filter @repo/database generate");
|
|
146
|
+
console.log(" 3. Copy any hand-written statements (seeds, backfills, data migrations) out of the");
|
|
147
|
+
console.log(" staged .sql at .generatesaas/staging/<path above> into the file step 2 wrote -");
|
|
148
|
+
console.log(" `generate` infers structure only, so those statements are lost otherwise");
|
|
149
|
+
console.log(" 4. Run: pnpm --filter @repo/database migrate");
|
|
150
|
+
}
|
|
117
151
|
|
|
118
152
|
try {
|
|
119
153
|
main();
|