@dk/hipp 0.1.8 → 0.1.12
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 +27 -4
- package/hipp.js +66 -20
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -148,20 +148,43 @@ INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
|
|
148
148
|
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
|
149
149
|
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
150
150
|
PERFORMANCE OF THIS SOFTWARE.
|
|
151
|
+
|
|
152
|
+
|
|
151
153
|
```json
|
|
152
154
|
{
|
|
153
155
|
"origin": "git@github.com:dmytri/hipp.git",
|
|
154
|
-
"tag": "v0.1.
|
|
156
|
+
"tag": "v0.1.10"
|
|
155
157
|
}
|
|
156
158
|
```
|
|
157
159
|
|
|
158
|
-
```npx @dk/hipp @dk/hipp@0.1.
|
|
160
|
+
```npx @dk/hipp @dk/hipp@0.1.10
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
<!-- HIPP-META -->
|
|
164
|
+
```json
|
|
165
|
+
{
|
|
166
|
+
"origin": "git@github.com:dmytri/hipp.git",
|
|
167
|
+
"tag": "v0.1.11"
|
|
168
|
+
}
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
```npx @dk/hipp @dk/hipp@0.1.11
|
|
172
|
+
```
|
|
173
|
+
<!-- /HIPP-META -->
|
|
174
|
+
|
|
175
|
+
```json
|
|
176
|
+
{
|
|
177
|
+
"origin": "git@github.com:dmytri/hipp.git",
|
|
178
|
+
"tag": "v0.1.12"
|
|
179
|
+
}
|
|
180
|
+
```
|
|
159
181
|
|
|
182
|
+
```npx @dk/hipp @dk/hipp@0.1.12```
|
|
160
183
|
<!-- HIPP-MANIFEST -->
|
|
161
184
|
```json
|
|
162
185
|
{
|
|
163
|
-
"hash": "
|
|
164
|
-
"signature": "
|
|
186
|
+
"hash": "9213a0bc269441f5dc3dc07fe07eba4d127815ae78f0d7e07474547bb342ce1b",
|
|
187
|
+
"signature": "Bg5/in81S3ia4x4W2C1WzvXeZPxspCdXEcSHCyLT3aUaEF5JrSANcIerZXrgbAvDyrxsf9O2wDJ/0cZNI4KOAA=="
|
|
165
188
|
}
|
|
166
189
|
```
|
|
167
190
|
<!-- /HIPP-MANIFEST -->
|
package/hipp.js
CHANGED
|
@@ -372,6 +372,19 @@ function getTrackedFiles() {
|
|
|
372
372
|
.filter(Boolean);
|
|
373
373
|
}
|
|
374
374
|
|
|
375
|
+
function getTrackedFilesFromDir(repoDir) {
|
|
376
|
+
const out = execFileSync('git', ['ls-files', '-z'], {
|
|
377
|
+
encoding: 'buffer',
|
|
378
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
379
|
+
cwd: repoDir,
|
|
380
|
+
});
|
|
381
|
+
|
|
382
|
+
return out
|
|
383
|
+
.toString('utf8')
|
|
384
|
+
.split('\0')
|
|
385
|
+
.filter(Boolean);
|
|
386
|
+
}
|
|
387
|
+
|
|
375
388
|
function copyTrackedFiles(stageDir, files) {
|
|
376
389
|
const repoRoot = process.cwd();
|
|
377
390
|
|
|
@@ -393,6 +406,25 @@ function copyTrackedFiles(stageDir, files) {
|
|
|
393
406
|
}
|
|
394
407
|
}
|
|
395
408
|
|
|
409
|
+
function copyTrackedFilesFromDir(stageDir, repoDir, files) {
|
|
410
|
+
for (const rel of files) {
|
|
411
|
+
const src = path.join(repoDir, rel);
|
|
412
|
+
const dest = path.join(stageDir, rel);
|
|
413
|
+
const stat = fs.lstatSync(src);
|
|
414
|
+
|
|
415
|
+
fs.mkdirSync(path.dirname(dest), { recursive: true });
|
|
416
|
+
|
|
417
|
+
if (stat.isSymbolicLink()) {
|
|
418
|
+
const target = fs.readlinkSync(src);
|
|
419
|
+
fs.symlinkSync(target, dest);
|
|
420
|
+
} else if (stat.isDirectory()) {
|
|
421
|
+
fs.mkdirSync(dest, { recursive: true });
|
|
422
|
+
} else if (stat.isFile()) {
|
|
423
|
+
fs.copyFileSync(src, dest);
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
|
|
396
428
|
async function runVerify(packageSpec) {
|
|
397
429
|
let pkgName, pkgVersion;
|
|
398
430
|
if (packageSpec.startsWith('@')) {
|
|
@@ -462,45 +494,55 @@ async function runVerify(packageSpec) {
|
|
|
462
494
|
fail(`❌ JSON meta (origin/tag) not found in README`);
|
|
463
495
|
}
|
|
464
496
|
|
|
465
|
-
const
|
|
466
|
-
if (!
|
|
497
|
+
const manifestStr = extractManifestFromReadme(stagedReadme);
|
|
498
|
+
if (!manifestStr) {
|
|
467
499
|
fail(`❌ Manifest not found in README`);
|
|
468
500
|
}
|
|
469
501
|
|
|
470
|
-
const manifest = parseManifest(
|
|
502
|
+
const manifest = parseManifest(manifestStr);
|
|
471
503
|
if (!manifest || !manifest.hash || !manifest.signature) {
|
|
472
504
|
fail(`❌ Invalid manifest format`);
|
|
473
505
|
}
|
|
474
506
|
|
|
507
|
+
const originUrl = jsonMeta.origin;
|
|
508
|
+
const tag = jsonMeta.tag;
|
|
509
|
+
|
|
475
510
|
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), `hipp-verify-git-`));
|
|
511
|
+
const stageDir = fs.mkdtempSync(path.join(os.tmpdir(), `hipp-verify-stage-`));
|
|
512
|
+
|
|
476
513
|
try {
|
|
477
|
-
log.info(`🌿 Fetching from git origin...`);
|
|
514
|
+
log.info(`🌿 Fetching from git origin at tag ${tag}...`);
|
|
515
|
+
git(['clone', '--branch', tag, '--depth', '1', originUrl, tmpDir], { stdio: 'pipe' });
|
|
478
516
|
|
|
479
|
-
const
|
|
480
|
-
|
|
517
|
+
const publicKeyPath = path.join(tmpDir, 'hipp.pub');
|
|
518
|
+
if (!fs.existsSync(publicKeyPath)) {
|
|
519
|
+
fail(`❌ hipp.pub not found in git at tag ${tag}`);
|
|
520
|
+
}
|
|
481
521
|
|
|
482
|
-
|
|
522
|
+
const publicKey = fs.readFileSync(publicKeyPath, 'utf8');
|
|
523
|
+
|
|
524
|
+
log.info(`🏗️ Staging git files...`);
|
|
525
|
+
const trackedFiles = getTrackedFilesFromDir(tmpDir);
|
|
526
|
+
copyTrackedFilesFromDir(stageDir, tmpDir, trackedFiles);
|
|
483
527
|
|
|
484
|
-
const
|
|
485
|
-
if (!fs.existsSync(
|
|
528
|
+
const stagedReadmePath = path.join(stageDir, 'README.md');
|
|
529
|
+
if (!fs.existsSync(stagedReadmePath)) {
|
|
486
530
|
fail(`❌ README.md not found in git at tag ${tag}`);
|
|
487
531
|
}
|
|
488
532
|
|
|
489
|
-
|
|
490
|
-
const
|
|
533
|
+
let stagedReadme = fs.readFileSync(stagedReadmePath, 'utf8');
|
|
534
|
+
const verifyBlock = '```npx @dk/hipp ' + pkgName + '@' + tag + '```';
|
|
535
|
+
const jsonMetaStr = '```json\n{\n "origin": "' + originUrl + '",\n "tag": "' + tag + '"\n}\n```';
|
|
536
|
+
stagedReadme = stagedReadme.trimEnd() + '\n\n' + jsonMetaStr + '\n\n' + verifyBlock + '\n';
|
|
537
|
+
|
|
538
|
+
const stagedHash = computeReadmeHash(stagedReadme);
|
|
491
539
|
|
|
492
|
-
if (
|
|
540
|
+
if (stagedHash !== manifest.hash) {
|
|
493
541
|
fail(`❌ Hash mismatch: git content does not match npm manifest`);
|
|
494
542
|
}
|
|
495
543
|
|
|
496
544
|
log.success(`🔒 Content hash verified: ${manifest.hash.slice(0, 12)}...`);
|
|
497
545
|
|
|
498
|
-
const publicKeyPath = path.join(tmpDir, 'hipp.pub');
|
|
499
|
-
if (!fs.existsSync(publicKeyPath)) {
|
|
500
|
-
fail(`❌ hipp.pub not found in git at tag ${tag}`);
|
|
501
|
-
}
|
|
502
|
-
|
|
503
|
-
const publicKey = fs.readFileSync(publicKeyPath, 'utf8');
|
|
504
546
|
const signData = buildSignData(manifest.hash, originUrl, tag);
|
|
505
547
|
const signatureValid = verifySignature(signData, manifest.signature, publicKey);
|
|
506
548
|
|
|
@@ -514,6 +556,7 @@ async function runVerify(packageSpec) {
|
|
|
514
556
|
log.info(`📍 Tag: ${tag}`);
|
|
515
557
|
} finally {
|
|
516
558
|
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
559
|
+
fs.rmSync(stageDir, { recursive: true, force: true });
|
|
517
560
|
}
|
|
518
561
|
} finally {
|
|
519
562
|
fs.rmSync(tarballPath, { recursive: true, force: true });
|
|
@@ -565,9 +608,10 @@ async function run() {
|
|
|
565
608
|
log.success('📝 hipp.pub committed.');
|
|
566
609
|
}
|
|
567
610
|
|
|
611
|
+
const { rawTag, version } = getVersionFromExactTagOnHead();
|
|
612
|
+
|
|
568
613
|
ensureCleanRepo(pkg);
|
|
569
614
|
|
|
570
|
-
const { rawTag, version } = getVersionFromExactTagOnHead();
|
|
571
615
|
const refInfo = ensureMutableRefPolicy();
|
|
572
616
|
const provenance = ensureRemoteProvenance(rawTag, refInfo.head);
|
|
573
617
|
const lockInfo = ensureLockIntegrity(pkg);
|
|
@@ -608,7 +652,9 @@ async function run() {
|
|
|
608
652
|
stagedReadme = fs.readFileSync(stagedReadmePath, 'utf8');
|
|
609
653
|
}
|
|
610
654
|
|
|
611
|
-
|
|
655
|
+
const verifyBlock = '```npx @dk/hipp ' + pkg.name + '@' + version + '```';
|
|
656
|
+
const jsonMetaStr = '```json\n{\n "origin": "' + provenance.remoteUrl + '",\n "tag": "' + rawTag + '"\n}\n```';
|
|
657
|
+
stagedReadme = stagedReadme.trimEnd() + '\n\n' + jsonMetaStr + '\n\n' + verifyBlock + '\n';
|
|
612
658
|
|
|
613
659
|
const readmeHash = computeReadmeHash(stagedReadme);
|
|
614
660
|
const dataToSign = buildSignData(readmeHash, provenance.remoteUrl, rawTag);
|