Skip to content

Commit

Permalink
Merge pull request #26 from Humanitec/WAL-5119-artefacts
Browse files Browse the repository at this point in the history
feat: use add new version endpoint
  • Loading branch information
delca85 authored Sep 14, 2022
2 parents 358ef37 + b3fee18 commit 11bb6c7
Show file tree
Hide file tree
Showing 5 changed files with 109 additions and 80 deletions.
15 changes: 7 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Build Push to Humanitec Action

This action builds a container image from a Dockerfile, then pushes the image to the Humanitec registry and finally
notifies the Humanitec platform that a new build is available.
notifies the Humanitec platform that a new version is available.

## Inputs

Expand All @@ -11,7 +11,7 @@ notifies the Humanitec platform that a new build is available.
to the action using a variable expansion. For example, if the token is store as a secret with name `HUMANITEC_TOKEN`,
the following code should be used to pass it to the action:

```
```yaml
uses: humanitec/build-push-to-humanitec@v1
with:
humanitec-token: ${{ secrets.HUMANITEC_TOKEN }}
Expand All @@ -24,8 +24,7 @@ the following code should be used to pass it to the action:

### `image-name`

_Optional_ The name you want to refer to the image to in the Humanitec Platform. The id must be all lowercase letters,
numbers and the "-" symbol. It cannot start or end with "-".
_Optional_ The name you want to use in the Docker registry. The name can only contain lowercase letters, numbers, hyphens ("-"), and underscores ("_").

### `file`

Expand All @@ -43,7 +42,7 @@ _Optional_ The same as `context`.

_Optional_ Define your own tag for the docker image to be tagged with.

```
```yaml
uses: humanitec/build-push-to-humanitec@v1
with:
humanitec-token: ${{ secrets.HUMANITEC_TOKEN }}
Expand All @@ -56,7 +55,7 @@ _Optional_ Define your own tag for the docker image to be tagged with.
_Optional_ Use `auto-tag` when you want to push tags/release by their git name (e.g. `refs/tags/MY_TAG_NAME`).
> CAUTION: Images produced by this feature can be overwritten by branches with the same name - without a way to restore.

