Skip to content

Commit 556e583

Browse files
authored
Update workflow to rebuild image after Trivy failure (#17)
* scan latest Docker image daily; if scan fails, tag, rebuild, and re-scan
1 parent 45704d3 commit 556e583

File tree

5 files changed

+178
-75
lines changed

5 files changed

+178
-75
lines changed

.github/workflows/docker_build_push.yml

Lines changed: 28 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -12,31 +12,27 @@ on:
1212
NOTEBOOK_TYPE:
1313
required: true
1414
type: string
15+
REF_TO_CHECKOUT:
16+
required: false
17+
type: string
18+
description: reference to checkout, e.g. a tag like v1.0.1. Defaults to the branch/tag of the current event.
19+
TAGS:
20+
required: true
21+
type: string
22+
description: "comma delimited list of tags, e.g., 2.3.4, or main,1.0,1.0.1"
1523

1624
env:
17-
IMAGE_PATH: ghcr.io/${{ github.repository }}-${{ inputs.NOTEBOOK_TYPE }}
1825
TARFILE_NAME: ${{ inputs.NOTEBOOK_TYPE }}-image.tar
1926

2027
jobs:
21-
build:
28+
tests:
2229
runs-on: ubuntu-latest
23-
permissions:
24-
contents: read
2530

2631
steps:
2732
- name: Checkout repository
2833
uses: actions/checkout@v4
29-
30-
- name: Extract metadata (tags, labels) for Docker
31-
id: meta
32-
uses: docker/metadata-action@v4.1.1
3334
with:
34-
images: ${{ env.IMAGE_PATH }}
35-
tags: |
36-
type=ref,event=branch
37-
type=ref,event=pr
38-
type=semver,pattern={{version}} # major.minor.patch
39-
type=semver,pattern={{major}}.{{minor}}
35+
ref: ${{ inputs.REF_TO_CHECKOUT }}
4036

4137
- name: Build Docker image for scanning, but don't push to ghcr.io yet
4238
uses: docker/build-push-action@v6.4.0
@@ -45,8 +41,6 @@ jobs:
4541
build-args: notebook_type=${{ inputs.NOTEBOOK_TYPE }}
4642
push: false
4743
outputs: type=tar,dest=${{ env.TARFILE_NAME }}
48-
tags: ${{ steps.meta.outputs.tags }}
49-
labels: ${{ steps.meta.outputs.labels }}
5044

5145
- name: Upload tarball for use by Trivy job
5246
uses: actions/upload-artifact@v4
@@ -55,28 +49,25 @@ jobs:
5549
path: ${{ env.TARFILE_NAME }}
5650

5751
outputs:
58-
meta_json: ${{ steps.meta.outputs.json }}
5952
tarfile_artifact: ${{ env.TARFILE_NAME }}
6053

6154
trivy-scan:
62-
needs: build
55+
needs: tests
6356
uses: "./.github/workflows/trivy.yml"
6457
with:
6558
NOTEBOOK_TYPE: ${{ inputs.NOTEBOOK_TYPE }}
6659
SOURCE_TYPE: tar
6760
IMAGE_NAME: image-name
68-
TARFILE_NAME: ${{ needs.build.outputs.tarfile_artifact }}
61+
TARFILE_NAME: ${{ needs.tests.outputs.tarfile_artifact }}
6962
EXIT_CODE: 1
7063

7164
push-image:
72-
needs: [build, trivy-scan]
65+
if: ${{ github.event_name == 'push' || github.event_name == 'schedule' }}
66+
needs: [tests, trivy-scan]
7367
runs-on: ubuntu-latest
7468
permissions:
7569
contents: read
7670
packages: write
77-
strategy:
78-
matrix:
79-
value: ${{ fromJSON(needs.build.outputs.meta_json).tags }}
8071

8172
steps:
8273
- name: Login to GitHub Container Registry
@@ -88,12 +79,25 @@ jobs:
8879

8980
- name: Checkout repository
9081
uses: actions/checkout@v4
82+
with:
83+
ref: ${{ inputs.REF_TO_CHECKOUT }}
84+
85+
- name: Prepend image references with repo' names to produce image references
86+
id: image_references
87+
run: |
88+
# convert repo' name to lower case to be used as a Docker name
89+
repo_name=ghcr.io/$(echo ${{ github.repository }} | tr '[:upper:]' '[:lower:]')-${{ inputs.NOTEBOOK_TYPE }}
90+
# comma separated list of tags
91+
tag_list=${{ inputs.TAGS }}
92+
# prepend each tag with repo name, e.g., "tag1"->"reponame:tag1"; "tag1,tag2"->"reponame:tag1,reponame:tag2"
93+
image_references=$repo_name:${tag_list//,/,$repo_name:}
94+
echo "image_references=$image_references" >> $GITHUB_ENV
9195
9296
- name: Build and push Docker image
9397
uses: docker/build-push-action@v6.4.0
9498
with:
9599
build-args: notebook_type=${{ inputs.NOTEBOOK_TYPE }}
96100
context: .
97101
push: true
98-
tags: ${{ matrix.value }}
102+
tags: ${{ env.image_references }}
99103
...

.github/workflows/main.yml

Lines changed: 39 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
---
2-
name: Run tests and conditionally build container
2+
name: Determine the Docker 'tags' to use, then invoke the build/test/publish workflow.
33

44
on:
55
push:
@@ -10,43 +10,53 @@ on:
1010
pull_request:
1111
branches:
1212
- '*'
13+
env:
14+
TAG_PREFIX: prefix # must be lower case
1315

1416
jobs:
1517
tests:
18+
uses: "./.github/workflows/test.yml"
19+
20+
get-tags:
1621
runs-on: ubuntu-latest
22+
needs: [tests]
1723
steps:
18-
- name: Checkout repository
19-
uses: actions/checkout@v4
20-
21-
- name: Static Analysis
22-
uses: pre-commit/action@v3.0.0
23-
24-
- name: Build Docker Image
25-
uses: docker/build-push-action@v6.4.0
24+
- name: Extract metadata (tags, labels) for Docker
25+
id: meta
26+
uses: docker/metadata-action@v4.1.1
2627
with:
27-
context: .
28-
build-args: notebook_type=jupyter
29-
push: false
30-
31-
- name: Install dependencies
32-
run: pip install -r requirements.txt -r requirements-dev.txt
33-
34-
- name: Run unit tests
35-
run: python -m pytest tests/ -s -v
36-
37-
docker-build-push-jupyter:
28+
images: ${{ env.TAG_PREFIX }} # dummy value since blank is not allowed
29+
tags: |
30+
type=ref,event=branch
31+
type=ref,event=pr
32+
type=semver,pattern={{version}} # major.minor.patch
33+
type=semver,pattern={{major}}.{{minor}}
34+
35+
- name: Remove dummy prefix
36+
id: removed
37+
run: |
38+
# comma separated list of tags
39+
reference_list=${{ steps.meta.outputs.tags }}
40+
prefix_to_remove=${{ env.TAG_PREFIX }}:
41+
# remove prefix, e.g., ->"prefix:tag1,prefix:tag2"->"tag1,tag2"
42+
tags=${reference_list//$prefix_to_remove/}
43+
echo "tags=$tags" >> $GITHUB_ENV
44+
45+
outputs:
46+
tags: ${{ env.tags }}
47+
48+
docker-build-push:
3849
if: ${{ github.event_name == 'push' }}
39-
needs: [tests]
50+
needs: [get-tags]
4051
uses: "./.github/workflows/docker_build_push.yml"
41-
secrets: inherit
42-
with:
43-
NOTEBOOK_TYPE: jupyter
52+
strategy:
53+
matrix:
54+
notebook_type:
55+
- jupyter
56+
- rstudio
4457

45-
docker-build-push-rstudio:
46-
if: ${{ github.event_name == 'push' }}
47-
needs: [tests]
48-
uses: "./.github/workflows/docker_build_push.yml"
4958
secrets: inherit
5059
with:
51-
NOTEBOOK_TYPE: rstudio
60+
TAGS: ${{ needs.get-tags.outputs.tags }}
61+
NOTEBOOK_TYPE: ${{ matrix.notebook_type }}
5262
...

.github/workflows/test.yml

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
---
2+
#
3+
# Run static analysis, verify that image will build, and run tests
4+
#
5+
name: Build and publish a Docker image
6+
7+
on:
8+
workflow_call:
9+
inputs:
10+
REF_TO_CHECKOUT:
11+
required: false
12+
type: string
13+
description: reference to checkout, e.g. a tag like v1.0.1. Defaults to the branch/tag of the current event.
14+
outputs:
15+
conclusion:
16+
value: ${{ jobs.tests.outputs.conclusion }}
17+
jobs:
18+
tests:
19+
runs-on: ubuntu-latest
20+
steps:
21+
- name: Checkout repository
22+
uses: actions/checkout@v4
23+
with:
24+
ref: ${{ inputs.REF_TO_CHECKOUT }}
25+
26+
- name: Static Analysis
27+
uses: pre-commit/action@v3.0.0
28+
29+
- name: Build Docker Image
30+
uses: docker/build-push-action@v6.4.0
31+
with:
32+
context: .
33+
build-args: notebook_type=jupyter
34+
push: false
35+
36+
- name: Install dependencies
37+
run: pip install -r requirements.txt -r requirements-dev.txt
38+
39+
- name: Run unit tests
40+
id: unit_tests
41+
run: python -m pytest tests/ -s -v
42+
43+
44+
outputs:
45+
conclusion: ${{ steps.unit_tests.conclusion }}
46+
...

.github/workflows/trivy.yml

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,15 @@ on:
2323
IMAGE_NAME:
2424
required: true
2525
type: string
26-
EXIT_CODE: # return code for failed scan. 0 means OK
26+
EXIT_CODE: # return code for failed scan. 0 means OK. Non-zero will fail the build when there are findings.
2727
required: false
2828
type: number
2929
default: 0
3030
outputs:
3131
trivy_conclusion:
32-
description: "The pass/fail return code from Trivy"
32+
description: "The pass/fail status from Trivy"
3333
value: ${{ jobs.trivy.outputs.trivy_conclusion }}
34+
3435
env:
3536
sarif_file_name: trivy-results-${{ inputs.NOTEBOOK_TYPE }}.sarif
3637
# downloading the trivy-db from its default GitHub location fails because
@@ -66,7 +67,7 @@ jobs:
6667
}} | docker import - ${{ inputs.IMAGE_NAME }}
6768

6869
- name: Run Trivy vulnerability scanner for any major issues
69-
uses: aquasecurity/trivy-action@0.24.0
70+
uses: aquasecurity/trivy-action@0.28.0
7071
id: trivy
7172
with:
7273
image-ref: ${{ inputs.IMAGE_NAME }}
@@ -88,12 +89,16 @@ jobs:
8889
# after Trivy exits with HIGH/CRITICAL findings
8990
# See https://github.com/aquasecurity/trivy-action?\
9091
# tab=readme-ov-file#using-trivy-with-github-code-scanning
92+
# Note that here instead of using `always()` which would
93+
# allow the step to run if *any* preceeding step failed,
94+
# this logic ensures that the step ony runs if all steps
95+
# succeed or if only the 'trivy' step fails.
9196
if: ${{ success() || steps.trivy.conclusion=='failure' }}
9297
with:
9398
sarif_file: ${{ env.sarif_file_name }}
9499
category: ${{ inputs.NOTEBOOK_TYPE }}
95100
wait-for-processing: true
96101

97102
outputs:
98-
trivy_conclusion: steps.trivy.conclusion
103+
trivy_conclusion: ${{ steps.trivy.conclusion }}
99104
...

0 commit comments

Comments
 (0)