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
2 changes: 2 additions & 0 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,6 @@ jobs:
with:
name: gitctl
module: .
run_check_generated_files: false
ko_build_path: "main.go"
lint_fail_on_issues: true
166 changes: 82 additions & 84 deletions .github/workflows/reusable-go-ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@ on:
description: "Name of the module for display purposes"
required: true
type: string
run_check_generated_files:
description: "Set to true to run checks for generated files"
required: false
type: boolean
default: false
run_tests:
description: "Set to true to run unit tests and code coverage"
required: false
Expand Down Expand Up @@ -46,6 +51,11 @@ on:
required: false
type: boolean
default: ${{ github.event_name == 'pull_request' || github.ref_name == 'main' }}
lint_fail_on_issues:
description: "Set to true to fail the build when golangci-lint finds issues"
required: false
type: boolean
default: false
github_repository:
description: "GitHub repository (owner/repo), e.g., github.repository. Required if run_build_image is true."
required: false
Expand All @@ -66,6 +76,11 @@ on:
required: false
type: string
default: ${{ github.event_name == 'schedule' && 'nightly' || '' }}
coverage_threshold:
description: "Minimum coverage percentage required. Set to 0 to disable."
required: false
type: number
default: 70

outputs:
image_digest:
Expand All @@ -90,7 +105,7 @@ jobs:
cache: false # Using a separate cache step

- name: Restore Go Cache
uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
with:
path: |
~/.cache/go-build
Expand All @@ -99,13 +114,22 @@ jobs:
restore-keys: |
${{ runner.os }}-go-${{ inputs.module }}-

- name: Check Generated Files
if: ${{ inputs.run_check_generated_files }}
shell: bash
working-directory: ${{ inputs.module }}
run: |
make manifests
make generate
git diff --exit-code -- . ':!go.sum' ':!go.mod'

- name: Run golangci-lint
if: ${{ inputs.run_lint }}
uses: golangci/golangci-lint-action@1e7e51e771db61008b38414a730f564565cf7c20 # v9.2.0
with:
version: v2.11
working-directory: ${{ inputs.module }}
args: --timeout 5m --issues-exit-code=0 --config .golangci.yml
args: --timeout 5m --issues-exit-code=${{ inputs.lint_fail_on_issues && '1' || '0' }} --config ${{ github.workspace }}/.golangci.yml

tests:
name: "Tests & Coverage for ${{ inputs.name }}"
Expand All @@ -126,7 +150,7 @@ jobs:
cache: false # Using a separate cache step

- name: Restore Go Cache
uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
with:
path: |
~/.cache/go-build
Expand All @@ -135,87 +159,62 @@ jobs:
restore-keys: |
${{ runner.os }}-go-${{ inputs.module }}-

- name: Set up gotestfmt
uses: gotesttools/gotestfmt-action@8b4478c7019be847373babde9300210e7de34bfb # v2.2.0
with:
token: ${{ secrets.GITHUB_TOKEN }}
- name: Install gotestsum
run: go install gotest.tools/gotestsum@v1.13.0 # h1:+Lh454O9mu9AMG1APV4o0y7oDYKyik/3kBOiCqiEpRo=

- name: Build
shell: bash
working-directory: ${{ inputs.module }}
run: go build -v ./...

- name: Run Tests
- name: Build and test module
shell: bash
working-directory: ${{ inputs.module }}
run: make test
continue-on-error: ${{ inputs.allow_tests_failure }}
run: |
set -euo pipefail
go test -coverprofile cover.out -json -v ./... 2>&1 | tee gotest.log | gotestfmt

- name: Generate HTML coverage report
- name: Coverage summary
if: ${{ always() }}
shell: bash
working-directory: ${{ inputs.module }}
run: go tool cover -html=cover.out -o coverage.html
run: |
if [ -f cover.out ]; then
total=$(go tool cover -func=cover.out | awk '/^total:/ {print $NF}')
echo "**Coverage for ${{ inputs.name }}:** ${total}" >> $GITHUB_STEP_SUMMARY
fi

- name: Generate Cobertura coverage report
- name: Check coverage threshold
if: ${{ always() }}
shell: bash
working-directory: ${{ inputs.module }}
env:
COVERAGE_THRESHOLD: ${{ inputs.coverage_threshold }}
run: |
go install github.com/boumenot/gocover-cobertura@latest
gocover-cobertura < cover.out > cobertura.xml

