UNPKG

@interopio/desktop-cli

Version:

CLI tool for setting up, building and packaging io.Connect Desktop projects

208 lines 8.74 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.GitHubComponentsStore = void 0; const error_handler_1 = require("../../../utils/error.handler"); const logger_1 = require("../../../utils/logger"); ; // The github release package name is in format {{component}}-{{version}} // each release has multiple assets; each asset can be platform specific // each release has a tag the same as the release name; this can be used to get a specific release class GitHubComponentsStore { baseUrl = ''; getByTagUrl = ''; logger = logger_1.Logger.getInstance(); constructor(repo = "interopio/desktop-releases/releases") { this.baseUrl = `https://api.github.com/repos/${repo}`; this.getByTagUrl = `${this.baseUrl}/tags/`; } async getAll() { const allReleases = []; let nextUrl = `${this.baseUrl}?per_page=100&page=1`; while (nextUrl) { const response = await fetch(nextUrl); if (!response.ok) { throw new Error(`GitHub API error: ${response.status} ${response.statusText}`); } const releases = await response.json(); // If no releases returned, we've reached the end if (releases.length === 0) { break; } allReleases.push(...releases); // Get next page URL from Link header const linkHeader = response.headers.get('link'); nextUrl = this.getNextPageUrl(linkHeader); // Safety check to prevent infinite loops (GitHub typically has reasonable limits) if (allReleases.length > 10000) { this.logger.warn('GitHub API pagination: Stopped at 10,000 releases to prevent excessive API calls'); break; } } return this.parseReleases(allReleases); } async download(name, version) { // Find the specific release and asset for this component const targetRelease = await this.findRelease(name, version); const asset = this.findAssetForPlatform(targetRelease, process.platform); if (!asset) { throw new Error(`No asset found for component in ${name}@${version} on platform '${process.platform}'`); } // Download the asset this.logger.info(`Downloading ${asset.name} from ${asset.browser_download_url}`); const response = await fetch(asset.browser_download_url); if (!response.ok) { throw new Error(`Failed to download asset: ${response.status} ${response.statusText}`); } // Get the data as buffer const buffer = await response.arrayBuffer(); this.logger.info(`Downloaded ${name} v${version || 'latest'} (${asset.name}) successfully!`); return { name: name, data: new Uint8Array(buffer), filename: asset.name }; } getInfo() { return `GitHub Releases Store: ${this.baseUrl}`; } /** * Find a specific release by component name and version */ async findRelease(componentName, version) { if (version === 'latest') { // If no version specified, find the latest release for this component const allReleases = await this.getAllReleases(); const componentReleases = allReleases.filter(release => release.tag_name.startsWith(`${componentName}-v`)); if (componentReleases.length === 0) { throw new Error(`No releases found for component '${componentName}'`); } // Return the most recent release (first in the list) return componentReleases[0]; } else { // If version is specified, look for exact match const targetTag = `${componentName}-v${version}`; const response = await fetch(`${this.getByTagUrl}${targetTag}`); if (response.ok) { return await response.json(); } throw new error_handler_1.CLIError(`Release not found for ${componentName} v${version} (was looking for tag: ${targetTag})`, { suggestions: [ `You are trying to install ${componentName}@${version} - check that the component name and version are correct`, `Use "desktop-cli components browse" to see available components and versions` ] }); } } /** * Find the appropriate asset for the current platform */ findAssetForPlatform(release, platform) { this.logger.info(`Finding asset for platform '${platform}' in release '${release.name}. Assets available: ${release.assets.map(a => a.name).join(', ')}'`); const platformAssets = release.assets.filter(asset => { const assetName = asset.name.toLowerCase(); switch (platform) { case 'win32': return assetName.includes('-win') || assetName.includes('.exe') || assetName.includes('.zip'); case 'darwin': return assetName.includes('darwin') || assetName.includes('mac') || assetName.includes('.dmg'); default: return false; } }); if (platformAssets.length === 0) { return null; } // Prefer more specific platform matches const specificAsset = platformAssets.find(asset => { const assetName = asset.name.toLowerCase(); if (platform === 'darwin') { // Prefer arm64 on Apple Silicon, otherwise take any darwin asset return process.arch === 'arm64' ? assetName.includes('arm64') || assetName.includes('darwin') : assetName.includes('darwin'); } return true; }); return specificAsset || platformAssets[0]; } /** * Get all releases without parsing them into components */ async getAllReleases() { const allReleases = []; let nextUrl = `${this.baseUrl}?per_page=100&page=1`; while (nextUrl) { const response = await fetch(nextUrl); if (!response.ok) { throw new Error(`GitHub API error: ${response.status} ${response.statusText}`); } const releases = await response.json(); if (releases.length === 0) { break; } allReleases.push(...releases); const linkHeader = response.headers.get('link'); nextUrl = this.getNextPageUrl(linkHeader); // Safety limit if (allReleases.length > 1000) { break; } } return allReleases; } /** * Extract the next page URL from the Link header * Link header format: <https://api.github.com/repos/owner/repo/releases?page=2>; rel="next", <https://api.github.com/repos/owner/repo/releases?page=5>; rel="last" */ getNextPageUrl(linkHeader) { if (!linkHeader) { return null; } const links = linkHeader.split(','); for (const link of links) { const match = link.match(/<([^>]+)>;\s*rel="next"/); if (match) { return match[1]; } } return null; } parseReleases(releases) { const components = []; for (const release of releases) { const componentName = this.getComponentFromName(release.name); const componentVersion = this.getVersionFromName(release.name); for (const asset of release.assets) { components.push({ name: componentName, version: componentVersion, platform: this.getPlatformFromAssetURL(asset.browser_download_url), downloadUrl: asset.browser_download_url }); } } return components; } getComponentFromName(name) { const parts = name.split('-'); return parts.length > 0 ? parts[0] : "unknown"; } getVersionFromName(name) { // name is in format {{component}}-{{version}} const parts = name.split('-'); return parts.length > 1 ? parts[1] : "unknown"; } getPlatformFromAssetURL(name) { // asset name is iocd-v10.0.0-202508271503-darwin-arm64.dmg" if (name.includes('darwin-arm64')) { return 'darwin-arm64'; } if (name.includes('darwin')) { return 'darwin'; } return 'win32'; } } exports.GitHubComponentsStore = GitHubComponentsStore; //# sourceMappingURL=github.store.js.map