@novacraft-engineering/pulsar-cli 1.2.2 → 1.2.3
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 +3 -3
- package/pulsar.js +23 -6
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@novacraft-engineering/pulsar-cli",
|
|
3
|
-
"version": "1.2.
|
|
4
|
-
"description": "Pulsar OTA release CLI
|
|
3
|
+
"version": "1.2.3",
|
|
4
|
+
"description": "Pulsar OTA release CLI — bundle, hash, and ship React Native updates over-the-air.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"pulsar": "./pulsar.js"
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
"node": ">=18"
|
|
27
27
|
},
|
|
28
28
|
"scripts": {
|
|
29
|
-
"test": "node test/hash.test.js && node test/security.test.js"
|
|
29
|
+
"test": "node test/hash.test.js && node test/security.test.js && node test/signing.test.js"
|
|
30
30
|
},
|
|
31
31
|
"keywords": [
|
|
32
32
|
"cli",
|
package/pulsar.js
CHANGED
|
@@ -9,7 +9,10 @@ import crypto from 'node:crypto';
|
|
|
9
9
|
import {computePackageHash, fileHashes} from './hash.js';
|
|
10
10
|
|
|
11
11
|
// Code signing: sign the package hash into an RS256 JWT the client verifies against its
|
|
12
|
-
// CodePushPublicKey (Info.plist).
|
|
12
|
+
// CodePushPublicKey (Info.plist). Both native SDKs read it from <update>/CodePush/.codepushrelease
|
|
13
|
+
// and nowhere else; a signature anywhere else counts as no signature.
|
|
14
|
+
const SIGNATURE_PATH = 'CodePush/.codepushrelease';
|
|
15
|
+
|
|
13
16
|
function signCodePushJWT(privateKeyPem, contentHash) {
|
|
14
17
|
const encode = (value) => Buffer.from(JSON.stringify(value)).toString('base64url');
|
|
15
18
|
const signingInput = `${encode({alg: 'RS256', typ: 'JWT'})}.${encode({contentHash})}`;
|
|
@@ -246,8 +249,11 @@ async function buildDiffPackage(baseZip, newDir) {
|
|
|
246
249
|
fs.writeFileSync(path.join(diffDir, 'hotcodepush.json'), JSON.stringify({deletedFiles}));
|
|
247
250
|
// The signature isn't part of the content hash, so it's excluded above — but the patched bundle
|
|
248
251
|
// still needs it to verify, so carry it into the diff when the release is signed.
|
|
249
|
-
const signature = path.join(newDir,
|
|
250
|
-
if (fs.existsSync(signature))
|
|
252
|
+
const signature = path.join(newDir, SIGNATURE_PATH);
|
|
253
|
+
if (fs.existsSync(signature)) {
|
|
254
|
+
fs.mkdirSync(path.dirname(path.join(diffDir, SIGNATURE_PATH)), {recursive: true});
|
|
255
|
+
fs.copyFileSync(signature, path.join(diffDir, SIGNATURE_PATH));
|
|
256
|
+
}
|
|
251
257
|
return diffDir;
|
|
252
258
|
}
|
|
253
259
|
|
|
@@ -417,9 +423,20 @@ function buildOnce(args) {
|
|
|
417
423
|
const contentsDir = fs.mkdtempSync(path.join(os.tmpdir(), 'pulsar-build-'));
|
|
418
424
|
bundle(platform, contentsDir);
|
|
419
425
|
const packageHash = computePackageHash(contentsDir);
|
|
426
|
+
// CI secrets arrive as environment variables holding the key itself, not as files, so the key can
|
|
427
|
+
// come from PULSAR_SIGNING_KEY as well as --private-key <file>. An unset or empty secret means
|
|
428
|
+
// "don't sign", exactly as before.
|
|
420
429
|
const privateKeyPath = getFlag(args, 'private-key');
|
|
421
|
-
|
|
422
|
-
|
|
430
|
+
const signingKey = privateKeyPath ? fs.readFileSync(privateKeyPath, 'utf8') : (process.env.PULSAR_SIGNING_KEY || '').trim();
|
|
431
|
+
if (signingKey) {
|
|
432
|
+
let signature;
|
|
433
|
+
try {
|
|
434
|
+
signature = signCodePushJWT(signingKey, packageHash);
|
|
435
|
+
} catch (error) {
|
|
436
|
+
die(`could not sign with the provided key (${error.message}) — nothing was uploaded`);
|
|
437
|
+
}
|
|
438
|
+
fs.mkdirSync(path.join(contentsDir, 'CodePush'), {recursive: true});
|
|
439
|
+
fs.writeFileSync(path.join(contentsDir, SIGNATURE_PATH), signature);
|
|
423
440
|
console.log('• signed release — devices with CodePushPublicKey set will verify it');
|
|
424
441
|
}
|
|
425
442
|
const zip = fs.readFileSync(zipContents(contentsDir));
|
|
@@ -594,7 +611,7 @@ async function cmdKeys(args) {
|
|
|
594
611
|
console.log(`✓ wrote ${Object.keys(vars).length} key(s) to ${target} (${written.size} updated, ${missing.length} added)`);
|
|
595
612
|
}
|
|
596
613
|
|
|
597
|
-
export {saveConfig, browserLogin};
|
|
614
|
+
export {saveConfig, browserLogin, signCodePushJWT, SIGNATURE_PATH};
|
|
598
615
|
|
|
599
616
|
// Only dispatch when run as the CLI — importing the module (e.g. from tests) must not execute a command.
|
|
600
617
|
if (import.meta.url === `file://${process.argv[1]}`) {
|