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
10 changes: 10 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
.git
.github
.claude
.DS_Store
.gitignore
.dockerignore
LICENSE
README.md
*.tar
*.tar.gz
43 changes: 0 additions & 43 deletions .github/workflows/build-base.yaml

This file was deleted.

157 changes: 157 additions & 0 deletions .github/workflows/build.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
name: Build, scan, and publish

on:
push:
branches: [main]
tags: ["v*"]
pull_request:
branches: [main]
schedule:
- cron: "0 6 * * 1" # weekly Monday 06:00 UTC
workflow_dispatch:

permissions:
contents: read
packages: write
security-events: write

env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}

jobs:
# -------------------------------------------------------------------
# Lint — hadolint Dockerfile linting
# -------------------------------------------------------------------
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Lint Dockerfile
uses: hadolint/hadolint-action@v3.1.0
with:
dockerfile: Dockerfile

# -------------------------------------------------------------------
# Build — multi-platform image, push to GHCR
# -------------------------------------------------------------------
build:
runs-on: ubuntu-latest
needs: lint
outputs:
digest: ${{ steps.build-push.outputs.digest }}
steps:
- uses: actions/checkout@v4

- name: Set up QEMU
uses: docker/setup-qemu-action@v3

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

- name: Log in to GHCR
if: github.event_name != 'pull_request'
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Extract metadata for tags and labels
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: |
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=raw,value={{date 'YYYYMMDD'}},enable={{is_default_branch}}
type=raw,value=latest,enable={{is_default_branch}}
type=sha,prefix=

- name: Build and push
id: build-push
uses: docker/build-push-action@v6
with:
context: .
# Multi-platform on push; single platform on PR (--load does not
# support manifest lists, so we build only the runner's native arch)
platforms: ${{ github.event_name != 'pull_request' && 'linux/amd64,linux/arm64' || 'linux/amd64' }}
push: ${{ github.event_name != 'pull_request' }}
load: ${{ github.event_name == 'pull_request' }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max

# -------------------------------------------------------------------
# Scan — Trivy vulnerability scanning with SARIF upload
# -------------------------------------------------------------------
scan:
runs-on: ubuntu-latest
needs: build
if: github.event_name != 'pull_request'
steps:
- uses: actions/checkout@v4

- name: Run Trivy vulnerability scanner
uses: aquasecurity/trivy-action@0.28.0
with:
image-ref: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}@${{ needs.build.outputs.digest }}
format: sarif
output: trivy-results.sarif
severity: CRITICAL,HIGH

- name: Upload Trivy SARIF to GitHub Security
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: trivy-results.sarif

# -------------------------------------------------------------------
# Test — run validate-tools.sh inside the built image
# -------------------------------------------------------------------
test:
runs-on: ubuntu-latest
needs: build
if: github.event_name != 'pull_request'
steps:
- name: Log in to GHCR
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Pull and validate tools
run: |
docker pull "${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}@${{ needs.build.outputs.digest }}"
docker run --rm "${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}@${{ needs.build.outputs.digest }}" \
/usr/local/bin/validate-tools.sh

# -------------------------------------------------------------------
# Size report — output image size to step summary
# -------------------------------------------------------------------
size-report:
runs-on: ubuntu-latest
needs: build
if: github.event_name != 'pull_request'
steps:
- name: Log in to GHCR
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Report image size
run: |
docker pull "${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}@${{ needs.build.outputs.digest }}"
SIZE=$(docker image inspect "${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}@${{ needs.build.outputs.digest }}" --format '{{.Size}}')
SIZE_MB=$((SIZE / 1024 / 1024))
echo "## Image Size Report" >> "$GITHUB_STEP_SUMMARY"
echo "" >> "$GITHUB_STEP_SUMMARY"
echo "| Metric | Value |" >> "$GITHUB_STEP_SUMMARY"
echo "|--------|-------|" >> "$GITHUB_STEP_SUMMARY"
echo "| Raw size | ${SIZE_MB} MB |" >> "$GITHUB_STEP_SUMMARY"
echo "| Digest | \`${{ needs.build.outputs.digest }}\` |" >> "$GITHUB_STEP_SUMMARY"
17 changes: 17 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# OS
.DS_Store
Thumbs.db

# IDE
.idea/
.vscode/
*.swp
*.swo
*~

# Docker artifacts
*.tar
*.tar.gz

# Claude
.claude/
6 changes: 6 additions & 0 deletions .hadolint.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# DL3008: Pin versions in apt-get install
# Ignored because system packages receive security patches via the weekly
# scheduled rebuild. Pinning apt versions across amd64/arm64 is fragile
# and defeats the purpose of automatic security updates.
ignored:
- DL3008
Loading