@everystack/cli 0.2.8 → 0.2.10
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 +2 -6
- package/src/cli/aws.ts +5 -11
- package/src/cli/commands/update.ts +105 -31
- package/src/handler/assets.ts +5 -0
- package/src/handler/index.ts +1 -0
- package/src/handler/manifest.ts +49 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@everystack/cli",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.10",
|
|
4
4
|
"description": "CLI and OTA updates for Expo apps on everystack",
|
|
5
5
|
"license": "AGPL-3.0-only",
|
|
6
6
|
"publishConfig": {
|
|
@@ -41,6 +41,7 @@
|
|
|
41
41
|
"lint": "tsc --noEmit"
|
|
42
42
|
},
|
|
43
43
|
"dependencies": {
|
|
44
|
+
"@aws-sdk/client-cloudfront": "3.1053.0",
|
|
44
45
|
"@aws-sdk/client-cloudfront-keyvaluestore": "3.1053.0",
|
|
45
46
|
"@aws-sdk/signature-v4a": "3.1048.0",
|
|
46
47
|
"glob": "13.0.6",
|
|
@@ -50,7 +51,6 @@
|
|
|
50
51
|
},
|
|
51
52
|
"peerDependencies": {
|
|
52
53
|
"@everystack/server": ">=0.1.0",
|
|
53
|
-
"@aws-sdk/client-cloudfront": "3.1053.0",
|
|
54
54
|
"@aws-sdk/client-cloudwatch-logs": "3.1053.0",
|
|
55
55
|
"@aws-sdk/client-lambda": "3.1053.0",
|
|
56
56
|
"@aws-sdk/client-s3": "3.1053.0",
|
|
@@ -64,9 +64,6 @@
|
|
|
64
64
|
"@everystack/server": {
|
|
65
65
|
"optional": true
|
|
66
66
|
},
|
|
67
|
-
"@aws-sdk/client-cloudfront": {
|
|
68
|
-
"optional": true
|
|
69
|
-
},
|
|
70
67
|
"@aws-sdk/client-cloudwatch-logs": {
|
|
71
68
|
"optional": true
|
|
72
69
|
},
|
|
@@ -90,7 +87,6 @@
|
|
|
90
87
|
}
|
|
91
88
|
},
|
|
92
89
|
"devDependencies": {
|
|
93
|
-
"@aws-sdk/client-cloudfront": "3.1053.0",
|
|
94
90
|
"@aws-sdk/client-cloudwatch-logs": "3.1053.0",
|
|
95
91
|
"@aws-sdk/client-lambda": "3.1053.0",
|
|
96
92
|
"@aws-sdk/client-s3": "3.1053.0",
|
package/src/cli/aws.ts
CHANGED
|
@@ -94,19 +94,13 @@ export async function invokeAction(
|
|
|
94
94
|
|
|
95
95
|
/**
|
|
96
96
|
* Resolve the CloudWatch log group for a Lambda function.
|
|
97
|
-
*
|
|
98
|
-
* the conventional `/aws/lambda/{functionName}` pattern if not set.
|
|
97
|
+
* Uses the conventional `/aws/lambda/{functionName}` naming — no IAM call needed.
|
|
99
98
|
*/
|
|
100
|
-
export
|
|
101
|
-
|
|
99
|
+
export function resolveLogGroup(
|
|
100
|
+
_region: string,
|
|
102
101
|
functionName: string,
|
|
103
|
-
):
|
|
104
|
-
|
|
105
|
-
const { GetFunctionConfigurationCommand } = await import('@aws-sdk/client-lambda');
|
|
106
|
-
const config = await client.send(
|
|
107
|
-
new GetFunctionConfigurationCommand({ FunctionName: functionName })
|
|
108
|
-
);
|
|
109
|
-
return config.LoggingConfig?.LogGroup ?? `/aws/lambda/${functionName}`;
|
|
102
|
+
): string {
|
|
103
|
+
return `/aws/lambda/${functionName}`;
|
|
110
104
|
}
|
|
111
105
|
|
|
112
106
|
/**
|
|
@@ -8,7 +8,7 @@ import { spawn } from 'node:child_process';
|
|
|
8
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
|
-
import { exportApp
|
|
11
|
+
import { exportApp } from '../utils/export.js';
|
|
12
12
|
import { walkDirectory } from '../utils/walk.js';
|
|
13
13
|
|
|
14
14
|
export interface UpdateFlags {
|
|
@@ -27,13 +27,19 @@ export async function updateCommand(flags: UpdateFlags & Record<string, string>)
|
|
|
27
27
|
// (e.g. OUTBOUND_HOST=localhost → LAN IP) to leak into the manifest.
|
|
28
28
|
process.env.EVERYSTACK_UPDATE = '1';
|
|
29
29
|
|
|
30
|
-
//
|
|
30
|
+
// Resolve ENVIRONMENT before app.config.js evaluation so the manifest
|
|
31
31
|
// snapshots the correct API URLs and feature flags for the target.
|
|
32
|
-
// --environment
|
|
33
|
-
// SST stage names (dev, prod) don't match EAS profile names
|
|
34
|
-
// (development, production) and conflating them causes config failures.
|
|
32
|
+
// Priority: --environment flag > eas.json channel match > existing env.
|
|
35
33
|
if (flags.environment) {
|
|
36
34
|
process.env.ENVIRONMENT ||= flags.environment;
|
|
35
|
+
} else if (!process.env.ENVIRONMENT) {
|
|
36
|
+
// Derive ENVIRONMENT from eas.json by matching --channel to a build profile.
|
|
37
|
+
// EAS profiles define ENVIRONMENT in their env blocks (e.g. production → "production").
|
|
38
|
+
const channel = flags.channel || flags.branch || flags.stage || 'production';
|
|
39
|
+
const easEnv = resolveEasEnvironment(channel);
|
|
40
|
+
if (easEnv) {
|
|
41
|
+
process.env.ENVIRONMENT = easEnv;
|
|
42
|
+
}
|
|
37
43
|
}
|
|
38
44
|
|
|
39
45
|
// When --stage is provided, resolve deployed config early so HOST_URL
|
|
@@ -64,6 +70,21 @@ export async function updateCommand(flags: UpdateFlags & Record<string, string>)
|
|
|
64
70
|
const appConfig = await loadAppConfig();
|
|
65
71
|
const runtimeVersion = appConfig.runtimeVersion;
|
|
66
72
|
|
|
73
|
+
// Validate: SSR flags in the expo-router plugin config will crash native devices.
|
|
74
|
+
// The bundle is compiled with server rendering code paths that have no server at runtime.
|
|
75
|
+
const ssrFlags = getExpoRouterSsrFlags(appConfig);
|
|
76
|
+
if (ssrFlags.useServerRendering || ssrFlags.useServerDataLoaders) {
|
|
77
|
+
fail('Export contains server rendering flags that will crash native devices.');
|
|
78
|
+
info('expo-router plugin has unstable_useServerRendering or unstable_useServerDataLoaders enabled.');
|
|
79
|
+
info('Use SSR_ENABLED to opt in only for web server exports:');
|
|
80
|
+
info('');
|
|
81
|
+
info(" ['expo-router', {");
|
|
82
|
+
info(' unstable_useServerRendering: !!process.env.SSR_ENABLED,');
|
|
83
|
+
info(' unstable_useServerDataLoaders: !!process.env.SSR_ENABLED,');
|
|
84
|
+
info(' }]');
|
|
85
|
+
process.exit(1);
|
|
86
|
+
}
|
|
87
|
+
|
|
67
88
|
if (!runtimeVersion) {
|
|
68
89
|
fail('No runtimeVersion found in app.json/app.config.js');
|
|
69
90
|
info('Add "runtimeVersion" to your app.json, e.g.:');
|
|
@@ -73,32 +94,26 @@ export async function updateCommand(flags: UpdateFlags & Record<string, string>)
|
|
|
73
94
|
}
|
|
74
95
|
success(`Runtime version: ${runtimeVersion}`);
|
|
75
96
|
|
|
76
|
-
// Export if needed
|
|
77
97
|
const distDir = path.resolve('dist');
|
|
78
|
-
const
|
|
79
|
-
let shouldExport = flags.export === 'true' || !distExists;
|
|
80
|
-
|
|
81
|
-
// Check if dist/ is stale (source files newer than dist/)
|
|
82
|
-
if (!shouldExport && flags['skip-export'] !== 'true' && distExists) {
|
|
83
|
-
if (await isDistStale(distDir)) {
|
|
84
|
-
step('Source files changed since last export, re-exporting...');
|
|
85
|
-
shouldExport = true;
|
|
86
|
-
}
|
|
87
|
-
}
|
|
98
|
+
const needsMobile = platformFilter !== 'web';
|
|
88
99
|
|
|
89
|
-
|
|
100
|
+
// First export: mobile-safe bundles (no SSR_ENABLED).
|
|
101
|
+
// Always re-export — stale dist/ is the #1 cause of broken OTA pushes.
|
|
102
|
+
if (needsMobile) {
|
|
90
103
|
step('Exporting app...');
|
|
91
104
|
await exportApp();
|
|
92
105
|
success('Export complete');
|
|
93
106
|
}
|
|
94
107
|
|
|
95
108
|
// Read metadata (mobile exports produce metadata.json; web-only exports don't)
|
|
96
|
-
const metadataPath = path.join(distDir, 'metadata.json');
|
|
97
109
|
let metadata: any = null;
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
110
|
+
if (needsMobile) {
|
|
111
|
+
const metadataPath = path.join(distDir, 'metadata.json');
|
|
112
|
+
try {
|
|
113
|
+
metadata = JSON.parse(await fs.readFile(metadataPath, 'utf8'));
|
|
114
|
+
} catch {
|
|
115
|
+
// No metadata.json
|
|
116
|
+
}
|
|
102
117
|
}
|
|
103
118
|
|
|
104
119
|
// Determine platforms to publish
|
|
@@ -228,6 +243,16 @@ export async function updateCommand(flags: UpdateFlags & Record<string, string>)
|
|
|
228
243
|
|
|
229
244
|
// Web publish — direct S3 upload + Lambda invoke (IAM auth)
|
|
230
245
|
if (hasWeb) {
|
|
246
|
+
// Re-export with SSR_ENABLED for web server rendering.
|
|
247
|
+
// The first export produced mobile-safe bundles (already uploaded above).
|
|
248
|
+
// This second export enables SSR flags so the web bundle includes
|
|
249
|
+
// server rendering code paths and data loaders.
|
|
250
|
+
step('Exporting web bundle (SSR)...');
|
|
251
|
+
process.env.SSR_ENABLED = '1';
|
|
252
|
+
await exportApp();
|
|
253
|
+
delete process.env.SSR_ENABLED;
|
|
254
|
+
success('Web export complete');
|
|
255
|
+
|
|
231
256
|
const serverDir = path.join(distDir, 'server');
|
|
232
257
|
const clientDir = path.join(distDir, 'client');
|
|
233
258
|
const routesJson = path.join(serverDir, '_expo', 'routes.json');
|
|
@@ -490,15 +515,6 @@ function mimeFromExt(ext: string): string {
|
|
|
490
515
|
return map[ext] || 'application/octet-stream';
|
|
491
516
|
}
|
|
492
517
|
|
|
493
|
-
async function dirExists(dir: string): Promise<boolean> {
|
|
494
|
-
try {
|
|
495
|
-
const stat = await fs.stat(dir);
|
|
496
|
-
return stat.isDirectory();
|
|
497
|
-
} catch {
|
|
498
|
-
return false;
|
|
499
|
-
}
|
|
500
|
-
}
|
|
501
|
-
|
|
502
518
|
async function fileExists(filePath: string): Promise<boolean> {
|
|
503
519
|
try {
|
|
504
520
|
const stat = await fs.stat(filePath);
|
|
@@ -530,3 +546,61 @@ async function createArchive(sourceDir: string, archivePath: string): Promise<vo
|
|
|
530
546
|
await pipeline(tar.stdout!, brotli, output);
|
|
531
547
|
await tarExit;
|
|
532
548
|
}
|
|
549
|
+
|
|
550
|
+
/**
|
|
551
|
+
* Read eas.json and find ENVIRONMENT for a given channel.
|
|
552
|
+
* Matches --channel to a build profile's `channel` field, then reads
|
|
553
|
+
* ENVIRONMENT from that profile's env block. Falls back to profile name
|
|
554
|
+
* match (e.g. --channel production matches the "production" profile).
|
|
555
|
+
* @internal Exported for testing.
|
|
556
|
+
*/
|
|
557
|
+
export function resolveEasEnvironment(channel: string, cwd?: string): string | undefined {
|
|
558
|
+
try {
|
|
559
|
+
const easPath = path.resolve(cwd || process.cwd(), 'eas.json');
|
|
560
|
+
// Synchronous read — runs once before export, performance irrelevant.
|
|
561
|
+
const eas = JSON.parse(require('fs').readFileSync(easPath, 'utf8'));
|
|
562
|
+
const profiles = eas?.build;
|
|
563
|
+
if (!profiles || typeof profiles !== 'object') return undefined;
|
|
564
|
+
|
|
565
|
+
// First pass: match on explicit channel field
|
|
566
|
+
for (const [, profile] of Object.entries(profiles)) {
|
|
567
|
+
const p = profile as any;
|
|
568
|
+
if (p?.channel === channel && p?.env?.ENVIRONMENT) {
|
|
569
|
+
return p.env.ENVIRONMENT;
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
// Second pass: match on profile name (e.g. "production" profile for --channel production)
|
|
574
|
+
if (profiles[channel]?.env?.ENVIRONMENT) {
|
|
575
|
+
return profiles[channel].env.ENVIRONMENT;
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
return undefined;
|
|
579
|
+
} catch {
|
|
580
|
+
return undefined;
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
/**
|
|
585
|
+
* Extract expo-router SSR flags from the resolved app config.
|
|
586
|
+
* Checks the plugins array for the expo-router entry.
|
|
587
|
+
* @internal Exported for testing.
|
|
588
|
+
*/
|
|
589
|
+
export function getExpoRouterSsrFlags(appConfig: any): {
|
|
590
|
+
useServerRendering: boolean;
|
|
591
|
+
useServerDataLoaders: boolean;
|
|
592
|
+
} {
|
|
593
|
+
const plugins: any[] = appConfig?.plugins || [];
|
|
594
|
+
for (const plugin of plugins) {
|
|
595
|
+
// Plugin can be a string or [name, options] tuple
|
|
596
|
+
const name = Array.isArray(plugin) ? plugin[0] : plugin;
|
|
597
|
+
const options = Array.isArray(plugin) ? plugin[1] : undefined;
|
|
598
|
+
if (name === 'expo-router' && options && typeof options === 'object') {
|
|
599
|
+
return {
|
|
600
|
+
useServerRendering: !!options.unstable_useServerRendering,
|
|
601
|
+
useServerDataLoaders: !!options.unstable_useServerDataLoaders,
|
|
602
|
+
};
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
return { useServerRendering: false, useServerDataLoaders: false };
|
|
606
|
+
}
|
package/src/handler/assets.ts
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
import type { UpdatesHandlerOptions } from './types';
|
|
2
2
|
|
|
3
|
+
const debug = process.env.DEBUG_UPDATES === '1';
|
|
4
|
+
|
|
3
5
|
export async function handleAssets(request: Request, options: UpdatesHandlerOptions): Promise<Response> {
|
|
4
6
|
const url = new URL(request.url);
|
|
5
7
|
const key = url.searchParams.get('key');
|
|
6
8
|
|
|
7
9
|
if (!key) {
|
|
10
|
+
console.warn('[updates] asset request rejected: no key');
|
|
8
11
|
return new Response(JSON.stringify({ error: 'No asset key provided.' }), {
|
|
9
12
|
status: 400,
|
|
10
13
|
headers: { 'content-type': 'application/json' },
|
|
@@ -15,6 +18,7 @@ export async function handleAssets(request: Request, options: UpdatesHandlerOpti
|
|
|
15
18
|
// Lambda function URLs cap responses at 6 MB; Hermes bytecode bundles exceed that.
|
|
16
19
|
if (options.storage.presignGet) {
|
|
17
20
|
const presignedUrl = await options.storage.presignGet(key);
|
|
21
|
+
debug && console.log('[updates] asset redirect', { key });
|
|
18
22
|
return new Response(null, {
|
|
19
23
|
status: 302,
|
|
20
24
|
headers: {
|
|
@@ -29,6 +33,7 @@ export async function handleAssets(request: Request, options: UpdatesHandlerOpti
|
|
|
29
33
|
const result = await options.storage.get(key);
|
|
30
34
|
|
|
31
35
|
if (!result) {
|
|
36
|
+
console.warn('[updates] asset not found', { key });
|
|
32
37
|
return new Response(JSON.stringify({ error: `Asset "${key}" not found.` }), {
|
|
33
38
|
status: 404,
|
|
34
39
|
headers: { 'content-type': 'application/json' },
|
package/src/handler/index.ts
CHANGED
|
@@ -71,6 +71,7 @@ export function createUpdatesHandler(options: UpdatesHandlerOptions): (request:
|
|
|
71
71
|
return handleEditChannel(request, options, channelName);
|
|
72
72
|
}
|
|
73
73
|
|
|
74
|
+
console.warn('[updates] 404 unmatched route', { method: request.method, pathname });
|
|
74
75
|
return new Response(JSON.stringify({ error: 'Not found' }), {
|
|
75
76
|
status: 404,
|
|
76
77
|
headers: { 'content-type': 'application/json' },
|
package/src/handler/manifest.ts
CHANGED
|
@@ -6,6 +6,8 @@ import { buildMultipartResponse } from './multipart';
|
|
|
6
6
|
import { signRSASHA256, convertSHA256HashToUUID, convertToDictionaryItemsRepresentation } from './signing';
|
|
7
7
|
import { jsonError } from './helpers';
|
|
8
8
|
|
|
9
|
+
const debug = process.env.DEBUG_UPDATES === '1';
|
|
10
|
+
|
|
9
11
|
export async function handleManifest(request: Request, options: UpdatesHandlerOptions): Promise<Response> {
|
|
10
12
|
const headers = request.headers;
|
|
11
13
|
|
|
@@ -14,24 +16,46 @@ export async function handleManifest(request: Request, options: UpdatesHandlerOp
|
|
|
14
16
|
|
|
15
17
|
const platform = headers.get('expo-platform');
|
|
16
18
|
if (platform !== 'ios' && platform !== 'android') {
|
|
19
|
+
console.warn('[updates] manifest request rejected: unsupported platform', {
|
|
20
|
+
platform: headers.get('expo-platform'),
|
|
21
|
+
});
|
|
17
22
|
return jsonError(400, 'Unsupported platform. Expected either ios or android.');
|
|
18
23
|
}
|
|
19
24
|
|
|
20
25
|
const runtimeVersion = headers.get('expo-runtime-version');
|
|
21
26
|
if (!runtimeVersion) {
|
|
27
|
+
console.warn('[updates] manifest request rejected: no runtimeVersion');
|
|
22
28
|
return jsonError(400, 'No runtimeVersion provided.');
|
|
23
29
|
}
|
|
24
30
|
|
|
25
31
|
const channelName = headers.get('expo-channel-name') || options.defaultChannel || 'production';
|
|
26
32
|
|
|
33
|
+
debug && console.log('[updates] manifest request', {
|
|
34
|
+
platform,
|
|
35
|
+
runtimeVersion,
|
|
36
|
+
channelName,
|
|
37
|
+
currentUpdateId: headers.get('expo-current-update-id'),
|
|
38
|
+
protocolVersion,
|
|
39
|
+
expectSignature: !!headers.get('expo-expect-signature'),
|
|
40
|
+
});
|
|
41
|
+
|
|
27
42
|
// Try S3 flat-file manifest first (works without DB)
|
|
43
|
+
const s3Key = `${channelName}/${platform}/${runtimeVersion}/manifest.json`;
|
|
28
44
|
const s3Manifest = await tryS3Manifest(options, channelName, platform, runtimeVersion);
|
|
29
45
|
if (s3Manifest) {
|
|
46
|
+
debug && console.log('[updates] S3 manifest found', {
|
|
47
|
+
key: s3Key,
|
|
48
|
+
updateId: s3Manifest.id,
|
|
49
|
+
assetCount: s3Manifest.assets?.length,
|
|
50
|
+
hasLaunchAsset: !!s3Manifest.launchAsset,
|
|
51
|
+
});
|
|
30
52
|
return buildManifestResponse(request, s3Manifest, options, protocolVersion);
|
|
31
53
|
}
|
|
54
|
+
debug && console.log('[updates] S3 manifest not found', { key: s3Key });
|
|
32
55
|
|
|
33
56
|
// Fall back to DB-based lookup (V2)
|
|
34
57
|
if (!options.db) {
|
|
58
|
+
debug && console.log('[updates] no DB configured, returning 404');
|
|
35
59
|
return jsonError(404, `No update found for ${channelName}/${platform}/${runtimeVersion}`);
|
|
36
60
|
}
|
|
37
61
|
|
|
@@ -50,13 +74,16 @@ export async function handleManifest(request: Request, options: UpdatesHandlerOp
|
|
|
50
74
|
.limit(1);
|
|
51
75
|
|
|
52
76
|
if (!channel) {
|
|
77
|
+
console.warn('[updates] DB channel not found', { channelName });
|
|
53
78
|
return jsonError(404, `Channel "${channelName}" not found.`);
|
|
54
79
|
}
|
|
55
80
|
|
|
56
81
|
const branchRef = (channel.data as Record<string, unknown>)?.branchRef as string;
|
|
57
82
|
if (!branchRef) {
|
|
83
|
+
console.warn('[updates] DB channel has no branch mapping', { channelName });
|
|
58
84
|
return jsonError(404, `Channel "${channelName}" has no branch mapping.`);
|
|
59
85
|
}
|
|
86
|
+
debug && console.log('[updates] DB channel resolved', { channelName, branchRef });
|
|
60
87
|
|
|
61
88
|
// Look up latest release (buildArtifact type='ota') via run branchRef
|
|
62
89
|
const releaseResults = await options.db
|
|
@@ -74,9 +101,15 @@ export async function handleManifest(request: Request, options: UpdatesHandlerOp
|
|
|
74
101
|
.limit(1);
|
|
75
102
|
|
|
76
103
|
if (!releaseResults[0]) {
|
|
104
|
+
console.warn('[updates] DB release not found', { branchRef, runtimeVersion, platform });
|
|
77
105
|
return jsonError(404, `No update found for runtime version: ${runtimeVersion}`);
|
|
78
106
|
}
|
|
79
107
|
release = releaseResults[0].release;
|
|
108
|
+
debug && console.log('[updates] DB release found', {
|
|
109
|
+
releaseId: release.id,
|
|
110
|
+
runId: release.runId,
|
|
111
|
+
updateId: (release.data as Record<string, unknown>)?.updateId,
|
|
112
|
+
});
|
|
80
113
|
|
|
81
114
|
// Look up assets (buildArtifacts type='ota_asset') for this release's run
|
|
82
115
|
assets = await options.db
|
|
@@ -86,8 +119,12 @@ export async function handleManifest(request: Request, options: UpdatesHandlerOp
|
|
|
86
119
|
eq(buildArtifacts.type, 'ota_asset'),
|
|
87
120
|
eq(buildArtifacts.runId, release.runId),
|
|
88
121
|
));
|
|
89
|
-
} catch {
|
|
122
|
+
} catch (err) {
|
|
90
123
|
// DB unavailable or tables don't exist — not an error in V1
|
|
124
|
+
console.error('[updates] DB lookup error', {
|
|
125
|
+
error: err instanceof Error ? err.message : String(err),
|
|
126
|
+
channelName, platform, runtimeVersion,
|
|
127
|
+
});
|
|
91
128
|
return jsonError(404, `No update found for ${channelName}/${platform}/${runtimeVersion}`);
|
|
92
129
|
}
|
|
93
130
|
|
|
@@ -95,6 +132,7 @@ export async function handleManifest(request: Request, options: UpdatesHandlerOp
|
|
|
95
132
|
|
|
96
133
|
// Handle rollback
|
|
97
134
|
if (releaseData.isRollback) {
|
|
135
|
+
debug && console.log('[updates] response: rollback', { protocolVersion });
|
|
98
136
|
if (protocolVersion === 0) {
|
|
99
137
|
return jsonError(400, 'Rollbacks not supported on protocol version 0');
|
|
100
138
|
}
|
|
@@ -106,6 +144,9 @@ export async function handleManifest(request: Request, options: UpdatesHandlerOp
|
|
|
106
144
|
const updateId = releaseData.updateId as string;
|
|
107
145
|
const releaseUUID = convertSHA256HashToUUID(updateId);
|
|
108
146
|
if (currentUpdateId === releaseUUID && protocolVersion === 1) {
|
|
147
|
+
debug && console.log('[updates] response: noUpdateAvailable', {
|
|
148
|
+
currentUpdateId, releaseUUID,
|
|
149
|
+
});
|
|
109
150
|
return buildNoUpdateAvailableResponse(request, options, protocolVersion);
|
|
110
151
|
}
|
|
111
152
|
|
|
@@ -148,6 +189,13 @@ export async function handleManifest(request: Request, options: UpdatesHandlerOp
|
|
|
148
189
|
},
|
|
149
190
|
};
|
|
150
191
|
|
|
192
|
+
debug && console.log('[updates] response: manifest', {
|
|
193
|
+
updateId: releaseUUID,
|
|
194
|
+
assetCount: regularAssets.length,
|
|
195
|
+
hasLaunchAsset: !!launchAsset,
|
|
196
|
+
source: 'db',
|
|
197
|
+
});
|
|
198
|
+
|
|
151
199
|
return buildManifestResponse(request, manifest, options, protocolVersion);
|
|
152
200
|
}
|
|
153
201
|
|