Skip to content

Commit 45bb558

Browse files
committed
CASMINST-7279: redesign build-sign-scan acrion as reusable workflow
1 parent ee03ec4 commit 45bb558

8 files changed

Lines changed: 989 additions & 5 deletions

File tree

.github/workflows/build-sign-scan.yaml

Lines changed: 471 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 239 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,239 @@
1+
#
2+
# MIT License
3+
#
4+
# (C) Copyright 2025 Hewlett Packard Enterprise Development LP
5+
#
6+
# Permission is hereby granted, free of charge, to any person obtaining a
7+
# copy of this software and associated documentation files (the "Software"),
8+
# to deal in the Software without restriction, including without limitation
9+
# the rights to use, copy, modify, merge, publish, distribute, sublicense,
10+
# and/or sell copies of the Software, and to permit persons to whom the
11+
# Software is furnished to do so, subject to the following conditions:
12+
#
13+
# The above copyright notice and this permission notice shall be included
14+
# in all copies or substantial portions of the Software.
15+
#
16+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19+
# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
20+
# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
21+
# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22+
# OTHER DEALINGS IN THE SOFTWARE.
23+
#
24+
name: Test build-sign-scan reusable workflow
25+
26+
on:
27+
push:
28+
paths:
29+
- .github/workflows/test-build-sign-scan.yaml
30+
- tests/build-sign-scan/**
31+
workflow_dispatch:
32+
33+
jobs:
34+
test-docker-build-local:
35+
uses: ./.github/workflows/build-sign-scan.yaml
36+
with:
37+
context_path: build-sign-scan/tests/alpine
38+
docker_tag: artifactory.algol60.net/csm-docker-backup/unstable/test-build-sign-scan:test-docker-build-local-1
39+
docker_additional_tags: |
40+
artifactory.algol60.net/csm-docker-backup/unstable/test-build-sign-scan:test-docker-build-local-2
41+
docker_build_args: |
42+
--label='Random label 1'
43+
--label="Random label 2"
44+
env: |
45+
VAR1=var1
46+
VAR2='var2 var2'
47+
VAR3="var3 var3"
48+
VAR4='var4
49+
var4'
50+
VAR5="var4
51+
var4"
52+
docker_push: false
53+
snyk: false
54+
sign: false
55+
secrets:
56+
docker_username: ${{ secrets.ARTIFACTORY_ALGOL60_USERNAME }}
57+
docker_password: ${{ secrets.ARTIFACTORY_ALGOL60_TOKEN }}
58+
docker_secrets: |
59+
SLES_REPO_USERNAME='${{ secrets.ARTIFACTORY_ALGOL60_READONLY_USERNAME }}'
60+
SLES_REPO_PASSWORD="${{ secrets.ARTIFACTORY_ALGOL60_READONLY_TOKEN }}"
61+
gcp_workload_identity_provider: ${{ secrets.COSIGN_GCP_WORKLOAD_IDENTITY_PROVIDER_RSA }}
62+
gcp_service_account: ${{ secrets.COSIGN_GCP_SERVICE_ACCOUNT_RSA }}
63+
gcp_cosign_key: ${{ secrets.COSIGN_KEY_RSA }}
64+
snyk_token: ${{ secrets.SNYK_TOKEN }}
65+
66+
test-docker-build-single-platform:
67+
uses: ./.github/workflows/build-sign-scan.yaml
68+
with:
69+
context_path: build-sign-scan/tests/alpine
70+
docker_tag: artifactory.algol60.net/csm-docker-backup/unstable/test-build-sign-scan:test-docker-build-single-platform-1
71+
docker_additional_tags: |
72+
artifactory.algol60.net/csm-docker-backup/unstable/test-build-sign-scan:test-docker-build-single-platform-2
73+
snyk: true
74+
sign: true
75+
secrets: inherit
76+
77+
test-docker-build-multi-platform:
78+
uses: ./.github/workflows/build-sign-scan.yaml
79+
with:
80+
context_path: build-sign-scan/tests/alpine
81+
docker_tag: artifactory.algol60.net/csm-docker-backup/unstable/test-build-sign-scan:test-docker-build-multi-platform-1
82+
docker_build_platforms: linux/amd64,linux/arm64
83+
docker_additional_tags: |
84+
artifactory.algol60.net/csm-docker-backup/unstable/test-build-sign-scan:test-docker-build-multi-platform-2
85+
snyk: true
86+
sign: true
87+
secrets: inherit
88+
89+
test-docker-build-google:
90+
uses: ./.github/workflows/build-sign-scan.yaml
91+
with:
92+
context_path: build-sign-scan/tests/alpine
93+
docker_login: false
94+
docker_oidc: true
95+
docker_tag: us-docker.pkg.dev/hpe-stage-csm-release/csm-docker/unstable/test-build-sign-scan:test-docker-build-google-1
96+
docker_build_platforms: linux/amd64,linux/arm64
97+
docker_additional_tags: |
98+
us-docker.pkg.dev/hpe-stage-csm-release/csm-docker/unstable/test-build-sign-scan:test-docker-build-google-2
99+
snyk: true
100+
sign: true
101+
secrets: inherit
102+
103+
test-makefile-local:
104+
uses: ./.github/workflows/build-sign-scan.yaml
105+
with:
106+
context_path: build-sign-scan/tests/alpine
107+
make_target: local
108+
docker_login: false
109+
docker_additional_tags: |
110+
artifactory.algol60.net/csm-docker-backup/unstable/test-build-sign-scan:test-makefile-local-2
111+
docker_build_args: |
112+
--label='Random label 1'
113+
--label="Random label 2"
114+
env: |
115+
VERSION=test-makefile-local-1
116+
VAR1=var1
117+
VAR2='var2 var2'
118+
VAR3="var3 var3"
119+
VAR4='var4
120+
var4'
121+
VAR5="var4
122+
var4"
123+
snyk: false
124+
sign: false
125+
secrets:
126+
docker_username: ${{ secrets.ARTIFACTORY_ALGOL60_USERNAME }}
127+
docker_password: ${{ secrets.ARTIFACTORY_ALGOL60_TOKEN }}
128+
docker_secrets: |
129+
SLES_REPO_USERNAME=${{ secrets.ARTIFACTORY_ALGOL60_READONLY_USERNAME }}
130+
SLES_REPO_PASSWORD=${{ secrets.ARTIFACTORY_ALGOL60_READONLY_TOKEN }}
131+
gcp_workload_identity_provider: ${{ secrets.COSIGN_GCP_WORKLOAD_IDENTITY_PROVIDER_RSA }}
132+
gcp_service_account: ${{ secrets.COSIGN_GCP_SERVICE_ACCOUNT_RSA }}
133+
gcp_cosign_key: ${{ secrets.COSIGN_KEY_RSA }}
134+
snyk_token: ${{ secrets.SNYK_TOKEN }}
135+
136+
test-makefile-single-platform:
137+
uses: ./.github/workflows/build-sign-scan.yaml
138+
with:
139+
context_path: build-sign-scan/tests/alpine
140+
make_target: unstable
141+
env: |
142+
PLATFORM=linux/amd64
143+
VERSION=test-makefile-single-platform-1
144+
docker_additional_tags: |
145+
artifactory.algol60.net/csm-docker-backup/unstable/test-build-sign-scan:test-makefile-single-platform-2
146+
snyk: true
147+
sign: true
148+
secrets: inherit
149+
150+
test-makefile-multi-platform:
151+
uses: ./.github/workflows/build-sign-scan.yaml
152+
with:
153+
context_path: build-sign-scan/tests/alpine
154+
make_target: unstable
155+
env: |
156+
VERSION=test-makefile-multi-platform-1
157+
docker_additional_tags: |
158+
artifactory.algol60.net/csm-docker-backup/unstable/test-build-sign-scan:test-makefile-multi-platform-2
159+
snyk: true
160+
sign: true
161+
secrets: inherit
162+
163+
test-makefile-google:
164+
uses: ./.github/workflows/build-sign-scan.yaml
165+
with:
166+
context_path: build-sign-scan/tests/alpine
167+
make_target: unstable
168+
docker_login: false
169+
docker_oidc: true
170+
env: |
171+
REGISTRY=us-docker.pkg.dev/hpe-stage-csm-release/csm-docker/unstable
172+
VERSION=test-makefile-google-1
173+
docker_additional_tags: |
174+
us-docker.pkg.dev/hpe-stage-csm-release/csm-docker/unstable/test-build-sign-scan:test-makefile-google-2
175+
snyk: true
176+
sign: true
177+
secrets: inherit
178+
179+
review:
180+
runs-on: ubuntu-latest
181+
182+
permissions:
183+
contents: 'read'
184+
id-token: 'write'
185+
186+
needs:
187+
- test-docker-build-local
188+
- test-docker-build-single-platform
189+
- test-docker-build-multi-platform
190+
- test-docker-build-google
191+
- test-makefile-local
192+
- test-makefile-single-platform
193+
- test-makefile-multi-platform
194+
- test-makefile-google
195+
196+
steps:
197+
- name: Report Test Results
198+
env:
199+
NEEDS_CONTEXT: ${{ toJSON(needs) }}
200+
run: |
201+
function assert() {
202+
if ! [[ "${2}" =~ ${3} ]]; then
203+
echo "::error::Test ${test_name}: ${1}: expected \"${3}\", got \"${2}\"."
204+
exit_code=$((exit_code+1))
205+
fi
206+
}
207+
208+
echo "$NEEDS_CONTEXT" > outputs.json
209+
exit_code=0
210+
for build_type in docker-build makefile; do
211+
for image_type in local single-platform multi-platform google; do
212+
test_name="test-${build_type}-${image_type}"
213+
214+
# Test built tags
215+
image_name=artifactory.algol60.net/csm-docker-backup/unstable/test-build-sign-scan
216+
test "${image_type}" == google && image_name=us-docker.pkg.dev/hpe-stage-csm-release/csm-docker/unstable/test-build-sign-scan
217+
tags=$(jq -r ".\"${test_name}\".outputs.image_tags" outputs.json | tr ',' '\n' | sort | tr '\n' ',' | sed -e 's/,$//')
218+
assert tags "${tags}" "${image_name}:${test_name}-1,${image_name}:${test_name}-2"
219+
220+
# Test built platforms
221+
platforms=$(jq -r ".\"${test_name}\".outputs.image_platforms" outputs.json)
222+
test "${image_type}" == single && assert platforms "${platforms}" '["linux/amd64"]'
223+
test "${image_type}" != local -a "${image_type}" != single-platform && assert platforms "${platforms}" '["linux/amd64","linux/arm64"]'
224+
225+
# Test Snyk output
226+
snyk_summary=$(jq -r ".\"${test_name}\".outputs.snyk_summary" outputs.json)
227+
test "${image_type}" != local && assert snyk_summary "${snyk_summary}" 'crtitical: [0-9]+, high: [0-9]+, medium: [0-9]+, low: [0-9]+'
228+
229+
# Test Trivy output
230+
trivy_summary=$(jq -r ".\"${test_name}\".outputs.trivy_summary" outputs.json)
231+
test "${image_type}" != local && assert trivy_summary "${trivy_summary}" '(Tests: [0-9]+ \(SUCCESSES: [0-9]+, FAILURES: [0-9]+\)|PASS)'
232+
233+
# Test Scan output
234+
sign_summary=$(jq -r ".\"${test_name}\".outputs.sign_summary" outputs.json)
235+
test "${image_type}" != local && assert sign_summary "${sign_summary}" 'Signed .+ with Cosign'
236+
done
237+
done
238+
exit $exit_code
239+
shell: bash

README.md

Lines changed: 76 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,78 @@
1-
# github-actions
2-
Cray-HPE Shared GitHub Actions
1+
# Resuable Github Actions and Workflows
32

4-
A place for shared GitHub actions to be used.
3+
## Build Sign Scan Reusable Workflow
54

6-
## build-sign-scan
7-
Builds, pushes, signs, and scans docker images. Currently used in the `container-images` repo
5+
### Location
6+
7+
Build, Sign and Scan reusable workflow is located in `.github/workflows//build-sign-scan.yaml` file.
8+
9+
### Features
10+
11+
- Container image builds via provided `Makefile` or without it (i.e. only `Dockerfle` is present).
12+
- Multi-platform builds (via `docker_platform` parameter).
13+
- Push into generic private registry (basic authentication) or Google Artifact Registry (OIDC auth).
14+
- Scan with Snyk.
15+
- Scan with Trivy.
16+
- Signing with Sigstore Cosign using key stored in Google KMS.
17+
18+
### Requirements
19+
Reusable workflow follows dry-out configuration approach. All input parameters are optional and have reasonable defaults.
20+
The only mandatory parameter is `docker_tag` (if no `Makefile` is provided) or `make_target` (if `Makefile` is provided).
21+
Also, access to `secrets` context is required. Please refer to workflow file for detailed description of input parameters.
22+
23+
### Minimal Configuration
24+
25+
- If `Makefile` is provided:
26+
27+
jobs:
28+
build-sign-scan:
29+
uses: Cray-HPE/.github/workflows//build-sign-scan.yaml@build-sign-scan-workflow/v1
30+
with:
31+
make_target: ${{ github.ref_type == 'tag' && 'stable' || 'unstable' }}
32+
secrets: inherit
33+
34+
NOTE: `Makefle` must honor `DOCKER_BUILD_ARGS` environment variable.
35+
36+
- If `Makefile` is not provided:
37+
38+
jobs:
39+
build-sign-scan:
40+
uses: Cray-HPE/.github/workflows//build-sign-scan.yaml@build-sign-scan-workflow/v1
41+
with:
42+
docker_tag: artifactory.algol60.net/csm-docker/${{ github.ref_type == 'tag' && 'stable' || 'unstable' }}/my-image:tag
43+
secrets: inherit
44+
45+
In these examples, `Makefile` target `stable` is called when build is invoked on git tag, otherwise `unstable` target is called (i.e. tag-based release strategy is used).
46+
47+
### Secrets in Build Context
48+
49+
Workflow accepts `docker_secrets` input secret as new-line separated list of `key=value` secret pairs, to be made available during build time. Each `key=value` pair is injected into build as environment variable named as `<key>`, with value set to `<value>`.
50+
51+
- If `Makefile` is used, it should provide `--secret id=<key>,env=<key>` command line option(s) for `docker buildx build` command to mount secrets during build time.
52+
- If `Makefile` is not used, `--secret id=<key>,env=<key>` command line option is added to `docker buildx build` command automatically for each secret.
53+
54+
Each secret will be available in build context as a file named `/run/secrets/<key>`.
55+
56+
#### NOTE 1:
57+
Multiline secrets are not supported (will be converted to space-separated secrets).
58+
59+
#### NOTE 2:
60+
Reusable workflow syntax does not allow mixing implicitly specified secrets (`inherit` keyword) and exlicitly specified secrets. Therefore all secrets needed by image build, push, sign and scan will need to be provided, if `docker_secrets` is used.
61+
62+
#### Example:
63+
64+
jobs:
65+
build-sign-scan:
66+
uses: Cray-HPE/.github/workflows//build-sign-scan.yaml@build-sign-scan-workflow/v1
67+
with:
68+
docker_tag: artifactory.algol60.net/csm-docker/${{ github.ref_type == 'tag' && 'stable' || 'unstable' }}/my-image:tag
69+
secrets:
70+
docker_username: ${{ secrets.ARTIFACTORY_ALGOL60_USERNAME }}
71+
docker_password: ${{ secrets.ARTIFACTORY_ALGOL60_TOKEN }}
72+
gcp_workload_identity_provider: ${{ secrets.COSIGN_GCP_WORKLOAD_IDENTITY_PROVIDER_RSA }}
73+
gcp_service_account: ${{ secrets.COSIGN_GCP_SERVICE_ACCOUNT_RSA }}
74+
gcp_cosign_key: ${{ secrets.COSIGN_KEY_RSA }}
75+
snyk_token: ${{ secrets.SNYK_TOKEN }}
76+
docker_secrets: |
77+
SECRET_1="${{ secrets.ORG_SECRET_1 }}"
78+
SECRET_2="${{ secrets.ORG_SECRET_2 }}"
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
#
2+
# MIT License
3+
#
4+
# (C) Copyright 2025 Hewlett Packard Enterprise Development LP
5+
#
6+
# Permission is hereby granted, free of charge, to any person obtaining a
7+
# copy of this software and associated documentation files (the "Software"),
8+
# to deal in the Software without restriction, including without limitation
9+
# the rights to use, copy, modify, merge, publish, distribute, sublicense,
10+
# and/or sell copies of the Software, and to permit persons to whom the
11+
# Software is furnished to do so, subject to the following conditions:
12+
#
13+
# The above copyright notice and this permission notice shall be included
14+
# in all copies or substantial portions of the Software.
15+
#
16+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19+
# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
20+
# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
21+
# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22+
# OTHER DEALINGS IN THE SOFTWARE.
23+
#
24+
FROM alpine:latest

0 commit comments

Comments
 (0)