@absolutejs/deploy 0.21.2 → 0.22.0
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/README.md +36 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +306 -1
- package/dist/index.js.map +4 -3
- package/dist/nativeRelease.d.ts +89 -0
- package/dist/nativeRelease.js +343 -0
- package/dist/nativeRelease.js.map +10 -0
- package/package.json +7 -2
package/README.md
CHANGED
|
@@ -12,6 +12,42 @@ atomically, `rollback(releaseId)` re-points the symlink and restarts.
|
|
|
12
12
|
Zero `ssh2` / `node-ssh` dependency — `sshTarget` shells out to the system
|
|
13
13
|
`ssh` / `rsync` binaries that already ship on Mac, Linux, and WSL.
|
|
14
14
|
|
|
15
|
+
## Native application releases (0.22.0)
|
|
16
|
+
|
|
17
|
+
`@absolutejs/deploy/native-release` publishes the immutable release directory
|
|
18
|
+
created by `absolute mobile build android` through any `@absolutejs/blob`
|
|
19
|
+
adapter. It verifies the local AAB's declared size and SHA-256 digest again,
|
|
20
|
+
requires a signed build by default, and writes the binary only once under its
|
|
21
|
+
content-derived release identity.
|
|
22
|
+
|
|
23
|
+
```ts
|
|
24
|
+
import { createNativeReleaseRegistry } from '@absolutejs/deploy/native-release';
|
|
25
|
+
import { s3BlobStore } from '@absolutejs/blob/s3';
|
|
26
|
+
|
|
27
|
+
const store = s3BlobStore({ client, bucket: 'absolute-releases' });
|
|
28
|
+
const releases = createNativeReleaseRegistry({ store });
|
|
29
|
+
|
|
30
|
+
const published = await releases.publish({
|
|
31
|
+
releaseRoot:
|
|
32
|
+
'.absolutejs/mobile/releases/android/amobile_android_<sha256>',
|
|
33
|
+
channel: 'internal'
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
await releases.promote({
|
|
37
|
+
appId: published.record.metadata.appId,
|
|
38
|
+
platform: 'android',
|
|
39
|
+
releaseId: published.record.metadata.releaseId,
|
|
40
|
+
channel: 'production'
|
|
41
|
+
});
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Channels are small mutable pointers; release records and AAB bytes are
|
|
45
|
+
immutable. Promoting an older retained release is therefore a rollback without
|
|
46
|
+
rebuilding or copying the binary. Passing `allowUnsigned: true` is required on
|
|
47
|
+
both publication and promotion for intentionally non-publishable local-testing
|
|
48
|
+
artifacts. The registry uses the structural BlobStore shape, so Deploy does not
|
|
49
|
+
take a runtime dependency on `@absolutejs/blob` or a cloud SDK.
|
|
50
|
+
|
|
15
51
|
## Infrastructure providers (0.14.0)
|
|
16
52
|
|
|
17
53
|
Control planes use the normalized `InfrastructureProvider` contract from
|
package/dist/index.d.ts
CHANGED
|
@@ -18,5 +18,7 @@ export type { DeployContext, DeployHooks, DeployOptions, DeployResult, DeploySte
|
|
|
18
18
|
export { createDeployer, defaultBunPipeline } from './deployer';
|
|
19
19
|
export type { CreatedReleaseArtifact, ReleaseArtifactMetadata, } from './releaseArtifact';
|
|
20
20
|
export { createReleaseArtifact, extractReleaseArtifact, receiveReleaseArtifact, ReleaseArtifactError, } from './releaseArtifact';
|
|
21
|
+
export type { AndroidNativeReleaseMetadata, NativeReleaseBlobObject, NativeReleaseBlobStore, NativeReleaseChannel, NativeReleaseMetadata, NativeReleasePublication, NativeReleaseRecord, NativeReleaseRegistry, NativeReleaseRegistryOptions, } from './nativeRelease';
|
|
22
|
+
export { createNativeReleaseRegistry, NATIVE_RELEASE_REGISTRY_FORMAT, NativeReleaseRegistryError, } from './nativeRelease';
|
|
21
23
|
export type { EdgeIngress, EdgeIngressBackend, EdgeIngressCapabilities, EdgeIngressProtocol, EdgeIngressProvider, EdgeIngressSpec, EdgeIngressState, } from './edgeIngress';
|
|
22
24
|
export { EdgeIngressValidationError, normalizedEdgeIngressBackends, validateEdgeIngressSpec, } from './edgeIngress';
|
package/dist/index.js
CHANGED
|
@@ -164,6 +164,308 @@ var extractReleaseArtifact = async (options) => {
|
|
|
164
164
|
return { extracted: true };
|
|
165
165
|
};
|
|
166
166
|
|
|
167
|
+
// src/nativeRelease.ts
|
|
168
|
+
import { createHash } from "crypto";
|
|
169
|
+
import { readFile, stat as stat2 } from "fs/promises";
|
|
170
|
+
import path2 from "path";
|
|
171
|
+
var DEFAULT_PREFIX = "absolutejs/native-releases";
|
|
172
|
+
var DEFAULT_MAX_ARTIFACT_BYTES = 2147483648;
|
|
173
|
+
var NATIVE_RELEASE_REGISTRY_FORMAT = 1;
|
|
174
|
+
var SHA256_PATTERN = /^[a-f0-9]{64}$/;
|
|
175
|
+
var APP_ID_PATTERN = /^[A-Za-z][\w]*(?:\.[A-Za-z][\w]*)+$/;
|
|
176
|
+
var CHANNEL_PATTERN = /^[a-z0-9][a-z0-9._-]{0,63}$/;
|
|
177
|
+
|
|
178
|
+
class NativeReleaseRegistryError extends Error {
|
|
179
|
+
}
|
|
180
|
+
var isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
181
|
+
var requireString = (value, field) => {
|
|
182
|
+
if (typeof value !== "string" || value.length === 0)
|
|
183
|
+
throw new NativeReleaseRegistryError(`Native release ${field} is invalid`);
|
|
184
|
+
return value;
|
|
185
|
+
};
|
|
186
|
+
var parseMetadata = (value) => {
|
|
187
|
+
if (!isRecord(value))
|
|
188
|
+
throw new NativeReleaseRegistryError("Native release metadata is invalid");
|
|
189
|
+
const appId = requireString(value.appId, "appId");
|
|
190
|
+
const sha256 = requireString(value.sha256, "sha256");
|
|
191
|
+
const releaseId = requireString(value.releaseId, "releaseId");
|
|
192
|
+
if (!APP_ID_PATTERN.test(appId))
|
|
193
|
+
throw new NativeReleaseRegistryError("Native release appId is invalid");
|
|
194
|
+
if (!SHA256_PATTERN.test(sha256))
|
|
195
|
+
throw new NativeReleaseRegistryError("Native release sha256 is invalid");
|
|
196
|
+
if (releaseId !== `amobile_android_${sha256}`)
|
|
197
|
+
throw new NativeReleaseRegistryError("Native release id does not match its artifact digest");
|
|
198
|
+
if (value.artifact !== "app-release.aab" || value.engine !== "capacitor" || value.format !== 1 || value.platform !== "android" || value.type !== "aab" || typeof value.signed !== "boolean" || !Number.isSafeInteger(value.bytes) || Number(value.bytes) < 1)
|
|
199
|
+
throw new NativeReleaseRegistryError("Native release metadata is invalid");
|
|
200
|
+
return {
|
|
201
|
+
appBuild: requireString(value.appBuild, "appBuild"),
|
|
202
|
+
appId,
|
|
203
|
+
artifact: "app-release.aab",
|
|
204
|
+
bytes: Number(value.bytes),
|
|
205
|
+
engine: "capacitor",
|
|
206
|
+
format: 1,
|
|
207
|
+
platform: "android",
|
|
208
|
+
releaseId,
|
|
209
|
+
runtime: requireString(value.runtime, "runtime"),
|
|
210
|
+
sha256,
|
|
211
|
+
signed: value.signed,
|
|
212
|
+
type: "aab"
|
|
213
|
+
};
|
|
214
|
+
};
|
|
215
|
+
var normalizedPrefix = (value) => {
|
|
216
|
+
const prefix = value.replace(/^\/+|\/+$/g, "");
|
|
217
|
+
if (prefix.length === 0 || prefix.split("/").some((segment) => segment === "." || segment === ".."))
|
|
218
|
+
throw new NativeReleaseRegistryError("Native release prefix is invalid");
|
|
219
|
+
return prefix;
|
|
220
|
+
};
|
|
221
|
+
var requireChannel = (value) => {
|
|
222
|
+
if (!CHANNEL_PATTERN.test(value))
|
|
223
|
+
throw new NativeReleaseRegistryError("Native release channel is invalid");
|
|
224
|
+
return value;
|
|
225
|
+
};
|
|
226
|
+
var appIdentity = (appId) => createHash("sha256").update(appId).digest("hex");
|
|
227
|
+
var sha256Bytes = (bytes) => createHash("sha256").update(bytes).digest("hex");
|
|
228
|
+
var isIsoTimestamp = (value) => {
|
|
229
|
+
if (typeof value !== "string" || Number.isNaN(Date.parse(value)))
|
|
230
|
+
return false;
|
|
231
|
+
return new Date(value).toISOString() === value;
|
|
232
|
+
};
|
|
233
|
+
var sha256File2 = async (file) => {
|
|
234
|
+
const hasher = new Bun.CryptoHasher("sha256");
|
|
235
|
+
for await (const chunk of file.stream())
|
|
236
|
+
hasher.update(chunk);
|
|
237
|
+
return hasher.digest("hex");
|
|
238
|
+
};
|
|
239
|
+
var encodedJson = (value) => new TextEncoder().encode(`${JSON.stringify(value, null, 2)}
|
|
240
|
+
`);
|
|
241
|
+
var decodedJson = (bytes) => JSON.parse(new TextDecoder().decode(bytes));
|
|
242
|
+
var sameMetadata = (left, right) => JSON.stringify(left) === JSON.stringify(right);
|
|
243
|
+
var parseRecord = (value) => {
|
|
244
|
+
if (!isRecord(value) || value.format !== NATIVE_RELEASE_REGISTRY_FORMAT || typeof value.artifactKey !== "string")
|
|
245
|
+
throw new NativeReleaseRegistryError("Native release record is invalid");
|
|
246
|
+
const metadata = parseMetadata(value.metadata);
|
|
247
|
+
return {
|
|
248
|
+
artifactKey: value.artifactKey,
|
|
249
|
+
format: NATIVE_RELEASE_REGISTRY_FORMAT,
|
|
250
|
+
metadata
|
|
251
|
+
};
|
|
252
|
+
};
|
|
253
|
+
var parseChannel = (value) => {
|
|
254
|
+
if (!isRecord(value) || value.format !== NATIVE_RELEASE_REGISTRY_FORMAT || value.platform !== "android" || !isIsoTimestamp(value.promotedAt))
|
|
255
|
+
throw new NativeReleaseRegistryError("Native release channel is invalid");
|
|
256
|
+
const appId = requireString(value.appId, "channel appId");
|
|
257
|
+
const channel = requireChannel(requireString(value.channel, "channel"));
|
|
258
|
+
const sha256 = requireString(value.sha256, "channel sha256");
|
|
259
|
+
const releaseId = requireString(value.releaseId, "channel releaseId");
|
|
260
|
+
if (!APP_ID_PATTERN.test(appId) || !SHA256_PATTERN.test(sha256))
|
|
261
|
+
throw new NativeReleaseRegistryError("Native release channel is invalid");
|
|
262
|
+
if (releaseId !== `amobile_android_${sha256}`)
|
|
263
|
+
throw new NativeReleaseRegistryError("Native release channel identity does not match");
|
|
264
|
+
return {
|
|
265
|
+
appId,
|
|
266
|
+
channel,
|
|
267
|
+
format: NATIVE_RELEASE_REGISTRY_FORMAT,
|
|
268
|
+
platform: "android",
|
|
269
|
+
promotedAt: value.promotedAt,
|
|
270
|
+
releaseId,
|
|
271
|
+
sha256
|
|
272
|
+
};
|
|
273
|
+
};
|
|
274
|
+
var createNativeReleaseRegistry = (options) => {
|
|
275
|
+
const prefix = normalizedPrefix(options.prefix ?? DEFAULT_PREFIX);
|
|
276
|
+
const maxArtifactBytes = options.maxArtifactBytes ?? DEFAULT_MAX_ARTIFACT_BYTES;
|
|
277
|
+
if (!Number.isSafeInteger(maxArtifactBytes) || maxArtifactBytes < 1)
|
|
278
|
+
throw new NativeReleaseRegistryError("Native release maxArtifactBytes is invalid");
|
|
279
|
+
const clock = options.clock ?? (() => new Date);
|
|
280
|
+
const appRoot = (appId, platform) => `${prefix}/${appIdentity(appId)}/${platform}`;
|
|
281
|
+
const releaseRoot = (metadata) => `${appRoot(metadata.appId, metadata.platform)}/releases/${metadata.releaseId}`;
|
|
282
|
+
const recordKey = (metadata) => `${releaseRoot(metadata)}/release.json`;
|
|
283
|
+
const artifactKey = (metadata) => `${releaseRoot(metadata)}/${metadata.artifact}`;
|
|
284
|
+
const channelKey = (appId, platform, channel) => `${appRoot(appId, platform)}/channels/${requireChannel(channel)}.json`;
|
|
285
|
+
const requireStoredArtifact = async (key, metadata) => {
|
|
286
|
+
const stored = await options.store.head(key);
|
|
287
|
+
if (!stored || stored.size !== metadata.bytes || stored.metadata?.sha256 !== metadata.sha256 || stored.metadata?.releaseId !== metadata.releaseId)
|
|
288
|
+
throw new NativeReleaseRegistryError("Stored native release artifact does not match its immutable identity");
|
|
289
|
+
};
|
|
290
|
+
const read = async (input) => {
|
|
291
|
+
if (!APP_ID_PATTERN.test(input.appId))
|
|
292
|
+
throw new NativeReleaseRegistryError("Native release appId is invalid");
|
|
293
|
+
const digest = input.releaseId.replace(/^amobile_android_/, "");
|
|
294
|
+
if (!SHA256_PATTERN.test(digest) || input.releaseId !== `amobile_android_${digest}`)
|
|
295
|
+
throw new NativeReleaseRegistryError("Native release id is invalid");
|
|
296
|
+
const identity = {
|
|
297
|
+
appBuild: "lookup",
|
|
298
|
+
appId: input.appId,
|
|
299
|
+
artifact: "app-release.aab",
|
|
300
|
+
bytes: 1,
|
|
301
|
+
engine: "capacitor",
|
|
302
|
+
format: 1,
|
|
303
|
+
platform: "android",
|
|
304
|
+
releaseId: input.releaseId,
|
|
305
|
+
runtime: "lookup",
|
|
306
|
+
sha256: digest,
|
|
307
|
+
signed: true,
|
|
308
|
+
type: "aab"
|
|
309
|
+
};
|
|
310
|
+
const key = recordKey(identity);
|
|
311
|
+
const bytes = await options.store.get(key);
|
|
312
|
+
if (!bytes)
|
|
313
|
+
return null;
|
|
314
|
+
const storedRecord = await options.store.head(key);
|
|
315
|
+
if (!storedRecord || storedRecord.size !== bytes.byteLength || storedRecord.metadata?.releaseId !== input.releaseId || storedRecord.metadata?.sha256 !== sha256Bytes(bytes))
|
|
316
|
+
throw new NativeReleaseRegistryError("Stored native release record does not match its immutable identity");
|
|
317
|
+
const record = parseRecord(decodedJson(bytes));
|
|
318
|
+
if (record.metadata.appId !== input.appId || record.metadata.platform !== input.platform || record.metadata.releaseId !== input.releaseId || record.artifactKey !== artifactKey(record.metadata))
|
|
319
|
+
throw new NativeReleaseRegistryError("Stored native release record identity does not match");
|
|
320
|
+
await requireStoredArtifact(record.artifactKey, record.metadata);
|
|
321
|
+
return record;
|
|
322
|
+
};
|
|
323
|
+
const promote = async (input) => {
|
|
324
|
+
input.signal?.throwIfAborted();
|
|
325
|
+
const record = await read(input);
|
|
326
|
+
if (!record)
|
|
327
|
+
throw new NativeReleaseRegistryError("Native release was not published");
|
|
328
|
+
if (!record.metadata.signed && !input.allowUnsigned)
|
|
329
|
+
throw new NativeReleaseRegistryError("Unsigned native releases cannot be promoted");
|
|
330
|
+
const key = channelKey(input.appId, input.platform, input.channel);
|
|
331
|
+
const existingBytes = await options.store.get(key);
|
|
332
|
+
if (existingBytes) {
|
|
333
|
+
const existing = parseChannel(decodedJson(existingBytes));
|
|
334
|
+
if (existing.appId !== input.appId || existing.platform !== input.platform || existing.channel !== input.channel)
|
|
335
|
+
throw new NativeReleaseRegistryError("Stored native release channel identity does not match");
|
|
336
|
+
if (existing.releaseId === input.releaseId)
|
|
337
|
+
return existing;
|
|
338
|
+
}
|
|
339
|
+
const channel = {
|
|
340
|
+
appId: input.appId,
|
|
341
|
+
channel: requireChannel(input.channel),
|
|
342
|
+
format: NATIVE_RELEASE_REGISTRY_FORMAT,
|
|
343
|
+
platform: input.platform,
|
|
344
|
+
promotedAt: clock().toISOString(),
|
|
345
|
+
releaseId: record.metadata.releaseId,
|
|
346
|
+
sha256: record.metadata.sha256
|
|
347
|
+
};
|
|
348
|
+
const serialized = encodedJson(channel);
|
|
349
|
+
await options.store.put(key, serialized, {
|
|
350
|
+
cacheControl: "no-cache",
|
|
351
|
+
contentType: "application/json",
|
|
352
|
+
maxBytes: serialized.byteLength,
|
|
353
|
+
metadata: {
|
|
354
|
+
channel: channel.channel,
|
|
355
|
+
releaseId: channel.releaseId,
|
|
356
|
+
sha256: channel.sha256
|
|
357
|
+
},
|
|
358
|
+
signal: input.signal
|
|
359
|
+
});
|
|
360
|
+
const stored = await options.store.get(key);
|
|
361
|
+
if (!stored || JSON.stringify(parseChannel(decodedJson(stored))) !== JSON.stringify(channel))
|
|
362
|
+
throw new NativeReleaseRegistryError("Native release channel verification failed");
|
|
363
|
+
return channel;
|
|
364
|
+
};
|
|
365
|
+
return {
|
|
366
|
+
promote,
|
|
367
|
+
publish: async (input) => {
|
|
368
|
+
input.signal?.throwIfAborted();
|
|
369
|
+
const localRoot = path2.resolve(input.releaseRoot);
|
|
370
|
+
const metadata = parseMetadata(JSON.parse(await readFile(path2.join(localRoot, "release.json"), "utf8")));
|
|
371
|
+
if (metadata.bytes > maxArtifactBytes)
|
|
372
|
+
throw new NativeReleaseRegistryError("Native release exceeds the configured artifact limit");
|
|
373
|
+
if (!metadata.signed && !input.allowUnsigned)
|
|
374
|
+
throw new NativeReleaseRegistryError("Unsigned native releases cannot be published");
|
|
375
|
+
const localArtifact = path2.join(localRoot, metadata.artifact);
|
|
376
|
+
const artifactStats = await stat2(localArtifact).catch(() => null);
|
|
377
|
+
if (!artifactStats?.isFile() || artifactStats.size !== metadata.bytes)
|
|
378
|
+
throw new NativeReleaseRegistryError("Native release artifact size does not match its metadata");
|
|
379
|
+
if (await sha256File2(Bun.file(localArtifact)) !== metadata.sha256)
|
|
380
|
+
throw new NativeReleaseRegistryError("Native release artifact digest does not match its metadata");
|
|
381
|
+
const existing = await read({
|
|
382
|
+
appId: metadata.appId,
|
|
383
|
+
platform: metadata.platform,
|
|
384
|
+
releaseId: metadata.releaseId
|
|
385
|
+
});
|
|
386
|
+
let record;
|
|
387
|
+
let reused = false;
|
|
388
|
+
if (existing) {
|
|
389
|
+
if (!sameMetadata(existing.metadata, metadata))
|
|
390
|
+
throw new NativeReleaseRegistryError("Published native release metadata is immutable");
|
|
391
|
+
record = existing;
|
|
392
|
+
reused = true;
|
|
393
|
+
} else {
|
|
394
|
+
const key = artifactKey(metadata);
|
|
395
|
+
const storedArtifact = await options.store.head(key);
|
|
396
|
+
if (storedArtifact) {
|
|
397
|
+
await requireStoredArtifact(key, metadata);
|
|
398
|
+
} else {
|
|
399
|
+
await options.store.put(key, Bun.file(localArtifact).stream(), {
|
|
400
|
+
cacheControl: "public, max-age=31536000, immutable",
|
|
401
|
+
contentType: "application/octet-stream",
|
|
402
|
+
maxBytes: metadata.bytes,
|
|
403
|
+
metadata: {
|
|
404
|
+
appId: metadata.appId,
|
|
405
|
+
releaseId: metadata.releaseId,
|
|
406
|
+
sha256: metadata.sha256
|
|
407
|
+
},
|
|
408
|
+
signal: input.signal
|
|
409
|
+
});
|
|
410
|
+
await requireStoredArtifact(key, metadata);
|
|
411
|
+
}
|
|
412
|
+
record = {
|
|
413
|
+
artifactKey: key,
|
|
414
|
+
format: NATIVE_RELEASE_REGISTRY_FORMAT,
|
|
415
|
+
metadata
|
|
416
|
+
};
|
|
417
|
+
const serialized = encodedJson(record);
|
|
418
|
+
await options.store.put(recordKey(metadata), serialized, {
|
|
419
|
+
cacheControl: "public, max-age=31536000, immutable",
|
|
420
|
+
contentType: "application/json",
|
|
421
|
+
maxBytes: serialized.byteLength,
|
|
422
|
+
metadata: {
|
|
423
|
+
releaseId: metadata.releaseId,
|
|
424
|
+
sha256: sha256Bytes(serialized)
|
|
425
|
+
},
|
|
426
|
+
signal: input.signal
|
|
427
|
+
});
|
|
428
|
+
const verified = await read({
|
|
429
|
+
appId: metadata.appId,
|
|
430
|
+
platform: metadata.platform,
|
|
431
|
+
releaseId: metadata.releaseId
|
|
432
|
+
});
|
|
433
|
+
if (!verified || JSON.stringify(verified) !== JSON.stringify(record))
|
|
434
|
+
throw new NativeReleaseRegistryError("Native release publication verification failed");
|
|
435
|
+
}
|
|
436
|
+
const channel = input.channel ? await promote({
|
|
437
|
+
allowUnsigned: input.allowUnsigned,
|
|
438
|
+
appId: metadata.appId,
|
|
439
|
+
channel: input.channel,
|
|
440
|
+
platform: metadata.platform,
|
|
441
|
+
releaseId: metadata.releaseId,
|
|
442
|
+
signal: input.signal
|
|
443
|
+
}) : undefined;
|
|
444
|
+
return { ...channel ? { channel } : {}, record, reused };
|
|
445
|
+
},
|
|
446
|
+
read,
|
|
447
|
+
resolve: async (input) => {
|
|
448
|
+
if (!APP_ID_PATTERN.test(input.appId))
|
|
449
|
+
throw new NativeReleaseRegistryError("Native release appId is invalid");
|
|
450
|
+
const key = channelKey(input.appId, input.platform, input.channel);
|
|
451
|
+
const bytes = await options.store.get(key);
|
|
452
|
+
if (!bytes)
|
|
453
|
+
return null;
|
|
454
|
+
const channel = parseChannel(decodedJson(bytes));
|
|
455
|
+
if (channel.appId !== input.appId || channel.platform !== input.platform || channel.channel !== input.channel)
|
|
456
|
+
throw new NativeReleaseRegistryError("Stored native release channel identity does not match");
|
|
457
|
+
const record = await read({
|
|
458
|
+
appId: channel.appId,
|
|
459
|
+
platform: channel.platform,
|
|
460
|
+
releaseId: channel.releaseId
|
|
461
|
+
});
|
|
462
|
+
if (!record || record.metadata.sha256 !== channel.sha256)
|
|
463
|
+
throw new NativeReleaseRegistryError("Native release channel points to a missing or invalid release");
|
|
464
|
+
return { channel, record };
|
|
465
|
+
}
|
|
466
|
+
};
|
|
467
|
+
};
|
|
468
|
+
|
|
167
469
|
// src/edgeIngress.ts
|
|
168
470
|
var validPort = (port) => Number.isInteger(port) && port >= 1 && port <= 65535;
|
|
169
471
|
|
|
@@ -859,11 +1161,14 @@ export {
|
|
|
859
1161
|
extractReleaseArtifact,
|
|
860
1162
|
defaultBunPipeline,
|
|
861
1163
|
createReleaseArtifact,
|
|
1164
|
+
createNativeReleaseRegistry,
|
|
862
1165
|
createDeployer,
|
|
863
1166
|
bareManager,
|
|
864
1167
|
ReleaseArtifactError,
|
|
1168
|
+
NativeReleaseRegistryError,
|
|
1169
|
+
NATIVE_RELEASE_REGISTRY_FORMAT,
|
|
865
1170
|
EdgeIngressValidationError
|
|
866
1171
|
};
|
|
867
1172
|
|
|
868
|
-
//# debugId=
|
|
1173
|
+
//# debugId=19FB526D8301A46164756E2164756E21
|
|
869
1174
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
|
-
"sources": ["../src/releaseArtifact.ts", "../src/edgeIngress.ts", "../src/targets.ts", "../src/processManagers.ts", "../src/deployer.ts"],
|
|
3
|
+
"sources": ["../src/releaseArtifact.ts", "../src/nativeRelease.ts", "../src/edgeIngress.ts", "../src/targets.ts", "../src/processManagers.ts", "../src/deployer.ts"],
|
|
4
4
|
"sourcesContent": [
|
|
5
5
|
"import { mkdir, mkdtemp, rm, stat } from \"node:fs/promises\";\nimport { tmpdir } from \"node:os\";\nimport path from \"node:path\";\n\nconst DEFAULT_MAX_BYTES = 2_147_483_648;\nconst ERROR_DETAIL_LIMIT = 500;\nconst SAFE_RELEASE_ID = /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/;\n\nexport type ReleaseArtifactMetadata = {\n bytes: number;\n releaseId: string;\n sha256: string;\n};\n\nexport type CreatedReleaseArtifact = {\n dispose: () => Promise<void>;\n file: Bun.BunFile;\n metadata: ReleaseArtifactMetadata;\n path: string;\n};\n\nexport class ReleaseArtifactError extends Error {}\n\nconst run = async (command: string[]) => {\n const process = Bun.spawn(command, { stderr: \"pipe\", stdout: \"pipe\" });\n const [exitCode, stderr, stdout] = await Promise.all([\n process.exited,\n new Response(process.stderr).text(),\n new Response(process.stdout).text(),\n ]);\n if (exitCode !== 0)\n throw new ReleaseArtifactError(\n `${command[0]} failed (${exitCode}): ${stderr.slice(0, ERROR_DETAIL_LIMIT)}`,\n );\n\n return stdout;\n};\n\nconst assertReleaseId = (value: string) => {\n if (!SAFE_RELEASE_ID.test(value))\n throw new ReleaseArtifactError(\"Release id is invalid\");\n\n return value;\n};\n\nconst assertExclude = (value: string) => {\n if (\n value.length === 0 ||\n value.startsWith(\"-\") ||\n value.startsWith(\"/\") ||\n value.split(\"/\").some((part) => part === \"..\") ||\n /[\\0\\r\\n]/.test(value)\n )\n throw new ReleaseArtifactError(`Invalid release exclusion: ${value}`);\n\n return value.replace(/^\\.\\//, \"\");\n};\n\nconst sha256File = async (file: Blob) => {\n const hasher = new Bun.CryptoHasher(\"sha256\");\n for await (const chunk of file.stream()) hasher.update(chunk);\n\n return hasher.digest(\"hex\");\n};\n\nexport const createReleaseArtifact = async (options: {\n exclude?: string[];\n releaseId?: string;\n sourceRoot: string;\n temporaryRoot?: string;\n}): Promise<CreatedReleaseArtifact> => {\n const releaseId = assertReleaseId(options.releaseId ?? crypto.randomUUID());\n const source = path.resolve(options.sourceRoot);\n const sourceStats = await stat(source).catch(() => null);\n if (!sourceStats?.isDirectory())\n throw new ReleaseArtifactError(\"Release source root is not a directory\");\n if (!(await Bun.file(path.join(source, \"package.json\")).exists()))\n throw new ReleaseArtifactError(\"Release source has no package.json\");\n const temporary = await mkdtemp(\n path.join(options.temporaryRoot ?? tmpdir(), \"absolutejs-release-\"),\n );\n const archivePath = path.join(temporary, `${releaseId}.tgz`);\n try {\n await run([\n \"tar\",\n \"-czf\",\n archivePath,\n ...(options.exclude ?? []).map(\n (value) => `--exclude=./${assertExclude(value)}`,\n ),\n \"-C\",\n source,\n \".\",\n ]);\n const file = Bun.file(archivePath);\n\n return {\n dispose: () => rm(temporary, { force: true, recursive: true }),\n file,\n metadata: {\n bytes: file.size,\n releaseId,\n sha256: await sha256File(file),\n },\n path: archivePath,\n };\n } catch (error) {\n await rm(temporary, { force: true, recursive: true });\n throw error;\n }\n};\n\nexport const receiveReleaseArtifact = async (options: {\n destination: string;\n expectedBytes: number;\n expectedSha256: string;\n maxBytes?: number;\n stream: ReadableStream<Uint8Array>;\n}) => {\n const maxBytes = options.maxBytes ?? DEFAULT_MAX_BYTES;\n if (\n !Number.isSafeInteger(options.expectedBytes) ||\n options.expectedBytes < 1 ||\n options.expectedBytes > maxBytes ||\n !/^[a-f0-9]{64}$/.test(options.expectedSha256)\n )\n throw new ReleaseArtifactError(\"Release artifact metadata is invalid\");\n await mkdir(path.dirname(options.destination), { recursive: true });\n const writer = Bun.file(options.destination).writer();\n const hasher = new Bun.CryptoHasher(\"sha256\");\n const reader = options.stream.getReader();\n let bytes = 0;\n try {\n while (true) {\n const { done, value: chunk } = await reader.read();\n if (done) break;\n bytes += chunk.byteLength;\n if (bytes > options.expectedBytes || bytes > maxBytes)\n throw new ReleaseArtifactError(\n \"Release artifact exceeds its declared size\",\n );\n hasher.update(chunk);\n writer.write(chunk);\n }\n } catch (error) {\n await rm(options.destination, { force: true });\n throw error;\n } finally {\n await writer.end();\n }\n if (\n bytes !== options.expectedBytes ||\n hasher.digest(\"hex\") !== options.expectedSha256\n ) {\n await rm(options.destination, { force: true });\n throw new ReleaseArtifactError(\n \"Release artifact integrity verification failed\",\n );\n }\n\n return { bytes, sha256: options.expectedSha256 };\n};\n\nexport const extractReleaseArtifact = async (options: {\n archivePath: string;\n destination: string;\n}) => {\n const [names, verbose] = await Promise.all([\n run([\"tar\", \"-tzf\", options.archivePath]),\n run([\"tar\", \"-tvzf\", options.archivePath]),\n ]);\n const unsafePath = names\n .split(\"\\n\")\n .filter(Boolean)\n .some(\n (entry) =>\n entry.startsWith(\"/\") || entry.split(\"/\").some((part) => part === \"..\"),\n );\n const unsafeType = verbose\n .split(\"\\n\")\n .filter(Boolean)\n .some((entry) => entry[0] !== \"-\" && entry[0] !== \"d\");\n if (unsafePath || unsafeType)\n throw new ReleaseArtifactError(\"Release artifact contains an unsafe entry\");\n await rm(options.destination, { force: true, recursive: true });\n await mkdir(options.destination, { recursive: true });\n await run([\n \"tar\",\n \"-xzf\",\n options.archivePath,\n \"--no-same-owner\",\n \"--no-same-permissions\",\n \"-C\",\n options.destination,\n ]);\n if (\n !(await Bun.file(path.join(options.destination, \"package.json\")).exists())\n )\n throw new ReleaseArtifactError(\"Release artifact has no package.json\");\n\n return { extracted: true } as const;\n};\n",
|
|
6
|
+
"import { createHash } from \"node:crypto\";\nimport { readFile, stat } from \"node:fs/promises\";\nimport path from \"node:path\";\n\nconst DEFAULT_PREFIX = \"absolutejs/native-releases\";\nconst DEFAULT_MAX_ARTIFACT_BYTES = 2_147_483_648;\nexport const NATIVE_RELEASE_REGISTRY_FORMAT = 1 as const;\nconst SHA256_PATTERN = /^[a-f0-9]{64}$/;\nconst APP_ID_PATTERN = /^[A-Za-z][\\w]*(?:\\.[A-Za-z][\\w]*)+$/;\nconst CHANNEL_PATTERN = /^[a-z0-9][a-z0-9._-]{0,63}$/;\n\nexport type AndroidNativeReleaseMetadata = {\n appBuild: string;\n appId: string;\n artifact: \"app-release.aab\";\n bytes: number;\n engine: \"capacitor\";\n format: 1;\n platform: \"android\";\n releaseId: string;\n runtime: string;\n sha256: string;\n signed: boolean;\n type: \"aab\";\n};\n\nexport type NativeReleaseMetadata = AndroidNativeReleaseMetadata;\n\nexport type NativeReleaseRecord = {\n artifactKey: string;\n format: typeof NATIVE_RELEASE_REGISTRY_FORMAT;\n metadata: NativeReleaseMetadata;\n};\n\nexport type NativeReleaseChannel = {\n appId: string;\n channel: string;\n format: typeof NATIVE_RELEASE_REGISTRY_FORMAT;\n platform: NativeReleaseMetadata[\"platform\"];\n promotedAt: string;\n releaseId: string;\n sha256: string;\n};\n\nexport type NativeReleaseBlobObject = {\n key: string;\n metadata?: Record<string, string>;\n size: number;\n};\n\nexport type NativeReleaseBlobStore = {\n get: (key: string) => Promise<Uint8Array | null>;\n head: (key: string) => Promise<NativeReleaseBlobObject | null>;\n put: (\n key: string,\n body: ReadableStream<Uint8Array> | Uint8Array | string,\n options?: {\n cacheControl?: string;\n contentType?: string;\n maxBytes?: number;\n metadata?: Record<string, string>;\n signal?: AbortSignal;\n },\n ) => Promise<unknown>;\n};\n\nexport type NativeReleasePublication = {\n channel?: NativeReleaseChannel;\n record: NativeReleaseRecord;\n reused: boolean;\n};\n\nexport type NativeReleaseRegistry = {\n promote: (options: {\n allowUnsigned?: boolean;\n appId: string;\n channel: string;\n platform: NativeReleaseMetadata[\"platform\"];\n releaseId: string;\n signal?: AbortSignal;\n }) => Promise<NativeReleaseChannel>;\n publish: (options: {\n allowUnsigned?: boolean;\n channel?: string;\n releaseRoot: string;\n signal?: AbortSignal;\n }) => Promise<NativeReleasePublication>;\n read: (options: {\n appId: string;\n platform: NativeReleaseMetadata[\"platform\"];\n releaseId: string;\n }) => Promise<NativeReleaseRecord | null>;\n resolve: (options: {\n appId: string;\n channel: string;\n platform: NativeReleaseMetadata[\"platform\"];\n }) => Promise<{\n channel: NativeReleaseChannel;\n record: NativeReleaseRecord;\n } | null>;\n};\n\nexport type NativeReleaseRegistryOptions = {\n clock?: () => Date;\n maxArtifactBytes?: number;\n prefix?: string;\n store: NativeReleaseBlobStore;\n};\n\nexport class NativeReleaseRegistryError extends Error {}\n\nconst isRecord = (value: unknown): value is Record<string, unknown> =>\n typeof value === \"object\" && value !== null && !Array.isArray(value);\n\nconst requireString = (value: unknown, field: string) => {\n if (typeof value !== \"string\" || value.length === 0)\n throw new NativeReleaseRegistryError(`Native release ${field} is invalid`);\n\n return value;\n};\n\nconst parseMetadata = (value: unknown): NativeReleaseMetadata => {\n if (!isRecord(value))\n throw new NativeReleaseRegistryError(\"Native release metadata is invalid\");\n const appId = requireString(value.appId, \"appId\");\n const sha256 = requireString(value.sha256, \"sha256\");\n const releaseId = requireString(value.releaseId, \"releaseId\");\n if (!APP_ID_PATTERN.test(appId))\n throw new NativeReleaseRegistryError(\"Native release appId is invalid\");\n if (!SHA256_PATTERN.test(sha256))\n throw new NativeReleaseRegistryError(\"Native release sha256 is invalid\");\n if (releaseId !== `amobile_android_${sha256}`)\n throw new NativeReleaseRegistryError(\n \"Native release id does not match its artifact digest\",\n );\n if (\n value.artifact !== \"app-release.aab\" ||\n value.engine !== \"capacitor\" ||\n value.format !== 1 ||\n value.platform !== \"android\" ||\n value.type !== \"aab\" ||\n typeof value.signed !== \"boolean\" ||\n !Number.isSafeInteger(value.bytes) ||\n Number(value.bytes) < 1\n )\n throw new NativeReleaseRegistryError(\"Native release metadata is invalid\");\n\n return {\n appBuild: requireString(value.appBuild, \"appBuild\"),\n appId,\n artifact: \"app-release.aab\",\n bytes: Number(value.bytes),\n engine: \"capacitor\",\n format: 1,\n platform: \"android\",\n releaseId,\n runtime: requireString(value.runtime, \"runtime\"),\n sha256,\n signed: value.signed,\n type: \"aab\",\n };\n};\n\nconst normalizedPrefix = (value: string) => {\n const prefix = value.replace(/^\\/+|\\/+$/g, \"\");\n if (\n prefix.length === 0 ||\n prefix.split(\"/\").some((segment) => segment === \".\" || segment === \"..\")\n )\n throw new NativeReleaseRegistryError(\"Native release prefix is invalid\");\n\n return prefix;\n};\n\nconst requireChannel = (value: string) => {\n if (!CHANNEL_PATTERN.test(value))\n throw new NativeReleaseRegistryError(\"Native release channel is invalid\");\n\n return value;\n};\n\nconst appIdentity = (appId: string) =>\n createHash(\"sha256\").update(appId).digest(\"hex\");\n\nconst sha256Bytes = (bytes: Uint8Array) =>\n createHash(\"sha256\").update(bytes).digest(\"hex\");\n\nconst isIsoTimestamp = (value: unknown): value is string => {\n if (typeof value !== \"string\" || Number.isNaN(Date.parse(value)))\n return false;\n\n return new Date(value).toISOString() === value;\n};\n\nconst sha256File = async (file: Bun.BunFile) => {\n const hasher = new Bun.CryptoHasher(\"sha256\");\n for await (const chunk of file.stream()) hasher.update(chunk);\n\n return hasher.digest(\"hex\");\n};\n\nconst encodedJson = (value: unknown) =>\n new TextEncoder().encode(`${JSON.stringify(value, null, 2)}\\n`);\n\nconst decodedJson = (bytes: Uint8Array) =>\n JSON.parse(new TextDecoder().decode(bytes)) as unknown;\n\nconst sameMetadata = (\n left: NativeReleaseMetadata,\n right: NativeReleaseMetadata,\n) => JSON.stringify(left) === JSON.stringify(right);\n\nconst parseRecord = (value: unknown): NativeReleaseRecord => {\n if (\n !isRecord(value) ||\n value.format !== NATIVE_RELEASE_REGISTRY_FORMAT ||\n typeof value.artifactKey !== \"string\"\n )\n throw new NativeReleaseRegistryError(\"Native release record is invalid\");\n const metadata = parseMetadata(value.metadata);\n\n return {\n artifactKey: value.artifactKey,\n format: NATIVE_RELEASE_REGISTRY_FORMAT,\n metadata,\n };\n};\n\nconst parseChannel = (value: unknown): NativeReleaseChannel => {\n if (\n !isRecord(value) ||\n value.format !== NATIVE_RELEASE_REGISTRY_FORMAT ||\n value.platform !== \"android\" ||\n !isIsoTimestamp(value.promotedAt)\n )\n throw new NativeReleaseRegistryError(\"Native release channel is invalid\");\n const appId = requireString(value.appId, \"channel appId\");\n const channel = requireChannel(requireString(value.channel, \"channel\"));\n const sha256 = requireString(value.sha256, \"channel sha256\");\n const releaseId = requireString(value.releaseId, \"channel releaseId\");\n if (!APP_ID_PATTERN.test(appId) || !SHA256_PATTERN.test(sha256))\n throw new NativeReleaseRegistryError(\"Native release channel is invalid\");\n if (releaseId !== `amobile_android_${sha256}`)\n throw new NativeReleaseRegistryError(\n \"Native release channel identity does not match\",\n );\n\n return {\n appId,\n channel,\n format: NATIVE_RELEASE_REGISTRY_FORMAT,\n platform: \"android\",\n promotedAt: value.promotedAt,\n releaseId,\n sha256,\n };\n};\n\nexport const createNativeReleaseRegistry = (\n options: NativeReleaseRegistryOptions,\n): NativeReleaseRegistry => {\n const prefix = normalizedPrefix(options.prefix ?? DEFAULT_PREFIX);\n const maxArtifactBytes =\n options.maxArtifactBytes ?? DEFAULT_MAX_ARTIFACT_BYTES;\n if (!Number.isSafeInteger(maxArtifactBytes) || maxArtifactBytes < 1)\n throw new NativeReleaseRegistryError(\n \"Native release maxArtifactBytes is invalid\",\n );\n const clock = options.clock ?? (() => new Date());\n const appRoot = (appId: string, platform: \"android\") =>\n `${prefix}/${appIdentity(appId)}/${platform}`;\n const releaseRoot = (metadata: NativeReleaseMetadata) =>\n `${appRoot(metadata.appId, metadata.platform)}/releases/${metadata.releaseId}`;\n const recordKey = (metadata: NativeReleaseMetadata) =>\n `${releaseRoot(metadata)}/release.json`;\n const artifactKey = (metadata: NativeReleaseMetadata) =>\n `${releaseRoot(metadata)}/${metadata.artifact}`;\n const channelKey = (appId: string, platform: \"android\", channel: string) =>\n `${appRoot(appId, platform)}/channels/${requireChannel(channel)}.json`;\n\n const requireStoredArtifact = async (\n key: string,\n metadata: NativeReleaseMetadata,\n ) => {\n const stored = await options.store.head(key);\n if (\n !stored ||\n stored.size !== metadata.bytes ||\n stored.metadata?.sha256 !== metadata.sha256 ||\n stored.metadata?.releaseId !== metadata.releaseId\n )\n throw new NativeReleaseRegistryError(\n \"Stored native release artifact does not match its immutable identity\",\n );\n };\n\n const read = async (input: {\n appId: string;\n platform: \"android\";\n releaseId: string;\n }) => {\n if (!APP_ID_PATTERN.test(input.appId))\n throw new NativeReleaseRegistryError(\"Native release appId is invalid\");\n const digest = input.releaseId.replace(/^amobile_android_/, \"\");\n if (\n !SHA256_PATTERN.test(digest) ||\n input.releaseId !== `amobile_android_${digest}`\n )\n throw new NativeReleaseRegistryError(\"Native release id is invalid\");\n const identity: NativeReleaseMetadata = {\n appBuild: \"lookup\",\n appId: input.appId,\n artifact: \"app-release.aab\",\n bytes: 1,\n engine: \"capacitor\",\n format: 1,\n platform: \"android\",\n releaseId: input.releaseId,\n runtime: \"lookup\",\n sha256: digest,\n signed: true,\n type: \"aab\",\n };\n const key = recordKey(identity);\n const bytes = await options.store.get(key);\n if (!bytes) return null;\n const storedRecord = await options.store.head(key);\n if (\n !storedRecord ||\n storedRecord.size !== bytes.byteLength ||\n storedRecord.metadata?.releaseId !== input.releaseId ||\n storedRecord.metadata?.sha256 !== sha256Bytes(bytes)\n )\n throw new NativeReleaseRegistryError(\n \"Stored native release record does not match its immutable identity\",\n );\n const record = parseRecord(decodedJson(bytes));\n if (\n record.metadata.appId !== input.appId ||\n record.metadata.platform !== input.platform ||\n record.metadata.releaseId !== input.releaseId ||\n record.artifactKey !== artifactKey(record.metadata)\n )\n throw new NativeReleaseRegistryError(\n \"Stored native release record identity does not match\",\n );\n await requireStoredArtifact(record.artifactKey, record.metadata);\n\n return record;\n };\n\n const promote: NativeReleaseRegistry[\"promote\"] = async (input) => {\n input.signal?.throwIfAborted();\n const record = await read(input);\n if (!record)\n throw new NativeReleaseRegistryError(\"Native release was not published\");\n if (!record.metadata.signed && !input.allowUnsigned)\n throw new NativeReleaseRegistryError(\n \"Unsigned native releases cannot be promoted\",\n );\n const key = channelKey(input.appId, input.platform, input.channel);\n const existingBytes = await options.store.get(key);\n if (existingBytes) {\n const existing = parseChannel(decodedJson(existingBytes));\n if (\n existing.appId !== input.appId ||\n existing.platform !== input.platform ||\n existing.channel !== input.channel\n )\n throw new NativeReleaseRegistryError(\n \"Stored native release channel identity does not match\",\n );\n if (existing.releaseId === input.releaseId) return existing;\n }\n const channel: NativeReleaseChannel = {\n appId: input.appId,\n channel: requireChannel(input.channel),\n format: NATIVE_RELEASE_REGISTRY_FORMAT,\n platform: input.platform,\n promotedAt: clock().toISOString(),\n releaseId: record.metadata.releaseId,\n sha256: record.metadata.sha256,\n };\n const serialized = encodedJson(channel);\n await options.store.put(key, serialized, {\n cacheControl: \"no-cache\",\n contentType: \"application/json\",\n maxBytes: serialized.byteLength,\n metadata: {\n channel: channel.channel,\n releaseId: channel.releaseId,\n sha256: channel.sha256,\n },\n signal: input.signal,\n });\n const stored = await options.store.get(key);\n if (\n !stored ||\n JSON.stringify(parseChannel(decodedJson(stored))) !==\n JSON.stringify(channel)\n )\n throw new NativeReleaseRegistryError(\n \"Native release channel verification failed\",\n );\n\n return channel;\n };\n\n return {\n promote,\n publish: async (input) => {\n input.signal?.throwIfAborted();\n const localRoot = path.resolve(input.releaseRoot);\n const metadata = parseMetadata(\n JSON.parse(\n await readFile(path.join(localRoot, \"release.json\"), \"utf8\"),\n ),\n );\n if (metadata.bytes > maxArtifactBytes)\n throw new NativeReleaseRegistryError(\n \"Native release exceeds the configured artifact limit\",\n );\n if (!metadata.signed && !input.allowUnsigned)\n throw new NativeReleaseRegistryError(\n \"Unsigned native releases cannot be published\",\n );\n const localArtifact = path.join(localRoot, metadata.artifact);\n const artifactStats = await stat(localArtifact).catch(() => null);\n if (!artifactStats?.isFile() || artifactStats.size !== metadata.bytes)\n throw new NativeReleaseRegistryError(\n \"Native release artifact size does not match its metadata\",\n );\n if ((await sha256File(Bun.file(localArtifact))) !== metadata.sha256)\n throw new NativeReleaseRegistryError(\n \"Native release artifact digest does not match its metadata\",\n );\n const existing = await read({\n appId: metadata.appId,\n platform: metadata.platform,\n releaseId: metadata.releaseId,\n });\n let record: NativeReleaseRecord;\n let reused = false;\n if (existing) {\n if (!sameMetadata(existing.metadata, metadata))\n throw new NativeReleaseRegistryError(\n \"Published native release metadata is immutable\",\n );\n record = existing;\n reused = true;\n } else {\n const key = artifactKey(metadata);\n const storedArtifact = await options.store.head(key);\n if (storedArtifact) {\n await requireStoredArtifact(key, metadata);\n } else {\n await options.store.put(key, Bun.file(localArtifact).stream(), {\n cacheControl: \"public, max-age=31536000, immutable\",\n contentType: \"application/octet-stream\",\n maxBytes: metadata.bytes,\n metadata: {\n appId: metadata.appId,\n releaseId: metadata.releaseId,\n sha256: metadata.sha256,\n },\n signal: input.signal,\n });\n await requireStoredArtifact(key, metadata);\n }\n record = {\n artifactKey: key,\n format: NATIVE_RELEASE_REGISTRY_FORMAT,\n metadata,\n };\n const serialized = encodedJson(record);\n await options.store.put(recordKey(metadata), serialized, {\n cacheControl: \"public, max-age=31536000, immutable\",\n contentType: \"application/json\",\n maxBytes: serialized.byteLength,\n metadata: {\n releaseId: metadata.releaseId,\n sha256: sha256Bytes(serialized),\n },\n signal: input.signal,\n });\n const verified = await read({\n appId: metadata.appId,\n platform: metadata.platform,\n releaseId: metadata.releaseId,\n });\n if (!verified || JSON.stringify(verified) !== JSON.stringify(record))\n throw new NativeReleaseRegistryError(\n \"Native release publication verification failed\",\n );\n }\n const channel = input.channel\n ? await promote({\n allowUnsigned: input.allowUnsigned,\n appId: metadata.appId,\n channel: input.channel,\n platform: metadata.platform,\n releaseId: metadata.releaseId,\n signal: input.signal,\n })\n : undefined;\n\n return { ...(channel ? { channel } : {}), record, reused };\n },\n read,\n resolve: async (input) => {\n if (!APP_ID_PATTERN.test(input.appId))\n throw new NativeReleaseRegistryError(\"Native release appId is invalid\");\n const key = channelKey(input.appId, input.platform, input.channel);\n const bytes = await options.store.get(key);\n if (!bytes) return null;\n const channel = parseChannel(decodedJson(bytes));\n if (\n channel.appId !== input.appId ||\n channel.platform !== input.platform ||\n channel.channel !== input.channel\n )\n throw new NativeReleaseRegistryError(\n \"Stored native release channel identity does not match\",\n );\n const record = await read({\n appId: channel.appId,\n platform: channel.platform,\n releaseId: channel.releaseId,\n });\n if (!record || record.metadata.sha256 !== channel.sha256)\n throw new NativeReleaseRegistryError(\n \"Native release channel points to a missing or invalid release\",\n );\n\n return { channel, record };\n },\n };\n};\n",
|
|
6
7
|
"/** Provider-neutral lifecycle for a global ingress in front of regional edge pools. */\n\nexport type EdgeIngressProtocol = \"tcp\" | \"http\" | \"https\";\n\nexport type EdgeIngressBackend = {\n /** Provider-native regional pool id (for example a DO regional LB UUID or GCP group self-link). */\n resourceId: string;\n region: string;\n /** Lower values are preferred during regional failover. */\n priority?: number;\n};\n\nexport type EdgeIngressSpec = {\n backends: readonly EdgeIngressBackend[];\n healthCheck: {\n path?: string;\n port: number;\n protocol: EdgeIngressProtocol;\n };\n idempotencyKey: string;\n listener: {\n port: number;\n protocol: EdgeIngressProtocol;\n targetPort: number;\n tlsPassthrough?: boolean;\n };\n name: string;\n};\n\nexport type EdgeIngressState = \"provisioning\" | \"ready\" | \"degraded\";\n\nexport type EdgeIngress = {\n addresses: string[];\n backends: EdgeIngressBackend[];\n id: string;\n name: string;\n provider: string;\n state: EdgeIngressState;\n};\n\nexport type EdgeIngressCapabilities = {\n automaticHealthFailover: boolean;\n global: boolean;\n tlsPassthrough: boolean;\n};\n\nexport type EdgeIngressProvider = {\n capabilities: EdgeIngressCapabilities;\n getIngress: (name: string) => Promise<EdgeIngress | null>;\n name: string;\n reconcileIngress: (spec: EdgeIngressSpec) => Promise<EdgeIngress>;\n removeIngress: (name: string, idempotencyKey: string) => Promise<void>;\n};\n\nconst validPort = (port: number) =>\n Number.isInteger(port) && port >= 1 && port <= 65_535;\n\nexport class EdgeIngressValidationError extends Error {}\n\nexport const validateEdgeIngressSpec = (spec: EdgeIngressSpec) => {\n if (!/^[a-z]([-a-z0-9]*[a-z0-9])?$/.test(spec.name))\n throw new EdgeIngressValidationError(\"Invalid edge ingress name\");\n if (spec.backends.length === 0)\n throw new EdgeIngressValidationError(\n \"Edge ingress requires at least one regional backend\",\n );\n if (!validPort(spec.listener.port) || !validPort(spec.listener.targetPort))\n throw new EdgeIngressValidationError(\"Invalid edge ingress listener port\");\n if (!validPort(spec.healthCheck.port))\n throw new EdgeIngressValidationError(\"Invalid edge ingress health port\");\n const resources = new Set<string>();\n for (const backend of spec.backends) {\n if (!backend.resourceId || !backend.region)\n throw new EdgeIngressValidationError(\"Invalid edge ingress backend\");\n if (resources.has(backend.resourceId))\n throw new EdgeIngressValidationError(\"Duplicate edge ingress backend\");\n resources.add(backend.resourceId);\n }\n if (\n spec.listener.tlsPassthrough &&\n spec.listener.protocol !== \"https\" &&\n spec.listener.protocol !== \"tcp\"\n )\n throw new EdgeIngressValidationError(\n \"TLS passthrough requires an HTTPS or TCP listener\",\n );\n\n return spec;\n};\n\nexport const normalizedEdgeIngressBackends = (\n backends: readonly EdgeIngressBackend[],\n) =>\n [...backends].sort(\n (left, right) =>\n (left.priority ?? Number.MAX_SAFE_INTEGER) -\n (right.priority ?? Number.MAX_SAFE_INTEGER) ||\n left.region.localeCompare(right.region) ||\n left.resourceId.localeCompare(right.resourceId),\n );\n",
|
|
7
8
|
"/**\n * Target interface + bundled adapters (localTarget, sshTarget).\n *\n * A Target is the narrowest abstraction over \"a place I can deploy to\":\n *\n * - `exec(cmd, opts?)` — run a shell command, capture stdout/stderr/exitCode.\n * - `upload(localPath, remotePath, opts?)` — copy a local file or directory\n * to the target. Implementation is free to use whatever is fast (rsync,\n * scp, mv).\n * - `close?()` — optional teardown.\n *\n * Two adapters are bundled:\n *\n * - `localTarget` runs in a temp directory on the local filesystem. Useful\n * for tests and for \"deploy\" workflows that happen on the same host.\n * - `sshTarget` shells out to the system `ssh` and `rsync` binaries. No\n * `ssh2` npm dependency — the controller machine just needs `ssh` and\n * (optionally) `rsync` in PATH, which is universal on Mac/Linux/WSL.\n *\n * Provider-specific targets (Cloudflare Workers HTTP API, Fly Machines API,\n * AWS Fargate) don't fit \"exec + upload\" and ship as siblings later.\n */\n\nimport { mkdir } from 'node:fs/promises';\nimport { join } from 'node:path';\n\nexport type ExecOptions = {\n\t/** Working directory on the target. Default: target's root. */\n\tcwd?: string;\n\t/** Env vars to set for this command (merged onto target.env). */\n\tenv?: Record<string, string>;\n\t/** Hard kill after this many ms. Default 600_000 (10 min). 0 disables. */\n\ttimeoutMs?: number;\n\t/** Pipe stdout/stderr through here as it streams (lines, newline-stripped). */\n\tonLog?: (line: string, stream: 'stdout' | 'stderr') => void;\n\t/** Stdin payload — a string is written verbatim. */\n\tstdin?: string;\n};\n\nexport type ExecResult = {\n\tstdout: string;\n\tstderr: string;\n\texitCode: number;\n};\n\nexport type UploadOptions = {\n\t/** Exclude paths matching these globs from a directory upload. */\n\texclude?: string[];\n\t/** When uploading a directory, delete remote files not present locally. */\n\tdeleteOrphans?: boolean;\n};\n\nexport type Target = {\n\t/** Human-readable description (e.g. \"ssh root@droplet-1.example.com\"). */\n\treadonly description: string;\n\texec: (cmd: string, opts?: ExecOptions) => Promise<ExecResult>;\n\tupload: (localPath: string, remotePath: string, opts?: UploadOptions) => Promise<void>;\n\tclose?: () => Promise<void>;\n};\n\n// -----------------------------------------------------------------------------\n// localTarget\n// -----------------------------------------------------------------------------\n\nexport type LocalTargetOptions = {\n\t/** Root directory the target operates in. Created if missing. */\n\troot: string;\n\t/** Env merged into every exec. */\n\tenv?: Record<string, string>;\n};\n\nconst decodeChunks = async (\n\treader: ReadableStream<Uint8Array> | null,\n\tonLine: ((line: string) => void) | undefined,\n): Promise<string> => {\n\tif (!reader) return '';\n\tconst decoder = new TextDecoder();\n\tlet buffer = '';\n\tlet collected = '';\n\tconst stream = reader.getReader();\n\ttry {\n\t\twhile (true) {\n\t\t\tconst { done, value } = await stream.read();\n\t\t\tif (done) break;\n\t\t\tconst chunk = decoder.decode(value, { stream: true });\n\t\t\tcollected += chunk;\n\t\t\tif (!onLine) continue;\n\t\t\tbuffer += chunk;\n\t\t\tlet newline = buffer.indexOf('\\n');\n\t\t\twhile (newline !== -1) {\n\t\t\t\tconst line = buffer.slice(0, newline).replace(/\\r$/, '');\n\t\t\t\tif (line.length > 0) onLine(line);\n\t\t\t\tbuffer = buffer.slice(newline + 1);\n\t\t\t\tnewline = buffer.indexOf('\\n');\n\t\t\t}\n\t\t}\n\t\tconst tail = decoder.decode();\n\t\tcollected += tail;\n\t\tif (onLine && (buffer + tail).length > 0) onLine((buffer + tail).replace(/\\r$/, ''));\n\t} finally {\n\t\tstream.releaseLock();\n\t}\n\treturn collected;\n};\n\nconst runSpawn = async (\n\targv: string[],\n\toptions: {\n\t\tcwd?: string;\n\t\tenv?: Record<string, string>;\n\t\ttimeoutMs?: number;\n\t\tonLog?: ExecOptions['onLog'];\n\t\tstdin?: string;\n\t},\n): Promise<ExecResult> => {\n\tconst proc = Bun.spawn(argv, {\n\t\tcwd: options.cwd,\n\t\tenv: options.env,\n\t\tstderr: 'pipe',\n\t\tstdin: options.stdin === undefined ? 'ignore' : 'pipe',\n\t\tstdout: 'pipe',\n\t});\n\n\tif (options.stdin !== undefined && proc.stdin) {\n\t\t// Bun.spawn returns a FileSink for piped stdin — `write` + `end`, not a\n\t\t// WritableStream. (We use a permissive cast because @types/bun's\n\t\t// Subprocess.stdin discriminant flips based on the stdin generic.)\n\t\tconst sink = proc.stdin as unknown as {\n\t\t\twrite: (chunk: string | Uint8Array) => number | Promise<number>;\n\t\t\tend: () => void | Promise<void>;\n\t\t};\n\t\tconst wrote = sink.write(options.stdin);\n\t\tif (wrote && typeof (wrote as Promise<number>).then === 'function') {\n\t\t\tawait wrote;\n\t\t}\n\t\tconst ended = sink.end();\n\t\tif (ended && typeof (ended as Promise<void>).then === 'function') {\n\t\t\tawait ended;\n\t\t}\n\t}\n\n\tconst timeout = options.timeoutMs ?? 600_000;\n\tlet timer: ReturnType<typeof setTimeout> | undefined;\n\tif (timeout > 0) {\n\t\ttimer = setTimeout(() => {\n\t\t\ttry { proc.kill(); } catch { /* already gone */ }\n\t\t}, timeout);\n\t}\n\n\tconst stdoutPromise = decodeChunks(\n\t\tproc.stdout as unknown as ReadableStream<Uint8Array>,\n\t\toptions.onLog ? (line) => options.onLog!(line, 'stdout') : undefined,\n\t);\n\tconst stderrPromise = decodeChunks(\n\t\tproc.stderr as unknown as ReadableStream<Uint8Array>,\n\t\toptions.onLog ? (line) => options.onLog!(line, 'stderr') : undefined,\n\t);\n\n\tconst [stdout, stderr, exitCode] = await Promise.all([\n\t\tstdoutPromise,\n\t\tstderrPromise,\n\t\tproc.exited,\n\t]);\n\tif (timer) clearTimeout(timer);\n\n\treturn { exitCode: exitCode ?? -1, stderr, stdout };\n};\n\nexport const localTarget = (options: LocalTargetOptions): Target => {\n\tconst baseEnv = { ...options.env };\n\tconst ensureRoot = async () => { await mkdir(options.root, { recursive: true }); };\n\n\treturn {\n\t\tdescription: `local ${options.root}`,\n\t\texec: async (cmd, opts) => {\n\t\t\tawait ensureRoot();\n\t\t\treturn runSpawn(['sh', '-c', cmd], {\n\t\t\t\tcwd: opts?.cwd ?? options.root,\n\t\t\t\tenv: { ...process.env, ...baseEnv, ...(opts?.env ?? {}) } as Record<string, string>,\n\t\t\t\tonLog: opts?.onLog,\n\t\t\t\tstdin: opts?.stdin,\n\t\t\t\ttimeoutMs: opts?.timeoutMs,\n\t\t\t});\n\t\t},\n\t\tupload: async (localPath, remotePath, opts) => {\n\t\t\tawait ensureRoot();\n\t\t\tconst dest = remotePath.startsWith('/') ? remotePath : join(options.root, remotePath);\n\t\t\tconst argv = ['rsync', '-a'];\n\t\t\tif (opts?.deleteOrphans) argv.push('--delete');\n\t\t\tfor (const pattern of opts?.exclude ?? []) argv.push('--exclude', pattern);\n\t\t\t// rsync semantics: a trailing slash on the source copies *contents*; without it the dir itself is nested.\n\t\t\targv.push(localPath, dest);\n\t\t\tconst result = await runSpawn(argv, { timeoutMs: 600_000 });\n\t\t\tif (result.exitCode !== 0) {\n\t\t\t\tthrow new Error(`local upload failed (exit ${result.exitCode}): ${result.stderr || result.stdout}`);\n\t\t\t}\n\t\t},\n\t};\n};\n\n// -----------------------------------------------------------------------------\n// sshTarget\n// -----------------------------------------------------------------------------\n\nexport type SshTargetOptions = {\n\t/** Hostname or IP of the remote. */\n\thost: string;\n\t/** Login user. Default `root`. */\n\tuser?: string;\n\t/** SSH port. Default 22. */\n\tport?: number;\n\t/** Path to SSH identity file. Default: ssh's own search. */\n\tidentity?: string;\n\t/** Extra flags appended to every `ssh` invocation. */\n\tsshFlags?: string[];\n\t/**\n\t * Use rsync for `upload`. Default true. When false, falls back to `scp`\n\t * which is universal but doesn't support delete / exclude.\n\t */\n\trsync?: boolean;\n\t/**\n\t * Env vars to forward via `ssh -o SendEnv=...`. Most remote sshd configs\n\t * accept only `LANG` and `LC_*` by default; for app env vars use the\n\t * step `env` option instead, which prepends `KEY=value` to the command.\n\t */\n\tforwardEnv?: string[];\n};\n\nconst sshTargetString = (options: SshTargetOptions): string => {\n\tconst user = options.user ?? 'root';\n\treturn `${user}@${options.host}`;\n};\n\nconst sshBaseFlags = (options: SshTargetOptions): string[] => {\n\tconst flags: string[] = [];\n\tif (options.port !== undefined && options.port !== 22) flags.push('-p', String(options.port));\n\tif (options.identity !== undefined) flags.push('-i', options.identity);\n\t// Never get stuck on a host-key prompt; treat unknown hosts as a fatal config issue rather than a UX detour.\n\tflags.push('-o', 'BatchMode=yes', '-o', 'StrictHostKeyChecking=accept-new');\n\tfor (const flag of options.sshFlags ?? []) flags.push(flag);\n\treturn flags;\n};\n\nconst shellQuote = (value: string): string => `'${value.replace(/'/g, `'\\\\''`)}'`;\n\nconst buildRemoteCmd = (cmd: string, opts: ExecOptions | undefined): string => {\n\tconst env = opts?.env;\n\tconst envPrefix = env\n\t\t? Object.entries(env).map(([k, v]) => `${k}=${shellQuote(v)}`).join(' ') + ' '\n\t\t: '';\n\tif (opts?.cwd) {\n\t\treturn `cd ${shellQuote(opts.cwd)} && ${envPrefix}${cmd}`;\n\t}\n\treturn `${envPrefix}${cmd}`;\n};\n\nexport const sshTarget = (options: SshTargetOptions): Target => {\n\tconst remote = sshTargetString(options);\n\tconst useRsync = options.rsync ?? true;\n\n\treturn {\n\t\tdescription: `ssh ${remote}${options.port && options.port !== 22 ? `:${options.port}` : ''}`,\n\t\texec: async (cmd, opts) => {\n\t\t\tconst argv = ['ssh', ...sshBaseFlags(options)];\n\t\t\tfor (const name of options.forwardEnv ?? []) argv.push('-o', `SendEnv=${name}`);\n\t\t\targv.push(remote, buildRemoteCmd(cmd, opts));\n\t\t\treturn runSpawn(argv, {\n\t\t\t\tonLog: opts?.onLog,\n\t\t\t\tstdin: opts?.stdin,\n\t\t\t\ttimeoutMs: opts?.timeoutMs,\n\t\t\t});\n\t\t},\n\t\tupload: async (localPath, remotePath, opts) => {\n\t\t\tif (useRsync) {\n\t\t\t\tconst sshCmd = ['ssh', ...sshBaseFlags(options)].map((part) => part.includes(' ') ? `'${part}'` : part).join(' ');\n\t\t\t\tconst argv = ['rsync', '-az', '-e', sshCmd];\n\t\t\t\tif (opts?.deleteOrphans) argv.push('--delete');\n\t\t\t\tfor (const pattern of opts?.exclude ?? []) argv.push('--exclude', pattern);\n\t\t\t\targv.push(localPath, `${remote}:${remotePath}`);\n\t\t\t\tconst result = await runSpawn(argv, { timeoutMs: 600_000 });\n\t\t\t\tif (result.exitCode !== 0) {\n\t\t\t\t\tthrow new Error(`rsync upload failed (exit ${result.exitCode}): ${result.stderr || result.stdout}`);\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t// scp fallback — no exclude, no delete. We still need -r to copy directories.\n\t\t\tconst argv = ['scp', '-r', ...sshBaseFlags(options), localPath, `${remote}:${remotePath}`];\n\t\t\tconst result = await runSpawn(argv, { timeoutMs: 600_000 });\n\t\t\tif (result.exitCode !== 0) {\n\t\t\t\tthrow new Error(`scp upload failed (exit ${result.exitCode}): ${result.stderr || result.stdout}`);\n\t\t\t}\n\t\t},\n\t};\n};\n",
|
|
8
9
|
"/**\n * ProcessManager — the abstraction that turns \"files are on the target\" into\n * \"the app is running.\" Two strategies ship: `bareManager` (nohup, lowest\n * dependency) and `systemdManager` (templated unit file, the way production\n * VMs should run).\n *\n * Callers can supply their own — anything that implements `start` / `stop` /\n * `reload` / `status` against a `Target` works. PM2, supervisord, runit,\n * @absolutejs/runtime all fit if someone writes the adapter.\n */\n\nimport type { Target } from './targets';\n\nexport type ProcessManagerContext = {\n\t/** Absolute path on the target to the active release dir (the symlink target). */\n\tcurrentPath: string;\n\t/** Absolute path on the target to the new release dir we just uploaded. */\n\treleasePath: string;\n\t/** Release id (timestamped). */\n\treleaseId: string;\n\t/** App name — supplied via deployer config; used for unit names, pid files, etc. */\n\tappName: string;\n\t/** Optional env to set on the process. */\n\tenv: Record<string, string>;\n\t/** Log sink for any commands the manager runs. */\n\tonLog?: (line: string, stream: 'stdout' | 'stderr') => void;\n};\n\nexport type ProcessManager = {\n\t/** Bring the new release up. Called after the `current` symlink has been swapped. */\n\treload: (target: Target, ctx: ProcessManagerContext) => Promise<void>;\n\t/** Stop the running process. */\n\tstop?: (target: Target, ctx: ProcessManagerContext) => Promise<void>;\n\t/** Return current status (best-effort; used by callers for diagnostics). */\n\tstatus?: (target: Target, ctx: ProcessManagerContext) => Promise<'running' | 'stopped' | 'unknown'>;\n};\n\n// -----------------------------------------------------------------------------\n// bareManager — nohup background + pid file\n// -----------------------------------------------------------------------------\n\nexport type BareManagerOptions = {\n\t/** Command to run. Default `bun run start`. */\n\tcommand?: string;\n\t/** Log files inside the app's data dir (default: alongside pid). */\n\tlogFileBaseName?: string;\n};\n\nconst pidPath = (appName: string) => `/var/lib/${appName}/${appName}.pid`;\nconst logDir = (appName: string) => `/var/log/${appName}`;\n\nexport const bareManager = (options: BareManagerOptions = {}): ProcessManager => {\n\tconst command = options.command ?? 'bun run start';\n\tconst logBase = options.logFileBaseName ?? 'app';\n\n\tconst envPrefix = (env: Record<string, string>) =>\n\t\tObject.entries(env).map(([k, v]) => `${k}='${v.replace(/'/g, `'\\\\''`)}'`).join(' ');\n\n\tconst startCmd = (ctx: ProcessManagerContext): string => {\n\t\tconst env = envPrefix(ctx.env);\n\t\tconst pid = pidPath(ctx.appName);\n\t\tconst out = `${logDir(ctx.appName)}/${logBase}.out.log`;\n\t\tconst err = `${logDir(ctx.appName)}/${logBase}.err.log`;\n\t\treturn `\nmkdir -p $(dirname ${pid}) ${logDir(ctx.appName)} &&\ncd ${ctx.currentPath} &&\nnohup env ${env} sh -c '${command.replace(/'/g, `'\\\\''`)}' >> ${out} 2>> ${err} &\necho $! > ${pid}\n`.trim();\n\t};\n\n\tconst stopCmd = (ctx: ProcessManagerContext): string => `\nPID=$(cat ${pidPath(ctx.appName)} 2>/dev/null || true);\nif [ -n \"$PID\" ] && kill -0 \"$PID\" 2>/dev/null; then\n kill \"$PID\" 2>/dev/null || true;\n for i in 1 2 3 4 5; do\n if ! kill -0 \"$PID\" 2>/dev/null; then break; fi;\n sleep 1;\n done;\n kill -9 \"$PID\" 2>/dev/null || true;\nfi\nrm -f ${pidPath(ctx.appName)}\n`.trim();\n\n\treturn {\n\t\treload: async (target, ctx) => {\n\t\t\tconst stop = await target.exec(stopCmd(ctx), { onLog: ctx.onLog, timeoutMs: 30_000 });\n\t\t\tif (stop.exitCode !== 0) {\n\t\t\t\tthrow new Error(`bareManager.stop failed (exit ${stop.exitCode}): ${stop.stderr}`);\n\t\t\t}\n\t\t\tconst start = await target.exec(startCmd(ctx), { onLog: ctx.onLog, timeoutMs: 30_000 });\n\t\t\tif (start.exitCode !== 0) {\n\t\t\t\tthrow new Error(`bareManager.start failed (exit ${start.exitCode}): ${start.stderr}`);\n\t\t\t}\n\t\t},\n\t\tstatus: async (target, ctx) => {\n\t\t\tconst result = await target.exec(\n\t\t\t\t`PID=$(cat ${pidPath(ctx.appName)} 2>/dev/null || true); if [ -n \"$PID\" ] && kill -0 \"$PID\" 2>/dev/null; then echo running; else echo stopped; fi`,\n\t\t\t\t{ timeoutMs: 5_000 },\n\t\t\t);\n\t\t\tconst out = result.stdout.trim();\n\t\t\tif (out === 'running') return 'running';\n\t\t\tif (out === 'stopped') return 'stopped';\n\t\t\treturn 'unknown';\n\t\t},\n\t\tstop: async (target, ctx) => {\n\t\t\tconst result = await target.exec(stopCmd(ctx), { onLog: ctx.onLog, timeoutMs: 30_000 });\n\t\t\tif (result.exitCode !== 0) {\n\t\t\t\tthrow new Error(`bareManager.stop failed (exit ${result.exitCode}): ${result.stderr}`);\n\t\t\t}\n\t\t},\n\t};\n};\n\n// -----------------------------------------------------------------------------\n// systemdManager — generates a unit file pointing at current/, restarts via systemctl\n// -----------------------------------------------------------------------------\n\nexport type SystemdManagerOptions = {\n\t/** Unit file name (defaults to `${appName}.service`). */\n\tunitName?: string;\n\t/** ExecStart command. Default `/usr/local/bin/bun run start`. */\n\texecStart?: string;\n\t/** User to run as. Default the deploy user. */\n\tuser?: string;\n\t/** Group. Default the deploy user. */\n\tgroup?: string;\n\t/** Restart policy. Default `always`. */\n\trestart?: 'always' | 'on-failure' | 'no';\n\t/** systemctl path. Default `systemctl`. */\n\tsystemctl?: string;\n\t/** Unit file directory. Default `/etc/systemd/system`. */\n\tunitDir?: string;\n};\n\nconst renderSystemdUnit = (\n\tctx: ProcessManagerContext,\n\toptions: SystemdManagerOptions,\n): string => {\n\tconst user = options.user ?? 'deploy';\n\tconst group = options.group ?? user;\n\tconst execStart = options.execStart ?? '/usr/local/bin/bun run start';\n\tconst restart = options.restart ?? 'always';\n\tconst envLines = Object.entries(ctx.env)\n\t\t.map(([k, v]) => `Environment=${k}=${v.replace(/\"/g, '\\\\\"')}`)\n\t\t.join('\\n');\n\treturn `[Unit]\nDescription=${ctx.appName} (managed by @absolutejs/deploy)\nAfter=network.target\n\n[Service]\nType=simple\nWorkingDirectory=${ctx.currentPath}\nExecStart=${execStart}\nRestart=${restart}\nRestartSec=2\nUser=${user}\nGroup=${group}\n${envLines}\nStandardOutput=append:/var/log/${ctx.appName}/app.out.log\nStandardError=append:/var/log/${ctx.appName}/app.err.log\n\n[Install]\nWantedBy=multi-user.target\n`;\n};\n\nexport const systemdManager = (options: SystemdManagerOptions = {}): ProcessManager => {\n\tconst systemctl = options.systemctl ?? 'systemctl';\n\tconst unitDir = options.unitDir ?? '/etc/systemd/system';\n\tconst unitName = (ctx: ProcessManagerContext) => options.unitName ?? `${ctx.appName}.service`;\n\n\treturn {\n\t\treload: async (target, ctx) => {\n\t\t\tconst unit = renderSystemdUnit(ctx, options);\n\t\t\tconst name = unitName(ctx);\n\t\t\t// Write the unit via a heredoc executed by the remote shell. tee/cat work but\n\t\t\t// stdin is the cleanest path that doesn't reveal unit text in `ps`.\n\t\t\tconst writeUnit = await target.exec(\n\t\t\t\t`mkdir -p /var/log/${ctx.appName} && cat > ${unitDir}/${name}`,\n\t\t\t\t{ onLog: ctx.onLog, stdin: unit, timeoutMs: 30_000 },\n\t\t\t);\n\t\t\tif (writeUnit.exitCode !== 0) {\n\t\t\t\tthrow new Error(`systemdManager: writing unit failed (exit ${writeUnit.exitCode}): ${writeUnit.stderr}`);\n\t\t\t}\n\t\t\tconst reload = await target.exec(`${systemctl} daemon-reload`, { onLog: ctx.onLog, timeoutMs: 15_000 });\n\t\t\tif (reload.exitCode !== 0) {\n\t\t\t\tthrow new Error(`systemdManager: daemon-reload failed (exit ${reload.exitCode}): ${reload.stderr}`);\n\t\t\t}\n\t\t\tconst enable = await target.exec(`${systemctl} enable ${name}`, { onLog: ctx.onLog, timeoutMs: 15_000 });\n\t\t\tif (enable.exitCode !== 0) {\n\t\t\t\tthrow new Error(`systemdManager: enable failed (exit ${enable.exitCode}): ${enable.stderr}`);\n\t\t\t}\n\t\t\tconst restart = await target.exec(`${systemctl} restart ${name}`, { onLog: ctx.onLog, timeoutMs: 60_000 });\n\t\t\tif (restart.exitCode !== 0) {\n\t\t\t\tthrow new Error(`systemdManager: restart failed (exit ${restart.exitCode}): ${restart.stderr}`);\n\t\t\t}\n\t\t},\n\t\tstatus: async (target, ctx) => {\n\t\t\tconst result = await target.exec(`${systemctl} is-active ${unitName(ctx)} || true`, { timeoutMs: 10_000 });\n\t\t\tconst out = result.stdout.trim();\n\t\t\tif (out === 'active') return 'running';\n\t\t\tif (out === 'inactive' || out === 'failed') return 'stopped';\n\t\t\treturn 'unknown';\n\t\t},\n\t\tstop: async (target, ctx) => {\n\t\t\tconst result = await target.exec(`${systemctl} stop ${unitName(ctx)}`, { onLog: ctx.onLog, timeoutMs: 30_000 });\n\t\t\tif (result.exitCode !== 0) {\n\t\t\t\tthrow new Error(`systemdManager: stop failed (exit ${result.exitCode}): ${result.stderr}`);\n\t\t\t}\n\t\t},\n\t};\n};\n",
|
|
9
10
|
"/**\n * createDeployer — drives a step pipeline against a Target.\n *\n * The default pipeline (`defaultBunPipeline`) is the right thing for a Bun\n * project on a Linux host: prepare → upload → install → build → link →\n * restart → verify. Callers can replace steps wholesale or splice their own\n * in via `steps: [...]`.\n *\n * Release model: every `deploy()` creates a fresh timestamped directory\n * under `<root>/releases/`, uploads into it, then atomically swaps the\n * `<root>/current` symlink. `rollback(id)` re-points the symlink and\n * reloads the process manager — no re-upload, no re-build, just a fast\n * switch.\n */\n\nimport type { ProcessManager, ProcessManagerContext } from './processManagers';\nimport { bareManager } from './processManagers';\nimport type { ExecResult, Target } from './targets';\n\nexport type Source = {\n\t/** Local directory to copy. */\n\tkind: 'directory';\n\troot: string;\n\t/** Globs excluded from upload. Defaults to common dev artifacts. */\n\texclude?: string[];\n};\n\nexport type VerifySpec =\n\t| { kind: 'http'; url: string; retries?: number; intervalMs?: number; expectStatus?: number }\n\t| { kind: 'tcp'; host: string; port: number; retries?: number; intervalMs?: number }\n\t| { kind: 'custom'; check: (ctx: DeployContext) => Promise<boolean> };\n\nexport type ReleaseAnnotations = {\n\t/** Git commit SHA being deployed (40-char hex; truncated forms accepted). */\n\tcommitSha?: string;\n\t/** Git ref (e.g. `refs/heads/main`, `v1.2.3`). */\n\tref?: string;\n\t/** Commit message (or any human-readable description). */\n\tmessage?: string;\n\t/** Committer / deployer identity. */\n\tauthor?: string;\n\t/** Arbitrary tags for downstream filtering (status pages, audits). */\n\ttags?: Record<string, string>;\n};\n\nexport type DeployContext = {\n\ttarget: Target;\n\tsource: Source;\n\treleaseId: string;\n\treleasePath: string;\n\tcurrentPath: string;\n\tappName: string;\n\tenv: Record<string, string>;\n\thooks: ResolvedHooks;\n\tprocessManager: ProcessManager;\n\tverify: VerifySpec | null;\n\tannotations: ReleaseAnnotations;\n\t/** Optional cancellation signal shared with custom steps and verify hooks. */\n\tsignal?: AbortSignal;\n\t/** When `true`, steps log what they WOULD do via hooks.onLog and do not mutate the target. */\n\tdryRun: boolean;\n};\n\nexport type DeployStep = {\n\tname: string;\n\trun: (ctx: DeployContext) => Promise<void>;\n};\n\nexport type DeployHooks = {\n\tonStepStart?: (step: { name: string; releaseId: string }) => void | Promise<void>;\n\tonStepEnd?: (step: { name: string; releaseId: string; durationMs: number }) => void | Promise<void>;\n\tonLog?: (line: string, stream: 'stdout' | 'stderr', step: string) => void;\n\tonError?: (error: { step: string; releaseId: string; error: Error }) => void | Promise<void>;\n};\n\ntype ResolvedHooks = Required<{\n\t[K in keyof DeployHooks]: NonNullable<DeployHooks[K]>;\n}>;\n\nexport type DeployOptions = {\n\t/** Cancel before the next pipeline step; custom steps may observe it directly. */\n\tsignal?: AbortSignal;\n\t/** Per-release annotations stored alongside the release dir as `.deploy-meta.json`. */\n\tannotations?: ReleaseAnnotations;\n\t/**\n\t * When `true`, the deploy plan is logged but no mutation happens on the\n\t * target — steps still call `target.exec` only via `cmd === 'echo'`-style\n\t * dry-run probes. Use this from `gh actions` to verify pipeline shape\n\t * before flipping a real `current` symlink.\n\t */\n\tdryRun?: boolean;\n\t/**\n\t * Resume a previously-failed release. The deployer reads the release's\n\t * `.deploy-meta.json`, finds the step that died, and starts from there.\n\t * Steps that completed successfully are skipped. Use this when a deploy\n\t * fails on `verify` (e.g. health-check timeout) but the release is\n\t * otherwise intact on disk.\n\t */\n\tresumeReleaseId?: string;\n};\n\nexport type DeployerOptions = {\n\ttarget: Target;\n\tsource: Source;\n\t/** App name; used by ProcessManagers for unit names, pid files, log paths. Required. */\n\tappName: string;\n\t/** Where deploys live on the target. Default `/srv/<appName>`. */\n\trootPath?: string;\n\t/** Steps in order. Default: `defaultBunPipeline()`. */\n\tsteps?: DeployStep[];\n\t/** Env merged into install / build / start. */\n\tenv?: Record<string, string>;\n\t/** Process manager. Default `bareManager()`. */\n\tprocessManager?: ProcessManager;\n\t/** How to verify the deploy is up. Default null (skip verify). */\n\tverify?: VerifySpec | null;\n\thooks?: DeployHooks;\n\t/** Override `Date.now` for deterministic release ids in tests. */\n\tclock?: () => number;\n};\n\nexport type ReleaseRecord = {\n\treleaseId: string;\n\tannotations: ReleaseAnnotations;\n\tstatus: 'in-progress' | 'completed' | 'failed';\n\tfailedStep?: string;\n\tcompletedSteps: string[];\n\tstartedAt: number;\n\tendedAt?: number;\n};\n\nexport type DeployResult = {\n\treleaseId: string;\n\treleasePath: string;\n\tcurrentPath: string;\n\tdurationMs: number;\n\tsteps: { name: string; durationMs: number; skipped?: boolean }[];\n\tannotations: ReleaseAnnotations;\n};\n\nexport type Deployer = {\n\tdeploy: (options?: DeployOptions) => Promise<DeployResult>;\n\trollback: (releaseId: string) => Promise<DeployResult>;\n\t/** Stop the active release through the configured process manager. */\n\tstop: () => Promise<void>;\n\t/** Best-effort active process status from the configured process manager. */\n\tstatus: () => Promise<'running' | 'stopped' | 'unknown'>;\n\tlistReleases: () => Promise<string[]>;\n\t/** Read the deploy meta for a specific release (or null if missing). */\n\treadReleaseMeta: (releaseId: string) => Promise<ReleaseRecord | null>;\n\tprune: (options: { keep: number }) => Promise<{ removed: string[] }>;\n\tdispose: () => Promise<void>;\n};\n\nconst DEFAULT_EXCLUDES = ['node_modules', 'dist', 'build', '.git', '.DS_Store', '*.log'];\n\nconst noopHooks: ResolvedHooks = {\n\tonError: () => {},\n\tonLog: () => {},\n\tonStepEnd: () => {},\n\tonStepStart: () => {},\n};\n\nconst resolveHooks = (hooks?: DeployHooks): ResolvedHooks => ({\n\tonError: hooks?.onError ?? noopHooks.onError,\n\tonLog: hooks?.onLog ?? noopHooks.onLog,\n\tonStepEnd: hooks?.onStepEnd ?? noopHooks.onStepEnd,\n\tonStepStart: hooks?.onStepStart ?? noopHooks.onStepStart,\n});\n\nconst makeReleaseId = (clock: () => number): string => {\n\tconst t = clock();\n\tconst date = new Date(t);\n\tconst pad = (n: number, w = 2) => n.toString().padStart(w, '0');\n\treturn `${date.getUTCFullYear()}${pad(date.getUTCMonth() + 1)}${pad(date.getUTCDate())}-${pad(date.getUTCHours())}${pad(date.getUTCMinutes())}${pad(date.getUTCSeconds())}`;\n};\n\nconst requireSuccess = (label: string, result: ExecResult) => {\n\tif (result.exitCode !== 0) {\n\t\tthrow new Error(`${label} failed (exit ${result.exitCode}): ${result.stderr || result.stdout || '(no output)'}`);\n\t}\n};\n\n// -----------------------------------------------------------------------------\n// Default Bun pipeline steps\n// -----------------------------------------------------------------------------\n\nexport const defaultBunPipeline = (): DeployStep[] => [\n\t{\n\t\tname: 'prepare',\n\t\trun: async (ctx) => {\n\t\t\tconst result = await ctx.target.exec(\n\t\t\t\t`mkdir -p ${ctx.releasePath}`,\n\t\t\t\t{ onLog: (line, stream) => ctx.hooks.onLog(line, stream, 'prepare') },\n\t\t\t);\n\t\t\trequireSuccess('prepare: mkdir', result);\n\t\t},\n\t},\n\t{\n\t\tname: 'upload',\n\t\trun: async (ctx) => {\n\t\t\tif (ctx.source.kind !== 'directory') {\n\t\t\t\tthrow new Error(`Unsupported source kind: ${(ctx.source as { kind: string }).kind}`);\n\t\t\t}\n\t\t\t// Trailing slash on source = copy contents, not the dir itself.\n\t\t\tconst localPath = ctx.source.root.endsWith('/') ? ctx.source.root : `${ctx.source.root}/`;\n\t\t\tawait ctx.target.upload(localPath, ctx.releasePath, {\n\t\t\t\texclude: ctx.source.exclude ?? DEFAULT_EXCLUDES,\n\t\t\t});\n\t\t},\n\t},\n\t{\n\t\tname: 'install',\n\t\trun: async (ctx) => {\n\t\t\tconst result = await ctx.target.exec(\n\t\t\t\t`bun install --production`,\n\t\t\t\t{\n\t\t\t\t\tcwd: ctx.releasePath,\n\t\t\t\t\tenv: ctx.env,\n\t\t\t\t\tonLog: (line, stream) => ctx.hooks.onLog(line, stream, 'install'),\n\t\t\t\t\ttimeoutMs: 600_000,\n\t\t\t\t},\n\t\t\t);\n\t\t\trequireSuccess('install', result);\n\t\t},\n\t},\n\t{\n\t\tname: 'build',\n\t\trun: async (ctx) => {\n\t\t\t// Run only if the project declares a `build` script. Detect by reading package.json on the remote.\n\t\t\tconst probe = await ctx.target.exec(\n\t\t\t\t`grep -E '\"build\"\\\\s*:' package.json || true`,\n\t\t\t\t{ cwd: ctx.releasePath, timeoutMs: 10_000 },\n\t\t\t);\n\t\t\tif (!probe.stdout.includes('\"build\"')) return;\n\t\t\tconst result = await ctx.target.exec(\n\t\t\t\t`bun run build`,\n\t\t\t\t{\n\t\t\t\t\tcwd: ctx.releasePath,\n\t\t\t\t\tenv: ctx.env,\n\t\t\t\t\tonLog: (line, stream) => ctx.hooks.onLog(line, stream, 'build'),\n\t\t\t\t\ttimeoutMs: 600_000,\n\t\t\t\t},\n\t\t\t);\n\t\t\trequireSuccess('build', result);\n\t\t},\n\t},\n\t{\n\t\tname: 'link',\n\t\trun: async (ctx) => {\n\t\t\t// Atomic-ish symlink swap: write a NEW symlink to a temp name, then rename onto current.\n\t\t\tconst tmpLink = `${ctx.currentPath}.next`;\n\t\t\tconst result = await ctx.target.exec(\n\t\t\t\t`ln -sfn ${ctx.releasePath} ${tmpLink} && mv -Tf ${tmpLink} ${ctx.currentPath}`,\n\t\t\t\t{ onLog: (line, stream) => ctx.hooks.onLog(line, stream, 'link'), timeoutMs: 10_000 },\n\t\t\t);\n\t\t\trequireSuccess('link', result);\n\t\t},\n\t},\n\t{\n\t\tname: 'restart',\n\t\trun: async (ctx) => {\n\t\t\tawait ctx.processManager.reload(ctx.target, {\n\t\t\t\tappName: ctx.appName,\n\t\t\t\tcurrentPath: ctx.currentPath,\n\t\t\t\tenv: ctx.env,\n\t\t\t\tonLog: (line, stream) => ctx.hooks.onLog(line, stream, 'restart'),\n\t\t\t\treleaseId: ctx.releaseId,\n\t\t\t\treleasePath: ctx.releasePath,\n\t\t\t});\n\t\t},\n\t},\n\t{\n\t\tname: 'verify',\n\t\trun: async (ctx) => {\n\t\t\tif (!ctx.verify) return;\n\t\t\tawait runVerify(ctx);\n\t\t},\n\t},\n];\n\nconst runVerify = async (ctx: DeployContext): Promise<void> => {\n\tconst spec = ctx.verify!;\n\tif (spec.kind === 'custom') {\n\t\tconst ok = await spec.check(ctx);\n\t\tif (!ok) throw new Error('verify: custom check returned false');\n\t\treturn;\n\t}\n\tif (spec.kind === 'http') {\n\t\tconst retries = spec.retries ?? 30;\n\t\tconst intervalMs = spec.intervalMs ?? 1_000;\n\t\tconst expectStatus = spec.expectStatus ?? 200;\n\t\tfor (let attempt = 0; attempt <= retries; attempt++) {\n\t\t\tconst probe = await ctx.target.exec(\n\t\t\t\t`curl -s -o /dev/null -w '%{http_code}' --max-time 5 ${spec.url}`,\n\t\t\t\t{ timeoutMs: 10_000 },\n\t\t\t);\n\t\t\tconst code = Number(probe.stdout.trim());\n\t\t\tif (code === expectStatus) return;\n\t\t\tif (attempt < retries) await new Promise((resolve) => setTimeout(resolve, intervalMs));\n\t\t}\n\t\tthrow new Error(`verify: HTTP ${spec.url} did not return ${expectStatus} after ${retries} retries`);\n\t}\n\t// tcp\n\tconst retries = spec.retries ?? 30;\n\tconst intervalMs = spec.intervalMs ?? 1_000;\n\tfor (let attempt = 0; attempt <= retries; attempt++) {\n\t\tconst probe = await ctx.target.exec(\n\t\t\t`bash -c 'cat < /dev/tcp/${spec.host}/${spec.port}' 2>/dev/null && echo open || echo closed`,\n\t\t\t{ timeoutMs: 10_000 },\n\t\t);\n\t\tif (probe.stdout.includes('open')) return;\n\t\tif (attempt < retries) await new Promise((resolve) => setTimeout(resolve, intervalMs));\n\t}\n\tthrow new Error(`verify: TCP ${spec.host}:${spec.port} not open after ${retries} retries`);\n};\n\n// -----------------------------------------------------------------------------\n// Deployer\n// -----------------------------------------------------------------------------\n\nexport const createDeployer = (options: DeployerOptions): Deployer => {\n\tconst clock = options.clock ?? Date.now;\n\tconst hooks = resolveHooks(options.hooks);\n\tconst rootPath = options.rootPath ?? `/srv/${options.appName}`;\n\tconst currentPath = `${rootPath}/current`;\n\tconst releasesPath = `${rootPath}/releases`;\n\tconst env: Record<string, string> = { NODE_ENV: 'production', ...options.env };\n\tconst processManager = options.processManager ?? bareManager();\n\tconst verify = options.verify === undefined ? null : options.verify;\n\tlet disposed = false;\n\n\tconst buildCtx = (\n\t\treleaseId: string,\n\t\topts: { annotations: ReleaseAnnotations; dryRun: boolean; signal?: AbortSignal },\n\t): DeployContext => ({\n\t\tannotations: opts.annotations,\n\t\tappName: options.appName,\n\t\tcurrentPath,\n\t\tdryRun: opts.dryRun,\n\t\tenv,\n\t\thooks,\n\t\tprocessManager,\n\t\tsignal: opts.signal,\n\t\treleaseId,\n\t\treleasePath: `${releasesPath}/${releaseId}`,\n\t\tsource: options.source,\n\t\ttarget: options.target,\n\t\tverify,\n\t});\n\n\tconst metaPath = (releaseId: string) => `${releasesPath}/${releaseId}/.deploy-meta.json`;\n\n\tconst writeMeta = async (releaseId: string, record: ReleaseRecord): Promise<void> => {\n\t\tconst json = JSON.stringify(record);\n\t\t// Use stdin to avoid quoting hassles + so the JSON doesn't appear in `ps`.\n\t\tconst result = await options.target.exec(`cat > ${metaPath(releaseId)}`, {\n\t\t\tstdin: json,\n\t\t\ttimeoutMs: 10_000,\n\t\t});\n\t\tif (result.exitCode !== 0) {\n\t\t\t// Non-fatal — the deploy doesn't depend on the meta file for success.\n\t\t\tconsole.warn(`[deploy] writeMeta(${releaseId}) failed: ${result.stderr || result.stdout}`);\n\t\t}\n\t};\n\n\tconst readMeta = async (releaseId: string): Promise<ReleaseRecord | null> => {\n\t\tconst result = await options.target.exec(`cat ${metaPath(releaseId)} 2>/dev/null || true`, {\n\t\t\ttimeoutMs: 10_000,\n\t\t});\n\t\tconst text = result.stdout.trim();\n\t\tif (text.length === 0) return null;\n\t\ttry {\n\t\t\treturn JSON.parse(text) as ReleaseRecord;\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t};\n\n\tconst cleanOrphanedSymlink = async (): Promise<void> => {\n\t\t// A prior deploy that crashed between `ln -sfn ... current.next` and\n\t\t// `mv -Tf current.next current` leaves `current.next` dangling. Clean\n\t\t// it so the next link step's `mv -Tf` is unambiguous.\n\t\tawait options.target.exec(`rm -f ${currentPath}.next`, { timeoutMs: 5_000 });\n\t};\n\n\tconst runSteps = async (\n\t\tsteps: DeployStep[],\n\t\treleaseId: string,\n\t\trunOpts: {\n\t\t\tannotations: ReleaseAnnotations;\n\t\t\tdryRun: boolean;\n\t\t\talreadyCompleted: string[];\n\t\t\tsignal?: AbortSignal;\n\t\t},\n\t): Promise<DeployResult> => {\n\t\tconst ctx = buildCtx(releaseId, {\n\t\t\tannotations: runOpts.annotations,\n\t\t\tdryRun: runOpts.dryRun,\n\t\t\tsignal: runOpts.signal,\n\t\t});\n\t\tconst stepDurations: { name: string; durationMs: number; skipped?: boolean }[] = [];\n\t\tconst startedAt = clock();\n\t\tconst completedSteps: string[] = [...runOpts.alreadyCompleted];\n\n\t\tconst record: ReleaseRecord = {\n\t\t\tannotations: runOpts.annotations,\n\t\t\tcompletedSteps,\n\t\t\treleaseId,\n\t\t\tstartedAt,\n\t\t\tstatus: 'in-progress',\n\t\t};\n\t\t// Write the meta-record as soon as the release directory exists, which\n\t\t// `prepare` sets up. Until then we have nowhere to put it.\n\n\t\tfor (const step of steps) {\n\t\t\tctx.signal?.throwIfAborted();\n\t\t\tif (completedSteps.includes(step.name) && step.name !== 'verify') {\n\t\t\t\t// Resume: skip steps already done. (Always re-run verify so a\n\t\t\t\t// healthy probe is recorded post-resume.)\n\t\t\t\tstepDurations.push({ durationMs: 0, name: step.name, skipped: true });\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tconst stepStartedAt = clock();\n\t\t\tawait hooks.onStepStart({ name: step.name, releaseId });\n\n\t\t\tif (runOpts.dryRun) {\n\t\t\t\thooks.onLog(`[dry-run] would run: ${step.name}`, 'stdout', step.name);\n\t\t\t\tstepDurations.push({ durationMs: 0, name: step.name, skipped: true });\n\t\t\t\tawait hooks.onStepEnd({ durationMs: 0, name: step.name, releaseId });\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tawait step.run(ctx);\n\t\t\t\tctx.signal?.throwIfAborted();\n\t\t\t} catch (error) {\n\t\t\t\tconst err = error instanceof Error ? error : new Error(String(error));\n\t\t\t\trecord.status = 'failed';\n\t\t\t\trecord.failedStep = step.name;\n\t\t\t\trecord.endedAt = clock();\n\t\t\t\t// Best-effort meta write so resume() works later.\n\t\t\t\tawait writeMeta(releaseId, record);\n\t\t\t\tawait hooks.onError({ error: err, releaseId, step: step.name });\n\t\t\t\tthrow err;\n\t\t\t}\n\n\t\t\tconst durationMs = clock() - stepStartedAt;\n\t\t\tstepDurations.push({ durationMs, name: step.name });\n\t\t\tcompletedSteps.push(step.name);\n\t\t\tawait hooks.onStepEnd({ durationMs, name: step.name, releaseId });\n\n\t\t\t// After `prepare` (the first step that creates the dir), persist meta.\n\t\t\tif (step.name === 'prepare') {\n\t\t\t\tawait writeMeta(releaseId, record);\n\t\t\t}\n\t\t}\n\n\t\trecord.status = 'completed';\n\t\trecord.endedAt = clock();\n\t\tif (!runOpts.dryRun) await writeMeta(releaseId, record);\n\n\t\treturn {\n\t\t\tannotations: runOpts.annotations,\n\t\t\tcurrentPath,\n\t\t\tdurationMs: clock() - startedAt,\n\t\t\treleaseId,\n\t\t\treleasePath: ctx.releasePath,\n\t\t\tsteps: stepDurations,\n\t\t};\n\t};\n\n\tconst ensureRoot = async () => {\n\t\tconst result = await options.target.exec(`mkdir -p ${releasesPath}`, { timeoutMs: 10_000 });\n\t\trequireSuccess('ensureRoot', result);\n\t};\n\tconst activeProcessContext = (): ProcessManagerContext => ({\n\t\tappName: options.appName,\n\t\tcurrentPath,\n\t\tenv,\n\t\treleaseId: 'current',\n\t\treleasePath: currentPath,\n\t});\n\n\treturn {\n\t\tdeploy: async (deployOpts: DeployOptions = {}) => {\n\t\t\tif (disposed) throw new Error('Deployer is disposed');\n\t\t\tawait ensureRoot();\n\t\t\tawait cleanOrphanedSymlink();\n\n\t\t\tconst annotations = deployOpts.annotations ?? {};\n\t\t\tconst dryRun = deployOpts.dryRun ?? false;\n\n\t\t\tif (deployOpts.resumeReleaseId !== undefined) {\n\t\t\t\tconst prior = await readMeta(deployOpts.resumeReleaseId);\n\t\t\t\tif (!prior) {\n\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t`resume: no .deploy-meta.json for release ${deployOpts.resumeReleaseId}`,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tif (prior.status === 'completed') {\n\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t`resume: release ${deployOpts.resumeReleaseId} already completed`,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\treturn runSteps(options.steps ?? defaultBunPipeline(), deployOpts.resumeReleaseId, {\n\t\t\t\t\talreadyCompleted: prior.completedSteps,\n\t\t\t\t\tannotations: prior.annotations ?? annotations,\n\t\t\t\t\tdryRun,\n\t\t\t\t\tsignal: deployOpts.signal,\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tconst releaseId = makeReleaseId(clock);\n\t\t\treturn runSteps(options.steps ?? defaultBunPipeline(), releaseId, {\n\t\t\t\talreadyCompleted: [],\n\t\t\t\tannotations,\n\t\t\t\tdryRun,\n\t\t\t\tsignal: deployOpts.signal,\n\t\t\t});\n\t\t},\n\t\tdispose: async () => {\n\t\t\tdisposed = true;\n\t\t\tif (options.target.close) await options.target.close();\n\t\t},\n\t\tlistReleases: async () => {\n\t\t\tawait ensureRoot();\n\t\t\tconst result = await options.target.exec(\n\t\t\t\t`ls -1 ${releasesPath} 2>/dev/null || true`,\n\t\t\t\t{ timeoutMs: 10_000 },\n\t\t\t);\n\t\t\treturn result.stdout\n\t\t\t\t.split('\\n')\n\t\t\t\t.map((line) => line.trim())\n\t\t\t\t.filter((line) => line.length > 0)\n\t\t\t\t.sort();\n\t\t},\n\t\tprune: async ({ keep }) => {\n\t\t\tif (disposed) throw new Error('Deployer is disposed');\n\t\t\tconst all = await (async () => {\n\t\t\t\tconst result = await options.target.exec(\n\t\t\t\t\t`ls -1 ${releasesPath} 2>/dev/null || true`,\n\t\t\t\t\t{ timeoutMs: 10_000 },\n\t\t\t\t);\n\t\t\t\treturn result.stdout\n\t\t\t\t\t.split('\\n')\n\t\t\t\t\t.map((line) => line.trim())\n\t\t\t\t\t.filter((line) => line.length > 0)\n\t\t\t\t\t.sort();\n\t\t\t})();\n\t\t\tif (all.length <= keep) return { removed: [] };\n\t\t\tconst removed = all.slice(0, all.length - keep);\n\t\t\tfor (const releaseId of removed) {\n\t\t\t\tawait options.target.exec(`rm -rf ${releasesPath}/${releaseId}`, { timeoutMs: 60_000 });\n\t\t\t}\n\t\t\treturn { removed };\n\t\t},\n\t\treadReleaseMeta: readMeta,\n\t\trollback: async (releaseId) => {\n\t\t\tif (disposed) throw new Error('Deployer is disposed');\n\t\t\tawait ensureRoot();\n\t\t\tawait cleanOrphanedSymlink();\n\t\t\tconst exists = await options.target.exec(\n\t\t\t\t`test -d ${releasesPath}/${releaseId} && echo ok || echo missing`,\n\t\t\t\t{ timeoutMs: 5_000 },\n\t\t\t);\n\t\t\tif (!exists.stdout.includes('ok')) {\n\t\t\t\tthrow new Error(`rollback: release ${releaseId} not found at ${releasesPath}/${releaseId}`);\n\t\t\t}\n\t\t\t// Rollback steps: re-link + restart (no upload, no install, no build).\n\t\t\tconst rollbackSteps: DeployStep[] = defaultBunPipeline().filter((step) =>\n\t\t\t\tstep.name === 'link' || step.name === 'restart' || step.name === 'verify',\n\t\t\t);\n\t\t\tconst prior = await readMeta(releaseId);\n\t\t\treturn runSteps(rollbackSteps, releaseId, {\n\t\t\t\talreadyCompleted: [],\n\t\t\t\tannotations: prior?.annotations ?? {},\n\t\t\t\tdryRun: false,\n\t\t\t});\n\t\t},\n\t\tstatus: async () => {\n\t\t\tif (disposed) throw new Error('Deployer is disposed');\n\t\t\tif (!processManager.status) return 'unknown';\n\t\t\treturn processManager.status(options.target, activeProcessContext());\n\t\t},\n\t\tstop: async () => {\n\t\t\tif (disposed) throw new Error('Deployer is disposed');\n\t\t\tif (!processManager.stop) {\n\t\t\t\tthrow new Error('Process manager does not support stop');\n\t\t\t}\n\t\t\tawait processManager.stop(options.target, activeProcessContext());\n\t\t},\n\t};\n};\n"
|
|
10
11
|
],
|
|
11
|
-
"mappings": "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",
|
|
12
|
-
"debugId": "
|
|
12
|
+
"mappings": "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",
|
|
13
|
+
"debugId": "19FB526D8301A46164756E2164756E21",
|
|
13
14
|
"names": []
|
|
14
15
|
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
export declare const NATIVE_RELEASE_REGISTRY_FORMAT: 1;
|
|
2
|
+
export type AndroidNativeReleaseMetadata = {
|
|
3
|
+
appBuild: string;
|
|
4
|
+
appId: string;
|
|
5
|
+
artifact: "app-release.aab";
|
|
6
|
+
bytes: number;
|
|
7
|
+
engine: "capacitor";
|
|
8
|
+
format: 1;
|
|
9
|
+
platform: "android";
|
|
10
|
+
releaseId: string;
|
|
11
|
+
runtime: string;
|
|
12
|
+
sha256: string;
|
|
13
|
+
signed: boolean;
|
|
14
|
+
type: "aab";
|
|
15
|
+
};
|
|
16
|
+
export type NativeReleaseMetadata = AndroidNativeReleaseMetadata;
|
|
17
|
+
export type NativeReleaseRecord = {
|
|
18
|
+
artifactKey: string;
|
|
19
|
+
format: typeof NATIVE_RELEASE_REGISTRY_FORMAT;
|
|
20
|
+
metadata: NativeReleaseMetadata;
|
|
21
|
+
};
|
|
22
|
+
export type NativeReleaseChannel = {
|
|
23
|
+
appId: string;
|
|
24
|
+
channel: string;
|
|
25
|
+
format: typeof NATIVE_RELEASE_REGISTRY_FORMAT;
|
|
26
|
+
platform: NativeReleaseMetadata["platform"];
|
|
27
|
+
promotedAt: string;
|
|
28
|
+
releaseId: string;
|
|
29
|
+
sha256: string;
|
|
30
|
+
};
|
|
31
|
+
export type NativeReleaseBlobObject = {
|
|
32
|
+
key: string;
|
|
33
|
+
metadata?: Record<string, string>;
|
|
34
|
+
size: number;
|
|
35
|
+
};
|
|
36
|
+
export type NativeReleaseBlobStore = {
|
|
37
|
+
get: (key: string) => Promise<Uint8Array | null>;
|
|
38
|
+
head: (key: string) => Promise<NativeReleaseBlobObject | null>;
|
|
39
|
+
put: (key: string, body: ReadableStream<Uint8Array> | Uint8Array | string, options?: {
|
|
40
|
+
cacheControl?: string;
|
|
41
|
+
contentType?: string;
|
|
42
|
+
maxBytes?: number;
|
|
43
|
+
metadata?: Record<string, string>;
|
|
44
|
+
signal?: AbortSignal;
|
|
45
|
+
}) => Promise<unknown>;
|
|
46
|
+
};
|
|
47
|
+
export type NativeReleasePublication = {
|
|
48
|
+
channel?: NativeReleaseChannel;
|
|
49
|
+
record: NativeReleaseRecord;
|
|
50
|
+
reused: boolean;
|
|
51
|
+
};
|
|
52
|
+
export type NativeReleaseRegistry = {
|
|
53
|
+
promote: (options: {
|
|
54
|
+
allowUnsigned?: boolean;
|
|
55
|
+
appId: string;
|
|
56
|
+
channel: string;
|
|
57
|
+
platform: NativeReleaseMetadata["platform"];
|
|
58
|
+
releaseId: string;
|
|
59
|
+
signal?: AbortSignal;
|
|
60
|
+
}) => Promise<NativeReleaseChannel>;
|
|
61
|
+
publish: (options: {
|
|
62
|
+
allowUnsigned?: boolean;
|
|
63
|
+
channel?: string;
|
|
64
|
+
releaseRoot: string;
|
|
65
|
+
signal?: AbortSignal;
|
|
66
|
+
}) => Promise<NativeReleasePublication>;
|
|
67
|
+
read: (options: {
|
|
68
|
+
appId: string;
|
|
69
|
+
platform: NativeReleaseMetadata["platform"];
|
|
70
|
+
releaseId: string;
|
|
71
|
+
}) => Promise<NativeReleaseRecord | null>;
|
|
72
|
+
resolve: (options: {
|
|
73
|
+
appId: string;
|
|
74
|
+
channel: string;
|
|
75
|
+
platform: NativeReleaseMetadata["platform"];
|
|
76
|
+
}) => Promise<{
|
|
77
|
+
channel: NativeReleaseChannel;
|
|
78
|
+
record: NativeReleaseRecord;
|
|
79
|
+
} | null>;
|
|
80
|
+
};
|
|
81
|
+
export type NativeReleaseRegistryOptions = {
|
|
82
|
+
clock?: () => Date;
|
|
83
|
+
maxArtifactBytes?: number;
|
|
84
|
+
prefix?: string;
|
|
85
|
+
store: NativeReleaseBlobStore;
|
|
86
|
+
};
|
|
87
|
+
export declare class NativeReleaseRegistryError extends Error {
|
|
88
|
+
}
|
|
89
|
+
export declare const createNativeReleaseRegistry: (options: NativeReleaseRegistryOptions) => NativeReleaseRegistry;
|
|
@@ -0,0 +1,343 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
4
|
+
var __defProp = Object.defineProperty;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
+
function __accessProp(key) {
|
|
8
|
+
return this[key];
|
|
9
|
+
}
|
|
10
|
+
var __toESMCache_node;
|
|
11
|
+
var __toESMCache_esm;
|
|
12
|
+
var __toESM = (mod, isNodeMode, target) => {
|
|
13
|
+
var canCache = mod != null && typeof mod === "object";
|
|
14
|
+
if (canCache) {
|
|
15
|
+
var cache = isNodeMode ? __toESMCache_node ??= new WeakMap : __toESMCache_esm ??= new WeakMap;
|
|
16
|
+
var cached = cache.get(mod);
|
|
17
|
+
if (cached)
|
|
18
|
+
return cached;
|
|
19
|
+
}
|
|
20
|
+
target = mod != null ? __create(__getProtoOf(mod)) : {};
|
|
21
|
+
const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
|
|
22
|
+
for (let key of __getOwnPropNames(mod))
|
|
23
|
+
if (!__hasOwnProp.call(to, key))
|
|
24
|
+
__defProp(to, key, {
|
|
25
|
+
get: __accessProp.bind(mod, key),
|
|
26
|
+
enumerable: true
|
|
27
|
+
});
|
|
28
|
+
if (canCache)
|
|
29
|
+
cache.set(mod, to);
|
|
30
|
+
return to;
|
|
31
|
+
};
|
|
32
|
+
var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
|
|
33
|
+
var __require = import.meta.require;
|
|
34
|
+
|
|
35
|
+
// src/nativeRelease.ts
|
|
36
|
+
import { createHash } from "crypto";
|
|
37
|
+
import { readFile, stat } from "fs/promises";
|
|
38
|
+
import path from "path";
|
|
39
|
+
var DEFAULT_PREFIX = "absolutejs/native-releases";
|
|
40
|
+
var DEFAULT_MAX_ARTIFACT_BYTES = 2147483648;
|
|
41
|
+
var NATIVE_RELEASE_REGISTRY_FORMAT = 1;
|
|
42
|
+
var SHA256_PATTERN = /^[a-f0-9]{64}$/;
|
|
43
|
+
var APP_ID_PATTERN = /^[A-Za-z][\w]*(?:\.[A-Za-z][\w]*)+$/;
|
|
44
|
+
var CHANNEL_PATTERN = /^[a-z0-9][a-z0-9._-]{0,63}$/;
|
|
45
|
+
|
|
46
|
+
class NativeReleaseRegistryError extends Error {
|
|
47
|
+
}
|
|
48
|
+
var isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
49
|
+
var requireString = (value, field) => {
|
|
50
|
+
if (typeof value !== "string" || value.length === 0)
|
|
51
|
+
throw new NativeReleaseRegistryError(`Native release ${field} is invalid`);
|
|
52
|
+
return value;
|
|
53
|
+
};
|
|
54
|
+
var parseMetadata = (value) => {
|
|
55
|
+
if (!isRecord(value))
|
|
56
|
+
throw new NativeReleaseRegistryError("Native release metadata is invalid");
|
|
57
|
+
const appId = requireString(value.appId, "appId");
|
|
58
|
+
const sha256 = requireString(value.sha256, "sha256");
|
|
59
|
+
const releaseId = requireString(value.releaseId, "releaseId");
|
|
60
|
+
if (!APP_ID_PATTERN.test(appId))
|
|
61
|
+
throw new NativeReleaseRegistryError("Native release appId is invalid");
|
|
62
|
+
if (!SHA256_PATTERN.test(sha256))
|
|
63
|
+
throw new NativeReleaseRegistryError("Native release sha256 is invalid");
|
|
64
|
+
if (releaseId !== `amobile_android_${sha256}`)
|
|
65
|
+
throw new NativeReleaseRegistryError("Native release id does not match its artifact digest");
|
|
66
|
+
if (value.artifact !== "app-release.aab" || value.engine !== "capacitor" || value.format !== 1 || value.platform !== "android" || value.type !== "aab" || typeof value.signed !== "boolean" || !Number.isSafeInteger(value.bytes) || Number(value.bytes) < 1)
|
|
67
|
+
throw new NativeReleaseRegistryError("Native release metadata is invalid");
|
|
68
|
+
return {
|
|
69
|
+
appBuild: requireString(value.appBuild, "appBuild"),
|
|
70
|
+
appId,
|
|
71
|
+
artifact: "app-release.aab",
|
|
72
|
+
bytes: Number(value.bytes),
|
|
73
|
+
engine: "capacitor",
|
|
74
|
+
format: 1,
|
|
75
|
+
platform: "android",
|
|
76
|
+
releaseId,
|
|
77
|
+
runtime: requireString(value.runtime, "runtime"),
|
|
78
|
+
sha256,
|
|
79
|
+
signed: value.signed,
|
|
80
|
+
type: "aab"
|
|
81
|
+
};
|
|
82
|
+
};
|
|
83
|
+
var normalizedPrefix = (value) => {
|
|
84
|
+
const prefix = value.replace(/^\/+|\/+$/g, "");
|
|
85
|
+
if (prefix.length === 0 || prefix.split("/").some((segment) => segment === "." || segment === ".."))
|
|
86
|
+
throw new NativeReleaseRegistryError("Native release prefix is invalid");
|
|
87
|
+
return prefix;
|
|
88
|
+
};
|
|
89
|
+
var requireChannel = (value) => {
|
|
90
|
+
if (!CHANNEL_PATTERN.test(value))
|
|
91
|
+
throw new NativeReleaseRegistryError("Native release channel is invalid");
|
|
92
|
+
return value;
|
|
93
|
+
};
|
|
94
|
+
var appIdentity = (appId) => createHash("sha256").update(appId).digest("hex");
|
|
95
|
+
var sha256Bytes = (bytes) => createHash("sha256").update(bytes).digest("hex");
|
|
96
|
+
var isIsoTimestamp = (value) => {
|
|
97
|
+
if (typeof value !== "string" || Number.isNaN(Date.parse(value)))
|
|
98
|
+
return false;
|
|
99
|
+
return new Date(value).toISOString() === value;
|
|
100
|
+
};
|
|
101
|
+
var sha256File = async (file) => {
|
|
102
|
+
const hasher = new Bun.CryptoHasher("sha256");
|
|
103
|
+
for await (const chunk of file.stream())
|
|
104
|
+
hasher.update(chunk);
|
|
105
|
+
return hasher.digest("hex");
|
|
106
|
+
};
|
|
107
|
+
var encodedJson = (value) => new TextEncoder().encode(`${JSON.stringify(value, null, 2)}
|
|
108
|
+
`);
|
|
109
|
+
var decodedJson = (bytes) => JSON.parse(new TextDecoder().decode(bytes));
|
|
110
|
+
var sameMetadata = (left, right) => JSON.stringify(left) === JSON.stringify(right);
|
|
111
|
+
var parseRecord = (value) => {
|
|
112
|
+
if (!isRecord(value) || value.format !== NATIVE_RELEASE_REGISTRY_FORMAT || typeof value.artifactKey !== "string")
|
|
113
|
+
throw new NativeReleaseRegistryError("Native release record is invalid");
|
|
114
|
+
const metadata = parseMetadata(value.metadata);
|
|
115
|
+
return {
|
|
116
|
+
artifactKey: value.artifactKey,
|
|
117
|
+
format: NATIVE_RELEASE_REGISTRY_FORMAT,
|
|
118
|
+
metadata
|
|
119
|
+
};
|
|
120
|
+
};
|
|
121
|
+
var parseChannel = (value) => {
|
|
122
|
+
if (!isRecord(value) || value.format !== NATIVE_RELEASE_REGISTRY_FORMAT || value.platform !== "android" || !isIsoTimestamp(value.promotedAt))
|
|
123
|
+
throw new NativeReleaseRegistryError("Native release channel is invalid");
|
|
124
|
+
const appId = requireString(value.appId, "channel appId");
|
|
125
|
+
const channel = requireChannel(requireString(value.channel, "channel"));
|
|
126
|
+
const sha256 = requireString(value.sha256, "channel sha256");
|
|
127
|
+
const releaseId = requireString(value.releaseId, "channel releaseId");
|
|
128
|
+
if (!APP_ID_PATTERN.test(appId) || !SHA256_PATTERN.test(sha256))
|
|
129
|
+
throw new NativeReleaseRegistryError("Native release channel is invalid");
|
|
130
|
+
if (releaseId !== `amobile_android_${sha256}`)
|
|
131
|
+
throw new NativeReleaseRegistryError("Native release channel identity does not match");
|
|
132
|
+
return {
|
|
133
|
+
appId,
|
|
134
|
+
channel,
|
|
135
|
+
format: NATIVE_RELEASE_REGISTRY_FORMAT,
|
|
136
|
+
platform: "android",
|
|
137
|
+
promotedAt: value.promotedAt,
|
|
138
|
+
releaseId,
|
|
139
|
+
sha256
|
|
140
|
+
};
|
|
141
|
+
};
|
|
142
|
+
var createNativeReleaseRegistry = (options) => {
|
|
143
|
+
const prefix = normalizedPrefix(options.prefix ?? DEFAULT_PREFIX);
|
|
144
|
+
const maxArtifactBytes = options.maxArtifactBytes ?? DEFAULT_MAX_ARTIFACT_BYTES;
|
|
145
|
+
if (!Number.isSafeInteger(maxArtifactBytes) || maxArtifactBytes < 1)
|
|
146
|
+
throw new NativeReleaseRegistryError("Native release maxArtifactBytes is invalid");
|
|
147
|
+
const clock = options.clock ?? (() => new Date);
|
|
148
|
+
const appRoot = (appId, platform) => `${prefix}/${appIdentity(appId)}/${platform}`;
|
|
149
|
+
const releaseRoot = (metadata) => `${appRoot(metadata.appId, metadata.platform)}/releases/${metadata.releaseId}`;
|
|
150
|
+
const recordKey = (metadata) => `${releaseRoot(metadata)}/release.json`;
|
|
151
|
+
const artifactKey = (metadata) => `${releaseRoot(metadata)}/${metadata.artifact}`;
|
|
152
|
+
const channelKey = (appId, platform, channel) => `${appRoot(appId, platform)}/channels/${requireChannel(channel)}.json`;
|
|
153
|
+
const requireStoredArtifact = async (key, metadata) => {
|
|
154
|
+
const stored = await options.store.head(key);
|
|
155
|
+
if (!stored || stored.size !== metadata.bytes || stored.metadata?.sha256 !== metadata.sha256 || stored.metadata?.releaseId !== metadata.releaseId)
|
|
156
|
+
throw new NativeReleaseRegistryError("Stored native release artifact does not match its immutable identity");
|
|
157
|
+
};
|
|
158
|
+
const read = async (input) => {
|
|
159
|
+
if (!APP_ID_PATTERN.test(input.appId))
|
|
160
|
+
throw new NativeReleaseRegistryError("Native release appId is invalid");
|
|
161
|
+
const digest = input.releaseId.replace(/^amobile_android_/, "");
|
|
162
|
+
if (!SHA256_PATTERN.test(digest) || input.releaseId !== `amobile_android_${digest}`)
|
|
163
|
+
throw new NativeReleaseRegistryError("Native release id is invalid");
|
|
164
|
+
const identity = {
|
|
165
|
+
appBuild: "lookup",
|
|
166
|
+
appId: input.appId,
|
|
167
|
+
artifact: "app-release.aab",
|
|
168
|
+
bytes: 1,
|
|
169
|
+
engine: "capacitor",
|
|
170
|
+
format: 1,
|
|
171
|
+
platform: "android",
|
|
172
|
+
releaseId: input.releaseId,
|
|
173
|
+
runtime: "lookup",
|
|
174
|
+
sha256: digest,
|
|
175
|
+
signed: true,
|
|
176
|
+
type: "aab"
|
|
177
|
+
};
|
|
178
|
+
const key = recordKey(identity);
|
|
179
|
+
const bytes = await options.store.get(key);
|
|
180
|
+
if (!bytes)
|
|
181
|
+
return null;
|
|
182
|
+
const storedRecord = await options.store.head(key);
|
|
183
|
+
if (!storedRecord || storedRecord.size !== bytes.byteLength || storedRecord.metadata?.releaseId !== input.releaseId || storedRecord.metadata?.sha256 !== sha256Bytes(bytes))
|
|
184
|
+
throw new NativeReleaseRegistryError("Stored native release record does not match its immutable identity");
|
|
185
|
+
const record = parseRecord(decodedJson(bytes));
|
|
186
|
+
if (record.metadata.appId !== input.appId || record.metadata.platform !== input.platform || record.metadata.releaseId !== input.releaseId || record.artifactKey !== artifactKey(record.metadata))
|
|
187
|
+
throw new NativeReleaseRegistryError("Stored native release record identity does not match");
|
|
188
|
+
await requireStoredArtifact(record.artifactKey, record.metadata);
|
|
189
|
+
return record;
|
|
190
|
+
};
|
|
191
|
+
const promote = async (input) => {
|
|
192
|
+
input.signal?.throwIfAborted();
|
|
193
|
+
const record = await read(input);
|
|
194
|
+
if (!record)
|
|
195
|
+
throw new NativeReleaseRegistryError("Native release was not published");
|
|
196
|
+
if (!record.metadata.signed && !input.allowUnsigned)
|
|
197
|
+
throw new NativeReleaseRegistryError("Unsigned native releases cannot be promoted");
|
|
198
|
+
const key = channelKey(input.appId, input.platform, input.channel);
|
|
199
|
+
const existingBytes = await options.store.get(key);
|
|
200
|
+
if (existingBytes) {
|
|
201
|
+
const existing = parseChannel(decodedJson(existingBytes));
|
|
202
|
+
if (existing.appId !== input.appId || existing.platform !== input.platform || existing.channel !== input.channel)
|
|
203
|
+
throw new NativeReleaseRegistryError("Stored native release channel identity does not match");
|
|
204
|
+
if (existing.releaseId === input.releaseId)
|
|
205
|
+
return existing;
|
|
206
|
+
}
|
|
207
|
+
const channel = {
|
|
208
|
+
appId: input.appId,
|
|
209
|
+
channel: requireChannel(input.channel),
|
|
210
|
+
format: NATIVE_RELEASE_REGISTRY_FORMAT,
|
|
211
|
+
platform: input.platform,
|
|
212
|
+
promotedAt: clock().toISOString(),
|
|
213
|
+
releaseId: record.metadata.releaseId,
|
|
214
|
+
sha256: record.metadata.sha256
|
|
215
|
+
};
|
|
216
|
+
const serialized = encodedJson(channel);
|
|
217
|
+
await options.store.put(key, serialized, {
|
|
218
|
+
cacheControl: "no-cache",
|
|
219
|
+
contentType: "application/json",
|
|
220
|
+
maxBytes: serialized.byteLength,
|
|
221
|
+
metadata: {
|
|
222
|
+
channel: channel.channel,
|
|
223
|
+
releaseId: channel.releaseId,
|
|
224
|
+
sha256: channel.sha256
|
|
225
|
+
},
|
|
226
|
+
signal: input.signal
|
|
227
|
+
});
|
|
228
|
+
const stored = await options.store.get(key);
|
|
229
|
+
if (!stored || JSON.stringify(parseChannel(decodedJson(stored))) !== JSON.stringify(channel))
|
|
230
|
+
throw new NativeReleaseRegistryError("Native release channel verification failed");
|
|
231
|
+
return channel;
|
|
232
|
+
};
|
|
233
|
+
return {
|
|
234
|
+
promote,
|
|
235
|
+
publish: async (input) => {
|
|
236
|
+
input.signal?.throwIfAborted();
|
|
237
|
+
const localRoot = path.resolve(input.releaseRoot);
|
|
238
|
+
const metadata = parseMetadata(JSON.parse(await readFile(path.join(localRoot, "release.json"), "utf8")));
|
|
239
|
+
if (metadata.bytes > maxArtifactBytes)
|
|
240
|
+
throw new NativeReleaseRegistryError("Native release exceeds the configured artifact limit");
|
|
241
|
+
if (!metadata.signed && !input.allowUnsigned)
|
|
242
|
+
throw new NativeReleaseRegistryError("Unsigned native releases cannot be published");
|
|
243
|
+
const localArtifact = path.join(localRoot, metadata.artifact);
|
|
244
|
+
const artifactStats = await stat(localArtifact).catch(() => null);
|
|
245
|
+
if (!artifactStats?.isFile() || artifactStats.size !== metadata.bytes)
|
|
246
|
+
throw new NativeReleaseRegistryError("Native release artifact size does not match its metadata");
|
|
247
|
+
if (await sha256File(Bun.file(localArtifact)) !== metadata.sha256)
|
|
248
|
+
throw new NativeReleaseRegistryError("Native release artifact digest does not match its metadata");
|
|
249
|
+
const existing = await read({
|
|
250
|
+
appId: metadata.appId,
|
|
251
|
+
platform: metadata.platform,
|
|
252
|
+
releaseId: metadata.releaseId
|
|
253
|
+
});
|
|
254
|
+
let record;
|
|
255
|
+
let reused = false;
|
|
256
|
+
if (existing) {
|
|
257
|
+
if (!sameMetadata(existing.metadata, metadata))
|
|
258
|
+
throw new NativeReleaseRegistryError("Published native release metadata is immutable");
|
|
259
|
+
record = existing;
|
|
260
|
+
reused = true;
|
|
261
|
+
} else {
|
|
262
|
+
const key = artifactKey(metadata);
|
|
263
|
+
const storedArtifact = await options.store.head(key);
|
|
264
|
+
if (storedArtifact) {
|
|
265
|
+
await requireStoredArtifact(key, metadata);
|
|
266
|
+
} else {
|
|
267
|
+
await options.store.put(key, Bun.file(localArtifact).stream(), {
|
|
268
|
+
cacheControl: "public, max-age=31536000, immutable",
|
|
269
|
+
contentType: "application/octet-stream",
|
|
270
|
+
maxBytes: metadata.bytes,
|
|
271
|
+
metadata: {
|
|
272
|
+
appId: metadata.appId,
|
|
273
|
+
releaseId: metadata.releaseId,
|
|
274
|
+
sha256: metadata.sha256
|
|
275
|
+
},
|
|
276
|
+
signal: input.signal
|
|
277
|
+
});
|
|
278
|
+
await requireStoredArtifact(key, metadata);
|
|
279
|
+
}
|
|
280
|
+
record = {
|
|
281
|
+
artifactKey: key,
|
|
282
|
+
format: NATIVE_RELEASE_REGISTRY_FORMAT,
|
|
283
|
+
metadata
|
|
284
|
+
};
|
|
285
|
+
const serialized = encodedJson(record);
|
|
286
|
+
await options.store.put(recordKey(metadata), serialized, {
|
|
287
|
+
cacheControl: "public, max-age=31536000, immutable",
|
|
288
|
+
contentType: "application/json",
|
|
289
|
+
maxBytes: serialized.byteLength,
|
|
290
|
+
metadata: {
|
|
291
|
+
releaseId: metadata.releaseId,
|
|
292
|
+
sha256: sha256Bytes(serialized)
|
|
293
|
+
},
|
|
294
|
+
signal: input.signal
|
|
295
|
+
});
|
|
296
|
+
const verified = await read({
|
|
297
|
+
appId: metadata.appId,
|
|
298
|
+
platform: metadata.platform,
|
|
299
|
+
releaseId: metadata.releaseId
|
|
300
|
+
});
|
|
301
|
+
if (!verified || JSON.stringify(verified) !== JSON.stringify(record))
|
|
302
|
+
throw new NativeReleaseRegistryError("Native release publication verification failed");
|
|
303
|
+
}
|
|
304
|
+
const channel = input.channel ? await promote({
|
|
305
|
+
allowUnsigned: input.allowUnsigned,
|
|
306
|
+
appId: metadata.appId,
|
|
307
|
+
channel: input.channel,
|
|
308
|
+
platform: metadata.platform,
|
|
309
|
+
releaseId: metadata.releaseId,
|
|
310
|
+
signal: input.signal
|
|
311
|
+
}) : undefined;
|
|
312
|
+
return { ...channel ? { channel } : {}, record, reused };
|
|
313
|
+
},
|
|
314
|
+
read,
|
|
315
|
+
resolve: async (input) => {
|
|
316
|
+
if (!APP_ID_PATTERN.test(input.appId))
|
|
317
|
+
throw new NativeReleaseRegistryError("Native release appId is invalid");
|
|
318
|
+
const key = channelKey(input.appId, input.platform, input.channel);
|
|
319
|
+
const bytes = await options.store.get(key);
|
|
320
|
+
if (!bytes)
|
|
321
|
+
return null;
|
|
322
|
+
const channel = parseChannel(decodedJson(bytes));
|
|
323
|
+
if (channel.appId !== input.appId || channel.platform !== input.platform || channel.channel !== input.channel)
|
|
324
|
+
throw new NativeReleaseRegistryError("Stored native release channel identity does not match");
|
|
325
|
+
const record = await read({
|
|
326
|
+
appId: channel.appId,
|
|
327
|
+
platform: channel.platform,
|
|
328
|
+
releaseId: channel.releaseId
|
|
329
|
+
});
|
|
330
|
+
if (!record || record.metadata.sha256 !== channel.sha256)
|
|
331
|
+
throw new NativeReleaseRegistryError("Native release channel points to a missing or invalid release");
|
|
332
|
+
return { channel, record };
|
|
333
|
+
}
|
|
334
|
+
};
|
|
335
|
+
};
|
|
336
|
+
export {
|
|
337
|
+
createNativeReleaseRegistry,
|
|
338
|
+
NativeReleaseRegistryError,
|
|
339
|
+
NATIVE_RELEASE_REGISTRY_FORMAT
|
|
340
|
+
};
|
|
341
|
+
|
|
342
|
+
//# debugId=1FEEE1047DE9DB9664756E2164756E21
|
|
343
|
+
//# sourceMappingURL=nativeRelease.js.map
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/nativeRelease.ts"],
|
|
4
|
+
"sourcesContent": [
|
|
5
|
+
"import { createHash } from \"node:crypto\";\nimport { readFile, stat } from \"node:fs/promises\";\nimport path from \"node:path\";\n\nconst DEFAULT_PREFIX = \"absolutejs/native-releases\";\nconst DEFAULT_MAX_ARTIFACT_BYTES = 2_147_483_648;\nexport const NATIVE_RELEASE_REGISTRY_FORMAT = 1 as const;\nconst SHA256_PATTERN = /^[a-f0-9]{64}$/;\nconst APP_ID_PATTERN = /^[A-Za-z][\\w]*(?:\\.[A-Za-z][\\w]*)+$/;\nconst CHANNEL_PATTERN = /^[a-z0-9][a-z0-9._-]{0,63}$/;\n\nexport type AndroidNativeReleaseMetadata = {\n appBuild: string;\n appId: string;\n artifact: \"app-release.aab\";\n bytes: number;\n engine: \"capacitor\";\n format: 1;\n platform: \"android\";\n releaseId: string;\n runtime: string;\n sha256: string;\n signed: boolean;\n type: \"aab\";\n};\n\nexport type NativeReleaseMetadata = AndroidNativeReleaseMetadata;\n\nexport type NativeReleaseRecord = {\n artifactKey: string;\n format: typeof NATIVE_RELEASE_REGISTRY_FORMAT;\n metadata: NativeReleaseMetadata;\n};\n\nexport type NativeReleaseChannel = {\n appId: string;\n channel: string;\n format: typeof NATIVE_RELEASE_REGISTRY_FORMAT;\n platform: NativeReleaseMetadata[\"platform\"];\n promotedAt: string;\n releaseId: string;\n sha256: string;\n};\n\nexport type NativeReleaseBlobObject = {\n key: string;\n metadata?: Record<string, string>;\n size: number;\n};\n\nexport type NativeReleaseBlobStore = {\n get: (key: string) => Promise<Uint8Array | null>;\n head: (key: string) => Promise<NativeReleaseBlobObject | null>;\n put: (\n key: string,\n body: ReadableStream<Uint8Array> | Uint8Array | string,\n options?: {\n cacheControl?: string;\n contentType?: string;\n maxBytes?: number;\n metadata?: Record<string, string>;\n signal?: AbortSignal;\n },\n ) => Promise<unknown>;\n};\n\nexport type NativeReleasePublication = {\n channel?: NativeReleaseChannel;\n record: NativeReleaseRecord;\n reused: boolean;\n};\n\nexport type NativeReleaseRegistry = {\n promote: (options: {\n allowUnsigned?: boolean;\n appId: string;\n channel: string;\n platform: NativeReleaseMetadata[\"platform\"];\n releaseId: string;\n signal?: AbortSignal;\n }) => Promise<NativeReleaseChannel>;\n publish: (options: {\n allowUnsigned?: boolean;\n channel?: string;\n releaseRoot: string;\n signal?: AbortSignal;\n }) => Promise<NativeReleasePublication>;\n read: (options: {\n appId: string;\n platform: NativeReleaseMetadata[\"platform\"];\n releaseId: string;\n }) => Promise<NativeReleaseRecord | null>;\n resolve: (options: {\n appId: string;\n channel: string;\n platform: NativeReleaseMetadata[\"platform\"];\n }) => Promise<{\n channel: NativeReleaseChannel;\n record: NativeReleaseRecord;\n } | null>;\n};\n\nexport type NativeReleaseRegistryOptions = {\n clock?: () => Date;\n maxArtifactBytes?: number;\n prefix?: string;\n store: NativeReleaseBlobStore;\n};\n\nexport class NativeReleaseRegistryError extends Error {}\n\nconst isRecord = (value: unknown): value is Record<string, unknown> =>\n typeof value === \"object\" && value !== null && !Array.isArray(value);\n\nconst requireString = (value: unknown, field: string) => {\n if (typeof value !== \"string\" || value.length === 0)\n throw new NativeReleaseRegistryError(`Native release ${field} is invalid`);\n\n return value;\n};\n\nconst parseMetadata = (value: unknown): NativeReleaseMetadata => {\n if (!isRecord(value))\n throw new NativeReleaseRegistryError(\"Native release metadata is invalid\");\n const appId = requireString(value.appId, \"appId\");\n const sha256 = requireString(value.sha256, \"sha256\");\n const releaseId = requireString(value.releaseId, \"releaseId\");\n if (!APP_ID_PATTERN.test(appId))\n throw new NativeReleaseRegistryError(\"Native release appId is invalid\");\n if (!SHA256_PATTERN.test(sha256))\n throw new NativeReleaseRegistryError(\"Native release sha256 is invalid\");\n if (releaseId !== `amobile_android_${sha256}`)\n throw new NativeReleaseRegistryError(\n \"Native release id does not match its artifact digest\",\n );\n if (\n value.artifact !== \"app-release.aab\" ||\n value.engine !== \"capacitor\" ||\n value.format !== 1 ||\n value.platform !== \"android\" ||\n value.type !== \"aab\" ||\n typeof value.signed !== \"boolean\" ||\n !Number.isSafeInteger(value.bytes) ||\n Number(value.bytes) < 1\n )\n throw new NativeReleaseRegistryError(\"Native release metadata is invalid\");\n\n return {\n appBuild: requireString(value.appBuild, \"appBuild\"),\n appId,\n artifact: \"app-release.aab\",\n bytes: Number(value.bytes),\n engine: \"capacitor\",\n format: 1,\n platform: \"android\",\n releaseId,\n runtime: requireString(value.runtime, \"runtime\"),\n sha256,\n signed: value.signed,\n type: \"aab\",\n };\n};\n\nconst normalizedPrefix = (value: string) => {\n const prefix = value.replace(/^\\/+|\\/+$/g, \"\");\n if (\n prefix.length === 0 ||\n prefix.split(\"/\").some((segment) => segment === \".\" || segment === \"..\")\n )\n throw new NativeReleaseRegistryError(\"Native release prefix is invalid\");\n\n return prefix;\n};\n\nconst requireChannel = (value: string) => {\n if (!CHANNEL_PATTERN.test(value))\n throw new NativeReleaseRegistryError(\"Native release channel is invalid\");\n\n return value;\n};\n\nconst appIdentity = (appId: string) =>\n createHash(\"sha256\").update(appId).digest(\"hex\");\n\nconst sha256Bytes = (bytes: Uint8Array) =>\n createHash(\"sha256\").update(bytes).digest(\"hex\");\n\nconst isIsoTimestamp = (value: unknown): value is string => {\n if (typeof value !== \"string\" || Number.isNaN(Date.parse(value)))\n return false;\n\n return new Date(value).toISOString() === value;\n};\n\nconst sha256File = async (file: Bun.BunFile) => {\n const hasher = new Bun.CryptoHasher(\"sha256\");\n for await (const chunk of file.stream()) hasher.update(chunk);\n\n return hasher.digest(\"hex\");\n};\n\nconst encodedJson = (value: unknown) =>\n new TextEncoder().encode(`${JSON.stringify(value, null, 2)}\\n`);\n\nconst decodedJson = (bytes: Uint8Array) =>\n JSON.parse(new TextDecoder().decode(bytes)) as unknown;\n\nconst sameMetadata = (\n left: NativeReleaseMetadata,\n right: NativeReleaseMetadata,\n) => JSON.stringify(left) === JSON.stringify(right);\n\nconst parseRecord = (value: unknown): NativeReleaseRecord => {\n if (\n !isRecord(value) ||\n value.format !== NATIVE_RELEASE_REGISTRY_FORMAT ||\n typeof value.artifactKey !== \"string\"\n )\n throw new NativeReleaseRegistryError(\"Native release record is invalid\");\n const metadata = parseMetadata(value.metadata);\n\n return {\n artifactKey: value.artifactKey,\n format: NATIVE_RELEASE_REGISTRY_FORMAT,\n metadata,\n };\n};\n\nconst parseChannel = (value: unknown): NativeReleaseChannel => {\n if (\n !isRecord(value) ||\n value.format !== NATIVE_RELEASE_REGISTRY_FORMAT ||\n value.platform !== \"android\" ||\n !isIsoTimestamp(value.promotedAt)\n )\n throw new NativeReleaseRegistryError(\"Native release channel is invalid\");\n const appId = requireString(value.appId, \"channel appId\");\n const channel = requireChannel(requireString(value.channel, \"channel\"));\n const sha256 = requireString(value.sha256, \"channel sha256\");\n const releaseId = requireString(value.releaseId, \"channel releaseId\");\n if (!APP_ID_PATTERN.test(appId) || !SHA256_PATTERN.test(sha256))\n throw new NativeReleaseRegistryError(\"Native release channel is invalid\");\n if (releaseId !== `amobile_android_${sha256}`)\n throw new NativeReleaseRegistryError(\n \"Native release channel identity does not match\",\n );\n\n return {\n appId,\n channel,\n format: NATIVE_RELEASE_REGISTRY_FORMAT,\n platform: \"android\",\n promotedAt: value.promotedAt,\n releaseId,\n sha256,\n };\n};\n\nexport const createNativeReleaseRegistry = (\n options: NativeReleaseRegistryOptions,\n): NativeReleaseRegistry => {\n const prefix = normalizedPrefix(options.prefix ?? DEFAULT_PREFIX);\n const maxArtifactBytes =\n options.maxArtifactBytes ?? DEFAULT_MAX_ARTIFACT_BYTES;\n if (!Number.isSafeInteger(maxArtifactBytes) || maxArtifactBytes < 1)\n throw new NativeReleaseRegistryError(\n \"Native release maxArtifactBytes is invalid\",\n );\n const clock = options.clock ?? (() => new Date());\n const appRoot = (appId: string, platform: \"android\") =>\n `${prefix}/${appIdentity(appId)}/${platform}`;\n const releaseRoot = (metadata: NativeReleaseMetadata) =>\n `${appRoot(metadata.appId, metadata.platform)}/releases/${metadata.releaseId}`;\n const recordKey = (metadata: NativeReleaseMetadata) =>\n `${releaseRoot(metadata)}/release.json`;\n const artifactKey = (metadata: NativeReleaseMetadata) =>\n `${releaseRoot(metadata)}/${metadata.artifact}`;\n const channelKey = (appId: string, platform: \"android\", channel: string) =>\n `${appRoot(appId, platform)}/channels/${requireChannel(channel)}.json`;\n\n const requireStoredArtifact = async (\n key: string,\n metadata: NativeReleaseMetadata,\n ) => {\n const stored = await options.store.head(key);\n if (\n !stored ||\n stored.size !== metadata.bytes ||\n stored.metadata?.sha256 !== metadata.sha256 ||\n stored.metadata?.releaseId !== metadata.releaseId\n )\n throw new NativeReleaseRegistryError(\n \"Stored native release artifact does not match its immutable identity\",\n );\n };\n\n const read = async (input: {\n appId: string;\n platform: \"android\";\n releaseId: string;\n }) => {\n if (!APP_ID_PATTERN.test(input.appId))\n throw new NativeReleaseRegistryError(\"Native release appId is invalid\");\n const digest = input.releaseId.replace(/^amobile_android_/, \"\");\n if (\n !SHA256_PATTERN.test(digest) ||\n input.releaseId !== `amobile_android_${digest}`\n )\n throw new NativeReleaseRegistryError(\"Native release id is invalid\");\n const identity: NativeReleaseMetadata = {\n appBuild: \"lookup\",\n appId: input.appId,\n artifact: \"app-release.aab\",\n bytes: 1,\n engine: \"capacitor\",\n format: 1,\n platform: \"android\",\n releaseId: input.releaseId,\n runtime: \"lookup\",\n sha256: digest,\n signed: true,\n type: \"aab\",\n };\n const key = recordKey(identity);\n const bytes = await options.store.get(key);\n if (!bytes) return null;\n const storedRecord = await options.store.head(key);\n if (\n !storedRecord ||\n storedRecord.size !== bytes.byteLength ||\n storedRecord.metadata?.releaseId !== input.releaseId ||\n storedRecord.metadata?.sha256 !== sha256Bytes(bytes)\n )\n throw new NativeReleaseRegistryError(\n \"Stored native release record does not match its immutable identity\",\n );\n const record = parseRecord(decodedJson(bytes));\n if (\n record.metadata.appId !== input.appId ||\n record.metadata.platform !== input.platform ||\n record.metadata.releaseId !== input.releaseId ||\n record.artifactKey !== artifactKey(record.metadata)\n )\n throw new NativeReleaseRegistryError(\n \"Stored native release record identity does not match\",\n );\n await requireStoredArtifact(record.artifactKey, record.metadata);\n\n return record;\n };\n\n const promote: NativeReleaseRegistry[\"promote\"] = async (input) => {\n input.signal?.throwIfAborted();\n const record = await read(input);\n if (!record)\n throw new NativeReleaseRegistryError(\"Native release was not published\");\n if (!record.metadata.signed && !input.allowUnsigned)\n throw new NativeReleaseRegistryError(\n \"Unsigned native releases cannot be promoted\",\n );\n const key = channelKey(input.appId, input.platform, input.channel);\n const existingBytes = await options.store.get(key);\n if (existingBytes) {\n const existing = parseChannel(decodedJson(existingBytes));\n if (\n existing.appId !== input.appId ||\n existing.platform !== input.platform ||\n existing.channel !== input.channel\n )\n throw new NativeReleaseRegistryError(\n \"Stored native release channel identity does not match\",\n );\n if (existing.releaseId === input.releaseId) return existing;\n }\n const channel: NativeReleaseChannel = {\n appId: input.appId,\n channel: requireChannel(input.channel),\n format: NATIVE_RELEASE_REGISTRY_FORMAT,\n platform: input.platform,\n promotedAt: clock().toISOString(),\n releaseId: record.metadata.releaseId,\n sha256: record.metadata.sha256,\n };\n const serialized = encodedJson(channel);\n await options.store.put(key, serialized, {\n cacheControl: \"no-cache\",\n contentType: \"application/json\",\n maxBytes: serialized.byteLength,\n metadata: {\n channel: channel.channel,\n releaseId: channel.releaseId,\n sha256: channel.sha256,\n },\n signal: input.signal,\n });\n const stored = await options.store.get(key);\n if (\n !stored ||\n JSON.stringify(parseChannel(decodedJson(stored))) !==\n JSON.stringify(channel)\n )\n throw new NativeReleaseRegistryError(\n \"Native release channel verification failed\",\n );\n\n return channel;\n };\n\n return {\n promote,\n publish: async (input) => {\n input.signal?.throwIfAborted();\n const localRoot = path.resolve(input.releaseRoot);\n const metadata = parseMetadata(\n JSON.parse(\n await readFile(path.join(localRoot, \"release.json\"), \"utf8\"),\n ),\n );\n if (metadata.bytes > maxArtifactBytes)\n throw new NativeReleaseRegistryError(\n \"Native release exceeds the configured artifact limit\",\n );\n if (!metadata.signed && !input.allowUnsigned)\n throw new NativeReleaseRegistryError(\n \"Unsigned native releases cannot be published\",\n );\n const localArtifact = path.join(localRoot, metadata.artifact);\n const artifactStats = await stat(localArtifact).catch(() => null);\n if (!artifactStats?.isFile() || artifactStats.size !== metadata.bytes)\n throw new NativeReleaseRegistryError(\n \"Native release artifact size does not match its metadata\",\n );\n if ((await sha256File(Bun.file(localArtifact))) !== metadata.sha256)\n throw new NativeReleaseRegistryError(\n \"Native release artifact digest does not match its metadata\",\n );\n const existing = await read({\n appId: metadata.appId,\n platform: metadata.platform,\n releaseId: metadata.releaseId,\n });\n let record: NativeReleaseRecord;\n let reused = false;\n if (existing) {\n if (!sameMetadata(existing.metadata, metadata))\n throw new NativeReleaseRegistryError(\n \"Published native release metadata is immutable\",\n );\n record = existing;\n reused = true;\n } else {\n const key = artifactKey(metadata);\n const storedArtifact = await options.store.head(key);\n if (storedArtifact) {\n await requireStoredArtifact(key, metadata);\n } else {\n await options.store.put(key, Bun.file(localArtifact).stream(), {\n cacheControl: \"public, max-age=31536000, immutable\",\n contentType: \"application/octet-stream\",\n maxBytes: metadata.bytes,\n metadata: {\n appId: metadata.appId,\n releaseId: metadata.releaseId,\n sha256: metadata.sha256,\n },\n signal: input.signal,\n });\n await requireStoredArtifact(key, metadata);\n }\n record = {\n artifactKey: key,\n format: NATIVE_RELEASE_REGISTRY_FORMAT,\n metadata,\n };\n const serialized = encodedJson(record);\n await options.store.put(recordKey(metadata), serialized, {\n cacheControl: \"public, max-age=31536000, immutable\",\n contentType: \"application/json\",\n maxBytes: serialized.byteLength,\n metadata: {\n releaseId: metadata.releaseId,\n sha256: sha256Bytes(serialized),\n },\n signal: input.signal,\n });\n const verified = await read({\n appId: metadata.appId,\n platform: metadata.platform,\n releaseId: metadata.releaseId,\n });\n if (!verified || JSON.stringify(verified) !== JSON.stringify(record))\n throw new NativeReleaseRegistryError(\n \"Native release publication verification failed\",\n );\n }\n const channel = input.channel\n ? await promote({\n allowUnsigned: input.allowUnsigned,\n appId: metadata.appId,\n channel: input.channel,\n platform: metadata.platform,\n releaseId: metadata.releaseId,\n signal: input.signal,\n })\n : undefined;\n\n return { ...(channel ? { channel } : {}), record, reused };\n },\n read,\n resolve: async (input) => {\n if (!APP_ID_PATTERN.test(input.appId))\n throw new NativeReleaseRegistryError(\"Native release appId is invalid\");\n const key = channelKey(input.appId, input.platform, input.channel);\n const bytes = await options.store.get(key);\n if (!bytes) return null;\n const channel = parseChannel(decodedJson(bytes));\n if (\n channel.appId !== input.appId ||\n channel.platform !== input.platform ||\n channel.channel !== input.channel\n )\n throw new NativeReleaseRegistryError(\n \"Stored native release channel identity does not match\",\n );\n const record = await read({\n appId: channel.appId,\n platform: channel.platform,\n releaseId: channel.releaseId,\n });\n if (!record || record.metadata.sha256 !== channel.sha256)\n throw new NativeReleaseRegistryError(\n \"Native release channel points to a missing or invalid release\",\n );\n\n return { channel, record };\n },\n };\n};\n"
|
|
6
|
+
],
|
|
7
|
+
"mappings": "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",
|
|
8
|
+
"debugId": "1FEEE1047DE9DB9664756E2164756E21",
|
|
9
|
+
"names": []
|
|
10
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@absolutejs/deploy",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.22.0",
|
|
4
4
|
"description": "Generic Bun-project deploy pipeline. A Target (localTarget / sshTarget) is anywhere you can exec + upload — DigitalOcean droplets, Linode, Hetzner, Vultr, your own boxes. Bundled pipeline: prepare → upload → install → build → link → restart → verify. Atomic symlink swap, release history, prune, hooks. SSH shells out to system ssh/rsync — zero ssh2 deps.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
"release"
|
|
31
31
|
],
|
|
32
32
|
"scripts": {
|
|
33
|
-
"build": "rm -rf dist && bun build src/index.ts src/releaseArtifact.ts src/infrastructure.ts src/ephemeralInfrastructure.ts src/edgeIngress.ts src/digitalocean.ts src/digitaloceanInfrastructure.ts src/digitaloceanEphemeralInfrastructure.ts src/digitaloceanIngress.ts src/gcp.ts src/gcpIngress.ts src/hetzner.ts src/hetznerInfrastructure.ts src/linode.ts src/linodeInfrastructure.ts src/vultr.ts src/vultrInfrastructure.ts src/dns.ts src/cloudflare.ts src/digitaloceanDns.ts src/hetznerDns.ts src/route53.ts src/tls.ts src/env.ts src/preview.ts src/managedPreview.ts --outdir dist --root src --sourcemap --target=bun && tsc --project tsconfig.build.json",
|
|
33
|
+
"build": "rm -rf dist && bun build src/index.ts src/releaseArtifact.ts src/nativeRelease.ts src/infrastructure.ts src/ephemeralInfrastructure.ts src/edgeIngress.ts src/digitalocean.ts src/digitaloceanInfrastructure.ts src/digitaloceanEphemeralInfrastructure.ts src/digitaloceanIngress.ts src/gcp.ts src/gcpIngress.ts src/hetzner.ts src/hetznerInfrastructure.ts src/linode.ts src/linodeInfrastructure.ts src/vultr.ts src/vultrInfrastructure.ts src/dns.ts src/cloudflare.ts src/digitaloceanDns.ts src/hetznerDns.ts src/route53.ts src/tls.ts src/env.ts src/preview.ts src/managedPreview.ts --outdir dist --root src --sourcemap --target=bun && tsc --project tsconfig.build.json",
|
|
34
34
|
"test": "bun test tests/",
|
|
35
35
|
"typecheck": "tsc --noEmit",
|
|
36
36
|
"format": "prettier --write \"./**/*.{ts,json,md}\"",
|
|
@@ -86,6 +86,11 @@
|
|
|
86
86
|
"import": "./dist/releaseArtifact.js",
|
|
87
87
|
"default": "./dist/releaseArtifact.js"
|
|
88
88
|
},
|
|
89
|
+
"./native-release": {
|
|
90
|
+
"types": "./dist/nativeRelease.d.ts",
|
|
91
|
+
"import": "./dist/nativeRelease.js",
|
|
92
|
+
"default": "./dist/nativeRelease.js"
|
|
93
|
+
},
|
|
89
94
|
"./digitalocean-ingress": {
|
|
90
95
|
"types": "./dist/digitaloceanIngress.d.ts",
|
|
91
96
|
"import": "./dist/digitaloceanIngress.js",
|