Skip to content

Commit 4cd7371

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

8 files changed

Lines changed: 997 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: 247 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,247 @@
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: tests/build-sign-scan/sles
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+
SECRET_1="SECRET_1
62+
SECRET_1"
63+
SECRET_2='SECRET_2
64+
SECRET_2'
65+
gcp_workload_identity_provider: ${{ secrets.COSIGN_GCP_WORKLOAD_IDENTITY_PROVIDER_RSA }}
66+
gcp_service_account: ${{ secrets.COSIGN_GCP_SERVICE_ACCOUNT_RSA }}
67+
gcp_cosign_key: ${{ secrets.COSIGN_KEY_RSA }}
68+
snyk_token: ${{ secrets.SNYK_TOKEN }}
69+
70+
test-docker-build-single-platform:
71+
uses: ./.github/workflows/build-sign-scan.yaml
72+
with:
73+
context_path: tests/build-sign-scan/alpine
74+
docker_tag: artifactory.algol60.net/csm-docker-backup/unstable/test-build-sign-scan:test-docker-build-single-platform-1
75+
docker_additional_tags: |
76+
artifactory.algol60.net/csm-docker-backup/unstable/test-build-sign-scan:test-docker-build-single-platform-2
77+
snyk: true
78+
sign: true
79+
secrets: inherit
80+
81+
test-docker-build-multi-platform:
82+
uses: ./.github/workflows/build-sign-scan.yaml
83+
with:
84+
context_path: tests/build-sign-scan/alpine
85+
docker_tag: artifactory.algol60.net/csm-docker-backup/unstable/test-build-sign-scan:test-docker-build-multi-platform-1
86+
docker_build_platforms: linux/amd64,linux/arm64
87+
docker_additional_tags: |
88+
artifactory.algol60.net/csm-docker-backup/unstable/test-build-sign-scan:test-docker-build-multi-platform-2
89+
snyk: true
90+
sign: true
91+
secrets: inherit
92+
93+
test-docker-build-google:
94+
uses: ./.github/workflows/build-sign-scan.yaml
95+
with:
96+
context_path: tests/build-sign-scan/alpine
97+
docker_login: false
98+
docker_oidc: true
99+
docker_tag: us-docker.pkg.dev/hpe-stage-csm-release/csm-docker/unstable/test-build-sign-scan:test-docker-build-google-1
100+
docker_build_platforms: linux/amd64,linux/arm64
101+
docker_additional_tags: |
102+
us-docker.pkg.dev/hpe-stage-csm-release/csm-docker/unstable/test-build-sign-scan:test-docker-build-google-2
103+
snyk: true
104+
sign: true
105+
secrets: inherit
106+
107+
test-makefile-local:
108+
uses: ./.github/workflows/build-sign-scan.yaml
109+
with:
110+
context_path: tests/build-sign-scan/sles
111+
make_target: local
112+
docker_login: false
113+
docker_additional_tags: |
114+
artifactory.algol60.net/csm-docker-backup/unstable/test-build-sign-scan:test-makefile-local-2
115+
docker_build_args: |
116+
--label='Random label 1'
117+
--label="Random label 2"
118+
env: |
119+
VERSION=test-makefile-local-1
120+
VAR1=var1
121+
VAR2='var2 var2'
122+
VAR3="var3 var3"
123+
VAR4='var4
124+
var4'
125+
VAR5="var4
126+
var4"
127+
snyk: false
128+
sign: false
129+
secrets:
130+
docker_username: ${{ secrets.ARTIFACTORY_ALGOL60_USERNAME }}
131+
docker_password: ${{ secrets.ARTIFACTORY_ALGOL60_TOKEN }}
132+
docker_secrets: |
133+
SLES_REPO_USERNAME=${{ secrets.ARTIFACTORY_ALGOL60_READONLY_USERNAME }}
134+
SLES_REPO_PASSWORD=${{ secrets.ARTIFACTORY_ALGOL60_READONLY_TOKEN }}
135+
SECRET_1="SECRET_1
136+
SECRET_1"
137+
SECRET_2='SECRET_2
138+
SECRET_2'
139+
gcp_workload_identity_provider: ${{ secrets.COSIGN_GCP_WORKLOAD_IDENTITY_PROVIDER_RSA }}
140+
gcp_service_account: ${{ secrets.COSIGN_GCP_SERVICE_ACCOUNT_RSA }}
141+
gcp_cosign_key: ${{ secrets.COSIGN_KEY_RSA }}
142+
snyk_token: ${{ secrets.SNYK_TOKEN }}
143+
144+
test-makefile-single-platform:
145+
uses: ./.github/workflows/build-sign-scan.yaml
146+
with:
147+
context_path: tests/build-sign-scan/alpine
148+
make_target: unstable
149+
env: |
150+
PLATFORM=linux/amd64
151+
VERSION=test-makefile-single-platform-1
152+
docker_additional_tags: |
153+
artifactory.algol60.net/csm-docker-backup/unstable/test-build-sign-scan:test-makefile-single-platform-2
154+
snyk: true
155+
sign: true
156+
secrets: inherit
157+
158+
test-makefile-multi-platform:
159+
uses: ./.github/workflows/build-sign-scan.yaml
160+
with:
161+
context_path: tests/build-sign-scan/alpine
162+
make_target: unstable
163+
env: |
164+
VERSION=test-makefile-multi-platform-1
165+
docker_additional_tags: |
166+
artifactory.algol60.net/csm-docker-backup/unstable/test-build-sign-scan:test-makefile-multi-platform-2
167+
snyk: true
168+
sign: true
169+
secrets: inherit
170+
171+
test-makefile-google:
172+
uses: ./.github/workflows/build-sign-scan.yaml
173+
with:
174+
context_path: tests/build-sign-scan/alpine
175+
make_target: unstable
176+
docker_login: false
177+
docker_oidc: true
178+
env: |
179+
REGISTRY=us-docker.pkg.dev/hpe-stage-csm-release/csm-docker/unstable
180+
VERSION=test-makefile-google-1
181+
docker_additional_tags: |
182+
us-docker.pkg.dev/hpe-stage-csm-release/csm-docker/unstable/test-build-sign-scan:test-makefile-google-2
183+
snyk: true
184+
sign: true
185+
secrets: inherit
186+
187+
review:
188+
runs-on: ubuntu-latest
189+
190+
permissions:
191+
contents: 'read'
192+
id-token: 'write'
193+
194+
needs:
195+
- test-docker-build-local
196+
- test-docker-build-single-platform
197+
- test-docker-build-multi-platform
198+
- test-docker-build-google
199+
- test-makefile-local
200+
- test-makefile-single-platform
201+
- test-makefile-multi-platform
202+
- test-makefile-google
203+
204+
steps:
205+
- name: Report Test Results
206+
env:
207+
NEEDS_CONTEXT: ${{ toJSON(needs) }}
208+
run: |
209+
function assert() {
210+
if ! [[ "${2}" =~ ${3} ]]; then
211+
echo "::error::Test ${test_name}: ${1}: expected \"${3}\", got \"${2}\"."
212+
exit_code=$((exit_code+1))
213+
fi
214+
}
215+
216+
echo "$NEEDS_CONTEXT" > outputs.json
217+
exit_code=0
218+
for build_type in docker-build makefile; do
219+
for image_type in local single-platform multi-platform google; do
220+
test_name="test-${build_type}-${image_type}"
221+
222+
# Test built tags
223+
image_name=artifactory.algol60.net/csm-docker-backup/unstable/test-build-sign-scan
224+
test "${image_type}" == google && image_name=us-docker.pkg.dev/hpe-stage-csm-release/csm-docker/unstable/test-build-sign-scan
225+
tags=$(jq -r ".\"${test_name}\".outputs.image_tags" outputs.json | tr ',' '\n' | sort | tr '\n' ',' | sed -e 's/,$//')
226+
assert tags "${tags}" "${image_name}:${test_name}-1,${image_name}:${test_name}-2"
227+
228+
# Test built platforms
229+
platforms=$(jq -r ".\"${test_name}\".outputs.image_platforms" outputs.json)
230+
test "${image_type}" == single && assert platforms "${platforms}" '["linux/amd64"]'
231+
test "${image_type}" != local -a "${image_type}" != single-platform && assert platforms "${platforms}" '["linux/amd64","linux/arm64"]'
232+
233+
# Test Snyk output
234+
snyk_summary=$(jq -r ".\"${test_name}\".outputs.snyk_summary" outputs.json)
235+
test "${image_type}" != local && assert snyk_summary "${snyk_summary}" 'crtitical: [0-9]+, high: [0-9]+, medium: [0-9]+, low: [0-9]+'
236+
237+
# Test Trivy output
238+
trivy_summary=$(jq -r ".\"${test_name}\".outputs.trivy_summary" outputs.json)
239+
test "${image_type}" != local && assert trivy_summary "${trivy_summary}" '(Tests: [0-9]+ \(SUCCESSES: [0-9]+, FAILURES: [0-9]+\)|PASS)'
240+
241+
# Test Scan output
242+
sign_summary=$(jq -r ".\"${test_name}\".outputs.sign_summary" outputs.json)
243+
test "${image_type}" != local && assert sign_summary "${sign_summary}" 'Signed .+ with Cosign'
244+
done
245+
done
246+
exit $exit_code
247+
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)