@stacksjs/ts-cloud 0.7.60 → 0.7.62

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.
Files changed (55) hide show
  1. package/dist/bin/cli.js +365 -365
  2. package/dist/{chunk-cz17nhnx.js → chunk-ff77qy0c.js} +1 -1
  3. package/dist/{chunk-sd9h1baj.js → chunk-vx2yva1a.js} +55 -9
  4. package/dist/deploy/index.js +2 -2
  5. package/dist/drivers/index.js +1 -1
  6. package/dist/drivers/shared/releases.d.ts +43 -0
  7. package/dist/index.js +2 -2
  8. package/dist/ui/account/automation.html +3 -3
  9. package/dist/ui/account/security.html +2 -2
  10. package/dist/ui/applications/compose.html +4 -4
  11. package/dist/ui/applications/new.html +2 -2
  12. package/dist/ui/data/backups.html +4 -4
  13. package/dist/ui/data/services.html +4 -4
  14. package/dist/ui/data/volumes.html +4 -4
  15. package/dist/ui/index.html +4 -4
  16. package/dist/ui/operations/alerts.html +4 -4
  17. package/dist/ui/operations/configuration.html +4 -4
  18. package/dist/ui/operations/jobs.html +4 -4
  19. package/dist/ui/operations/maintenance.html +3 -3
  20. package/dist/ui/operations/observability.html +4 -4
  21. package/dist/ui/operations/previews.html +3 -3
  22. package/dist/ui/operations/queue.html +3 -3
  23. package/dist/ui/operations/regions.html +3 -3
  24. package/dist/ui/operations/releases.html +3 -3
  25. package/dist/ui/operations/workloads.html +4 -4
  26. package/dist/ui/security.html +2 -2
  27. package/dist/ui/server/actions.html +3 -3
  28. package/dist/ui/server/activity.html +2 -2
  29. package/dist/ui/server/capacity.html +4 -4
  30. package/dist/ui/server/database.html +3 -3
  31. package/dist/ui/server/deployments.html +4 -4
  32. package/dist/ui/server/diagnostics.html +2 -2
  33. package/dist/ui/server/firewall.html +4 -4
  34. package/dist/ui/server/fleet.html +4 -4
  35. package/dist/ui/server/logs.html +4 -4
  36. package/dist/ui/server/metrics.html +2 -2
  37. package/dist/ui/server/security.html +2 -2
  38. package/dist/ui/server/services.html +2 -2
  39. package/dist/ui/server/sites.html +4 -4
  40. package/dist/ui/server/ssh-keys.html +4 -4
  41. package/dist/ui/server/team.html +4 -4
  42. package/dist/ui/server/terminal.html +2 -2
  43. package/dist/ui/serverless/alarms.html +4 -4
  44. package/dist/ui/serverless/assets.html +2 -2
  45. package/dist/ui/serverless/cost.html +2 -2
  46. package/dist/ui/serverless/data.html +4 -4
  47. package/dist/ui/serverless/deployments.html +2 -2
  48. package/dist/ui/serverless/functions.html +4 -4
  49. package/dist/ui/serverless/logs.html +4 -4
  50. package/dist/ui/serverless/metrics.html +2 -2
  51. package/dist/ui/serverless/queues.html +4 -4
  52. package/dist/ui/serverless/secrets.html +4 -4
  53. package/dist/ui/serverless/traces.html +4 -4
  54. package/dist/ui/serverless.html +4 -4
  55. package/package.json +3 -3
@@ -45,7 +45,7 @@ import {
45
45
  resolveSiteKind,
46
46
  resolveUiSource,
47
47
  siteInstallBase
48
- } from "./chunk-sd9h1baj.js";
48
+ } from "./chunk-vx2yva1a.js";
49
49
  import {
50
50
  artifactKey,
51
51
  buildCloudFormationTemplate,
@@ -3808,6 +3808,48 @@ function buildEnsureReleaseLayout(paths, sharedPaths = DEFAULT_SHARED_PATHS) {
3808
3808
  }
3809
3809
  return lines;
3810
3810
  }
