UNPKG

@shopify/cli-kit

Version:

A set of utilities, interfaces, and models that are common across all the platform features

102 lines 4.9 kB
/* eslint-disable @typescript-eslint/no-non-null-assertion */ import { err, ok } from './result.js'; import { fetch } from './http.js'; import { writeFile, mkdir, inTemporaryDirectory, moveFile, chmod } from './fs.js'; import { dirname, joinPath } from './path.js'; import { runWithTimer } from './metadata.js'; import { AbortError } from './error.js'; import { outputContent, outputDebug, outputToken } from '../../public/node/output.js'; class GitHubClientError extends Error { // eslint-disable-next-line @typescript-eslint/no-explicit-any constructor(url, statusCode, bodyJson) { super(`The request to GitHub API URL ${url} failed with status code ${statusCode} and the following error message: ${bodyJson.message}`); } } /** * Given a GitHub repository it obtains the latest release. * @param owner - Repository owner (e.g., shopify) * @param repo - Repository name (e.g., cli) * @param options - Options */ export async function getLatestGitHubRelease(owner, repo, options = { filter: () => true }) { outputDebug(outputContent `Getting the latest release of GitHub repository ${owner}/${repo}...`); const url = `https://api.github.com/repos/${owner}/${repo}/releases`; const fetchResult = await fetch(url); // eslint-disable-next-line @typescript-eslint/no-explicit-any const jsonBody = await fetchResult.json(); if (fetchResult.status !== 200) { throw new GitHubClientError(url, fetchResult.status, jsonBody); } return jsonBody.find(options.filter); } /** * Given a GitHub repository URL, it parses it and returns its coomponents. * @param url - The GitHub repository URL */ export function parseGitHubRepositoryURL(url) { const match = /^(?:(?:https:\/\/)?([^:/]+\.[^:/]+)\/|git@([^:/]+)[:/]|([^/]+):)?([^/\s]+)\/([^/\s#]+)(?:((?:\/[^/\s#]+)+))?(?:\/)?(?:#(.+))?/.exec(url); if (!match) { const exampleFormats = [ 'github:user/repo', 'user/repo/subdirectory', 'git@github.com:user/repo', 'user/repo#dev', 'https://github.com/user/repo', ]; return err(new Error(`Parsing the url ${url} failed. Supported formats are ${exampleFormats.join(', ')}.`)); } const site = match[1] ?? match[2] ?? match[3] ?? 'github.com'; const normalizedSite = site === 'github' ? 'github.com' : site; const user = match[4]; const name = match[5].replace(/\.git$/, ''); // eslint-disable-next-line @typescript-eslint/no-non-null-asserted-optional-chain const subDirectory = match[6]?.slice(1); const ref = match[7]; const branch = ref ? `#${ref}` : ''; const ssh = `git@${normalizedSite}:${user}/${name}`; const http = `https://${normalizedSite}/${user}/${name}`; const full = ['https:/', normalizedSite, user, name, subDirectory].join('/').concat(branch); return ok({ full, site: normalizedSite, user, name, ref, subDirectory, ssh, http }); } /** * Given a GitHub repository URL it parses it and extracts the branch, file path, * and base URL components * @param reference - A GitHub repository URL (e.g. https://github.com/Shopify/cli/blob/main/package.json) */ export function parseGitHubRepositoryReference(reference) { const url = new URL(reference); const branch = url.hash ? url.hash.slice(1) : undefined; const [_, user, repo, ...repoPath] = url.pathname.split('/'); const filePath = repoPath.length > 0 ? repoPath.join('/') : undefined; return { baseURL: `${url.origin}/${user}/${repo}`, branch, filePath, }; } export async function downloadGitHubRelease(repo, version, assetName, targetPath) { const url = `https://github.com/${repo}/releases/download/${version}/${assetName}`; return runWithTimer('cmd_all_timing_network_ms')(async () => { outputDebug(outputContent `Downloading ${outputToken.link(assetName, url)}`); await inTemporaryDirectory(async (tmpDir) => { const tempPath = joinPath(tmpDir, assetName); let response; try { response = await fetch(url, undefined, 'slow-request'); if (!response.ok) { throw new AbortError(`Failed to download ${assetName}: ${response.statusText}`); } } catch (error) { throw new AbortError(`Failed to download ${assetName}: ${error instanceof Error ? error.message : 'unknown error'}`); } const buffer = await response.arrayBuffer(); await writeFile(tempPath, Buffer.from(buffer)); await chmod(tempPath, 0o755); await mkdir(dirname(targetPath)); await moveFile(tempPath, targetPath); }); outputDebug(outputContent `${outputToken.successIcon()} Successfully downloaded ${outputToken.path(targetPath)}`); }); } //# sourceMappingURL=github.js.map