unoverse 0.1.92 → 0.1.93

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.
@@ -102,14 +102,11 @@ NODE
102
102
  # password afterwards. This opens a door in the network layer; it does not open the
103
103
  # database.
104
104
  cmd_db_allow() {
105
- local cloud="" g
106
- for g in digitalocean aws; do
107
- [ -f "$ROOT/infra/$g/terraform.tfvars" ] && { cloud="$g"; break; }
108
- done
109
- if [ -z "$cloud" ]; then
110
- fail "No ground here. There is no database to reach"
105
+ local cloud
106
+ SELF_CMD="unoverse db-allow" cloud=$(_pick_ground "${1:-}") || {
107
+ [ -z "${1:-}" ] && [ ! -d "$ROOT/infra" ] && fail "No ground here. There is no database to reach"
111
108
  return 1
112
- fi
109
+ }
113
110
 
114
111
  local cluster
115
112
  cluster=$(grep -E '^existing_pg_cluster_name[[:space:]]*=' "$ROOT/infra/$cloud/terraform.tfvars" 2>/dev/null | sed -E 's/.*"([^"]+)".*/\1/')
@@ -188,6 +185,51 @@ NODE
188
185
  # `unoverse destroy` ran with no credential at all and every API call came back 401 —
189
186
  # the same class of bug as the Postgres question living in one branch of two. Anything
190
187
  # that talks to a ground calls this first.
188
+ # WHICH GROUND THIS COMMAND MEANS.
189
+ # _pick_ground [name] → echoes the ground, or fails with a reason
190
+ #
191
+ # Every command used `for g in digitalocean aws; do ... break; done`: first match wins, and
192
+ # digitalocean is first in the list. With both grounds configured there was no way to reach
193
+ # the AWS one — and `unoverse destroy` would have torn down DigitalOcean while the operator
194
+ # meant AWS, silently, because the word "aws" had nowhere to go.
195
+ #
196
+ # So a ground can be named. With one configured, naming it is optional and the answer is
197
+ # obvious. With two, the name is REQUIRED rather than guessed: a wrong guess here destroys
198
+ # the wrong cloud.
199
+ _pick_ground() {
200
+ local want="${1:-}" g found=""
201
+ case "$want" in
202
+ do|digitalocean) want="digitalocean" ;;
203
+ aws|amazon) want="aws" ;;
204
+ "") want="" ;;
205
+ *) fail "Unknown ground '$want'. Use ${BOLD}digitalocean${NC} or ${BOLD}aws${NC}" >&2; return 1 ;;
206
+ esac
207
+
208
+ local configured=()
209
+ for g in digitalocean aws; do
210
+ [ -f "$ROOT/infra/$g/terraform.tfvars" ] && configured+=("$g")
211
+ done
212
+
213
+ if [ -n "$want" ]; then
214
+ for g in "${configured[@]}"; do
215
+ [ "$g" = "$want" ] && { printf '%s' "$want"; return 0; }
216
+ done
217
+ fail "No $want ground here. Run ${BOLD}unoverse deploy $want${NC} to create one" >&2
218
+ return 1
219
+ fi
220
+
221
+ case "${#configured[@]}" in
222
+ 0) return 1 ;; # caller offers to create one
223
+ 1) printf '%s' "${configured[0]}"; return 0 ;;
224
+ *)
225
+ # Two grounds and no name. Refuse rather than pick: this decides which cloud a
226
+ # destroy lands on.
227
+ fail "Two grounds are configured. Say which: ${BOLD}${SELF_CMD:-unoverse deploy} digitalocean${NC} or ${BOLD}${SELF_CMD:-unoverse deploy} aws${NC}" >&2
228
+ return 1
229
+ ;;
230
+ esac
231
+ }
232
+
191
233
  _ground_credentials() {
192
234
  [ -n "${DIGITALOCEAN_TOKEN:-}" ] && return 0
193
235
  local _docfg
@@ -472,6 +514,13 @@ _ground_apply() {
472
514
  }
473
515
 
474
516
  cmd_deploy() {
517
+ # A LEADING GROUND NAME IS NOT A SUBCOMMAND. `unoverse deploy aws` names the cloud;
518
+ # `unoverse deploy init` names the step; `unoverse deploy aws init` does both. Take the
519
+ # ground off the front when it is there, and leave everything else exactly as it was.
520
+ local GROUND_ARG=""
521
+ case "${1:-}" in
522
+ do|digitalocean|aws|amazon) GROUND_ARG="$1"; shift ;;
523
+ esac
475
524
 
476
525
  # .env.production IS NOT A FILE IN THE UNIVERSE. It is `terraform output -raw
477
526
  # env_production`: derived, complete, and regenerated in full on every deploy. Writing it
@@ -492,10 +541,14 @@ cmd_deploy() {
492
541
  # ── ONE FLOW ────────────────────────────────────────────────────────────────
493
542
  # Which ground, is it configured, plan it, apply it, ship it. In that order, every
494
543
  # time, whether this is the first deploy or the fiftieth.
495
- local cloud="" g rc
496
- for g in digitalocean aws; do
497
- [ -f "$ROOT/infra/$g/terraform.tfvars" ] && { cloud="$g"; break; }
498
- done
544
+ # A named ground wins; one configured ground is implied; two without a name is refused
545
+ # (see _pick_ground). Nothing configured at all falls through to creating one below,
546
+ # which is the first-run path and the only case where an empty answer is not a mistake.
547
+ local cloud="" rc have=0
548
+ [ -f "$ROOT/infra/digitalocean/terraform.tfvars" ] || [ -f "$ROOT/infra/aws/terraform.tfvars" ] && have=1
549
+ if [ "$have" = "1" ]; then
550
+ cloud=$(SELF_CMD="unoverse deploy" _pick_ground "${GROUND_ARG:-}") || exit 1
551
+ fi
499
552
 
500
553
  if [ -z "$cloud" ]; then
501
554
  echo ""
@@ -19,14 +19,17 @@
19
19
  # cloud resources this ground owns.
20
20
 
21
21
  cmd_destroy() {
22
- local cloud="" g
23
- for g in digitalocean aws; do
24
- [ -f "$ROOT/infra/$g/terraform.tfvars" ] && { cloud="$g"; break; }
25
- done
26
- if [ -z "$cloud" ]; then
27
- fail "No ground here. There is nothing deployed to take down"
22
+ # NAME THE GROUND. This took the first configured one, and digitalocean is first in the
23
+ # list so an operator with both grounds who typed `unoverse destroy` meaning AWS would
24
+ # have torn down DigitalOcean instead, with the confirmation prompt happily naming the
25
+ # universe they thought they were destroying. _pick_ground refuses to guess when there is
26
+ # more than one.
27
+ local cloud
28
+ cloud=$(SELF_CMD="unoverse destroy" _pick_ground "${1:-}") || {
29
+ [ -f "$ROOT/infra/digitalocean/terraform.tfvars" ] || [ -f "$ROOT/infra/aws/terraform.tfvars" ] \
30
+ || fail "No ground here. There is nothing deployed to take down"
28
31
  return 1
29
- fi
32
+ }
30
33
 
31
34
  local dir="$ROOT/infra/$cloud"
32
35
  if [ ! -d "$dir/.terraform" ]; then
@@ -24,6 +24,7 @@ cmd_help() {
24
24
  echo -e " ${GREEN}logs${NC} What is it doing ${DIM}(unoverse logs <service> for one)${NC}"
25
25
  echo -e " ${GREEN}deploy${NC} Ship it to a server ${DIM}(first run asks which cloud)${NC}"
26
26
  echo -e " ${GREEN}destroy${NC} Take the deployment down ${DIM}(shows what goes, and what stays)${NC}"
27
+ echo -e " ${DIM}With two grounds, name one: deploy aws · destroy digitalocean${NC}"
27
28
  echo -e " ${GREEN}db-allow${NC} Let this machine reach the database ${DIM}(run it when you change network)${NC}"
28
29
  echo ""
29
30
  # Owner-only lane. Printed ONLY when publish.sh is present, so a starter kit never
@@ -65,8 +65,8 @@ case "${1:-}" in
65
65
  check) cmd_check && cmd_db_verify && cmd_doctor ;;
66
66
  dev) cmd_dev ;;
67
67
  ground) shift; cmd_ground "$@" ;;
68
- destroy) cmd_destroy ;;
69
- db-allow) cmd_db_allow ;;
68
+ destroy) cmd_destroy "${2:-}" ;;
69
+ db-allow) cmd_db_allow "${2:-}" ;;
70
70
  refresh-images)
71
71
  # internal: `unoverse update` runs this after updating the CLI. Pull newer images
72
72
  # if the registry has them, and recreate only what is already running — an update
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "unoverse",
3
- "version": "0.1.92",
3
+ "version": "0.1.93",
4
4
  "description": "The Unoverse front door — create a Studio project, a universe, or a client app, and launch Studio.",
5
5
  "license": "SEE LICENSE IN README.md",
6
6
  "type": "module",