Compare commits

..

5 Commits

Author SHA1 Message Date
Rob Herley
0742efc19b set new 'latest' attribute for list operations 2023-12-04 13:08:24 -05:00
Rob Herley
5e4b342272 consume latest @actions/artifact 2023-12-01 13:20:09 -05:00
Rob Herley
98c6f16055 Merge pull request #239 from actions/robherley/bump-v4-beta-toolkit
Bump to latest artifact version from toolkit
2023-11-21 10:58:42 -05:00
Rob Herley
9140050fd5 consume latest @actions/artifact from toolkit 2023-11-20 20:57:50 -05:00
Konrad Pabjan
88dadfbcfc [v4 beta] Fixes to download directory structure (#233)
* Fix extra root extra root directory if downloading single artifact on v4-beta

* Fix for all downloads

* Bump to node20 runtime

* ncc

---------

Co-authored-by: Rob Herley <robherley@github.com>
2023-10-27 10:11:07 -04:00
2 changed files with 95 additions and 36 deletions

105
dist/index.js vendored
View File

@@ -7041,7 +7041,8 @@ class ListArtifactsResponse_MonolithArtifact$Type extends runtime_5.MessageType
{ no: 2, name: "workflow_job_run_backend_id", kind: "scalar", T: 9 /*ScalarType.STRING*/ }, { no: 2, name: "workflow_job_run_backend_id", kind: "scalar", T: 9 /*ScalarType.STRING*/ },
{ no: 3, name: "database_id", kind: "scalar", T: 3 /*ScalarType.INT64*/ }, { no: 3, name: "database_id", kind: "scalar", T: 3 /*ScalarType.INT64*/ },
{ no: 4, name: "name", kind: "scalar", T: 9 /*ScalarType.STRING*/ }, { no: 4, name: "name", kind: "scalar", T: 9 /*ScalarType.STRING*/ },
{ no: 5, name: "size", kind: "scalar", T: 3 /*ScalarType.INT64*/ } { no: 5, name: "size", kind: "scalar", T: 3 /*ScalarType.INT64*/ },
{ no: 6, name: "created_at", kind: "message", T: () => timestamp_1.Timestamp }
]); ]);
} }
create(value) { create(value) {
@@ -7071,6 +7072,9 @@ class ListArtifactsResponse_MonolithArtifact$Type extends runtime_5.MessageType
case /* int64 size */ 5: case /* int64 size */ 5:
message.size = reader.int64().toString(); message.size = reader.int64().toString();
break; break;
case /* google.protobuf.Timestamp created_at */ 6:
message.createdAt = timestamp_1.Timestamp.internalBinaryRead(reader, reader.uint32(), options, message.createdAt);
break;
default: default:
let u = options.readUnknownField; let u = options.readUnknownField;
if (u === "throw") if (u === "throw")
@@ -7098,6 +7102,9 @@ class ListArtifactsResponse_MonolithArtifact$Type extends runtime_5.MessageType
/* int64 size = 5; */ /* int64 size = 5; */
if (message.size !== "0") if (message.size !== "0")
writer.tag(5, runtime_1.WireType.Varint).int64(message.size); writer.tag(5, runtime_1.WireType.Varint).int64(message.size);
/* google.protobuf.Timestamp created_at = 6; */
if (message.createdAt)
timestamp_1.Timestamp.internalBinaryWrite(message.createdAt, writer.tag(6, runtime_1.WireType.LengthDelimited).fork(), options).join();
let u = options.writeUnknownFields; let u = options.writeUnknownFields;
if (u !== false) if (u !== false)
(u == true ? runtime_2.UnknownFieldHandler.onWrite : u)(this.typeName, message, writer); (u == true ? runtime_2.UnknownFieldHandler.onWrite : u)(this.typeName, message, writer);
@@ -7734,7 +7741,7 @@ If the error persists, please check whether Actions is operating normally at [ht
const { findBy: { repositoryOwner, repositoryName, token } } = options, downloadOptions = __rest(options, ["findBy"]); const { findBy: { repositoryOwner, repositoryName, token } } = options, downloadOptions = __rest(options, ["findBy"]);
return (0, download_artifact_1.downloadArtifactPublic)(artifactId, repositoryOwner, repositoryName, token, downloadOptions); return (0, download_artifact_1.downloadArtifactPublic)(artifactId, repositoryOwner, repositoryName, token, downloadOptions);
} }
return (0, download_artifact_1.downloadArtifactInternal)(artifactId); return (0, download_artifact_1.downloadArtifactInternal)(artifactId, options);
} }
catch (error) { catch (error) {
(0, core_1.warning)(`Artifact download failed with error: ${error}. (0, core_1.warning)(`Artifact download failed with error: ${error}.
@@ -7762,9 +7769,9 @@ If the error persists, please check whether Actions and API requests are operati
try { try {
if (options === null || options === void 0 ? void 0 : options.findBy) { if (options === null || options === void 0 ? void 0 : options.findBy) {
const { findBy: { workflowRunId, repositoryOwner, repositoryName, token } } = options; const { findBy: { workflowRunId, repositoryOwner, repositoryName, token } } = options;
return (0, list_artifacts_1.listArtifactsPublic)(workflowRunId, repositoryOwner, repositoryName, token); return (0, list_artifacts_1.listArtifactsPublic)(workflowRunId, repositoryOwner, repositoryName, token, options === null || options === void 0 ? void 0 : options.latest);
} }
return (0, list_artifacts_1.listArtifactsInternal)(); return (0, list_artifacts_1.listArtifactsInternal)(options === null || options === void 0 ? void 0 : options.latest);
} }
catch (error) { catch (error) {
(0, core_1.warning)(`Listing Artifacts failed with error: ${error}. (0, core_1.warning)(`Listing Artifacts failed with error: ${error}.
@@ -7864,6 +7871,7 @@ const unzipper_1 = __importDefault(__nccwpck_require__(80686));
const user_agent_1 = __nccwpck_require__(79681); const user_agent_1 = __nccwpck_require__(79681);
const config_1 = __nccwpck_require__(95042); const config_1 = __nccwpck_require__(95042);
const artifact_twirp_client_1 = __nccwpck_require__(63550); const artifact_twirp_client_1 = __nccwpck_require__(63550);
const generated_1 = __nccwpck_require__(90265);
const util_1 = __nccwpck_require__(80565); const util_1 = __nccwpck_require__(80565);
const scrubQueryParameters = (url) => { const scrubQueryParameters = (url) => {
const parsed = new URL(url); const parsed = new URL(url);
@@ -7937,7 +7945,8 @@ function downloadArtifactInternal(artifactId, options) {
const { workflowRunBackendId, workflowJobRunBackendId } = (0, util_1.getBackendIdsFromToken)(); const { workflowRunBackendId, workflowJobRunBackendId } = (0, util_1.getBackendIdsFromToken)();
const listReq = { const listReq = {
workflowRunBackendId, workflowRunBackendId,
workflowJobRunBackendId workflowJobRunBackendId,
idFilter: generated_1.Int64Value.create({ value: artifactId.toString() })
}; };
const { artifacts } = yield artifactClient.ListArtifacts(listReq); const { artifacts } = yield artifactClient.ListArtifacts(listReq);
if (artifacts.length === 0) { if (artifacts.length === 0) {
@@ -8060,15 +8069,18 @@ function getArtifactPublic(artifactName, workflowRunId, repositoryOwner, reposit
success: false success: false
}; };
} }
let artifact = getArtifactResp.data.artifacts[0];
if (getArtifactResp.data.artifacts.length > 1) { if (getArtifactResp.data.artifacts.length > 1) {
core.warning('more than one artifact found for a single name, returning first'); artifact = getArtifactResp.data.artifacts.sort((a, b) => b.id - a.id)[0];
core.debug(`More than one artifact found for a single name, returning newest (id: ${artifact.id})`);
} }
return { return {
success: true, success: true,
artifact: { artifact: {
name: getArtifactResp.data.artifacts[0].name, name: artifact.name,
id: getArtifactResp.data.artifacts[0].id, id: artifact.id,
size: getArtifactResp.data.artifacts[0].size_in_bytes size: artifact.size_in_bytes,
createdAt: artifact.created_at ? new Date(artifact.created_at) : undefined
} }
}; };
}); });
@@ -8090,19 +8102,20 @@ function getArtifactInternal(artifactName) {
success: false success: false
}; };
} }
let artifact = res.artifacts[0];
if (res.artifacts.length > 1) { if (res.artifacts.length > 1) {
core.warning('more than one artifact found for a single name, returning first'); artifact = res.artifacts.sort((a, b) => Number(b.databaseId) - Number(a.databaseId))[0];
core.debug(`more than one artifact found for a single name, returning newest (id: ${artifact.databaseId})`);
} }
// In the case of reruns, we may have artifacts with the same name scoped under the same workflow run.
// Let's prefer the artifact closest scoped to this run.
// If it doesn't exist (e.g. partial rerun) we'll use the first match.
const artifact = res.artifacts.find(artifact => artifact.workflowRunBackendId === workflowRunBackendId) || res.artifacts[0];
return { return {
success: true, success: true,
artifact: { artifact: {
name: artifact.name, name: artifact.name,
id: Number(artifact.databaseId), id: Number(artifact.databaseId),
size: Number(artifact.size) size: Number(artifact.size),
createdAt: artifact.createdAt
? generated_1.Timestamp.toDate(artifact.createdAt)
: undefined
} }
}; };
}); });
@@ -8137,14 +8150,15 @@ const plugin_request_log_1 = __nccwpck_require__(73665);
const plugin_retry_1 = __nccwpck_require__(69980); const plugin_retry_1 = __nccwpck_require__(69980);
const artifact_twirp_client_1 = __nccwpck_require__(63550); const artifact_twirp_client_1 = __nccwpck_require__(63550);
const util_1 = __nccwpck_require__(80565); const util_1 = __nccwpck_require__(80565);
const generated_1 = __nccwpck_require__(90265);
// Limiting to 1000 for perf reasons // Limiting to 1000 for perf reasons
const maximumArtifactCount = 1000; const maximumArtifactCount = 1000;
const paginationCount = 100; const paginationCount = 100;
const maxNumberOfPages = maximumArtifactCount / paginationCount; const maxNumberOfPages = maximumArtifactCount / paginationCount;
function listArtifactsPublic(workflowRunId, repositoryOwner, repositoryName, token) { function listArtifactsPublic(workflowRunId, repositoryOwner, repositoryName, token, latest = false) {
return __awaiter(this, void 0, void 0, function* () { return __awaiter(this, void 0, void 0, function* () {
(0, core_1.info)(`Fetching artifact list for workflow run ${workflowRunId} in repository ${repositoryOwner}/${repositoryName}`); (0, core_1.info)(`Fetching artifact list for workflow run ${workflowRunId} in repository ${repositoryOwner}/${repositoryName}`);
const artifacts = []; let artifacts = [];
const [retryOpts, requestOpts] = (0, retry_options_1.getRetryOptions)(utils_1.defaults); const [retryOpts, requestOpts] = (0, retry_options_1.getRetryOptions)(utils_1.defaults);
const opts = { const opts = {
log: undefined, log: undefined,
@@ -8173,7 +8187,8 @@ function listArtifactsPublic(workflowRunId, repositoryOwner, repositoryName, tok
artifacts.push({ artifacts.push({
name: artifact.name, name: artifact.name,
id: artifact.id, id: artifact.id,
size: artifact.size_in_bytes size: artifact.size_in_bytes,
createdAt: artifact.created_at ? new Date(artifact.created_at) : undefined
}); });
} }
// Iterate over any remaining pages // Iterate over any remaining pages
@@ -8191,10 +8206,16 @@ function listArtifactsPublic(workflowRunId, repositoryOwner, repositoryName, tok
artifacts.push({ artifacts.push({
name: artifact.name, name: artifact.name,
id: artifact.id, id: artifact.id,
size: artifact.size_in_bytes size: artifact.size_in_bytes,
createdAt: artifact.created_at
? new Date(artifact.created_at)
: undefined
}); });
} }
} }
if (latest) {
artifacts = filterLatest(artifacts);
}
(0, core_1.info)(`Found ${artifacts.length} artifact(s)`); (0, core_1.info)(`Found ${artifacts.length} artifact(s)`);
return { return {
artifacts artifacts
@@ -8202,7 +8223,7 @@ function listArtifactsPublic(workflowRunId, repositoryOwner, repositoryName, tok
}); });
} }
exports.listArtifactsPublic = listArtifactsPublic; exports.listArtifactsPublic = listArtifactsPublic;
function listArtifactsInternal() { function listArtifactsInternal(latest = false) {
return __awaiter(this, void 0, void 0, function* () { return __awaiter(this, void 0, void 0, function* () {
const artifactClient = (0, artifact_twirp_client_1.internalArtifactTwirpClient)(); const artifactClient = (0, artifact_twirp_client_1.internalArtifactTwirpClient)();
const { workflowRunBackendId, workflowJobRunBackendId } = (0, util_1.getBackendIdsFromToken)(); const { workflowRunBackendId, workflowJobRunBackendId } = (0, util_1.getBackendIdsFromToken)();
@@ -8211,11 +8232,17 @@ function listArtifactsInternal() {
workflowJobRunBackendId workflowJobRunBackendId
}; };
const res = yield artifactClient.ListArtifacts(req); const res = yield artifactClient.ListArtifacts(req);
const artifacts = res.artifacts.map(artifact => ({ let artifacts = 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),
createdAt: artifact.createdAt
? generated_1.Timestamp.toDate(artifact.createdAt)
: undefined
})); }));
if (latest) {
artifacts = filterLatest(artifacts);
}
(0, core_1.info)(`Found ${artifacts.length} artifact(s)`); (0, core_1.info)(`Found ${artifacts.length} artifact(s)`);
return { return {
artifacts artifacts
@@ -8223,6 +8250,23 @@ function listArtifactsInternal() {
}); });
} }
exports.listArtifactsInternal = listArtifactsInternal; exports.listArtifactsInternal = listArtifactsInternal;
/**
* 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) {
artifacts.sort((a, b) => b.id - a.id);
const latestArtifacts = [];
const seenArtifactNames = new Set();
for (const artifact of artifacts) {
if (!seenArtifactNames.has(artifact.name)) {
latestArtifacts.push(artifact);
seenArtifactNames.add(artifact.name);
}
}
return latestArtifacts;
}
//# sourceMappingURL=list-artifacts.js.map //# sourceMappingURL=list-artifacts.js.map
/***/ }), /***/ }),
@@ -119353,6 +119397,7 @@ function run() {
if (inputs.path.startsWith(`~`)) { if (inputs.path.startsWith(`~`)) {
inputs.path = inputs.path.replace('~', os.homedir()); inputs.path = inputs.path.replace('~', os.homedir());
} }
const isSingleArtifactDownload = !!inputs.name;
const resolvedPath = path.resolve(inputs.path); const resolvedPath = path.resolve(inputs.path);
core.debug(`Resolved path is ${resolvedPath}`); core.debug(`Resolved path is ${resolvedPath}`);
const options = {}; const options = {};
@@ -119370,25 +119415,27 @@ function run() {
} }
const artifactClient = artifact.create(); const artifactClient = artifact.create();
let artifacts = []; let artifacts = [];
if (inputs.name) { if (isSingleArtifactDownload) {
core.info(`Downloading single artifact`);
const { artifact: targetArtifact } = yield artifactClient.getArtifact(inputs.name, options); const { artifact: targetArtifact } = yield artifactClient.getArtifact(inputs.name, options);
if (!targetArtifact) { if (!targetArtifact) {
throw new Error(`Artifact '${inputs.name}' not found`); throw new Error(`Artifact '${inputs.name}' not found`);
} }
core.debug('Found named artifact:'); core.debug(`Found named artifact '${inputs.name}' (ID: ${targetArtifact.id}, Size: ${targetArtifact.size})`);
core.debug(JSON.stringify(targetArtifact, null, 2));
artifacts = [targetArtifact]; artifacts = [targetArtifact];
} }
else { else {
const listArtifactResponse = yield artifactClient.listArtifacts(options); core.info(`No input name specified, downloading all artifacts. Extra directory with the artifact name will be created for each download`);
const listArtifactResponse = yield artifactClient.listArtifacts(Object.assign({ latest: true }, options));
if (listArtifactResponse.artifacts.length === 0) { if (listArtifactResponse.artifacts.length === 0) {
throw new Error(`No artifacts found for run '${inputs.runID}' in '${inputs.repository}'`); throw new Error(`No artifacts found for run '${inputs.runID}' in '${inputs.repository}'`);
} }
core.debug(`Found ${listArtifactResponse.artifacts.length} artifacts:`); core.debug(`Found ${listArtifactResponse.artifacts.length} artifacts`);
core.debug(JSON.stringify(listArtifactResponse, null, 2));
artifacts = listArtifactResponse.artifacts; artifacts = listArtifactResponse.artifacts;
} }
const downloadPromises = artifacts.map(artifact => artifactClient.downloadArtifact(artifact.id, Object.assign(Object.assign({}, options), { path: path.join(resolvedPath, artifact.name) }))); const downloadPromises = artifacts.map(artifact => artifactClient.downloadArtifact(artifact.id, Object.assign(Object.assign({}, options), { path: isSingleArtifactDownload
? resolvedPath
: path.join(resolvedPath, artifact.name) })));
const chunkedPromises = exports.chunk(downloadPromises, PARALLEL_DOWNLOADS); const chunkedPromises = exports.chunk(downloadPromises, PARALLEL_DOWNLOADS);
for (const chunk of chunkedPromises) { for (const chunk of chunkedPromises) {
yield Promise.all(chunk); yield Promise.all(chunk);

View File

@@ -30,6 +30,7 @@ async function run(): Promise<void> {
inputs.path = inputs.path.replace('~', os.homedir()) inputs.path = inputs.path.replace('~', os.homedir())
} }
const isSingleArtifactDownload = !!inputs.name
const resolvedPath = path.resolve(inputs.path) const resolvedPath = path.resolve(inputs.path)
core.debug(`Resolved path is ${resolvedPath}`) core.debug(`Resolved path is ${resolvedPath}`)
@@ -53,7 +54,9 @@ async function run(): Promise<void> {
const artifactClient = artifact.create() const artifactClient = artifact.create()
let artifacts: artifact.Artifact[] = [] let artifacts: artifact.Artifact[] = []
if (inputs.name) { if (isSingleArtifactDownload) {
core.info(`Downloading single artifact`)
const {artifact: targetArtifact} = await artifactClient.getArtifact( const {artifact: targetArtifact} = await artifactClient.getArtifact(
inputs.name, inputs.name,
options options
@@ -63,12 +66,20 @@ async function run(): Promise<void> {
throw new Error(`Artifact '${inputs.name}' not found`) throw new Error(`Artifact '${inputs.name}' not found`)
} }
core.debug('Found named artifact:') core.debug(
core.debug(JSON.stringify(targetArtifact, null, 2)) `Found named artifact '${inputs.name}' (ID: ${targetArtifact.id}, Size: ${targetArtifact.size})`
)
artifacts = [targetArtifact] artifacts = [targetArtifact]
} else { } else {
const listArtifactResponse = await artifactClient.listArtifacts(options) core.info(
`No input name specified, downloading all artifacts. Extra directory with the artifact name will be created for each download`
)
const listArtifactResponse = await artifactClient.listArtifacts({
latest: true,
...options
})
if (listArtifactResponse.artifacts.length === 0) { if (listArtifactResponse.artifacts.length === 0) {
throw new Error( throw new Error(
@@ -76,15 +87,16 @@ async function run(): Promise<void> {
) )
} }
core.debug(`Found ${listArtifactResponse.artifacts.length} artifacts:`) core.debug(`Found ${listArtifactResponse.artifacts.length} artifacts`)
core.debug(JSON.stringify(listArtifactResponse, null, 2))
artifacts = listArtifactResponse.artifacts artifacts = listArtifactResponse.artifacts
} }
const downloadPromises = artifacts.map(artifact => const downloadPromises = artifacts.map(artifact =>
artifactClient.downloadArtifact(artifact.id, { artifactClient.downloadArtifact(artifact.id, {
...options, ...options,
path: path.join(resolvedPath, artifact.name) path: isSingleArtifactDownload
? resolvedPath
: path.join(resolvedPath, artifact.name)
}) })
) )