grails-cli 0.0.0-stage → 0.3.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/LICENSE.md +250 -0
- package/README.md +279 -2
- package/dist/cli.js +28652 -0
- package/package.json +45 -4
- package/templates/grails-cli-sync.yml +56 -0
- package/templates/skill/grails-cli/SKILL.md +150 -0
package/LICENSE.md
ADDED
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
# Grails CLI: terms of use
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Grails Services LLC. All rights reserved.
|
|
4
|
+
|
|
5
|
+
`grails-cli` (the "Software") is proprietary software. It is not open source.
|
|
6
|
+
|
|
7
|
+
## What you may do
|
|
8
|
+
|
|
9
|
+
You may install the Software and run it, free of charge, to work with Grails (https://grails.design): to read your
|
|
10
|
+
own code and send its design tokens and component specs to your own Grails account, from your computer or from your
|
|
11
|
+
CI. Using Grails itself is covered by the Grails Terms of Service (https://grails.design/terms).
|
|
12
|
+
|
|
13
|
+
## What you may not do
|
|
14
|
+
|
|
15
|
+
No other right is granted. Without the written permission of Grails Services LLC you may not:
|
|
16
|
+
|
|
17
|
+
- copy the Software, except as installing it requires;
|
|
18
|
+
- modify it, or make anything derived from it;
|
|
19
|
+
- redistribute, sublicense, sell, rent or host it;
|
|
20
|
+
- use it for anything other than working with Grails.
|
|
21
|
+
|
|
22
|
+
## No warranty
|
|
23
|
+
|
|
24
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
|
|
25
|
+
WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL GRAILS SERVICES LLC BE LIABLE
|
|
26
|
+
FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR ITS USE.
|
|
27
|
+
|
|
28
|
+
## Third-party software
|
|
29
|
+
|
|
30
|
+
The Grails terms above do not apply to the open-source packages that `dist/cli.js` includes. Each is used under its
|
|
31
|
+
own license, reproduced below as that license requires. (`typescript` is not included: it is installed next to the
|
|
32
|
+
Software as a dependency, under its own license.) The CLI also includes the Design Tokens Community Group's JSON
|
|
33
|
+
Schemas for the format and the resolver, version 2025.10, published at https://www.designtokens.org.
|
|
34
|
+
|
|
35
|
+
### ajv 8.20.0 (MIT)
|
|
36
|
+
|
|
37
|
+
The MIT License (MIT)
|
|
38
|
+
|
|
39
|
+
Copyright (c) 2015-2021 Evgeny Poberezkin
|
|
40
|
+
|
|
41
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
42
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
43
|
+
in the Software without restriction, including without limitation the rights
|
|
44
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
45
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
46
|
+
furnished to do so, subject to the following conditions:
|
|
47
|
+
|
|
48
|
+
The above copyright notice and this permission notice shall be included in all
|
|
49
|
+
copies or substantial portions of the Software.
|
|
50
|
+
|
|
51
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
52
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
53
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
54
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
55
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
56
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
57
|
+
SOFTWARE.
|
|
58
|
+
|
|
59
|
+
### ajv-formats 3.0.1 (MIT)
|
|
60
|
+
|
|
61
|
+
MIT License
|
|
62
|
+
|
|
63
|
+
Copyright (c) 2020 Evgeny Poberezkin
|
|
64
|
+
|
|
65
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
66
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
67
|
+
in the Software without restriction, including without limitation the rights
|
|
68
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
69
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
70
|
+
furnished to do so, subject to the following conditions:
|
|
71
|
+
|
|
72
|
+
The above copyright notice and this permission notice shall be included in all
|
|
73
|
+
copies or substantial portions of the Software.
|
|
74
|
+
|
|
75
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
76
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
77
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
78
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
79
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
80
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
81
|
+
SOFTWARE.
|
|
82
|
+
|
|
83
|
+
### colorjs.io 0.7.1 (MIT)
|
|
84
|
+
|
|
85
|
+
MIT License
|
|
86
|
+
|
|
87
|
+
Copyright (c) 2021 Lea Verou, Chris Lilley
|
|
88
|
+
|
|
89
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
90
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
91
|
+
in the Software without restriction, including without limitation the rights
|
|
92
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
93
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
94
|
+
furnished to do so, subject to the following conditions:
|
|
95
|
+
|
|
96
|
+
The above copyright notice and this permission notice shall be included in all
|
|
97
|
+
copies or substantial portions of the Software.
|
|
98
|
+
|
|
99
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
100
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
101
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
102
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
103
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
104
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
105
|
+
SOFTWARE.
|
|
106
|
+
|
|
107
|
+
### fast-deep-equal 3.1.3 (MIT)
|
|
108
|
+
|
|
109
|
+
MIT License
|
|
110
|
+
|
|
111
|
+
Copyright (c) 2017 Evgeny Poberezkin
|
|
112
|
+
|
|
113
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
114
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
115
|
+
in the Software without restriction, including without limitation the rights
|
|
116
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
117
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
118
|
+
furnished to do so, subject to the following conditions:
|
|
119
|
+
|
|
120
|
+
The above copyright notice and this permission notice shall be included in all
|
|
121
|
+
copies or substantial portions of the Software.
|
|
122
|
+
|
|
123
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
124
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
125
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
126
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
127
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
128
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
129
|
+
SOFTWARE.
|
|
130
|
+
|
|
131
|
+
### fast-uri 3.1.8 (BSD-3-Clause)
|
|
132
|
+
|
|
133
|
+
Copyright (c) 2011-2021, Gary Court until https://github.com/garycourt/uri-js/commit/a1acf730b4bba3f1097c9f52e7d9d3aba8cdcaae
|
|
134
|
+
Copyright (c) 2021-present The Fastify team <https://github.com/fastify/fastify#team>
|
|
135
|
+
All rights reserved.
|
|
136
|
+
|
|
137
|
+
Redistribution and use in source and binary forms, with or without
|
|
138
|
+
modification, are permitted provided that the following conditions are met:
|
|
139
|
+
* Redistributions of source code must retain the above copyright
|
|
140
|
+
notice, this list of conditions and the following disclaimer.
|
|
141
|
+
* Redistributions in binary form must reproduce the above copyright
|
|
142
|
+
notice, this list of conditions and the following disclaimer in the
|
|
143
|
+
documentation and/or other materials provided with the distribution.
|
|
144
|
+
* The names of any contributors may not be used to endorse or promote
|
|
145
|
+
products derived from this software without specific prior written
|
|
146
|
+
permission.
|
|
147
|
+
|
|
148
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
149
|
+
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
150
|
+
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
151
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY
|
|
152
|
+
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
153
|
+
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
154
|
+
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
155
|
+
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
156
|
+
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
157
|
+
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
158
|
+
|
|
159
|
+
* * *
|
|
160
|
+
|
|
161
|
+
The complete list of contributors can be found at:
|
|
162
|
+
- https://github.com/garycourt/uri-js/graphs/contributors
|
|
163
|
+
|
|
164
|
+
### json-schema-traverse 1.0.0 (MIT)
|
|
165
|
+
|
|
166
|
+
MIT License
|
|
167
|
+
|
|
168
|
+
Copyright (c) 2017 Evgeny Poberezkin
|
|
169
|
+
|
|
170
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
171
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
172
|
+
in the Software without restriction, including without limitation the rights
|
|
173
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
174
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
175
|
+
furnished to do so, subject to the following conditions:
|
|
176
|
+
|
|
177
|
+
The above copyright notice and this permission notice shall be included in all
|
|
178
|
+
copies or substantial portions of the Software.
|
|
179
|
+
|
|
180
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
181
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
182
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
183
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
184
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
185
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
186
|
+
SOFTWARE.
|
|
187
|
+
|
|
188
|
+
### nanoid 3.3.20 (MIT)
|
|
189
|
+
|
|
190
|
+
The MIT License (MIT)
|
|
191
|
+
|
|
192
|
+
Copyright 2017 Andrey Sitnik <andrey@sitnik.ru>
|
|
193
|
+
|
|
194
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
|
195
|
+
this software and associated documentation files (the "Software"), to deal in
|
|
196
|
+
the Software without restriction, including without limitation the rights to
|
|
197
|
+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
|
198
|
+
the Software, and to permit persons to whom the Software is furnished to do so,
|
|
199
|
+
subject to the following conditions:
|
|
200
|
+
|
|
201
|
+
The above copyright notice and this permission notice shall be included in all
|
|
202
|
+
copies or substantial portions of the Software.
|
|
203
|
+
|
|
204
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
205
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
|
206
|
+
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
|
207
|
+
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
|
208
|
+
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
209
|
+
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
210
|
+
|
|
211
|
+
### picocolors 1.1.1 (ISC)
|
|
212
|
+
|
|
213
|
+
ISC License
|
|
214
|
+
|
|
215
|
+
Copyright (c) 2021-2024 Oleksii Raspopov, Kostiantyn Denysov, Anton Verinov
|
|
216
|
+
|
|
217
|
+
Permission to use, copy, modify, and/or distribute this software for any
|
|
218
|
+
purpose with or without fee is hereby granted, provided that the above
|
|
219
|
+
copyright notice and this permission notice appear in all copies.
|
|
220
|
+
|
|
221
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
222
|
+
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
223
|
+
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
224
|
+
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
225
|
+
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
226
|
+
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
227
|
+
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
228
|
+
|
|
229
|
+
### postcss 8.5.28 (MIT)
|
|
230
|
+
|
|
231
|
+
The MIT License (MIT)
|
|
232
|
+
|
|
233
|
+
Copyright 2013 Andrey Sitnik <andrey@sitnik.es>
|
|
234
|
+
|
|
235
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
|
236
|
+
this software and associated documentation files (the "Software"), to deal in
|
|
237
|
+
the Software without restriction, including without limitation the rights to
|
|
238
|
+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
|
239
|
+
the Software, and to permit persons to whom the Software is furnished to do so,
|
|
240
|
+
subject to the following conditions:
|
|
241
|
+
|
|
242
|
+
The above copyright notice and this permission notice shall be included in all
|
|
243
|
+
copies or substantial portions of the Software.
|
|
244
|
+
|
|
245
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
246
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
|
247
|
+
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
|
248
|
+
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
|
249
|
+
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
250
|
+
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,280 @@
|
|
|
1
|
-
#
|
|
1
|
+
# grails-cli
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Grails Code Sync from the command line: `grails-cli` reads a component library's code and uploads its design
|
|
4
|
+
tokens and each component's spec to [Grails](https://grails.design), so the vault shows what the code actually
|
|
5
|
+
holds, next to Figma. The product is Grails Code Sync the way the Figma plugin is Grails Sync; this is its
|
|
6
|
+
command line. Full documentation: https://docs.grails.design/cli/overview/
|
|
7
|
+
|
|
8
|
+
**Requirements:** Node 20 or newer. The package is free to install and use with Grails; see `LICENSE.md`.
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
npm i -D grails-cli # or run it without installing: npx grails-cli <command>
|
|
12
|
+
|
|
13
|
+
npx grails-cli scan /path/to/library # the manifest of components
|
|
14
|
+
npx grails-cli tokens /path/to/library # the design tokens Grails would read; sends nothing
|
|
15
|
+
npx grails-cli sync /path/to/library --dry-run # what would be uploaded; sends nothing, needs no key
|
|
16
|
+
|
|
17
|
+
export GRAILS_API_KEY=grails_sk_… # see "The key"
|
|
18
|
+
npx grails-cli sync /path/to/library
|
|
19
|
+
npx grails-cli status
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
The examples below write `grails-cli`; use `npx grails-cli` if it is not installed globally.
|
|
23
|
+
|
|
24
|
+
## Commands
|
|
25
|
+
|
|
26
|
+
| Command | What it does |
|
|
27
|
+
|---|---|
|
|
28
|
+
| `scan [root] [--out f] [--profile f] [--tier syntax\|typed] [--in-repo]` | The manifest (stdout without `--out`). Refuses to write it inside a git repository unless `--in-repo`. Exits 1 when the scan is incomplete. |
|
|
29
|
+
| `chi [root] [--profile f] [--tier …] [--json]` | The Component Health Index of every component, worst first. |
|
|
30
|
+
| `check-classes [root] [--unknown] [--json]` | Probable Tailwind typos; exits 1 when there is one. |
|
|
31
|
+
| `init [root] [--write] [--force]` | Proposes a `grails.code.json` from the code, with the evidence. |
|
|
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
|
+
| `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
|
+
| `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"). |
|
|
37
|
+
| `rules [root] [--set code=figma \| --delete code \| --threshold n] [--json]` | The naming rules of the Code ↔ Figma match (see "Naming rules"). |
|
|
38
|
+
| `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
|
+
|
|
40
|
+
### `sync`
|
|
41
|
+
|
|
42
|
+
**Only from the declared branch.** `grails.code.json` says which branch IS the
|
|
43
|
+
source in Grails: `"branch": "main"` (or `preview`, or whatever merges land on); `init` proposes the branch the
|
|
44
|
+
checkout is on. A real sync:
|
|
45
|
+
- **without a declared branch** is refused before anything is read (exit 2), with that sentence;
|
|
46
|
+
- **on another branch** uploads nothing and exits **0**, so a pull request's job stays green, and says so where a CI
|
|
47
|
+
shows it: the terminal, the job summary, a `::notice` on the run, and the step outputs `result=skipped-branch` and
|
|
48
|
+
`exit_code=0` (`GITHUB_OUTPUT`). A detached checkout is judged by `CI_COMMIT_REF_NAME` / `GITHUB_REF_NAME`, so a
|
|
49
|
+
GitHub pull request run (`<number>/merge`) is "another branch"; a folder that is not a checkout has no branch;
|
|
50
|
+
- **on the declared branch** syncs as below, and writes `result=synced` (or `failed`, with its exit code).
|
|
51
|
+
A dry run is never held back: it stores nothing. The server enforces the version that carries this guard: the two
|
|
52
|
+
storing routes refuse a CLI older than 0.2.0 with 400 `GRAILS_CODE_CLI_OUTDATED` (exit 3).
|
|
53
|
+
|
|
54
|
+
**Tokens go first.** The token list is read and
|
|
55
|
+
recorded with `POST /v1/code/token-syncs` in one request. Grails builds the DTCG document from it and answers what
|
|
56
|
+
changed against the source's previous snapshot; the same content is stored once. A token step that fails (a
|
|
57
|
+
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. Then the components:
|
|
59
|
+
|
|
60
|
+
1. **Opens** the sync with every component key the scan found (`declared_keys`). That is how the server tells a
|
|
61
|
+
component deleted from the code from one that failed to arrive.
|
|
62
|
+
2. **Sends** the components in requests of at most 50 components and 1 MB (the API refuses more than 50, and
|
|
63
|
+
accepts 2 MB). Grails answers the open with the hash of the spec it already holds for each key; the spec
|
|
64
|
+
travels **only** for a key that is new or whose hash differs.
|
|
65
|
+
3. **Finalizes** with the scan's own `complete`. Only a complete scan lets the server mark anything deleted, and
|
|
66
|
+
even then it refuses to delete 5 or more components and more than 20 % of the source in one sync.
|
|
67
|
+
Since 0.3.0 the finalize also carries **what each component uses** (canonical entities D1): the components of
|
|
68
|
+
the same source it renders, resolved with the TypeScript checker to their keys, and the tokens its styles
|
|
69
|
+
reference, as the collection and name of the token list just recorded. Grails keeps them as edges of the
|
|
70
|
+
project graph. They are sent only when the tokens were read and recorded (or there are none): otherwise Grails
|
|
71
|
+
keeps the usage it had. A reference no token of the source answers to (a framework default, a source missing
|
|
72
|
+
from `grails.code.json`) is counted in a warning and not sent.
|
|
73
|
+
|
|
74
|
+
If a sync fails after it was opened, the CLI closes it as incomplete before raising the error. Otherwise it stays
|
|
75
|
+
open for two hours and the next run of the same source (a CI retry) is answered "another sync is running".
|
|
76
|
+
|
|
77
|
+
The **source** (what Grails calls this library) is named after the package's own `name`, so it is the same on a
|
|
78
|
+
laptop and in CI; `--name` sets another one, for a monorepo whose packages share nothing else. The platform is
|
|
79
|
+
`react-native` when the package depends on React Native or Expo or any file imports `react-native`, and `web`
|
|
80
|
+
otherwise; `--platform` overrides. The commit and branch come from git when the root is in a checkout (the branch
|
|
81
|
+
from `CI_COMMIT_REF_NAME` or `GITHUB_REF_NAME` when the checkout is detached, as GitLab's is). `--trigger` is `ci`
|
|
82
|
+
when `CI=true` and `local` otherwise.
|
|
83
|
+
|
|
84
|
+
### `--dry-run`, `--json` and the job summary
|
|
85
|
+
|
|
86
|
+
- **`--dry-run` with `GRAILS_API_KEY` set** asks Grails what each step would change, through the preview routes
|
|
87
|
+
(`POST /v1/code/token-syncs:preview`, `POST /v1/code/syncs:preview`), which store nothing: tokens added, changed
|
|
88
|
+
and removed; components added, changed, reactivated and **those that would be deleted**, with the same floor the
|
|
89
|
+
real sync applies.
|
|
90
|
+
- **`--dry-run` without the key** counts locally and sends nothing, as it always did.
|
|
91
|
+
- **`--json`** prints one report on stdout (`mode`, `tokens`, `components`, `exit_code`); the human lines go to
|
|
92
|
+
stderr, so the output can be piped.
|
|
93
|
+
- **In GitHub Actions** (`GITHUB_STEP_SUMMARY` is set) the same report is appended to the job summary: counts,
|
|
94
|
+
and the changed tokens and would-be-deleted components folded. There is no comment on a pull request.
|
|
95
|
+
|
|
96
|
+
No state is written to the repository: what Grails holds is the only source of truth, and the comparison is
|
|
97
|
+
always against it.
|
|
98
|
+
|
|
99
|
+
### Code ↔ Figma
|
|
100
|
+
|
|
101
|
+
After a real sync records the tokens, `sync` asks Grails how they compare with the Figma ones
|
|
102
|
+
(`GET /v1/code/token-match`) and prints one line: same, differ, conflicts, by value only, only in code, only in
|
|
103
|
+
Figma, and the colour threshold it used (ΔE2000, 1.5 by default). The job summary adds the table, what differs,
|
|
104
|
+
the conflicts (code tokens that disagree about one design token, never resolved) and the **suggested** naming
|
|
105
|
+
rules, which are never applied: naming rules live in Grails, not in this repository.
|
|
106
|
+
|
|
107
|
+
- A project with no Figma snapshot yet says so instead of calling every token missing.
|
|
108
|
+
- A dry run does not ask: Grails would compare the code tokens it already stores, not the ones on disk.
|
|
109
|
+
- It is a report, not a step of the sync: a failure is said and kept in the report, and the exit code does not change.
|
|
110
|
+
|
|
111
|
+
### Naming rules
|
|
112
|
+
|
|
113
|
+
A naming rule tells the match that a word of your code is another word in Figma: `gray` in the code is `neutral`
|
|
114
|
+
in Figma. Rules belong to one source and live in Grails, never in the repository: the CLI,
|
|
115
|
+
the REST API and MCP all edit the same rules.
|
|
116
|
+
|
|
117
|
+
grails-cli rules # this source's rules and the project's colour threshold
|
|
118
|
+
grails-cli rules --set gray=neutral # create, or move its target; the same rule twice is one rule
|
|
119
|
+
grails-cli rules --delete gray
|
|
120
|
+
grails-cli rules --threshold 2 # ΔE2000 under which two colours are equal, for the whole project
|
|
121
|
+
|
|
122
|
+
The source is named as `sync` names it (`--name`, else the package name, else the folder). Listing needs the
|
|
123
|
+
usual key; editing needs a key with the `write:tokens` scope. The job summary of a sync lists the rules applied
|
|
124
|
+
and the ones Grails suggests from tokens with the same value and one different word.
|
|
125
|
+
|
|
126
|
+
### Changes asked for in Grails
|
|
127
|
+
|
|
128
|
+
In Grails, an admin or an editor can ask for changes to this repository's tokens: a new value, a new token,
|
|
129
|
+
collection or mode, a removal. **Grails never writes your repository, and neither does this CLI.**
|
|
130
|
+
|
|
131
|
+
grails-cli tokens pending # by collection, before → after, as DTCG values
|
|
132
|
+
grails-cli tokens pending --json # the list as Grails answers it, every value exact
|
|
133
|
+
grails-cli tokens pending --limit 1000 # the read answers 500 by default, at most 1000
|
|
134
|
+
|
|
135
|
+
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). ⚠️ **A change carried over still shows as pending for now:** marking it applied when a sync
|
|
137
|
+
shows the repository says the same is designed but not live yet (it comes with the token comparison in the sync).
|
|
138
|
+
|
|
139
|
+
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). It needs the usual key (`write:code`).
|
|
141
|
+
|
|
142
|
+
### Exit codes
|
|
143
|
+
|
|
144
|
+
A contract a CI step can branch on. Only 4 is worth retrying. A sync on another branch is 0; its own
|
|
145
|
+
result, `skipped-branch`, is in the step output `result` (see `sync` above), since any other code would turn the step
|
|
146
|
+
red.
|
|
147
|
+
|
|
148
|
+
| code | meaning | retry? |
|
|
149
|
+
|---|---|---|
|
|
150
|
+
| 0 | done: synced, nothing changed, or a dry run | – |
|
|
151
|
+
| 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 |
|
|
155
|
+
|
|
156
|
+
### Where the tokens come from
|
|
157
|
+
|
|
158
|
+
**The team's own DTCG files, when `grails.code.json` names them** (the official way in):
|
|
159
|
+
|
|
160
|
+
"dtcg": { "files": ["tokens/**/*.json"], "resolver": "tokens/resolver.json" }
|
|
161
|
+
|
|
162
|
+
- **One source.** With `dtcg`, these files are the only token source. Nothing is read from Tailwind, CSS or `tokens`,
|
|
163
|
+
and a run that also lists `css` or `tokens` says so.
|
|
164
|
+
- **Sent as written.** `sync` uploads them as they are (`dtcg: { files, resolver? }` with `client: grails-cli`), and
|
|
165
|
+
the API reads them with `@grails/dtcg`'s `readDtcg`.
|
|
166
|
+
- **Checked here first**, so a wrong repository sends nothing (exit 2):
|
|
167
|
+
- the file count (500) and the body size (4 MiB);
|
|
168
|
+
- a file outside the resolver's folder (a resolver names its files from where it is, so every key is relative to it);
|
|
169
|
+
- two keys that differ only in case;
|
|
170
|
+
- JSON that does not parse.
|
|
171
|
+
- **What the API refuses comes back as findings** (422, or 413 for a limit). The run prints each one with the file the
|
|
172
|
+
person knows and its JSON pointer, and exits 3.
|
|
173
|
+
- **Usage.** Component uses still go with the finalize. Token uses do not: nothing tells which DTCG token a style's
|
|
174
|
+
`var(--x)` is, and guessing it from the spelling would be a heuristic, so the CLI does not. The run says so.
|
|
175
|
+
|
|
176
|
+
**Without `dtcg`, the scanner reads** (a convenience for a first sync):
|
|
177
|
+
|
|
178
|
+
- **A Tailwind 3 theme**, read with the **project's own** `tailwindcss` (`loadConfig` + `resolveConfig`), defaults
|
|
179
|
+
included. ⚠️ That executes `tailwind.config.*`, as the project's build does. Install the project's dependencies
|
|
180
|
+
first (`npm ci`): without them the run exits 2, and never reads the theme with a copy of its own.
|
|
181
|
+
- **Tailwind 4**: the `@theme` stylesheet, read with the project's `tailwindcss` (`__unstable__loadDesignSystem`),
|
|
182
|
+
defaults included; its CSS variables become a collection with a mode per selector (`:root` light, `.dark` dark).
|
|
183
|
+
- **CSS files** listed in `grails.code.json`'s `css`, read with postcss.
|
|
184
|
+
- **TypeScript token objects and enums** listed in `grails.code.json`'s `tokens`.
|
|
185
|
+
|
|
186
|
+
`calc()` is computed (DTCG has no expressions). Values it cannot express are left out and listed, never guessed.
|
|
187
|
+
|
|
188
|
+
**Text styles** are tokens too (`typography`, one per style, all five fields): a Tailwind 4 `--text-X` with its
|
|
189
|
+
`--text-X--line-height`, `--letter-spacing` and `--font-weight`; a Tailwind 3 `theme.fontSize` entry (`[size]`,
|
|
190
|
+
`[size, lineHeight]` or `[size, { lineHeight, letterSpacing, fontWeight }]`); or a TypeScript object with a
|
|
191
|
+
`fontSize` and another typography key. Each field says where it comes from: the style's own value, a token it
|
|
192
|
+
inherits (the family of `fontFamily.sans`), a CSS rule that is not a token (`body { font-family }`), or CSS's initial
|
|
193
|
+
value (weight 400, letter spacing 0). An `em` letter spacing is converted against the size and keeps its spelling. A
|
|
194
|
+
family written as a `next/font` variable becomes the font the layout names, marked as a heuristic. A style that
|
|
195
|
+
cannot be formed (no family to inherit, a size that is not a length) is left out and listed.
|
|
196
|
+
|
|
197
|
+
#### Moving from the scanner to DTCG: `tokens baseline`
|
|
198
|
+
|
|
199
|
+
grails-cli tokens baseline --out ../acme-tokens # outside the repository: nothing else to say
|
|
200
|
+
grails-cli tokens baseline --out tokens --in-repo # inside it: only because you asked
|
|
201
|
+
|
|
202
|
+
The scanner's tokens, written as the DTCG files and the resolver Grails' own export writes (`exportProject` from
|
|
203
|
+
`@grails/dtcg`, through the same conversion the API runs, `fromCodeUpload`). It is the team's first DTCG version
|
|
204
|
+
: review the files, commit them, and declare them in `dtcg`. From then on the team owns its
|
|
205
|
+
generation rules and the scanner stops being the source. The command prints the `dtcg` block to
|
|
206
|
+
add and lists the values left out, which are not in the files: the ones the scanner could not express (the list
|
|
207
|
+
`tokens` prints, with the reason for each) and what the DTCG export could not carry.
|
|
208
|
+
|
|
209
|
+
- **It writes only where `--out` says.** A folder inside the git repository also needs `--in-repo`, exactly like
|
|
210
|
+
`scan --out`. A folder that already has files is refused: a baseline never overwrites anything.
|
|
211
|
+
- **It refuses while `dtcg` is set.** Those files are the token source, and a baseline from the scanner would compete
|
|
212
|
+
with them (exit 2).
|
|
213
|
+
- **A round trip pins it:** what `readDtcg` reads back from the files equals
|
|
214
|
+
`snapshotProject` of the scanner's upload, a text style included: its five fields and where each comes from.
|
|
215
|
+
|
|
216
|
+
## The key
|
|
217
|
+
|
|
218
|
+
`GRAILS_API_KEY` is read from the environment **and from nowhere else**. A flag would land in the shell history,
|
|
219
|
+
in the process list and in a CI log that prints the command line, so the CLI refuses any flag that looks like a
|
|
220
|
+
credential (`--api-key`, `--token`, …) before it reads anything. It goes in one header, is never printed, and is
|
|
221
|
+
scrubbed from everything the CLI prints in case an upstream message echoes it.
|
|
222
|
+
|
|
223
|
+
Create it in **Team Settings › Integrations › API Keys** with the `write:code` scope. It is a personal key: if its
|
|
224
|
+
owner is demoted to viewer or leaves the team the key is **deleted**, and the API answers it like a key that never
|
|
225
|
+
existed (`GRAILS_AUTH_INVALID_TOKEN`). The CLI says so in those words.
|
|
226
|
+
|
|
227
|
+
`GRAILS_API_URL` points the CLI at another API (default `https://api.grails.design`; the docs' `…/v1` form is
|
|
228
|
+
accepted). `http://` is accepted only for this machine, and a redirect is never followed, so the key cannot be sent
|
|
229
|
+
to an address nobody chose.
|
|
230
|
+
|
|
231
|
+
One retry on a network error, and one on a `429` after the `retry_after_seconds` the API gives. The Worker counts
|
|
232
|
+
rate limits but does not block yet, so no real `429` exists today: that retry is the contract's, not a measured
|
|
233
|
+
behaviour. Opening a sync is retried only when the connection was *refused*: if the server may have received it, a
|
|
234
|
+
second open would be answered "another sync is running" and the first would hold the source for two hours.
|
|
235
|
+
|
|
236
|
+
## What leaves the repository
|
|
237
|
+
|
|
238
|
+
An allow-list, built field by field (never a copy with fields deleted), enforced again by the
|
|
239
|
+
API and a last time by the database:
|
|
240
|
+
|
|
241
|
+
- the source: name, platform, package name, and the repository address **without userinfo, query or fragment**
|
|
242
|
+
(`https://gitlab-ci-token:<token>@gitlab.com/…` and `?private_token=…` are what a CI checkout's remote can look
|
|
243
|
+
like; an SSH remote becomes its `https://` address, or is dropped);
|
|
244
|
+
- git: the commit sha and the branch;
|
|
245
|
+
- per component: key (`<file>#<export>`), name, file path, export name, the spec hash and **the spec**, the
|
|
246
|
+
stories summary (files, titles, count, tags), the coverage numbers, `unresolved` as `{ what, line }` and other
|
|
247
|
+
props as `{ name, kind }`;
|
|
248
|
+
- at finalize, per component: the keys of the components of the same source it renders, and the collection and
|
|
249
|
+
name of each token its styles reference.
|
|
250
|
+
|
|
251
|
+
The spec is the component's public API: prop names, their values, types as declared, anatomy names and resolved
|
|
252
|
+
style values. It is what a design system documents. **Not** sent: source code, the CHI (the server computes it),
|
|
253
|
+
`unresolved[].detail` (it quotes a fragment of the code), `otherProps[].tsType`, the scan root, any absolute path.
|
|
254
|
+
`sync --dry-run` prints what would go, in counts and bytes, and sends nothing.
|
|
255
|
+
|
|
256
|
+
**Tokens:** token names, their declared type, a literal or an alias per mode, and the file each came from, relative
|
|
257
|
+
to the root. Not sent: file contents, comments, absolute paths, or any environment value a Tailwind config reads
|
|
258
|
+
outside its theme. `grails-cli tokens --json` prints exactly what would go.
|
|
259
|
+
|
|
260
|
+
**With `dtcg`, the files go whole.** The token half sends the DTCG files `dtcg.files` matches, and the resolver, as
|
|
261
|
+
they are written: every `$description`, `$extensions` and `$deprecated` in them goes too. Grails keeps the
|
|
262
|
+
descriptions and the extensions with each token. Their keys are paths relative to the resolver's folder (or to the
|
|
263
|
+
root), never absolute. Only the files the globs match leave; `node_modules` and dot folders are never walked.
|
|
264
|
+
`grails-cli tokens --json` prints the upload exactly.
|
|
265
|
+
|
|
266
|
+
## The GitHub workflow
|
|
267
|
+
|
|
268
|
+
`templates/grails-cli-sync.yml` (in this package, and shown in Grails under Team Settings › Integrations) runs
|
|
269
|
+
`npx --yes grails-cli sync --trigger ci` on a push to the branch under `branches:` (`main` by default), which must
|
|
270
|
+
be the one `grails.code.json` declares. Copy it to `.github/workflows/grails-cli-sync.yml`. Its step has
|
|
271
|
+
`id: grails`, so a later step reads `steps.grails.outputs.result`.
|
|
272
|
+
The key reaches it through the step's `env:` from a repository secret named `GRAILS_API_KEY`, and never appears in a
|
|
273
|
+
`run:` line. Do not run it on `pull_request`: a fork's run has no secrets and no business syncing.
|
|
274
|
+
|
|
275
|
+
A new release of the CLI can change what a push uploads. To choose when that happens, pin a version in the
|
|
276
|
+
workflow: `npx --yes grails-cli@<version> sync --trigger ci`.
|
|
277
|
+
|
|
278
|
+
## License
|
|
279
|
+
|
|
280
|
+
Proprietary. You may install and use `grails-cli` to work with Grails; no other right is granted. See `LICENSE.md`.
|