2020-11-07 13:04:04 +01:00
|
|
|
import * as core from '@actions/core';
|
|
|
|
import * as exec from '@actions/exec';
|
|
|
|
import * as io from '@actions/io';
|
2021-01-07 18:49:38 -05:00
|
|
|
import * as fs from "fs";
|
2020-11-26 14:01:32 -05:00
|
|
|
import * as path from "path";
|
2020-11-07 13:04:04 +01:00
|
|
|
|
|
|
|
export async function run(): Promise<void> {
|
2021-01-07 18:49:38 -05:00
|
|
|
const imageInput = core.getInput('image', { required: true });
|
2020-11-13 15:12:47 +01:00
|
|
|
const tag = core.getInput('tag') || 'latest';
|
2020-11-17 14:19:05 -05:00
|
|
|
const registry = core.getInput('registry', { required: true });
|
|
|
|
const username = core.getInput('username', { required: true });
|
|
|
|
const password = core.getInput('password', { required: true });
|
2020-11-27 11:44:15 +05:30
|
|
|
const tlsVerify = core.getInput('tls-verify');
|
2021-01-07 18:49:38 -05:00
|
|
|
const digestFileInput = core.getInput('digestfile');
|
2020-11-07 13:04:04 +01:00
|
|
|
|
|
|
|
// get podman cli
|
2020-11-13 15:12:47 +01:00
|
|
|
const podman = await io.which('podman', true);
|
|
|
|
|
2021-01-07 18:49:38 -05:00
|
|
|
const imageToPush = `${imageInput}:${tag}`;
|
2020-11-23 18:11:35 -05:00
|
|
|
let pushMsg = `Pushing ${imageToPush} to ${registry}`;
|
|
|
|
if (username) {
|
|
|
|
pushMsg += ` as ${username}`;
|
|
|
|
}
|
|
|
|
core.info(pushMsg);
|
|
|
|
|
2020-11-13 15:12:47 +01:00
|
|
|
//check if images exist in podman's local storage
|
2020-11-26 14:01:32 -05:00
|
|
|
const checkImages = await execute(podman, ['images', '--format', 'json']);
|
|
|
|
|
|
|
|
const parsedCheckImages = JSON.parse(checkImages.stdout);
|
|
|
|
|
2020-11-17 14:19:05 -05:00
|
|
|
// this is to temporarily solve an issue with the case-sensitive of the property field name. i.e it is Names or names??
|
2020-11-13 15:37:29 +01:00
|
|
|
const nameKeyMixedCase = parsedCheckImages[0] && Object.keys(parsedCheckImages[0]).find(key => 'names' === key.toLowerCase());
|
2020-11-13 15:12:47 +01:00
|
|
|
const imagesFound = parsedCheckImages.
|
2020-11-26 14:01:32 -05:00
|
|
|
filter((image: string) => image[nameKeyMixedCase] && image[nameKeyMixedCase].find((name: string) => name.includes(`${imageToPush}`))).
|
|
|
|
map((image: string ) => image[nameKeyMixedCase]);
|
|
|
|
|
2020-11-13 15:12:47 +01:00
|
|
|
if (imagesFound.length === 0) {
|
|
|
|
//check inside the docker daemon local storage
|
2021-01-07 18:49:38 -05:00
|
|
|
await execute(podman, [ 'pull', `docker-daemon:${imageToPush}` ]);
|
2020-11-13 15:12:47 +01:00
|
|
|
}
|
2020-11-07 13:04:04 +01:00
|
|
|
|
|
|
|
// push image
|
2020-11-23 18:11:35 -05:00
|
|
|
const registryPath = `${registry.replace(/\/$/, '')}/${imageToPush}`;
|
|
|
|
|
|
|
|
const creds: string = `${username}:${password}`;
|
2020-11-26 14:01:32 -05:00
|
|
|
|
2021-01-07 18:49:38 -05:00
|
|
|
const digestFile = digestFileInput || `${imageToPush.replace(":", "_")}_digest.txt`;
|
|
|
|
|
|
|
|
const args = [ 'push',
|
|
|
|
'--quiet',
|
|
|
|
'--digestfile', digestFile,
|
|
|
|
'--creds', creds,
|
|
|
|
imageToPush,
|
|
|
|
registryPath
|
|
|
|
];
|
2020-11-27 11:44:15 +05:30
|
|
|
|
|
|
|
// check if tls-verify is not set to null
|
|
|
|
if (tlsVerify) {
|
|
|
|
args.push(`--tls-verify=${tlsVerify}`);
|
|
|
|
}
|
|
|
|
|
|
|
|
await execute(podman, args);
|
2020-11-26 14:01:32 -05:00
|
|
|
|
2020-11-23 18:11:35 -05:00
|
|
|
core.info(`Successfully pushed ${imageToPush} to ${registryPath}.`);
|
|
|
|
core.setOutput('registry-path', registryPath);
|
2021-01-07 18:49:38 -05:00
|
|
|
|
|
|
|
try {
|
|
|
|
const digest = (await fs.promises.readFile(digestFile)).toString();
|
|
|
|
core.info(digest);
|
|
|
|
core.setOutput('digest', digest);
|
|
|
|
}
|
|
|
|
catch (err) {
|
|
|
|
core.warning(`Failed to read digest file "${digestFile}": ${err}`);
|
|
|
|
}
|
2020-11-07 13:04:04 +01:00
|
|
|
}
|
|
|
|
|
2020-11-26 14:01:32 -05:00
|
|
|
async function execute(executable: string, args: string[], execOptions: exec.ExecOptions = {}): Promise<{ exitCode: number, stdout: string, stderr: string }> {
|
|
|
|
let stdout = "";
|
|
|
|
let stderr = "";
|
2020-11-17 14:19:05 -05:00
|
|
|
|
2020-11-26 14:01:32 -05:00
|
|
|
const finalExecOptions = { ...execOptions };
|
|
|
|
finalExecOptions.ignoreReturnCode = true; // the return code is processed below
|
|
|
|
|
|
|
|
finalExecOptions.listeners = {
|
|
|
|
stdline: (line) => {
|
|
|
|
stdout += line + "\n";
|
|
|
|
},
|
|
|
|
errline: (line) => {
|
|
|
|
stderr += line + "\n"
|
2020-11-13 15:12:47 +01:00
|
|
|
},
|
2020-11-26 14:01:32 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
const exitCode = await exec.exec(executable, args, finalExecOptions);
|
|
|
|
|
|
|
|
if (execOptions.ignoreReturnCode !== true && exitCode !== 0) {
|
|
|
|
// Throwing the stderr as part of the Error makes the stderr show up in the action outline, which saves some clicking when debugging.
|
|
|
|
let error = `${path.basename(executable)} exited with code ${exitCode}`;
|
|
|
|
if (stderr) {
|
|
|
|
error += `\n${stderr}`;
|
2020-11-07 13:04:04 +01:00
|
|
|
}
|
2020-11-26 14:01:32 -05:00
|
|
|
throw new Error(error);
|
2020-11-17 14:19:05 -05:00
|
|
|
}
|
2020-11-26 14:01:32 -05:00
|
|
|
|
|
|
|
return {
|
|
|
|
exitCode, stdout, stderr
|
|
|
|
};
|
2020-11-07 13:04:04 +01:00
|
|
|
}
|
|
|
|
|
2020-11-17 14:19:05 -05:00
|
|
|
run().catch(core.setFailed);
|