3811
+ function buildDeployLock(paths, waitSeconds = 900) {
3812
+ const lock = `${deployMetaDir(paths.base)}/deploy.lock`;
3813
+ return [
3814
+ `mkdir -p "$(dirname ${lock})"`,
3815
+ `exec 9>${lock}`,
3816
+ `flock -w ${waitSeconds} 9 || { echo "[ts-cloud] another deploy of this site has held the lock for ${waitSeconds}s — refusing to race it" >&2; exit 1; }`
3817
+ ];
3818
+ }
3819
+ function buildResetReleaseDir(paths) {
3820
+ const staging = `${paths.release}.incoming`;
3821
+ return [
3822
+ `rm -rf ${staging}`,
3823
+ `mkdir -p ${staging}`,
3824
+ ...buildIsReleaseLive(paths),
3825
+ 'if [ "$TS_CLOUD_IS_LIVE" = "yes" ]; then',
3826
+ ` TS_CLOUD_STAGED=${staging}`,
3827
+ "else",
3828
+ ` rm -rf ${paths.release}`,
3829
+ ` mkdir -p ${paths.release}`,
3830
+ ` rmdir ${staging}`,
3831
+ ` TS_CLOUD_STAGED=${paths.release}`,
3832
+ "fi"
3833
+ ];
3834
+ }
3835
+ function buildIsReleaseLive(paths) {
3836
+ return [
3837
+ `TS_CLOUD_LIVE_PATH="$(readlink -f ${paths.current} 2>/dev/null || true)"`,
3838
+ `TS_CLOUD_RELEASE_PATH="$(readlink -f ${paths.release} 2>/dev/null || echo ${paths.release})"`,
3839
+ 'if [ -n "$TS_CLOUD_LIVE_PATH" ] && [ "$TS_CLOUD_LIVE_PATH" = "$TS_CLOUD_RELEASE_PATH" ]; then TS_CLOUD_IS_LIVE=yes; else TS_CLOUD_IS_LIVE=no; fi'
3840
+ ];
3841
+ }
3842
+ function buildPromoteStagedRelease(paths) {
3843
+ const staging = `${paths.release}.incoming`;
3844
+ return [
3845
+ `if [ "$TS_CLOUD_STAGED" = "${staging}" ]; then`,
3846
+ ` rm -rf ${paths.release}.previous`,
3847
+ ` mv ${paths.release} ${paths.release}.previous`,
3848
+ ` mv ${staging} ${paths.release}`,
3849
+ ` rm -rf ${paths.release}.previous`,
3850
+ "fi"
3851
+ ];
3852
+ }
3811
3853
  function buildLinkSharedPaths(paths, sharedPaths = DEFAULT_SHARED_PATHS) {
3812
3854
  const lines = [];
3813
3855
  for (const p of sharedPaths) {
@@ -3921,17 +3963,19 @@ function buildSiteDeployScript(options) {
3921
3963
  const preStart = preStartCommands.length > 0 ? [`cd ${paths.release}`, ...preStartCommands] : [];
3922
3964
  const stageRelease = [
3923
3965
  "set -euo pipefail",
3924
- `trap 'if [ $? -ne 0 ] && [ "$(readlink -f ${paths.current} 2>/dev/null || true)" != "${paths.release}" ]; then rm -rf ${paths.release}; fi' EXIT`,
3966
+ ...buildDeployLock(paths),
3967
+ ...buildIsReleaseLive(paths),
3968
+ `trap 'if [ $? -ne 0 ] && [ "$TS_CLOUD_IS_LIVE" != "yes" ]; then rm -rf ${paths.release}; fi' EXIT`,
3925
3969
  ...artifactFetch,
3926
3970
  ...buildEnsureReleaseLayout(paths, sharedPaths),
3927
- `rm -rf ${paths.release}`,
3928
- `mkdir -p ${paths.release}`,
3929
- `tar xzf ${tarball} -C ${paths.release}`,
3971
+ ...buildResetReleaseDir(paths),
3972
+ `tar xzf ${tarball} -C "$TS_CLOUD_STAGED"`,
3930
3973
  `rm -f ${tarball}`,
3931
3974
  `cat > ${paths.shared}/.env <<'TS_CLOUD_ENV_EOF'`,
3932
3975
  envFile,
3933
3976
  "TS_CLOUD_ENV_EOF",
3934
3977
  `chmod 600 ${paths.shared}/.env`,
3978
+ ...buildPromoteStagedRelease(paths),
3935
3979
  `for TS_CLOUD_ENV in ${paths.shared}/.env ${paths.release}/.env ${paths.release}/.env.*; do [ -f "$TS_CLOUD_ENV" ] && sed -i -E '/^[[:space:]]*(PORT|PORT_BACKEND|PORT_ADMIN|PORT_FRONTEND)[[:space:]]*=/d' "$TS_CLOUD_ENV" 2>/dev/null || true; done`,
3936
3980
  ...buildLinkSharedPaths(paths, sharedPaths),
3937
3981
  ...preStart
@@ -3962,7 +4006,7 @@ function buildSiteDeployScript(options) {
3962
4006
  "systemctl daemon-reload",
3963
4007
  `TS_CLOUD_OLD_UNITS=$(systemctl list-units --plain --no-legend --type=service "${unitBase}@*.service" 2>/dev/null | awk '{print $1}' | grep -v "^${instance}$" || true)`,
3964
4008
  `if [ -f /etc/systemd/system/${serviceName} ] && systemctl is-active --quiet ${serviceName}; then echo "[ts-cloud] retiring pre-zero-downtime unit ${serviceName} (one-time restart cutover)"; systemctl stop ${serviceName}; fi`,
3965
- `systemctl start ${instance}`,
4009
+ `systemctl restart ${instance}`,
3966
4010
  `TS_CLOUD_GATE_OK=1; for TS_CLOUD_I in $(seq 1 ${Math.max(1, healthGateSeconds)}); do sleep 1; systemctl is-active --quiet ${instance} || { TS_CLOUD_GATE_OK=0; break; }; done`,
3967
4011
  `if [ "$TS_CLOUD_GATE_OK" -ne 1 ]; then echo "[ts-cloud] release ${releaseId} could not overlap the previous release (no SO_REUSEPORT?) — retiring old instances and retrying" >&2; for TS_CLOUD_RU in \${TS_CLOUD_OLD_UNITS}; do systemctl stop "$TS_CLOUD_RU" 2>/dev/null || true; done; systemctl restart ${instance}; for TS_CLOUD_I in $(seq 1 ${Math.max(1, healthGateSeconds)}); do sleep 1; systemctl is-active --quiet ${instance} || ${failGate}; done; fi`,
3968
4012
  ...gatePath ? [`curl -sf -o /dev/null --max-time 10 "http://127.0.0.1:${port}${gatePath}" || ${failGate}`] : [],
@@ -4009,12 +4053,14 @@ function buildStaticSiteDeployScript(options) {
4009
4053
  const preStart = preStartCommands.length > 0 ? [`cd ${paths.release}`, ...preStartCommands] : [];
4010
4054
  return [
4011
4055
  "set -euo pipefail",
4012
- `trap 'if [ $? -ne 0 ] && [ "$(readlink -f ${paths.current} 2>/dev/null || true)" != "${paths.release}" ]; then rm -rf ${paths.release}; fi' EXIT`,
4056
+ ...buildDeployLock(paths),
4057
+ ...buildIsReleaseLive(paths),
4058
+ `trap 'if [ $? -ne 0 ] && [ "$TS_CLOUD_IS_LIVE" != "yes" ]; then rm -rf ${paths.release}; fi' EXIT`,
4013
4059
  ...artifactFetch,
4014
4060
  ...buildEnsureReleaseLayout(paths, []),
4015
- `rm -rf ${paths.release}`,
4016
- `mkdir -p ${paths.release}`,
4017
- `tar xzf ${tarball} -C ${paths.release}`,
4061
+ ...buildResetReleaseDir(paths),
4062
+ `tar xzf ${tarball} -C "$TS_CLOUD_STAGED"`,
4063
+ ...buildPromoteStagedRelease(paths),
4018
4064
  `rm -f ${tarball}`,
4019
4065
  ...preStart,
4020
4066
  ...buildActivateRelease(paths),
@@ -32,7 +32,7 @@ import {
32
32
  synchronizeDashboardUsers,
33
33
  trackDashboardOperation,
34
34
  verifyStaticApiOrigin
35
- } from "../chunk-cz17nhnx.js";
35
+ } from "../chunk-ff77qy0c.js";
36
36
  import {
37
37
  deleteStaticSite,
38
38
  deployStaticSite,
@@ -65,7 +65,7 @@ import {
65
65
  shipsARelease,
66
66
  siteInstallBase,
67
67
  validateDeploymentConfig
68
- } from "../chunk-sd9h1baj.js";
68
+ } from "../chunk-vx2yva1a.js";
69
69
  import"../chunk-tq46tnxv.js";
70
70
  import"../chunk-703nkybg.js";
71
71
  import"../chunk-4cjrg98a.js";
@@ -52,7 +52,7 @@ import {
52
52
  waitForCloudInit,
53
53
  waitForSsh,
54
54
  wrapCloudInitUserData
55
- } from "../chunk-sd9h1baj.js";
55
+ } from "../chunk-vx2yva1a.js";
56
56
  import"../chunk-tq46tnxv.js";
57
57
  import"../chunk-703nkybg.js";
58
58
  import"../chunk-4cjrg98a.js";
@@ -43,6 +43,49 @@ export declare function releasePaths(base: string, releaseId: string): ReleasePa
43
43
  * `storage` tree and an empty shared `.env` so symlinks never dangle.
44
44
  */
45
45
  export declare function buildEnsureReleaseLayout(paths: ReleasePaths, sharedPaths?: readonly string[]): string[];
46
+ /**
47
+ * Take the site's deploy lock for the rest of the script.
48
+ *
49
+ * Two deploys of one site used to be free to run at the same time, and they
50
+ * write to the same directories: the second one's `rm -rf releases/<id>` ran
51
+ * against a tree the first was still extracting into, which fails with
52
+ * "Directory not empty" — an error that reads like a permissions problem and
53
+ * is really a race. It happens more easily than it sounds, because a deploy
54
+ * whose *client* is interrupted keeps running on the box; the operator sees a
55
+ * dead terminal and re-runs.
56
+ *
57
+ * `flock` on a descriptor held by the shell serializes them, and the lock is
58
+ * released when the script exits however it exits. Waiting rather than failing
59
+ * outright, because the common case is a retry that is only a few seconds
60
+ * ahead of itself.
61
+ */
62
+ export declare function buildDeployLock(paths: ReleasePaths, waitSeconds?: number): string[];
63
+ /**
64
+ * Clear the release directory, unless it is the one currently being served.
65
+ *
66
+ * Re-deploying a release id that is already live — the same commit twice, a
67
+ * retry after an interrupted run — used to `rm -rf` the directory `current`
68
+ * points at, so the site was serving a half-deleted tree until the new
69
+ * extraction finished. The replacement is staged beside it and swapped in with
70
+ * two renames, which is atomic enough that no request lands mid-swap.
71
+ */
72
+ export declare function buildResetReleaseDir(paths: ReleasePaths): string[];
73
+ /**
74
+ * Set `TS_CLOUD_IS_LIVE` to "yes" when `current` resolves to this release.
75
+ *
76
+ * Both sides are resolved before comparing. Comparing a resolved `current`
77
+ * against the literal release path looks equivalent and is not: one symlinked
78
+ * ancestor anywhere above the site — `/var/www` moved onto a data volume, a
79
+ * `/tmp` that is really `/private/tmp` — makes the two spellings differ, the
80
+ * guard misses, and the branch it was guarding deletes the directory the site
81
+ * is being served from.
82
+ */
83
+ export declare function buildIsReleaseLive(paths: ReleasePaths): string[];
84
+ /**
85
+ * Move a staged release into place, if it was staged (see
86
+ * {@link buildResetReleaseDir}). A no-op when the deploy extracted directly.
87
+ */
88
+ export declare function buildPromoteStagedRelease(paths: ReleasePaths): string[];
46
89
  /**
47
90
  * Symlink every shared path from `shared/` into the freshly checked-out release,
48
91
  * replacing whatever the checkout shipped (e.g. the repo's empty `storage`).
package/dist/index.js CHANGED
@@ -285,7 +285,7 @@ import {
285
285
  volumeCapabilities,
286
286
  webhookEndpoint,
287
287
  zeroCapacity
288
- } from "./chunk-cz17nhnx.js";
288
+ } from "./chunk-ff77qy0c.js";
289
289
  import {
290
290
  deleteStaticSite,
291
291
  deployStaticSite,
@@ -398,7 +398,7 @@ import {
398
398
  waitForCloudInit,
399
399
  waitForSsh,
400
400
  wrapCloudInitUserData
401
- } from "./chunk-sd9h1baj.js";
401
+ } from "./chunk-vx2yva1a.js";
402
402
  import {
403
403
  ABTestManager,
404
404
  AI,