Skip to content

Commit

Permalink
Merge pull request #40 from humanitec/existing-image
Browse files Browse the repository at this point in the history
feat: allow to use an existing image
  • Loading branch information
johanneswuerbach authored Jan 23, 2023
2 parents 49e2de2 + c9294b3 commit cb8a2f9
Show file tree
Hide file tree
Showing 5 changed files with 107 additions and 20 deletions.
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ the following code should be used to pass it to the action:

_Optional_ The name you want to use in the Docker registry. The name can only contain lowercase letters, numbers, hyphens ("-"), and underscores ("_").

### `existing-image`

_Optional_ Use an existing image instead of building an image. The image name needs to include a tag or digest.

### `file`

_Optional_ A path to an alternative Dockerfile.
Expand Down
51 changes: 49 additions & 2 deletions action.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,20 @@ import {runAction} from './action';
import {randomBytes} from 'crypto';
import {mkdir} from 'node:fs/promises';
import {createApiClient} from './humanitec';
import {exec as actionsExec} from '@actions/exec';

// Emulate https://github.com/actions/toolkit/blob/819157bf8/packages/core/src/core.ts#L128
const setInput = (name: string, value: string): void => {
process.env[`INPUT_${name.replace(/ /g, '_').toUpperCase()}`] = value;
const setInputWithState = (state: string[], name: string, value: string): void => {
const envName = `INPUT_${name.replace(/ /g, '_').toUpperCase()}`;
process.env[envName] = value;

state.push(envName);
};

const clearInputs = (envNames: string[]) => {
for (const envName of envNames) {
process.env[envName] = '';
}
};

const fixtures = pathJoin(__dirname, './fixtures');
Expand All @@ -32,6 +42,10 @@ describe('action', () => {
let repo: string;
let commit: string;

const inputs: string[] = [];
const setInput = (name: string, value: string): void => {
return setInputWithState(inputs, name, value);
};

afterAll(async () => {
const res = await humanitecClient.orgsOrgIdArtefactsGet(orgId, 'container');
Expand Down Expand Up @@ -73,6 +87,7 @@ describe('action', () => {

afterEach(() => {
process.exitCode = undefined;
clearInputs(inputs);
});

test('succeeds', async () => {
Expand Down Expand Up @@ -147,4 +162,36 @@ describe('action', () => {
),
);
});

test('supports pushing an already existing image', async () => {
actionsExec('docker', ['pull', 'hello-world:latest']);

setInput('existing-image', 'hello-world:latest');

await runAction();
expect(process.exitCode).toBeFalsy();

const res = await humanitecClient.orgsOrgIdArtefactVersionsGet(orgId);
expect(res.status).toBe(200);
expect(res.data).toEqual(
expect.arrayContaining(
[
expect.objectContaining({
commit: commit,
name: `registry.humanitec.io/${orgId}/${repo}`,
}),
],
),
);
});

test('fails when trying to specific an image on the same registry with a different tag', async () => {
actionsExec('docker', ['pull', 'hello-world:latest']);
actionsExec('docker', ['tag', 'hello-world:latest', `registry.humanitec.io/${orgId}/hello-world:latest`]);

setInput('existing-image', `registry.humanitec.io/${orgId}/hello-world:latest`);

await runAction();
expect(process.exitCode).toBeTruthy();
});
});
35 changes: 26 additions & 9 deletions action.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ export async function runAction() {
const token = core.getInput('humanitec-token', {required: true});
const orgId = core.getInput('organization', {required: true});
const imageName = core.getInput('image-name') || (process.env.GITHUB_REPOSITORY || '').replace(/.*\//, '');
const existingImage = core.getInput('existing-image') || '';
const context = core.getInput('context') || core.getInput('dockerfile') || '.';
const file = core.getInput('file') || '';
let registryHost = core.getInput('humanitec-registry') || 'registry.humanitec.io';
Expand Down Expand Up @@ -76,18 +77,34 @@ export async function runAction() {
version = commit;
}
const imageWithVersion = `${imageName}:${version}`;
const localTag = `${orgId}/${imageWithVersion}`;
const imageId = await docker.build(localTag, file, additionalDockerArguments, context);
if (!imageId) {
core.setFailed('Unable build image from Dockerfile.');
return;

let imageId;
if (existingImage) {
imageId = existingImage;
} else {
const localTag = `${orgId}/${imageWithVersion}`;
imageId = await docker.build(localTag, file, additionalDockerArguments, context);
if (!imageId) {
core.setFailed('Unable build image from Dockerfile.');
return;
}
}

const remoteTag = `${registryHost}/${imageWithVersion}`;
const pushed = await docker.push(imageId, remoteTag);
if (!pushed) {
core.setFailed('Unable to push image to registry');
return;
if (existingImage !== remoteTag) {
if (existingImage.startsWith(registryHost)) {
core.setFailed(
`The provided image seems to be already pushed, but the version tag is not matching.\n` +
`Expected: ${remoteTag}\n` +
`Provided: ${existingImage}`);
return;
}

const pushed = await docker.push(imageId, remoteTag);
if (!pushed) {
core.setFailed('Unable to push image to registry');
return;
}
}

const payload: AddArtefactVersionPayloadRequest = {
Expand Down
4 changes: 4 additions & 0 deletions action.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ inputs:
description: 'The name you want to refer to the image to in the Humanitec Platform.'
required: false
default: ''
existing-image:
description: 'Use an existing image instead of building an image. The image name needs to include a tag or digest.'
required: false
default: ''
tag:
description: 'Use tag when you want to bring your own tag.'
required: false
Expand Down
33 changes: 24 additions & 9 deletions dist/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -41085,6 +41085,7 @@ async function runAction() {
const token = core.getInput('humanitec-token', { required: true });
const orgId = core.getInput('organization', { required: true });
const imageName = core.getInput('image-name') || (process.env.GITHUB_REPOSITORY || '').replace(/.*\//, '');
const existingImage = core.getInput('existing-image') || '';
const context = core.getInput('context') || core.getInput('dockerfile') || '.';
const file = core.getInput('file') || '';
let registryHost = core.getInput('humanitec-registry') || 'registry.humanitec.io';
Expand Down Expand Up @@ -41138,17 +41139,31 @@ async function runAction() {
version = commit;
}
const imageWithVersion = `${imageName}:${version}`;
const localTag = `${orgId}/${imageWithVersion}`;
const imageId = await docker.build(localTag, file, additionalDockerArguments, context);
if (!imageId) {
core.setFailed('Unable build image from Dockerfile.');
return;
let imageId;
if (existingImage) {
imageId = existingImage;
}
else {
const localTag = `${orgId}/${imageWithVersion}`;
imageId = await docker.build(localTag, file, additionalDockerArguments, context);
if (!imageId) {
core.setFailed('Unable build image from Dockerfile.');
return;
}
}
const remoteTag = `${registryHost}/${imageWithVersion}`;
const pushed = await docker.push(imageId, remoteTag);
if (!pushed) {
core.setFailed('Unable to push image to registry');
return;
if (existingImage !== remoteTag) {
if (existingImage.startsWith(registryHost)) {
core.setFailed(`The provided image seems to be already pushed, but the version tag is not matching.\n` +
`Expected: ${remoteTag}\n` +
`Provided: ${existingImage}`);
return;
}
const pushed = await docker.push(imageId, remoteTag);
if (!pushed) {
core.setFailed('Unable to push image to registry');
return;
}
}
const payload = {
name: `${registryHost}/${imageName}`,
Expand Down

0 comments on commit cb8a2f9

Please sign in to comment.