mirror of https://github.com/actions/toolkit
Initial upload structure setup
parent
c24983bf08
commit
4dfce941f1
|
@ -1,12 +1,16 @@
|
||||||
import { ArtifactClient, DefaultArtifactClient} from './internal/artifact-client'
|
import { ArtifactClient, DefaultArtifactClient} from './internal/artifact-client'
|
||||||
|
import { UploadOptions } from './internal/upload-options'
|
||||||
export {
|
import { UploadResponse } from './internal/upload-response'
|
||||||
ArtifactClient,
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs an ArtifactClient
|
* Exported functionality that we want to expose for any users of @actions/artifact
|
||||||
*/
|
*/
|
||||||
|
export {
|
||||||
|
ArtifactClient,
|
||||||
|
UploadOptions,
|
||||||
|
UploadResponse,
|
||||||
|
}
|
||||||
|
|
||||||
export function create(): ArtifactClient {
|
export function create(): ArtifactClient {
|
||||||
return DefaultArtifactClient.create()
|
return DefaultArtifactClient.create()
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,58 @@
|
||||||
|
import {checkArtifactName} from './path-and-artifact-name-validation'
|
||||||
|
import {UploadOptions} from './upload-options'
|
||||||
|
import {UploadResponse} from './upload-response'
|
||||||
|
|
||||||
|
export interface ArtifactClient {
|
||||||
|
/**
|
||||||
|
* Uploads an artifact
|
||||||
|
*
|
||||||
|
* @param name the name of the artifact, required
|
||||||
|
* @param files a list of absolute or relative paths that denote what files should be uploaded
|
||||||
|
* @param rootDirectory an absolute or relative file path that denotes the root parent directory of the files being uploaded
|
||||||
|
* @param options extra options for customizing the upload behavior
|
||||||
|
* @returns single UploadInfo object
|
||||||
|
*/
|
||||||
|
uploadArtifact(
|
||||||
|
name: string,
|
||||||
|
files: string[],
|
||||||
|
rootDirectory: string,
|
||||||
|
options?: UploadOptions
|
||||||
|
): Promise<UploadResponse>
|
||||||
|
}
|
||||||
|
|
||||||
|
export class DefaultArtifactClient implements ArtifactClient {
|
||||||
|
/**
|
||||||
|
* Constructs a DefaultArtifactClient
|
||||||
|
*/
|
||||||
|
static create(): DefaultArtifactClient {
|
||||||
|
return new DefaultArtifactClient()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Uploads an artifact
|
||||||
|
*/
|
||||||
|
async uploadArtifact(
|
||||||
|
name: string,
|
||||||
|
files: string[],
|
||||||
|
rootDirectory: string,
|
||||||
|
options?: UploadOptions | undefined
|
||||||
|
): Promise<UploadResponse> {
|
||||||
|
|
||||||
|
// Need to keep checking the artifact name
|
||||||
|
checkArtifactName(name)
|
||||||
|
|
||||||
|
// TODO Twirp call to create new artifact
|
||||||
|
|
||||||
|
// TODO Upload to blob storage using SAS URL
|
||||||
|
|
||||||
|
// TODO Twirp call to finalize the new artifact upload
|
||||||
|
|
||||||
|
const uploadResponse: UploadResponse = {
|
||||||
|
artifactName: name,
|
||||||
|
artifactItems: [],
|
||||||
|
size: 0
|
||||||
|
}
|
||||||
|
|
||||||
|
return uploadResponse
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,82 @@
|
||||||
|
import {info} from '@actions/core'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invalid characters that cannot be in the artifact name or an uploaded file. Will be rejected
|
||||||
|
* from the server if attempted to be sent over. These characters are not allowed due to limitations with certain
|
||||||
|
* file systems such as NTFS. To maintain platform-agnostic behavior, all characters that are not supported by an
|
||||||
|
* individual filesystem/platform will not be supported on all fileSystems/platforms
|
||||||
|
*
|
||||||
|
* FilePaths can include characters such as \ and / which are not permitted in the artifact name alone
|
||||||
|
*/
|
||||||
|
const invalidArtifactFilePathCharacters = new Map<string, string>([
|
||||||
|
['"', ' Double quote "'],
|
||||||
|
[':', ' Colon :'],
|
||||||
|
['<', ' Less than <'],
|
||||||
|
['>', ' Greater than >'],
|
||||||
|
['|', ' Vertical bar |'],
|
||||||
|
['*', ' Asterisk *'],
|
||||||
|
['?', ' Question mark ?'],
|
||||||
|
['\r', ' Carriage return \\r'],
|
||||||
|
['\n', ' Line feed \\n']
|
||||||
|
])
|
||||||
|
|
||||||
|
const invalidArtifactNameCharacters = new Map<string, string>([
|
||||||
|
...invalidArtifactFilePathCharacters,
|
||||||
|
['\\', ' Backslash \\'],
|
||||||
|
['/', ' Forward slash /']
|
||||||
|
])
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scans the name of the artifact to make sure there are no illegal characters
|
||||||
|
*/
|
||||||
|
export function checkArtifactName(name: string): void {
|
||||||
|
if (!name) {
|
||||||
|
throw new Error(`Artifact name: ${name}, is incorrectly provided`)
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const [
|
||||||
|
invalidCharacterKey,
|
||||||
|
errorMessageForCharacter
|
||||||
|
] of invalidArtifactNameCharacters) {
|
||||||
|
if (name.includes(invalidCharacterKey)) {
|
||||||
|
throw new Error(
|
||||||
|
`Artifact name is not valid: ${name}. Contains the following character: ${errorMessageForCharacter}
|
||||||
|
|
||||||
|
Invalid characters include: ${Array.from(
|
||||||
|
invalidArtifactNameCharacters.values()
|
||||||
|
).toString()}
|
||||||
|
|
||||||
|
These characters are not allowed in the artifact name due to limitations with certain file systems such as NTFS. To maintain file system agnostic behavior, these characters are intentionally not allowed to prevent potential problems with downloads on different file systems.`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
info(`Artifact name is valid!`)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scans the name of the filePath used to make sure there are no illegal characters
|
||||||
|
*/
|
||||||
|
export function checkArtifactFilePath(path: string): void {
|
||||||
|
if (!path) {
|
||||||
|
throw new Error(`Artifact path: ${path}, is incorrectly provided`)
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const [
|
||||||
|
invalidCharacterKey,
|
||||||
|
errorMessageForCharacter
|
||||||
|
] of invalidArtifactFilePathCharacters) {
|
||||||
|
if (path.includes(invalidCharacterKey)) {
|
||||||
|
throw new Error(
|
||||||
|
`Artifact path is not valid: ${path}. Contains the following character: ${errorMessageForCharacter}
|
||||||
|
|
||||||
|
Invalid characters include: ${Array.from(
|
||||||
|
invalidArtifactFilePathCharacters.values()
|
||||||
|
).toString()}
|
||||||
|
|
||||||
|
The following characters are not allowed in files that are uploaded due to limitations with certain file systems such as NTFS. To maintain file system agnostic behavior, these characters are intentionally not allowed to prevent potential problems with downloads on different file systems.
|
||||||
|
`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
export interface UploadOptions {
|
||||||
|
/**
|
||||||
|
* Duration after which artifact will expire in days.
|
||||||
|
*
|
||||||
|
* By default artifact expires after 90 days:
|
||||||
|
* https://docs.github.com/en/actions/configuring-and-managing-workflows/persisting-workflow-data-using-artifacts#downloading-and-deleting-artifacts-after-a-workflow-run-is-complete
|
||||||
|
*
|
||||||
|
* Use this option to override the default expiry.
|
||||||
|
*
|
||||||
|
* Min value: 1
|
||||||
|
* Max value: 90 unless changed by repository setting
|
||||||
|
*
|
||||||
|
* If this is set to a greater value than the retention settings allowed, the retention on artifacts
|
||||||
|
* will be reduced to match the max value allowed on server, and the upload process will continue. An
|
||||||
|
* input of 0 assumes default retention setting.
|
||||||
|
*/
|
||||||
|
retentionDays?: number
|
||||||
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
export interface UploadResponse {
|
||||||
|
/**
|
||||||
|
* The name of the artifact that was uploaded
|
||||||
|
*/
|
||||||
|
artifactName: string
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Total size of the artifact in bytes that was uploaded
|
||||||
|
*/
|
||||||
|
size: number
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A list of items that were uploaded as part of the artifact
|
||||||
|
*/
|
||||||
|
artifactItems: string[]
|
||||||
|
}
|
Loading…
Reference in New Issue