@quatronic/qore 0.1.0 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +224 -2
- package/dist/cli.js +2136 -278
- package/dist/cli.js.map +1 -1
- package/package.json +3 -1
- package/schema/qore.schema.json +17 -0
package/README.md
CHANGED
|
@@ -20,6 +20,9 @@ qore tenant list # list tenants you have access to
|
|
|
20
20
|
qore tenant use <slug> # set the active tenant
|
|
21
21
|
qore logout
|
|
22
22
|
qore version # CLI + portal version info
|
|
23
|
+
|
|
24
|
+
qore env pull # write this module's local secrets into an env file
|
|
25
|
+
qore run -- <command> # run a command with those secrets in its environment
|
|
23
26
|
```
|
|
24
27
|
|
|
25
28
|
### Portals
|
|
@@ -33,6 +36,225 @@ it reports).
|
|
|
33
36
|
qore login --portal staging
|
|
34
37
|
```
|
|
35
38
|
|
|
39
|
+
### Repository context
|
|
40
|
+
|
|
41
|
+
Commands like `qore status` need to know which module you mean. By default
|
|
42
|
+
that comes from the git remote of the repository you're standing in:
|
|
43
|
+
`qore` reads `git remote -v`, prefers `origin`, falls back to `upstream`,
|
|
44
|
+
then the rest in git order, and resolves the remote's URL to a module on
|
|
45
|
+
the portal. Working from a personal fork whose `origin` the portal doesn't
|
|
46
|
+
know? As long as an `upstream` remote points at the real repository, that one
|
|
47
|
+
resolves — the context line then says `(upstream)`.
|
|
48
|
+
|
|
49
|
+
Pass `--module <slug|id>` to pick a module explicitly; the flag always wins
|
|
50
|
+
over the git remote. Inside a repository, `qore` still looks up the remote's
|
|
51
|
+
own module (from the cache, or with one extra request) only so the context
|
|
52
|
+
line can tell you when the flag overrides it. If a module is linked to more
|
|
53
|
+
than one app you can read, add `--app <slug|id>` to disambiguate.
|
|
54
|
+
|
|
55
|
+
Every context-aware command prints a context line to stderr (never stdout,
|
|
56
|
+
so it's safe to pipe `--json` output):
|
|
57
|
+
|
|
58
|
+
```
|
|
59
|
+
→ <Tenant> / <App> / <module> (<source>)
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
`<source>` is the remote name that resolved it (`origin`, `upstream`, …),
|
|
63
|
+
or `--module` when you passed the flag. If `--module` resolves to a
|
|
64
|
+
different module than the repository's own remote would have, it reads
|
|
65
|
+
`--module, overrides <remote>`. With `--json`, this same information is
|
|
66
|
+
under `data.context` instead, and nothing about it goes to stderr.
|
|
67
|
+
|
|
68
|
+
#### `qore status`
|
|
69
|
+
|
|
70
|
+
Shows deployment and health per environment for the current module:
|
|
71
|
+
|
|
72
|
+
```sh
|
|
73
|
+
qore status
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
| Column | Meaning |
|
|
77
|
+
|--------|---------|
|
|
78
|
+
| ENV | Environment name |
|
|
79
|
+
| DEPLOY | Deployment status, or `paused` |
|
|
80
|
+
| COMMIT | Deployed commit SHA |
|
|
81
|
+
| DEPLOYED | How long ago that commit was deployed |
|
|
82
|
+
| APP HEALTH | The app's public health check (not the module's) |
|
|
83
|
+
|
|
84
|
+
#### `qore env pull`
|
|
85
|
+
|
|
86
|
+
Writes this module's local secrets into an env file in the current
|
|
87
|
+
repository:
|
|
88
|
+
|
|
89
|
+
```sh
|
|
90
|
+
qore env pull
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
- The target file defaults to the module type's own convention (e.g.
|
|
94
|
+
`.env.local`), or `.qore.json`'s `envFile`, or `--target <path>` — always
|
|
95
|
+
resolved against, and required to stay inside, the repository.
|
|
96
|
+
- If the file doesn't exist yet, it's seeded from `.env.example` when one is
|
|
97
|
+
present.
|
|
98
|
+
- Existing values you've hand-edited are kept by default. A key the portal
|
|
99
|
+
manages (e.g. `CONN_*`, `DATABASE_URL`) is always taken from the portal.
|
|
100
|
+
For any other key that differs between your file and the portal, pass
|
|
101
|
+
`--use-portal` to take the portal's value, or answer the interactive
|
|
102
|
+
prompt per key (which shows only the **length** of each value, never its
|
|
103
|
+
content — see "Values are never printed" below).
|
|
104
|
+
- The file is written with mode `0600` via a temp file in the same
|
|
105
|
+
directory, then renamed over the target — it's never truncated in place.
|
|
106
|
+
(On Windows, POSIX file modes don't apply — setting `0600` there is a
|
|
107
|
+
no-op; there's no equivalent file-permission hardening on that platform.)
|
|
108
|
+
|
|
109
|
+
**First use of a repository** requires confirmation — see "Trust on first
|
|
110
|
+
use" below. **Values are never printed**: not in `qore env pull`'s output,
|
|
111
|
+
not in the interactive conflict prompt, not in `--verbose` logs, and not in
|
|
112
|
+
the portal's own audit trail, which records key names only (see
|
|
113
|
+
`app.local_env.fetched`).
|
|
114
|
+
|
|
115
|
+
#### `qore run`
|
|
116
|
+
|
|
117
|
+
Runs a command with this module's local secrets injected into its
|
|
118
|
+
environment, without ever writing them to disk:
|
|
119
|
+
|
|
120
|
+
```sh
|
|
121
|
+
qore run -- node server.js
|
|
122
|
+
qore run -- pnpm dev
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
- Secrets are fetched fresh on every `run` — never cached — merged with
|
|
126
|
+
your current environment, and passed only to the child process.
|
|
127
|
+
- The command runs without a shell (`shell: false`); everything after `--`
|
|
128
|
+
is passed to it verbatim, including its own flags (a `--json` or
|
|
129
|
+
`--no-input` after `--` is the child's, never `qore`'s own). On Windows,
|
|
130
|
+
this is true in spirit but not literally for `.cmd` shims (e.g. `pnpm`,
|
|
131
|
+
`npm`, or anything installed as a `.cmd` wrapper): `cross-spawn` runs
|
|
132
|
+
those through `cmd.exe /d /s /c` under the hood, with its own escaping.
|
|
133
|
+
Pressing Ctrl+C there can also surface cmd's own
|
|
134
|
+
`Terminate batch job (Y/N)?` prompt.
|
|
135
|
+
- The child's exit code, or the signal it was killed with (e.g. Ctrl+C →
|
|
136
|
+
128+signal), becomes `qore run`'s own exit code.
|
|
137
|
+
- **Secrets live in the child's environment**, which is inherited by any
|
|
138
|
+
process the child spawns (its grandchildren) and is readable by other
|
|
139
|
+
processes running as the same user (`/proc/<pid>/environ`, `ps eww`) —
|
|
140
|
+
no worse than a `.env.local` on disk, but worth knowing before running an
|
|
141
|
+
untrusted command this way.
|
|
142
|
+
|
|
143
|
+
#### `.qore.json`
|
|
144
|
+
|
|
145
|
+
An optional file at the repository root that configures `qore env pull`
|
|
146
|
+
for that repository. Schema:
|
|
147
|
+
[`schema/qore.schema.json`](./schema/qore.schema.json).
|
|
148
|
+
|
|
149
|
+
```json
|
|
150
|
+
{
|
|
151
|
+
"$schema": "https://unpkg.com/@quatronic/qore/schema/qore.schema.json",
|
|
152
|
+
"envFile": ".env.local",
|
|
153
|
+
"localSecrets": ["MY_LOCAL_OVERRIDE"]
|
|
154
|
+
}
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
It never holds a portal URL, tenant, app or module id — those are always
|
|
158
|
+
resolved from the git remote or `--module`/`--app`, never committed.
|
|
159
|
+
|
|
160
|
+
#### Trust on first use
|
|
161
|
+
|
|
162
|
+
The first time you run `qore env pull` or `qore run` against a given
|
|
163
|
+
repository (for a given module), `qore` asks you to confirm before pulling
|
|
164
|
+
secrets into it — trust-on-first-use, recorded locally, not a portal-side
|
|
165
|
+
grant. Pass `--yes` to trust non-interactively (CI, scripts) instead of
|
|
166
|
+
being prompted.
|
|
167
|
+
|
|
168
|
+
#### Resolution cache
|
|
169
|
+
|
|
170
|
+
Resolved `git remote` → module lookups are cached at
|
|
171
|
+
`<config dir>/qore/cache/repos.json` so repeat commands in the same
|
|
172
|
+
repository skip an extra request. It's pure cache — deleting the file is
|
|
173
|
+
always harmless, at worst the next command re-resolves.
|
|
174
|
+
|
|
175
|
+
If a repository resolves to no module on any portal you're logged in to,
|
|
176
|
+
the error lists the portals it tried and, when a staging repository looks
|
|
177
|
+
likely, suggests `qore login --portal staging`.
|
|
178
|
+
|
|
179
|
+
### Workspaces
|
|
180
|
+
|
|
181
|
+
`qore clone` sets up a local directory holding one or more of an app's
|
|
182
|
+
modules, each in its own repository; `qore sync` keeps that directory in
|
|
183
|
+
step with the app afterwards.
|
|
184
|
+
|
|
185
|
+
```sh
|
|
186
|
+
qore clone <app> # clones every module of <app> into ./<app-slug>
|
|
187
|
+
qore clone <app> <dir> # ...into <dir> instead
|
|
188
|
+
cd <app-slug>
|
|
189
|
+
qore status # app-level table, one row per module/environment
|
|
190
|
+
cd <module>
|
|
191
|
+
qore env pull # no trust prompt — a clone made by qore is already trusted
|
|
192
|
+
cd ..
|
|
193
|
+
qore sync # clone any module that's new, fetch the rest
|
|
194
|
+
qore sync --pull # ...and fast-forward every clean repository with an upstream
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
- **`--protocol https|ssh`** picks the clone URL (default `https`). `ssh`
|
|
198
|
+
rewrites `https://github.com/o/r` to `git@github.com:o/r.git`; your own
|
|
199
|
+
git `insteadOf` config still applies on top of either.
|
|
200
|
+
- **`--tenant <slug>`** on `qore clone` disambiguates an app slug that
|
|
201
|
+
exists in more than one tenant you have access to.
|
|
202
|
+
- **The workspace file** is `qore.workspace.json` at the directory `qore
|
|
203
|
+
clone` created, holding `{"schemaVersion": 1, "portal": "<apiOrigin>",
|
|
204
|
+
"appId": "<uuid>"}`. It never lists modules — those always come from the
|
|
205
|
+
portal, so a module added to the app later shows up on the next `qore
|
|
206
|
+
sync` with no local change needed. `qore` finds it by walking up from the
|
|
207
|
+
current directory (stopping at your home directory). The module commands
|
|
208
|
+
(`env pull`, `run`, …), `qore status` and `qore sync` pick up its portal
|
|
209
|
+
(and, for a module shared by several apps, its app) automatically when run
|
|
210
|
+
from inside the workspace — including from a module's own repository;
|
|
211
|
+
`qore clone`, `qore whoami` and `qore tenant` do not read it. An explicit
|
|
212
|
+
`--portal` flag always wins over it. `qore status` shows the app-level
|
|
213
|
+
table only when run in the directory that holds `qore.workspace.json`
|
|
214
|
+
itself (not inside one of its module repositories, which shows that
|
|
215
|
+
module).
|
|
216
|
+
- **Nothing is ever deleted, moved or overwritten.** A present module
|
|
217
|
+
directory is left exactly as it is; `sync` only fetches into it, and
|
|
218
|
+
only fast-forwards with `--pull`, and only when the tree is clean and has
|
|
219
|
+
an upstream — a dirty tree, one with no upstream, or one that would need
|
|
220
|
+
a merge is reported, never touched. `sync` runs a plain `git fetch` (no
|
|
221
|
+
`--prune`). A module's directory whose `origin` doesn't point at that
|
|
222
|
+
module's repository is reported as re-pointed, and one that isn't a git
|
|
223
|
+
repository at all is reported too — neither is repaired; move it aside
|
|
224
|
+
and run `qore sync` again. A repository directory in the workspace that
|
|
225
|
+
isn't one of the app's modules (typically a module that left the app) is
|
|
226
|
+
reported as removed, and left on disk.
|
|
227
|
+
- **A target directory that already holds something else** — someone
|
|
228
|
+
else's files, or another app's or portal's workspace — makes `qore
|
|
229
|
+
clone` fail with `QORE_WORKSPACE_CONFLICT` before writing anything,
|
|
230
|
+
including `qore.workspace.json`. Pick a different directory
|
|
231
|
+
(`qore clone <app> <dir>`).
|
|
232
|
+
- **Per-module clone failures** are diagnosed individually and don't stop
|
|
233
|
+
the rest of the run:
|
|
234
|
+
- `QORE_GITHUB_NOT_LINKED` — your GitHub account isn't linked in Qore;
|
|
235
|
+
link it at the account page, then `qore sync`.
|
|
236
|
+
- `QORE_REPO_NO_ACCESS` — you have no seat on the repository; the error
|
|
237
|
+
names the dev team(s) that build the module and their maintainers to ask.
|
|
238
|
+
- `QORE_REPO_ACCESS_PENDING` — access was granted recently, or your
|
|
239
|
+
GitHub organisation invitation isn't accepted yet; the error links the
|
|
240
|
+
invitation page.
|
|
241
|
+
- `QORE_GIT_AUTH` — git itself couldn't authenticate (no SSH key,
|
|
242
|
+
GitHub's SSH host key not accepted yet, no `gh auth login`, or SSO
|
|
243
|
+
authorisation needed for the org); the error names the fix.
|
|
244
|
+
|
|
245
|
+
`clone`/`sync` exits `0` when at least one module cloned or was already
|
|
246
|
+
present and every failure is one of the first three above
|
|
247
|
+
(`QORE_GITHUB_NOT_LINKED`, `QORE_REPO_NO_ACCESS`,
|
|
248
|
+
`QORE_REPO_ACCESS_PENDING`) — so a developer missing a seat on one module
|
|
249
|
+
of a multi-module app still gets a usable workspace for the rest.
|
|
250
|
+
`QORE_GIT_AUTH` is not an access failure in this sense: it fails the run
|
|
251
|
+
(exit `5`). Any other failure, or a run where nothing succeeded at all,
|
|
252
|
+
exits with that failure's own code. With `--json`, once the per-module
|
|
253
|
+
loop has run its results are under `data.results` (or
|
|
254
|
+
`error.details.results` on failure); an error before that (not logged in,
|
|
255
|
+
no workspace, a conflicting target directory) is an ordinary error
|
|
256
|
+
document without results.
|
|
257
|
+
|
|
36
258
|
### `--json`
|
|
37
259
|
|
|
38
260
|
Every command accepts `--json`, which prints exactly one JSON document on
|
|
@@ -49,8 +271,8 @@ qore whoami --json
|
|
|
49
271
|
| Code | Meaning |
|
|
50
272
|
|------|---------|
|
|
51
273
|
| 0 | Success |
|
|
52
|
-
| 1 | Internal error, or the OS keychain is unavailable/access was denied |
|
|
53
|
-
| 2 | Missing or invalid context (e.g. a bad `--portal` value) |
|
|
274
|
+
| 1 | Internal error, `git` is not installed or not on `PATH`, or the OS keychain is unavailable/access was denied |
|
|
275
|
+
| 2 | Missing or invalid context (e.g. a bad `--portal` value, or no module selected), or the module is part of several apps and no `--app` was given |
|
|
54
276
|
| 4 | Not logged in, session/login expired, login denied, or clock skew |
|
|
55
277
|
| 5 | Access denied — lost tenant access, resource not found, no developer access |
|
|
56
278
|
| 6 | Auth or API temporarily unreachable/unavailable |
|