120 lines
2.6 KiB
JavaScript
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<HttpResult>((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();
|
|
});
|
|
}
|