import path from 'path' import fs from 'fs/promises' import * as github from '@actions/github' import * as core from '@actions/core' import * as httpClient from '@actions/http-client' import unzipper from 'unzipper' import { DownloadArtifactOptions, DownloadArtifactResponse } from '../shared/interfaces' import {getUserAgentString} from '../shared/user-agent' import {getGitHubWorkspaceDir} from '../shared/config' const scrubQueryParameters = (url: string): string => { const parsed = new URL(url) parsed.search = '' return parsed.toString() } async function exists(path: string): Promise { try { await fs.access(path) return true } catch (error) { if (error.code === 'ENOENT') { return false } else { throw error } } } async function streamExtract(url: string, directory: string): Promise { const client = new httpClient.HttpClient(getUserAgentString()) const response = await client.get(url) if (response.message.statusCode !== 200) { throw new Error( `Unexpected HTTP response from blob storage: ${response.message.statusCode} ${response.message.statusMessage}` ) } return new Promise((resolve, reject) => { response.message .pipe(unzipper.Extract({path: directory})) .on('finish', resolve) .on('error', reject) }) } export async function downloadArtifact( artifactId: number, repositoryOwner: string, repositoryName: string, token: string, options?: DownloadArtifactOptions ): Promise { let downloadPath = options?.path || getGitHubWorkspaceDir() if (options?.createArtifactFolderName) { downloadPath = path.join(downloadPath, options?.createArtifactFolderName) } if (!(await exists(downloadPath))) { core.debug( `Artifact destination folder does not exist, creating: ${downloadPath}` ) await fs.mkdir(downloadPath, {recursive: true}) } else { core.debug(`Artifact destination folder already exists: ${downloadPath}`) } const api = github.getOctokit(token) core.info( `Downloading artifact '${artifactId}' from '${repositoryOwner}/${repositoryName}'` ) const {headers, status} = await api.rest.actions.downloadArtifact({ owner: repositoryOwner, repo: repositoryName, artifact_id: artifactId, archive_format: 'zip', request: { redirect: 'manual' } }) if (status !== 302) { throw new Error(`Unable to download artifact. Unexpected status: ${status}`) } const {location} = headers if (!location) { throw new Error(`Unable to redirect to artifact download url`) } core.info( `Redirecting to blob download url: ${scrubQueryParameters(location)}` ) try { core.info(`Starting download of artifact to: ${downloadPath}`) await streamExtract(location, downloadPath) core.info(`Artifact download completed successfully.`) } catch (error) { throw new Error(`Unable to download and extract artifact: ${error.message}`) } return {success: true, downloadPath} }