Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/__shared-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ jobs:
linter:
uses: hoverkraft-tech/ci-github-common/.github/workflows/linter.yml@6857ef6d10f704e0998aa4955282f27d1b9be778 # 0.23.1
with:
# FIXME: Remove this on next super-linter release
# FIXME: Remove useless linters on next super-linter release
linter-env: |
VALIDATE_JAVASCRIPT_PRETTIER=false
VALIDATE_KUBERNETES_KUBECONFORM=false
Expand Down
89 changes: 26 additions & 63 deletions .github/workflows/__test-action-docker-build-image.yml
Original file line number Diff line number Diff line change
Expand Up @@ -65,16 +65,12 @@ jobs:
"hoverkraft-tech/ci-github-container/application-test",
`"repository" output is not valid`
);
assert.equal(
builtImage.digests.length,
1,
`"digests" output is not valid`
);
assert.match(
builtImage.digests[0],
/^ghcr\.io\/hoverkraft-tech\/ci-github-container\/application-test@sha256:[a-f0-9]{64}$/,
`"digests" output is not valid`
builtImage.digest,
/^sha256:[a-f0-9]{64}$/,
`"digest" output is not valid`
);
assert.equal(builtImage.image, `ghcr.io/hoverkraft-tech/ci-github-container/application-test@${builtImage.digest}`, `"image" output is not valid`);

