build
This commit is contained in:
parent
db4149302a
commit
9f2eb608c7
197
dist/index.js
vendored
197
dist/index.js
vendored
@ -1794,6 +1794,59 @@ function isLoopbackAddress(host) {
|
||||
}
|
||||
//# sourceMappingURL=proxy.js.map
|
||||
|
||||
/***/ }),
|
||||
|
||||
/***/ 632:
|
||||
/***/ ((module) => {
|
||||
|
||||
module.exports = (data, opts = {}) => {
|
||||
const {
|
||||
sorted, skipIndex, ignorenull, skipBracket, useDot, whitespace = '+'
|
||||
} = opts;
|
||||
|
||||
const encode = value => String(value)
|
||||
.replace(/[^ !'()~*]/gu, encodeURIComponent)
|
||||
.replace(/ /g, whitespace)
|
||||
.replace(/[!'()~*]/g, ch =>
|
||||
`%${ch.charCodeAt().toString(16).slice(-2).toUpperCase()}`);
|
||||
|
||||
const keys = (obj, keyarr = Object.keys(obj)) =>
|
||||
sorted ? keyarr.sort() : keyarr;
|
||||
|
||||
const filterjoin = arr => arr.filter(e => e).join('&');
|
||||
|
||||
const objnest = (name, obj) => filterjoin(keys(obj).map(key => useDot
|
||||
? nest(`${name}.${key}`, obj[key])
|
||||
: nest(`${name}[${key}]`, obj[key])));
|
||||
|
||||
const arrnest = (name, arr, brackets = skipBracket ? '' : '[]') => arr.length
|
||||
? filterjoin(arr.map((elem, index) => skipIndex
|
||||
? nest(name + brackets, elem)
|
||||
: nest(name + '[' + index + ']', elem)))
|
||||
: encode(name + brackets);
|
||||
|
||||
const setnest = (name, set) => filterjoin(
|
||||
Array.from(set).map(elem => nest(name, elem)));
|
||||
|
||||
const nest = (name, value, type = typeof value, f = null) => {
|
||||
if (value === f)
|
||||
f = ignorenull ? f : encode(name) + '=' + f;
|
||||
else if (/string|number|boolean/.test(type))
|
||||
f = encode(name) + '=' + encode(value);
|
||||
else if (Array.isArray(value))
|
||||
f = arrnest(name, value);
|
||||
else if (value instanceof Set)
|
||||
f = setnest(name, value);
|
||||
else if (type === 'object')
|
||||
f = objnest(name, value);
|
||||
|
||||
return f;
|
||||
};
|
||||
|
||||
return data && filterjoin(keys(data).map(key => nest(key, data[key])));
|
||||
};
|
||||
|
||||
|
||||
/***/ }),
|
||||
|
||||
/***/ 294:
|
||||
@ -2794,6 +2847,14 @@ module.exports = require("path");
|
||||
|
||||
/***/ }),
|
||||
|
||||
/***/ 477:
|
||||
/***/ ((module) => {
|
||||
|
||||
"use strict";
|
||||
module.exports = require("querystring");
|
||||
|
||||
/***/ }),
|
||||
|
||||
/***/ 404:
|
||||
/***/ ((module) => {
|
||||
|
||||
@ -2855,6 +2916,8 @@ var __webpack_exports__ = {};
|
||||
const core = __nccwpck_require__(186);
|
||||
const http = __nccwpck_require__(685);
|
||||
const https = __nccwpck_require__(687);
|
||||
const querystring = __nccwpck_require__(477);
|
||||
const form_urlencoded = __nccwpck_require__(632);
|
||||
|
||||
const req_timeout_ms = 30_000;
|
||||
const manifest_media_type = 'application/vnd.docker.distribution.manifest.v2+json';
|
||||
@ -2865,17 +2928,23 @@ async function main() {
|
||||
try {
|
||||
const input = {
|
||||
registry: core.getInput('registry'),
|
||||
registry_url: null,
|
||||
insecure_registry: core.getInput('insecure-registry') === 'true',
|
||||
image: core.getInput('image'),
|
||||
old_tag: core.getInput('old-tag'),
|
||||
new_tags: core.getInput('new-tags').trim().split('\n'),
|
||||
username: core.getInput('username'),
|
||||
password: core.getInput('password'),
|
||||
token: null,
|
||||
};
|
||||
|
||||
input.registry_url = `http${input.insecure_registry ? '' : 's'}://${input.registry}`;
|
||||
console.log('Tagging %s/%s:%s with new tags', input.registry, input.image, input.old_tag, input.new_tags);
|
||||
|
||||
const manifest = await get_manifest(`${input.registry}/v2/${input.image}/manifests/${input.old_tag}`);
|
||||
const manifest = await get_manifest(input);
|
||||
|
||||
const promises = input.new_tags.map((new_tag) => {
|
||||
return put_manifest(`${input.registry}/v2/${input.image}/manifests/${new_tag}`, manifest);
|
||||
return put_manifest(new_tag, manifest, input);
|
||||
});
|
||||
|
||||
await Promise.all(promises);
|
||||
@ -2887,11 +2956,21 @@ async function main() {
|
||||
}
|
||||
}
|
||||
|
||||
async function get_manifest(url_str) {
|
||||
const { status, headers, body } = await http_req('GET', url_str, {
|
||||
async function get_manifest(input) {
|
||||
const path = `/v2/${input.image}/manifests/${input.old_tag}`;
|
||||
let { status, headers, body } = await http_req('GET', input.registry_url + path, {
|
||||
accept: manifest_media_type,
|
||||
});
|
||||
|
||||
if (status === 401 && headers['www-authenticate']) {
|
||||
await get_token(headers['www-authenticate'], input);
|
||||
|
||||
({ status, headers, body } = await http_req(input.insecure_registry, 'GET', input.registry_url + path, {
|
||||
accept: manifest_media_type,
|
||||
authorization: `Bearer ${input.token}`,
|
||||
}));
|
||||
}
|
||||
|
||||
if (status !== 200) {
|
||||
console.error('get manifest response', { status, headers, body });
|
||||
throw new Error('failed to fetch existing manifest');
|
||||
@ -2900,10 +2979,17 @@ async function get_manifest(url_str) {
|
||||
return body;
|
||||
}
|
||||
|
||||
async function put_manifest(url_str, manifest) {
|
||||
const { status, headers, body } = await http_req('PUT', url_str, {
|
||||
async function put_manifest(new_tag, manifest, input) {
|
||||
const path = `/v2/${input.image}/manifests/${new_tag}`;
|
||||
const req_headers = {
|
||||
'content-type': manifest_media_type,
|
||||
}, manifest);
|
||||
};
|
||||
|
||||
if (token) {
|
||||
req_headers.authorization = `Bearer ${input.token}`;
|
||||
}
|
||||
|
||||
const { status, headers, body } = await http_req('PUT', input.registry_url + path, req_headers, manifest);
|
||||
|
||||
if (status >= 400) {
|
||||
console.error('get manifest response', { status, headers, body });
|
||||
@ -2911,6 +2997,40 @@ async function put_manifest(url_str, manifest) {
|
||||
}
|
||||
}
|
||||
|
||||
async function get_token(www_authenticate, input) {
|
||||
const params = parse_www_authenticate(www_authenticate);
|
||||
|
||||
if (! input.username || ! input.password) {
|
||||
throw new Error('registry requested authentication, but not credentials were provided');
|
||||
}
|
||||
|
||||
const query_params = {
|
||||
service: params.service,
|
||||
scope: params.scope,
|
||||
grant_type: 'password',
|
||||
username: input.username,
|
||||
password: input.password,
|
||||
};
|
||||
|
||||
const { status, headers, body } = await http_req('POST', params.realm, {
|
||||
'content-type': 'application/x-www-form-urlencoded',
|
||||
}, form_urlencoded(query_params));
|
||||
|
||||
if (status !== 200) {
|
||||
console.error('get token response', { status, headers, body });
|
||||
throw new Error('error response from get token request');
|
||||
}
|
||||
|
||||
try {
|
||||
const { token } = JSON.parse(body);
|
||||
input.token = token;
|
||||
}
|
||||
|
||||
catch (error) {
|
||||
throw new Error('invalid response from get token request');
|
||||
}
|
||||
}
|
||||
|
||||
async function http_req(method, url_str, headers, body) {
|
||||
const url = new URL(url_str);
|
||||
const make_request
|
||||
@ -2975,6 +3095,69 @@ async function http_req(method, url_str, headers, body) {
|
||||
});
|
||||
}
|
||||
|
||||
function parse_www_authenticate(www_authenticate) {
|
||||
if (! www_authenticate.startsWith('Bearer ')) {
|
||||
throw new Error('invalid www-authenticate header (no "Bearer " prefix)');
|
||||
}
|
||||
|
||||
let remaining = www_authenticate.slice(7);
|
||||
const parameters = Object.create(null);
|
||||
|
||||
while (remaining) {
|
||||
const eq_index = remaining.indexOf('=');
|
||||
|
||||
if (eq_index < 0) {
|
||||
parameters[remaining] = '';
|
||||
break;
|
||||
}
|
||||
|
||||
const key = remaining.slice(0, eq_index);
|
||||
remaining = remaining.slice(eq_index + 1);
|
||||
|
||||
if (! remaining) {
|
||||
parameters[key] = '';
|
||||
break;
|
||||
}
|
||||
|
||||
if (remaining[0] === '"') {
|
||||
const close_index = remaining.slice(1).indexOf('"');
|
||||
|
||||
if (close_index < 0) {
|
||||
throw new Error('invalid www-authenticate header (unclosed quotes)');
|
||||
}
|
||||
|
||||
parameters[key] = remaining.slice(1, close_index);
|
||||
remaining = remaining.slice(close_index + 1);
|
||||
|
||||
if (remaining && remaining[0] !== ',') {
|
||||
throw new Error('invalid www-authenticate header (expected comma after closing quote)');
|
||||
}
|
||||
|
||||
remaining = remaining.slice(1);
|
||||
}
|
||||
|
||||
else {
|
||||
const comma_index = remaining.indexOf(',');
|
||||
|
||||
if (comma_index < 0) {
|
||||
parameters[key] = remaining;
|
||||
break;
|
||||
}
|
||||
|
||||
parameters[key] = remaining.slice(1, comma_index);
|
||||
remaining = remaining.slice(comma_index + 1);
|
||||
}
|
||||
}
|
||||
|
||||
const { realm, service, scope } = parameters;
|
||||
|
||||
if (! realm || ! service || ! scope) {
|
||||
throw new Error('invalid www-authenticate header (missing "realm", "service", or "scope")');
|
||||
}
|
||||
|
||||
return parameters;
|
||||
}
|
||||
|
||||
})();
|
||||
|
||||
module.exports = __webpack_exports__;
|
||||
|
2
dist/index.js.map
vendored
2
dist/index.js.map
vendored
File diff suppressed because one or more lines are too long
25
dist/licenses.txt
vendored
25
dist/licenses.txt
vendored
@ -35,6 +35,31 @@ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
|
||||
form-urlencoded
|
||||
MIT
|
||||
(The MIT License)
|
||||
|
||||
Copyright (c) Bumblehead <chris@bumblehead.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the 'Software'), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do
|
||||
so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
IN THE SOFTWARE.
|
||||
|
||||
|
||||
tunnel
|
||||
MIT
|
||||
The MIT License (MIT)
|
||||
|
Loading…
x
Reference in New Issue
Block a user