Rearranged the code and added CLI that installs tools locally.
All checks were successful
Test Action / test (push) Successful in 3s
All checks were successful
Test Action / test (push) Successful in 3s
This commit is contained in:
155
src/index.ts
155
src/index.ts
@@ -3,61 +3,31 @@ import * as tc from '@actions/tool-cache';
|
||||
import * as path from 'path';
|
||||
import * as os from 'os';
|
||||
import * as fs from 'fs';
|
||||
|
||||
function findBinary(dir: string, pattern: string | RegExp, debug: boolean): string | undefined {
|
||||
const items = fs.readdirSync(dir);
|
||||
if (debug) {
|
||||
core.info(`Searching for binary in ${dir}...`);
|
||||
items.forEach(item => core.info(` - ${item}`));
|
||||
}
|
||||
|
||||
for (const item of items) {
|
||||
const fullPath = path.join(dir, item);
|
||||
const stat = fs.statSync(fullPath);
|
||||
if (stat.isDirectory()) {
|
||||
const found = findBinary(fullPath, pattern, debug);
|
||||
if (found) return found;
|
||||
} else {
|
||||
let isMatch = false;
|
||||
if (pattern instanceof RegExp) {
|
||||
isMatch = pattern.test(item);
|
||||
} else {
|
||||
isMatch = item === pattern;
|
||||
// On Windows, also check for .exe extension if the pattern doesn't have it
|
||||
if (!isMatch && process.platform === 'win32' && !pattern.toLowerCase().endsWith('.exe')) {
|
||||
isMatch = item.toLowerCase() === `${pattern.toLowerCase()}.exe`;
|
||||
}
|
||||
}
|
||||
if (isMatch) return fullPath;
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
import { getPlatformInfo } from './core/platform';
|
||||
import { getMatchingAsset } from './core/matcher';
|
||||
import { findBinary } from './core/finder';
|
||||
import { fetchLatestRelease } from './core/downloader';
|
||||
|
||||
async function run() {
|
||||
try {
|
||||
const repository = core.getInput('repository', { required: true });
|
||||
let fileName = core.getInput('file-name');
|
||||
const fileNameInput = core.getInput('file-name');
|
||||
const binaryInput = core.getInput('binary-name');
|
||||
const fileType = core.getInput('file-type') || 'archive';
|
||||
const updateCache = core.getInput('update-cache') || 'false';
|
||||
const debug = core.getBooleanInput('debug');
|
||||
const token = core.getInput('token') || process.env.GITHUB_TOKEN;
|
||||
|
||||
// Detect system and architecture
|
||||
const platform = os.platform(); // 'linux', 'darwin', 'win32'
|
||||
const arch = os.arch(); // 'x64', 'arm64'
|
||||
|
||||
const platformInfo = getPlatformInfo();
|
||||
const toolName = repository.split('/').pop() || repository;
|
||||
|
||||
// Rule for update-cache: 'false' means use ANY cached version if available
|
||||
if (updateCache === 'false') {
|
||||
const allVersions = tc.findAllVersions(toolName, arch);
|
||||
const allVersions = tc.findAllVersions(toolName, platformInfo.arch);
|
||||
if (allVersions.length > 0) {
|
||||
// Simple sort to pick the 'latest' local version
|
||||
const latestVersion = allVersions.sort().pop();
|
||||
if (latestVersion) {
|
||||
const cachedDir = tc.find(toolName, latestVersion, arch);
|
||||
const cachedDir = tc.find(toolName, latestVersion, platformInfo.arch);
|
||||
if (cachedDir) {
|
||||
core.info(`Found ${toolName} version ${latestVersion} in local cache (update-cache: false)`);
|
||||
core.addPath(cachedDir);
|
||||
@@ -67,105 +37,21 @@ async function run() {
|
||||
}
|
||||
}
|
||||
|
||||
const systemPatterns: Record<string, string> = {
|
||||
linux: 'linux',
|
||||
darwin: '(darwin|macos|mac)',
|
||||
win32: '(windows|win)'
|
||||
};
|
||||
|
||||
const archPatterns: Record<string, string> = {
|
||||
x64: '(x86_64|x64|amd64)',
|
||||
arm64: '(aarch64|arm64)'
|
||||
};
|
||||
|
||||
const systemPattern = systemPatterns[platform] || platform;
|
||||
const archPattern = archPatterns[arch] || arch;
|
||||
|
||||
let extPattern: string;
|
||||
if (fileType === 'archive') {
|
||||
extPattern = '\\.(zip|tar\\.gz|tar|tgz|7z)';
|
||||
} else if (fileType === 'package') {
|
||||
extPattern = '\\.(deb|rpm|pkg)';
|
||||
} else {
|
||||
extPattern = fileType;
|
||||
}
|
||||
|
||||
const url = `https://api.github.com/repos/${repository}/releases/latest`;
|
||||
const headers: Record<string, string> = {
|
||||
'Accept': 'application/vnd.github.v3+json',
|
||||
'User-Agent': 'setup-github-release-action'
|
||||
};
|
||||
if (token) {
|
||||
headers['Authorization'] = `token ${token}`;
|
||||
}
|
||||
|
||||
core.info(`Fetching latest release information for ${repository}...`);
|
||||
const response = await fetch(url, { headers });
|
||||
if (!response.ok) {
|
||||
throw new Error(`Failed to fetch release: ${response.statusText} (${response.status})`);
|
||||
}
|
||||
const release = await fetchLatestRelease(repository, token);
|
||||
const asset = getMatchingAsset(release.assets, platformInfo, {
|
||||
fileName: fileNameInput,
|
||||
fileType: fileType
|
||||
});
|
||||
|
||||
const data: any = await response.json();
|
||||
let asset;
|
||||
core.info(`Selected asset: ${asset.name}`);
|
||||
|
||||
if (!fileName) {
|
||||
// Rule 1: Default matching rule
|
||||
const pattern = `${systemPattern}[_-]${archPattern}.*${extPattern}$`;
|
||||
const regex = new RegExp(pattern, 'i');
|
||||
core.info(`No file-name provided. Using default pattern: ${pattern}`);
|
||||
const matchingAssets = data.assets.filter((a: any) => regex.test(a.name));
|
||||
if (matchingAssets.length === 0) {
|
||||
throw new Error(`No assets matched the default criteria: ${pattern}`);
|
||||
}
|
||||
if (matchingAssets.length > 1) {
|
||||
throw new Error(`Multiple assets matched the default criteria: ${matchingAssets.map((a: any) => a.name).join(', ')}`);
|
||||
}
|
||||
asset = matchingAssets[0];
|
||||
} else if (fileName.startsWith('~')) {
|
||||
// Rule 3: Regex matching rule
|
||||
let pattern = fileName.substring(1);
|
||||
const hasSystem = pattern.includes('{{SYSTEM}}');
|
||||
const hasArch = pattern.includes('{{ARCH}}');
|
||||
const hasExt = pattern.includes('{{EXT_PATTERN}}');
|
||||
const hasEnd = pattern.endsWith('$');
|
||||
|
||||
if (!hasSystem && !hasArch && !hasExt && !hasEnd) {
|
||||
pattern += `.*{{SYSTEM}}[_-]{{ARCH}}.*{{EXT_PATTERN}}$`;
|
||||
} else if (hasSystem && hasArch && !hasExt && !hasEnd) {
|
||||
pattern += `.*{{EXT_PATTERN}}$`;
|
||||
}
|
||||
|
||||
const finalPattern = pattern
|
||||
.replace(/{{SYSTEM}}/g, systemPattern)
|
||||
.replace(/{{ARCH}}/g, archPattern)
|
||||
.replace(/{{EXT_PATTERN}}/g, extPattern);
|
||||
|
||||
const regex = new RegExp(finalPattern, 'i');
|
||||
core.info(`Using regex pattern: ${finalPattern}`);
|
||||
const matchingAssets = data.assets.filter((a: any) => regex.test(a.name));
|
||||
if (matchingAssets.length === 0) {
|
||||
throw new Error(`No assets matched the regex: ${finalPattern}`);
|
||||
}
|
||||
if (matchingAssets.length > 1) {
|
||||
throw new Error(`Multiple assets matched the criteria: ${matchingAssets.map((a: any) => a.name).join(', ')}`);
|
||||
}
|
||||
asset = matchingAssets[0];
|
||||
} else {
|
||||
// Literal matching rule
|
||||
core.info(`Using literal match for: ${fileName}`);
|
||||
asset = data.assets.find((a: any) => a.name === fileName);
|
||||
}
|
||||
|
||||
if (!asset) {
|
||||
throw new Error(`No asset found matching the criteria in release ${data.tag_name}`);
|
||||
}
|
||||
|
||||
const version = data.tag_name.replace(/^v/, '');
|
||||
const version = release.tag_name.replace(/^v/, '');
|
||||
const binaryName = binaryInput || toolName;
|
||||
|
||||
// Check if the tool is already in the cache (if not 'always' update)
|
||||
if (updateCache !== 'always') {
|
||||
const cachedDir = tc.find(toolName, version, arch);
|
||||
const cachedDir = tc.find(toolName, version, platformInfo.arch);
|
||||
if (cachedDir) {
|
||||
core.info(`Found ${toolName} version ${version} in cache at ${cachedDir}`);
|
||||
core.addPath(cachedDir);
|
||||
@@ -176,7 +62,7 @@ async function run() {
|
||||
const downloadUrl = asset.browser_download_url;
|
||||
core.info(`Downloading ${asset.name} from ${downloadUrl}...`);
|
||||
|
||||
const downloadPath = await tc.downloadTool(downloadUrl);
|
||||
const downloadPath = await tc.downloadTool(downloadUrl, undefined, token ? `token ${token}` : undefined);
|
||||
|
||||
const nameLower = asset.name.toLowerCase();
|
||||
let toolDir: string;
|
||||
@@ -200,7 +86,6 @@ async function run() {
|
||||
}
|
||||
fs.renameSync(downloadPath, destPath);
|
||||
|
||||
// Make it executable on Linux/macOS
|
||||
if (process.platform !== 'win32') {
|
||||
fs.chmodSync(destPath, '755');
|
||||
}
|
||||
@@ -216,14 +101,14 @@ async function run() {
|
||||
core.info(`Searching for binary named: ${binaryName}`);
|
||||
}
|
||||
|
||||
const binaryPath = findBinary(toolDir, binaryPattern, debug);
|
||||
const binaryPath = findBinary(toolDir, binaryPattern, debug, (msg) => core.info(msg));
|
||||
if (!binaryPath) {
|
||||
throw new Error(`Could not find binary "${binaryName}" in the extracted asset.`);
|
||||
}
|
||||
|
||||
// The tool directory is the one containing the binary
|
||||
toolDir = path.dirname(binaryPath);
|
||||
core.info(`Binary found at ${binaryPath}. Setting tool directory to ${toolDir}`);
|
||||
core.info(`Binary found at ${binaryPath}.`);
|
||||
|
||||
// Make binary executable just in case it's not
|
||||
if (process.platform !== 'win32') {
|
||||
@@ -231,7 +116,7 @@ async function run() {
|
||||
}
|
||||
|
||||
// Cache the tool
|
||||
const finalCachedDir = await tc.cacheDir(toolDir, toolName, version, arch);
|
||||
const finalCachedDir = await tc.cacheDir(toolDir, toolName, version, platformInfo.arch);
|
||||
core.info(`Cached ${toolName} version ${version} to ${finalCachedDir}`);
|
||||
|
||||
core.addPath(finalCachedDir);
|
||||
|
||||
Reference in New Issue
Block a user