@stacksjs/ts-cloud 0.5.0 → 0.5.2

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.
@@ -51,3 +51,10 @@ export declare function rollbackServerlessApp(config: CloudConfig, environment:
51
51
  export declare function setMaintenance(config: CloudConfig, environment: EnvironmentType, enabled: boolean, bypassSecret?: string): Promise<void>;
52
52
  /** Invoke the CLI function with an arbitrary command (e.g. `cloud command "migrate"`). */
53
53
  export declare function runRemoteCommand(config: CloudConfig, environment: EnvironmentType, command: string): Promise<string>;
54
+ /**
55
+ * Run a SQL statement against a (private, in-VPC) serverless database via the CLI
56
+ * function — no bastion needed. Requires the `tscloud/serverless` PHP bridge
57
+ * (`tscloud:db-query`). The SQL is base64-encoded so it survives the runtime's
58
+ * whitespace argument parsing.
59
+ */
60
+ export declare function runDbQuery(config: CloudConfig, environment: EnvironmentType, sql: string): Promise<string>;
@@ -10,11 +10,13 @@
10
10
  * For the `custom` provider the vhost is rendered with the operator's cert
11
11
  * directly (see nginx-vhost's `ssl` option), so certbot is not involved.
12
12
  */
13
- import type { SiteConfig } from '@ts-cloud/core';
13
+ import type { SiteConfig, SslDnsConfig } from '@ts-cloud/core';
14
14
  /** Resolve the effective SSL provider for a site (Let's Encrypt by default when it has a domain). */
15
15
  export declare function resolveSslProvider(site: SiteConfig): 'letsencrypt' | 'custom' | 'none';
16
- /** Install certbot + the nginx plugin and ensure the auto-renew timer is enabled. */
17
- export declare function buildCertbotInstallScript(): string[];
16
+ /** Path of the certbot credentials INI for a DNS provider. */
17
+ export declare function dnsCredentialsPath(provider: SslDnsConfig['provider']): string;
18
+ /** Install certbot (+ optional DNS plugin) and ensure the auto-renew timer is enabled. */
19
+ export declare function buildCertbotInstallScript(dns?: SslDnsConfig): string[];
18
20
  export interface CertbotIssueOptions {
19
21
  /** Primary domain. */
20
22
  domain: string;
@@ -24,15 +26,27 @@ export interface CertbotIssueOptions {
24
26
  email?: string;
25
27
  /** Redirect HTTP → HTTPS (Forge default). @default true */
26
28
  redirect?: boolean;
29
+ /** Issue `*.<domain>` + `<domain>` (requires `dns`). */
30
+ wildcard?: boolean;
31
+ /** DNS-01 validation via a certbot DNS plugin (required for wildcard). */
32
+ dns?: SslDnsConfig;
27
33
  }
28
34
  /**
29
- * Issue (or expand) a Let's Encrypt cert for the domain via the nginx plugin.
30
- * Idempotent: `--keep-until-expiring` reuses a valid cert, so re-running on
31
- * every deploy is safe.
35
+ * Build the commands that write a DNS provider's certbot credentials INI
36
+ * (root-only) so certbot can create the `_acme-challenge` records. Returns `[]`
37
+ * when there are no credentials (e.g. route53 using instance-role creds).
38
+ */
39
+ export declare function buildDnsCredentialsScript(dns: SslDnsConfig): string[];
40
+ /**
41
+ * Issue (or expand) a Let's Encrypt cert. Uses the nginx (HTTP-01) plugin by
42
+ * default; with `dns` set, uses DNS-01 via the provider plugin (the only way to
43
+ * get a wildcard). Idempotent: `--keep-until-expiring` reuses a valid cert.
32
44
  */
33
45
  export declare function buildCertbotIssueScript(options: CertbotIssueOptions): string[];
34
46
  /**
35
47
  * Full SSL script for a site, dispatched on its provider. Returns `[]` for
36
- * `custom`/`none` (custom certs are baked into the vhost already).
48
+ * `custom`/`none` (custom certs are baked into the vhost already). A wildcard
49
+ * request without a DNS config is impossible (HTTP-01 can't do wildcards), so
50
+ * it's skipped.
37
51
  */
38
52
  export declare function buildSslScript(site: SiteConfig): string[];
@@ -0,0 +1,59 @@
1
+ /**
2
+ * Forge-style operational commands for a provisioned compute fleet, run over
3
+ * the active driver (SSH/SSM): roll a site back to a previous release, read a
4
+ * site's deployment history, and run a reusable server recipe across servers.
5
+ *
6
+ * Each finds the project's targets via the driver, builds a small shell script
7
+ * with the shared generators, and runs it on every box — mirroring how
8
+ * {@link import('./compute-deploy').deploySiteRelease} drives a deploy.
9
+ */
10
+ import type { CloudDriver, EnvironmentType, RemoteDeployInstanceResult } from '@ts-cloud/core';
11
+ export interface ComputeOpsLogger {
12
+ info(message: string): void;
13
+ warn(message: string): void;
14
+ error(message: string): void;
15
+ step(message: string): void;
16
+ success(message: string): void;
17
+ }
18
+ export interface ComputeOpsContext {
19
+ driver: CloudDriver;
20
+ slug: string;
21
+ environment: EnvironmentType;
22
+ /** Target role label. @default 'app' */
23
+ role?: string;
24
+ logger?: ComputeOpsLogger;
25
+ }
26
+ export interface ComputeOpsResult {
27
+ success: boolean;
28
+ error?: string;
29
+ perInstance?: RemoteDeployInstanceResult[];
30
+ }
31
+ /**
32
+ * Roll a site back to a previous release (Forge-style). With `to`, points
33
+ * `current` at `releases/<to>`; otherwise the most recent prior release. After
34
+ * flipping the symlink, php-fpm is restarted and queue workers are signalled so
35
+ * they pick up the rolled-back code.
36
+ */
37
+ export declare function rollbackComputeSite(ctx: ComputeOpsContext, options: {
38
+ siteName: string;
39
+ to?: string;
40
+ }): Promise<ComputeOpsResult>;
41
+ /**
42
+ * Read a site's on-box deployment history (the log written by
43
+ * {@link import('./releases').buildDeployHistoryHeader}). Returns the per-server
44
+ * output so the caller can print the most recent deploys.
45
+ */
46
+ export declare function getComputeDeployHistory(ctx: ComputeOpsContext, options: {
47
+ siteName: string;
48
+ limit?: number;
49
+ }): Promise<ComputeOpsResult>;
50
+ /**
51
+ * Run a reusable server recipe (a bash script) across the project's servers as
52
+ * a chosen user (Forge's Recipes). The recipe runs through a login shell so
53
+ * pantry's env is loaded; output is captured per server.
54
+ */
55
+ export declare function runComputeRecipe(ctx: ComputeOpsContext, options: {
56
+ name: string;
57
+ script: string[];
58
+ user?: string;
59
+ }): Promise<ComputeOpsResult>;
@@ -14,7 +14,7 @@
14
14
  * (not-yet-live) release, so a failure leaves the previous release serving —
15
15
  * the Envoyer zero-downtime guarantee.
16
16
  */
17
- import type { SiteConfig } from '@ts-cloud/core';
17
+ import type { SiteConfig, SiteCredentialsConfig } from '@ts-cloud/core';
18
18
  export declare const MACRO_CREATE_RELEASE = "$CREATE_RELEASE";
19
19
  export declare const MACRO_ACTIVATE_RELEASE = "$ACTIVATE_RELEASE";
20
20
  export declare const MACRO_RESTART_QUEUES = "$RESTART_QUEUES";
@@ -35,6 +35,11 @@ export interface LaravelDeployOptions {
35
35
  /** PHP version selecting the `phpX.Y` binary. @default `site.phpVersion` ?? '8.3' */
36
36
  defaultPhpVersion?: string;
37
37
  }
38
+ /**
39
+ * Write private-registry credential files into the release before build:
40
+ * Composer `auth.json` and/or `.npmrc`. Quoted heredocs keep tokens literal.
41
+ */
42
+ export declare function buildCredentialFiles(releaseDir: string, creds?: SiteCredentialsConfig): string[];
38
43
  /**
39
44
  * Build the full remote shell script for a PHP/Laravel git deploy, expanding the
40
45
  * release macros. Requires `site.repository` to be set.
@@ -13,8 +13,15 @@
13
13
  * The block listens on :80 only; TLS (the `:443` block + redirect) is layered
14
14
  * on by certbot in the SSL step, so this stays stable across cert renewals.
15
15
  */
16
- import type { SiteConfig } from '@ts-cloud/core';
16
+ import type { SiteConfig, SiteNginxConfig } from '@ts-cloud/core';
17
17
  export type NginxSiteType = NonNullable<SiteConfig['type']>;
18
+ /**
19
+ * Resolve the nginx directive lines for a site's vhost from its
20
+ * {@link SiteNginxConfig} and the server's reusable templates: the referenced
21
+ * template's lines first, then the per-site `serverSnippet`. Unknown template
22
+ * names resolve to nothing. Returns `[]` when there's no customization.
23
+ */
24
+ export declare function resolveNginxSnippet(nginx: SiteNginxConfig | undefined, templates: Record<string, string[]> | undefined): string[];
18
25
  export interface NginxVhostOptions {
19
26
  /** Site key — names the config file (`/etc/nginx/sites-available/<siteName>`). */
20
27
  siteName: string;
@@ -56,6 +63,14 @@ export interface NginxVhostOptions {
56
63
  password: string;
57
64
  realm?: string;
58
65
  };
66
+ /**
67
+ * Custom nginx directive lines injected into the server block (after the
68
+ * managed directives) — a resolved reusable template plus per-site snippet.
69
+ * See {@link import('@ts-cloud/core').SiteNginxConfig}.
70
+ */
71
+ serverSnippet?: string[];
72
+ /** `client_max_body_size` override for this vhost (e.g. `'256M'`). */
73
+ clientMaxBodySize?: string;
59
74
  }
60
75
  /** Path of the htpasswd file for a site. */
61
76
  export declare function htpasswdPath(siteName: string): string;
@@ -16,7 +16,21 @@ export interface PhpProvisionOptions {
16
16
  installNginx?: boolean;
17
17
  /** Install Composer. @default true */
18
18
  installComposer?: boolean;
19
+ /**
20
+ * Apply production OPcache + php.ini tuning (Forge's "Optimize for
21
+ * Production"). @default true
22
+ */
23
+ optimizeForProduction?: boolean;
24
+ /** Extra `php.ini` directives merged on top of (or instead of) the tuning. */
25
+ ini?: Record<string, string>;
19
26
  }
27
+ /**
28
+ * The production OPcache + runtime tuning Forge applies via "Optimize for
29
+ * Production". Timestamp validation is off because every deploy restarts
30
+ * php-fpm (so a fresh release is always recompiled); the larger buffers and
31
+ * realpath cache cut per-request overhead.
32
+ */
33
+ export declare const PRODUCTION_PHP_INI: Readonly<Record<string, string>>;
20
34
  /** PHP-FPM listen address for nginx `fastcgi_pass` (pantry's php-fpm is TCP). */
21
35
  export declare const PHP_FPM_LISTEN = "127.0.0.1:9074";
22
36
  /**
@@ -27,10 +41,20 @@ export declare const PHP_FPM_LISTEN = "127.0.0.1:9074";
27
41
  export declare function phpFpmSocketPath(_version?: string): string;
28
42
  /** Resolve the default PHP version from the requested set. */
29
43
  export declare function resolveDefaultPhpVersion(options?: PhpProvisionOptions): string;
44
+ /** Resolve the effective php.ini directives for the given options. */
45
+ export declare function resolvePhpIni(options?: PhpProvisionOptions): Record<string, string>;
46
+ /**
47
+ * Write a ts-cloud-managed `php.ini` drop-in with the resolved directives and
48
+ * restart php-fpm so it takes effect. PHP's ini layout is discovered at runtime
49
+ * via `php -i` (works regardless of pantry's compiled paths): the additional-ini
50
+ * scan dir is used when present, otherwise a managed marker block is merged into
51
+ * the loaded `php.ini`. Returns `[]` when there's nothing to set.
52
+ */
53
+ export declare function buildPhpTuningScript(options?: PhpProvisionOptions): string[];
30
54
  /**
31
55
  * Build the shell command lines that install PHP-FPM + Composer (+ the nginx
32
- * binary) via pantry and start php-fpm as a system service. Assumes the pantry
33
- * CLI is already bootstrapped (see
56
+ * binary) via pantry, start php-fpm as a system service, and apply production
57
+ * php.ini tuning. Assumes the pantry CLI is already bootstrapped (see
34
58
  * {@link import('./package-manager').buildPantryBootstrapScript}).
35
59
  */
36
60
  export declare function buildPhpProvisionScript(options?: PhpProvisionOptions): string[];
@@ -16,6 +16,14 @@
16
16
  export declare const DEFAULT_SHARED_PATHS: readonly string[];
17
17
  /** Default number of past releases to retain for rollback. */
18
18
  export declare const DEFAULT_KEEP_RELEASES = 4;
19
+ /** Number of per-deploy output logs to keep on the box. */
20
+ export declare const DEFAULT_KEEP_DEPLOY_LOGS = 20;
21
+ /** ts-cloud metadata dir for a site (deploy history + per-deploy logs). */
22
+ export declare function deployMetaDir(base: string): string;
23
+ /** Append-only deploy history log path for a site. */
24
+ export declare function deployHistoryPath(base: string): string;
25
+ /** Per-deploy output log path for a release. */
26
+ export declare function deployLogPath(base: string, releaseId: string): string;
19
27
  export interface ReleasePaths {
20
28
  /** Site base directory (`/var/www/<site>`). */
21
29
  base: string;
@@ -45,8 +53,39 @@ export declare function buildLinkSharedPaths(paths: ReleasePaths, sharedPaths?:
45
53
  * `mv -T`s it over `current` so there is no window where `current` is missing.
46
54
  */
47
55
  export declare function buildActivateRelease(paths: ReleasePaths): string[];
56
+ /**
57
+ * Roll the active release back to a previous one (Forge-style rollback). With
58
+ * `to` set, points `current` at `releases/<to>`; otherwise picks the most recent
59
+ * release that isn't the one `current` resolves to. Atomic (temp symlink + `mv
60
+ * -T`), and a no-op-safe guard fails loudly if the target is missing rather than
61
+ * leaving `current` dangling. The caller appends the engine reload
62
+ * (php-fpm/queues) — see {@link import('./laravel-deploy')}.
63
+ */
64
+ export declare function buildRollbackScript(paths: ReleasePaths, options?: {
65
+ to?: string;
66
+ }): string[];
48
67
  /**
49
68
  * Remove all but the newest `keep` releases (by mtime). `current` always points
50
69
  * at the newest, so it is never pruned.
51
70
  */
52
71
  export declare function buildPruneReleases(paths: ReleasePaths, keep?: number): string[];
72
+ export interface DeployHistoryOptions {
73
+ /** This deploy's release id. */
74
+ releaseId: string;
75
+ /** Commit SHA being deployed (recorded in the history line). */
76
+ commit?: string;
77
+ /** Branch being deployed. */
78
+ branch?: string;
79
+ /** Per-deploy logs to retain. @default {@link DEFAULT_KEEP_DEPLOY_LOGS} */
80
+ keepLogs?: number;
81
+ }
82
+ /**
83
+ * Header lines that record deployment history + capture per-deploy output
84
+ * (Forge's deployment log). Emitted near the top of the deploy script: it tees
85
+ * all stdout/stderr to `<base>/.ts-cloud/deploys/<releaseId>.log` and installs
86
+ * an EXIT trap that appends a `<ts>\t<releaseId>\t<commit>\t<status>` line to
87
+ * `<base>/.ts-cloud/deploy-history.log` — so both successful and failed deploys
88
+ * are recorded (the trap reads `$?`). Requires bash (the deploy script already
89
+ * uses `set -euo pipefail`).
90
+ */
91
+ export declare function buildDeployHistoryHeader(base: string, options: DeployHistoryOptions): string[];
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Server "recipes" — reusable bash scripts run on demand across one or more
3
+ * provisioned servers, as a chosen user (Forge's Recipes feature). The driver
4
+ * fans a recipe out over the target servers (SSH/SSM); this module builds the
5
+ * single wrapped script that runs on each box: it sets a strict shell, runs the
6
+ * recipe body as the requested user with a login shell (so PATH/pantry env are
7
+ * loaded), and prints clear begin/end markers + the exit code so captured
8
+ * output is easy to scan.
9
+ */
10
+ export interface ServerRecipeOptions {
11
+ /** Recipe name (shown in the output markers). */
12
+ name: string;
13
+ /** The recipe body — bash command lines run on the server. */
14
+ script: string[];
15
+ /**
16
+ * User to run the recipe as. Defaults to `root`. A non-root user is invoked
17
+ * via `runuser -l` so it gets a login shell (profile + PATH, incl. pantry).
18
+ */
19
+ user?: string;
20
+ }
21
+ /**
22
+ * Build the wrapped recipe script run on a server. The body is written to a
23
+ * temp file and executed as `user`; the wrapper captures the exit code and
24
+ * prints `__TS_CLOUD_RECIPE_*__` markers around the run for the driver to parse.
25
+ */
26
+ export declare function buildServerRecipeScript(options: ServerRecipeOptions): string[];