refactor ghcr client for reusable upload functions
This commit is contained in:
+25
-24
@@ -107430,31 +107430,28 @@ const fsHelper = __importStar(__nccwpck_require__(76642));
|
||||
// Publish the OCI artifact and return the URL where it can be downloaded
|
||||
async function publishOCIArtifact(token, registry, repository, semver, zipFile, tarFile, manifest) {
|
||||
const b64Token = Buffer.from(token).toString('base64');
|
||||
const checkBlobEndpoint = new URL(`v2/${repository}/blobs/`, registry).toString();
|
||||
const uploadBlobEndpoint = new URL(`v2/${repository}/blobs/uploads/`, registry).toString();
|
||||
const manifestEndpoint = new URL(`v2/${repository}/manifests/${semver}`, registry).toString();
|
||||
core.info(`Creating GHCR package for release with semver:${semver} with path:"${zipFile.path}" and "${tarFile.path}".`);
|
||||
const layerUploads = manifest.layers.map(async (layer) => {
|
||||
switch (layer.mediaType) {
|
||||
case 'application/vnd.github.actions.package.layer.v1.tar+gzip':
|
||||
return uploadLayer(layer, tarFile, registry, checkBlobEndpoint, uploadBlobEndpoint, b64Token);
|
||||
return uploadLayer(layer, fsHelper.readFileContents(zipFile.path), registry, repository, b64Token);
|
||||
case 'application/vnd.github.actions.package.layer.v1.zip':
|
||||
return uploadLayer(layer, zipFile, registry, checkBlobEndpoint, uploadBlobEndpoint, b64Token);
|
||||
return uploadLayer(layer, fsHelper.readFileContents(zipFile.path), registry, repository, b64Token);
|
||||
case 'application/vnd.oci.empty.v1+json':
|
||||
return uploadLayer(layer, { path: '', size: 2, sha256: layer.digest }, registry, checkBlobEndpoint, uploadBlobEndpoint, b64Token);
|
||||
return uploadLayer(layer, Buffer.from('{}'), registry, repository, b64Token);
|
||||
default:
|
||||
throw new Error(`Unknown media type ${layer.mediaType}`);
|
||||
}
|
||||
});
|
||||
await Promise.all(layerUploads);
|
||||
const digest = await uploadManifest(JSON.stringify(manifest), manifestEndpoint, b64Token);
|
||||
const digest = await uploadManifest(JSON.stringify(manifest), manifest.mediaType, registry, repository, semver, b64Token);
|
||||
return {
|
||||
packageURL: new URL(`${repository}:${semver}`, registry),
|
||||
publishedDigest: digest
|
||||
};
|
||||
}
|
||||
async function uploadLayer(layer, file, registryURL, checkBlobEndpoint, uploadBlobEndpoint, b64Token) {
|
||||
const checkExistsResponse = await fetchWithDebug(checkBlobEndpoint + layer.digest, {
|
||||
async function uploadLayer(layer, data, registryURL, repository, b64Token) {
|
||||
const checkExistsResponse = await fetchWithDebug(checkBlobEndpoint(registryURL, repository, layer.digest), {
|
||||
method: 'HEAD',
|
||||
headers: {
|
||||
Authorization: `Bearer ${b64Token}`
|
||||
@@ -107469,7 +107466,8 @@ async function uploadLayer(layer, file, registryURL, checkBlobEndpoint, uploadBl
|
||||
throw new Error(await errorMessageForFailedRequest(`check blob (${layer.digest}) exists`, checkExistsResponse));
|
||||
}
|
||||
core.info(`Uploading layer ${layer.digest}.`);
|
||||
const initiateUploadResponse = await fetchWithDebug(uploadBlobEndpoint, {
|
||||
const initiateUploadBlobURL = uploadBlobEndpoint(registryURL, repository);
|
||||
const initiateUploadResponse = await fetchWithDebug(initiateUploadBlobURL, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
Authorization: `Bearer ${b64Token}`
|
||||
@@ -107481,18 +107479,10 @@ async function uploadLayer(layer, file, registryURL, checkBlobEndpoint, uploadBl
|
||||
}
|
||||
const locationResponseHeader = initiateUploadResponse.headers.get('location');
|
||||
if (locationResponseHeader === undefined) {
|
||||
throw new Error(`No location header in response from upload post ${uploadBlobEndpoint} for layer ${layer.digest}`);
|
||||
throw new Error(`No location header in response from upload post ${initiateUploadBlobURL} for layer ${layer.digest}`);
|
||||
}
|
||||
const pathname = `${locationResponseHeader}?digest=${layer.digest}`;
|
||||
const uploadBlobUrl = new URL(pathname, registryURL).toString();
|
||||
// TODO: must we handle the empty config layer? Maybe we can just skip calling this at all
|
||||
let data;
|
||||
if (layer.mediaType === 'application/vnd.oci.empty.v1+json') {
|
||||
data = Buffer.from('{}');
|
||||
}
|
||||
else {
|
||||
data = fsHelper.readFileContents(file.path);
|
||||
}
|
||||
const putResponse = await fetchWithDebug(uploadBlobUrl, {
|
||||
method: 'PUT',
|
||||
headers: {
|
||||
@@ -107508,13 +107498,14 @@ async function uploadLayer(layer, file, registryURL, checkBlobEndpoint, uploadBl
|
||||
}
|
||||
}
|
||||
// Uploads the manifest and returns the digest returned by GHCR
|
||||
async function uploadManifest(manifestJSON, manifestEndpoint, b64Token) {
|
||||
core.info(`Uploading manifest to ${manifestEndpoint}.`);
|
||||
const putResponse = await fetchWithDebug(manifestEndpoint, {
|
||||
async function uploadManifest(manifestJSON, manifestMediaType, registry, repository, version, b64Token) {
|
||||
const manifestUrl = manifestEndpoint(registry, repository, version);
|
||||
core.info(`Uploading manifest to ${manifestUrl}.`);
|
||||
const putResponse = await fetchWithDebug(manifestUrl, {
|
||||
method: 'PUT',
|
||||
headers: {
|
||||
Authorization: `Bearer ${b64Token}`,
|
||||
'Content-Type': 'application/vnd.oci.image.manifest.v1+json'
|
||||
'Content-Type': manifestMediaType
|
||||
},
|
||||
body: manifestJSON
|
||||
});
|
||||
@@ -107523,7 +107514,7 @@ async function uploadManifest(manifestJSON, manifestEndpoint, b64Token) {
|
||||
}
|
||||
const digestResponseHeader = putResponse.headers.get('docker-content-digest');
|
||||
if (digestResponseHeader === undefined || digestResponseHeader === null) {
|
||||
throw new Error(`No digest header in response from PUT manifest ${manifestEndpoint}`);
|
||||
throw new Error(`No digest header in response from PUT manifest ${manifestUrl}`);
|
||||
}
|
||||
return digestResponseHeader;
|
||||
}
|
||||
@@ -107561,6 +107552,16 @@ function isGHCRError(obj) {
|
||||
'message' in obj &&
|
||||
typeof obj.message === 'string');
|
||||
}
|
||||
function checkBlobEndpoint(registry, repository, digest) {
|
||||
return new URL(`v2/${repository}/blobs/${digest}`, registry).toString();
|
||||
}
|
||||
function uploadBlobEndpoint(registry, repository) {
|
||||
return new URL(`v2/${repository}/blobs/uploads/`, registry).toString();
|
||||
}
|
||||
function manifestEndpoint(registry, repository, version) {
|
||||
return new URL(`v2/${repository}/manifests/${version}`, registry).toString();
|
||||
}
|
||||
// TODO: Add retries with backoff
|
||||
const fetchWithDebug = async (url, config = {}) => {
|
||||
core.debug(`Request from ${url} with config: ${JSON.stringify(config)}`);
|
||||
try {
|
||||
|
||||
Reference in New Issue
Block a user