@stacksjs/ts-cloud 0.2.20 → 0.2.21

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.
@@ -91,10 +91,12 @@ export interface CreateSshKeyOptions {
91
91
  publicKey: string;
92
92
  labels?: Record<string, string>;
93
93
  }
94
+ /** Minimal fetch signature the client relies on (always called with a string URL). */
95
+ export type HetznerFetch = (url: string, init?: RequestInit) => Promise<Response>;
94
96
  export interface HetznerClientOptions {
95
97
  apiToken: string;
96
98
  baseUrl?: string;
97
- fetchImpl?: typeof fetch;
99
+ fetchImpl?: HetznerFetch;
98
100
  }
99
101
  export declare class HetznerClient {
100
102
  readonly name = "hetzner";
@@ -13,5 +13,12 @@ export interface UbuntuBootstrapOptions {
13
13
  export declare function generateUbuntuAppCloudInit(options?: UbuntuBootstrapOptions): string;
14
14
  /**
15
15
  * Wrap a bash bootstrap script as Hetzner cloud-init user_data (#cloud-config).
16
+ *
17
+ * The script is written to disk via `write_files` and then executed through an
18
+ * explicit `bash` invocation in `runcmd`. cloud-init runs bare `runcmd` entries
19
+ * with `/bin/sh` (dash on Ubuntu), which chokes on bash-only syntax like
20
+ * `set -o pipefail` and aborts the whole bootstrap — so embedding the script
21
+ * inline under `runcmd:` silently breaks bun/caddy installation. Writing the
22
+ * file (shebang preserved) and running `bash <file>` guarantees a bash shell.
16
23
  */
17
24
  export declare function wrapCloudInitUserData(bootstrapScript: string): string;
@@ -15,6 +15,13 @@ export interface BuildSiteDeployScriptOptions {
15
15
  execStart: string;
16
16
  envEntries: Record<string, string>;
17
17
  port?: number;
18
+ /**
19
+ * Commands run inside `appDir` after extraction + `.env` write, before the
20
+ * systemd unit is (re)written and started. Typically dependency install
21
+ * and/or build steps (e.g. `bun install --frozen-lockfile`, `bun run build`)
22
+ * so the release tarball can omit `node_modules`.
23
+ */
24
+ preStartCommands?: string[];
18
25
  }
19
26
  /**
20
27
  * Build the remote shell commands that install/refresh a site on a compute target.
package/dist/index.js CHANGED
@@ -81435,12 +81435,19 @@ echo "ts-cloud bootstrap complete — instance is ready for site deploys"
81435
81435
  return script;
81436
81436
  }
81437
81437
  function wrapCloudInitUserData(bootstrapScript) {
81438
+ const scriptPath = "/var/lib/cloud/ts-cloud-bootstrap.sh";
81439
+ const indented = bootstrapScript.split(`
81440
+ `).map((line) => ` ${line}`).join(`
81441
+ `);
81438
81442
  return `#cloud-config
81443
+ write_files:
81444
+ - path: ${scriptPath}
81445
+ permissions: '0755'
81446
+ owner: root:root
81447
+ content: |
81448
+ ${indented}
81439
81449
  runcmd:
81440
- - |
81441
- ${bootstrapScript.split(`
81442
- `).join(`
81443
- `)}
81450
+ - [ bash, ${scriptPath} ]
81444
81451
  `;
81445
81452
  }
81446
81453
 
@@ -81464,10 +81471,10 @@ function buildHetznerFirewallRules(config6) {
81464
81471
  // src/drivers/hetzner/instance-sizes.ts
81465
81472
  var HETZNER_INSTANCE_TYPES = {
81466
81473
  micro: "cpx11",
81467
- small: "cx22",
81468
- medium: "cx32",
81469
- large: "cx42",
81470
- xlarge: "cx52",
81474
+ small: "cx23",
81475
+ medium: "cx33",
81476
+ large: "cx43",
81477
+ xlarge: "cx53",
81471
81478
  "2xlarge": "ccx33"
81472
81479
  };
81473
81480
  function resolveHetznerServerType(size) {
@@ -81516,6 +81523,7 @@ async function writeDriverState(stackName, state) {
81516
81523
  }
81517
81524
 
81518
81525
  // src/drivers/hetzner/driver.ts
81526
+ var SSH_MAX_BUFFER = 1024 * 1024 * 256;
81519
81527
  function expandHome(path) {
81520
81528
  return path.startsWith("~/") ? join13(homedir7(), path.slice(2)) : path;
81521
81529
  }
@@ -81736,13 +81744,14 @@ class HetznerDriver {
81736
81744
  "BatchMode=yes",
81737
81745
  localPath,
81738
81746
  `${this.sshUser}@${host}:${remotePath}`
81739
- ].map((arg) => `"${arg.replace(/"/g, "\\\"")}"`).join(" "), { stdio: "pipe" });
81747
+ ].map((arg) => `"${arg.replace(/"/g, "\\\"")}"`).join(" "), { stdio: "pipe", maxBuffer: SSH_MAX_BUFFER });
81740
81748
  }
81741
81749
  sshExec(host, script) {
81742
81750
  const escaped = script.replace(/'/g, `'\\''`);
81743
81751
  return execSync(`ssh ${this.sshBaseArgs(host).map((a) => `"${a.replace(/"/g, "\\\"")}"`).join(" ")} '${escaped}'`, {
81744
81752
  encoding: "utf8",
81745
- stdio: ["pipe", "pipe", "pipe"]
81753
+ stdio: ["pipe", "pipe", "pipe"],
81754
+ maxBuffer: SSH_MAX_BUFFER
81746
81755
  });
81747
81756
  }
81748
81757
  }
@@ -81792,12 +81801,14 @@ function buildSiteDeployScript(options) {
81792
81801
  artifactFetch,
81793
81802
  execStart,
81794
81803
  envEntries,
81795
- port
81804
+ port,
81805
+ preStartCommands = []
81796
81806
  } = options;
81797
81807
  const appDir = options.appDir ?? `/var/www/${siteName}`;
81798
81808
  const serviceName = `${slug}-${siteName}.service`;
81799
81809
  const envFile = Object.entries(envEntries).map(([k, v]) => `${k}=${JSON.stringify(String(v))}`).join(`
81800
81810
  `);
81811
+ const preStart = preStartCommands.length > 0 ? [`cd ${appDir}`, ...preStartCommands] : [];
81801
81812
  return [
81802
81813
  "set -euo pipefail",
81803
81814
  ...artifactFetch,
@@ -81808,6 +81819,7 @@ function buildSiteDeployScript(options) {
81808
81819
  envFile,
81809
81820
  "TS_CLOUD_ENV_EOF",
81810
81821
  `chmod 600 ${appDir}/.env`,
81822
+ ...preStart,
81811
81823
  `cat > /etc/systemd/system/${serviceName} <<'TS_CLOUD_UNIT_EOF'`,
81812
81824
  "[Unit]",
81813
81825
  `Description=${siteName} (managed by ts-cloud)`,
@@ -81886,7 +81898,8 @@ async function deploySiteRelease(driver, options, logger4 = noopLogger) {
81886
81898
  artifactFetch,
81887
81899
  execStart: resolveExecStart(site.start, runtime),
81888
81900
  envEntries: site.env || {},
81889
- port: site.port
81901
+ port: site.port,
81902
+ preStartCommands: site.preStart
81890
81903
  });
81891
81904
  logger4.step(`Deploying to ${targets.length} target(s)...`);
81892
81905
  const result = await driver.runRemoteDeploy({
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@stacksjs/ts-cloud",
3
3
  "type": "module",
4
- "version": "0.2.20",
4
+ "version": "0.2.21",
5
5
  "description": "A lightweight, performant infrastructure-as-code library and CLI for deploying both server-based (EC2) and serverless applications.",
6
6
  "author": "Chris Breuer <chris@stacksjs.com>",
7
7
  "license": "MIT",
@@ -89,8 +89,8 @@
89
89
  "test": "bun test"
90
90
  },
91
91
  "dependencies": {
92
- "@ts-cloud/aws-types": "0.2.20",
93
- "@ts-cloud/core": "0.2.20",
92
+ "@ts-cloud/aws-types": "0.2.21",
93
+ "@ts-cloud/core": "0.2.21",
94
94
  "@stacksjs/ts-xml": "^0.1.0"
95
95
  },
96
96
  "devDependencies": {