tag-manifest/index.js

120 lines
2.6 KiB
JavaScript

const core = require('@actions/core');
const http = require('http');
const https = require('https');
const manifest_media_type = 'application/vnd.docker.distribution.manifest.v2+json';
main();
async function main() {
try {
const input = {
registry: core.getInput('registry'),
image: core.getInput('image'),
old_tag: core.getInput('old-tag'),
new_tags: core.getInput('new-tags').trim().split('\n'),
};
console.log('Tagging %s:%s with new tags', input.image, input.old_tag, input);
const manifest = await get_manifest(`${input.registry}/v2/${input.image}/manifests/${input.old_tag}`);
const promises = input.new_tags.map((new_tag) => {
return put_manifest(`${input.registry}/v2/${input.image}/manifests/${new_tag}`, manifest);
});
await Promise.all(promises);
}
catch (error) {
console.error(error);
core.setFailed(error.message);
}
}
async function get_manifest(url_str) {
const { status, body: existing_manifest } = await http_req('GET', url_str, {
accept: manifest_media_type,
});
if (status !== 200) {
throw new Error('failed to fetch existing manifest');
}
return existing_manifest;
}
async function put_manifest(url_str, manifest) {
const { status } = await http_req('PUT', url_str, {
'content-type': manifest_media_type,
}, manifest);
if (status >= 400) {
throw new Error('failed to put manifest');
}
}
async function http_req(method, url_str, headers, body) {
const url = new URL(url_str);
const make_request
= url.protocol === 'https:' ? https.request
: url.protocol === 'http:' ? http.request
: null;
if (! make_request) {
throw new Error('registry URL protocol not http(s)');
}
if (url.username || url.password) {
throw new Error('urls containing user credentials not allowed');
}
const result = {
url,
status: null,
body: null,
headers: null,
};
const path = url.pathname + (url.search || '');
const port = url.port ? parseInt(url.port, 10) : (url.protocol === 'https:' ? 443 : 80);
return new Promise((resolve, reject) => {
const req = make_request({
method: method,
protocol: url.protocol,
hostname: url.hostname,
port: port,
path: path,
headers: headers
}, (res) => {
result.status = res.statusCode;
result.body = '';
res.on('data', (chunk) => {
result.body += chunk;
});
res.on('end', () => {
result.headers = res.headers;
resolve(result);
});
});
req.setTimeout(timeout, () => {
req.destroy();
reject(new Error('request timeout'));
});
req.on('error', (error) => {
reject(error);
});
if (body) {
req.write(body);
}
req.end();
});
}