Refactor lock component to a single class (#773)

This commit is contained in:
Jerry Ma 2025-06-18 14:05:43 +08:00 committed by GitHub
parent 1c439a01a1
commit ba0ea5b40a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 284 additions and 147 deletions

View File

@ -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];

View File

@ -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);
}

View File

@ -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) {

View File

@ -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('<info>Removing old PHP source...</info>');
unlink($file);
LockFile::put('php-src', null);
}
// Download new PHP source

View File

@ -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;
}

View File

@ -208,34 +208,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]);
}
/**
@ -281,7 +254,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]);
/*
// 复制目录过去
@ -377,7 +350,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,
@ -493,7 +466,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,
@ -617,43 +590,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.
*
@ -689,33 +625,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;
}
}

227
src/SPC/store/LockFile.php Normal file
View File

@ -0,0 +1,227 @@
<?php
declare(strict_types=1);
namespace SPC\store;
use SPC\exception\FileSystemException;
use SPC\exception\RuntimeException;
use SPC\exception\WrongUsageException;
class LockFile
{
private const string LOCK_FILE = DOWNLOAD_PATH . '/.lock.json';
private static ?array $lock_file_content = null;
/**
* Get a lock entry by its name.
*
* @param string $lock_name Lock name to retrieve
* @return null|array{
* source_type: string,
* filename: ?string,
* dirname: ?string,
* move_path: ?string,
* lock_as: int,
* hash: string
* } Returns the lock entry as an associative array if it exists, or null if it does not
*/
public static function get(string $lock_name): ?array
{
self::init();
// Return the specific lock entry if it exists, otherwise return an empty array
$result = self::$lock_file_content[$lock_name] ?? null;
// Add old `dir` compatibility
if (($result['source_type'] ?? null) === 'dir') {
logger()->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.");
}
}
}
}

View File

@ -33,10 +33,11 @@ class PackageManager
// Download package
Downloader::downloadPackage($pkg_name, $config, $force);
// 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

View File

@ -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']}"),
};
}
}

View File

@ -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);