Skip to content
Open
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
1 change: 1 addition & 0 deletions .github/workflows/__main-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ jobs:
uses: ./.github/workflows/__shared-ci.yml
permissions:
actions: read
attestations: write
contents: read
id-token: write
issues: write
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/__pull-request-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ jobs:
uses: ./.github/workflows/__shared-ci.yml
permissions:
actions: read
attestations: write
contents: read
id-token: write
issues: write
Expand Down
14 changes: 14 additions & 0 deletions .github/workflows/__shared-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -141,3 +141,17 @@ jobs:
pull-requests: read
secrets:
CI_BOT_APP_PRIVATE_KEY: ${{ secrets.CI_BOT_APP_PRIVATE_KEY }}

test-workflow-docker-build-images-attestation:
name: Test docker build images - Attestation
needs: linter
uses: ./.github/workflows/__test-workflow-docker-build-images-attestation.yml
permissions:
attestations: write
contents: read
id-token: write
issues: read
packages: write
pull-requests: read
secrets:
CI_BOT_APP_PRIVATE_KEY: ${{ secrets.CI_BOT_APP_PRIVATE_KEY }}
177 changes: 177 additions & 0 deletions .github/workflows/__test-workflow-docker-build-images-attestation.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
---
name: Test for "docker-build-images" workflow - Attestation
run-name: Test for "docker-build-images" workflow - Attestation

on: # yamllint disable-line rule:truthy
workflow_call:
secrets:
CI_BOT_APP_PRIVATE_KEY:
required: false

permissions: {}

# jscpd:ignore-start
jobs:
arrange:
name: Arrange
runs-on: ubuntu-latest
outputs:
matrix: ${{ steps.define-matrix.outputs.result }}
steps:
- run: |
if [ -z "${{ secrets.GITHUB_TOKEN }}" ]; then
echo "GitHub token secret is not set"
exit 1
fi

- id: define-matrix
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
with:
result-encoding: json
script: |
let imageNamePrefix;

if (context.eventName === "pull_request") {
const pullRequestNumber = context.payload.pull_request?.number;
if (!pullRequestNumber) {
throw new Error("Pull request number is not available in the event payload");
}
imageNamePrefix = `pr-${pullRequestNumber}`;
} else {
imageNamePrefix = process.env.GITHUB_REF_NAME;
if (!imageNamePrefix) {
throw new Error("GITHUB_REF_NAME environment variable is not set");
}
}

