@novacraft-engineering/pulsar-cli 1.2.4 → 1.3.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/package.json +1 -1
- package/pulsar.js +43 -27
package/package.json
CHANGED
package/pulsar.js
CHANGED
|
@@ -350,9 +350,35 @@ async function cmdApps() {
|
|
|
350
350
|
}
|
|
351
351
|
}
|
|
352
352
|
|
|
353
|
-
//
|
|
354
|
-
//
|
|
355
|
-
async function
|
|
353
|
+
// Sign for this app — with a local key when one was given, otherwise by asking Pulsar, which signs for
|
|
354
|
+
// anyone allowed to publish here. Each app can hold a different key, so a release train re-signs.
|
|
355
|
+
async function signForApp(app, deployment, contentsDir, packageHash, localKey) {
|
|
356
|
+
const signaturePath = path.join(contentsDir, SIGNATURE_PATH);
|
|
357
|
+
fs.rmSync(signaturePath, {force: true});
|
|
358
|
+
let signature = null;
|
|
359
|
+
if (localKey) {
|
|
360
|
+
try {
|
|
361
|
+
signature = signCodePushJWT(localKey, packageHash);
|
|
362
|
+
} catch (error) {
|
|
363
|
+
die(`could not sign with the provided key (${error.message}) — nothing was uploaded`);
|
|
364
|
+
}
|
|
365
|
+
} else {
|
|
366
|
+
({signature} = await api(`/apps/${app.id}/deployments/${deployment}/sign`, {method: 'POST', body: {packageHash}}));
|
|
367
|
+
}
|
|
368
|
+
if (!signature) {
|
|
369
|
+
console.log(`• ${app.name} has no signing key — uploading unsigned`);
|
|
370
|
+
return;
|
|
371
|
+
}
|
|
372
|
+
fs.mkdirSync(path.dirname(signaturePath), {recursive: true});
|
|
373
|
+
fs.writeFileSync(signaturePath, signature);
|
|
374
|
+
console.log(`• signed${localKey ? ' with your local key' : ' by Pulsar'}`);
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
// Sign and push an already-built bundle to one app's deployment, then generate and upload a
|
|
378
|
+
// differential patch against whatever that deployment was serving.
|
|
379
|
+
async function pushBuiltRelease(app, deployment, args, contentsDir, packageHash, localKey) {
|
|
380
|
+
await signForApp(app, deployment, contentsDir, packageHash, localKey);
|
|
381
|
+
const zip = fs.readFileSync(zipContents(contentsDir));
|
|
356
382
|
let prevLatest = null;
|
|
357
383
|
if (!args.includes('--no-diff')) {
|
|
358
384
|
try {
|
|
@@ -393,6 +419,9 @@ async function pushBuiltRelease(app, deployment, args, contentsDir, packageHash,
|
|
|
393
419
|
].filter(Boolean);
|
|
394
420
|
const reach = scopes.length ? scopes.join(' + ') : `${release.rollout}% of devices`;
|
|
395
421
|
console.log(`✓ released ${release.label} to ${app.name}/${deployment} — ${release.isDisabled ? 'DISABLED' : reach}`);
|
|
422
|
+
if (release.signed === false) {
|
|
423
|
+
console.log('⚠ UNSIGNED — this build is blocked and will not reach devices. An org owner can enable it from the dashboard.');
|
|
424
|
+
}
|
|
396
425
|
if (release.startsAt) console.log(` scheduled — goes live ${new Date(release.startsAt).toLocaleString()}`);
|
|
397
426
|
if (release.rolloutRamp) console.log(` gradual rollout — ${release.rollout}% to ${release.rolloutRamp.to}% over ${release.rolloutRamp.hours}h`);
|
|
398
427
|
|
|
@@ -418,31 +447,18 @@ async function pushBuiltRelease(app, deployment, args, contentsDir, packageHash,
|
|
|
418
447
|
return release;
|
|
419
448
|
}
|
|
420
449
|
|
|
421
|
-
// Build the JS bundle once
|
|
450
|
+
// Build the JS bundle once and return what every target app's release is made from. A local key
|
|
451
|
+
// (--private-key <file>, or PULSAR_SIGNING_KEY holding the key itself) overrides Pulsar's signing.
|
|
422
452
|
function buildOnce(args) {
|
|
423
453
|
const platform = getFlag(args, 'platform', 'ios');
|
|
454
|
+
const privateKeyPath = getFlag(args, 'private-key');
|
|
455
|
+
if (privateKeyPath && !fs.existsSync(privateKeyPath)) die(`no signing key at ${privateKeyPath} — drop --private-key to have Pulsar sign`);
|
|
456
|
+
const localKey = privateKeyPath ? fs.readFileSync(privateKeyPath, 'utf8') : (process.env.PULSAR_SIGNING_KEY || '').trim();
|
|
424
457
|
const contentsDir = fs.mkdtempSync(path.join(os.tmpdir(), 'pulsar-build-'));
|
|
425
458
|
bundle(platform, contentsDir);
|
|
426
459
|
const packageHash = computePackageHash(contentsDir);
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
// "don't sign", exactly as before.
|
|
430
|
-
const privateKeyPath = getFlag(args, 'private-key');
|
|
431
|
-
const signingKey = privateKeyPath ? fs.readFileSync(privateKeyPath, 'utf8') : (process.env.PULSAR_SIGNING_KEY || '').trim();
|
|
432
|
-
if (signingKey) {
|
|
433
|
-
let signature;
|
|
434
|
-
try {
|
|
435
|
-
signature = signCodePushJWT(signingKey, packageHash);
|
|
436
|
-
} catch (error) {
|
|
437
|
-
die(`could not sign with the provided key (${error.message}) — nothing was uploaded`);
|
|
438
|
-
}
|
|
439
|
-
fs.mkdirSync(path.join(contentsDir, 'CodePush'), {recursive: true});
|
|
440
|
-
fs.writeFileSync(path.join(contentsDir, SIGNATURE_PATH), signature);
|
|
441
|
-
console.log('• signed release — devices with CodePushPublicKey set will verify it');
|
|
442
|
-
}
|
|
443
|
-
const zip = fs.readFileSync(zipContents(contentsDir));
|
|
444
|
-
console.log(`• hash ${packageHash.slice(0, 12)}… size ${(zip.length / 1e6).toFixed(2)} MB`);
|
|
445
|
-
return {contentsDir, packageHash, zip};
|
|
460
|
+
console.log(`• hash ${packageHash.slice(0, 12)}…`);
|
|
461
|
+
return {contentsDir, packageHash, localKey};
|
|
446
462
|
}
|
|
447
463
|
|
|
448
464
|
async function cmdRelease(args) {
|
|
@@ -453,8 +469,8 @@ async function cmdRelease(args) {
|
|
|
453
469
|
if (app.os !== getFlag(args, 'platform', 'ios')) {
|
|
454
470
|
console.log(`• note: resolved app "${app.name}" is ${app.os}; bundling ${getFlag(args, 'platform', 'ios')}`);
|
|
455
471
|
}
|
|
456
|
-
const {contentsDir, packageHash,
|
|
457
|
-
await pushBuiltRelease(app, deployment, args, contentsDir, packageHash,
|
|
472
|
+
const {contentsDir, packageHash, localKey} = buildOnce(args);
|
|
473
|
+
await pushBuiltRelease(app, deployment, args, contentsDir, packageHash, localKey);
|
|
458
474
|
}
|
|
459
475
|
|
|
460
476
|
// Release the same built bundle to several apps at once (shared-codebase / white-label fleets).
|
|
@@ -479,11 +495,11 @@ async function cmdReleaseTrain(args) {
|
|
|
479
495
|
apps = apps.filter((app) => app.os === platform);
|
|
480
496
|
if (!apps.length) die(`No ${platform} apps matched.`);
|
|
481
497
|
|
|
482
|
-
const {contentsDir, packageHash,
|
|
498
|
+
const {contentsDir, packageHash, localKey} = buildOnce(args);
|
|
483
499
|
console.log(`• release train → ${apps.length} app(s): ${apps.map((app) => app.name).join(', ')}`);
|
|
484
500
|
for (const app of apps) {
|
|
485
501
|
try {
|
|
486
|
-
await pushBuiltRelease(app, deployment, args, contentsDir, packageHash,
|
|
502
|
+
await pushBuiltRelease(app, deployment, args, contentsDir, packageHash, localKey);
|
|
487
503
|
} catch (err) {
|
|
488
504
|
console.log(`✗ ${app.name}: ${err.message}`);
|
|
489
505
|
}
|