- name: Upload go test log
if: ${{ always() }} # Upload even if previous steps fail
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
name: ${{ inputs.name }}-gotest.log
path: ${{ inputs.module }}/gotest.log
if-no-files-found: error
if [ ! -f cover.out ]; then
echo "::warning::No coverage file found"
exit 0
fi
total=$(go tool cover -func=cover.out | awk '/^total:/ {print $NF}' | tr -d '%')
echo "Total coverage: ${total}% (threshold: ${COVERAGE_THRESHOLD}%)"
if [ "$(echo "${total} < ${COVERAGE_THRESHOLD}" | bc -l)" -eq 1 ]; then
echo "::error::Coverage ${total}% is below threshold ${COVERAGE_THRESHOLD}%"
exit 1
fi

- name: Upload test reports artifact
- name: Upload test reports
id: upload_test_reports_artifact_step
if: ${{ always() }} # Upload even if previous steps fail
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
if: ${{ always() }}
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: ${{ inputs.name }}-test-report
path: |
${{ inputs.module }}/cover.out
${{ inputs.module }}/coverage.html
${{ inputs.module }}/cobertura.xml
${{ inputs.module }}/junit.xml
${{ inputs.module }}/gotest.log

- name: Publish test report as Check
if: ${{ always() && (github.event_name == 'pull_request') }}
uses: mikepenz/action-junit-report@bccf2e31636835cf0874589931c4116687171386 # v6.4.0
with:
report_paths: "${{ inputs.module }}/junit.xml"
check_name: "Test Report (${{ inputs.name }})"
comment: true
include_passed: true

- name: Prepare coverage comment
id: prepare_coverage_comment_step
if: ${{ always() && (github.event_name == 'pull_request') }}
uses: irongut/CodeCoverageSummary@51cc3a756ddcd398d447c044c02cb6aa83fdae95 # v1.3.0
with:
filename: ${{ inputs.module }}/cobertura.xml
badge: true
fail_below_min: false
format: markdown
indicators: true
output: both
thresholds: "60 80"

- name: Add Coverage PR Comment
- name: Publish test report
if: ${{ always() && (github.event_name == 'pull_request') }}
uses: marocchino/sticky-pull-request-comment@70d2764d1a7d5d9560b100cbea0077fc8f633987 # v3.0.2
uses: dorny/test-reporter@a43b3a5f7366b97d083190328d2c652e1a8b6aa2 # v3.0.0
continue-on-error: ${{ inputs.allow_tests_failure }}
with:
header: module-coverage-${{ inputs.name }}
message: |
**Coverage for ${{ inputs.name }}**
${{ steps.prepare_coverage_comment_step.outputs.markdown }}
Download the latest HTML coverage report for ${{ inputs.name }} [here](${{ github.server_url }}/${{ inputs.github_repository }}/actions/runs/${{ github.run_id }}/artifacts/${{ steps.upload_test_reports_artifact_step.outputs.artifact-id }}).
name: "Test Results (${{ inputs.name }})"
path: "${{ inputs.module }}/junit.xml"
reporter: java-junit

source_scan:
name: "Source Vulnerability Scan for ${{ inputs.name }}"
Expand All @@ -235,7 +234,7 @@ jobs:
cache: false

- name: Restore Go Cache
uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
with:
path: |
~/.cache/go-build
Expand All @@ -245,7 +244,7 @@ jobs:
${{ runner.os }}-go-${{ inputs.module }}-

- name: Run govulncheck
uses: golang/govulncheck-action@b625fbe08f3bccbe446d94fbf87fcc875a4f50ee # v1.0.4
uses: golang/govulncheck-action@31f7c5463448f83528bd771c2d978d940080c9fd # unreleased, includes fix for golang/go#75908
with:
go-package: ./...
work-dir: ${{ inputs.module }}
Expand All @@ -267,7 +266,7 @@ jobs:
cache: false

- name: Restore Go Cache
uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
with:
path: |
~/.cache/go-build
Expand All @@ -277,7 +276,7 @@ jobs:
${{ runner.os }}-go-${{ inputs.module }}-

