From fefcbf4029d89c5628c190565ed155894565c024 Mon Sep 17 00:00:00 2001 From: crazywhalecc Date: Thu, 11 Dec 2025 15:51:32 +0800 Subject: [PATCH] Allow automatically get latest gRPC source (#909) --- config/artifact.json | 2 +- .../Artifact/Downloader/Type/Git.php | 55 ++++++++++++++++++- src/StaticPHP/Config/ConfigValidator.php | 2 +- 3 files changed, 54 insertions(+), 5 deletions(-) diff --git a/config/artifact.json b/config/artifact.json index 75ee9cfc..c8fd6621 100644 --- a/config/artifact.json +++ b/config/artifact.json @@ -399,7 +399,7 @@ "binary": "hosted", "source": { "type": "git", - "rev": "v1.75.x", + "regex": "v(?1.\\d+).x", "url": "https://github.com/grpc/grpc.git" } }, diff --git a/src/StaticPHP/Artifact/Downloader/Type/Git.php b/src/StaticPHP/Artifact/Downloader/Type/Git.php index 8b1f20d3..83c236eb 100644 --- a/src/StaticPHP/Artifact/Downloader/Type/Git.php +++ b/src/StaticPHP/Artifact/Downloader/Type/Git.php @@ -6,6 +6,8 @@ namespace StaticPHP\Artifact\Downloader\Type; use StaticPHP\Artifact\ArtifactDownloader; use StaticPHP\Artifact\Downloader\DownloadResult; +use StaticPHP\Exception\DownloaderException; +use StaticPHP\Util\FileSystem; /** git */ class Git implements DownloadTypeInterface @@ -15,8 +17,55 @@ class Git implements DownloadTypeInterface $path = DOWNLOAD_PATH . "/{$name}"; logger()->debug("Cloning git repository for {$name} from {$config['url']}"); $shallow = !$downloader->getOption('no-shallow-clone', false); - default_shell()->executeGitClone($config['url'], $config['rev'], $path, $shallow, $config['submodules'] ?? null); - $version = "dev-{$config['rev']}"; - return DownloadResult::git($name, $config, extract: $config['extract'] ?? null, version: $version); + + // direct branch clone + if (isset($config['rev'])) { + default_shell()->executeGitClone($config['url'], $config['rev'], $path, $shallow, $config['submodules'] ?? null); + $version = "dev-{$config['rev']}"; + return DownloadResult::git($name, $config, extract: $config['extract'] ?? null, version: $version); + } + if (!isset($config['regex'])) { + throw new DownloaderException('Either "rev" or "regex" must be specified for git download type.'); + } + + // regex matches branch first, we need to fetch all refs in emptyfirst + $gitdir = sys_get_temp_dir() . '/' . $name; + FileSystem::resetDir($gitdir); + $shell = PHP_OS_FAMILY === 'Windows' ? cmd(false) : shell(false); + $result = $shell->cd($gitdir) + ->exec(SPC_GIT_EXEC . ' init') + ->exec(SPC_GIT_EXEC . ' remote add origin ' . escapeshellarg($config['url'])) + ->execWithResult(SPC_GIT_EXEC . ' ls-remote origin'); + if ($result[0] !== 0) { + throw new DownloaderException("Failed to ls-remote from {$config['url']}"); + } + $refs = $result[1]; + $matched_version_branch = []; + $matched_count = 0; + + $regex = '/^' . $config['regex'] . '$/'; + foreach ($refs as $ref) { + $matches = null; + if (preg_match('/^[0-9a-f]{40}\s+refs\/heads\/(.+)$/', $ref, $matches)) { + ++$matched_count; + $branch = $matches[1]; + if (preg_match($regex, $branch, $vermatch) && isset($vermatch['version'])) { + $matched_version_branch[$vermatch['version']] = $vermatch[0]; + } + } + } + // sort versions + uksort($matched_version_branch, function ($a, $b) { + return version_compare($b, $a); + }); + if (!empty($matched_version_branch)) { + // use the highest version + $version = array_key_first($matched_version_branch); + $branch = $matched_version_branch[$version]; + logger()->info("Matched version {$version} from branch {$branch} for {$name}"); + default_shell()->executeGitClone($config['url'], $branch, $path, $shallow, $config['submodules'] ?? null); + return DownloadResult::git($name, $config, extract: $config['extract'] ?? null, version: $version); + } + throw new DownloaderException("No matching branch found for regex {$config['regex']} (checked {$matched_count} branches)."); } } diff --git a/src/StaticPHP/Config/ConfigValidator.php b/src/StaticPHP/Config/ConfigValidator.php index 4de0529f..b32f4106 100644 --- a/src/StaticPHP/Config/ConfigValidator.php +++ b/src/StaticPHP/Config/ConfigValidator.php @@ -79,7 +79,7 @@ class ConfigValidator public const array ARTIFACT_TYPE_FIELDS = [ // [required_fields, optional_fields] 'filelist' => [['url', 'regex'], ['extract']], - 'git' => [['url', 'rev'], ['extract', 'submodules']], + 'git' => [['url'], ['extract', 'submodules', 'rev', 'regex']], 'ghtagtar' => [['repo'], ['extract', 'prefer-stable', 'match']], 'ghtar' => [['repo'], ['extract', 'prefer-stable', 'match']], 'ghrel' => [['repo', 'match'], ['extract', 'prefer-stable']],