Skip to content

Commit

Permalink
feat: added reusable workflows for standard build, lint, release, and…
Browse files Browse the repository at this point in the history
… schedules (#3)

* feat: added reusable workflows for standard build, lint, release, and schedules

* ci: run release only on push events (to master)
  • Loading branch information
chgl authored Oct 23, 2023
1 parent ae3f823 commit 1a2d5d2
Show file tree
Hide file tree
Showing 18 changed files with 746 additions and 521 deletions.
101 changes: 101 additions & 0 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
name: ci

on:
push:
branches: [master]
release:
types: [created]
pull_request:
branches: [master]

permissions: read-all

jobs:
build:
uses: ./.github/workflows/standard-build.yaml
permissions:
contents: read
id-token: write
packages: write
pull-requests: write
actions: read
security-events: write
with:
# can't use the default "ghcr.io/${{ github.repository }}" since
# "ghcr.io/miracum/.github:pr-1": invalid reference format"
image: ghcr.io/miracum/github-reusable-workflow
enable-build-test-layer: true
enable-upload-test-image: true
secrets:
github-token: ${{ secrets.GITHUB_TOKEN }}

build-without-test-image:
uses: ./.github/workflows/standard-build.yaml
permissions:
contents: read
id-token: write
packages: write
pull-requests: write
actions: read
security-events: write
with:
image: ghcr.io/miracum/github-reusable-workflow
enable-build-test-layer: false
enable-upload-test-image: false
secrets:
github-token: ${{ secrets.GITHUB_TOKEN }}

lint:
uses: ./.github/workflows/standard-lint.yaml
permissions:
contents: read
pull-requests: write
issues: write
security-events: write
actions: read
with:
codeql-languages: '["python"]'
enable-codeql: true
secrets:
github-token: ${{ secrets.GITHUB_TOKEN }}

test:
runs-on: ubuntu-22.04
needs:
- build
steps:
- name: Download build image
uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # v3.0.2
if: ${{ github.event_name == 'pull_request' }}
with:
name: container-image
path: /tmp

- name: Download test image
uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # v3.0.2
if: ${{ github.event_name == 'pull_request' }}
with:
name: test-image
path: /tmp

- name: load image
if: ${{ github.event_name == 'pull_request' }}
run: |
docker load --input /tmp/image.tar
docker load --input /tmp/image-test.tar
- name: load image
run: |
docker image ls
release:
uses: ./.github/workflows/standard-release.yaml
needs:
- build
- test
permissions:
contents: write
pull-requests: write
issues: write
secrets:
semantic-release-token: ${{ secrets.GITHUB_TOKEN }}
21 changes: 21 additions & 0 deletions .github/workflows/schedule.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
name: scheduled

on:
repository_dispatch: {}
workflow_dispatch: {}
schedule:
- cron: "00 18 * * *"

permissions: read-all

jobs:
schedule:
uses: ./.github/workflows/standard-schedule.yaml
permissions:
contents: read
issues: write
security-events: write
with:
image: ghcr.io/miracum/github-reusable-workflow
secrets:
github-token: ${{ secrets.GITHUB_TOKEN }}
247 changes: 247 additions & 0 deletions .github/workflows/standard-build.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,247 @@
name: Standard workflow for building a container image from a Dockerfile

permissions:
contents: read

defaults:
run:
shell: bash

on:
workflow_call:
inputs:
image:
description: "Name of the image to build and push, including its registry but excluding any tags"
required: false
default: "ghcr.io/${{ github.repository }}"
type: string
enable-upload-image:
description: "If enabled, upload the image as a build artifact"
required: false
default: true
type: boolean
enable-build-test-layer:
description: "If enabled, builds a layer called `test` inside the Dockerfile. Useful for unit testing."
required: false
default: false
type: boolean
enable-upload-test-image:
description: "If enabled, uploads the image built from the test layer as an artifact."
required: false
default: false
type: boolean
outputs:
image-tags:
value: ${{ jobs.build.outputs.image-tags }}
description: "The built image tags"
image-meta-json:
value: ${{ jobs.build.outputs.image-meta-json }}
description: "The built image meta data in JSON format"
image-digest:
value: ${{ jobs.build.outputs.image-digest }}
description: "The built image digest"
secrets:
github-token:
description: "The GitHub workflow token used to push to ghcr.io. Also used by MegaLinter"
required: true

jobs:
build:
name: build image
runs-on: ubuntu-22.04
permissions:
contents: read
packages: write
# for uploading trivy scan results to security tab
security-events: write
outputs:
image-tags: ${{ steps.image_meta.outputs.tags }}
image-meta-json: ${{ steps.image_meta.outputs.json }}
image-digest: ${{ steps.build.outputs.digest }}
steps:
- name: Checkout
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
with:
# via <https://stackoverflow.com/questions/74744498/github-pushing-to-protected-branches-with-fine-grained-token/76550826#76550826>
persist-credentials: false

- name: Container image meta
id: image_meta
uses: docker/metadata-action@96383f45573cb7f253c731d3b3ab81c87ef81934 # v5
with:
images: |
${{ inputs.image }}
- name: Container meta for the test image
id: tests_image_meta
if: ${{ inputs.enable-build-test-layer == true }}
uses: docker/metadata-action@96383f45573cb7f253c731d3b3ab81c87ef81934 # v5
with:
images: |
${{ inputs.image }}-test
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@f95db51fddba0c2d1ec667646a06c2ce06100226 # v3

- name: Login to GitHub Container Registry
uses: docker/login-action@343f7c4344506bcbf9b4de18042ae17996df046d # v3
if: ${{ github.event_name != 'pull_request' }}
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.github-token }}