- name: Initialize CodeQL
uses: github/codeql-action/init@c10b8064de6f491fea524254123dbe5e09572f13 # v4.35.1
uses: github/codeql-action/init@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 # v4.35.2
with:
languages: go
build-mode: manual # Set to manual as we provide a build step
Expand All @@ -298,7 +297,7 @@ jobs:
fi

- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@c10b8064de6f491fea524254123dbe5e09572f13 # v4.35.1
uses: github/codeql-action/analyze@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 # v4.35.2
with:
category: "/language:go/${{ inputs.name }}"

Expand All @@ -309,6 +308,7 @@ jobs:
runs-on: ubuntu-latest
outputs:
image_digest: ${{ steps.build_image_ko_step.outputs.digest }}
repo_lower: ${{ steps.repo_lower.outputs.repo }}
steps:
- name: Checkout Code
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
Expand All @@ -322,7 +322,7 @@ jobs:
cache: false

- name: Restore Go Cache
uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
with:
path: |
~/.cache/go-build
Expand All @@ -335,24 +335,28 @@ jobs:
uses: ko-build/setup-ko@d006021bd0c28d1ce33a07e7943d48b079944c8d # v0.9

- name: Inject slug vars
uses: rlespinasse/github-slug-action@9e7def61550737ba68c62d34a32dd31792e3f429 # v5.5.0
uses: rlespinasse/github-slug-action@e6f261660910b273384c5c42b17a0217881b217a # v5.6.0

- name: Convert repository to lowercase
id: repo_lower
shell: bash
run: |
REPO_LOWER=$(echo "${{ inputs.github_repository }}/${{ inputs.name }}" | tr '[:upper:]' '[:lower:]')
echo "repo=${REPO_LOWER}" >> $GITHUB_OUTPUT

- name: Build and Push Image with Ko
id: build_image_ko_step
shell: bash
working-directory: ${{ inputs.module }}
env:
KO_DOCKER_REPO: ${{ inputs.container_registry }}/${{ steps.repo_lower.outputs.repo }}
KO_CONFIG_PATH: ${{ github.workspace }}/.ko.yaml
run: |
if [ -z "${{ inputs.github_repository }}" ] || [ -z "${{ inputs.github_ref }}" ]; then
echo "Error: github_repository and github_ref inputs are required when run_build_image is true."
exit 1
fi

# Convert repository name to lowercase for registry
REPO_LOWER=$(echo "${{ inputs.github_repository }}" | tr '[:upper:]' '[:lower:]')
export KO_DOCKER_REPO="${{ inputs.container_registry }}/${REPO_LOWER}"

effective_tags=""
if [ -n "${{ inputs.image_tags }}" ]; then
effective_tags="${{ inputs.image_tags }}"
Expand All @@ -377,17 +381,11 @@ jobs:
needs: [build]
runs-on: ubuntu-latest
steps:
- name: Convert repository name to lowercase
id: repo_lower
shell: bash
run: |
REPO_LOWER=$(echo "${{ inputs.github_repository }}" | tr '[:upper:]' '[:lower:]')
echo "repo=${REPO_LOWER}" >> $GITHUB_OUTPUT

# No checkout or Go setup needed if only running Trivy on a remote image
- name: Run Trivy vulnerability scanner
uses: aquasecurity/trivy-action@57a97c7e7821a5776cebc9bb87c984fa69cba8f1 # v0.35.0
uses: aquasecurity/trivy-action@ed142fd0673e97e23eac54620cfb913e5ce36c25 # v0.36.0
with:
image-ref: ${{ inputs.container_registry }}/${{ steps.repo_lower.outputs.repo }}@${{ needs.build.outputs.image_digest }}
image-ref: ${{ inputs.container_registry }}/${{ needs.build.outputs.repo_lower }}@${{ needs.build.outputs.image_digest }}
exit-code: "1"
vuln-type: "os,library"
severity: "CRITICAL,HIGH"
severity: "CRITICAL,HIGH"
7 changes: 7 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
# If you prefer the allow list template instead of the deny list, see community template:
# https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore
#
# Build output
bin/

# Binaries for programs and plugins
*.exe
*.exe~
Expand All @@ -14,6 +17,10 @@
# Output of the go coverage tool, specifically when used with LiteIDE
*.out

# Test artifacts
junit.xml
gotest.log

# Dependency directories (remove the comment below to include it)
# vendor/

Expand Down
Loading
Loading