```
```yaml
uses: humanitec/build-push-to-humanitec@v1
with:
humanitec-token: ${{ secrets.HUMANITEC_TOKEN }}
Expand All @@ -69,7 +68,7 @@ _Optional_ Use `auto-tag` when you want to push tags/release by their git name (
_Optional_ Use `additional-docker-arguments` if you need to provide additional arguments (e.g.,build arguments) to the docker build process.
> NOTE: You can provide multiple argument by placing them in one long list of commands, e.g., `--build-arg env1=value1 --build-arg env2=value2`.

```
```yaml
uses: humanitec/build-push-to-humanitec@v1
with:
humanitec-token: ${{ secrets.HUMANITEC_TOKEN }}
Expand All @@ -83,7 +82,7 @@ _None._

## Example usage

```
```yaml
uses: humanitec/build-push-to-humanitec@v1
with:
humanitec-token: ${{ secrets.HUMANITEC_TOKEN }}
Expand Down
87 changes: 51 additions & 36 deletions dist/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ function login(username, password, server) {
* @param {string} file - A path to an alternative dockerfile.
* @param {string} additionalDockerArguments - Additional docker arguments
* @param {string} contextPath - A directory of a build's context.
* @return {string} - The container ID assuming a successful build. falsy otherwise.
* @return {string} - The container ID assuming a successful build, falsy otherwise.
*/
async function build(tag, file, additionalDockerArguments, contextPath) {
try {
Expand All @@ -133,16 +133,30 @@ async function build(tag, file, additionalDockerArguments, contextPath) {
}
}

/**
* Retrieve the digest of an image. Assumes it has already been pushed to the registry.
* @param {string} imageId - The id of the image.
* @return {string} - The digest of the image, falsy otherwise.
*/
async function getDigest(imageId) {
try {
return cp.execSync(`docker image inspect "${imageId}" -f '{{index .RepoDigests 0}}' | cut -d'@' -f2`)
.toString().trim();
} catch (err) {
return false;
}
}


/**
* Pushes the specified local image to a the remote server. Assumes docker.login has already been called.
* @param {string} imagId - The id of the tag being pushed. (Usually returned from docker.build)
* @param {string} imageId - The id of the tag being pushed. (Usually returned from docker.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.
*/
function push(imagId, remoteTag) {
function push(imageId, remoteTag) {
try {
cp.execSync(`docker tag "${imagId}" "${remoteTag}"`);
cp.execSync(`docker tag "${imageId}" "${remoteTag}"`);
cp.execSync(`docker push "${remoteTag}"`);
} catch (err) {
return false;
Expand All @@ -153,6 +167,7 @@ function push(imagId, remoteTag) {
module.exports = {
login,
build,
getDigest,
push,
};

Expand All @@ -171,10 +186,11 @@ const fetch = __nccwpck_require__(467);

/**
* @typedef {Object} Payload
* @property {string} name - The full image name excluding the tag. It should include the registry and the repository.
* @property {string} version - The tag for the docker image to be tagged with.
* @property {string} ref - The ref of the image.
* @property {string} commit - The GIT SHA of the commit being notified about.
* @property {string} branch - The branch this commit is on.
* @property {Array|string} tags - An array of tags refering to this commit.
* @property {string} image - The fully qualidied image name that should be used for this build.
* @property {string} digest - The digest of the version.
*/

module.exports = function(token, orgId, apiHost) {
Expand Down Expand Up @@ -202,26 +218,26 @@ module.exports = function(token, orgId, apiHost) {
}

/**
* Notifies Humanitec that a build has completed
* @param {string} imageName - The name of the image to be added to Huamnitec.
* @param {Payload} payload - Details about the image.
* 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 addNewBuild(imageName, payload) {
function addNewVersion(payload) {
return fetch(
`https://${apiHost}/orgs/${orgId}/modules/${imageName}/builds`, {
`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);
}

return {
getRegistryCredentials,
addNewBuild,
addNewVersion,
};
};

Expand Down Expand Up @@ -5804,9 +5820,11 @@ async function runAction() {
const registryHost = core.getInput('humanitec-registry') || 'registry.humanitec.io';
const apiHost = core.getInput('humanitec-api') || 'api.humanitec.io';
const tag = core.getInput('tag') || '';
const commit = process.env.GITHUB_SHA;
const autoTag = /^\s*(true|1)\s*$/i.test(core.getInput('auto-tag'));
const additionalDockerArguments = core.getInput('additional-docker-arguments') || '';

const ref = process.env.GITHUB_REF;
if (!fs.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? ' +
Expand All @@ -5815,14 +5833,6 @@ async function runAction() {
return;
}

// As the user can choose their image name, we need to ensure it is a valid slug (i.e. lowercase kebab case)
if (! imageName.match(/^[a-z0-9][a-z0-9-]*[a-z0-9]$/)) {
core.error('image-name must be all lowercase letters, numbers and the "-" symbol. ' +
'It cannot start or end with "-".');
core.setFailed('image-name: "${imageName}" is not valid.');
return;
}

if (file != '' && !fs.existsSync(file)) {
core.error(`Cannot find file ${file}`);
core.setFailed('Cannot find file.');
Expand Down Expand Up @@ -5855,13 +5865,15 @@ async function runAction() {

process.chdir(process.env.GITHUB_WORKSPACE);

let localTag = `${orgId}/${imageName}:${process.env.GITHUB_SHA}`;
if (process.env.GITHUB_REF.includes('\/tags\/') && autoTag) {
localTag = `${orgId}/${imageName}:${process.env.GITHUB_REF.replace(/.*\/tags\//, '')}`;
let version = '';
if (autoTag && ref.includes('\/tags\/')) {
version = ref.replace(/.*\/tags\//, '');
} else if (tag) {
localTag = `${orgId}/${imageName}:${tag}`;
version = tag;
} else {
version = commit;
}

const localTag = `${orgId}/${imageName}:${version}`;
const imageId = await docker.build(localTag, file, additionalDockerArguments, context);
if (!imageId) {
core.setFailed('Unable build image from Dockerfile.');
Expand All @@ -5874,20 +5886,23 @@ async function runAction() {
return;
}

let digest = await docker.getDigest(imageId);
if (!digest) {
core.error('Unable to retrieve the digest of the image');
digest = '';
}

const payload = {
commit: process.env.GITHUB_SHA,
image: remoteTag,
name: `${registryHost}/${orgId}/${imageName}`,
type: 'container',
version,
ref,
commit,
digest,
};
if (process.env.GITHUB_REF.includes('\/heads\/')) {
payload.branch = process.env.GITHUB_REF.replace(/.*\/heads\//, '');
payload.tags = [];
} else {
payload.branch = '';
payload.tags = [process.env.GITHUB_REF.replace(/.*\/tags\//, '')];
}

try {
await humanitec.addNewBuild(imageName, payload);
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? ' +
Expand Down
23 changes: 19 additions & 4 deletions docker.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ function login(username, password, server) {
* @param {string} file - A path to an alternative dockerfile.
* @param {string} additionalDockerArguments - Additional docker arguments
* @param {string} contextPath - A directory of a build's context.
* @return {string} - The container ID assuming a successful build. falsy otherwise.
* @return {string} - The container ID assuming a successful build, falsy otherwise.
*/
async function build(tag, file, additionalDockerArguments, contextPath) {
try {
Expand All @@ -50,16 +50,30 @@ async function build(tag, file, additionalDockerArguments, contextPath) {
}
}

/**
* Retrieve the digest of an image. Assumes it has already been pushed to the registry.
* @param {string} imageId - The id of the image.
* @return {string} - The digest of the image, falsy otherwise.
*/
async function getDigest(imageId) {
try {
return cp.execSync(`docker image inspect "${imageId}" -f '{{index .RepoDigests 0}}' | cut -d'@' -f2`)
.toString().trim();
} catch (err) {
return false;
}
}


/**
* Pushes the specified local image to a the remote server. Assumes docker.login has already been called.
* @param {string} imagId - The id of the tag being pushed. (Usually returned from docker.build)
* @param {string} imageId - The id of the tag being pushed. (Usually returned from docker.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.
*/
function push(imagId, remoteTag) {
function push(imageId, remoteTag) {
try {
cp.execSync(`docker tag "${imagId}" "${remoteTag}"`);
cp.execSync(`docker tag "${imageId}" "${remoteTag}"`);
cp.execSync(`docker push "${remoteTag}"`);
} catch (err) {
return false;
Expand All @@ -70,5 +84,6 @@ function push(imagId, remoteTag) {
module.exports = {
login,
build,
getDigest,
push,
};
19 changes: 10 additions & 9 deletions humanitec.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,11 @@ const fetch = require('node-fetch');

/**
* @typedef {Object} Payload
* @property {string} name - The full image name excluding the tag. It should include the registry and the repository.
* @property {string} version - The tag for the docker image to be tagged with.
* @property {string} ref - The ref of the image.
* @property {string} commit - The GIT SHA of the commit being notified about.
* @property {string} branch - The branch this commit is on.
* @property {Array|string} tags - An array of tags refering to this commit.
* @property {string} image - The fully qualidied image name that should be used for this build.
* @property {string} digest - The digest of the version.
*/

module.exports = function(token, orgId, apiHost) {
Expand Down Expand Up @@ -38,25 +39,25 @@ module.exports = function(token, orgId, apiHost) {
}

/**
* Notifies Humanitec that a build has completed
* @param {string} imageName - The name of the image to be added to Huamnitec.
* @param {Payload} payload - Details about the image.
* 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 addNewBuild(imageName, payload) {
function addNewVersion(payload) {
return fetch(
`https://${apiHost}/orgs/${orgId}/modules/${imageName}/builds`, {
`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);
}

return {
getRegistryCredentials,
addNewBuild,
addNewVersion,
};
};
Loading

0 comments on commit 11bb6c7

Please sign in to comment.