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
52 changes: 28 additions & 24 deletions .github/workflows/docker_build_push.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,31 +12,27 @@ on:
NOTEBOOK_TYPE:
required: true
type: string
REF_TO_CHECKOUT:
required: false
type: string
description: reference to checkout, e.g. a tag like v1.0.1. Defaults to the branch/tag of the current event.
TAGS:
required: true
type: string
description: "comma delimited list of tags, e.g., 2.3.4, or main,1.0,1.0.1"

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

jobs:
build:
tests:
runs-on: ubuntu-latest
permissions:
contents: read

steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Extract metadata (tags, labels) for Docker
id: meta
uses: docker/metadata-action@v4.1.1
with:
images: ${{ env.IMAGE_PATH }}
tags: |
type=ref,event=branch
type=ref,event=pr
type=semver,pattern={{version}} # major.minor.patch
type=semver,pattern={{major}}.{{minor}}
ref: ${{ inputs.REF_TO_CHECKOUT }}

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

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

outputs:
meta_json: ${{ steps.meta.outputs.json }}
tarfile_artifact: ${{ env.TARFILE_NAME }}

trivy-scan:
needs: build
needs: tests
uses: "./.github/workflows/trivy.yml"
with:
NOTEBOOK_TYPE: ${{ inputs.NOTEBOOK_TYPE }}
SOURCE_TYPE: tar
IMAGE_NAME: image-name
TARFILE_NAME: ${{ needs.build.outputs.tarfile_artifact }}
TARFILE_NAME: ${{ needs.tests.outputs.tarfile_artifact }}
EXIT_CODE: 1

push-image:
needs: [build, trivy-scan]
if: ${{ github.event_name == 'push' || github.event_name == 'schedule' }}
needs: [tests, trivy-scan]
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
strategy:
matrix:
value: ${{ fromJSON(needs.build.outputs.meta_json).tags }}

steps:
- name: Login to GitHub Container Registry
Expand All @@ -88,12 +79,25 @@ jobs:

- name: Checkout repository
uses: actions/checkout@v4
with:
ref: ${{ inputs.REF_TO_CHECKOUT }}

- name: Prepend image references with repo' names to produce image references
id: image_references
run: |
# convert repo' name to lower case to be used as a Docker name
repo_name=ghcr.io/$(echo ${{ github.repository }} | tr '[:upper:]' '[:lower:]')-${{ inputs.NOTEBOOK_TYPE }}
# comma separated list of tags
tag_list=${{ inputs.TAGS }}
# prepend each tag with repo name, e.g., "tag1"->"reponame:tag1"; "tag1,tag2"->"reponame:tag1,reponame:tag2"
image_references=$repo_name:${tag_list//,/,$repo_name:}
echo "image_references=$image_references" >> $GITHUB_ENV

- name: Build and push Docker image
uses: docker/build-push-action@v6.4.0
with:
build-args: notebook_type=${{ inputs.NOTEBOOK_TYPE }}
context: .
push: true
tags: ${{ matrix.value }}
tags: ${{ env.image_references }}
...
68 changes: 39 additions & 29 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
name: Run tests and conditionally build container
name: Determine the Docker 'tags' to use, then invoke the build/test/publish workflow.

on:
push:
Expand All @@ -10,43 +10,53 @@ on:
pull_request:
branches:
- '*'
env:
TAG_PREFIX: prefix # must be lower case

jobs:
tests:
uses: "./.github/workflows/test.yml"

get-tags:
runs-on: ubuntu-latest
needs: [tests]
steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Static Analysis
uses: pre-commit/action@v3.0.0

- name: Build Docker Image
uses: docker/build-push-action@v6.4.0
- name: Extract metadata (tags, labels) for Docker
id: meta
uses: docker/metadata-action@v4.1.1
with:
context: .
build-args: notebook_type=jupyter
push: false

- name: Install dependencies
run: pip install -r requirements.txt -r requirements-dev.txt

- name: Run unit tests
run: python -m pytest tests/ -s -v

docker-build-push-jupyter:
images: ${{ env.TAG_PREFIX }} # dummy value since blank is not allowed
tags: |
type=ref,event=branch
type=ref,event=pr
type=semver,pattern={{version}} # major.minor.patch
type=semver,pattern={{major}}.{{minor}}

- name: Remove dummy prefix
id: removed
run: |
# comma separated list of tags
reference_list=${{ steps.meta.outputs.tags }}
prefix_to_remove=${{ env.TAG_PREFIX }}:
# remove prefix, e.g., ->"prefix:tag1,prefix:tag2"->"tag1,tag2"
tags=${reference_list//$prefix_to_remove/}
echo "tags=$tags" >> $GITHUB_ENV

outputs:
tags: ${{ env.tags }}

docker-build-push:
if: ${{ github.event_name == 'push' }}
needs: [tests]
needs: [get-tags]
uses: "./.github/workflows/docker_build_push.yml"
secrets: inherit
with:
NOTEBOOK_TYPE: jupyter
strategy:
matrix:
notebook_type:
- jupyter
- rstudio

docker-build-push-rstudio:
if: ${{ github.event_name == 'push' }}
needs: [tests]
uses: "./.github/workflows/docker_build_push.yml"
secrets: inherit
with:
NOTEBOOK_TYPE: rstudio
TAGS: ${{ needs.get-tags.outputs.tags }}
NOTEBOOK_TYPE: ${{ matrix.notebook_type }}
...
46 changes: 46 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
---
#
# Run static analysis, verify that image will build, and run tests
#
name: Build and publish a Docker image

on:
workflow_call:
inputs:
REF_TO_CHECKOUT:
required: false
type: string
description: reference to checkout, e.g. a tag like v1.0.1. Defaults to the branch/tag of the current event.
outputs:
conclusion:
value: ${{ jobs.tests.outputs.conclusion }}
jobs:
tests:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
ref: ${{ inputs.REF_TO_CHECKOUT }}

- name: Static Analysis
uses: pre-commit/action@v3.0.0

- name: Build Docker Image
uses: docker/build-push-action@v6.4.0
with:
context: .
build-args: notebook_type=jupyter
push: false

- name: Install dependencies
run: pip install -r requirements.txt -r requirements-dev.txt

- name: Run unit tests
id: unit_tests
run: python -m pytest tests/ -s -v


outputs:
conclusion: ${{ steps.unit_tests.conclusion }}
...
13 changes: 9 additions & 4 deletions .github/workflows/trivy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,15 @@ on:
IMAGE_NAME:
required: true
type: string
EXIT_CODE: # return code for failed scan. 0 means OK
EXIT_CODE: # return code for failed scan. 0 means OK. Non-zero will fail the build when there are findings.
required: false
type: number
default: 0
outputs:
trivy_conclusion:
description: "The pass/fail return code from Trivy"
description: "The pass/fail status from Trivy"
value: ${{ jobs.trivy.outputs.trivy_conclusion }}

env:
sarif_file_name: trivy-results-${{ inputs.NOTEBOOK_TYPE }}.sarif
# downloading the trivy-db from its default GitHub location fails because
Expand Down Expand Up @@ -66,7 +67,7 @@ jobs:
}} | docker import - ${{ inputs.IMAGE_NAME }}

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

outputs:
trivy_conclusion: steps.trivy.conclusion
trivy_conclusion: ${{ steps.trivy.conclusion }}
...
Loading
Loading