@everystack/cli 0.2.1 → 0.2.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.
- package/package.json +1 -1
- package/src/cli/commands/update.ts +52 -18
- package/src/cli/index.ts +21 -2
package/package.json
CHANGED
|
@@ -5,7 +5,7 @@ import zlib from 'node:zlib';
|
|
|
5
5
|
import { pipeline } from 'node:stream/promises';
|
|
6
6
|
import { createWriteStream } from 'node:fs';
|
|
7
7
|
import { spawn } from 'node:child_process';
|
|
8
|
-
import { resolveConfig } from '../config.js';
|
|
8
|
+
import { resolveConfig, type CliConfig } from '../config.js';
|
|
9
9
|
import { uploadToS3, invokeAction } from '../aws.js';
|
|
10
10
|
import { step, success, warn, fail, info } from '../output.js';
|
|
11
11
|
import { exportApp, isDistStale } from '../utils/export.js';
|
|
@@ -20,6 +20,32 @@ export interface UpdateFlags {
|
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
export async function updateCommand(flags: UpdateFlags & Record<string, string>): Promise<void> {
|
|
23
|
+
// Propagate --stage to ENVIRONMENT before app.config.js evaluation.
|
|
24
|
+
// Without this, the manifest snapshots whatever the local shell has
|
|
25
|
+
// (often unset → 'development'), baking wrong API URLs and feature
|
|
26
|
+
// flags into the OTA update. Only set if not already supplied.
|
|
27
|
+
if (flags.stage) {
|
|
28
|
+
process.env.ENVIRONMENT ||= flags.stage;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// When --stage is provided, resolve deployed config early so HOST_URL
|
|
32
|
+
// is correct before app.config.js evaluation and expo export.
|
|
33
|
+
// loadSstOutputs() reads .sst/outputs.json (last deploy, any stage)
|
|
34
|
+
// which may be from a different stage entirely.
|
|
35
|
+
let resolvedConfig: CliConfig | undefined;
|
|
36
|
+
if (flags.stage) {
|
|
37
|
+
try {
|
|
38
|
+
step('Resolving deployed config...');
|
|
39
|
+
resolvedConfig = await resolveConfig(flags.stage);
|
|
40
|
+
if (resolvedConfig.baseUrl) {
|
|
41
|
+
process.env.HOST_URL ||= resolvedConfig.baseUrl;
|
|
42
|
+
}
|
|
43
|
+
success(`Region: ${resolvedConfig.region}, Function: ${resolvedConfig.apiFunctionName}`);
|
|
44
|
+
} catch {
|
|
45
|
+
// Stage may not be deployed yet — HOST_URL falls back to loadSstOutputs
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
23
49
|
const branch = flags.branch;
|
|
24
50
|
const channel = flags.channel || branch || flags.stage || 'production';
|
|
25
51
|
const message = flags.message || '';
|
|
@@ -82,15 +108,19 @@ export async function updateCommand(flags: UpdateFlags & Record<string, string>)
|
|
|
82
108
|
// Mobile platforms — direct S3 upload + IAM-authed Lambda invoke (mirrors web path).
|
|
83
109
|
// No HTTP publish endpoint, no shared bearer token, no WAF body-size limits.
|
|
84
110
|
if (mobilePlatforms.length > 0) {
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
111
|
+
let config: CliConfig;
|
|
112
|
+
if (resolvedConfig) {
|
|
113
|
+
config = resolvedConfig;
|
|
114
|
+
} else {
|
|
115
|
+
step('Resolving deployed config...');
|
|
116
|
+
try {
|
|
117
|
+
config = await resolveConfig(flags.stage);
|
|
118
|
+
} catch (err: any) {
|
|
119
|
+
fail(err.message);
|
|
120
|
+
process.exit(1);
|
|
121
|
+
}
|
|
122
|
+
success(`Region: ${config.region}, Function: ${config.apiFunctionName}`);
|
|
92
123
|
}
|
|
93
|
-
success(`Region: ${config.region}, Function: ${config.apiFunctionName}`);
|
|
94
124
|
|
|
95
125
|
for (const platform of mobilePlatforms) {
|
|
96
126
|
const platformMetadata = metadata?.fileMetadata?.[platform];
|
|
@@ -197,16 +227,20 @@ export async function updateCommand(flags: UpdateFlags & Record<string, string>)
|
|
|
197
227
|
if (!(await fileExists(routesJson))) {
|
|
198
228
|
warn('Skipping web (no server export found — ensure app.json has "output": "server")');
|
|
199
229
|
} else {
|
|
200
|
-
// Resolve SST config for direct AWS access
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
230
|
+
// Resolve SST config for direct AWS access (reuse early resolution if available)
|
|
231
|
+
let config: CliConfig;
|
|
232
|
+
if (resolvedConfig) {
|
|
233
|
+
config = resolvedConfig;
|
|
234
|
+
} else {
|
|
235
|
+
step('Resolving deployed config...');
|
|
236
|
+
try {
|
|
237
|
+
config = await resolveConfig(flags.stage);
|
|
238
|
+
} catch (err: any) {
|
|
239
|
+
fail(err.message);
|
|
240
|
+
process.exit(1);
|
|
241
|
+
}
|
|
242
|
+
success(`Region: ${config.region}, Function: ${config.apiFunctionName}`);
|
|
208
243
|
}
|
|
209
|
-
success(`Region: ${config.region}, Function: ${config.apiFunctionName}`);
|
|
210
244
|
|
|
211
245
|
// 1. Create tar+brotli archive of server bundle
|
|
212
246
|
step('Creating server bundle archive...');
|
package/src/cli/index.ts
CHANGED
|
@@ -38,10 +38,29 @@ function parseFlags(args: string[]): Record<string, string> {
|
|
|
38
38
|
* Auto-detect HOST_URL from SST outputs.
|
|
39
39
|
* SST writes .sst/outputs.json after every deploy with { routerUrl, apiUrl, ... }.
|
|
40
40
|
* If HOST_URL isn't already set, use the routerUrl from the last deploy.
|
|
41
|
+
*
|
|
42
|
+
* When a stage is specified, checks the stage-specific cached config first.
|
|
43
|
+
* .sst/outputs.json reflects the last deploy (any stage) and may be wrong
|
|
44
|
+
* when publishing to a different stage.
|
|
41
45
|
*/
|
|
42
|
-
async function loadSstOutputs(): Promise<void> {
|
|
46
|
+
async function loadSstOutputs(stage?: string): Promise<void> {
|
|
43
47
|
if (process.env.HOST_URL) return;
|
|
44
48
|
|
|
49
|
+
// When --stage is provided, try stage-specific cache first
|
|
50
|
+
if (stage) {
|
|
51
|
+
const stagePath = path.resolve('.sst', `outputs.${stage}.json`);
|
|
52
|
+
try {
|
|
53
|
+
const raw = await fs.readFile(stagePath, 'utf8');
|
|
54
|
+
const cached = JSON.parse(raw);
|
|
55
|
+
if (cached.routerUrl) {
|
|
56
|
+
process.env.HOST_URL = cached.routerUrl;
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
} catch {
|
|
60
|
+
// No cached config for this stage — fall through to default
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
45
64
|
const outputsPath = path.resolve('.sst', 'outputs.json');
|
|
46
65
|
try {
|
|
47
66
|
const raw = await fs.readFile(outputsPath, 'utf8');
|
|
@@ -63,7 +82,7 @@ async function main() {
|
|
|
63
82
|
const flags = parseFlags(args.slice(1));
|
|
64
83
|
|
|
65
84
|
// Auto-detect deployed URL from SST outputs
|
|
66
|
-
await loadSstOutputs();
|
|
85
|
+
await loadSstOutputs(flags.stage);
|
|
67
86
|
|
|
68
87
|
switch (command) {
|
|
69
88
|
case 'update':
|