vela 0.11.4 → 0.11.6

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vela",
3
- "version": "0.11.4",
3
+ "version": "0.11.6",
4
4
  "type": "module",
5
5
  "description": "A CLI for creating and updating SvelteKit projects",
6
6
  "license": "MIT",
@@ -36,7 +36,7 @@
36
36
  "dependencies": {
37
37
  "@clack/prompts": "^1.7.0",
38
38
  "@faker-js/faker": "^10.6.0",
39
- "@velastack/patterns": "^0.2.3",
39
+ "@velastack/patterns": "^0.2.5",
40
40
  "@velastack/pocketbase-codegen": "^0.1.0",
41
41
  "annotate-json-schema": "^0.1.0",
42
42
  "commander": "^13.1.0",
@@ -45,7 +45,7 @@
45
45
  "dotenv": "^17.4.2",
46
46
  "fetch-cookie": "^3.2.0",
47
47
  "json-schema-faker": "^0.6.3",
48
- "magic-string": "^0.30.21",
48
+ "magic-string": "^1.2.3",
49
49
  "package-manager-detector": "^1.8.0",
50
50
  "picocolors": "^1.1.1",
51
51
  "pocketbase": "^0.28.0",
@@ -62,10 +62,10 @@
62
62
  "@types/node": "^22.0.0",
63
63
  "esbuild": "^0.28.2",
64
64
  "prettier": "^3.9.6",
65
+ "shellcheck": "^4.1.0",
65
66
  "typescript": "^5.6.0",
66
67
  "vite": "^8.2.2",
67
- "vitest": "^4.1.11",
68
- "shellcheck": "^4.1.0"
68
+ "vitest": "^4.1.11"
69
69
  },
70
70
  "peerDependencies": {
71
71
  "@sveltejs/kit": "^2.57.1",
@@ -79,6 +79,18 @@
79
79
  "optional": true
80
80
  }
81
81
  },
82
+ "overrides": {
83
+ "global-agent": "^4.1.3",
84
+ "@xhmikosr/decompress-unzip": {
85
+ "file-type": "^21.3.1"
86
+ },
87
+ "@xhmikosr/decompress-tar": {
88
+ "file-type": "^21.3.1"
89
+ },
90
+ "@felipecrs/decompress-tarxz": {
91
+ "file-type": "^21.3.1"
92
+ }
93
+ },
82
94
  "keywords": [
83
95
  "svelte",
84
96
  "sveltekit",
@@ -16,7 +16,7 @@
16
16
  },
