import {createWriteStream} from 'fs' import * as core from '@actions/core' import * as github from '@actions/github' import {GitHub} from '@actions/github/lib/utils' import {EventPayloads} from '@octokit/webhooks' import * as stream from 'stream' import {promisify} from 'util' import got from 'got' const asyncStream = promisify(stream.pipeline) export function getCheckRunContext(): {sha: string; runId: number} { if (github.context.eventName === 'workflow_run') { core.info('Action was triggered by workflow_run: using SHA and RUN_ID from triggering workflow') const event = github.context.payload as EventPayloads.WebhookPayloadWorkflowRun if (!event.workflow_run) { throw new Error("Event of type 'workflow_run' is missing 'workflow_run' field") } return { sha: event.workflow_run.head_commit.id, runId: event.workflow_run.id } } const runId = github.context.runId if (github.context.payload.pull_request) { core.info(`Action was triggered by ${github.context.eventName}: using SHA from head of source branch`) const pr = github.context.payload.pull_request as EventPayloads.WebhookPayloadPullRequestPullRequest return {sha: pr.head.sha, runId} } return {sha: github.context.sha, runId} } export async function downloadArtifact( octokit: InstanceType, artifactId: number, fileName: string, token: string ): Promise { core.startGroup(`Downloading artifact ${fileName}`) try { core.info(`Artifact ID: ${artifactId}`) const req = octokit.actions.downloadArtifact.endpoint({ ...github.context.repo, artifact_id: artifactId, archive_format: 'zip' }) const headers = { Authorization: `Bearer ${token}` } const resp = await got(req.url, { headers, followRedirect: false }) core.info(`Fetch artifact URL: ${resp.statusCode} ${resp.statusMessage}`) if (resp.statusCode !== 302) { throw new Error('Fetch artifact URL failed: received unexpected status code') } const url = resp.headers.location if (url === undefined) { const receivedHeaders = Object.keys(resp.headers) core.info(`Received headers: ${receivedHeaders.join(', ')}`) throw new Error('Location header was not found in API response') } if (typeof url !== 'string') { throw new Error(`Location header has unexpected value: ${url}`) } const downloadStream = got.stream(url, {headers}) const fileWriterStream = createWriteStream(fileName) core.info(`Downloading ${url}`) downloadStream.on('downloadProgress', ({transferred}) => { core.info(`Progress: ${transferred} B`) }) await asyncStream(downloadStream, fileWriterStream) } finally { core.endGroup() } } export async function listFiles(octokit: InstanceType, sha: string): Promise { core.startGroup('Fetching list of tracked files from GitHub') try { const commit = await octokit.git.getCommit({ commit_sha: sha, ...github.context.repo }) const files = await listGitTree(octokit, commit.data.tree.sha, '') return files } finally { core.endGroup() } } async function listGitTree(octokit: InstanceType, sha: string, path: string): Promise { const pathLog = path ? ` at ${path}` : '' core.info(`Fetching tree ${sha}${pathLog}`) let truncated = false let tree = await octokit.git.getTree({ recursive: 'true', tree_sha: sha, ...github.context.repo }) if (tree.data.truncated) { truncated = true tree = await octokit.git.getTree({ tree_sha: sha, ...github.context.repo }) } const result: string[] = [] for (const tr of tree.data.tree) { const file = `${path}${tr.path}` if (tr.type === 'blob') { result.push(file) } else if (tr.type === 'tree' && truncated) { const files = await listGitTree(octokit, tr.sha, `${file}/`) result.push(...files) } } return result }