import logging from '../logging' import process from "process" import fs from 'fs' import * as download from './download' import * as semver from 'semver' export async function formatToLatestPatchVersion( toolName: string, prefix: string, inputVersion: string ): Promise { // 使用正则表达式匹配 x.y.z 形式的版本号 const versionRegex = /^\d+\.\d+\.\d+$/; if (versionRegex.test(inputVersion)) { return prefix + inputVersion; } const toolRegistryUrl = process.env['FLOW_TOOL_REGISTRY_URL'] if(!toolRegistryUrl) { throw new Error('env var "FLOW_TOOL_REGISTRY_URL" must be set') } const downloadUrl = `${toolRegistryUrl}/${toolName}/versions.txt` logging.info(`Downloading from ${downloadUrl}`) let downloadedTmpFilePath: string | null = null try { downloadedTmpFilePath = await download.download(downloadUrl, {}) logging.info(`Downloaded to ${downloadedTmpFilePath}`) const versionData = fs.readFileSync(downloadedTmpFilePath, 'utf8') const versionLines = versionData.trim().split('\n') const matchedVersions = getMatchedVersions(versionLines, toolName, inputVersion, prefix) if (matchedVersions.length === 0) { throw new Error(`Get latest patch version for ${toolName}-${inputVersion} failed.`) } const latestVersion = prefix + matchedVersions[matchedVersions.length - 1] return Promise.resolve(latestVersion) } catch (e) { throw new Error(`Get tool ${toolName}-${inputVersion} from ${downloadUrl} error: ${e}.`) } finally { if (downloadedTmpFilePath) { fs.rmSync(downloadedTmpFilePath) } } } function getMatchedVersions(versionLines: string[], toolName: string, inputVersion: string, prefix: string): string[] { // 统一去掉inputVersion的前缀,便于后面匹配过滤 const formattedInputVersion = inputVersion.startsWith(prefix) ? inputVersion.slice(prefix.length) : inputVersion; // 基于主版本和次版本构建一个正则表达式,该正则会匹配第三位补丁版本号 const baseVersionPattern = formattedInputVersion.replace(/(\d+)(\.\d+)?/, '$1$2.*'); // 过滤版本列表,查找符合格式的最新版本 return versionLines .map(line => { const cleanLine = line.replace(new RegExp(`^${prefix}|/$`, 'g'), '') // 移除前缀和末尾的斜杠(如果有) return semver.valid(cleanLine) ? cleanLine : null // 检查是否符合semver版本号格式 x.y.z }) .filter((line): line is string => Boolean(line)) // 检查每个 line 并排除掉所有 null 值 .filter(line => new RegExp(`^${baseVersionPattern}`).test(line)) .sort(semver.compare) }