// Annotations
assert.match(
Expand Down Expand Up @@ -123,47 +119,18 @@ jobs:
assert.equal(builtImage.tags[0], prShaTag, `"tags" output is not valid`);
assert.equal(builtImage.tags[1], prTag, `"tags" output is not valid`);

assert.equal(
builtImage.images.length,
2,
`"images" output is not valid`
);
assert.equal(
builtImage.images[0],
`ghcr.io/hoverkraft-tech/ci-github-container/application-test:${prShaTag}`,
`"images" output is not valid`
);
assert.equal(
builtImage.images[1],
`ghcr.io/hoverkraft-tech/ci-github-container/application-test:${prTag}`,
`"images" output is not valid`
);
assert.equal(
builtImage.annotations["org.opencontainers.image.version"],
prTag,
`"annotations.org.opencontainers.image.version" output is not valid`
);

} else {
const refTag = `${{ github.ref_name }}`;

assert.equal(builtImage.tags.length, 2, `"tags" output is not valid`);
assert.equal(builtImage.tags[0], refTag, `"tags" output is not valid`);
assert.equal(builtImage.tags[1], "latest", `"tags" output is not valid`);

assert.equal(builtImage.images.length, 2, `"images" output is not valid`);
assert.equal(
builtImage.images[0],
`ghcr.io/hoverkraft-tech/ci-github-container/application-test:${refTag}`,
`"images" output is not valid`
);

assert.equal(
builtImage.images[1],
`ghcr.io/hoverkraft-tech/ci-github-container/application-test:latest`,
`"images" output is not valid`
);

assert.equal(
builtImage.annotations["org.opencontainers.image.version"],
refTag,
Expand All @@ -177,20 +144,18 @@ jobs:
username: ${{ github.repository_owner }}
password: ${{ github.token }}

- name: Assert - Check docker digests
- name: Assert - Check docker image
run: |
DIGESTS=$(echo '${{ steps.build-image.outputs.built-image }}' | jq -r '.digests[]')
for DIGEST in $DIGESTS; do
if ! docker pull "$DIGEST"; then
echo "Failed to pull $DIGEST"
exit 1
fi

if ! docker manifest inspect "$DIGEST"; then
echo "Failed to inspect $DIGEST"
exit 1
fi
done
IMAGE=$(echo '${{ steps.build-image.outputs.built-image }}' | jq -r '.image')
if ! docker pull "$IMAGE"; then
echo "Failed to pull $IMAGE"
exit 1
fi

if ! docker manifest inspect "$IMAGE"; then
echo "Failed to inspect $IMAGE"
exit 1
fi

tests-with-given-tag:
name: Test for "docker/build-image" action with given tag
Expand Down Expand Up @@ -249,19 +214,17 @@ jobs:
username: ${{ github.repository_owner }}
password: ${{ github.token }}

- name: Assert - Check docker digests
- name: Assert - Check docker image
run: |
DIGESTS=$(echo '${{ steps.build-image.outputs.built-image }}' | jq -r '.digests[]')
for DIGEST in $DIGESTS; do
if ! docker pull "$DIGEST"; then
echo "Failed to pull $DIGEST"
exit 1
fi

if ! docker manifest inspect "$DIGEST"; then
echo "Failed to inspect $DIGEST"
exit 1
fi
done
IMAGE=$(echo '${{ steps.build-image.outputs.built-image }}' | jq -r '.image')
if ! docker pull "$IMAGE"; then
echo "Failed to pull $IMAGE"
exit 1
fi

if ! docker manifest inspect "$IMAGE"; then
echo "Failed to inspect $IMAGE"
exit 1
fi

# jscpd:ignore-end
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ jobs:

echo "$MANIFEST"

# Ensure all manifest digests didn't get deleted
# Ensure all manifests digest didn't get deleted
for DIGEST in $(echo "$MANIFEST" | jq -r '.manifests[].digest'); do
IMAGE_MANIFEST="ghcr.io/hoverkraft-tech/ci-github-container/${{ env.IMAGE }}@$DIGEST"
docker pull "$IMAGE_MANIFEST"
Expand Down
36 changes: 17 additions & 19 deletions .github/workflows/__test-workflow-docker-build-images.yml
Original file line number Diff line number Diff line change
Expand Up @@ -90,42 +90,36 @@ jobs:
}

const applicationMultiArchImage = builtImages["test-multi-arch"];

assert.equal(applicationMultiArchImage.name, "test-multi-arch");
assert.equal(applicationMultiArchImage.registry, "ghcr.io");
assert.equal(applicationMultiArchImage.repository,"hoverkraft-tech/ci-github-container/test-multi-arch");
assert.match(applicationMultiArchImage.digest, /^sha256:[0-9a-f]{64}$/);

assert(applicationMultiArchImage.tags.length);
assert(applicationMultiArchImage.images.length);
applicationMultiArchImage.images.forEach(
image => assert.match(
image,
/^ghcr\.io\/hoverkraft-tech\/ci-github-container\/test-multi-arch:[\.a-z0-9-]+$/
/^ghcr\.io\/hoverkraft-tech\/ci-github-container\/test-multi-arch:[\.a-z0-9-]+@sha256:[0-9a-f]{64}$/
)
);

const applicationMonoArchImage = builtImages["test-mono-arch"];
assert.equal(
applicationMonoArchImage.name,
"test-mono-arch"
);
assert.equal(
applicationMonoArchImage.registry,
"ghcr.io"
);

assert.equal(applicationMonoArchImage.name, "test-mono-arch");
assert.equal(applicationMonoArchImage.registry, "ghcr.io");
assert.equal(
applicationMonoArchImage.repository,
"hoverkraft-tech/ci-github-container/test-mono-arch"
);
assert.equal(
applicationMonoArchImage.tags.length,
1
);
assert.equal(
applicationMonoArchImage.images.length,
1
);
assert.match(applicationMonoArchImage.digest, /^sha256:[0-9a-f]{64}$/);

assert.equal(applicationMonoArchImage.tags.length, 1);
assert.equal(applicationMonoArchImage.images.length, 1);
assert.equal(
applicationMonoArchImage.images[0],
"ghcr.io/hoverkraft-tech/ci-github-container/test-mono-arch:0.1.0"
`ghcr.io/hoverkraft-tech/ci-github-container/test-mono-arch:0.1.0@${applicationMonoArchImage.digest}`
);

- uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3.4.0
Expand Down Expand Up @@ -367,8 +361,12 @@ jobs:
expectedTag = `${{ github.ref_name }}`;
}

const digest = `${{ fromJson(needs.act-build-args-secrets-and-registry-caching.outputs.built-images).test-build-args-secrets.digest }}`;
assert(digest.length, `"built-images" output does not contain digest for "test-build-args-secrets" image`);
assert.match(digest, /^sha256:[0-9a-f]{64}$/, `"built-images" output does not contain valid digest for "test-build-args-secrets" image`);

const expectedImage = `ghcr.io/hoverkraft-tech/ci-github-container/test-build-args-secrets`;
const expectedImageTag = `${expectedImage}:${expectedTag}`;
const expectedImageTag = `${expectedImage}:${expectedTag}@${digest}`;