imageNamePrefix = `test-${imageNamePrefix}-${context.runNumber}`.replace(/\//g, "-");

const tag = '0.1.0';

const matrix = {
include: [
{
name: "mono-arch - attested",
"image-name": `${imageNamePrefix}-mono-arch-attested`,
platforms: '["linux/amd64"]',
attest: true,
tag
},
{
name: "multi-arch - attested",
"image-name": `${imageNamePrefix}-multi-arch-attested`,
platforms: '["linux/amd64","linux/arm64"]',
attest: true,
tag
},
{
name: "mono-arch - not attested",
"image-name": `${imageNamePrefix}-mono-arch-not-attested`,
platforms: '["linux/amd64"]',
attest: false,
tag
}
]
};
return matrix;

act-build-images:
name: Act - Build images (${{ matrix.name }})
needs: arrange
strategy:
fail-fast: false
matrix: ${{ fromJson(needs.arrange.outputs.matrix) }}
uses: ./.github/workflows/docker-build-images.yml
permissions:
attestations: write
contents: read
id-token: write
issues: read
packages: write
pull-requests: read
secrets:
oci-registry-password: ${{ secrets.GITHUB_TOKEN }}
build-secret-github-app-key: ${{ secrets.CI_BOT_APP_PRIVATE_KEY }}
with:
attest: ${{ matrix.attest }}
sign: false
images: |
[
{
"name": "${{ matrix.image-name }}",
"context": ".",
"dockerfile": "./tests/application/Dockerfile",
"build-args": { "BUILD_RUN_ID": "${{ github.run_id }}" },
"target": "base",
"platforms": ${{ matrix.platforms }},
"tag": "${{ matrix.tag }}"
}
]

assert-images-attestation:
name: Assert - Images attestation (${{ matrix.name }})
needs: [arrange, act-build-images]
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix: ${{ fromJson(needs.arrange.outputs.matrix) }}
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false

- uses: ./actions/docker/setup
with:
oci-registry: ghcr.io
oci-registry-username: ${{ github.repository_owner }}
oci-registry-password: ${{ secrets.GITHUB_TOKEN }}

- name: Verify image attestation
env:
SHOULD_HAVE_ATTESTATION: ${{ matrix.attest }}
IMAGE_NAME: "${{ matrix.image-name }}:${{ matrix.tag }}"
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
if [ "${SHOULD_HAVE_ATTESTATION}" = "true" ]; then
echo "Verifying attestation for image: ${IMAGE_NAME}"
gh attestation verify \
oci://ghcr.io/hoverkraft-tech/ci-github-container/"${IMAGE_NAME}" \
--repo hoverkraft-tech/ci-github-container
else
echo "Image ${IMAGE_NAME} should not have an attestation"
if gh attestation verify \
oci://ghcr.io/hoverkraft-tech/ci-github-container/"${IMAGE_NAME}" \
--repo hoverkraft-tech/ci-github-container; then
echo "::error::Image ${IMAGE_NAME} should not be attested but attestation verification succeeded"
exit 1
fi
echo "Attestation verification failed as expected for non-attested image ${IMAGE_NAME}"
fi

cleanup:
name: Cleanup ephemeral test packages
runs-on: ubuntu-latest
if: always()
needs:
- arrange
- assert-images-attestation
strategy:
fail-fast: false
matrix: ${{ fromJson(needs.arrange.outputs.matrix) }}
permissions:
packages: write
steps:
- name: Delete test packages
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
env:
IMAGE_NAME: ${{ matrix.image-name }}
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
await github.rest.packages.deletePackageForOrg({
package_type: 'container',
package_name: `ci-github-container/${process.env.IMAGE_NAME}`,
org: 'hoverkraft-tech',
});

# jscpd:ignore-end
10 changes: 10 additions & 0 deletions .github/workflows/docker-build-images.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ This includes [multi-platform](https://docs.docker.com/build/building/multi-plat
- **`issues`**: `read`
- **`packages`**: `write`
- **`pull-requests`**: `read`
- **`attestations`**: `write` (only required when `attest: true`)

<!-- overview:end -->

Expand All @@ -58,6 +59,7 @@ jobs:
issues: read
packages: write
pull-requests: read
attestations: write # only required when `attest: true`
secrets:
# Password or GitHub token (`packages:read` and `packages:write` scopes) configuration used to log against OCI registries.
# Accepts either a single password/token string (default format) or a JSON object using the same keys as `oci-registry`.
Expand Down Expand Up @@ -178,6 +180,12 @@ jobs:
#
# Default: `true`
sign: true

# Generate build provenance attestations for built images.
# See [attest-images](../../actions/docker/attest-images/README.md).
#
# Default: `false`
attest: false
````

<!-- usage:end -->
Expand Down Expand Up @@ -229,6 +237,8 @@ jobs:
| | <!-- textlint-disable --><pre lang="ini">[registry."my-registry.local:5000"]&#13; http = true&#13; insecure = true</pre><!-- textlint-enable --> | | | |
| **`sign`** | Sign built images. | **false** | **boolean** | `true` |
| | See [sign-images](../../actions/docker/sign-images/README.md). | | | |
| **`attest`** | Generate build provenance attestations for built images. | **false** | **boolean** | `false` |
| | See [attest-images](../../actions/docker/attest-images/README.md). | | | |

<!-- inputs:end -->

Expand Down
37 changes: 36 additions & 1 deletion .github/workflows/docker-build-images.yml
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,13 @@ on: # yamllint disable-line rule:truthy
type: boolean
default: true
required: false
attest:
description: |
Generate build provenance attestations for built images.
See [attest-images](../../actions/docker/attest-images/README.md).
type: boolean
default: false
required: false
secrets:
oci-registry-password:
description: |
Expand Down Expand Up @@ -461,7 +468,8 @@ jobs:
permissions:
contents: read
packages: write
id-token: write # Needed for signing images
id-token: write # Needed for signing and attesting images
attestations: write # Needed for attesting images
needs: [prepare-variables, build-images]
runs-on: ${{ fromJson(inputs.runs-on) }}
outputs:
Expand Down Expand Up @@ -542,3 +550,30 @@ jobs:
with:
images: ${{ steps.get-images-to-sign.outputs.images-to-sign }}
github-token: ${{ secrets.GITHUB_TOKEN }}

- id: get-images-to-attest
if: inputs.attest
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
env:
BUILT_IMAGES_INPUT: ${{ steps.create-images-manifests.outputs.built-images }}
with:
script: |
const builtImagesInput = process.env.BUILT_IMAGES_INPUT;
let builtImages = null;
try {
builtImages = JSON.parse(builtImagesInput);
} catch (error) {
throw new Error(`"built-images" input is not a valid JSON: ${error}`);
}

// Get images to attest
const imagesToAttest = Object.values(builtImages).map(image => image.images).flat();
if (imagesToAttest.length > 0) {
core.setOutput('images-to-attest', JSON.stringify(imagesToAttest));
}

- uses: ./../self-workflow/actions/docker/attest-images
if: steps.get-images-to-attest.outputs.images-to-attest
with:
images: ${{ steps.get-images-to-attest.outputs.images-to-attest }}
github-token: ${{ secrets.GITHUB_TOKEN }}
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ _Actions that operate on OCI images across their build, metadata, and lifecycle

#### - [Setup](actions/docker/setup/README.md)

#### - [Attest images](actions/docker/attest-images/README.md)

#### - [Sign images](actions/docker/sign-images/README.md)

### Helm
Expand Down
Loading