diff --git a/src/SPC/builder/BuilderBase.php b/src/SPC/builder/BuilderBase.php index daa27765..6303ffec 100644 --- a/src/SPC/builder/BuilderBase.php +++ b/src/SPC/builder/BuilderBase.php @@ -12,6 +12,7 @@ use SPC\exception\RuntimeException; use SPC\exception\WrongUsageException; use SPC\store\Config; use SPC\store\FileSystem; +use SPC\store\LockFile; use SPC\store\SourceManager; use SPC\util\CustomExt; @@ -350,15 +351,11 @@ abstract class BuilderBase public function getPHPVersionFromArchive(?string $file = null): false|string { if ($file === null) { - $lock = file_exists(DOWNLOAD_PATH . '/.lock.json') ? file_get_contents(DOWNLOAD_PATH . '/.lock.json') : false; - if ($lock === false) { - return false; - } - $lock = json_decode($lock, true); - $file = $lock['php-src']['filename'] ?? null; - if ($file === null) { + $lock = LockFile::get('php-src'); + if ($lock === null) { return false; } + $file = LockFile::getLockFullPath($lock); } if (preg_match('/php-(\d+\.\d+\.\d+(?:RC\d+)?)\.tar\.(?:gz|bz2|xz)/', $file, $match)) { return $match[1]; diff --git a/src/SPC/builder/LibraryBase.php b/src/SPC/builder/LibraryBase.php index ac2d8c5c..a130e8a8 100644 --- a/src/SPC/builder/LibraryBase.php +++ b/src/SPC/builder/LibraryBase.php @@ -10,6 +10,7 @@ use SPC\exception\WrongUsageException; use SPC\store\Config; use SPC\store\Downloader; use SPC\store\FileSystem; +use SPC\store\LockFile; use SPC\store\SourceManager; use SPC\util\GlobalValueTrait; @@ -46,12 +47,11 @@ abstract class LibraryBase */ public function setup(bool $force = false): int { - $lock = json_decode(FileSystem::readFile(DOWNLOAD_PATH . '/.lock.json'), true) ?? []; $source = Config::getLib(static::NAME, 'source'); // if source is locked as pre-built, we just tryInstall it $pre_built_name = Downloader::getPreBuiltLockName($source); - if (isset($lock[$pre_built_name]) && ($lock[$pre_built_name]['lock_as'] ?? SPC_DOWNLOAD_SOURCE) === SPC_DOWNLOAD_PRE_BUILT) { - return $this->tryInstall($lock[$pre_built_name], $force); + if (($lock = LockFile::get($pre_built_name)) && $lock['lock_as'] === SPC_DOWNLOAD_PRE_BUILT) { + return $this->tryInstall($lock, $force); } return $this->tryBuild($force); } diff --git a/src/SPC/command/DeleteDownloadCommand.php b/src/SPC/command/DeleteDownloadCommand.php index 12b0b420..0306de4c 100644 --- a/src/SPC/command/DeleteDownloadCommand.php +++ b/src/SPC/command/DeleteDownloadCommand.php @@ -9,6 +9,7 @@ use SPC\exception\FileSystemException; use SPC\exception\WrongUsageException; use SPC\store\Downloader; use SPC\store\FileSystem; +use SPC\store\LockFile; use Symfony\Component\Console\Attribute\AsCommand; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; @@ -46,29 +47,29 @@ class DeleteDownloadCommand extends BaseCommand return static::SUCCESS; } $chosen_sources = $sources; - $lock = json_decode(FileSystem::readFile(DOWNLOAD_PATH . '/.lock.json'), true) ?? []; $deleted_sources = []; foreach ($chosen_sources as $source) { $source = trim($source); foreach ([$source, Downloader::getPreBuiltLockName($source)] as $name) { - if (isset($lock[$name])) { + if (LockFile::get($name)) { $deleted_sources[] = $name; } } } foreach ($deleted_sources as $lock_name) { + $lock = LockFile::get($lock_name); // remove download file/dir if exists - if ($lock[$lock_name]['source_type'] === SPC_SOURCE_ARCHIVE) { - if (file_exists($path = FileSystem::convertPath(DOWNLOAD_PATH . '/' . $lock[$lock_name]['filename']))) { + if ($lock['source_type'] === SPC_SOURCE_ARCHIVE) { + if (file_exists($path = FileSystem::convertPath(DOWNLOAD_PATH . '/' . $lock['filename']))) { logger()->info('Deleting file ' . $path); unlink($path); } else { logger()->warning("Source/Package [{$lock_name}] file not found, skip deleting file."); } } else { - if (is_dir($path = FileSystem::convertPath(DOWNLOAD_PATH . '/' . $lock[$lock_name]['dirname']))) { + if (is_dir($path = FileSystem::convertPath(DOWNLOAD_PATH . '/' . $lock['dirname']))) { logger()->info('Deleting dir ' . $path); FileSystem::removeDir($path); } else { @@ -76,9 +77,8 @@ class DeleteDownloadCommand extends BaseCommand } } // remove locked sources - unset($lock[$lock_name]); + LockFile::put($lock_name, null); } - FileSystem::writeFile(DOWNLOAD_PATH . '/.lock.json', json_encode($lock, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE)); logger()->info('Delete success!'); return static::SUCCESS; } catch (DownloaderException $e) { diff --git a/src/SPC/command/SwitchPhpVersionCommand.php b/src/SPC/command/SwitchPhpVersionCommand.php index ef463ef4..30ee0c79 100644 --- a/src/SPC/command/SwitchPhpVersionCommand.php +++ b/src/SPC/command/SwitchPhpVersionCommand.php @@ -7,6 +7,7 @@ namespace SPC\command; use SPC\store\Config; use SPC\store\Downloader; use SPC\store\FileSystem; +use SPC\store\LockFile; use Symfony\Component\Console\Attribute\AsCommand; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputOption; @@ -40,16 +41,9 @@ class SwitchPhpVersionCommand extends BaseCommand } } - // detect if downloads/.lock.json exists - $lock_file = DOWNLOAD_PATH . '/.lock.json'; - // parse php-src part of lock file - $lock_data = json_decode(file_get_contents($lock_file), true); - // get php-src downloaded file name - $php_src = $lock_data['php-src']; - $file = DOWNLOAD_PATH . '/' . ($php_src['filename'] ?? '.donot.delete.me'); - if (file_exists($file)) { + if (LockFile::isLockFileExists('php-src')) { $this->output->writeln('Removing old PHP source...'); - unlink($file); + LockFile::put('php-src', null); } // Download new PHP source diff --git a/src/SPC/command/dev/PackLibCommand.php b/src/SPC/command/dev/PackLibCommand.php index 924c3171..d0d9797e 100644 --- a/src/SPC/command/dev/PackLibCommand.php +++ b/src/SPC/command/dev/PackLibCommand.php @@ -14,6 +14,7 @@ use SPC\exception\RuntimeException; use SPC\exception\WrongUsageException; use SPC\store\Config; use SPC\store\FileSystem; +use SPC\store\LockFile; use SPC\util\DependencyUtil; use Symfony\Component\Console\Attribute\AsCommand; use Symfony\Component\Console\Input\InputArgument; @@ -47,9 +48,8 @@ class PackLibCommand extends BuildCommand $lib->setup(); } else { // Get lock info - $lock = json_decode(file_get_contents(DOWNLOAD_PATH . '/.lock.json'), true) ?? []; $source = Config::getLib($lib->getName(), 'source'); - if (!isset($lock[$source]) || ($lock[$source]['lock_as'] ?? SPC_DOWNLOAD_SOURCE) === SPC_DOWNLOAD_PRE_BUILT) { + if (($lock = LockFile::get($source)) === null || ($lock['lock_as'] === SPC_DOWNLOAD_PRE_BUILT)) { logger()->critical("The library {$lib->getName()} is downloaded as pre-built, we need to build it instead of installing pre-built."); return static::FAILURE; } diff --git a/src/SPC/store/Downloader.php b/src/SPC/store/Downloader.php index e5cc6aae..9435202f 100644 --- a/src/SPC/store/Downloader.php +++ b/src/SPC/store/Downloader.php @@ -209,34 +209,7 @@ class Downloader if ($download_as === SPC_DOWNLOAD_PRE_BUILT) { $name = self::getPreBuiltLockName($name); } - self::lockSource($name, ['source_type' => SPC_SOURCE_ARCHIVE, 'filename' => $filename, 'move_path' => $move_path, 'lock_as' => $download_as]); - } - - /** - * Try to lock source. - * - * @param string $name Source name - * @param array{ - * source_type: string, - * dirname: ?string, - * filename: ?string, - * move_path: ?string, - * lock_as: int - * } $data Source data - * @throws FileSystemException - */ - public static function lockSource(string $name, array $data): void - { - if (!file_exists(FileSystem::convertPath(DOWNLOAD_PATH . '/.lock.json'))) { - $lock = []; - } else { - $lock = json_decode(FileSystem::readFile(DOWNLOAD_PATH . '/.lock.json'), true) ?? []; - } - // calculate hash - $hash = self::getLockSourceHash($data); - $data['hash'] = $hash; - $lock[$name] = $data; - FileSystem::writeFile(DOWNLOAD_PATH . '/.lock.json', json_encode($lock, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE)); + LockFile::lockSource($name, ['source_type' => SPC_SOURCE_ARCHIVE, 'filename' => $filename, 'move_path' => $move_path, 'lock_as' => $download_as]); } /** @@ -282,7 +255,7 @@ class Downloader } // Lock logger()->debug("Locking git source {$name}"); - self::lockSource($name, ['source_type' => SPC_SOURCE_GIT, 'dirname' => $name, 'move_path' => $move_path, 'lock_as' => $lock_as]); + LockFile::lockSource($name, ['source_type' => SPC_SOURCE_GIT, 'dirname' => $name, 'move_path' => $move_path, 'lock_as' => $lock_as]); /* // 复制目录过去 @@ -378,7 +351,7 @@ class Downloader case 'local': // Local directory, do nothing, just lock it logger()->debug("Locking local source {$name}"); - self::lockSource($name, [ + LockFile::lockSource($name, [ 'source_type' => SPC_SOURCE_LOCAL, 'dirname' => $pkg['dirname'], 'move_path' => $pkg['extract'] ?? null, @@ -497,7 +470,7 @@ class Downloader case 'local': // Local directory, do nothing, just lock it logger()->debug("Locking local source {$name}"); - self::lockSource($name, [ + LockFile::lockSource($name, [ 'source_type' => SPC_SOURCE_LOCAL, 'dirname' => $source['dirname'], 'move_path' => $source['extract'] ?? null, @@ -621,43 +594,6 @@ class Downloader return "{$source}-" . PHP_OS_FAMILY . '-' . getenv('GNU_ARCH') . '-' . (getenv('SPC_LIBC') ?: 'default') . '-' . (SystemUtil::getLibcVersionIfExists() ?? 'default'); } - /** - * Get the hash of the lock source based on the lock options. - * - * @param array $lock_options Lock options - * @return string Hash of the lock source - * @throws RuntimeException - */ - public static function getLockSourceHash(array $lock_options): string - { - $result = match ($lock_options['source_type']) { - SPC_SOURCE_ARCHIVE => sha1_file(DOWNLOAD_PATH . '/' . $lock_options['filename']), - SPC_SOURCE_GIT => exec('cd ' . escapeshellarg(DOWNLOAD_PATH . '/' . $lock_options['dirname']) . ' && ' . SPC_GIT_EXEC . ' rev-parse HEAD'), - SPC_SOURCE_LOCAL => 'LOCAL HASH IS ALWAYS DIFFERENT', - default => filter_var(getenv('SPC_IGNORE_BAD_HASH'), FILTER_VALIDATE_BOOLEAN) ? '' : throw new RuntimeException("Unknown source type: {$lock_options['source_type']}"), - }; - if ($result === false && !filter_var(getenv('SPC_IGNORE_BAD_HASH'), FILTER_VALIDATE_BOOLEAN)) { - throw new RuntimeException("Failed to get hash for source: {$lock_options['source_type']}"); - } - return $result ?: ''; - } - - /** - * @param array $lock_options Lock options - * @param string $destination Target directory - * @throws FileSystemException - * @throws RuntimeException - */ - public static function putLockSourceHash(array $lock_options, string $destination): void - { - $hash = self::getLockSourceHash($lock_options); - if ($lock_options['source_type'] === SPC_SOURCE_LOCAL) { - logger()->debug("Source [{$lock_options['dirname']}] is local, no hash will be written."); - return; - } - FileSystem::writeFile("{$destination}/.spc-hash", $hash); - } - /** * Register CTRL+C event for different OS. * @@ -693,32 +629,24 @@ class Downloader /** * @throws FileSystemException + * @throws WrongUsageException */ private static function isAlreadyDownloaded(string $name, bool $force, int $download_as = SPC_DOWNLOAD_SOURCE): bool { - if (!file_exists(DOWNLOAD_PATH . '/.lock.json')) { - $lock = []; - } else { - $lock = json_decode(FileSystem::readFile(DOWNLOAD_PATH . '/.lock.json'), true) ?? []; - } - // If lock file exists, skip downloading for source mode - if (!$force && $download_as === SPC_DOWNLOAD_SOURCE && isset($lock[$name])) { - if ( - $lock[$name]['source_type'] === SPC_SOURCE_ARCHIVE && file_exists(DOWNLOAD_PATH . '/' . $lock[$name]['filename']) || - $lock[$name]['source_type'] === SPC_SOURCE_GIT && is_dir(DOWNLOAD_PATH . '/' . $lock[$name]['dirname']) - ) { - logger()->notice("Source [{$name}] already downloaded: " . ($lock[$name]['filename'] ?? $lock[$name]['dirname'])); + // If the lock file exists, skip downloading for source mode + $lock_item = LockFile::get($name); + if (!$force && $download_as === SPC_DOWNLOAD_SOURCE && $lock_item !== null) { + if (file_exists($path = LockFile::getLockFullPath($lock_item))) { + logger()->notice("Source [{$name}] already downloaded: {$path}"); return true; } } - // If lock file exists for current arch and glibc target, skip downloading - if (!$force && $download_as === SPC_DOWNLOAD_PRE_BUILT && isset($lock[$lock_name = self::getPreBuiltLockName($name)])) { + $lock_name = self::getPreBuiltLockName($name); + $lock_item = LockFile::get($lock_name); + if (!$force && $download_as === SPC_DOWNLOAD_PRE_BUILT && $lock_item !== null) { // lock name with env - if ( - $lock[$lock_name]['source_type'] === SPC_SOURCE_ARCHIVE && file_exists(DOWNLOAD_PATH . '/' . $lock[$lock_name]['filename']) || - $lock[$lock_name]['source_type'] === SPC_SOURCE_GIT && is_dir(DOWNLOAD_PATH . '/' . $lock[$lock_name]['dirname']) - ) { - logger()->notice("Pre-built content [{$name}] already downloaded: " . ($lock[$lock_name]['filename'] ?? $lock[$lock_name]['dirname'])); + if (file_exists($path = LockFile::getLockFullPath($lock_item))) { + logger()->notice("Pre-built content [{$name}] already downloaded: {$path}"); return true; } } diff --git a/src/SPC/store/LockFile.php b/src/SPC/store/LockFile.php new file mode 100644 index 00000000..45e3a94c --- /dev/null +++ b/src/SPC/store/LockFile.php @@ -0,0 +1,227 @@ +warning("Lock entry for '{$lock_name}' has 'source_type' set to 'dir', which is deprecated. Please re-download your dependencies."); + $result['source_type'] = SPC_SOURCE_GIT; + } + + return $result; + } + + /** + * Check if a lock file exists for a given lock name. + * + * @param string $lock_name Lock name to check + */ + public static function isLockFileExists(string $lock_name): bool + { + return match (self::get($lock_name)['source_type'] ?? null) { + SPC_SOURCE_ARCHIVE => file_exists(DOWNLOAD_PATH . '/' . (self::get($lock_name)['filename'] ?? '.never-exist-file')), + SPC_SOURCE_GIT, SPC_SOURCE_LOCAL => is_dir(DOWNLOAD_PATH . '/' . (self::get($lock_name)['dirname'] ?? '.never-exist-dir')), + default => false, + }; + } + + /** + * Put a lock entry into the lock file. + * + * @param string $lock_name Lock name to set or remove + * @param null|array $lock_content lock content to set, or null to remove the lock entry + * @throws FileSystemException + * @throws WrongUsageException + */ + public static function put(string $lock_name, ?array $lock_content): void + { + self::init(); + + $data = self::$lock_file_content; + if ($lock_content === null && isset($data[$lock_name])) { + self::removeLockFileIfExists($data[$lock_name]); + unset($data[$lock_name]); + } else { + $data[$lock_name] = $lock_content; + } + + // Write the updated lock data back to the file + file_put_contents(self::LOCK_FILE, json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES)); + } + + /** + * Get the full path of a lock file or directory based on the lock options. + * + * @param array $lock_options lock item options, must contain 'source_type', 'filename' or 'dirname' + * @return string the absolute path to the lock file or directory + * @throws WrongUsageException + */ + public static function getLockFullPath(array $lock_options): string + { + return match ($lock_options['source_type']) { + SPC_SOURCE_ARCHIVE => FileSystem::isRelativePath($lock_options['filename']) ? (DOWNLOAD_PATH . '/' . $lock_options['filename']) : $lock_options['filename'], + SPC_SOURCE_GIT, SPC_SOURCE_LOCAL => FileSystem::isRelativePath($lock_options['dirname']) ? (DOWNLOAD_PATH . '/' . $lock_options['dirname']) : $lock_options['dirname'], + default => throw new WrongUsageException("Unknown source type: {$lock_options['source_type']}"), + }; + } + + public static function getExtractPath(string $lock_name, string $default_path): ?string + { + $lock = self::get($lock_name); + if ($lock === null) { + return null; + } + + // If move_path is set, use it; otherwise, use the default extract directory + if (isset($lock['move_path'])) { + if (FileSystem::isRelativePath($lock['move_path'])) { + // If move_path is relative, prepend the default extract directory + return match ($lock['lock_as']) { + SPC_DOWNLOAD_SOURCE, SPC_DOWNLOAD_PRE_BUILT => FileSystem::convertPath(SOURCE_PATH . '/' . $lock['move_path']), + SPC_DOWNLOAD_PACKAGE => FileSystem::convertPath(PKG_ROOT_PATH . '/' . $lock['move_path']), + default => throw new WrongUsageException("Unknown lock type: {$lock['lock_as']}"), + }; + } + return FileSystem::convertPath($lock['move_path']); + } + return FileSystem::convertPath($default_path); + } + + /** + * Get the hash of the lock source based on the lock options. + * + * @param array $lock_options Lock options + * @return string Hash of the lock source + * @throws RuntimeException + */ + public static function getLockSourceHash(array $lock_options): string + { + $result = match ($lock_options['source_type']) { + SPC_SOURCE_ARCHIVE => sha1_file(DOWNLOAD_PATH . '/' . $lock_options['filename']), + SPC_SOURCE_GIT => exec('cd ' . escapeshellarg(DOWNLOAD_PATH . '/' . $lock_options['dirname']) . ' && ' . SPC_GIT_EXEC . ' rev-parse HEAD'), + SPC_SOURCE_LOCAL => 'LOCAL HASH IS ALWAYS DIFFERENT', + default => filter_var(getenv('SPC_IGNORE_BAD_HASH'), FILTER_VALIDATE_BOOLEAN) ? '' : throw new RuntimeException("Unknown source type: {$lock_options['source_type']}"), + }; + if ($result === false && !filter_var(getenv('SPC_IGNORE_BAD_HASH'), FILTER_VALIDATE_BOOLEAN)) { + throw new RuntimeException("Failed to get hash for source: {$lock_options['source_type']}"); + } + return $result ?: ''; + } + + /** + * @param array $lock_options Lock options + * @param string $destination Target directory + * @throws FileSystemException + * @throws RuntimeException + */ + public static function putLockSourceHash(array $lock_options, string $destination): void + { + $hash = LockFile::getLockSourceHash($lock_options); + if ($lock_options['source_type'] === SPC_SOURCE_LOCAL) { + logger()->debug("Source [{$lock_options['dirname']}] is local, no hash will be written."); + return; + } + FileSystem::writeFile("{$destination}/.spc-hash", $hash); + } + + /** + * Try to lock source with hash. + * + * @param string $name Source name + * @param array{ + * source_type: string, + * dirname: ?string, + * filename: ?string, + * move_path: ?string, + * lock_as: int + * } $data Source data + * @throws FileSystemException + * @throws RuntimeException + * @throws WrongUsageException + */ + public static function lockSource(string $name, array $data): void + { + // calculate hash + $hash = LockFile::getLockSourceHash($data); + $data['hash'] = $hash; + self::put($name, $data); + } + + private static function init(): void + { + if (self::$lock_file_content === null) { + // Initialize the lock file content if it hasn't been loaded yet + if (!file_exists(self::LOCK_FILE)) { + logger()->debug('Lock file does not exist: ' . self::LOCK_FILE . ', initializing empty lock file.'); + self::$lock_file_content = []; + file_put_contents(self::LOCK_FILE, json_encode(self::$lock_file_content, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES)); + } else { + $file_content = file_get_contents(self::LOCK_FILE); + self::$lock_file_content = json_decode($file_content, true); + if (self::$lock_file_content === null) { + throw new \RuntimeException('Failed to decode lock file: ' . self::LOCK_FILE); + } + } + } + } + + /** + * Remove the lock file or directory if it exists. + * + * @param array $lock_options lock item options, must contain 'source_type', 'filename' or 'dirname' + * @throws WrongUsageException + * @throws FileSystemException + */ + private static function removeLockFileIfExists(array $lock_options): void + { + if ($lock_options['source_type'] === SPC_SOURCE_ARCHIVE) { + $path = self::getLockFullPath($lock_options); + if (file_exists($path)) { + logger()->info('Removing file ' . $path); + unlink($path); + } else { + logger()->debug("Lock file [{$lock_options['filename']}] not found, skip removing file."); + } + } else { + $path = self::getLockFullPath($lock_options); + if (is_dir($path)) { + logger()->info('Removing directory ' . $path); + FileSystem::removeDir($path); + } else { + logger()->debug("Lock directory [{$lock_options['dirname']}] not found, skip removing directory."); + } + } + } +} diff --git a/src/SPC/store/PackageManager.php b/src/SPC/store/PackageManager.php index 7e8ae3fd..d5c1043d 100644 --- a/src/SPC/store/PackageManager.php +++ b/src/SPC/store/PackageManager.php @@ -48,10 +48,11 @@ class PackageManager return; } // After download, read lock file name - $lock = json_decode(FileSystem::readFile(DOWNLOAD_PATH . '/.lock.json'), true); - $source_type = $lock[$pkg_name]['source_type']; - $filename = DOWNLOAD_PATH . '/' . ($lock[$pkg_name]['filename'] ?? $lock[$pkg_name]['dirname']); - $extract = $lock[$pkg_name]['move_path'] === null ? (PKG_ROOT_PATH . '/' . $pkg_name) : $lock[$pkg_name]['move_path']; + $lock = LockFile::get($pkg_name); + $source_type = $lock['source_type']; + $filename = LockFile::getLockFullPath($lock); + $extract = LockFile::getExtractPath($pkg_name, PKG_ROOT_PATH . '/' . $pkg_name); + FileSystem::extractPackage($pkg_name, $source_type, $filename, $extract); // if contains extract-files, we just move this file to destination, and remove extract dir diff --git a/src/SPC/store/SourceManager.php b/src/SPC/store/SourceManager.php index dd419e35..02d07263 100644 --- a/src/SPC/store/SourceManager.php +++ b/src/SPC/store/SourceManager.php @@ -17,11 +17,6 @@ class SourceManager */ public static function initSource(?array $sources = null, ?array $libs = null, ?array $exts = null, bool $source_only = false): void { - if (!file_exists(DOWNLOAD_PATH . '/.lock.json')) { - throw new WrongUsageException('Download lock file "downloads/.lock.json" not found, maybe you need to download sources first ?'); - } - $lock = json_decode(FileSystem::readFile(DOWNLOAD_PATH . '/.lock.json'), true); - $sources_extracted = []; // source check exist if (is_array($sources)) { @@ -56,8 +51,8 @@ class SourceManager } // check source downloaded $pre_built_name = Downloader::getPreBuiltLockName($source); - if ($source_only || !isset($lock[$pre_built_name])) { - if (!isset($lock[$source])) { + if ($source_only || LockFile::get($pre_built_name) === null) { + if (LockFile::get($source) === null) { throw new WrongUsageException("Source [{$source}] not downloaded or not locked, you should download it first !"); } $lock_name = $source; @@ -65,20 +60,23 @@ class SourceManager $lock_name = $pre_built_name; } + $lock_content = LockFile::get($lock_name); + // check source dir exist - $check = $lock[$lock_name]['move_path'] === null ? (SOURCE_PATH . '/' . $source) : (SOURCE_PATH . '/' . $lock[$lock_name]['move_path']); + $check = LockFile::getExtractPath($lock_name, SOURCE_PATH . '/' . $source); + // $check = $lock[$lock_name]['move_path'] === null ? (SOURCE_PATH . '/' . $source) : (SOURCE_PATH . '/' . $lock[$lock_name]['move_path']); if (!is_dir($check)) { logger()->debug('Extracting source [' . $source . '] to ' . $check . ' ...'); - $filename = self::getSourceFullPath($lock[$lock_name]); - FileSystem::extractSource($source, $lock[$lock_name]['source_type'], $filename, $lock[$lock_name]['move_path']); - Downloader::putLockSourceHash($lock[$lock_name], $check); + $filename = LockFile::getLockFullPath($lock_content); + FileSystem::extractSource($source, $lock_content['source_type'], $filename, $check); + LockFile::putLockSourceHash($lock_content, $check); continue; } // if a lock file does not have hash, calculate with the current source (backward compatibility) - if (!isset($lock[$lock_name]['hash'])) { - $hash = Downloader::getLockSourceHash($lock[$lock_name]); + if (!isset($lock_content['hash'])) { + $hash = LockFile::getLockSourceHash($lock_content); } else { - $hash = $lock[$lock_name]['hash']; + $hash = $lock_content['hash']; } // when source already extracted, detect if the extracted source hash is the same as the lock file one @@ -90,18 +88,10 @@ class SourceManager // if not, remove the source dir and extract again logger()->notice("Source [{$source}] hash mismatch, removing old source dir and extracting again ..."); FileSystem::removeDir($check); - $filename = self::getSourceFullPath($lock[$lock_name]); - FileSystem::extractSource($source, $lock[$lock_name]['source_type'], $filename, $lock[$lock_name]['move_path']); - Downloader::putLockSourceHash($lock[$lock_name], $check); + $filename = LockFile::getLockFullPath($lock_content); + $move_path = LockFile::getExtractPath($lock_name, SOURCE_PATH . '/' . $source); + FileSystem::extractSource($source, $lock_content['source_type'], $filename, $move_path); + LockFile::putLockSourceHash($lock_content, $check); } } - - private static function getSourceFullPath(array $lock_options): string - { - return match ($lock_options['source_type']) { - SPC_SOURCE_ARCHIVE => FileSystem::isRelativePath($lock_options['filename']) ? (DOWNLOAD_PATH . '/' . $lock_options['filename']) : $lock_options['filename'], - SPC_SOURCE_GIT, SPC_SOURCE_LOCAL => FileSystem::isRelativePath($lock_options['dirname']) ? (DOWNLOAD_PATH . '/' . $lock_options['dirname']) : $lock_options['dirname'], - default => throw new WrongUsageException("Unknown source type: {$lock_options['source_type']}"), - }; - } } diff --git a/tests/SPC/store/DownloaderTest.php b/tests/SPC/store/DownloaderTest.php index 5c15d42e..43de396a 100644 --- a/tests/SPC/store/DownloaderTest.php +++ b/tests/SPC/store/DownloaderTest.php @@ -7,6 +7,7 @@ namespace SPC\Tests\store; use PHPUnit\Framework\TestCase; use SPC\exception\WrongUsageException; use SPC\store\Downloader; +use SPC\store\LockFile; /** * @internal @@ -57,7 +58,7 @@ class DownloaderTest extends TestCase public function testLockSource() { - Downloader::lockSource('fake-file', ['source_type' => SPC_SOURCE_ARCHIVE, 'filename' => 'fake-file-name', 'move_path' => 'fake-path', 'lock_as' => 'fake-lock-as']); + LockFile::lockSource('fake-file', ['source_type' => SPC_SOURCE_ARCHIVE, 'filename' => 'fake-file-name', 'move_path' => 'fake-path', 'lock_as' => 'fake-lock-as']); $this->assertFileExists(DOWNLOAD_PATH . '/.lock.json'); $json = json_decode(file_get_contents(DOWNLOAD_PATH . '/.lock.json'), true); $this->assertIsArray($json);