@spinajs/orm-cli 2.0.487
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/README.md +247 -0
- package/lib/cjs/cli/MigrateCreate.d.ts +59 -0
- package/lib/cjs/cli/MigrateCreate.d.ts.map +1 -0
- package/lib/cjs/cli/MigrateCreate.js +211 -0
- package/lib/cjs/cli/MigrateCreate.js.map +1 -0
- package/lib/cjs/cli/MigrateDown.d.ts +23 -0
- package/lib/cjs/cli/MigrateDown.d.ts.map +1 -0
- package/lib/cjs/cli/MigrateDown.js +74 -0
- package/lib/cjs/cli/MigrateDown.js.map +1 -0
- package/lib/cjs/cli/MigrateResolve.d.ts +19 -0
- package/lib/cjs/cli/MigrateResolve.d.ts.map +1 -0
- package/lib/cjs/cli/MigrateResolve.js +66 -0
- package/lib/cjs/cli/MigrateResolve.js.map +1 -0
- package/lib/cjs/cli/MigrateStatus.d.ts +19 -0
- package/lib/cjs/cli/MigrateStatus.d.ts.map +1 -0
- package/lib/cjs/cli/MigrateStatus.js +109 -0
- package/lib/cjs/cli/MigrateStatus.js.map +1 -0
- package/lib/cjs/cli/MigrateUp.d.ts +33 -0
- package/lib/cjs/cli/MigrateUp.d.ts.map +1 -0
- package/lib/cjs/cli/MigrateUp.js +92 -0
- package/lib/cjs/cli/MigrateUp.js.map +1 -0
- package/lib/cjs/config/orm-cli.d.ts +9 -0
- package/lib/cjs/config/orm-cli.d.ts.map +1 -0
- package/lib/cjs/config/orm-cli.js +26 -0
- package/lib/cjs/config/orm-cli.js.map +1 -0
- package/lib/cjs/index.d.ts +18 -0
- package/lib/cjs/index.d.ts.map +1 -0
- package/lib/cjs/index.js +34 -0
- package/lib/cjs/index.js.map +1 -0
- package/lib/cjs/orm.d.ts +31 -0
- package/lib/cjs/orm.d.ts.map +1 -0
- package/lib/cjs/orm.js +38 -0
- package/lib/cjs/orm.js.map +1 -0
- package/lib/cjs/package.json +1 -0
- package/lib/mjs/cli/MigrateCreate.d.ts +59 -0
- package/lib/mjs/cli/MigrateCreate.d.ts.map +1 -0
- package/lib/mjs/cli/MigrateCreate.js +173 -0
- package/lib/mjs/cli/MigrateCreate.js.map +1 -0
- package/lib/mjs/cli/MigrateDown.d.ts +23 -0
- package/lib/mjs/cli/MigrateDown.d.ts.map +1 -0
- package/lib/mjs/cli/MigrateDown.js +71 -0
- package/lib/mjs/cli/MigrateDown.js.map +1 -0
- package/lib/mjs/cli/MigrateResolve.d.ts +19 -0
- package/lib/mjs/cli/MigrateResolve.d.ts.map +1 -0
- package/lib/mjs/cli/MigrateResolve.js +63 -0
- package/lib/mjs/cli/MigrateResolve.js.map +1 -0
- package/lib/mjs/cli/MigrateStatus.d.ts +19 -0
- package/lib/mjs/cli/MigrateStatus.d.ts.map +1 -0
- package/lib/mjs/cli/MigrateStatus.js +106 -0
- package/lib/mjs/cli/MigrateStatus.js.map +1 -0
- package/lib/mjs/cli/MigrateUp.d.ts +33 -0
- package/lib/mjs/cli/MigrateUp.d.ts.map +1 -0
- package/lib/mjs/cli/MigrateUp.js +89 -0
- package/lib/mjs/cli/MigrateUp.js.map +1 -0
- package/lib/mjs/config/orm-cli.d.ts +9 -0
- package/lib/mjs/config/orm-cli.d.ts.map +1 -0
- package/lib/mjs/config/orm-cli.js +24 -0
- package/lib/mjs/config/orm-cli.js.map +1 -0
- package/lib/mjs/index.d.ts +18 -0
- package/lib/mjs/index.d.ts.map +1 -0
- package/lib/mjs/index.js +18 -0
- package/lib/mjs/index.js.map +1 -0
- package/lib/mjs/orm.d.ts +31 -0
- package/lib/mjs/orm.d.ts.map +1 -0
- package/lib/mjs/orm.js +34 -0
- package/lib/mjs/orm.js.map +1 -0
- package/lib/mjs/package.json +1 -0
- package/lib/tsconfig.cjs.tsbuildinfo +1 -0
- package/lib/tsconfig.mjs.tsbuildinfo +1 -0
- package/package.json +68 -0
package/README.md
ADDED
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
# @spinajs/orm-cli
|
|
2
|
+
|
|
3
|
+
Command line front end for spinajs ORM migrations. Five commands — apply, roll back, report,
|
|
4
|
+
force a state, scaffold — over the `orm.Migration` facade in `@spinajs/orm`.
|
|
5
|
+
|
|
6
|
+
The package is a thin wrapper on purpose. Everything that decides what a migration run means
|
|
7
|
+
lives in `@spinajs/orm`; what lives here is the argument handling, the operator-facing wording
|
|
8
|
+
and the exit codes. The dependency runs one way only — `orm-cli` → `orm` — so the ORM stays
|
|
9
|
+
usable, and testable, with no CLI in its dependency tree.
|
|
10
|
+
|
|
11
|
+
## Install
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
npm i @spinajs/orm-cli
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
The package ships a config fragment that appends its own command directory to
|
|
18
|
+
`system.dirs.cli`, which is where `@spinajs/cli` looks for commands. Installing it is therefore
|
|
19
|
+
enough — `spinajs migrate-status` works with no import and no wiring on your side. If your
|
|
20
|
+
application builds its own command list instead, `import '@spinajs/orm-cli'` is all that is
|
|
21
|
+
needed: `@Command` registers each class in DI the moment the module is evaluated.
|
|
22
|
+
|
|
23
|
+
The commands are also plain DI classes, so a script can drive them without commander:
|
|
24
|
+
|
|
25
|
+
```ts
|
|
26
|
+
import { DI } from '@spinajs/di';
|
|
27
|
+
import { MigrateStatusCommand } from '@spinajs/orm-cli';
|
|
28
|
+
|
|
29
|
+
await (await DI.resolve(MigrateStatusCommand)).execute();
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Running a command never migrates anything
|
|
33
|
+
|
|
34
|
+
Every command starts by resolving an `Orm`, and an ordinary `DI.resolve(Orm)` ends with the boot
|
|
35
|
+
migration pass — every pending migration on every connection whose `Migration.OnStartup` is on.
|
|
36
|
+
For an application that is the point. For a migration tool it is a trap, twice over:
|
|
37
|
+
|
|
38
|
+
- a connection holding a **failed** migration refuses every migration run, so the resolve throws
|
|
39
|
+
before the command body starts. That took down every command on the row it was invoked about,
|
|
40
|
+
including `migrate-resolve` — the one command that clears it, and the one the refusal names as
|
|
41
|
+
the remedy.
|
|
42
|
+
- `migrate-status` would apply everything pending and only then report, so the deploy gate asking
|
|
43
|
+
"is this database current?" made it current, answered "yes" and exited `0`, with the DDL it was
|
|
44
|
+
meant to hold back already run.
|
|
45
|
+
|
|
46
|
+
So the commands resolve their Orm through `resolveCliOrm()`, which passes `MigrateOnStartup:
|
|
47
|
+
false` (an `IOrmOptions` field of `@spinajs/orm`). Everything else about resolving happens —
|
|
48
|
+
connections, models, value converters, `orm.Migration` — only the boot pass is skipped. It is
|
|
49
|
+
opt-**in**: nothing changes for an application that resolves an Orm the ordinary way, and this
|
|
50
|
+
package ships no configuration that would switch startup migrations off for anybody.
|
|
51
|
+
|
|
52
|
+
Two consequences worth knowing:
|
|
53
|
+
|
|
54
|
+
- `migrate-up --fake` means what it says on a `Migration.OnStartup` connection. A boot pass would
|
|
55
|
+
have really applied the migrations the flag promises only to record.
|
|
56
|
+
- **A migration applied by the CLI never gets its `data()` hook.** Seeding belongs to the boot
|
|
57
|
+
pass: `Orm.resolve()` seeds what its own startup run applied, and a later boot finds the
|
|
58
|
+
migration already applied and seeds nothing. That was already true of every connection with
|
|
59
|
+
`Migration.OnStartup` off; it is now true of all of them. Migrations that must be seeded have to
|
|
60
|
+
be applied by an application boot, not by `migrate-up`.
|
|
61
|
+
|
|
62
|
+
## Commands
|
|
63
|
+
|
|
64
|
+
| Command | Options | Does |
|
|
65
|
+
| --- | --- | --- |
|
|
66
|
+
| `migrate-up` | `-n, --name [name]`, `-c, --connection [connection]`, `-f, --fake` | Applies pending migrations on every configured connection |
|
|
67
|
+
| `migrate-down` | `-n, --name [name]`, `-c, --connection [connection]`, `-a, --all`, `-f, --fake` | Rolls back — **the last applied batch only** unless `--all` |
|
|
68
|
+
| `migrate-status` | — | Prints one line per migration per connection; the deploy gate |
|
|
69
|
+
| `migrate-resolve` | `-n, --name [name]` (required), `--applied`, `--rolled-back` | Records the outcome of a FAILED migration |
|
|
70
|
+
| `migrate-create` | `-n, --name [name]` (required), `-d, --dir [dir]`, `-c, --connection [connection]` | Scaffolds a migration file |
|
|
71
|
+
|
|
72
|
+
### `migrate-up`
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
spinajs migrate-up
|
|
76
|
+
spinajs migrate-up --name AddUserTable_2026_07_29_10_00_00
|
|
77
|
+
spinajs migrate-up --connection reporting # this connection only
|
|
78
|
+
spinajs migrate-up --fake # record as applied without running anything
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
Without `--name` it applies everything pending, in `(timestamp, name)` order, across every
|
|
82
|
+
configured connection. With `--name` it applies exactly that one.
|
|
83
|
+
|
|
84
|
+
`--connection` limits the run to one connection. Every other configured connection is left
|
|
85
|
+
completely untouched — its migration service is never reached, so its tracking table is not even
|
|
86
|
+
created. The name is matched against the configured connections (aliases included, since they
|
|
87
|
+
resolve to the same connection), and one nothing answers to **throws** rather than running
|
|
88
|
+
nothing: a filter that silently matched nothing would exit `0` reporting "0 migrations applied".
|
|
89
|
+
|
|
90
|
+
Two named-run outcomes are deliberately **not** reported as success:
|
|
91
|
+
|
|
92
|
+
- the name matches nothing in the registry — the facade throws rather than returning an empty
|
|
93
|
+
list, because "0 migrations applied" from a typo is indistinguishable from "already current";
|
|
94
|
+
- the name is registered but the connection it declares is not configured in this deployment.
|
|
95
|
+
The facade only warns and returns `[]` there, so this command checks `status()` afterwards and
|
|
96
|
+
exits non-zero with an explanation.
|
|
97
|
+
|
|
98
|
+
### `migrate-down`
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
spinajs migrate-down # the LAST APPLIED BATCH, not everything
|
|
102
|
+
spinajs migrate-down --all # every applied migration, on every connection
|
|
103
|
+
spinajs migrate-down --name AddUserTable_2026_07_29_10_00_00
|
|
104
|
+
spinajs migrate-down --connection reporting --all # everything, on one connection
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
The default scope is the last applied batch — one `migrate-up` run undone, not the whole
|
|
108
|
+
history. `--all` reverses everything. `--connection` narrows whichever of those two applies, and
|
|
109
|
+
is announced first for that reason: `--all --connection reporting` is "every applied migration on
|
|
110
|
+
*one* connection". The command says which scope it is about to reverse *before* it does it,
|
|
111
|
+
because by the time the result line prints, the schema has already changed.
|
|
112
|
+
|
|
113
|
+
A rollback drops the tracking row rather than stamping it "rolled back": the table is meant to
|
|
114
|
+
hold only migrations that are actually present in the database, and both a missing row and a
|
|
115
|
+
rolled-back one read as pending to the next `migrate-up`.
|
|
116
|
+
|
|
117
|
+
`--name` has a known sharp edge in the migration service: it is handed a one-element unit list,
|
|
118
|
+
so every *other* applied row in the target batch looks unmatched and gets warned about as
|
|
119
|
+
"no registered migration matches them (file deleted or renamed)". Those rows are healthy, and
|
|
120
|
+
the remedy that warning suggests — removing the row by hand — is destructive here. This command
|
|
121
|
+
prints a line saying exactly that before the run, so the warnings can be ignored.
|
|
122
|
+
|
|
123
|
+
### `migrate-status`
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
spinajs migrate-status
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
```
|
|
130
|
+
STATE BATCH CONNECTION MIGRATION
|
|
131
|
+
applied 1 default AddUserTable_2026_07_29_10_00_00
|
|
132
|
+
!! FAILED 0 default AddOrderIndex_2026_07_29_11_00_00
|
|
133
|
+
?? INTERRUPTED 0 default BackfillTotals_2026_07_29_12_00_00
|
|
134
|
+
pending - default AddInvoices_2026_07_30_09_00_00
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
Output goes to stdout via `console.log`, not through the framework logger: it is this command's
|
|
138
|
+
*product*, something an operator greps and a script pipes, and routing it through the log would
|
|
139
|
+
let a configured level or target swallow it.
|
|
140
|
+
|
|
141
|
+
A failed row carries `!!` in the leftmost column, not just the word `FAILED`. That row is the
|
|
142
|
+
one line in the report that stops every later `migrate-up` on its connection, and it has to
|
|
143
|
+
survive being skimmed in a wall of `applied`. Below the table the command prints the two exact
|
|
144
|
+
`migrate-resolve` invocations for each failed migration.
|
|
145
|
+
|
|
146
|
+
`??` marks an **interrupted** migration — one that was started and never finished, because the
|
|
147
|
+
process running it was killed before it could record either outcome. It carries the opposite
|
|
148
|
+
warning to `FAILED`: it blocks nothing, and the next `migrate-up` re-runs it from the top, whether
|
|
149
|
+
or not anybody looked. Under the default `Transaction.Mode: None` that means non-idempotent data
|
|
150
|
+
changes get applied twice, silently. The same two `migrate-resolve` invocations are printed for
|
|
151
|
+
it. See "Interrupted runs" in
|
|
152
|
+
[the ORM migration docs](../orm/docs/10-schema-and-migrations.md#interrupted-runs).
|
|
153
|
+
|
|
154
|
+
`[checksum mismatch]` marks a migration whose source changed after it was applied. It is
|
|
155
|
+
reported but does **not** on its own make the command exit non-zero — only pending and failed
|
|
156
|
+
work do.
|
|
157
|
+
|
|
158
|
+
### `migrate-resolve`
|
|
159
|
+
|
|
160
|
+
The escape hatch for a run that died halfway. Valid on the two row shapes whose real outcome
|
|
161
|
+
nobody recorded — **failed** (`FinishedAt` NULL and `Logs` set) and **interrupted** (`StartedAt`
|
|
162
|
+
set, `FinishedAt` and `Logs` both NULL). Anything healthy, rolled back or absent is refused rather
|
|
163
|
+
than silently rewritten.
|
|
164
|
+
|
|
165
|
+
```bash
|
|
166
|
+
spinajs migrate-resolve --name AddOrderIndex_2026_07_29_11_00_00 --applied # the change IS in the database
|
|
167
|
+
spinajs migrate-resolve --name AddOrderIndex_2026_07_29_11_00_00 --rolled-back # the change is NOT
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
Exactly one of the two flags, never both and never neither: the point of the command is to state
|
|
171
|
+
which of the two things actually happened, and neither the CLI nor the ORM can find that out on
|
|
172
|
+
its own. The refusal happens before any Orm is resolved, so a malformed command line never opens
|
|
173
|
+
a database connection.
|
|
174
|
+
|
|
175
|
+
`--rolled-back` makes the migration pending again — it *will* run on the next `migrate-up`.
|
|
176
|
+
|
|
177
|
+
### `migrate-create`
|
|
178
|
+
|
|
179
|
+
```bash
|
|
180
|
+
spinajs migrate-create --name AddInvoices
|
|
181
|
+
spinajs migrate-create --name AddInvoices --dir ./src/migrations --connection reporting
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
Prints the path it wrote, on its own line, so `$(spinajs migrate-create -n AddInvoices)` is
|
|
185
|
+
usable. Defaults: `./src/migrations` and the `default` connection.
|
|
186
|
+
|
|
187
|
+
`--name` takes the *prefix* only, letters and digits, starting with a letter. The
|
|
188
|
+
`_yyyy_MM_dd_HH_mm_ss` suffix is appended here, and it is not decoration: that timestamp is the
|
|
189
|
+
only ordering the migration runner has, and it is read back out of the class name. A name the
|
|
190
|
+
runner cannot parse is refused up front, and an existing file is never overwritten.
|
|
191
|
+
|
|
192
|
+
The generated class only takes effect once it is *imported* — the `@Migration` decorator has to
|
|
193
|
+
run to register it. Re-export it from your package or application index, the way `src/migrations/*.ts`
|
|
194
|
+
files are re-exported elsewhere in spinajs.
|
|
195
|
+
|
|
196
|
+
## Exit codes
|
|
197
|
+
|
|
198
|
+
| Command | `0` | non-zero |
|
|
199
|
+
| --- | --- | --- |
|
|
200
|
+
| `migrate-up` | migrations applied, or nothing was pending | a named run applied nothing because its connection is not configured, or it is still pending/failed; a `--connection` nothing answers to; any error from the run |
|
|
201
|
+
| `migrate-down` | rollback completed, or nothing to roll back | a `--connection` nothing answers to; any error from the run |
|
|
202
|
+
| `migrate-status` | every migration is applied | anything is pending or failed |
|
|
203
|
+
| `migrate-resolve` | the state was recorded | both/neither flag given; the row is neither failed nor interrupted |
|
|
204
|
+
| `migrate-create` | file written | invalid name or connection; the file already exists |
|
|
205
|
+
|
|
206
|
+
`migrate-status` is meant to be a deploy gate — "is this database current?" — so an un-run
|
|
207
|
+
migration is a "no", not just a failed one.
|
|
208
|
+
|
|
209
|
+
Two things the table does not say:
|
|
210
|
+
|
|
211
|
+
- **A `0` from `migrate-status` means "nothing is pending", not "the database is reachable and
|
|
212
|
+
configured".** With no connections configured, nothing is registered, so nothing is pending and
|
|
213
|
+
the command exits `0`. A gate that must also catch a failed config should check that the command
|
|
214
|
+
reported migrations at all.
|
|
215
|
+
- **Requires a `@spinajs/cli` that propagates `process.exitCode`.** Earlier versions ended the
|
|
216
|
+
bin's success path with a bare `process.exit(0)`, which discards whatever a command set — driven
|
|
217
|
+
through such a bin, `migrate-status` exits `0` even with pending work. If you are pinned to one,
|
|
218
|
+
call the command class directly (see the snippet at the top) rather than going through the bin.
|
|
219
|
+
|
|
220
|
+
## The blocking guarantee is best-effort
|
|
221
|
+
|
|
222
|
+
A failed migration blocks every later `migrate-up` on its connection. That is what makes
|
|
223
|
+
`migrate-status` + `migrate-resolve` a safe recovery loop instead of a suggestion: a half-applied
|
|
224
|
+
schema change cannot be built on top of.
|
|
225
|
+
|
|
226
|
+
The guarantee holds only as far as the bookkeeping does. When a migration fails, the ORM writes
|
|
227
|
+
the failure into the tracking table — and if *that* write fails too (the connection dropped, the
|
|
228
|
+
table is locked), the error is caught and logged rather than raised. The run still fails, but the
|
|
229
|
+
row that would have blocked the next `migrate-up` was never written, and the next run proceeds as
|
|
230
|
+
if nothing had happened.
|
|
231
|
+
|
|
232
|
+
In practice this needs the database to fail twice, in a specific order. It matters when you are
|
|
233
|
+
reading logs after an incident: a `migrate-up` that succeeded shortly after a failed one is not
|
|
234
|
+
by itself proof that the failure was resolved. Check `migrate-status`.
|
|
235
|
+
|
|
236
|
+
## Notes
|
|
237
|
+
|
|
238
|
+
- Migrations run against a schema no model is wired to yet. Use the `OrmDriver` passed to `up()`,
|
|
239
|
+
never a model class. The `data()` hook runs later, once models are available.
|
|
240
|
+
- `--fake` records the outcome without executing anything, on both `migrate-up` and
|
|
241
|
+
`migrate-down`. It is for a database that was changed out of band and needs the tracking table
|
|
242
|
+
brought in line.
|
|
243
|
+
- `migrate-status` reports every configured connection, including ones whose
|
|
244
|
+
`Migration.OnStartup` is off — hiding those would answer "nothing to see" for exactly the
|
|
245
|
+
connections somebody is most likely asking about. It has no `--connection` of its own, for the
|
|
246
|
+
same reason: the report is the deploy gate, and a gate that can be narrowed is a gate that can
|
|
247
|
+
be talked past.
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { CliCommand } from '@spinajs/cli';
|
|
2
|
+
import { Log } from '@spinajs/log-common';
|
|
3
|
+
export interface IMigrateCreateCommandOptions {
|
|
4
|
+
name: string;
|
|
5
|
+
dir?: string;
|
|
6
|
+
connection?: string;
|
|
7
|
+
env?: string;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* The prefix half of `Prefix_yyyy_MM_dd_HH_mm_ss`. Deliberately narrower than what a TS class
|
|
11
|
+
* name allows: underscores and digits are how the runner finds the timestamp - `MIGRATION_FILE_REGEXP`
|
|
12
|
+
* splits on the LAST `_yyyy_MM_dd_HH_mm_ss`-shaped run - so a prefix that carries its own
|
|
13
|
+
* underscore-digit groups is a name nobody can read back with confidence.
|
|
14
|
+
*/
|
|
15
|
+
export declare const MIGRATION_NAME_REGEXP: RegExp;
|
|
16
|
+
/**
|
|
17
|
+
* The connection name is interpolated into `@Migration('...')`, so anything that could close that
|
|
18
|
+
* string literal has to be refused here rather than emitted into a file that will not parse.
|
|
19
|
+
*/
|
|
20
|
+
export declare const CONNECTION_NAME_REGEXP: RegExp;
|
|
21
|
+
/**
|
|
22
|
+
* The env tag becomes a dot-segment in the file name AND a string literal inside `@Migration()`,
|
|
23
|
+
* so it may carry neither a dot ( which would read as a second tag ) nor anything that could close
|
|
24
|
+
* that literal.
|
|
25
|
+
*/
|
|
26
|
+
export declare const ENV_NAME_REGEXP: RegExp;
|
|
27
|
+
/**
|
|
28
|
+
* Reads `--env` directly off `process.argv`, deliberately more flexible than `Configuration`'s own
|
|
29
|
+
* `parseArgv` ( `packages/configuration/src/util.ts` - not exported from that package's public surface,
|
|
30
|
+
* so this logic is duplicated rather than imported ).
|
|
31
|
+
*
|
|
32
|
+
* `parseArgv` handles only the space-separated form ( `--env local` ), but this helper also accepts
|
|
33
|
+
* the equals form ( `--env=local` ). This is correct here: `packages/cli/src/args.ts` strips both
|
|
34
|
+
* forms from commander's argv, because `Configuration` consumes the framework-level `--env` directly.
|
|
35
|
+
* With `--env=local`, the value is stripped but `Configuration`'s `parseArgv` cannot recognize the
|
|
36
|
+
* equals form, so the CLI process boots under the default environment while the scaffolded file gets
|
|
37
|
+
* its `.local` suffix — harmless for this command, which uses the value only as a filename tag and
|
|
38
|
+
* a decorator string. `-e` is untouched by the strip and reaches commander normally, so it needs no
|
|
39
|
+
* duplicate handling here.
|
|
40
|
+
*/
|
|
41
|
+
export declare function parseEnvArgv(argv?: string[]): string | undefined;
|
|
42
|
+
export declare const DEFAULT_MIGRATION_DIR = "./src/migrations";
|
|
43
|
+
export declare const DEFAULT_MIGRATION_CONNECTION = "default";
|
|
44
|
+
/**
|
|
45
|
+
* The scaffold. `connection` is named ( rather than `_connection` ) in both hooks because both
|
|
46
|
+
* bodies are meant to be filled in immediately - and the eslint pragma on the first line is the
|
|
47
|
+
* repo's own convention for a migration whose `down()` legitimately ignores it.
|
|
48
|
+
*/
|
|
49
|
+
export declare function migrationTemplate(cls: string, connection: string, env?: string): string;
|
|
50
|
+
/**
|
|
51
|
+
* The one command here that needs no database and no Orm: it writes a file. Keeping it free of
|
|
52
|
+
* `DI.resolve(Orm)` means a developer can scaffold a migration in a checkout whose connections
|
|
53
|
+
* are not configured, or not reachable, which is exactly when new migrations get written.
|
|
54
|
+
*/
|
|
55
|
+
export declare class MigrateCreateCommand extends CliCommand {
|
|
56
|
+
protected Log: Log;
|
|
57
|
+
execute(options: IMigrateCreateCommandOptions): Promise<void>;
|
|
58
|
+
}
|
|
59
|
+
//# sourceMappingURL=MigrateCreate.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MigrateCreate.d.ts","sourceRoot":"","sources":["../../../src/cli/MigrateCreate.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAmB,MAAM,cAAc,CAAC;AAE3D,OAAO,EAAE,GAAG,EAAU,MAAM,qBAAqB,CAAC;AAKlD,MAAM,WAAW,4BAA4B;IAC3C,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED;;;;;GAKG;AACH,eAAO,MAAM,qBAAqB,QAA2B,CAAC;AAE9D;;;GAGG;AACH,eAAO,MAAM,sBAAsB,QAAiC,CAAC;AAErE;;;;GAIG;AACH,eAAO,MAAM,eAAe,QAA4B,CAAC;AAYzD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,YAAY,CAAC,IAAI,GAAE,MAAM,EAAiB,GAAG,MAAM,GAAG,SAAS,CAc9E;AAED,eAAO,MAAM,qBAAqB,qBAAqB,CAAC;AAExD,eAAO,MAAM,4BAA4B,YAAY,CAAC;AAEtD;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,MAAM,CA0BvF;AAED;;;;GAIG;AACH,qBAKa,oBAAqB,SAAQ,UAAU;IAElD,SAAS,CAAC,GAAG,EAAE,GAAG,CAAC;IAEN,OAAO,CAAC,OAAO,EAAE,4BAA4B,GAAG,OAAO,CAAC,IAAI,CAAC;CA6D3E"}
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
19
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
20
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
21
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
22
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
23
|
+
};
|
|
24
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
25
|
+
var ownKeys = function(o) {
|
|
26
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
27
|
+
var ar = [];
|
|
28
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
29
|
+
return ar;
|
|
30
|
+
};
|
|
31
|
+
return ownKeys(o);
|
|
32
|
+
};
|
|
33
|
+
return function (mod) {
|
|
34
|
+
if (mod && mod.__esModule) return mod;
|
|
35
|
+
var result = {};
|
|
36
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
37
|
+
__setModuleDefault(result, mod);
|
|
38
|
+
return result;
|
|
39
|
+
};
|
|
40
|
+
})();
|
|
41
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
42
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
43
|
+
};
|
|
44
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
45
|
+
exports.MigrateCreateCommand = exports.DEFAULT_MIGRATION_CONNECTION = exports.DEFAULT_MIGRATION_DIR = exports.ENV_NAME_REGEXP = exports.CONNECTION_NAME_REGEXP = exports.MIGRATION_NAME_REGEXP = void 0;
|
|
46
|
+
exports.parseEnvArgv = parseEnvArgv;
|
|
47
|
+
exports.migrationTemplate = migrationTemplate;
|
|
48
|
+
const cli_1 = require("@spinajs/cli");
|
|
49
|
+
const exceptions_1 = require("@spinajs/exceptions");
|
|
50
|
+
const log_common_1 = require("@spinajs/log-common");
|
|
51
|
+
const luxon_1 = require("luxon");
|
|
52
|
+
const fs = __importStar(require("node:fs"));
|
|
53
|
+
const path = __importStar(require("node:path"));
|
|
54
|
+
/**
|
|
55
|
+
* The prefix half of `Prefix_yyyy_MM_dd_HH_mm_ss`. Deliberately narrower than what a TS class
|
|
56
|
+
* name allows: underscores and digits are how the runner finds the timestamp - `MIGRATION_FILE_REGEXP`
|
|
57
|
+
* splits on the LAST `_yyyy_MM_dd_HH_mm_ss`-shaped run - so a prefix that carries its own
|
|
58
|
+
* underscore-digit groups is a name nobody can read back with confidence.
|
|
59
|
+
*/
|
|
60
|
+
exports.MIGRATION_NAME_REGEXP = /^[A-Za-z][A-Za-z0-9]*$/;
|
|
61
|
+
/**
|
|
62
|
+
* The connection name is interpolated into `@Migration('...')`, so anything that could close that
|
|
63
|
+
* string literal has to be refused here rather than emitted into a file that will not parse.
|
|
64
|
+
*/
|
|
65
|
+
exports.CONNECTION_NAME_REGEXP = /^[A-Za-z0-9][A-Za-z0-9_.-]*$/;
|
|
66
|
+
/**
|
|
67
|
+
* The env tag becomes a dot-segment in the file name AND a string literal inside `@Migration()`,
|
|
68
|
+
* so it may carry neither a dot ( which would read as a second tag ) nor anything that could close
|
|
69
|
+
* that literal.
|
|
70
|
+
*/
|
|
71
|
+
exports.ENV_NAME_REGEXP = /^[A-Za-z][A-Za-z0-9-]*$/;
|
|
72
|
+
/**
|
|
73
|
+
* The three middle segments `parseMigrationFileEnv` provably cannot read back as an environment
|
|
74
|
+
* tag - they are carved out by name there ( a test suite named after its migration, a TypeScript
|
|
75
|
+
* declaration file ), not because they collide with a real tag but because a `<Name>.<tag>.ts` file
|
|
76
|
+
* whose tag is one of these is never read as tagged at all. `--env test` would therefore write a
|
|
77
|
+
* file whose suffix channel is silently dead - and one many projects' `**\/*.test.ts` globs would
|
|
78
|
+
* try to execute as a test suite besides. Refused here rather than left to surprise someone later.
|
|
79
|
+
*/
|
|
80
|
+
const RESERVED_ENV_NAMES = ['test', 'spec', 'd'];
|
|
81
|
+
/**
|
|
82
|
+
* Reads `--env` directly off `process.argv`, deliberately more flexible than `Configuration`'s own
|
|
83
|
+
* `parseArgv` ( `packages/configuration/src/util.ts` - not exported from that package's public surface,
|
|
84
|
+
* so this logic is duplicated rather than imported ).
|
|
85
|
+
*
|
|
86
|
+
* `parseArgv` handles only the space-separated form ( `--env local` ), but this helper also accepts
|
|
87
|
+
* the equals form ( `--env=local` ). This is correct here: `packages/cli/src/args.ts` strips both
|
|
88
|
+
* forms from commander's argv, because `Configuration` consumes the framework-level `--env` directly.
|
|
89
|
+
* With `--env=local`, the value is stripped but `Configuration`'s `parseArgv` cannot recognize the
|
|
90
|
+
* equals form, so the CLI process boots under the default environment while the scaffolded file gets
|
|
91
|
+
* its `.local` suffix — harmless for this command, which uses the value only as a filename tag and
|
|
92
|
+
* a decorator string. `-e` is untouched by the strip and reaches commander normally, so it needs no
|
|
93
|
+
* duplicate handling here.
|
|
94
|
+
*/
|
|
95
|
+
function parseEnvArgv(argv = process.argv) {
|
|
96
|
+
for (let i = 0; i < argv.length; i++) {
|
|
97
|
+
const arg = argv[i];
|
|
98
|
+
if (arg === '--env') {
|
|
99
|
+
return argv[i + 1];
|
|
100
|
+
}
|
|
101
|
+
if (arg.startsWith('--env=')) {
|
|
102
|
+
return arg.slice('--env='.length);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
return undefined;
|
|
106
|
+
}
|
|
107
|
+
exports.DEFAULT_MIGRATION_DIR = './src/migrations';
|
|
108
|
+
exports.DEFAULT_MIGRATION_CONNECTION = 'default';
|
|
109
|
+
/**
|
|
110
|
+
* The scaffold. `connection` is named ( rather than `_connection` ) in both hooks because both
|
|
111
|
+
* bodies are meant to be filled in immediately - and the eslint pragma on the first line is the
|
|
112
|
+
* repo's own convention for a migration whose `down()` legitimately ignores it.
|
|
113
|
+
*/
|
|
114
|
+
function migrationTemplate(cls, connection, env) {
|
|
115
|
+
return `/* eslint-disable @typescript-eslint/no-unused-vars */
|
|
116
|
+
import { Migration, OrmDriver, OrmMigration } from '@spinajs/orm';
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* TODO: describe the schema change this migration makes.
|
|
120
|
+
*/
|
|
121
|
+
@Migration('${connection}'${env ? `, { Env: '${env}' }` : ''})
|
|
122
|
+
export class ${cls} extends OrmMigration {
|
|
123
|
+
/**
|
|
124
|
+
* Schema changes. Models are NOT wired up yet at this point - reach the database through
|
|
125
|
+
* \`connection\`, never through a model class.
|
|
126
|
+
*/
|
|
127
|
+
public async up(connection: OrmDriver): Promise<void> {
|
|
128
|
+
// TODO: await connection.schema().createTable('table_name', (table) => { ... });
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Undoes \`up()\`. Leave it empty only when the change genuinely cannot be reversed - an empty
|
|
133
|
+
* \`down()\` makes migrate-down report success while changing nothing.
|
|
134
|
+
*/
|
|
135
|
+
public async down(connection: OrmDriver): Promise<void> {
|
|
136
|
+
// TODO: await connection.schema().dropTable('table_name');
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
`;
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* The one command here that needs no database and no Orm: it writes a file. Keeping it free of
|
|
143
|
+
* `DI.resolve(Orm)` means a developer can scaffold a migration in a checkout whose connections
|
|
144
|
+
* are not configured, or not reachable, which is exactly when new migrations get written.
|
|
145
|
+
*/
|
|
146
|
+
let MigrateCreateCommand = class MigrateCreateCommand extends cli_1.CliCommand {
|
|
147
|
+
async execute(options) {
|
|
148
|
+
const name = options.name ?? '';
|
|
149
|
+
const connection = options.connection ?? exports.DEFAULT_MIGRATION_CONNECTION;
|
|
150
|
+
// An explicit `options.env` wins - that is how the tests in this suite call `execute()`
|
|
151
|
+
// directly - and falls back to a direct argv read for the real CLI path, where
|
|
152
|
+
// `packages/cli/src/args.ts` has already stripped `--env <value>` out of what commander sees.
|
|
153
|
+
// See `parseEnvArgv()`.
|
|
154
|
+
const env = options.env ?? parseEnvArgv();
|
|
155
|
+
if (!exports.MIGRATION_NAME_REGEXP.test(name)) {
|
|
156
|
+
throw new exceptions_1.InvalidArgument(`Invalid migration name "${name}" - it must be a plain class-name prefix: a letter followed by letters or digits, no spaces, dashes or underscores. The _yyyy_MM_dd_HH_mm_ss suffix is appended here.`);
|
|
157
|
+
}
|
|
158
|
+
if (!exports.CONNECTION_NAME_REGEXP.test(connection)) {
|
|
159
|
+
throw new exceptions_1.InvalidArgument(`Invalid connection name "${connection}" - expected the name of a connection from db.Connections, eg. "default"`);
|
|
160
|
+
}
|
|
161
|
+
if (env !== undefined) {
|
|
162
|
+
if (!exports.ENV_NAME_REGEXP.test(env)) {
|
|
163
|
+
throw new exceptions_1.InvalidArgument(`Invalid environment name "${env}" - a letter followed by letters, digits or dashes. It becomes both a file suffix and a string inside @Migration().`);
|
|
164
|
+
}
|
|
165
|
+
if (RESERVED_ENV_NAMES.includes(env)) {
|
|
166
|
+
throw new exceptions_1.InvalidArgument(`Invalid environment name "${env}" - "test", "spec" and "d" are carved out by the migration file parser as file-kind markers (a test suite, a TypeScript declaration file), never read back as an environment tag. Choose a different name.`);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
// The timestamp is not decoration: it is the ONLY ordering the migration runner has, and it is
|
|
170
|
+
// read back out of the class name rather than out of the file's mtime or its position on disk.
|
|
171
|
+
const cls = `${name}_${luxon_1.DateTime.now().toFormat('yyyy_MM_dd_HH_mm_ss')}`;
|
|
172
|
+
const dir = options.dir ?? exports.DEFAULT_MIGRATION_DIR;
|
|
173
|
+
const file = path.join(dir, `${cls}${env ? `.${env}` : ''}.ts`);
|
|
174
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
175
|
+
try {
|
|
176
|
+
// 'wx' - never clobber. Two `migrate-create` runs inside the same second produce the same
|
|
177
|
+
// class name, and silently overwriting the first one would delete work that was just written.
|
|
178
|
+
fs.writeFileSync(file, migrationTemplate(cls, connection, env), { flag: 'wx', encoding: 'utf-8' });
|
|
179
|
+
}
|
|
180
|
+
catch (err) {
|
|
181
|
+
if (err.code === 'EEXIST') {
|
|
182
|
+
throw new exceptions_1.IOFail(`Migration file ${file} already exists - a migration with this name was created in the same second. Wait a second and run it again, or pass a different --name.`, err);
|
|
183
|
+
}
|
|
184
|
+
throw err;
|
|
185
|
+
}
|
|
186
|
+
// The path goes to stdout on its own line so `$(spinajs migrate-create -n Foo)` is usable;
|
|
187
|
+
// everything else is guidance and belongs in the log.
|
|
188
|
+
// eslint-disable-next-line no-console
|
|
189
|
+
console.log(file);
|
|
190
|
+
// The scan is about the APPLICATION's own directories ( `system.dirs.migrations`, resolved
|
|
191
|
+
// against ITS cwd at runtime ) - not about where this file happened to be scaffolded. Scaffold
|
|
192
|
+
// it inside a package under `<pkg>/src/migrations` and it sits in a directory this same list
|
|
193
|
+
// would scan too, but only when the consumer boots FROM that package's own cwd, which almost
|
|
194
|
+
// never happens: the running process's cwd is the application, not any of its dependencies. A
|
|
195
|
+
// package's migrations must always be re-exported from its own index, or they never run.
|
|
196
|
+
this.Log.info(`Created migration ${cls} for connection "${connection}"${env ? ` in environment "${env}"` : ''}. A file under the application's own system.dirs.migrations is discovered automatically. Inside a package, always re-export it from the package's index - a package's own directories are never scanned at a consumer's runtime - so the @Migration decorator runs and registers it.`);
|
|
197
|
+
}
|
|
198
|
+
};
|
|
199
|
+
exports.MigrateCreateCommand = MigrateCreateCommand;
|
|
200
|
+
__decorate([
|
|
201
|
+
(0, log_common_1.Logger)('ORM-CLI'),
|
|
202
|
+
__metadata("design:type", log_common_1.Log)
|
|
203
|
+
], MigrateCreateCommand.prototype, "Log", void 0);
|
|
204
|
+
exports.MigrateCreateCommand = MigrateCreateCommand = __decorate([
|
|
205
|
+
(0, cli_1.Command)('migrate-create', 'Scaffolds a new migration file'),
|
|
206
|
+
(0, cli_1.Option)('-n, --name [name]', true, 'migration name prefix - a plain class-name prefix, letters and digits only'),
|
|
207
|
+
(0, cli_1.Option)('-d, --dir [dir]', false, `target directory, default ${exports.DEFAULT_MIGRATION_DIR}`),
|
|
208
|
+
(0, cli_1.Option)('-c, --connection [connection]', false, `connection the migration runs on, default "${exports.DEFAULT_MIGRATION_CONNECTION}"`),
|
|
209
|
+
(0, cli_1.Option)('-e, --env [env]', false, 'environment this migration belongs to, eg. local - omit to run it in every environment')
|
|
210
|
+
], MigrateCreateCommand);
|
|
211
|
+
//# sourceMappingURL=MigrateCreate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MigrateCreate.js","sourceRoot":"","sources":["../../../src/cli/MigrateCreate.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { CliCommand } from '@spinajs/cli';
|
|
2
|
+
import { Log } from '@spinajs/log-common';
|
|
3
|
+
export interface IMigrateDownCommandOptions {
|
|
4
|
+
name?: string;
|
|
5
|
+
all?: boolean;
|
|
6
|
+
fake?: boolean;
|
|
7
|
+
connection?: string;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* The description says LAST BATCH out loud, and so does the pre-run line below: `down()` defaults
|
|
11
|
+
* to the last applied batch, not to everything, and an operator who assumed otherwise would read
|
|
12
|
+
* a short "rolled back 1 migration" as a complete teardown.
|
|
13
|
+
*/
|
|
14
|
+
export declare class MigrateDownCommand extends CliCommand {
|
|
15
|
+
protected Log: Log;
|
|
16
|
+
execute(options: IMigrateDownCommandOptions): Promise<void>;
|
|
17
|
+
/**
|
|
18
|
+
* Says what scope is about to be reversed BEFORE it is reversed, because by the time the result
|
|
19
|
+
* line is printed the schema change has already happened.
|
|
20
|
+
*/
|
|
21
|
+
protected announce(options: IMigrateDownCommandOptions): void;
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=MigrateDown.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MigrateDown.d.ts","sourceRoot":"","sources":["../../../src/cli/MigrateDown.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAmB,MAAM,cAAc,CAAC;AAC3D,OAAO,EAAE,GAAG,EAAU,MAAM,qBAAqB,CAAC;AAGlD,MAAM,WAAW,0BAA0B;IACzC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;;;GAIG;AACH,qBAKa,kBAAmB,SAAQ,UAAU;IAEhD,SAAS,CAAC,GAAG,EAAE,GAAG,CAAC;IAEN,OAAO,CAAC,OAAO,EAAE,0BAA0B,GAAG,OAAO,CAAC,IAAI,CAAC;IAoBxE;;;OAGG;IACH,SAAS,CAAC,QAAQ,CAAC,OAAO,EAAE,0BAA0B,GAAG,IAAI;CAwB9D"}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.MigrateDownCommand = void 0;
|
|
13
|
+
const cli_1 = require("@spinajs/cli");
|
|
14
|
+
const log_common_1 = require("@spinajs/log-common");
|
|
15
|
+
const orm_js_1 = require("../orm.js");
|
|
16
|
+
/**
|
|
17
|
+
* The description says LAST BATCH out loud, and so does the pre-run line below: `down()` defaults
|
|
18
|
+
* to the last applied batch, not to everything, and an operator who assumed otherwise would read
|
|
19
|
+
* a short "rolled back 1 migration" as a complete teardown.
|
|
20
|
+
*/
|
|
21
|
+
let MigrateDownCommand = class MigrateDownCommand extends cli_1.CliCommand {
|
|
22
|
+
async execute(options) {
|
|
23
|
+
// Not `DI.resolve(Orm)`: a boot migration pass would apply the pending migrations on every
|
|
24
|
+
// `Migration.OnStartup` connection and this command would then roll back the batch it had
|
|
25
|
+
// just created. See `resolveCliOrm`.
|
|
26
|
+
const orm = await (0, orm_js_1.resolveCliOrm)();
|
|
27
|
+
this.announce(options);
|
|
28
|
+
const executed = await orm.Migration.down(options.name, { all: options.all, fake: options.fake, connection: options.connection });
|
|
29
|
+
if (executed.length === 0) {
|
|
30
|
+
const scope = options.connection ? ` on connection ${options.connection}` : ' on any configured connection';
|
|
31
|
+
this.Log.info(options.name ? `Nothing rolled back - ${options.name} is not applied${scope}` : `Nothing to roll back - no applied migrations found${options.connection ? scope : ''}`);
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
this.Log.success(`${options.fake ? 'Recorded as rolled back ( --fake, nothing was executed )' : 'Rolled back'} ${executed.length} migration(s): ${executed.map((m) => m.constructor.name).join(', ')}`);
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Says what scope is about to be reversed BEFORE it is reversed, because by the time the result
|
|
38
|
+
* line is printed the schema change has already happened.
|
|
39
|
+
*/
|
|
40
|
+
announce(options) {
|
|
41
|
+
// first, and separate from the three below: it narrows all of them, and an operator reading
|
|
42
|
+
// "rolling back EVERY applied migration" needs to know it means every one on ONE connection
|
|
43
|
+
if (options.connection) {
|
|
44
|
+
this.Log.info(`Limited to connection ${options.connection} - no other configured connection is touched`);
|
|
45
|
+
}
|
|
46
|
+
if (options.name) {
|
|
47
|
+
// A named rollback hands the migration service a one-element unit list, so every other
|
|
48
|
+
// applied row in the target batch looks unmatched to it and it warns about them. Those rows
|
|
49
|
+
// are healthy - only the warning is wrong - and the remedy it suggests is destructive if
|
|
50
|
+
// followed here, which is why this line says what to do with those warnings instead of
|
|
51
|
+
// leaving the operator to act on them.
|
|
52
|
+
this.Log.warn(`Rolling back a single migration: ${options.name}. The migration service may warn during this run that other migrations have no matching registered class - that is an artifact of a named rollback, those rows are fine. Do not edit or delete them.`);
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
if (options.all) {
|
|
56
|
+
this.Log.warn('Rolling back EVERY applied migration on every configured connection ( --all )');
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
this.Log.info('Rolling back the LAST APPLIED BATCH only - pass --all to reverse every applied migration');
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
exports.MigrateDownCommand = MigrateDownCommand;
|
|
63
|
+
__decorate([
|
|
64
|
+
(0, log_common_1.Logger)('ORM-CLI'),
|
|
65
|
+
__metadata("design:type", log_common_1.Log)
|
|
66
|
+
], MigrateDownCommand.prototype, "Log", void 0);
|
|
67
|
+
exports.MigrateDownCommand = MigrateDownCommand = __decorate([
|
|
68
|
+
(0, cli_1.Command)('migrate-down', 'Rolls ORM migrations back - the LAST APPLIED BATCH only, unless --all is given'),
|
|
69
|
+
(0, cli_1.Option)('-n, --name [name]', false, 'roll back a single migration, by class name'),
|
|
70
|
+
(0, cli_1.Option)('-c, --connection [connection]', false, 'limit the rollback to one connection, by name'),
|
|
71
|
+
(0, cli_1.Option)('-a, --all', false, 'roll back EVERY applied migration, not just the last batch'),
|
|
72
|
+
(0, cli_1.Option)('-f, --fake', false, 'record the migrations as rolled back without executing them')
|
|
73
|
+
], MigrateDownCommand);
|
|
74
|
+
//# sourceMappingURL=MigrateDown.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MigrateDown.js","sourceRoot":"","sources":["../../../src/cli/MigrateDown.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,sCAA2D;AAC3D,oDAAkD;AAClD,sCAA0C;AAS1C;;;;GAIG;AAMI,IAAM,kBAAkB,GAAxB,MAAM,kBAAmB,SAAQ,gBAAU;IAIzC,KAAK,CAAC,OAAO,CAAC,OAAmC;QACtD,2FAA2F;QAC3F,0FAA0F;QAC1F,qCAAqC;QACrC,MAAM,GAAG,GAAG,MAAM,IAAA,sBAAa,GAAE,CAAC;QAElC,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;QAEvB,MAAM,QAAQ,GAAG,MAAM,GAAG,CAAC,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,GAAG,EAAE,OAAO,CAAC,GAAG,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,EAAE,UAAU,EAAE,OAAO,CAAC,UAAU,EAAE,CAAC,CAAC;QAElI,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC1B,MAAM,KAAK,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,kBAAkB,OAAO,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC,+BAA+B,CAAC;YAE5G,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,yBAAyB,OAAO,CAAC,IAAI,kBAAkB,KAAK,EAAE,CAAC,CAAC,CAAC,qDAAqD,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YACtL,OAAO;QACT,CAAC;QAED,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,0DAA0D,CAAC,CAAC,CAAC,aAAa,IAAI,QAAQ,CAAC,MAAM,kBAAkB,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAC1M,CAAC;IAED;;;OAGG;IACO,QAAQ,CAAC,OAAmC;QACpD,4FAA4F;QAC5F,4FAA4F;QAC5F,IAAI,OAAO,CAAC,UAAU,EAAE,CAAC;YACvB,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,yBAAyB,OAAO,CAAC,UAAU,8CAA8C,CAAC,CAAC;QAC3G,CAAC;QAED,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC;YACjB,uFAAuF;YACvF,4FAA4F;YAC5F,yFAAyF;YACzF,uFAAuF;YACvF,uCAAuC;YACvC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,oCAAoC,OAAO,CAAC,IAAI,sMAAsM,CAAC,CAAC;YACtQ,OAAO;QACT,CAAC;QAED,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC;YAChB,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,+EAA+E,CAAC,CAAC;YAC/F,OAAO;QACT,CAAC;QAED,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,0FAA0F,CAAC,CAAC;IAC5G,CAAC;CACF,CAAA;AApDY,gDAAkB;AAEnB;IADT,IAAA,mBAAM,EAAC,SAAS,CAAC;8BACH,gBAAG;+CAAC;6BAFR,kBAAkB;IAL9B,IAAA,aAAO,EAAC,cAAc,EAAE,gFAAgF,CAAC;IACzG,IAAA,YAAM,EAAC,mBAAmB,EAAE,KAAK,EAAE,6CAA6C,CAAC;IACjF,IAAA,YAAM,EAAC,+BAA+B,EAAE,KAAK,EAAE,+CAA+C,CAAC;IAC/F,IAAA,YAAM,EAAC,WAAW,EAAE,KAAK,EAAE,4DAA4D,CAAC;IACxF,IAAA,YAAM,EAAC,YAAY,EAAE,KAAK,EAAE,6DAA6D,CAAC;GAC9E,kBAAkB,CAoD9B"}
|