add download apis to stream zip from blob storage

This commit is contained in:
Rob Herley
2023-08-21 21:23:54 +00:00
committed by GitHub
parent 7b617c260d
commit 9b383229c1
4 changed files with 294 additions and 5 deletions

View File

@@ -1,10 +1,10 @@
import {ArtifactClient, Client} from './internal/client'
import {UploadOptions, UploadResponse} from './internal/shared/interfaces'
/**
* Exported functionality that we want to expose for any users of @actions/artifact
*/
export {ArtifactClient, UploadOptions, UploadResponse}
export * from './internal/shared/interfaces'
export {ArtifactClient}
export function create(): ArtifactClient {
return Client.create()

View File

@@ -1,7 +1,52 @@
import path from 'path'
import fs from 'fs/promises'
import {PathLike} from 'fs'
import github from '@actions/github'
import core from '@actions/core'
import httpClient from '@actions/http-client'
import unzipper from 'unzipper'
import {
DownloadArtifactOptions,
DownloadArtifactResponse
} from '../shared/interfaces'
import {getUserAgentString} from '../shared/user-agent'
const scrubQueryParameters = (url: string): string => {
const parsed = new URL(url)
parsed.search = ''
return parsed.toString()
}
async function exists(path: string): Promise<boolean> {
try {
await fs.access(path)
return true
} catch (error) {
if (error.code === 'ENOENT') {
return false
} else {
throw error
}
}
}
async function streamExtract(url: string, directory: PathLike): Promise<void> {
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,
@@ -10,5 +55,54 @@ export async function downloadArtifact(
token: string,
options?: DownloadArtifactOptions
): Promise<DownloadArtifactResponse> {
throw new Error('Not implemented')
let downloadPath = options?.path || process.cwd() // TODO: make this align with GITHUB_WORKSPACE
if (options?.createArtifactFolder) {
downloadPath = path.join(downloadPath, 'my-artifact') // TODO: need to pass artifact name
}
if (!(await exists(downloadPath))) {
core.debug(`Creating artifact folder: ${downloadPath}`)
await fs.mkdir(downloadPath, {recursive: true})
} else {
core.warning(`Artifact 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}
}