const image = `${{ fromJson(needs.act-build-args-secrets-and-registry-caching.outputs.built-images).test-build-args-secrets.images[0] }}`;

Expand Down
18 changes: 9 additions & 9 deletions .github/workflows/docker-build-images.md
Original file line number Diff line number Diff line change
Expand Up @@ -187,15 +187,15 @@ If a platform entry omits the <code>runs-on</code> field, the following default

### Built image data

| **Parameter** | **Description** | **Example** |
| ---------------------------- | ---------------------------------- | -------------------------------------------------------------------------------------------------------------------------- |
| **<code>name</code>** | Image name | `application` |
| **<code>registry</code>** | Registry where the image is stored | `ghcr.io` |
| **<code>repository</code>** | Repository name | `my-org/my-repo/application` |
| **<code>tags</code>** | List of tags | `["pr-63-5222075","pr-63"]` |
| **<code>images</code>** | List of images | `["ghcr.io/my-org/my-repo/application:pr-63-5222075","ghcr.io/my-org/my-repo/application:pr-63"]` |
| **<code>digests</code>** | List of digests | `["ghcr.io/my-org/my-repo/application@sha256:d31aa93410434ac9dcfc9179cac2cb1fd4d7c27f11527addc40299c7c675f49d"]` |
| **<code>annotations</code>** | List of annotations | `{"org.opencontainers.image.created": "2021-09-30T14:00:00Z","org.opencontainers.image.description": "Application image"}` |
| **Parameter** | **Description** | **Example** |
| ---------------------------- | ---------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **<code>name</code>** | Image name | `application` |
| **<code>registry</code>** | Registry where the image is stored | `ghcr.io` |
| **<code>repository</code>** | Repository name | `my-org/my-repo/application` |
| **<code>tags</code>** | List of tags | `["pr-63-5222075","pr-63"]` |
| **<code>images</code>** | List of images | `["ghcr.io/my-org/my-repo/application:pr-63-5222075@sha256:d31aa93410434ac9dcfc9179cac2cb1fd4d7c27f11527addc40299c7c675f49d","ghcr.io/my-org/my-repo/application:pr-63@sha256:d31aa93410434ac9dcfc9179cac2cb1fd4d7c27f11527addc40299c7c675f49d"]` |
| **<code>digest</code>** | | `sha256:d31aa93410434ac9dcfc9179cac2cb1fd4d7c27f11527addc40299c7c675f49d` |
| **<code>annotations</code>** | List of annotations | `{"org.opencontainers.image.created": "2021-09-30T14:00:00Z","org.opencontainers.image.description": "Application image"}` |

