1
0
Fork 0

ability to filter artifacts by latest

pull/1592/head
Rob Herley 2023-12-03 05:01:20 +00:00 committed by GitHub
parent fa7657714a
commit c94ca49c9c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 65 additions and 8 deletions

View File

@ -5,6 +5,7 @@ import {
UploadResponse, UploadResponse,
DownloadArtifactOptions, DownloadArtifactOptions,
GetArtifactResponse, GetArtifactResponse,
ListArtifactsOptions,
ListArtifactsResponse, ListArtifactsResponse,
DownloadArtifactResponse, DownloadArtifactResponse,
FindOptions FindOptions
@ -44,7 +45,9 @@ export interface ArtifactClient {
* @param options Extra options that allow for the customization of the list behavior * @param options Extra options that allow for the customization of the list behavior
* @returns ListArtifactResponse object * @returns ListArtifactResponse object
*/ */
listArtifacts(options?: FindOptions): Promise<ListArtifactsResponse> listArtifacts(
options?: ListArtifactsOptions & FindOptions
): Promise<ListArtifactsResponse>
/** /**
* Finds an artifact by name. * Finds an artifact by name.
@ -171,7 +174,9 @@ If the error persists, please check whether Actions and API requests are operati
/** /**
* List Artifacts * List Artifacts
*/ */
async listArtifacts(options?: FindOptions): Promise<ListArtifactsResponse> { async listArtifacts(
options?: ListArtifactsOptions & FindOptions
): Promise<ListArtifactsResponse> {
if (isGhes()) { if (isGhes()) {
warning( warning(
`@actions/artifact v2.0.0+ and download-artifact@v4+ are not currently supported on GHES.` `@actions/artifact v2.0.0+ and download-artifact@v4+ are not currently supported on GHES.`
@ -191,11 +196,12 @@ If the error persists, please check whether Actions and API requests are operati
workflowRunId, workflowRunId,
repositoryOwner, repositoryOwner,
repositoryName, repositoryName,
token token,
options?.latest
) )
} }
return listArtifactsInternal() return listArtifactsInternal(options?.latest)
} catch (error: unknown) { } catch (error: unknown) {
warning( warning(
`Listing Artifacts failed with error: ${error}. `Listing Artifacts failed with error: ${error}.

View File

@ -20,13 +20,14 @@ export async function listArtifactsPublic(
workflowRunId: number, workflowRunId: number,
repositoryOwner: string, repositoryOwner: string,
repositoryName: string, repositoryName: string,
token: string token: string,
latest = false
): Promise<ListArtifactsResponse> { ): Promise<ListArtifactsResponse> {
info( info(
`Fetching artifact list for workflow run ${workflowRunId} in repository ${repositoryOwner}/${repositoryName}` `Fetching artifact list for workflow run ${workflowRunId} in repository ${repositoryOwner}/${repositoryName}`
) )
const artifacts: Artifact[] = [] let artifacts: Artifact[] = []
const [retryOpts, requestOpts] = getRetryOptions(defaultGitHubOptions) const [retryOpts, requestOpts] = getRetryOptions(defaultGitHubOptions)
const opts: OctokitOptions = { const opts: OctokitOptions = {
@ -100,6 +101,10 @@ export async function listArtifactsPublic(
} }
} }
if (latest) {
artifacts = filterLatest(artifacts)
}
info(`Found ${artifacts.length} artifact(s)`) info(`Found ${artifacts.length} artifact(s)`)
return { return {
@ -107,7 +112,9 @@ export async function listArtifactsPublic(
} }
} }
export async function listArtifactsInternal(): Promise<ListArtifactsResponse> { export async function listArtifactsInternal(
latest = false
): Promise<ListArtifactsResponse> {
const artifactClient = internalArtifactTwirpClient() const artifactClient = internalArtifactTwirpClient()
const {workflowRunBackendId, workflowJobRunBackendId} = const {workflowRunBackendId, workflowJobRunBackendId} =
@ -119,7 +126,7 @@ export async function listArtifactsInternal(): Promise<ListArtifactsResponse> {
} }
const res = await artifactClient.ListArtifacts(req) const res = await artifactClient.ListArtifacts(req)
const artifacts = res.artifacts.map(artifact => ({ let artifacts: Artifact[] = res.artifacts.map(artifact => ({
name: artifact.name, name: artifact.name,
id: Number(artifact.databaseId), id: Number(artifact.databaseId),
size: Number(artifact.size), size: Number(artifact.size),
@ -128,9 +135,44 @@ export async function listArtifactsInternal(): Promise<ListArtifactsResponse> {
: undefined : undefined
})) }))
if (latest) {
artifacts = filterLatest(artifacts)
}
info(`Found ${artifacts.length} artifact(s)`) info(`Found ${artifacts.length} artifact(s)`)
return { return {
artifacts artifacts
} }
} }
/**
* Filters a list of artifacts to only include the latest artifact for each name
* @param artifacts The artifacts to filter
* @returns The filtered list of artifacts
*/
function filterLatest(artifacts: Artifact[]): Artifact[] {
artifacts.sort((a, b) => {
if (!a.createdAt && !b.createdAt) {
return 0
}
if (!a.createdAt) {
return -1
}
if (!b.createdAt) {
return 1
}
return b.createdAt.getTime() - a.createdAt.getTime()
})
const latestArtifacts: Artifact[] = []
const seenArtifactNames = new Set<string>()
for (const artifact of artifacts) {
if (!seenArtifactNames.has(artifact.name)) {
latestArtifacts.push(artifact)
seenArtifactNames.add(artifact.name)
}
}
return latestArtifacts
}

View File

@ -74,6 +74,15 @@ export interface GetArtifactResponse {
* ListArtifact * * ListArtifact *
* * * *
*****************************************************************************/ *****************************************************************************/
export interface ListArtifactsOptions {
/**
* Filter the workflow run's artifacts to the latest by name
* In the case of reruns, this can be useful to avoid duplicates
*/
latest?: boolean
}
export interface ListArtifactsResponse { export interface ListArtifactsResponse {
/** /**
* A list of artifacts that were found * A list of artifacts that were found