first commit

This commit is contained in:
James Brumond 2023-08-18 20:10:28 -07:00
commit 8d4c98a481
Signed by: james
GPG Key ID: E8F2FC44BAA3357A
3 changed files with 159 additions and 0 deletions

18
action.yaml Normal file
View File

@ -0,0 +1,18 @@
name: Docker Tag
description: Tags an existing container manifest in a remote registry without pulling/pushing any images
runs:
using: node16
main: main.js
inputs:
registry:
description: URL to the container registry
required: true
image:
description: The name of the image in the registry
required: true
old-tag:
description: The tag of the existing image to be re-tagged
required: true
new-tags:
description: Newline delimited list of new tags to apply to the image
required: true

116
main.js Normal file
View File

@ -0,0 +1,116 @@
const core = require('@actions/core');
const http = require('http');
const https = require('https');
main();
const manifest_media_type = 'application/vnd.docker.distribution.manifest.v2+json';
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<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();
});
}

25
readme.md Normal file
View File

@ -0,0 +1,25 @@
Action for re-tagging an existing docker multi-arch manifest
## Inputs
<!-- -->
## Example usage
```yaml
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Tag the "gitea.example.com/owner/package:latest" image with some new tags
uses: https://gitea.jbrumond.me/actions/docker-tag@v0.1
with:
registry: https://gitea.example.com
image: owner/package
old-tag: latest
new-tags: |
tag1
tag2
tag3
```