@dk/hipp 0.1.8 → 0.1.13

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.
Files changed (3) hide show
  1. package/README.md +27 -4
  2. package/hipp.js +69 -22
  3. 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.8"
156
+ "tag": "v0.1.10"
155
157
  }
156
158
  ```
157
159
 
158
- ```npx @dk/hipp @dk/hipp@0.1.8```
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.13"
179
+ }
180
+ ```
159
181
 
182
+ ```npx @dk/hipp @dk/hipp@0.1.13```
160
183
  <!-- HIPP-MANIFEST -->
161
184
  ```json
162
185
  {
163
- "hash": "57d56d76a120bbc755700c3aa1f91f756e2cda09076dc0df522beb6a22018dca",
164
- "signature": "eclO/AvbMfQMGS7g/vwX1DbB3dbm4XxWvlMFjo06KpE+d3w7KagtIv9klyjTazls74yGy3qcHhoy3i6/zWmZAg=="
186
+ "hash": "0157b775d49e9715e954bc9838f5017632c0fd2e2418d814b0e2bbb92abe3fc3",
187
+ "signature": "jyq8xJjFAt5HtqQWPPHw52A2ph7zVxEYD1QClEY3jRFcVFdUlaJj+sGatgP/FChjaI57HZM8flYUvl2r1DA5Dw=="
165
188
  }
166
189
  ```
167
190
  <!-- /HIPP-MANIFEST -->
