Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
0a0a6c1
chore: split lint/test/coverage from image build/vuln scan, add relea…
nisargabd Aug 31, 2026
c941b24
fix: get security.yml's DHI login, Trivy pin, and PR comments right [#9]
nisargabd Sep 1, 2026
db036e7
feat: gate the PR on 80% minimum test coverage [#9]
nisargabd Aug 31, 2026
4f2b4d8
fix: make release.yml portable across forks and orgs [#9]
nisargabd Sep 1, 2026
16c917f
feat: build multi-arch images and standardize the dhi.io login secret…
nisargabd Sep 1, 2026
14d90ca
fix: scan the release image with Trivy before it reaches GHCR [#9]
nisargabd Sep 1, 2026
0e791c4
fix: accept a combined DHI_AUTH secret or DHI_USERNAME/DHI_PAT, not j…
nisargabd Sep 1, 2026
9089f81
chore: rename test.yml to run-tests.yml for a clearer, action-first n…
nisargabd Sep 1, 2026
a15ede7
fix: render govulncheck PR comment as a table, not raw output [#9]
nisargabd Sep 1, 2026
eaa544c
fix: render govulncheck findings as a box table, matching Trivy's [#9]
nisargabd Sep 1, 2026
d50ae05
fix: add plain-English issue and copy-paste fix to govulncheck commen…
nisargabd Sep 1, 2026
b521a49
fix: fold the govulncheck Issue description into the box table [#9]
nisargabd Sep 1, 2026
99b12ef
fix: draw a divider between every govulncheck row, matching Trivy [#9]
nisargabd Sep 1, 2026
4982112
fix: drop the standalone Advisory column, wrap the link into Issue [#9]
nisargabd Sep 1, 2026
5afb87e
fix: add a clickable advisory-links line below the govulncheck table …
nisargabd Sep 1, 2026
27b1c7e
fix: keep the govulncheck link inside the table, brief the Issue text…
nisargabd Sep 1, 2026
13ca7d1
fix: switch govulncheck's PR table to real markdown, fix stdlib rows …
nisargabd Sep 1, 2026
ed05280
fix: restrict release tags to plain SemVer or an -rcN suffix [#9]
nisargabd Sep 1, 2026
358cf1b
feat: make build-and-push registry-agnostic behind repo-variable flag…
nisargabd Sep 1, 2026
5e79ca6
fix: show full per-function coverage breakdown, not just the total [#9]
nisargabd Sep 1, 2026
8325354
refactor: replace govulncheck with a Trivy dependency scan [#9]
nisargabd Sep 1, 2026
13b06d5
refactor: report Trivy findings as SARIF, gate once from the reports …
nisargabd Sep 1, 2026
e76cf29
refactor: run tests through gotestsum, gate coverage with go-test-cov…
nisargabd Sep 1, 2026
19bfb98
fix: make the lint job non-blocking on PR merge [#9]
nisargabd Sep 1, 2026
4b07b9a
fix: don't fail CI when a fork PR can't post the coverage comment [#9]
nisargabd Sep 1, 2026
974a1a9
fix: replace the coverage and Trivy PR-comment dumps with clean table…
nisargabd Sep 1, 2026
49b2d8e
fix: make Trivy PR-comment jq compatible with the runner's jq 1.7.1 [#9]
nisargabd Sep 1, 2026
b3897b3
fix: let Dockerfile build without a DHI subscription [#9]
nisargabd Sep 1, 2026
da14673
fix: bump golang.org/x/crypto to close CVE-2026-56854 [#9]
nisargabd Sep 1, 2026
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
251 changes: 251 additions & 0 deletions .github/workflows/build-and-push.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,251 @@
name: Build and Push

# Annotated tags only, per OAN convention: v{SemVer} (e.g. v0.0.1), with an
# optional -rcN pre-release suffix (e.g. v0.1.1-rc3). Anything else (-test,
# -beta, ...) does not match, on purpose.
on:
push:
tags:
- "v[0-9]+.[0-9]+.[0-9]+"
- "v[0-9]+.[0-9]+.[0-9]+-rc[0-9]+"

permissions:
contents: read
packages: write

env:
IMAGE_NAME: discovery-service

jobs:
build:
strategy:
matrix:
include:
- runner: ubuntu-latest
platform: linux/amd64
arch: amd64
- runner: ubuntu-24.04-arm
platform: linux/arm64
arch: arm64

runs-on: ${{ matrix.runner }}
steps:
- uses: actions/checkout@v4

- uses: docker/setup-buildx-action@v3

# One flag per registry, read from repo variables (Settings > Secrets
# and variables > Actions > Variables), so a registry is switched on
# without a workflow edit. GHCR is the only one we use today, so it
# defaults on; everything else defaults off until its secrets exist.
# Adding a registry later is: a flag here, a login step below, and one
# more branch in "Tag and push" and the manifest job's create_manifest
# calls — the build/scan steps never change.
- name: Set registry flags
run: |
echo "GHCR_ENABLED=${{ vars.GHCR_ENABLED != 'false' }}" >> "$GITHUB_ENV"
echo "DOCKERHUB_ENABLED=${{ vars.DOCKERHUB_ENABLED == 'true' }}" >> "$GITHUB_ENV"
echo "AZURE_CONTAINER_REGISTRY_ENABLED=${{ vars.AZURE_CONTAINER_REGISTRY_ENABLED == 'true' }}" >> "$GITHUB_ENV"
echo "GOOGLE_ARTIFACT_REGISTRY_ENABLED=${{ vars.GOOGLE_ARTIFACT_REGISTRY_ENABLED == 'true' }}" >> "$GITHUB_ENV"

# Base images are dhi.io/*, a Docker Hardened Images registry gated on a
# Docker subscription — anonymous pulls 401. Unrelated to which
# registry we push the built image to below.
#
# Accepts either credential shape: DHI_AUTH already holds the combined
# base64 "user:token" blob docker login itself writes to config.json,
# or DHI_USERNAME/DHI_PAT are two plain values to combine into one.
# Either way it's config.json's own auth format, so it's written
# directly rather than through docker/login-action, whose
# username/password inputs don't accept a pre-combined blob.
- name: Log in to dhi.io
run: |
if [ -n "${{ secrets.DHI_AUTH }}" ]; then
AUTH="${{ secrets.DHI_AUTH }}"
else
AUTH=$(printf '%s:%s' "${{ secrets.DHI_USERNAME }}" "${{ secrets.DHI_PAT }}" | base64 -w0)
fi
mkdir -p ~/.docker
cat > ~/.docker/config.json <<EOF
{"auths":{"dhi.io":{"auth":"${AUTH}"}}}
EOF

# GITHUB_TOKEN can be denied write_package when creating a brand-new
# package under a personal (non-org) namespace, even with this
# workflow's `packages: write` and the repo's default token permissions
# both set to write — a documented GHCR quirk. GHCR_PAT (classic PAT,
# write:packages) is the standard workaround; falls back to
# GITHUB_TOKEN when no such secret is set, which is enough once the
# package exists or for an org-owned repo.
- name: Log in to GHCR
if: env.GHCR_ENABLED == 'true'
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GHCR_PAT || secrets.GITHUB_TOKEN }}

- name: Log in to Docker Hub
if: env.DOCKERHUB_ENABLED == 'true'
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_PASSWORD }}

- name: Log in to Azure Container Registry
if: env.AZURE_CONTAINER_REGISTRY_ENABLED == 'true'
uses: docker/login-action@v3
with:
registry: ${{ secrets.AZURE_REGISTRY_NAME }}
username: ${{ secrets.AZURE_REGISTRY_USERNAME }}
password: ${{ secrets.AZURE_REGISTRY_PASSWORD }}

- name: Authenticate to Google Cloud
if: env.GOOGLE_ARTIFACT_REGISTRY_ENABLED == 'true'
uses: google-github-actions/auth@v3
with:
token_format: "access_token"
workload_identity_provider: ${{ secrets.GOOGLE_WORKLOAD_IDENTITY_PROVIDER }}
service_account: ${{ secrets.GOOGLE_SERVICE_ACCOUNT_EMAIL }}

- name: Configure Docker for Google Artifact Registry
if: env.GOOGLE_ARTIFACT_REGISTRY_ENABLED == 'true'
run: gcloud auth configure-docker ${{ secrets.GCP_REGION }}-docker.pkg.dev --quiet

# GHCR image refs must be lowercase; github.repository_owner preserves
# the owner's real case (OpenAgriNet), so it can't be used as-is. This
# resolves to whichever account or org owns the repo the tag was pushed
# to — a fork pushes under the fork owner, no per-person edit needed.
- name: Set image owner
run: echo "OWNER=$(echo '${{ github.repository_owner }}' | tr '[:upper:]' '[:lower:]')" >> "$GITHUB_ENV"

# One local, unpushed tag per arch — the manifest job below stitches
# amd64 and arm64 into the single tag users actually pull.
#
# load: true instead of push: true — the image lands in this runner's
# local Docker daemon first so Trivy can scan the exact bytes about to
# ship, before it is tagged for or pushed to any registry.
- name: Build (local, unpushed)
uses: docker/build-push-action@v6
with:
context: .
platforms: ${{ matrix.platform }}
load: true
tags: ${{ env.IMAGE_NAME }}:${{ github.ref_name }}-${{ matrix.arch }}

# exit-code: 1 fails the job here, before any push below runs — the
# release gate, not just a report. One scan covers every registry.
- name: Trivy image scan (gate)
uses: aquasecurity/trivy-action@v0.36.0
with:
image-ref: ${{ env.IMAGE_NAME }}:${{ github.ref_name }}-${{ matrix.arch }}
severity: HIGH,CRITICAL
exit-code: "1"
format: table

# Re-tag the already-scanned local image for each enabled registry and
# push it there. No rebuild, no re-scan.
- name: Tag and push
run: |
LOCAL="${{ env.IMAGE_NAME }}:${{ github.ref_name }}-${{ matrix.arch }}"
TAG="${{ github.ref_name }}-${{ matrix.arch }}"

if [ "$GHCR_ENABLED" = "true" ]; then
DEST="ghcr.io/${OWNER}/${{ env.IMAGE_NAME }}:${TAG}"
docker tag "$LOCAL" "$DEST"
docker push "$DEST"
fi
if [ "$DOCKERHUB_ENABLED" = "true" ]; then
DEST="${{ secrets.DOCKERHUB_NAMESPACE }}/${{ env.IMAGE_NAME }}:${TAG}"
docker tag "$LOCAL" "$DEST"
docker push "$DEST"
fi
if [ "$AZURE_CONTAINER_REGISTRY_ENABLED" = "true" ]; then
DEST="${{ secrets.AZURE_REGISTRY_NAME }}/${{ env.IMAGE_NAME }}:${TAG}"
docker tag "$LOCAL" "$DEST"
docker push "$DEST"
fi
if [ "$GOOGLE_ARTIFACT_REGISTRY_ENABLED" = "true" ]; then
DEST="${{ secrets.GCP_REGION }}-docker.pkg.dev/${{ secrets.GCP_PROJECT_ID }}/${{ secrets.GOOGLE_ARTIFACT_REPO }}/${{ env.IMAGE_NAME }}:${TAG}"
docker tag "$LOCAL" "$DEST"
docker push "$DEST"
fi

manifest:
runs-on: ubuntu-latest
needs: build
steps:
- uses: docker/setup-buildx-action@v3

- name: Set registry flags
run: |
echo "GHCR_ENABLED=${{ vars.GHCR_ENABLED != 'false' }}" >> "$GITHUB_ENV"
echo "DOCKERHUB_ENABLED=${{ vars.DOCKERHUB_ENABLED == 'true' }}" >> "$GITHUB_ENV"
echo "AZURE_CONTAINER_REGISTRY_ENABLED=${{ vars.AZURE_CONTAINER_REGISTRY_ENABLED == 'true' }}" >> "$GITHUB_ENV"
echo "GOOGLE_ARTIFACT_REGISTRY_ENABLED=${{ vars.GOOGLE_ARTIFACT_REGISTRY_ENABLED == 'true' }}" >> "$GITHUB_ENV"

- name: Log in to GHCR
if: env.GHCR_ENABLED == 'true'
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GHCR_PAT || secrets.GITHUB_TOKEN }}

- name: Log in to Docker Hub
if: env.DOCKERHUB_ENABLED == 'true'
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_PASSWORD }}

- name: Log in to Azure Container Registry
if: env.AZURE_CONTAINER_REGISTRY_ENABLED == 'true'
uses: docker/login-action@v3
with:
registry: ${{ secrets.AZURE_REGISTRY_NAME }}
username: ${{ secrets.AZURE_REGISTRY_USERNAME }}
password: ${{ secrets.AZURE_REGISTRY_PASSWORD }}

- name: Authenticate to Google Cloud
if: env.GOOGLE_ARTIFACT_REGISTRY_ENABLED == 'true'
uses: google-github-actions/auth@v3
with:
token_format: "access_token"
workload_identity_provider: ${{ secrets.GOOGLE_WORKLOAD_IDENTITY_PROVIDER }}
service_account: ${{ secrets.GOOGLE_SERVICE_ACCOUNT_EMAIL }}

- name: Configure Docker for Google Artifact Registry
if: env.GOOGLE_ARTIFACT_REGISTRY_ENABLED == 'true'
run: gcloud auth configure-docker ${{ secrets.GCP_REGION }}-docker.pkg.dev --quiet

- name: Set image owner
run: echo "OWNER=$(echo '${{ github.repository_owner }}' | tr '[:upper:]' '[:lower:]')" >> "$GITHUB_ENV"

# imagetools create makes one manifest list from the two arch-specific
# images the build job just pushed to that registry — this is what
# lets `docker pull` resolve the right arch automatically on either
# amd64 or arm64. One create_manifest call per enabled registry.
- name: Create multi-arch manifests
run: |
create_manifest() {
local BASE="$1"
docker buildx imagetools create \
-t "${BASE}:${{ github.ref_name }}" \
-t "${BASE}:latest" \
"${BASE}:${{ github.ref_name }}-amd64" \
"${BASE}:${{ github.ref_name }}-arm64"
}

if [ "$GHCR_ENABLED" = "true" ]; then
create_manifest "ghcr.io/${OWNER}/${{ env.IMAGE_NAME }}"
fi
if [ "$DOCKERHUB_ENABLED" = "true" ]; then
create_manifest "${{ secrets.DOCKERHUB_NAMESPACE }}/${{ env.IMAGE_NAME }}"
fi
if [ "$AZURE_CONTAINER_REGISTRY_ENABLED" = "true" ]; then
create_manifest "${{ secrets.AZURE_REGISTRY_NAME }}/${{ env.IMAGE_NAME }}"
fi
if [ "$GOOGLE_ARTIFACT_REGISTRY_ENABLED" = "true" ]; then
create_manifest "${{ secrets.GCP_REGION }}-docker.pkg.dev/${{ secrets.GCP_PROJECT_ID }}/${{ secrets.GOOGLE_ARTIFACT_REPO }}/${{ env.IMAGE_NAME }}"
fi
74 changes: 0 additions & 74 deletions .github/workflows/ci.yml

This file was deleted.

Loading
Loading