Skip to content
Merged
Show file tree
Hide file tree
Changes from 11 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
19 changes: 12 additions & 7 deletions .github/workflows/build_pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@ on:
pull_request:
branches: [ main*, feat/*, hotfix/* ]

concurrency:
group: pr-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true

jobs:
build:
if: github.event_name != 'push'
Expand All @@ -12,36 +16,37 @@ jobs:
- uses: actions/checkout@v5
with:
fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis

- name: Set up JDK 21
uses: actions/setup-java@v5
with:
java-version: 21
distribution: 'temurin'
- name: Cache Maven packages
uses: actions/cache@v4
with:
path: ~/.m2
key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }}
restore-keys: ${{ runner.os }}-m2
cache: maven

- name: Build
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Needed to get PR information, if any
run: mvn -B -U verify
run: mvn -f pom.xml -U -B -ntp -T 1C clean package

# Write PR number to file
- name: Write PR number to file
run: echo "${{ github.event.pull_request.number }}" > pr-number.txt

# Upload PR number artifact
- name: Upload PR number artifact
uses: actions/upload-artifact@v6
with:
name: pr-number
path: pr-number.txt

# Upload compiled classes
- name: Upload Compiled Classes
uses: actions/upload-artifact@v6
with:
name: compiled-classes
path: target/classes

# Upload Jacoco XML report(s)
- name: Upload Coverage Reports
uses: actions/upload-artifact@v6
Expand Down
286 changes: 217 additions & 69 deletions .github/workflows/publish_docker_3key.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,118 +7,266 @@ on:
- '*'
workflow_dispatch:

env:
REGISTRY: docker.io
REGISTRY_USERNAME: ${{ secrets.DOCKER_HUB_3KEY_USERNAME }}
REGISTRY_PASSWORD: ${{ secrets.DOCKER_HUB_3KEY_PASSWORD }}
REGISTRY_PROVIDER: dockerhub
IMAGE: 3keycompany/czertainly-core
DOCKERFILE: ./Dockerfile
# cache scope so both jobs share the same cache namespace
CACHE_SCOPE: czertainly-core
# must be multiline string to preserve newlines for docker/metadata-action parsing
DOCKER_TAG_RULES: |
type=ref,event=tag
type=semver,pattern={{major}}.{{minor}}
type=semver,pattern={{major}}
type=raw,value=latest,enable=${{ startsWith(github.ref, 'refs/tags/') }}
type=raw,value=develop-latest,enable=${{ startsWith(github.ref, 'refs/heads/') }}
type=sha,prefix=develop-,format=long,enable=${{ startsWith(github.ref, 'refs/heads/') }}

concurrency:
group: publish-${{ github.ref }}
cancel-in-progress: true

jobs:
push_to_registry:
name: Push Docker images
build_artifacts:
name: Build app artifacts (once)
runs-on: ubuntu-latest
steps:
- name: Check out the repo
uses: actions/checkout@v5
- uses: actions/checkout@v6

- name: Install Cosign
uses: sigstore/cosign-installer@v4.0.0
- name: Set up Java 21 + Maven cache
uses: actions/setup-java@v4
with:
distribution: temurin
java-version: "21"
cache: maven

- name: Prepare data dir
run: |
rm -rf data
mkdir -p data

- name: Maven package
run: |
mvn -f pom.xml -U -B -ntp -T 1C -DskipTests clean package

- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Collect artifacts into data/
run: |
mkdir -p data/target
cp -a target/*.jar data/target/
cp -a docker data/docker

- name: Upload build artifacts
uses: actions/upload-artifact@v6
with:
name: app-data
path: data/
if-no-files-found: error

build_amd64:
name: Build amd64
runs-on: ubuntu-latest
needs: build_artifacts
outputs:
digest: ${{ steps.build.outputs.digest }}

steps:
- name: Check out the repo
uses: actions/checkout@v6

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Run pre-build
run: |
./prebuild_image_script
- name: Download build artifacts
uses: actions/download-artifact@v7
with:
name: app-data
path: data

- name: Log in to 3Key Docker Hub
- name: Log in to registry
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_HUB_3KEY_USERNAME }}
password: ${{ secrets.DOCKER_HUB_3KEY_PASSWORD }}
registry: ${{ env.REGISTRY }}
username: ${{ env.REGISTRY_USERNAME }}
password: ${{ env.REGISTRY_PASSWORD }}

- name: Extract metadata (tags, labels) for Docker
id: meta
uses: docker/metadata-action@v5
with:
images: |
3keycompany/czertainly-core
tags: |
type=ref,event=tag
type=semver,pattern={{major}}.{{minor}}
type=semver,pattern={{major}}
type=raw,value=latest,enable=${{ startsWith(github.ref, 'refs/tags/') }}
type=raw,value=develop-latest
type=sha,prefix=develop-,format=long

- name: Test build Docker image
id: build-and-load
- name: Build + push (amd64) by digest only (no tags)
id: build
uses: docker/build-push-action@v6
with:
context: .
file: ${{ env.DOCKERFILE }}
platforms: linux/amd64
file: ./Dockerfile
# Build only the host architecture for the pre-scan build;
# --load works only when a single platform is produced.
load: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}

- name: Select tag for scanning
id: tag
run: |
FIRST_TAG="$(echo "${{ steps.meta.outputs.tags }}" | head -n 1)"
echo "value=$FIRST_TAG" >> "$GITHUB_OUTPUT"
provenance: mode=max
sbom: true
outputs: type=image,name=${{ env.IMAGE }},push-by-digest=true,name-canonical=true,push=true
cache-from: type=gha,scope=${{ env.CACHE_SCOPE }}-amd64
cache-to: type=gha,mode=max,scope=${{ env.CACHE_SCOPE }}-amd64

- name: Run Trivy vulnerability scanner
uses: aquasecurity/trivy-action@0.33.1
uses: aquasecurity/trivy-action@0.34.0
env:
TRIVY_IMAGE_SRC: remote
TRIVY_PLATFORM: linux/amd64
with:
image-ref: ${{ steps.tag.outputs.value }}
image-ref: ${{ env.IMAGE }}@${{ steps.build.outputs.digest }}
format: json
output: trivy-report.json
output: trivy-report-amd64.json
exit-code: 0

- name: Upload vulnerability report
uses: actions/upload-artifact@v6
with:
name: trivy-report
path: trivy-report.json
name: trivy-report-amd64
path: trivy-report-amd64.json

- name: Fail build on vulnerabilities
uses: aquasecurity/trivy-action@0.33.1
uses: aquasecurity/trivy-action@0.34.0
env:
TRIVY_IMAGE_SRC: remote
TRIVY_PLATFORM: linux/amd64
with:
image-ref: ${{ steps.tag.outputs.value }}
image-ref: ${{ env.IMAGE }}@${{ steps.build.outputs.digest }}
trivy-config: config/trivy.yaml
skip-setup-trivy: true

- name: Build and push Docker image
build_arm64:
name: Build arm64
runs-on: ubuntu-24.04-arm
needs: build_artifacts
outputs:
digest: ${{ steps.build.outputs.digest }}
steps:
- name: Check out the repo
uses: actions/checkout@v6

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Download build artifacts
uses: actions/download-artifact@v7
with:
name: app-data
path: data

- name: Log in to registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ env.REGISTRY_USERNAME }}
password: ${{ env.REGISTRY_PASSWORD }}

- name: Build + push (arm64) by digest only (no tags)
id: build
uses: docker/build-push-action@v6
id: build-and-push
with:
context: .
platforms: linux/amd64,linux/arm64
file: ./Dockerfile
push: true
file: ${{ env.DOCKERFILE }}
platforms: linux/arm64
provenance: mode=max
sbom: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
outputs: type=image,name=${{ env.IMAGE }},push-by-digest=true,name-canonical=true,push=true
cache-from: type=gha,scope=${{ env.CACHE_SCOPE }}-arm64
cache-to: type=gha,mode=max,scope=${{ env.CACHE_SCOPE }}-arm64

- name: Sign images with a key
run: |
images=""
for tag in ${TAGS}; do
images+="${tag}@${DIGEST} "
done
cosign sign --yes --key env://COSIGN_PRIVATE_KEY ${images}
- name: Run Trivy vulnerability scanner
uses: aquasecurity/trivy-action@0.34.0
env:
TRIVY_IMAGE_SRC: remote
TRIVY_PLATFORM: linux/arm64
with:
image-ref: ${{ env.IMAGE }}@${{ steps.build.outputs.digest }}
format: json
output: trivy-report-arm64.json
exit-code: 0

- name: Upload vulnerability report
uses: actions/upload-artifact@v6
with:
name: trivy-report-arm64
path: trivy-report-arm64.json

- name: Fail build on vulnerabilities
uses: aquasecurity/trivy-action@0.34.0
env:
TRIVY_IMAGE_SRC: remote
TRIVY_PLATFORM: linux/arm64
with:
image-ref: ${{ env.IMAGE }}@${{ steps.build.outputs.digest }}
trivy-config: config/trivy.yaml
skip-setup-trivy: true

publish_manifest:
name: Publish multiarch manifest
runs-on: ubuntu-latest
needs: [build_amd64, build_arm64]
steps:
- name: Check out the repo
uses: actions/checkout@v6

- name: Extract metadata (tags, labels) for Docker
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.IMAGE }}
tags: ${{ env.DOCKER_TAG_RULES }}

- name: Log in to registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ env.REGISTRY_USERNAME }}
password: ${{ env.REGISTRY_PASSWORD }}

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Install Cosign
uses: sigstore/cosign-installer@v4.0.0

- name: Create + push multiarch manifests (all tags)
env:
TAGS: ${{ steps.meta.outputs.tags }}
COSIGN_PRIVATE_KEY: ${{ secrets.COSIGN_PRIVATE_KEY }}
COSIGN_PASSWORD: ${{ secrets.COSIGN_PASSWORD }}
DIGEST: ${{ steps.build-and-push.outputs.digest }}
run: |
set -euo pipefail

AMD_DIGEST="${{ needs.build_amd64.outputs.digest }}"
ARM_DIGEST="${{ needs.build_arm64.outputs.digest }}"

echo "amd64 digest: ${AMD_DIGEST}"
echo "arm64 digest: ${ARM_DIGEST}"

echo "Signing per-arch images by digest"
cosign sign --yes --key env://COSIGN_PRIVATE_KEY "${IMAGE}@${AMD_DIGEST}"
cosign sign --yes --key env://COSIGN_PRIVATE_KEY "${IMAGE}@${ARM_DIGEST}"

echo "Tags to publish:"
echo "${TAGS}"

while IFS= read -r tag; do
[ -z "$tag" ] && continue
echo "Publishing manifest: $tag"

docker buildx imagetools create \
--tag "$tag" \
"${IMAGE}@${AMD_DIGEST}" \
"${IMAGE}@${ARM_DIGEST}"

docker buildx imagetools inspect "$tag"

echo "Signing multiarch manifest tag: $tag"
cosign sign --yes --key env://COSIGN_PRIVATE_KEY "$tag"
done <<< "${TAGS}"

- name: Push README to 3Key Docker Hub
- name: Push README to registry
uses: christian-korneck/update-container-description-action@v1
env:
DOCKER_USER: ${{ secrets.DOCKER_HUB_3KEY_USERNAME }}
DOCKER_PASS: ${{ secrets.DOCKER_HUB_3KEY_PASSWORD }}
DOCKER_USER: ${{ env.REGISTRY_USERNAME }}
DOCKER_PASS: ${{ env.REGISTRY_PASSWORD }}
with:
destination_container_repo: 3keycompany/czertainly-core
provider: dockerhub
destination_container_repo: ${{ env.IMAGE }}
provider: ${{ env.REGISTRY_PROVIDER }}
Loading
Loading