@@ -208,7 +208,7 @@ public static function downloadFile(string $name, string $url, string $filename,
208
208
if ($ download_as === SPC_DOWNLOAD_PRE_BUILT ) {
209
209
$ name = self ::getPreBuiltLockName ($ name );
210
210
}
211
- self ::lockSource ($ name , ['source_type ' => ' archive ' , 'filename ' => $ filename , 'move_path ' => $ move_path , 'lock_as ' => $ download_as ]);
211
+ self ::lockSource ($ name , ['source_type ' => SPC_SOURCE_ARCHIVE , 'filename ' => $ filename , 'move_path ' => $ move_path , 'lock_as ' => $ download_as ]);
212
212
}
213
213
214
214
/**
@@ -231,6 +231,9 @@ public static function lockSource(string $name, array $data): void
231
231
} else {
232
232
$ lock = json_decode (FileSystem::readFile (DOWNLOAD_PATH . '/.lock.json ' ), true ) ?? [];
233
233
}
234
+ // calculate hash
235
+ $ hash = self ::getLockSourceHash ($ data );
236
+ $ data ['hash ' ] = $ hash ;
234
237
$ lock [$ name ] = $ data ;
235
238
FileSystem::writeFile (DOWNLOAD_PATH . '/.lock.json ' , json_encode ($ lock , JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE ));
236
239
}
@@ -278,7 +281,7 @@ public static function downloadGit(string $name, string $url, string $branch, ?s
278
281
}
279
282
// Lock
280
283
logger ()->debug ("Locking git source {$ name }" );
281
- self ::lockSource ($ name , ['source_type ' => ' dir ' , 'dirname ' => $ name , 'move_path ' => $ move_path , 'lock_as ' => $ lock_as ]);
284
+ self ::lockSource ($ name , ['source_type ' => SPC_SOURCE_GIT , 'dirname ' => $ name , 'move_path ' => $ move_path , 'lock_as ' => $ lock_as ]);
282
285
283
286
/*
284
287
// 复制目录过去
@@ -371,6 +374,16 @@ public static function downloadPackage(string $name, ?array $pkg = null, bool $f
371
374
SPC_DOWNLOAD_PRE_BUILT
372
375
);
373
376
break ;
377
+ case 'local ' :
378
+ // Local directory, do nothing, just lock it
379
+ logger ()->debug ("Locking local source {$ name }" );
380
+ self ::lockSource ($ name , [
381
+ 'source_type ' => SPC_SOURCE_LOCAL ,
382
+ 'dirname ' => $ pkg ['dirname ' ],
383
+ 'move_path ' => $ pkg ['extract ' ] ?? null ,
384
+ 'lock_as ' => SPC_DOWNLOAD_PACKAGE ,
385
+ ]);
386
+ break ;
374
387
case 'custom ' : // Custom download method, like API-based download or other
375
388
$ classes = FileSystem::getClassesPsr4 (ROOT_DIR . '/src/SPC/store/source ' , 'SPC\store\source ' );
376
389
foreach ($ classes as $ class ) {
@@ -477,6 +490,16 @@ public static function downloadSource(string $name, ?array $source = null, bool
477
490
$ download_as
478
491
);
479
492
break ;
493
+ case 'local ' :
494
+ // Local directory, do nothing, just lock it
495
+ logger ()->debug ("Locking local source {$ name }" );
496
+ self ::lockSource ($ name , [
497
+ 'source_type ' => SPC_SOURCE_LOCAL ,
498
+ 'dirname ' => $ source ['dirname ' ],
499
+ 'move_path ' => $ source ['extract ' ] ?? null ,
500
+ 'lock_as ' => $ download_as ,
501
+ ]);
502
+ break ;
480
503
case 'custom ' : // Custom download method, like API-based download or other
481
504
if (isset ($ source ['func ' ]) && is_callable ($ source ['func ' ])) {
482
505
$ source ['name ' ] = $ name ;
@@ -594,6 +617,43 @@ public static function getPreBuiltLockName(string $source): string
594
617
return "{$ source }- " . PHP_OS_FAMILY . '- ' . getenv ('GNU_ARCH ' ) . '- ' . (getenv ('SPC_LIBC ' ) ?: 'default ' ) . '- ' . (SystemUtil::getLibcVersionIfExists () ?? 'default ' );
595
618
}
596
619
620
+ /**
621
+ * Get the hash of the lock source based on the lock options.
622
+ *
623
+ * @param array $lock_options Lock options
624
+ * @return string Hash of the lock source
625
+ * @throws RuntimeException
626
+ */
627
+ public static function getLockSourceHash (array $ lock_options ): string
628
+ {
629
+ $ result = match ($ lock_options ['source_type ' ]) {
630
+ SPC_SOURCE_ARCHIVE => sha1_file (DOWNLOAD_PATH . '/ ' . $ lock_options ['filename ' ]),
631
+ SPC_SOURCE_GIT => exec ('cd ' . escapeshellarg (DOWNLOAD_PATH . '/ ' . $ lock_options ['dirname ' ]) . ' && ' . SPC_GIT_EXEC . ' rev-parse HEAD ' ),
632
+ SPC_SOURCE_LOCAL => 'LOCAL HASH IS ALWAYS DIFFERENT ' ,
633
+ default => filter_var (getenv ('SPC_IGNORE_BAD_HASH ' ), FILTER_VALIDATE_BOOLEAN ) ? '' : throw new RuntimeException ("Unknown source type: {$ lock_options ['source_type ' ]}" ),
634
+ };
635
+ if ($ result === false && !filter_var (getenv ('SPC_IGNORE_BAD_HASH ' ), FILTER_VALIDATE_BOOLEAN )) {
636
+ throw new RuntimeException ("Failed to get hash for source: {$ lock_options ['source_type ' ]}" );
637
+ }
638
+ return $ result ?: '' ;
639
+ }
640
+
641
+ /**
642
+ * @param array $lock_options Lock options
643
+ * @param string $destination Target directory
644
+ * @throws FileSystemException
645
+ * @throws RuntimeException
646
+ */
647
+ public static function putLockSourceHash (array $ lock_options , string $ destination ): void
648
+ {
649
+ $ hash = self ::getLockSourceHash ($ lock_options );
650
+ if ($ lock_options ['source_type ' ] === SPC_SOURCE_LOCAL ) {
651
+ logger ()->debug ("Source [ {$ lock_options ['dirname ' ]}] is local, no hash will be written. " );
652
+ return ;
653
+ }
654
+ FileSystem::writeFile ("{$ destination }/.spc-hash " , $ hash );
655
+ }
656
+
597
657
/**
598
658
* Register CTRL+C event for different OS.
599
659
*
@@ -640,8 +700,8 @@ private static function isAlreadyDownloaded(string $name, bool $force, int $down
640
700
// If lock file exists, skip downloading for source mode
641
701
if (!$ force && $ download_as === SPC_DOWNLOAD_SOURCE && isset ($ lock [$ name ])) {
642
702
if (
643
- $ lock [$ name ]['source_type ' ] === ' archive ' && file_exists (DOWNLOAD_PATH . '/ ' . $ lock [$ name ]['filename ' ]) ||
644
- $ lock [$ name ]['source_type ' ] === ' dir ' && is_dir (DOWNLOAD_PATH . '/ ' . $ lock [$ name ]['dirname ' ])
703
+ $ lock [$ name ]['source_type ' ] === SPC_SOURCE_ARCHIVE && file_exists (DOWNLOAD_PATH . '/ ' . $ lock [$ name ]['filename ' ]) ||
704
+ $ lock [$ name ]['source_type ' ] === SPC_SOURCE_GIT && is_dir (DOWNLOAD_PATH . '/ ' . $ lock [$ name ]['dirname ' ])
645
705
) {
646
706
logger ()->notice ("Source [ {$ name }] already downloaded: " . ($ lock [$ name ]['filename ' ] ?? $ lock [$ name ]['dirname ' ]));
647
707
return true ;
@@ -652,8 +712,8 @@ private static function isAlreadyDownloaded(string $name, bool $force, int $down
652
712
if (!$ force && $ download_as === SPC_DOWNLOAD_PRE_BUILT && isset ($ lock [$ lock_name = self ::getPreBuiltLockName ($ name )])) {
653
713
// lock name with env
654
714
if (
655
- $ lock [$ lock_name ]['source_type ' ] === ' archive ' && file_exists (DOWNLOAD_PATH . '/ ' . $ lock [$ lock_name ]['filename ' ]) ||
656
- $ lock [$ lock_name ]['source_type ' ] === ' dir ' && is_dir (DOWNLOAD_PATH . '/ ' . $ lock [$ lock_name ]['dirname ' ])
715
+ $ lock [$ lock_name ]['source_type ' ] === SPC_SOURCE_ARCHIVE && file_exists (DOWNLOAD_PATH . '/ ' . $ lock [$ lock_name ]['filename ' ]) ||
716
+ $ lock [$ lock_name ]['source_type ' ] === SPC_SOURCE_GIT && is_dir (DOWNLOAD_PATH . '/ ' . $ lock [$ lock_name ]['dirname ' ])
657
717
) {
658
718
logger ()->notice ("Pre-built content [ {$ name }] already downloaded: " . ($ lock [$ lock_name ]['filename ' ] ?? $ lock [$ lock_name ]['dirname ' ]));
659
719
return true ;
0 commit comments