@thebaycloud/cli 1.0.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/CHANGELOG.md +224 -0
- package/README.md +53 -0
- package/index.js +1269 -0
- package/lib/brand.js +124 -0
- package/lib/bundle.js +399 -0
- package/lib/check.js +281 -0
- package/lib/confirm.js +46 -0
- package/lib/draft.js +488 -0
- package/lib/envfile.js +158 -0
- package/lib/exec-args.js +40 -0
- package/lib/prebuilt.js +86 -0
- package/lib/resolver.js +63 -0
- package/lib/who.js +14 -0
- package/package.json +48 -0
- package/vendor/README.md +6 -0
- package/vendor/detector.js +423 -0
- package/vendor/inputs.json +21 -0
- package/vendor/resolve.js +2156 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.12.1
|
|
4
|
+
|
|
5
|
+
**A ship now says who is shipping.**
|
|
6
|
+
|
|
7
|
+
Every deploy request carries `x-supersonic-who`, set from `SUPERSONIC_WHO` and
|
|
8
|
+
nothing else — no TTY check, no `CI` check, no guessing. An agent that sets
|
|
9
|
+
`SUPERSONIC_WHO=agent` is recorded as one; anything else, including a CI
|
|
10
|
+
runner with no terminal at all, is recorded as `someone`. Reporting "agent"
|
|
11
|
+
just because there was no TTY would have been a confident lie in the one
|
|
12
|
+
field this feature exists to keep honest, so the CLI declares only what it
|
|
13
|
+
was told and otherwise says it doesn't know.
|
|
14
|
+
|
|
15
|
+
## 0.12.0
|
|
16
|
+
|
|
17
|
+
**`ship` is the word now. `deploy` is an alias and always will be.**
|
|
18
|
+
|
|
19
|
+
`supersonic ship` is the act of sending your work out — it is what people
|
|
20
|
+
say, and it leaves "deploy" to mean the thing sysadmins do. `deploy` keeps
|
|
21
|
+
working, unchanged, forever: it is typed by every existing user and written
|
|
22
|
+
into every agent prompt and script that already exists, so it is not
|
|
23
|
+
deprecated, not warned about, and not going anywhere. `reship` joins
|
|
24
|
+
`redeploy` on the same terms.
|
|
25
|
+
|
|
26
|
+
**A flag `ship` does not understand now stops it.**
|
|
27
|
+
|
|
28
|
+
It used to be dropped in silence and the deploy went ahead anyway, so a
|
|
29
|
+
single typo — `--drt-run` — reserved a slug, uploaded a folder and created
|
|
30
|
+
an app nobody asked for. Found by doing exactly that by accident. The cost
|
|
31
|
+
lands hardest on agents: a person sees a stray app appear, an agent reads
|
|
32
|
+
"deploying — your app will be live at" and reports success for something it
|
|
33
|
+
never requested.
|
|
34
|
+
|
|
35
|
+
## 0.11.0
|
|
36
|
+
|
|
37
|
+
**`--dev-cmd`, `--dev-port` and `--no-preview` are gone, and the link they were
|
|
38
|
+
for is better without them.**
|
|
39
|
+
|
|
40
|
+
Those flags ran your app locally and tunnelled your public URL to it while the
|
|
41
|
+
real build ran, so the address showed something instead of a placeholder. What
|
|
42
|
+
the address shows now is the room: the build itself, drawn, live, at the app's
|
|
43
|
+
own URL — for you and for anyone you send the link to. It needs nothing running
|
|
44
|
+
on your machine, works the same for every stack, and turns into the app the
|
|
45
|
+
moment it first answers.
|
|
46
|
+
|
|
47
|
+
Passing the removed flags is now an unknown-flag error rather than a silent
|
|
48
|
+
no-op, so a script still passing them says so instead of quietly changing
|
|
49
|
+
behaviour.
|
|
50
|
+
|
|
51
|
+
One thing the tunnel got wrong goes with it. A redeploy flips an app back to
|
|
52
|
+
"deploying", and an open tunnel outranked the published build for that whole
|
|
53
|
+
window — visitors to a working app were sent to a laptop mid-edit. A landed
|
|
54
|
+
build is now served whatever the status says.
|
|
55
|
+
|
|
56
|
+
## 0.10.1
|
|
57
|
+
|
|
58
|
+
**`env set` and `env unset` stop announcing a rollout that is not happening.**
|
|
59
|
+
|
|
60
|
+
Both printed "— new revision rolling out", unconditionally and on both runtimes.
|
|
61
|
+
An app on a node has no revisions, and the line went out even for a write that
|
|
62
|
+
changed nothing — which is how the command read as a success while `env` went on
|
|
63
|
+
listing the key you had just removed. The server now says what it actually did:
|
|
64
|
+
"the node applies this within about ten seconds", or the new revision on Cloud
|
|
65
|
+
Run. The CLI repeats that instead of guessing.
|
|
66
|
+
|
|
67
|
+
What was genuinely broken is on the server, and is already fixed for 0.10.0 as
|
|
68
|
+
well: anything that came from your `.env` is stored as a secret, and these two
|
|
69
|
+
commands only ever edited the plain variables sitting beside it. Upgrading does
|
|
70
|
+
not fix that and staying does not break it — this release only stops the CLI
|
|
71
|
+
describing it wrongly.
|
|
72
|
+
|
|
73
|
+
## 0.10.0
|
|
74
|
+
|
|
75
|
+
**`supersonic init` and `supersonic check` — the deploy loop, on your machine, in
|
|
76
|
+
two seconds.**
|
|
77
|
+
|
|
78
|
+
The loop for getting a `supersonic.json` right ran through a real deploy: upload,
|
|
79
|
+
provision, build, fail, read a log, guess. Eleven minutes an attempt, and eleven
|
|
80
|
+
attempts is two hours and a Cloud Build bill for every one of them. Both new
|
|
81
|
+
commands are local — no cloud, no build, no model.
|
|
82
|
+
|
|
83
|
+
`init` writes a **draft**, and says so. It reads what the files actually state: the
|
|
84
|
+
monorepo split, the install command from the lockfile, the build command and output
|
|
85
|
+
directory, the start command bound to `$PORT`, the runtime version from
|
|
86
|
+
`engines.node` / `.nvmrc` / `requires-python` / `.python-version`, the database from
|
|
87
|
+
a dependency scan, the framework, and every env var name a `process.env` /
|
|
88
|
+
`os.environ` grep can see. Then it prints what no static analysis can answer —
|
|
89
|
+
which service owns `/` when both are servers, whether `alembic upgrade head` should
|
|
90
|
+
run before traffic or is merely installed, whether a committed `dist/` is the
|
|
91
|
+
deliverable or stale, SPA-fallback intent, and which of those env var names are
|
|
92
|
+
secrets.
|
|
93
|
+
|
|
94
|
+
The detector doing the reading is the same one that read a `frontend/` + `backend/`
|
|
95
|
+
root as "Static site, 80% confidence" — its own highest-confidence answer, and
|
|
96
|
+
wrong. Nothing here makes it better. What changes is that its answer now lands in a
|
|
97
|
+
file, in front of the agent that wrote the app, instead of silently selecting a lane
|
|
98
|
+
on a server 200 seconds later. Never ask an agent to author JSON from nothing; ask
|
|
99
|
+
it to correct a draft.
|
|
100
|
+
|
|
101
|
+
`check` is that file resolved and validated exactly as a deploy would, printing per
|
|
102
|
+
service the command each phase runs — install, build, release, start, health, scale
|
|
103
|
+
— and exiting non-zero on any problem. It catches a static service that declared a
|
|
104
|
+
migration, a build with nowhere to publish from, two services claiming one path, a
|
|
105
|
+
directory that is not there, and a `requires-python` the runner does not have.
|
|
106
|
+
|
|
107
|
+
Both go through the control plane's own resolver, compiled into `vendor/resolve.js`
|
|
108
|
+
rather than ported. A second implementation would agree the day it was written and
|
|
109
|
+
diverge the first time a lane changed, and "check passes, the deploy resolves
|
|
110
|
+
differently" is indistinguishable from the platform being broken. `npm run bundle`
|
|
111
|
+
rebuilds it and a test fails a stale one — which caught the vendored detector still
|
|
112
|
+
answering `python:3.12` two days after the runner moved to 3.14, a number that
|
|
113
|
+
decided the `FROM python:…-slim` of every containerised Python build.
|
|
114
|
+
|
|
115
|
+
**git decides what gets uploaded, and the CLI says what it left behind.**
|
|
116
|
+
|
|
117
|
+
Packaging was nineteen `tar --exclude=` patterns plus `--exclude-from=.gitignore`.
|
|
118
|
+
tar matches those on basename at any depth, so a module named `src/build/`, a
|
|
119
|
+
Composer or `go mod vendor` `app/vendor/`, and a committed `dist/` that *was* the
|
|
120
|
+
deliverable were stripped out of the upload — silently, so the first sign of it was
|
|
121
|
+
"module not found" three stages later. And tar does not speak gitignore: `!keep.js`
|
|
122
|
+
was a literal filename, an anchored `/dist` matched nothing, `**/` meant something
|
|
123
|
+
else. `deploy` now runs `git ls-files --cached --others --exclude-standard` and packs
|
|
124
|
+
exactly that, so negations, anchors, nested .gitignore files and your global excludes
|
|
125
|
+
all work the way `git status` says they do. A folder that is not a repository still
|
|
126
|
+
gets the old denylist.
|
|
127
|
+
|
|
128
|
+
It also prints what it skipped and why — the ignored count with the directories
|
|
129
|
+
responsible, and the `.env` files held back to be sent as env vars instead.
|
|
130
|
+
|
|
131
|
+
**Four things that used to ship broken:**
|
|
132
|
+
|
|
133
|
+
- files git records as executable but whose working copy lost the bit are named
|
|
134
|
+
before upload, instead of arriving 0644 and exiting "permission denied"
|
|
135
|
+
- `.git` goes up when the build reads the version out of it (setuptools-scm,
|
|
136
|
+
hatch-vcs, versioneer), which otherwise publishes `0.0.0` without complaining
|
|
137
|
+
- paths that differ only by case are one file on your Mac and two on Linux; you now
|
|
138
|
+
hear about them here rather than from a failing import
|
|
139
|
+
- git-lfs pointers are fetched, or the deploy stops and names the files. They are
|
|
140
|
+
130-byte text stubs with `.mp4` names, and every stage after this one reports
|
|
141
|
+
success over them.
|
|
142
|
+
|
|
143
|
+
**`status` can say an app is still coming.**
|
|
144
|
+
|
|
145
|
+
`ready ? live : down` had no third state, so an app mid-build was reported as
|
|
146
|
+
`○ down · revision — · env none` while its build was running normally and its URL
|
|
147
|
+
was serving 200. Now `◐ deploying`, with the stage it has reached.
|
|
148
|
+
|
|
149
|
+
**The live preview stops writing in your folder unannounced.**
|
|
150
|
+
|
|
151
|
+
It runs your project's dev command in your checkout, and on a fresh one it
|
|
152
|
+
installs first — which meant `npm install` in repositories that use bun, pnpm or
|
|
153
|
+
yarn, dropping a foreign lockfile into a clean tree two minutes after the command
|
|
154
|
+
had already returned. It now uses the package manager your lockfile names, prints
|
|
155
|
+
the exact command before running it, and `--no-preview` declines it entirely.
|
|
156
|
+
|
|
157
|
+
**Everything fixed since 0.9.1 that had never been published.**
|
|
158
|
+
|
|
159
|
+
Chiefly: `deploy` no longer prints "✓ your app is live at" one second in, before
|
|
160
|
+
anything has been built. That was fixed in the repository on 31 July and never
|
|
161
|
+
reached anyone, because publishing is something a person has to remember — which
|
|
162
|
+
is the actual bug behind this whole section.
|
|
163
|
+
|
|
164
|
+
## 0.9.0
|
|
165
|
+
|
|
166
|
+
**Your `.env` goes up with the deploy.**
|
|
167
|
+
|
|
168
|
+
An app that reads an API key used to come up crash-looping until somebody noticed and
|
|
169
|
+
set the vars by hand. `deploy` now reads `.env` and `.env.local` and sets what the app
|
|
170
|
+
needs, so it starts working the first time.
|
|
171
|
+
|
|
172
|
+
What is deliberately left behind: vars Supersonic sets itself (`DATABASE_URL`, `PORT`,
|
|
173
|
+
`STORAGE_BUCKET`), anything whose value points at your own machine, and any key the app
|
|
174
|
+
already has — the value you set in production wins over the one in your `.env`, which is
|
|
175
|
+
usually a test key. Only names are ever printed, never values. `--no-env` opts out.
|
|
176
|
+
|
|
177
|
+
**The live preview works on every deploy, not just an app's first.**
|
|
178
|
+
|
|
179
|
+
The proxy preferred a published build over an open tunnel, so from an app's second
|
|
180
|
+
deploy onwards the URL showed the previous release and the preview was never reached.
|
|
181
|
+
An open tunnel now wins, for the duration of the build.
|
|
182
|
+
|
|
183
|
+
Finding the dev server's port no longer depends on it printing a line we recognise —
|
|
184
|
+
a server that announces nothing is found anyway, so `--dev-cmd` produces a preview on
|
|
185
|
+
stacks where it used to silently produce none.
|
|
186
|
+
|
|
187
|
+
## 0.7.0
|
|
188
|
+
|
|
189
|
+
**`deploy` now builds on your machine and uploads only the result.**
|
|
190
|
+
|
|
191
|
+
Until now the CLI zipped your sources, uploaded them, and the cloud installed the
|
|
192
|
+
dependencies and ran the build from nothing — about 80 seconds. Your machine already has
|
|
193
|
+
the project and already builds it in under a second, so it does that instead and sends
|
|
194
|
+
only the output directory.
|
|
195
|
+
|
|
196
|
+
Measured on a real Vite project: **26 seconds** for a first deploy, **19 seconds** when
|
|
197
|
+
the output changed, and **1-2 seconds** when it did not change at all — the CLI hashes
|
|
198
|
+
what it built, asks whether that exact output is already live, and stops if it is.
|
|
199
|
+
|
|
200
|
+
Applies to projects that build to a directory: Vite, Create React App, Astro without an
|
|
201
|
+
SSR adapter, Next.js with `output: 'export'`, and plain static folders. Apps that run a
|
|
202
|
+
server still build in the cloud, unchanged — their artifact is an image, not a folder.
|
|
203
|
+
|
|
204
|
+
Every step falls back rather than failing. A server app, a project the detector cannot
|
|
205
|
+
place, a failed install, a failed build, an empty output directory, an unreachable
|
|
206
|
+
server: all of them continue to the cloud build exactly as before. `--cloud-build` forces
|
|
207
|
+
it.
|
|
208
|
+
|
|
209
|
+
**`deploy` signs you in by itself.** No separate `supersonic login` step the first time.
|
|
210
|
+
|
|
211
|
+
**A refused deploy now says why.** A plan limit used to surface as "the build may have
|
|
212
|
+
failed or timed out — check supersonic logs", sending you to hunt a failure that never
|
|
213
|
+
happened. It now shows what the server actually said.
|
|
214
|
+
|
|
215
|
+
**`whoami` shows your account email and plan.**
|
|
216
|
+
|
|
217
|
+
### Notes
|
|
218
|
+
|
|
219
|
+
The stack detector is now compiled into this package rather than reimplemented in it, so
|
|
220
|
+
the CLI and the server always agree about what a project is.
|
|
221
|
+
|
|
222
|
+
Nothing you were relying on changed shape: `deploy`, `apps`, `status`, `logs`, `errors`,
|
|
223
|
+
`env`, `exec`, `rollback` and `diagnose` all behave as before, and `--json` output is
|
|
224
|
+
unchanged.
|
package/README.md
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# @thebaycloud/cli
|
|
2
|
+
|
|
3
|
+
Deploy anything to [Bay](https://app.thebay.cloud) in one command.
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
npm install -g @thebaycloud/cli
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
## Usage
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
bay init # write a DRAFT bay.json from this repo
|
|
13
|
+
bay check # what each phase would run, and what would fail
|
|
14
|
+
bay login # authenticate (defaults to app.thebay.cloud)
|
|
15
|
+
bay deploy # deploy the current repo (uses your git origin)
|
|
16
|
+
bay deploy --repo <url> # or deploy any public git repo
|
|
17
|
+
bay whoami
|
|
18
|
+
bay logout
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
`bay deploy` streams the live build log — clone → detect stack → build → Cloud Run — and prints your live URL when it's up.
|
|
22
|
+
|
|
23
|
+
## Before you deploy
|
|
24
|
+
|
|
25
|
+
`init` and `check` are local. No cloud, no build, no model, about two seconds each,
|
|
26
|
+
and they are the loop an agent authoring a `bay.json` should be in — because
|
|
27
|
+
the same loop through a real deploy is eleven minutes long.
|
|
28
|
+
|
|
29
|
+
`bay init` reads the repository and writes a **draft** `bay.json`: the
|
|
30
|
+
monorepo split, the install command from the lockfile, the build command and output
|
|
31
|
+
directory, the start command bound to `$PORT`, the runtime version the manifests ask
|
|
32
|
+
for, and the framework. Then it prints what it could not determine — which service
|
|
33
|
+
owns `/`, whether a migration should run before traffic, SPA fallback, which env var
|
|
34
|
+
names are secrets — because none of those are answerable from files, and a guess
|
|
35
|
+
would be indistinguishable from a decision. It refuses to overwrite an existing
|
|
36
|
+
config without `--force`.
|
|
37
|
+
|
|
38
|
+
`bay check` resolves and validates that file exactly as a deploy would, and
|
|
39
|
+
prints, per service, the command each phase runs. Non-zero exit on any problem.
|
|
40
|
+
|
|
41
|
+
Both go through `vendor/resolve.js`, which is `apps/web/lib/{resolve,app-config,
|
|
42
|
+
infer-services,repo-facts,lanes,plan-deps}.ts` compiled by
|
|
43
|
+
`scripts/bundle-resolver.mjs` — the control plane's own resolver, not a port of it.
|
|
44
|
+
Edit any of those and run `npm run bundle`; `test/vendor.test.js` fails on a stale
|
|
45
|
+
bundle, which is how a committed detector spent two days answering `python:3.12`
|
|
46
|
+
after the runner had moved to 3.14.
|
|
47
|
+
|
|
48
|
+
## Options
|
|
49
|
+
|
|
50
|
+
- `bay login --url <control-plane>` — point at a different control-plane (defaults to `https://app.thebay.cloud`; also settable via `BAY_URL`).
|
|
51
|
+
- `bay login --email <e>` — skip the email prompt. Password can be piped via `SUPERSONIC_PASSWORD`.
|
|
52
|
+
|
|
53
|
+
Your session is stored in `~/.bay/config.json`.
|