package/hipp.js CHANGED
@@ -167,6 +167,7 @@ function extractJsonMetaFromReadme(readmeContent) {
167
167
  let jsonStart = -1;
168
168
  let braceCount = 0;
169
169
  let inJson = false;
170
+ let lastValid = null;
170
171
 
171
172
  for (let i = 0; i < lines.length; i++) {
172
173
  const line = lines[i];
@@ -186,7 +187,7 @@ function extractJsonMetaFromReadme(readmeContent) {
186
187
  try {
187
188
  const parsed = JSON.parse(jsonStr);
188
189
  if (parsed.origin && parsed.tag) {
189
- return parsed;
190
+ lastValid = parsed;
190
191
  }
191
192
  } catch {
192
193
  // continue searching
@@ -195,7 +196,7 @@ function extractJsonMetaFromReadme(readmeContent) {
195
196
  }
196
197
  }
197
198
  }
198
- return null;
199
+ return lastValid;
199
200
  }
200
201
 
201
202
  function safeStageName(name) {
@@ -372,6 +373,19 @@ function getTrackedFiles() {
372
373
  .filter(Boolean);
373
374
  }
374
375
 
376
+ function getTrackedFilesFromDir(repoDir) {
377
+ const out = execFileSync('git', ['ls-files', '-z'], {
378
+ encoding: 'buffer',
379
+ stdio: ['pipe', 'pipe', 'pipe'],
380
+ cwd: repoDir,
381
+ });
382
+
383
+ return out
384
+ .toString('utf8')
385
+ .split('\0')
386
+ .filter(Boolean);
387
+ }
388
+
375
389
  function copyTrackedFiles(stageDir, files) {
376
390
  const repoRoot = process.cwd();
377
391
 
@@ -393,6 +407,25 @@ function copyTrackedFiles(stageDir, files) {
393
407
  }
394
408
  }
395
409
 
410
+ function copyTrackedFilesFromDir(stageDir, repoDir, files) {
411
+ for (const rel of files) {
412
+ const src = path.join(repoDir, rel);
413
+ const dest = path.join(stageDir, rel);
414
+ const stat = fs.lstatSync(src);
415
+
416
+ fs.mkdirSync(path.dirname(dest), { recursive: true });
417
+
418
+ if (stat.isSymbolicLink()) {
419
+ const target = fs.readlinkSync(src);
420
+ fs.symlinkSync(target, dest);
421
+ } else if (stat.isDirectory()) {
422
+ fs.mkdirSync(dest, { recursive: true });
423
+ } else if (stat.isFile()) {
424
+ fs.copyFileSync(src, dest);
425
+ }
426
+ }
427
+ }
428
+
396
429
  async function runVerify(packageSpec) {
397
430
  let pkgName, pkgVersion;
398
431
  if (packageSpec.startsWith('@')) {
@@ -462,45 +495,55 @@ async function runVerify(packageSpec) {
462
495
  fail(`❌ JSON meta (origin/tag) not found in README`);
463
496
  }
464
497
 
465
- const manifestBase64 = extractManifestFromReadme(stagedReadme);
466
- if (!manifestBase64) {
498
+ const manifestStr = extractManifestFromReadme(stagedReadme);
499
+ if (!manifestStr) {
467
500
  fail(`❌ Manifest not found in README`);
468
501
  }
469
502
 
470
- const manifest = parseManifest(manifestBase64);
503
+ const manifest = parseManifest(manifestStr);
471
504
  if (!manifest || !manifest.hash || !manifest.signature) {
472
505
  fail(`❌ Invalid manifest format`);
473
506
  }
474
507
 
508
+ const originUrl = jsonMeta.origin;
509
+ const tag = jsonMeta.tag;
510
+
475
511
  const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), `hipp-verify-git-`));
512
+ const stageDir = fs.mkdtempSync(path.join(os.tmpdir(), `hipp-verify-stage-`));
513
+
476
514
  try {
477
- log.info(`🌿 Fetching from git origin...`);
515
+ log.info(`🌿 Fetching from git origin at tag ${tag}...`);
516
+ git(['clone', '--branch', tag, '--depth', '1', originUrl, tmpDir], { stdio: 'pipe' });
478
517
 
479
- const originUrl = jsonMeta.origin;
480
- const tag = jsonMeta.tag;
518
+ const publicKeyPath = path.join(tmpDir, 'hipp.pub');
519
+ if (!fs.existsSync(publicKeyPath)) {
520
+ fail(`❌ hipp.pub not found in git at tag ${tag}`);
521
+ }
481
522
 
482
- git(['clone', '--branch', tag, '--depth', '1', originUrl, tmpDir], { stdio: 'pipe' });
523
+ const publicKey = fs.readFileSync(publicKeyPath, 'utf8');
524
+
525
+ log.info(`🏗️ Staging git files...`);
526
+ const trackedFiles = getTrackedFilesFromDir(tmpDir);
527
+ copyTrackedFilesFromDir(stageDir, tmpDir, trackedFiles);
483
528
 
484
- const clonedReadmePath = path.join(tmpDir, 'README.md');
485
- if (!fs.existsSync(clonedReadmePath)) {
529
+ const stagedReadmePath = path.join(stageDir, 'README.md');
530
+ if (!fs.existsSync(stagedReadmePath)) {
486
531
  fail(`❌ README.md not found in git at tag ${tag}`);
487
532
  }
488
533
 
489
- const clonedReadme = fs.readFileSync(clonedReadmePath, 'utf8');
490
- const clonedHash = computeReadmeHash(clonedReadme);
534
+ let stagedReadme = fs.readFileSync(stagedReadmePath, 'utf8');
535
+ const verifyBlock = '```npx @dk/hipp ' + pkgName + '@' + tag + '```';
536
+ const jsonMetaStr = '```json\n{\n "origin": "' + originUrl + '",\n "tag": "' + tag + '"\n}\n```';
537
+ stagedReadme = stagedReadme.trimEnd() + '\n\n' + jsonMetaStr + '\n\n' + verifyBlock + '\n';
538
+
539
+ const stagedHash = computeReadmeHash(stagedReadme);
491
540
 
492
- if (clonedHash !== manifest.hash) {
541
+ if (stagedHash !== manifest.hash) {
493
542
  fail(`❌ Hash mismatch: git content does not match npm manifest`);
494
543
  }
495
544
 
496
545
  log.success(`🔒 Content hash verified: ${manifest.hash.slice(0, 12)}...`);
497
546
 
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
547
  const signData = buildSignData(manifest.hash, originUrl, tag);
505
548
  const signatureValid = verifySignature(signData, manifest.signature, publicKey);
506
549
 
@@ -514,6 +557,7 @@ async function runVerify(packageSpec) {
514
557
  log.info(`📍 Tag: ${tag}`);
515
558
  } finally {
516
559
  fs.rmSync(tmpDir, { recursive: true, force: true });
560
+ fs.rmSync(stageDir, { recursive: true, force: true });
517
561
  }
518
562
  } finally {
519
563
  fs.rmSync(tarballPath, { recursive: true, force: true });
@@ -565,9 +609,10 @@ async function run() {
565
609
  log.success('📝 hipp.pub committed.');
566
610
  }
567
611
 
612
+ const { rawTag, version } = getVersionFromExactTagOnHead();
613
+
568
614
  ensureCleanRepo(pkg);
569
615
 
570
- const { rawTag, version } = getVersionFromExactTagOnHead();
571
616
  const refInfo = ensureMutableRefPolicy();
572
617
  const provenance = ensureRemoteProvenance(rawTag, refInfo.head);
573
618
  const lockInfo = ensureLockIntegrity(pkg);
@@ -608,7 +653,9 @@ async function run() {
608
653
  stagedReadme = fs.readFileSync(stagedReadmePath, 'utf8');
609
654
  }
610
655
 
611
- stagedReadme += '\n```json\n{\n "origin": "' + provenance.remoteUrl + '",\n "tag": "' + rawTag + '"\n}\n```\n\n```npx @dk/hipp ' + pkg.name + '@' + version + '```\n\n';
656
+ const verifyBlock = '```npx @dk/hipp ' + pkg.name + '@' + version + '```';
657
+ const jsonMetaStr = '```json\n{\n "origin": "' + provenance.remoteUrl + '",\n "tag": "' + rawTag + '"\n}\n```';
658
+ stagedReadme = stagedReadme.trimEnd() + '\n\n' + jsonMetaStr + '\n\n' + verifyBlock + '\n';
612
659
 
613
660
  const readmeHash = computeReadmeHash(stagedReadme);
614
661
  const dataToSign = buildSignData(readmeHash, provenance.remoteUrl, rawTag);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dk/hipp",
3
- "version": "0.1.8",
3
+ "version": "0.1.13",
4
4
  "description": "High Integrity Package Publisher",
5
5
  "main": "hipp.js",
6
6
  "bin": {