17
17
  "devDependencies": {
18
18
  "@lucide/svelte": "^1.40.0",
19
- "@sveltejs/adapter-auto": "^7.0.1",
19
+ "@sveltejs/adapter-node": "^5.5.7",
20
20
  "@sveltejs/kit": "^2.70.3",
21
21
  "@sveltejs/vite-plugin-svelte": "^7.3.0",
22
22
  "@tailwindcss/forms": "^0.5.11",
@@ -1,7 +1,7 @@
1
1
  import { defineConfig } from 'vite';
2
2
  import tailwindcss from '@tailwindcss/vite';
3
3
  import { sveltekit } from '@sveltejs/kit/vite';
4
- import adapter from '@sveltejs/adapter-auto';
4
+ import adapter from '@sveltejs/adapter-node';
5
5
 
6
6
  export default defineConfig({
7
7
  plugins: [
@@ -84,28 +84,68 @@ if [ -n "$ACTIVE" ] && [ -L "$APP/current" ] && ! [[ "$RELEASE" > "$ACTIVE" ]];
84
84
  die "release $RELEASE is not newer than the active release $ACTIVE on $INSTANCE - a newer deploy went live first, so this one was dropped"
85
85
  fi
86
86
 
87
- mkdir -p "$APP"/{releases,shared,bin,deps} "$APP/shared/pb_data"
87
+ # Whether the release this one replaces had a PocketBase backend. The CLI
88
+ # detects the backend from the project on every deploy, so an instance can gain
89
+ # one (`vela bless` between two deploys) or lose one; both are handled below.
90
+ # Empty on a first deploy.
91
+ PREVIOUS_BACKEND=""
92
+ if [ -f "$(state_file "$INSTANCE")" ]; then
93
+ PREVIOUS_BACKEND=$(instance_backend "$INSTANCE")
94
+ fi
95
+
96
+ # Where this app keeps state that has to outlive a release.
97
+ #
98
+ # An app that works this out from its own working directory puts it inside the
99
+ # release, which is the one place it cannot survive: `current` moves on the next
100
+ # deploy and the pruner deletes what it left behind. An instance with a database
101
+ # shares PocketBase's directory, so anything the app writes there is inside the
102
+ # archives `vela backup` takes and inside the directory `vela restore` swaps. An
103
+ # instance without one gets a directory of its own, which nothing backs up -
104
+ # there is no database to back it up alongside.
105
+ if [ "$BACKEND" = "1" ]; then
106
+ APP_DATA_DIR="$APP/shared/pb_data"
107
+ else
108
+ APP_DATA_DIR="$APP/shared/data"
109
+ fi
110
+
111
+ mkdir -p "$APP"/{releases,shared,bin,deps} "$APP_DATA_DIR"
88
112
  mkdir -p "$ETC"
89
113
  chmod 0700 "$ETC"
90
114
 
91
115
  # Only the directories just created need their ownership set, and only at the
92
- # top level: everything below pb_data is written by PocketBase as $VELA_USER
93
- # already. Recursing here would walk every uploaded file on every deploy, which
94
- # makes deploy time grow with the size of the app's storage forever.
116
+ # top level: everything below the data directory is written by PocketBase (or
117
+ # the app) as $VELA_USER already. Recursing here would walk every uploaded file
118
+ # on every deploy, which makes deploy time grow with the size of the app's
119
+ # storage forever.
95
120
  chown "$VELA_USER:$VELA_USER" \
96
- "$APP" "$APP/releases" "$APP/shared" "$APP/shared/pb_data" "$APP/bin" "$APP/deps"
121
+ "$APP" "$APP/releases" "$APP/shared" "$APP_DATA_DIR" "$APP/bin" "$APP/deps"
97
122
  # The release is the exception - rsync uploaded it as whoever we ssh'd in as.
98
123
  chown -R "$VELA_USER:$VELA_USER" "$RELEASE_DIR"
99
124
 
100
- # A pb_data the app cannot write is a dead instance, and it fails as an opaque
101
- # 500 rather than anything that names a cause. This is what the blanket recurse
102
- # above used to paper over; checking costs one stat, repairing costs a walk that
103
- # now happens only when something is actually wrong.
104
- if ! runuser -u "$VELA_USER" -- test -w "$APP/shared/pb_data"; then
125
+ # A data directory the app cannot write is a dead instance, and it fails as an
126
+ # opaque 500 rather than anything that names a cause. This is what the blanket
127
+ # recurse above used to paper over; checking costs one stat, repairing costs a
128
+ # walk that now happens only when something is actually wrong.
129
+ if ! runuser -u "$VELA_USER" -- test -w "$APP_DATA_DIR"; then
105
130
  log "repairing ownership under shared/"
106
131
  chown -R "$VELA_USER:$VELA_USER" "$APP/shared"
107
132
  fi
108
133
 
134
+ # An instance gaining a backend moves its data directory: VELA_DATA_DIR was
135
+ # shared/data and is now shared/pb_data, which is what `vela backup` archives.
136
+ # Whatever the app kept follows it, into a pb_data that PocketBase has not yet
137
+ # written - once there is a database in there, nothing is moved over it.
138
+ if [ "$PREVIOUS_BACKEND" = "false" ] && [ "$BACKEND" = "1" ] \
139
+ && [ -d "$APP/shared/data" ] && [ -n "$(ls -A "$APP/shared/data")" ]; then
140
+ if [ ! -e "$APP/shared/pb_data/data.db" ]; then
141
+ log "adding a backend - moving shared/data into shared/pb_data"
142
+ find "$APP/shared/data" -mindepth 1 -maxdepth 1 -exec mv -t "$APP/shared/pb_data" {} +
143
+ else
144
+ log "warning: this instance now has a backend, and its data directory is $APP/shared/pb_data"
145
+ log "warning: what the app kept in $APP/shared/data was left there"
146
+ fi
147
+ fi
148
+
109
149
  # ---------------------------------------------------------------- ports & env
110
150
 
111
151
  PORTS=$(allocate_ports "$INSTANCE")
@@ -126,23 +166,6 @@ fi
126
166
  # Nothing here reads or rewrites that file.
127
167
  [ -f "$ETC/env" ] || { : > "$ETC/env"; chmod 0600 "$ETC/env"; chown root:root "$ETC/env"; }
128
168
 
129
- # Where this app keeps state that has to outlive a release.
130
- #
131
- # An app that works this out from its own working directory puts it inside the
132
- # release, which is the one place it cannot survive: `current` moves on the next
133
- # deploy and the pruner deletes what it left behind. An instance with a database
134
- # shares PocketBase's directory, so anything the app writes there is inside the
135
- # archives `vela backup` takes and inside the directory `vela restore` swaps. An
136
- # instance without one gets a directory of its own, which nothing backs up -
137
- # there is no database to back it up alongside.
138
- if [ "$BACKEND" = "1" ]; then
139
- APP_DATA_DIR="$APP/shared/pb_data"
140
- else
141
- APP_DATA_DIR="$APP/shared/data"
142
- mkdir -p "$APP_DATA_DIR"
143
- chown "$VELA_USER:$VELA_USER" "$APP_DATA_DIR"
144
- fi
145
-
146
169
  runtime_tmp=$(mktemp "$ETC/.runtime.XXXXXX")
147
170
  {
148
171
  printf '# Generated by vela on each deploy. Edit /etc/vela/apps/%s/env instead.\n' "$INSTANCE"
@@ -150,8 +173,14 @@ runtime_tmp=$(mktemp "$ETC/.runtime.XXXXXX")
150
173
  printf 'HOST=127.0.0.1\n'
151
174
  printf 'PORT=%s\n' "$WEB_PORT"
152
175
  printf 'ORIGIN=%s\n' "$ORIGIN"
153
- printf 'PB_PORT=%s\n' "$PB_PORT"
154
- printf 'POCKETBASE_URL=http://127.0.0.1:%s\n' "$PB_PORT"
176
+ # Only an instance with a PocketBase gets pointed at one: a URL to a port
177
+ # nothing listens on is not a setting, it is a trap. Kept for one more
178
+ # deploy when the backend is being removed, so that a failed deploy can put
179
+ # the previous release - which still needs it - back.
180
+ if [ "$BACKEND" = "1" ] || [ "$PREVIOUS_BACKEND" = "true" ]; then
181
+ printf 'PB_PORT=%s\n' "$PB_PORT"
182
+ printf 'POCKETBASE_URL=http://127.0.0.1:%s\n' "$PB_PORT"
183
+ fi
155
184
  printf 'VELA_DATA_DIR=%s\n' "$APP_DATA_DIR"
156
185
  printf 'VELA_APP_ID=%s\n' "$APP_ID"
157
186
  printf 'VELA_APP_NAME=%s\n' "$APP_NAME"
@@ -253,7 +282,10 @@ restore() {
253
282
  # runtime.env was already written for the release that failed; the
254
283
  # services about to restart must read the one they will actually run.
255
284
  set_runtime_release "$ETC" "$PREVIOUS"
256
- if [ "$BACKEND" = "1" ]; then systemctl restart "$PB_UNIT" >/dev/null 2>&1 || true; fi
285
+ # The previous release needs its PocketBase whether or not this one did.
286
+ if [ "$BACKEND" = "1" ] || [ "$PREVIOUS_BACKEND" = "true" ]; then
287
+ systemctl restart "$PB_UNIT" >/dev/null 2>&1 || true
288
+ fi
257
289
  systemctl restart "$WEB_UNIT" >/dev/null 2>&1 || true
258
290
  else
259
291
  systemctl stop "$WEB_UNIT" >/dev/null 2>&1 || true
@@ -365,6 +397,15 @@ wait_for_http "http://127.0.0.1:$WEB_PORT$HEALTH_PATH" 60 0.5 \
365
397
 
366
398
  ACTIVATED=1
367
399
 
400
+ # An instance that no longer has a backend keeps its database on disk - only
401
+ # `vela destroy deployment --purge` removes data - but the unit serving it is
402
+ # retired, or `vela status` would keep reporting a PocketBase and every restart
403
+ # would bring it back up.
404
+ if [ "$PREVIOUS_BACKEND" = "true" ] && [ "$BACKEND" = "0" ]; then
405
+ log "removing the backend - stopping PocketBase; $APP/shared/pb_data is kept"
406
+ systemctl disable --now "$PB_UNIT" >/dev/null 2>&1 || true
407
+ fi
408
+
368
409
  # --------------------------------------------------------------------- state
369
410
 
370
411
  state_merge "$INSTANCE" "$(jq -c -n \
@@ -58,11 +58,20 @@ require_provisioned() {
58
58
  [ -f "$VELA_ETC/provisioned" ] || die "server is not provisioned - run 'vela provision' first"
59
59
  }
60
60
 
61
- # Refuse on a frontend-only instance. Absent state is treated as having one, so
62
- # this only ever fires on an instance that was deployed with --backend 0.
61
+ # Whether an instance was last deployed with a PocketBase backend: `true` or
62
+ # `false`. Absent state, or state from before the flag existed, is treated as
63
+ # having one, so this only ever answers `false` for an instance that was
64
+ # deployed with --backend 0.
65
+ instance_backend() {
66
+ local instance=$1 value
67
+ value=$(state_get "$instance" backend 2>/dev/null || echo true)
68
+ [ "$value" = "false" ] && printf 'false' || printf 'true'
69
+ }
70
+
71
+ # Refuse on a frontend-only instance.
63
72
  require_backend() {
64
73
  local instance=$1
65
- [ "$(state_get "$instance" backend 2>/dev/null || echo true)" = "true" ] \
74
+ [ "$(instance_backend "$instance")" = "true" ] \
66
75
  || die "$instance has no database - there is nothing to back up or restore"
67
76
  }
68
77
 
@@ -71,8 +80,10 @@ state_get() {
71
80
  local instance=$1 key=$2 file
72
81
  file=$(state_file "$instance")
73
82
  [ -f "$file" ] || return 1
74
- # `// empty` would swallow a legitimate `false`, so test for the key itself.
75
- jq -er --arg k "$key" 'if has($k) and .[$k] != null then .[$k] else empty end' "$file" 2>/dev/null
83
+ # Presence is checked on its own: `// empty` would swallow a legitimate
84
+ # `false`, and so would `-e` on the read, which exits non-zero for one.
85
+ jq -e --arg k "$key" 'has($k) and .[$k] != null' "$file" >/dev/null 2>&1 || return 1
86
+ jq -r --arg k "$key" '.[$k]' "$file" 2>/dev/null
76
87
  }
77
88
 
78
89
  # Merge a JSON object into an instance's state file, atomically.
@@ -41,10 +41,10 @@ CURRENT=$(state_get "$INSTANCE" activeRelease || echo "")
41
41
  [ -d "$APP/releases/$TARGET" ] || die "release $TARGET is no longer on disk"
42
42
  [ "$TARGET" != "$CURRENT" ] || die "$TARGET is already the active release"
43
43
 
44
- BACKEND=$(state_get "$INSTANCE" backend 2>/dev/null || echo "true")
44
+ BACKEND=$(instance_backend "$INSTANCE")
45
45
  HEALTH_PATH=$(state_get "$INSTANCE" healthCheckPath 2>/dev/null || echo "/")
46
46
  WEB_PORT=$(state_get "$INSTANCE" webPort)
47
- PB_PORT=$(state_get "$INSTANCE" pbPort)
47
+ PB_PORT=$(state_get "$INSTANCE" pbPort 2>/dev/null || echo "")
48
48
  WEB_UNIT=$(unit_web "$INSTANCE")
49
49
  PB_UNIT=$(unit_pb "$INSTANCE")
50
50