grails-cli 0.3.0 → 0.4.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.
- package/README.md +166 -20
- package/dist/cli.js +6427 -3898
- package/package.json +1 -1
- package/templates/skill/grails-cli/SKILL.md +201 -14
package/README.md
CHANGED
|
@@ -14,8 +14,8 @@ npx grails-cli scan /path/to/library # the manifest of components
|
|
|
14
14
|
npx grails-cli tokens /path/to/library # the design tokens Grails would read; sends nothing
|
|
15
15
|
npx grails-cli sync /path/to/library --dry-run # what would be uploaded; sends nothing, needs no key
|
|
16
16
|
|
|
17
|
-
|
|
18
|
-
npx grails-cli sync /path/to/library
|
|
17
|
+
npx grails-cli connect # once: keeps the API key you copied in the system keychain; see "The key"
|
|
18
|
+
npx grails-cli sync /path/to/library # in CI, GRAILS_API_KEY from a secret does the same job
|
|
19
19
|
npx grails-cli status
|
|
20
20
|
```
|
|
21
21
|
|
|
@@ -32,9 +32,13 @@ The examples below write `grails-cli`; use `npx grails-cli` if it is not install
|
|
|
32
32
|
| `tokens [root] [--profile f] [--json]` | The design tokens Grails would read (a Tailwind theme, CSS listed in the profile's `css`, token sources in its `tokens`), and every value left out. `--json` is the exact token list a sync uploads. Sends nothing, needs no key. |
|
|
33
33
|
| `tokens baseline [root] --out <dir> [--in-repo] [--profile f]` | The scanner's tokens as DTCG files and a resolver, a first DTCG version for the team to own (see "Moving from the scanner to DTCG"). Writes only where `--out` says, refuses a path inside a git repository unless `--in-repo`, and never touches a folder that has files. Sends nothing, needs no key. |
|
|
34
34
|
| `sync [root] [--dry-run] [--json] [--trigger ci\|local] [--profile f] [--tier …] [--name n] [--platform p]` | **Tokens first, then components, in one step.** Records the tokens in one request, then opens a component sync, sends the components, finalizes, and prints the result and the vault link. See "Exit codes". |
|
|
35
|
-
| `status` | The code sources of the key's project, with the last component sync and the last token sync of each. |
|
|
36
|
-
| `tokens pending [root] [--name n] [--limit 1..1000] [--json]` | The token changes people asked for in Grails, by collection, before → after. Writes nothing (see "Changes asked for in Grails")
|
|
35
|
+
| `status` | The code sources of the key's project, with the last component sync and the last token sync of each, and how many changes asked for in Grails wait for it. |
|
|
36
|
+
| `tokens pending [root] [--name n] [--collection c] [--request id] [--limit 1..1000] [--json]` | The token changes people asked for in Grails, by collection, with each one's type, before → after and id; `--collection` and `--request` narrow it. Writes nothing (see "Changes asked for in Grails") |
|
|
37
|
+
| `tokens discard <change-id> \| --all [--collection c] \| --request id` `--reason "…" [--dry-run] [root] [--name n] [--json]` | Closes one change, or many in one step, that your team will not carry into the repository, with the reason; `--dry-run` lists what would go and changes nothing (see "Changes asked for in Grails") |
|
|
37
38
|
| `rules [root] [--set code=figma \| --delete code \| --threshold n] [--json]` | The naming rules of the Code ↔ Figma match (see "Naming rules"). |
|
|
39
|
+
| `connect [--prompt]` | Keeps your API key in the operating system's keychain, so no command needs `GRAILS_API_KEY` in your shell. Reads the key from the clipboard (then clears it) or, with `--prompt`, from a prompt that shows nothing as you type; checks it against Grails first, and stores nothing if Grails refuses it. See "The key". |
|
|
40
|
+
| `disconnect` | Removes the key `connect` stored for the API `GRAILS_API_URL` points at. |
|
|
41
|
+
| `update [root]` | Asks the npm registry now for the latest `grails-cli` and prints the installed version, the latest, and the command that updates it for your package manager. It does not run it. Exits 4 when the registry cannot be reached. See "Updates". |
|
|
38
42
|
| `skill` | Prints the agent skill the package ships (`templates/skill/grails-cli/SKILL.md`), for a team to save where its coding agent reads skills. Sends nothing, writes nothing. |
|
|
39
43
|
|
|
40
44
|
### `sync`
|
|
@@ -55,7 +59,9 @@ storing routes refuse a CLI older than 0.2.0 with 400 `GRAILS_CODE_CLI_OUTDATED`
|
|
|
55
59
|
recorded with `POST /v1/code/token-syncs` in one request. Grails builds the DTCG document from it and answers what
|
|
56
60
|
changed against the source's previous snapshot; the same content is stored once. A token step that fails (a
|
|
57
61
|
project Grails cannot read, the server refusing the list) does **not** stop the components: they sync anyway, and
|
|
58
|
-
the run exits with the token step's code.
|
|
62
|
+
the run exits with the token step's code. Once the tokens are recorded, Grails also compares the changes people asked
|
|
63
|
+
for in Grails with what the repository now holds, marks the ones it satisfies applied, and the sync says how many (see
|
|
64
|
+
"Changes asked for in Grails"); that comparison never fails the sync. Then the components:
|
|
59
65
|
|
|
60
66
|
1. **Opens** the sync with every component key the scan found (`declared_keys`). That is how the server tells a
|
|
61
67
|
component deleted from the code from one that failed to arrive.
|
|
@@ -83,10 +89,12 @@ when `CI=true` and `local` otherwise.
|
|
|
83
89
|
|
|
84
90
|
### `--dry-run`, `--json` and the job summary
|
|
85
91
|
|
|
86
|
-
- **`--dry-run` with `GRAILS_API_KEY`
|
|
92
|
+
- **`--dry-run` with a key** (`GRAILS_API_KEY`, or the one `connect` kept) asks Grails what each step would change, through the preview routes
|
|
87
93
|
(`POST /v1/code/token-syncs:preview`, `POST /v1/code/syncs:preview`), which store nothing: tokens added, changed
|
|
88
94
|
and removed; components added, changed, reactivated and **those that would be deleted**, with the same floor the
|
|
89
|
-
real sync applies.
|
|
95
|
+
real sync applies. It also says what the real sync would do with the changes asked for in Grails (see below): how many
|
|
96
|
+
it would mark applied, close because their token is gone, or leave open, with their ids (`tokens.preview.token_changes`
|
|
97
|
+
in `--json`, and in the job summary). The same verdict the real sync applies, written nowhere.
|
|
90
98
|
- **`--dry-run` without the key** counts locally and sends nothing, as it always did.
|
|
91
99
|
- **`--json`** prints one report on stdout (`mode`, `tokens`, `components`, `exit_code`); the human lines go to
|
|
92
100
|
stderr, so the output can be piped.
|
|
@@ -128,16 +136,50 @@ and the ones Grails suggests from tokens with the same value and one different w
|
|
|
128
136
|
In Grails, an admin or an editor can ask for changes to this repository's tokens: a new value, a new token,
|
|
129
137
|
collection or mode, a removal. **Grails never writes your repository, and neither does this CLI.**
|
|
130
138
|
|
|
131
|
-
grails-cli tokens pending # by collection, before → after
|
|
139
|
+
grails-cli tokens pending # by collection: type, before → after as DTCG values, and the id
|
|
132
140
|
grails-cli tokens pending --json # the list as Grails answers it, every value exact
|
|
133
141
|
grails-cli tokens pending --limit 1000 # the read answers 500 by default, at most 1000
|
|
142
|
+
grails-cli tokens pending --collection color # only that collection's changes
|
|
143
|
+
grails-cli tokens pending --request <request-id> # only one change request's (its id is under the heading)
|
|
134
144
|
|
|
135
145
|
Carry each change into your token source, or into the rules that generate it (a Style Dictionary build, a Tokens
|
|
136
|
-
Studio export, your CSS).
|
|
137
|
-
|
|
146
|
+
Studio export, your CSS). The loop closes by itself:
|
|
147
|
+
|
|
148
|
+
- **The next `sync` marks a change applied** once your repository holds its value. Grails compares the **resolved**
|
|
149
|
+
value of the token in that mode (an alias counts by the value it resolves to), with the tolerance the Diff uses for
|
|
150
|
+
colours and sizes. A token you removed closes its change; a created token, collection or mode closes when it exists.
|
|
151
|
+
The sync prints how many (`token_changes` in `--json`).
|
|
152
|
+
- **If the repository holds another value, the change stays.** Grails keeps proposing its value, checks again on every
|
|
153
|
+
sync, and records what your code holds. The "before" that `tokens pending` prints is what your repository holds now.
|
|
154
|
+
- **A change your team will not carry is closed with a reason:**
|
|
155
|
+
|
|
156
|
+
grails-cli tokens discard <change-id> --reason "Handled in the design-tokens repo"
|
|
157
|
+
|
|
158
|
+
The id is the one `tokens pending` prints beside the change. Grails keeps the change, closed, with who discarded
|
|
159
|
+
it (the owner of the API key: it is the only identity the API can verify), that it came from code, and the reason,
|
|
160
|
+
which is required (1 to 1000 characters, one line, no control characters). Discarding a collection takes its modes
|
|
161
|
+
and tokens with it; one value of a token being created cannot go alone, discard the token. A change that is
|
|
162
|
+
already applied or discarded answers exit 3.
|
|
163
|
+
|
|
164
|
+
- **Many at once, in one step.** Exactly one of three picks the changes, and the reason is required:
|
|
165
|
+
|
|
166
|
+
grails-cli tokens discard --all --reason "Handled in the design-tokens repo" # everything waiting for this repository
|
|
167
|
+
grails-cli tokens discard --all --collection color --reason "…" # one collection's
|
|
168
|
+
grails-cli tokens discard --request <request-id> --reason "…" # one change request's
|
|
169
|
+
|
|
170
|
+
The set is closed all together or not at all, and at most 1000 at once (more is refused with the way out: a
|
|
171
|
+
collection, or a request). **`--dry-run` lists what would go (id, token, before → after) and changes nothing**; the
|
|
172
|
+
reason is optional then. There is no `--yes`: the reason is the commitment. The answer, and `--json`, say how many
|
|
173
|
+
were discarded and which. `--collection` goes with `--all` only, and the same words select the same changes in
|
|
174
|
+
`tokens pending`, so you can look before you close. A change someone discards in Grails is a different path and is
|
|
175
|
+
deleted; these stay, with the reason.
|
|
176
|
+
- **A dry-run sync shows what the next sync would close:** `grails-cli sync --dry-run` (with the key) prints how many
|
|
177
|
+
changes it would mark applied, close because their token is gone, or leave open, and their ids, without storing
|
|
178
|
+
anything. `grails-cli status` shows how many changes wait for each source.
|
|
138
179
|
|
|
139
180
|
An sRGB colour shows as its hex and a dimension as `24px`; anything else shows as its DTCG JSON, and `--json` has the
|
|
140
|
-
exact components. The source is named as `sync` names it (`--name`, else the package name)
|
|
181
|
+
exact components. The source is named as `sync` names it (`--name`, else the package name); a change must belong to it.
|
|
182
|
+
Both commands need the usual key (`write:code`).
|
|
141
183
|
|
|
142
184
|
### Exit codes
|
|
143
185
|
|
|
@@ -149,9 +191,9 @@ red.
|
|
|
149
191
|
|---|---|---|
|
|
150
192
|
| 0 | done: synced, nothing changed, or a dry run | – |
|
|
151
193
|
| 1 | the component sync ran and ended incomplete (unreadable components, or deletions held back) | no: read the warnings |
|
|
152
|
-
| 2 | misconfigured: a flag, `grails.code.json` (including no declared `branch` on a real sync), no key, or a project Grails cannot read (its own tailwindcss missing, or of a major other than 3 and 4) | no: fix it |
|
|
153
|
-
| 3 | the server refused it: the key, the plan, content it does not accept, or a CLI older than the minimum (400) | no: fix it |
|
|
154
|
-
| 4 | the server could not be reached or failed (network, 5xx, a 429 after one retry) | yes |
|
|
194
|
+
| 2 | misconfigured: a flag, `grails.code.json` (including no declared `branch` on a real sync), no key, or a project Grails cannot read (its own tailwindcss missing, or of a major other than 3 and 4); for `connect`, a key of the wrong shape, an empty or unreadable clipboard, or no keychain on the machine | no: fix it |
|
|
195
|
+
| 3 | the server refused it: the key (also the one `connect` was given), the plan, content it does not accept, or a CLI older than the minimum (400) | no: fix it |
|
|
196
|
+
| 4 | the server could not be reached or failed (network, 5xx, a 429 after one retry); for `update`, the npm registry | yes |
|
|
155
197
|
|
|
156
198
|
### Where the tokens come from
|
|
157
199
|
|
|
@@ -170,8 +212,52 @@ red.
|
|
|
170
212
|
- JSON that does not parse.
|
|
171
213
|
- **What the API refuses comes back as findings** (422, or 413 for a limit). The run prints each one with the file the
|
|
172
214
|
person knows and its JSON pointer, and exits 3.
|
|
173
|
-
- **Usage.** Component uses
|
|
174
|
-
|
|
215
|
+
- **Usage.** Component uses go with the finalize, and so do the token uses a `var(--x)` can be tied to. Nothing in the
|
|
216
|
+
files says which token a custom property is, so the team says how its CSS is named:
|
|
217
|
+
|
|
218
|
+
"dtcg": { "files": [...], "cssVariables": { "prefix": "ds", "exceptions": { "--brand": "color.brand.500" }, "ignore": ["--radix-*", "--tw-*"] } }
|
|
219
|
+
|
|
220
|
+
- **The convention** is Style Dictionary's default, its `css` transform group (`name/kebab`): the token's path
|
|
221
|
+
segments joined with `-`, lower case, behind the build's `prefix` (`color.brand.500` is `--ds-color-brand-500`).
|
|
222
|
+
A digit stays with its letters (`2xl`, `body2`), camelCase is split, any other character is a separator, and a name
|
|
223
|
+
is case-sensitive. `cssVariableName` in `@grails/dtcg` is that rule, pinned against the real Style Dictionary.
|
|
224
|
+
- **The direction is token → name.** A name does not say its token (`brandPrimary` and `brand-primary` are two tokens and
|
|
225
|
+
one name), so the CLI names every token of the upload and looks a variable up in that index. A name
|
|
226
|
+
two tokens share is reported as ambiguous, never given to one of them.
|
|
227
|
+
- **`exceptions`** map a custom property that does not follow the convention to the token it is. They win over the
|
|
228
|
+
convention, and an exception whose token is not in the upload is reported (and the name does not fall back to the
|
|
229
|
+
convention).
|
|
230
|
+
- **`ignore`** is a list of patterns for custom properties that are not the team's tokens at all (a library's, the
|
|
231
|
+
framework's): `["--radix-*", "--tw-*"]`. A pattern starts with `--`, `*` is the only wildcard, and there are at most
|
|
232
|
+
200. A name it covers is taken out first: not resolved (so not sent, **even when the convention would have found a
|
|
233
|
+
token of that name**), not counted as used, not listed. An exception for the same name wins over `ignore`: it names a
|
|
234
|
+
token on purpose. A pattern that covers nothing used is not an error; `--json` lists it in
|
|
235
|
+
`css_variables.ignore_unused` so the team can delete it. The terminal line says how many were ignored, never which.
|
|
236
|
+
Use `exceptions` for what a name IS, `ignore` for "this is not ours".
|
|
237
|
+
- **The files are read here** with `readDtcg`, the reader the API runs, to know the tokens: `sync --dry-run` lists the
|
|
238
|
+
result with no key and sends nothing. A token goes to the API as `{ collection, name }` (the first level of its
|
|
239
|
+
path, and the rest joined with `/`), which the server turns back into the path with `tokenPath`; a token with a `/`
|
|
240
|
+
inside a level cannot be spelt that way and is reported.
|
|
241
|
+
- **Every `var(--x)` that resolves to no token is listed** (the terminal prints 20, `--json` carries 200 in
|
|
242
|
+
`css_variables`, the job summary folds them), with an exception that names a missing token. It never changes the
|
|
243
|
+
exit code.
|
|
244
|
+
- **What it reads.** A `var(--x)` a component can use, in three places, as `uses` outside the spec
|
|
245
|
+
(no hash moves, and the manifest keeps its shape and its version):
|
|
246
|
+
- the `style` of any element of the component, **every property** (`margin`, `border`, `boxShadow`, …), also `*Style`,
|
|
247
|
+
`css` and `sx` props, a constant or style sheet it refers to, and every branch of a condition or lookup;
|
|
248
|
+
- **the stylesheet its file imports** (`.css`, CSS modules, `.scss`, `.less`, `.pcss`), for every component that file
|
|
249
|
+
holds; a file two files import counts for both. Each file is read once per scan, only inside the project and never
|
|
250
|
+
under `node_modules`, at most 1 MiB, and 16 MiB of CSS in all (what is left out is a warning). `@import` is **not**
|
|
251
|
+
followed: a stylesheet that imports a tokens sheet would hand the component the aliases of the whole token layer.
|
|
252
|
+
A custom property a rule *declares* in `:root`, a theme selector or `@theme` is a token's definition, not a use;
|
|
253
|
+
- **Tailwind classes** in `className` and in `cn`, `clsx`, `classnames`, `cx`, `twMerge` and `cva` calls, variants
|
|
254
|
+
included: `bg-[var(--x)]` in either major, `bg-(--x)` (also `bg-(color:--x)`) in Tailwind 4 and `bg-[--x]` in
|
|
255
|
+
Tailwind 3. Measured on 4.2.1 and 3.4.17: 4 compiles `bg-[--x]` to a literal `--x`, and 3 does not know `bg-(--x)`,
|
|
256
|
+
so each major reads the form it compiles. With no Tailwind version found, the 4 form is read.
|
|
257
|
+
- **What it does not read.** A name a template assembles at run time, a class helper it does not know (`tv()`),
|
|
258
|
+
CSS-in-JS template literals, a component its file defines but does not export, a property set from JavaScript. Other
|
|
259
|
+
kinds of token reference (a Tailwind theme class such as `bg-primary`, a TypeScript token object) are counted, not
|
|
260
|
+
tied. A `bg-[var(--x)]` stays a literal colour in the spec itself, as before: only `uses` changed.
|
|
175
261
|
|
|
176
262
|
**Without `dtcg`, the scanner reads** (a convenience for a first sync):
|
|
177
263
|
|
|
@@ -215,15 +301,57 @@ add and lists the values left out, which are not in the files: the ones the scan
|
|
|
215
301
|
|
|
216
302
|
## The key
|
|
217
303
|
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
304
|
+
The CLI reads the API key from two places, in this order, and from nowhere else:
|
|
305
|
+
|
|
306
|
+
1. **`GRAILS_API_KEY` in the environment**: CI, and anyone who prefers it. When it is set the keychain is not even asked.
|
|
307
|
+
2. **The operating system's keychain**, where `grails-cli connect` put it.
|
|
308
|
+
|
|
309
|
+
With neither, a command stops with exit 2 and says to run `grails-cli connect` or set `GRAILS_API_KEY`.
|
|
310
|
+
|
|
311
|
+
A flag would land in the shell history, in the process list and in a CI log that prints the command line, so the CLI
|
|
312
|
+
refuses any flag that looks like a credential (`--api-key`, `--token`, …), and any argument that is itself a key,
|
|
313
|
+
before it reads anything. The key goes in one header, is never printed, and is scrubbed from everything the CLI
|
|
314
|
+
prints in case an upstream message echoes it.
|
|
222
315
|
|
|
223
316
|
Create it in **Team Settings › Integrations › API Keys** with the `write:code` scope. It is a personal key: if its
|
|
224
317
|
owner is demoted to viewer or leaves the team the key is **deleted**, and the API answers it like a key that never
|
|
225
318
|
existed (`GRAILS_AUTH_INVALID_TOKEN`). The CLI says so in those words.
|
|
226
319
|
|
|
320
|
+
### `connect` and `disconnect`
|
|
321
|
+
|
|
322
|
+
The API shows a key once, when it is created. Copy it, then:
|
|
323
|
+
|
|
324
|
+
```bash
|
|
325
|
+
grails-cli connect # reads the clipboard
|
|
326
|
+
grails-cli connect --prompt # asks for it instead; nothing is shown as you type, and the clipboard is not used
|
|
327
|
+
grails-cli disconnect # removes it from the keychain
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
- **It checks the key before keeping it.** The shape first (`grails_sk_` and 43 characters), then the same
|
|
331
|
+
authenticated read `status` makes. It prints the code sources of the key's project; Grails does not say the team's
|
|
332
|
+
name yet. A key Grails refuses (unknown, revoked, expired, without the `write:code` scope) stores **nothing**, and so
|
|
333
|
+
does one it could not ask about because the API was unreachable.
|
|
334
|
+
- **The clipboard is cleared once it has been read, whatever came of the key**, because a key Grails refuses may still be
|
|
335
|
+
a real one (for another environment, say). It is cleared only if it still holds what was read, so something you
|
|
336
|
+
copied meanwhile is left alone, and a clipboard with nothing like a key in it is not touched. Clipboard managers can
|
|
337
|
+
keep a history of what was copied, and nothing here can undo that: `--prompt` avoids the clipboard altogether.
|
|
338
|
+
- **No keychain, no key stored.** On a machine with none (a headless Linux box without `secret-tool`, a platform
|
|
339
|
+
without an integration) `connect` says so and stores nothing: use `GRAILS_API_KEY`. It checks this before it reads the
|
|
340
|
+
clipboard, so the key you copied is still there, and it never writes the key to a file.
|
|
341
|
+
- **The key never goes in an argument**, which every process on the machine can read. It reaches the keychain on standard
|
|
342
|
+
input of the platform's own tool, so there is no native module in the bundle:
|
|
343
|
+
|
|
344
|
+
| Platform | Keychain | Clipboard |
|
|
345
|
+
|---|---|---|
|
|
346
|
+
| macOS | the Keychain, through `/usr/bin/security -i` (the command is written to its stdin) | `pbpaste`, `pbcopy` |
|
|
347
|
+
| Linux | the Secret Service (GNOME Keyring, KWallet), through `secret-tool` from `libsecret-tools` | `wl-paste` and `wl-copy`, else `xclip` |
|
|
348
|
+
| Windows | the Credential Manager's `PasswordVault`, through Windows PowerShell (the key on its stdin) | `Get-Clipboard`, `Set-Clipboard` |
|
|
349
|
+
|
|
350
|
+
- **One entry per API.** The service is `grails-cli` and the account is the host of `GRAILS_API_URL`, so a staging key
|
|
351
|
+
and a production key coexist, and a `GRAILS_API_URL` that something else points at another server finds no key: a
|
|
352
|
+
stored key is never offered to an address you did not connect it to.
|
|
353
|
+
- `disconnect` removes the entry. It does not revoke the key: that is done in Team Settings.
|
|
354
|
+
|
|
227
355
|
`GRAILS_API_URL` points the CLI at another API (default `https://api.grails.design`; the docs' `…/v1` form is
|
|
228
356
|
accepted). `http://` is accepted only for this machine, and a redirect is never followed, so the key cannot be sent
|
|
229
357
|
to an address nobody chose.
|
|
@@ -233,6 +361,24 @@ rate limits but does not block yet, so no real `429` exists today: that retry is
|
|
|
233
361
|
behaviour. Opening a sync is retried only when the connection was *refused*: if the server may have received it, a
|
|
234
362
|
second open would be answered "another sync is running" and the first would hold the source for two hours.
|
|
235
363
|
|
|
364
|
+
## Updates
|
|
365
|
+
|
|
366
|
+
Once a day, `grails-cli` asks the npm registry whether a newer version exists: one plain
|
|
367
|
+
`GET https://registry.npmjs.org/grails-cli/latest`, with a 1.5 second limit, no retry, no header of its own, no query,
|
|
368
|
+
and nothing that says who is asking (not the key, the team, the project or the repository). It never fails or delays a
|
|
369
|
+
command beyond that limit; offline is silence. The answer is remembered in `update-check.json` in the config folder
|
|
370
|
+
(`$XDG_CONFIG_HOME/grails-cli`, else `~/.config/grails-cli`; `%APPDATA%\grails-cli` on Windows), a file with no secret in it.
|
|
371
|
+
|
|
372
|
+
While the installed version is behind, **every run** ends with one line on stderr that names both versions and the
|
|
373
|
+
command. CI included: a workflow that pins a version is where nobody sees a release go by. A comparison follows
|
|
374
|
+
semver, so `0.5.0-rc.1` is behind `0.5.0`. `GRAILS_CLI_NO_UPDATE_CHECK=1` turns it off.
|
|
375
|
+
|
|
376
|
+
`grails-cli update [root]` asks now, ignoring the cache, and prints the command for the project's package manager,
|
|
377
|
+
read from the lockfile in the root: `pnpm-lock.yaml` is `pnpm add -D grails-cli@latest`, `yarn.lock` is
|
|
378
|
+
`yarn add -D grails-cli@latest`, anything else is `npm i -D grails-cli@latest`; a project that does not depend on
|
|
379
|
+
`grails-cli` gets `npx grails-cli@latest`. **It does not run it**: the CLI never changes your repository on its own.
|
|
380
|
+
It exits 0 whether you are behind or not, and 4 when the registry cannot be reached.
|
|
381
|
+
|
|
236
382
|
## What leaves the repository
|
|
237
383
|
|
|
238
384
|
An allow-list, built field by field (never a copy with fields deleted), enforced again by the
|