<!-- end outputs -->
<!-- start [.github/ghadocs/examples/] -->
Expand Down
20 changes: 9 additions & 11 deletions .github/workflows/docker-build-images.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,10 @@ on: # yamllint disable-line rule:truthy
"repository": "my-org/my-repo/application",
"tags": ["pr-63-5222075","pr-63"],
"images": [
"ghcr.io/my-org/my-repo/application:pr-63-5222075",
"ghcr.io/my-org/my-repo/application:pr-63"
],
"digests": [
"ghcr.io/my-org/my-repo/application@sha256:d31aa93410434ac9dcfc9179cac2cb1fd4d7c27f11527addc40299c7c675f49d"
"ghcr.io/my-org/my-repo/application:pr-63-5222075@sha256:d31aa93410434ac9dcfc9179cac2cb1fd4d7c27f11527addc40299c7c675f49d",
"ghcr.io/my-org/my-repo/application:pr-63@sha256:d31aa93410434ac9dcfc9179cac2cb1fd4d7c27f11527addc40299c7c675f49d"
],
"digest": "sha256:d31aa93410434ac9dcfc9179cac2cb1fd4d7c27f11527addc40299c7c675f49d",
"annotations": {
"org.opencontainers.image.created": "2021-09-30T14:00:00Z",
"org.opencontainers.image.description": "Application image"
Expand Down Expand Up @@ -441,7 +439,7 @@ jobs:
needs: [prepare-variables, build-images]
runs-on: ${{ fromJson(inputs.runs-on) }}
outputs:
built-images: ${{ steps.built-images.outputs.built-images }}
built-images: ${{ steps.create-images-manifests.outputs.built-images }}
steps:
- id: get-matrix-outputs
uses: hoverkraft-tech/ci-github-common/actions/get-matrix-outputs@6857ef6d10f704e0998aa4955282f27d1b9be778 # 0.23.1
Expand All @@ -463,15 +461,15 @@ jobs:
// Group by image name
const images = {};
builtImages.forEach(builtImage => {
const { name, digests, ...image } = builtImage;
const { name, image, ...rest } = builtImage;
if (!images[name]) {
images[name] = {
name,
digests,
...image,
images: [image],
...rest,
};
} else {
images[name].digests = [...new Set([...images[name].digests, ...digests])];
images[name].images = [...new Set([...images[name].images, image])];
}
});

Expand Down Expand Up @@ -504,7 +502,7 @@ jobs:
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
with:
script: |
const builtImagesInput = `${{ steps.built-images.outputs.built-images }}`;
const builtImagesInput = `${{ steps.create-images-manifests.outputs.built-images }}`;
let builtImages = null;
try {
builtImages = JSON.parse(builtImagesInput);
Expand Down
43 changes: 26 additions & 17 deletions actions/docker/build-image/action.yml
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
---
name: "Build image"
description: "Action to build an image with Docker for a specific platform"
author: Hoverkraft
name: "Docker - Build image"
description: |
Action to build and push a "raw" image with Docker for a specific platform.
This action uses the Docker Buildx plugin to build the image.
It supports caching.
It returns the image digest uri, tags, and annotations, but does not handle it itself.
author: hoverkraft
branding:
icon: package
color: gray-dark
color: blue

outputs:
built-image:
Expand All @@ -14,17 +18,12 @@ outputs:
"name": "application",
"registry": "ghcr.io",
"repository": "my-org/my-repo/application",
"digest": "sha256:d31aa93410434ac9dcfc9179cac2cb1fd4d7c27f11527addc40299c7c675f49d",
"image": "ghcr.io/my-org/my-repo/application@sha256:d31aa93410434ac9dcfc9179cac2cb1fd4d7c27f11527addc40299c7c675f49d",
"tags": [
"pr-63-5222075",
"pr-63"
],
"images": [
"ghcr.io/my-org/my-repo/application:pr-63-5222075",
"ghcr.io/my-org/my-repo/application:pr-63"
],
"digests": [
"ghcr.io/my-org/my-repo/application@sha256:d31aa93410434ac9dcfc9179cac2cb1fd4d7c27f11527addc40299c7c675f49d"
],
"annotations": {
"org.opencontainers.image.created": "2021-09-30T14:00:00Z",
"org.opencontainers.image.description": "Application image"
Expand Down Expand Up @@ -257,8 +256,7 @@ runs:
}

if (builtMetadata["containerimage.digest"] === undefined) {
core.setFailed('Given "metadata"."containerimage.digest" output is undefined.');
return;
return core.setFailed('Given "metadata"."containerimage.digest" output is undefined.');
}

const name = `${{ inputs.image }}`;
Expand All @@ -273,15 +271,26 @@ runs:
.map(tag => tag.replace(/[^\/]+\/[^:]+:(.+)/,'$1').trim())
.filter(tag => tag !== "");

const images = tags.map(tag => `${image}:${tag}`);
const digests = builtMetadata["containerimage.digest"]
.split(",")
.map(digest => {
const cleanedDigest = digest.trim();
return cleanedDigest !== "" ? `${image}@${cleanedDigest}` : null;
return cleanedDigest !== "" ? cleanedDigest : null;
})
.filter(digest => digest !== null);

const uniqueDigests = [...new Set(digests)];
if (uniqueDigests.length === 0) {
return core.setFailed('No valid digests found in "containerimage.digest" output.');
}

if( uniqueDigests.length > 1 ) {
return core.setFailed(`Multiple digests found: ${uniqueDigests.join(", ")}.`);
}

const digest = uniqueDigests[0];
const imageWithDigest = `${image}@${digest}`;

const annotations = `${{ steps.metadata.outputs.annotations }}`
.split("\n")
.map(annotation => {
Expand All @@ -303,8 +312,8 @@ runs:
annotations,
registry,
repository,
images,
digests,
image: imageWithDigest,
digest
};

core.setOutput("built-image", JSON.stringify(builtImage));
Loading
Loading