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'), }; 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) { 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(); }); }