Skip to content

Commit

Permalink
Merge pull request #35 from humanitec/fail-on-error
Browse files Browse the repository at this point in the history
fix: fail on http error
  • Loading branch information
johanneswuerbach authored Jan 9, 2023
2 parents cf5063c + 6476400 commit fdcedf4
Show file tree
Hide file tree
Showing 8 changed files with 128 additions and 54 deletions.
1 change: 1 addition & 0 deletions .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
"eslint:recommended",
"plugin:@typescript-eslint/eslint-recommended",
"plugin:@typescript-eslint/recommended",
"plugin:jest/recommended",
"google"
],
"globals": {
Expand Down
22 changes: 18 additions & 4 deletions action.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {describe, expect, test, beforeEach, afterAll} from '@jest/globals';
import {describe, expect, test, beforeEach, afterAll, afterEach} from '@jest/globals';
import {join as pathJoin} from 'node:path';
import {runAction} from './action';
import {randomBytes} from 'crypto';
Expand Down Expand Up @@ -49,6 +49,7 @@ describe('action', () => {
afterAll(async () => {
const res = await humanitecReq(`orgs/${orgId}/artefacts?type=container`, {method: 'GET'});

// eslint-disable-next-line jest/no-standalone-expect
expect(res.status).toBe(200);

const body = await res.json();
Expand All @@ -65,13 +66,15 @@ describe('action', () => {
const res = await humanitecReq(`orgs/${orgId}/artefacts/${artefact.id}`, {method: 'DELETE'});

// Multiple tests might delete artifacts
// eslint-disable-next-line jest/no-standalone-expect
expect([204, 404]).toContain(res.status);
}
});

beforeEach(async () => {
await mkdir(pathJoin(fixtures, '.git'), {recursive: true});

setInput('ref', '');
setInput('humanitec-token', token);
setInput('organization', orgId);
setInput('context', '.');
Expand All @@ -84,9 +87,13 @@ describe('action', () => {
process.env['GITHUB_REPOSITORY'] = repo;
});

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

test('succeeds', async () => {
await runAction();
expect(process.exitCode).toBeFalsy;
expect(process.exitCode).toBeFalsy();

const res = await humanitecReq(`orgs/${orgId}/artefact-versions`, {method: 'GET'});
expect(res.status).toBe(200);
Expand All @@ -105,6 +112,13 @@ describe('action', () => {
);
});

test('fails with an invalid ref', async () => {
setInput('ref', 'invalid');

await runAction();
expect(process.exitCode).toBeTruthy();
});

test('with slashed docker build args', async () => {
setInput('additional-docker-arguments', `
--build-arg version=123 \\
Expand All @@ -114,7 +128,7 @@ describe('action', () => {
`);

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

const res = await humanitecReq(`orgs/${orgId}/artefact-versions`, {method: 'GET'});
expect(res.status).toBe(200);
Expand All @@ -140,7 +154,7 @@ describe('action', () => {
setInput('external-registry-url', 'ghcr.io/humanitec/build-push-to-humanitec');

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

const res = await humanitecReq(`orgs/${orgId}/artefact-versions`, {method: 'GET'});
expect(res.status).toBe(200);
Expand Down
21 changes: 14 additions & 7 deletions action.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import {humanitecFactory} from './humanitec';
import {existsSync} from 'node:fs';
import * as core from '@actions/core';

const DOC_URL = 'https://docs.humanitec.com/guides/connect-ci-setup/connect-ci-pipelines#github-actions-workflow';

/**
* Performs the GitHub action.
*/
Expand All @@ -25,8 +27,7 @@ export async function runAction() {
const ref = core.getInput('ref') || process.env.GITHUB_REF || '';
if (!existsSync(`${process.env.GITHUB_WORKSPACE}/.git`)) {
core.error('It does not look like anything was checked out.');
core.error('Did you run a checkout step before this step? ' +
'http:/docs.humanitec.com/connecting-your-ci#github-actions');
core.error(`Did you run a checkout step before this step? ${DOC_URL}`);
core.setFailed('No .git directory found in workspace.');
return;
}
Expand All @@ -51,8 +52,7 @@ export async function runAction() {
registryCreds = await humanitec.getRegistryCredentials();
} catch (error) {
core.error('Unable to fetch repository credentials.');
core.error('Did you add the token to your Github Secrets? ' +
'http:/docs.humanitec.com/connecting-your-ci#github-actions');
core.error(`Did you add the token to your Github Secrets? ${DOC_URL}`);
core.setFailed('Unable to access Humanitec.');
return;
}
Expand Down Expand Up @@ -85,7 +85,8 @@ export async function runAction() {
}

const remoteTag = `${registryHost}/${imageWithVersion}`;
if (!docker.push(imageId, remoteTag)) {
const pushed = await docker.push(imageId, remoteTag);
if (!pushed) {
core.setFailed('Unable to push image to registry');
return;
}
Expand All @@ -102,8 +103,14 @@ export async function runAction() {
await humanitec.addNewVersion(payload);
} catch (error) {
core.error('Unable to notify Humanitec about build.');
core.error('Did you add the token to your Github Secrets? ' +
'http:/docs.humanitec.com/connecting-your-ci#github-actions');
core.error(`Did you add the token to your Github Secrets? ${DOC_URL}`);

if (error instanceof Error) {
core.error(error);
} else {
core.error(`Unexpected error: ${error}`);
}

core.setFailed('Unable to access Humanitec.');
return;
}
Expand Down
49 changes: 29 additions & 20 deletions dist/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -7763,6 +7763,7 @@ const docker = __importStar(__nccwpck_require__(1723));
const humanitec_1 = __nccwpck_require__(9362);
const node_fs_1 = __nccwpck_require__(7561);
const core = __importStar(__nccwpck_require__(2186));
const DOC_URL = 'https://docs.humanitec.com/guides/connect-ci-setup/connect-ci-pipelines#github-actions-workflow';
/**
* Performs the GitHub action.
*/
Expand All @@ -7783,8 +7784,7 @@ async function runAction() {
const ref = core.getInput('ref') || process.env.GITHUB_REF || '';
if (!(0, node_fs_1.existsSync)(`${process.env.GITHUB_WORKSPACE}/.git`)) {
core.error('It does not look like anything was checked out.');
core.error('Did you run a checkout step before this step? ' +
'http:/docs.humanitec.com/connecting-your-ci#github-actions');
core.error(`Did you run a checkout step before this step? ${DOC_URL}`);
core.setFailed('No .git directory found in workspace.');
return;
}
Expand All @@ -7806,8 +7806,7 @@ async function runAction() {
}
catch (error) {
core.error('Unable to fetch repository credentials.');
core.error('Did you add the token to your Github Secrets? ' +
'http:/docs.humanitec.com/connecting-your-ci#github-actions');
core.error(`Did you add the token to your Github Secrets? ${DOC_URL}`);
core.setFailed('Unable to access Humanitec.');
return;
}
Expand Down Expand Up @@ -7839,7 +7838,8 @@ async function runAction() {
return;
}
const remoteTag = `${registryHost}/${imageWithVersion}`;
if (!docker.push(imageId, remoteTag)) {
const pushed = await docker.push(imageId, remoteTag);
if (!pushed) {
core.setFailed('Unable to push image to registry');
return;
}
Expand All @@ -7855,8 +7855,13 @@ async function runAction() {
}
catch (error) {
core.error('Unable to notify Humanitec about build.');
core.error('Did you add the token to your Github Secrets? ' +
'http:/docs.humanitec.com/connecting-your-ci#github-actions');
core.error(`Did you add the token to your Github Secrets? ${DOC_URL}`);
if (error instanceof Error) {
core.error(error);
}
else {
core.error(`Unexpected error: ${error}`);
}
core.setFailed('Unable to access Humanitec.');
return;
}
Expand Down Expand Up @@ -7928,10 +7933,10 @@ exports.build = build;
* @param {string} remoteTag - The tag that the image will use remotely. (Should indclude registry host, name and tags.)
* @return {boolean} - true if successful, otherwise false.
*/
const push = function (imageId, remoteTag) {
const push = async function (imageId, remoteTag) {
try {
(0, node_child_process_1.execSync)(`docker tag "${imageId}" "${remoteTag}"`);
(0, node_child_process_1.execSync)(`docker push "${remoteTag}"`);
await (0, exec_1.exec)('docker', ['tag', imageId, remoteTag]);
await (0, exec_1.exec)('docker', ['push', remoteTag]);
}
catch (err) {
return false;
Expand Down Expand Up @@ -7976,31 +7981,35 @@ const humanitecFactory = function (token, orgId, apiHost) {
* Fetches the registry credentials from Humanitec
* @return {Promise} - A promise wich returns a {Credentials} object.
*/
function getRegistryCredentials() {
return (0, node_fetch_1.default)(`https://${apiHost}/orgs/${orgId}/registries/humanitec/creds`, {
async function getRegistryCredentials() {
const res = await (0, node_fetch_1.default)(`https://${apiHost}/orgs/${orgId}/registries/humanitec/creds`, {
headers: { 'Authorization': `Bearer ${token}` },
}).then((res) => {
if (res.ok && (res.headers.get('Content-Type') || '').startsWith('application/json')) {
return res.json();
}
throw new Error('Unable to access Humanitec.');
});
const body = await res.text();
if (!res.ok) {
throw new Error(`Unexpected http response ${res.status}: ${body}`);
}
return JSON.parse(body);
}
/**
* Notifies Humanitec that a version has been added
* @param {Payload} payload - Details about the artefact version.
* @return {Promise} - A promise which resolves to true if successful, false otherwise.
*/
function addNewVersion(payload) {
return (0, node_fetch_1.default)(`https://${apiHost}/orgs/${orgId}/artefact-versions`, {
async function addNewVersion(payload) {
const res = await (0, node_fetch_1.default)(`https://${apiHost}/orgs/${orgId}/artefact-versions`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json',
'User-Agent': 'gh-action-build-push-to-humanitec/latest',
},
body: JSON.stringify(payload),
}).then((res) => res.ok);
});
const body = await res.text();
if (!res.ok) {
throw new Error(`Unexpected http response ${res.status}: ${body}`);
}
}
return {
getRegistryCredentials,
Expand Down
6 changes: 3 additions & 3 deletions docker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,10 +56,10 @@ export const build = async function(
* @param {string} remoteTag - The tag that the image will use remotely. (Should indclude registry host, name and tags.)
* @return {boolean} - true if successful, otherwise false.
*/
export const push = function(imageId: string, remoteTag: string): boolean {
export const push = async function(imageId: string, remoteTag: string): Promise<boolean> {
try {
execSync(`docker tag "${imageId}" "${remoteTag}"`);
execSync(`docker push "${remoteTag}"`);
await actionsExec('docker', ['tag', imageId, remoteTag]);
await actionsExec('docker', ['push', remoteTag]);
} catch (err) {
return false;
}
Expand Down
48 changes: 28 additions & 20 deletions humanitec.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import fetch from 'node-fetch';


/**
* @typedef {Object} Credentials
* @property {string} username - The username used to access the registry
Expand All @@ -26,34 +27,41 @@ export const humanitecFactory = function(token: string, orgId: string, apiHost:
* Fetches the registry credentials from Humanitec
* @return {Promise} - A promise wich returns a {Credentials} object.
*/
function getRegistryCredentials() {
return fetch(
`https://${apiHost}/orgs/${orgId}/registries/humanitec/creds`, {
headers: {'Authorization': `Bearer ${token}`},
}).then((res) => {
if (res.ok && (res.headers.get('Content-Type') || '').startsWith('application/json')) {
return res.json();
}
throw new Error('Unable to access Humanitec.');
async function getRegistryCredentials() {
const res = await fetch(`https://${apiHost}/orgs/${orgId}/registries/humanitec/creds`, {
headers: {'Authorization': `Bearer ${token}`},
});

const body = await res.text();

if (!res.ok) {
throw new Error(`Unexpected http response ${res.status}: ${body}`);
}

return JSON.parse(body);
}

/**
* Notifies Humanitec that a version has been added
* @param {Payload} payload - Details about the artefact version.
* @return {Promise} - A promise which resolves to true if successful, false otherwise.
*/
function addNewVersion(payload: unknown): Promise<boolean> {
return fetch(
`https://${apiHost}/orgs/${orgId}/artefact-versions`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json',
'User-Agent': 'gh-action-build-push-to-humanitec/latest',
},
body: JSON.stringify(payload),
}).then((res) => res.ok);
async function addNewVersion(payload: unknown): Promise<void> {
const res = await fetch(`https://${apiHost}/orgs/${orgId}/artefact-versions`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json',
'User-Agent': 'gh-action-build-push-to-humanitec/latest',
},
body: JSON.stringify(payload),
});

const body = await res.text();

if (!res.ok) {
throw new Error(`Unexpected http response ${res.status}: ${body}`);
}
}

return {
Expand Down
34 changes: 34 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit fdcedf4

Please sign in to comment.