vela 0.14.1 → 0.14.3
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 +23 -8
- package/dist/bin.js +2627 -2390
- package/dist/bin.js.map +4 -4
- package/package.json +2 -2
- package/templates/server/apply.sh +28 -8
- package/templates/server/lib.sh +45 -2
- package/templates/server/restore.sh +1 -1
- package/templates/server/rollback.sh +1 -1
- package/templates/static/vite.config.ts +16 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vela",
|
|
3
|
-
"version": "0.14.
|
|
3
|
+
"version": "0.14.3",
|
|
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.3.
|
|
39
|
+
"@velastack/patterns": "^0.3.2",
|
|
40
40
|
"@velastack/pocketbase-codegen": "^0.1.0",
|
|
41
41
|
"annotate-json-schema": "^0.1.0",
|
|
42
42
|
"commander": "^13.1.0",
|
|
@@ -157,9 +157,9 @@ PB_PORT=$(printf '%s' "$PORTS" | jq -r .pb)
|
|
|
157
157
|
PRIMARY_DOMAIN=$(printf '%s' "$DOMAIN" | cut -d, -f1 | tr -d ' ')
|
|
158
158
|
[ -n "$PRIMARY_DOMAIN" ] || PRIMARY_DOMAIN=$(printf '%s' "$MANAGED" | cut -d, -f1 | tr -d ' ')
|
|
159
159
|
if [ -n "$PRIMARY_DOMAIN" ]; then
|
|
160
|
-
|
|
160
|
+
PRIMARY_URL="https://$PRIMARY_DOMAIN"
|
|
161
161
|
else
|
|
162
|
-
|
|
162
|
+
PRIMARY_URL="http://127.0.0.1:$WEB_PORT"
|
|
163
163
|
fi
|
|
164
164
|
|
|
165
165
|
# Production secrets live in $ETC/env and are managed only by `vela env`.
|
|
@@ -172,7 +172,7 @@ runtime_tmp=$(mktemp "$ETC/.runtime.XXXXXX")
|
|
|
172
172
|
printf 'NODE_ENV=production\n'
|
|
173
173
|
printf 'HOST=127.0.0.1\n'
|
|
174
174
|
printf 'PORT=%s\n' "$WEB_PORT"
|
|
175
|
-
|
|
175
|
+
runtime_origin_lines "$DOMAIN" "$PRIMARY_URL"
|
|
176
176
|
# Only an instance with a PocketBase gets pointed at one: a URL to a port
|
|
177
177
|
# nothing listens on is not a setting, it is a trap. Kept for one more
|
|
178
178
|
# deploy when the backend is being removed, so that a failed deploy can put
|
|
@@ -209,7 +209,7 @@ fi
|
|
|
209
209
|
|
|
210
210
|
install_deps() {
|
|
211
211
|
local lock="$RELEASE_DIR/package-lock.json"
|
|
212
|
-
local key
|
|
212
|
+
local key foreign="" name
|
|
213
213
|
local -a install_cmd
|
|
214
214
|
# `--no-engine-strict`: vela projects set engine-strict for development, but a
|
|
215
215
|
# production install takes only `dependencies` - a dev tool that wants a newer
|
|
@@ -219,7 +219,13 @@ install_deps() {
|
|
|
219
219
|
key=$(sha256sum "$lock" | cut -c1-16)
|
|
220
220
|
install_cmd=(npm ci "${flags[@]}")
|
|
221
221
|
elif [ -f "$RELEASE_DIR/package.json" ]; then
|
|
222
|
-
|
|
222
|
+
# A project pinned by pnpm, yarn or bun: npm cannot replay the lockfile, so
|
|
223
|
+
# this resolves afresh, but the key still follows it - a dependency bump
|
|
224
|
+
# that only touched the lockfile must not reuse the old tree.
|
|
225
|
+
for name in pnpm-lock.yaml yarn.lock bun.lock bun.lockb; do
|
|
226
|
+
if [ -f "$RELEASE_DIR/$name" ]; then foreign="$RELEASE_DIR/$name"; break; fi
|
|
227
|
+
done
|
|
228
|
+
key=$(cat "$RELEASE_DIR/package.json" ${foreign:+"$foreign"} | sha256sum | cut -c1-16)
|
|
223
229
|
install_cmd=(npm install "${flags[@]}")
|
|
224
230
|
else
|
|
225
231
|
log "no package.json in release - skipping dependency install"
|
|
@@ -233,6 +239,20 @@ install_deps() {
|
|
|
233
239
|
mkdir -p "$deps"
|
|
234
240
|
cp "$RELEASE_DIR/package.json" "$deps/"
|
|
235
241
|
if [ -f "$lock" ]; then cp "$lock" "$deps/"; fi
|
|
242
|
+
# npm reads a yarn.lock beside package.json for resolution hints.
|
|
243
|
+
if [ "${foreign##*/}" = "yarn.lock" ]; then cp "$foreign" "$deps/"; fi
|
|
244
|
+
# `prepare` is a development hook (`svelte-kit sync`, husky) whose tools
|
|
245
|
+
# are devDependencies, which are not installed here: it can only print
|
|
246
|
+
# "svelte-kit: not found". Dependencies' own install scripts still run.
|
|
247
|
+
node -e '
|
|
248
|
+
const fs = require("fs");
|
|
249
|
+
const file = process.argv[1];
|
|
250
|
+
const pkg = JSON.parse(fs.readFileSync(file, "utf8"));
|
|
251
|
+
if (pkg.scripts && pkg.scripts.prepare) {
|
|
252
|
+
delete pkg.scripts.prepare;
|
|
253
|
+
fs.writeFileSync(file, JSON.stringify(pkg, null, 2) + "\n");
|
|
254
|
+
}
|
|
255
|
+
' "$deps/package.json" || die "could not read package.json"
|
|
236
256
|
if [ -f "$RELEASE_DIR/.npmrc" ]; then cp "$RELEASE_DIR/.npmrc" "$deps/"; fi
|
|
237
257
|
chown -R "$VELA_USER:$VELA_USER" "$deps"
|
|
238
258
|
# npm needs a writable HOME and cache; $VELA_ROOT itself stays root-owned
|
|
@@ -392,7 +412,7 @@ fi
|
|
|
392
412
|
log "starting app on 127.0.0.1:$WEB_PORT"
|
|
393
413
|
systemctl enable "$WEB_UNIT" >/dev/null 2>&1 || true
|
|
394
414
|
systemctl restart "$WEB_UNIT"
|
|
395
|
-
wait_for_http "http://127.0.0.1:$WEB_PORT$HEALTH_PATH" 60 0.5 \
|
|
415
|
+
wait_for_http "http://127.0.0.1:$WEB_PORT$HEALTH_PATH" 60 0.5 "$PRIMARY_DOMAIN" \
|
|
396
416
|
|| die "app did not become healthy at $HEALTH_PATH - journalctl -u $WEB_UNIT"
|
|
397
417
|
|
|
398
418
|
ACTIVATED=1
|
|
@@ -411,7 +431,7 @@ fi
|
|
|
411
431
|
state_merge "$INSTANCE" "$(jq -c -n \
|
|
412
432
|
--arg app "$APP_ID" --arg name "$APP_NAME" --arg env "$ENV_TAG" \
|
|
413
433
|
--arg instance "$INSTANCE" --arg release "$RELEASE" --arg previous "$PREVIOUS" \
|
|
414
|
-
--arg domain "$DOMAIN" --arg managed "$MANAGED" --arg url "$
|
|
434
|
+
--arg domain "$DOMAIN" --arg managed "$MANAGED" --arg url "$PRIMARY_URL" \
|
|
415
435
|
--arg health "$HEALTH_PATH" --arg pb "$PB_VERSION" \
|
|
416
436
|
--arg sha "$GIT_SHA" --arg at "$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
|
|
417
437
|
--argjson web "$WEB_PORT" --argjson pbport "$PB_PORT" --argjson backend "$BACKEND" \
|
|
@@ -510,6 +530,6 @@ fi
|
|
|
510
530
|
|
|
511
531
|
emit_result \
|
|
512
532
|
--arg instance "$INSTANCE" --arg release "$RELEASE" --arg domain "$DOMAIN" --arg managed "$MANAGED" \
|
|
513
|
-
--arg url "$
|
|
533
|
+
--arg url "$PRIMARY_URL" --argjson web "$WEB_PORT" --argjson pb "$PB_PORT" --argjson su "$SU_CREATED" \
|
|
514
534
|
'{instance: $instance, release: $release, domain: $domain, managed: $managed, url: $url,
|
|
515
535
|
webPort: $web, pbPort: $pb, superuserCreated: ($su == 1)}'
|
package/templates/server/lib.sh
CHANGED
|
@@ -171,11 +171,17 @@ unit_active() { systemctl is-active --quiet "$1"; }
|
|
|
171
171
|
# a health path behind auth). A 404 is not that: it is what a wrong
|
|
172
172
|
# --health-path or a route that never mounted looks like, and it used to pass.
|
|
173
173
|
# No `-f`: curl has to report the status of an error response, not fail on it.
|
|
174
|
+
#
|
|
175
|
+
# A fourth argument is the public host to ask as, the way Caddy would. An app
|
|
176
|
+
# serving several hosts has no pinned ORIGIN and tells its sites apart by
|
|
177
|
+
# `Host`; asked as 127.0.0.1 it may rightly answer that it serves no such site.
|
|
174
178
|
wait_for_http() {
|
|
175
|
-
local url=$1 attempts=${2:-60} delay=${3:-0.5} code
|
|
179
|
+
local url=$1 attempts=${2:-60} delay=${3:-0.5} host=${4:-} code
|
|
176
180
|
local i=0
|
|
181
|
+
local -a as=()
|
|
182
|
+
[ -z "$host" ] || as=(-H "Host: $host" -H 'X-Forwarded-Proto: https')
|
|
177
183
|
while [ "$i" -lt "$attempts" ]; do
|
|
178
|
-
code=$(curl -sS -o /dev/null -w '%{http_code}' --max-time 5 "$url" 2>/dev/null || echo 000)
|
|
184
|
+
code=$(curl -sS -o /dev/null -w '%{http_code}' --max-time 5 ${as[@]+"${as[@]}"} "$url" 2>/dev/null || echo 000)
|
|
179
185
|
case "$code" in
|
|
180
186
|
2*|3*|401|403) return 0 ;;
|
|
181
187
|
esac
|
|
@@ -185,6 +191,43 @@ wait_for_http() {
|
|
|
185
191
|
return 1
|
|
186
192
|
}
|
|
187
193
|
|
|
194
|
+
# The public host an instance's health check should ask as: the host of the
|
|
195
|
+
# `url` its last deploy recorded, or nothing for an instance no domain points
|
|
196
|
+
# at (whose url is its own loopback address).
|
|
197
|
+
#
|
|
198
|
+
# usage: state_primary_host <instance>
|
|
199
|
+
state_primary_host() {
|
|
200
|
+
local url
|
|
201
|
+
url=$(state_get "$1" url 2>/dev/null) || return 0
|
|
202
|
+
case "$url" in
|
|
203
|
+
https://*) url=${url#https://}; printf '%s' "${url%%/*}" ;;
|
|
204
|
+
esac
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
# The lines of runtime.env that tell adapter-node what origin a request has.
|
|
208
|
+
#
|
|
209
|
+
# One served host: pin it with ORIGIN, so nothing a client sends can change
|
|
210
|
+
# what the app believes its own address is. Several hosts served directly
|
|
211
|
+
# (velastack.dev and velabase.dev from one app) cannot share an ORIGIN - the
|
|
212
|
+
# second host would render as the first and have its form posts refused as
|
|
213
|
+
# cross-site - so the origin comes from the request instead: `Host`, which
|
|
214
|
+
# Caddy passes through and only ever for the names in this instance's site
|
|
215
|
+
# block, and the scheme from the X-Forwarded-Proto Caddy sets itself.
|
|
216
|
+
#
|
|
217
|
+
# Managed velastack.app names do not count: beside a direct host they redirect
|
|
218
|
+
# at the edge rather than being served.
|
|
219
|
+
#
|
|
220
|
+
# usage: runtime_origin_lines <direct_hosts_csv> <primary_url>
|
|
221
|
+
runtime_origin_lines() {
|
|
222
|
+
local direct=$1 primary_url=$2 count
|
|
223
|
+
count=$(printf '%s' "$direct" | tr ',' '\n' | sed 's/^ *//; s/ *$//' | grep -c -v '^$' || true)
|
|
224
|
+
if [ "$count" -gt 1 ]; then
|
|
225
|
+
printf 'PROTOCOL_HEADER=x-forwarded-proto\n'
|
|
226
|
+
else
|
|
227
|
+
printf 'ORIGIN=%s\n' "$primary_url"
|
|
228
|
+
fi
|
|
229
|
+
}
|
|
230
|
+
|
|
188
231
|
# How many migrations the current release has that the target release does not.
|
|
189
232
|
# PocketBase reverts by count, so this is what `migrate down` needs.
|
|
190
233
|
migrations_ahead() {
|
|
@@ -220,7 +220,7 @@ assert_superuser_auth "$PB_PORT" "$SU_EMAIL" "$SU_PASSWORD" \
|
|
|
220
220
|
|
|
221
221
|
log "starting app on 127.0.0.1:$WEB_PORT"
|
|
222
222
|
systemctl start "$WEB_UNIT"
|
|
223
|
-
wait_for_http "http://127.0.0.1:$WEB_PORT$HEALTH_PATH" 60 0.5 \
|
|
223
|
+
wait_for_http "http://127.0.0.1:$WEB_PORT$HEALTH_PATH" 60 0.5 "$(state_primary_host "$INSTANCE")" \
|
|
224
224
|
|| die "app did not become healthy at $HEALTH_PATH - journalctl -u $WEB_UNIT"
|
|
225
225
|
|
|
226
226
|
RESTORED=1
|
|
@@ -74,7 +74,7 @@ if [ "$BACKEND" = "true" ]; then
|
|
|
74
74
|
|| die "PocketBase did not become healthy after rollback"
|
|
75
75
|
fi
|
|
76
76
|
systemctl restart "$WEB_UNIT"
|
|
77
|
-
wait_for_http "http://127.0.0.1:$WEB_PORT$HEALTH_PATH" 60 0.5 \
|
|
77
|
+
wait_for_http "http://127.0.0.1:$WEB_PORT$HEALTH_PATH" 60 0.5 "$(state_primary_host "$INSTANCE")" \
|
|
78
78
|
|| die "app did not become healthy after rollback"
|
|
79
79
|
|
|
80
80
|
state_merge "$INSTANCE" "$(jq -c -n --arg t "$TARGET" --arg c "$CURRENT" \
|
|
@@ -3,6 +3,14 @@ import tailwindcss from '@tailwindcss/vite';
|
|
|
3
3
|
import { sveltekit } from '@sveltejs/kit/vite';
|
|
4
4
|
import adapter from '@sveltejs/adapter-static';
|
|
5
5
|
|
|
6
|
+
/**
|
|
7
|
+
* The footer links to these before they exist; `vela legal privacy` and
|
|
8
|
+
* `vela legal terms` generate them. Prerendering crawls every link and fails
|
|
9
|
+
* the build on a 404, so a project would not build until both were written.
|
|
10
|
+
* Once generated they prerender like any other page.
|
|
11
|
+
*/
|
|
12
|
+
const PENDING_LEGAL_ROUTES = ['/privacy', '/terms'];
|
|
13
|
+
|
|
6
14
|
export default defineConfig({
|
|
7
15
|
plugins: [
|
|
8
16
|
tailwindcss(),
|
|
@@ -14,7 +22,14 @@ export default defineConfig({
|
|
|
14
22
|
adapter: adapter({
|
|
15
23
|
fallback: '200.html'
|
|
16
24
|
}),
|
|
17
|
-
|
|
25
|
+
prerender: {
|
|
26
|
+
// Every other broken link still fails the build.
|
|
27
|
+
handleHttpError: ({ path, status, message }) => {
|
|
28
|
+
if (status === 404 && PENDING_LEGAL_ROUTES.includes(path)) return;
|
|
29
|
+
throw new Error(message);
|
|
30
|
+
},
|
|
31
|
+
...(process.env.VELA_ORIGIN ? { origin: process.env.VELA_ORIGIN } : {})
|
|
32
|
+
}
|
|
18
33
|
})
|
|
19
34
|
]
|
|
20
35
|
});
|