# ran first to avoid pushing failing images when running on master.
- name: Build unit test image layer
if: ${{ inputs.enable-build-test-layer == true }}
uses: docker/build-push-action@0565240e2d4ab88bba5387d719585280857ece09 # v5
with:
push: false
load: true
tags: ${{ steps.tests_image_meta.outputs.tags }}
labels: ${{ steps.tests_image_meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
target: test

- name: Save test image as tar archive
if: ${{ (inputs.enable-build-test-layer == true) && (inputs.enable-upload-test-image == true) }}
env:
IMAGE: ${{ steps.tests_image_meta.outputs.json && fromJson(steps.tests_image_meta.outputs.json).tags[0] }}
run: |
docker save "$IMAGE" -o /tmp/image-test.tar
- name: Upload test image
if: ${{ (inputs.enable-build-test-layer == true) && (inputs.enable-upload-test-image == true) }}
uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3.1.3
with:
name: test-image
path: |
/tmp/image-test.tar
- name: Build and push image
id: build
uses: docker/build-push-action@0565240e2d4ab88bba5387d719585280857ece09 # v5
with:
# when in a PR, load the image into the local docker daemon
# this way, we can save it as an artifact in later steps
load: ${{ github.event_name == 'pull_request' }}
push: ${{ github.event_name != 'pull_request' }}
tags: ${{ steps.image_meta.outputs.tags }}
labels: ${{ steps.image_meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max

- name: Save container image as tar archive
if: ${{ github.event_name == 'pull_request' && inputs.enable-upload-image == true }}
env:
IMAGE: ${{ fromJson(steps.image_meta.outputs.json).tags[0] }}
run: |
docker save "$IMAGE" -o /tmp/image.tar
- name: Upload container image
if: ${{ github.event_name == 'pull_request' && inputs.enable-upload-image == true }}
uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3.1.3
with:
name: container-image
path: |
/tmp/image.tar
- name: Save Trivy vulnerability attestation
uses: aquasecurity/trivy-action@fbd16365eb88e12433951383f5e99bd901fc618f # 0.12.0
with:
image-ref: ${{ fromJson(steps.image_meta.outputs.json).tags[0] }}
exit-code: "0"
format: cosign-vuln
output: trivy-vuln-attestation.json

- name: Upload image vulnerability attestation
uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3.1.3
with:
name: image-trivy-vuln-attestation
path: |
trivy-vuln-attestation.json
- name: Run Trivy vulnerability scanner for GitHub Security tab
uses: aquasecurity/trivy-action@fbd16365eb88e12433951383f5e99bd901fc618f # 0.12.0
if: ${{ github.event_name != 'pull_request' }}
with:
image-ref: ${{ fromJson(steps.image_meta.outputs.json).tags[0] }}
exit-code: "0"
format: "sarif"
output: "trivy-results.sarif"

- name: Upload Trivy scan results to GitHub Security tab
uses: github/codeql-action/upload-sarif@0116bc2df50751f9724a2e35ef1f24d22f90e4e1 # v2.22.3
if: ${{ github.event_name != 'pull_request' }}
with:
sarif_file: "trivy-results.sarif"

# requires content: write access
# - name: Run Trivy in GitHub SBOM mode and submit results to Dependency Graph
# uses: aquasecurity/trivy-action@fbd16365eb88e12433951383f5e99bd901fc618f # 0.12.0
# if: ${{ github.event_name != 'pull_request' }}
# with:
# image-ref: ${{ fromJson(steps.image_meta.outputs.json).tags[0] }}
# format: "github"
# output: "dependency-results.sbom.json"
# github-pat: ${{ secrets.github-token }}

image-provenance:
if: ${{ startsWith(github.ref, 'refs/tags/') }}
needs:
- build
permissions:
actions: read # for detecting the Github Actions environment.
id-token: write
packages: write # for uploading attestations.
uses: slsa-framework/slsa-github-generator/.github/workflows/[email protected]
with:
image: ${{ inputs.image }}
digest: ${{ needs.build.outputs.image-digest }}
registry-username: ${{ github.actor }}
secrets:
registry-password: ${{ secrets.github-token }}

sign-image:
name: sign image
runs-on: ubuntu-22.04
if: ${{ github.event_name != 'pull_request' }}
needs:
- build
permissions:
contents: read
id-token: write
packages: write
steps:
- name: Login to GitHub Container Registry
uses: docker/login-action@343f7c4344506bcbf9b4de18042ae17996df046d # v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.github-token }}

- name: Download attestations
uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # v3.0.2
with:
name: image-trivy-vuln-attestation
path: /tmp

- name: Install Cosign
uses: sigstore/cosign-installer@11086d25041f77fe8fe7b9ea4e48e3b9192b8f19 # v3.1.2

# via <https://github.com/actions/starter-workflows/blob/main/ci/docker-publish.yml>
- name: Sign image
env:
# <https://docs.github.com/en/actions/security-guides/security-hardening-for-github-actions#using-an-intermediate-environment-variable>
TAGS: ${{ needs.build.outputs.image-tags }}
DIGEST: ${{ needs.build.outputs.image-digest }}
run: |
echo "${TAGS}" | xargs -I {} cosign sign --yes {}@"${DIGEST}"
- name: Attest image vulnerability report
env:
IMAGE: "${{ inputs.image }}@${{ needs.build.outputs.image-digest }}"
run: |
echo "Attest vulnerability report for '$IMAGE' using keyless approach"
cosign attest --yes --type vuln --predicate /tmp/trivy-vuln-attestation.json "$IMAGE"
Loading

0 comments on commit 1a2d5d2

Please sign in to comment.