From 1c63054e92b7c5c031f8a16280b2b0c0edd98505 Mon Sep 17 00:00:00 2001 From: "A. Renz" Date: Thu, 11 Dec 2025 13:23:47 +0100 Subject: [PATCH] ci: Add Gitea Actions CI/CD pipeline - Add workflow with test, lint, build, release jobs - Add goreleaser config for multi-platform releases - Add golangci-lint configuration --- .gitea/workflows/ci.yml | 284 ++++++++++++++++++++++++++++++++++++++++ .golangci.yml | 129 ++++++++++++++++++ .goreleaser.yml | 160 ++++++++++++++++++++++ 3 files changed, 573 insertions(+) create mode 100644 .gitea/workflows/ci.yml create mode 100644 .golangci.yml create mode 100644 .goreleaser.yml diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml new file mode 100644 index 0000000..87f3347 --- /dev/null +++ b/.gitea/workflows/ci.yml @@ -0,0 +1,284 @@ +# CI/CD Pipeline for dbbackup +# Triggers: push, pull_request, tags + +name: CI/CD + +on: + push: + branches: + - main + - master + - develop + tags: + - 'v*' + pull_request: + branches: + - main + - master + +env: + GO_VERSION: '1.23' + GOLANGCI_LINT_VERSION: 'v1.62.2' + +jobs: + # ============================================================================= + # Test Job - Runs tests with race detection and coverage + # ============================================================================= + test: + name: Test + runs-on: ubuntu-latest + container: + image: golang:1.23-bookworm + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Cache Go modules + uses: actions/cache@v4 + with: + path: | + ~/.cache/go-build + /go/pkg/mod + key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} + restore-keys: | + ${{ runner.os }}-go- + + - name: Download dependencies + run: go mod download + + - name: Run tests with race detection + run: | + go test -race -coverprofile=coverage.out -covermode=atomic ./... + + - name: Generate coverage report + run: | + go tool cover -func=coverage.out + go tool cover -html=coverage.out -o coverage.html + + - name: Upload coverage artifacts + uses: actions/upload-artifact@v4 + with: + name: coverage-report + path: | + coverage.out + coverage.html + retention-days: 30 + + - name: Generate coverage badge + run: | + COVERAGE=$(go tool cover -func=coverage.out | grep total | awk '{print $3}' | sed 's/%//') + echo "Total coverage: ${COVERAGE}%" + + # Determine badge color + if [ "$(echo "$COVERAGE >= 80" | bc)" -eq 1 ]; then + COLOR="brightgreen" + elif [ "$(echo "$COVERAGE >= 60" | bc)" -eq 1 ]; then + COLOR="green" + elif [ "$(echo "$COVERAGE >= 40" | bc)" -eq 1 ]; then + COLOR="yellow" + else + COLOR="red" + fi + + # Create badge JSON (can be served via shields.io endpoint) + echo "{\"schemaVersion\":1,\"label\":\"coverage\",\"message\":\"${COVERAGE}%\",\"color\":\"${COLOR}\"}" > coverage-badge.json + + - name: Upload coverage badge + uses: actions/upload-artifact@v4 + with: + name: coverage-badge + path: coverage-badge.json + retention-days: 90 + + # ============================================================================= + # Lint Job - Static code analysis with golangci-lint + # ============================================================================= + lint: + name: Lint + runs-on: ubuntu-latest + container: + image: golang:1.23-bookworm + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Cache Go modules + uses: actions/cache@v4 + with: + path: | + ~/.cache/go-build + /go/pkg/mod + key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} + restore-keys: | + ${{ runner.os }}-go- + + - name: Install golangci-lint + run: | + curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b /usr/local/bin ${{ env.GOLANGCI_LINT_VERSION }} + + - name: Run golangci-lint + run: | + golangci-lint run --timeout=5m --out-format=colored-line-number ./... + + # ============================================================================= + # Build Job - Verify build for all platforms + # ============================================================================= + build: + name: Build + runs-on: ubuntu-latest + needs: [test, lint] + container: + image: golang:1.23-bookworm + strategy: + matrix: + goos: [linux, darwin, windows] + goarch: [amd64, arm64] + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Cache Go modules + uses: actions/cache@v4 + with: + path: | + ~/.cache/go-build + /go/pkg/mod + key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} + restore-keys: | + ${{ runner.os }}-go- + + - name: Build binary + env: + GOOS: ${{ matrix.goos }} + GOARCH: ${{ matrix.goarch }} + CGO_ENABLED: 0 + run: | + BINARY_NAME=dbbackup + if [ "${{ matrix.goos }}" = "windows" ]; then + BINARY_NAME="${BINARY_NAME}.exe" + fi + + go build -ldflags="-s -w -X main.version=${{ github.ref_name }} -X main.commit=${{ github.sha }}" \ + -o dist/${BINARY_NAME}-${{ matrix.goos }}-${{ matrix.goarch }} \ + ./... + + - name: Upload build artifact + uses: actions/upload-artifact@v4 + with: + name: binary-${{ matrix.goos }}-${{ matrix.goarch }} + path: dist/ + retention-days: 7 + + # ============================================================================= + # SBOM Job - Generate Software Bill of Materials + # ============================================================================= + sbom: + name: Generate SBOM + runs-on: ubuntu-latest + needs: [test] + container: + image: golang:1.23-bookworm + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Install Syft + run: | + curl -sSfL https://raw.githubusercontent.com/anchore/syft/main/install.sh | sh -s -- -b /usr/local/bin + + - name: Generate SBOM (SPDX) + run: | + syft . -o spdx-json=sbom-spdx.json + + - name: Generate SBOM (CycloneDX) + run: | + syft . -o cyclonedx-json=sbom-cyclonedx.json + + - name: Upload SBOM artifacts + uses: actions/upload-artifact@v4 + with: + name: sbom + path: | + sbom-spdx.json + sbom-cyclonedx.json + retention-days: 90 + + # ============================================================================= + # Release Job - Create release with goreleaser (only on tags) + # ============================================================================= + release: + name: Release + runs-on: ubuntu-latest + needs: [test, lint, build] + if: startsWith(github.ref, 'refs/tags/v') + container: + image: golang:1.23-bookworm + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Install goreleaser + run: | + apt-get update && apt-get install -y git + curl -sSfL https://github.com/goreleaser/goreleaser/releases/download/v2.4.8/goreleaser_Linux_x86_64.tar.gz | tar xz -C /usr/local/bin goreleaser + + - name: Install Syft for SBOM + run: | + curl -sSfL https://raw.githubusercontent.com/anchore/syft/main/install.sh | sh -s -- -b /usr/local/bin + + - name: Download SBOM artifacts + uses: actions/download-artifact@v4 + with: + name: sbom + path: ./sbom/ + + - name: Run goreleaser + env: + GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }} + run: | + goreleaser release --clean + + - name: Upload release artifacts + uses: actions/upload-artifact@v4 + with: + name: release-artifacts + path: dist/ + retention-days: 90 + + # ============================================================================= + # Docker Job - Build and push Docker image (optional, on tags) + # ============================================================================= + docker: + name: Docker Image + runs-on: ubuntu-latest + needs: [test, lint] + if: startsWith(github.ref, 'refs/tags/v') + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Docker Buildx + run: | + docker buildx create --use --name multiarch + + - name: Login to Gitea Registry + run: | + echo "${{ secrets.GITEA_TOKEN }}" | docker login git.uuxo.net -u ${{ github.repository_owner }} --password-stdin + + - name: Extract version + id: version + run: | + VERSION=${GITHUB_REF#refs/tags/} + echo "version=${VERSION}" >> $GITHUB_OUTPUT + echo "version_short=${VERSION#v}" >> $GITHUB_OUTPUT + + - name: Build and push Docker image + run: | + docker buildx build \ + --platform linux/amd64,linux/arm64 \ + --tag git.uuxo.net/${{ github.repository }}:${{ steps.version.outputs.version }} \ + --tag git.uuxo.net/${{ github.repository }}:latest \ + --push \ + . diff --git a/.golangci.yml b/.golangci.yml new file mode 100644 index 0000000..ab1dca5 --- /dev/null +++ b/.golangci.yml @@ -0,0 +1,129 @@ +# golangci-lint Configuration +# https://golangci-lint.run/usage/configuration/ + +run: + timeout: 5m + issues-exit-code: 1 + tests: true + modules-download-mode: readonly + +output: + formats: + - format: colored-line-number + print-issued-lines: true + print-linter-name: true + sort-results: true + +linters: + enable: + # Default linters + - errcheck + - gosimple + - govet + - ineffassign + - staticcheck + - unused + + # Additional recommended linters + - bodyclose + - contextcheck + - dupl + - durationcheck + - errorlint + - exhaustive + - exportloopref + - gocognit + - goconst + - gocritic + - gocyclo + - godot + - gofmt + - goimports + - gosec + - misspell + - nilerr + - nilnil + - noctx + - prealloc + - predeclared + - revive + - sqlclosecheck + - stylecheck + - tenv + - tparallel + - unconvert + - unparam + - whitespace + +linters-settings: + errcheck: + check-type-assertions: true + check-blank: true + + govet: + enable-all: true + + gocyclo: + min-complexity: 15 + + gocognit: + min-complexity: 20 + + dupl: + threshold: 100 + + goconst: + min-len: 3 + min-occurrences: 3 + + misspell: + locale: US + + revive: + rules: + - name: blank-imports + - name: context-as-argument + - name: context-keys-type + - name: dot-imports + - name: error-return + - name: error-strings + - name: error-naming + - name: exported + - name: increment-decrement + - name: var-naming + - name: var-declaration + - name: package-comments + - name: range + - name: receiver-naming + - name: time-naming + - name: unexported-return + - name: indent-error-flow + - name: errorf + - name: empty-block + - name: superfluous-else + - name: unreachable-code + + gosec: + excludes: + - G104 # Audit errors not checked + - G304 # File path provided as taint input + +issues: + exclude-rules: + # Exclude some linters from running on tests files + - path: _test\.go + linters: + - dupl + - gocyclo + - gocognit + - gosec + - errcheck + + # Exclude known issues in generated files + - path: ".*_generated\\.go" + linters: + - all + + max-issues-per-linter: 50 + max-same-issues: 10 + new: false diff --git a/.goreleaser.yml b/.goreleaser.yml new file mode 100644 index 0000000..bca1b93 --- /dev/null +++ b/.goreleaser.yml @@ -0,0 +1,160 @@ +# GoReleaser Configuration for dbbackup +# https://goreleaser.com/customization/ +# Run: goreleaser release --clean + +version: 2 + +project_name: dbbackup + +before: + hooks: + - go mod tidy + - go generate ./... + +builds: + - id: dbbackup + main: ./ + binary: dbbackup + env: + - CGO_ENABLED=0 + goos: + - linux + - darwin + - windows + goarch: + - amd64 + - arm64 + - arm + goarm: + - "7" + ignore: + - goos: windows + goarch: arm + - goos: windows + goarch: arm64 + ldflags: + - -s -w + - -X main.version={{.Version}} + - -X main.commit={{.Commit}} + - -X main.date={{.Date}} + - -X main.builtBy=goreleaser + flags: + - -trimpath + mod_timestamp: '{{ .CommitTimestamp }}' + +archives: + - id: default + format: tar.gz + name_template: >- + {{ .ProjectName }}_ + {{- .Version }}_ + {{- .Os }}_ + {{- .Arch }} + {{- if .Arm }}v{{ .Arm }}{{ end }} + format_overrides: + - goos: windows + format: zip + files: + - README* + - LICENSE* + - CHANGELOG* + - docs/* + +checksum: + name_template: 'checksums.txt' + algorithm: sha256 + +snapshot: + version_template: "{{ incpatch .Version }}-next" + +changelog: + sort: asc + use: github + filters: + exclude: + - '^docs:' + - '^test:' + - '^ci:' + - '^chore:' + - Merge pull request + - Merge branch + groups: + - title: '๐Ÿš€ Features' + regexp: '^.*?feat(\([[:word:]]+\))??!?:.+$' + order: 0 + - title: '๐Ÿ› Bug Fixes' + regexp: '^.*?fix(\([[:word:]]+\))??!?:.+$' + order: 1 + - title: '๐Ÿ“š Documentation' + regexp: '^.*?docs(\([[:word:]]+\))??!?:.+$' + order: 2 + - title: '๐Ÿงช Tests' + regexp: '^.*?test(\([[:word:]]+\))??!?:.+$' + order: 3 + - title: '๐Ÿ”ง Maintenance' + order: 999 + +sboms: + - artifacts: archive + documents: + - "{{ .ProjectName }}_{{ .Version }}_sbom.spdx.json" + +signs: + - cmd: cosign + env: + - COSIGN_EXPERIMENTAL=1 + certificate: '${artifact}.pem' + args: + - sign-blob + - '--output-certificate=${certificate}' + - '--output-signature=${signature}' + - '${artifact}' + - '--yes' + artifacts: checksum + output: true + +# Gitea Release +release: + gitea: + owner: "{{ .Env.GITHUB_REPOSITORY_OWNER }}" + name: dbbackup + # Use Gitea API URL + # This is auto-detected from GITEA_TOKEN environment + draft: false + prerelease: auto + mode: replace + header: | + ## dbbackup {{ .Tag }} + + Released on {{ .Date }} + footer: | + --- + + **Full Changelog**: {{ .PreviousTag }}...{{ .Tag }} + + ### Installation + + ```bash + # Linux (amd64) + curl -LO https://git.uuxo.net/{{ .Env.GITHUB_REPOSITORY_OWNER }}/dbbackup/releases/download/{{ .Tag }}/dbbackup_{{ .Version }}_linux_amd64.tar.gz + tar xzf dbbackup_{{ .Version }}_linux_amd64.tar.gz + chmod +x dbbackup + sudo mv dbbackup /usr/local/bin/ + + # macOS (Apple Silicon) + curl -LO https://git.uuxo.net/{{ .Env.GITHUB_REPOSITORY_OWNER }}/dbbackup/releases/download/{{ .Tag }}/dbbackup_{{ .Version }}_darwin_arm64.tar.gz + tar xzf dbbackup_{{ .Version }}_darwin_arm64.tar.gz + chmod +x dbbackup + sudo mv dbbackup /usr/local/bin/ + ``` + extra_files: + - glob: ./sbom/*.json + +# Optional: Upload to Gitea Package Registry +# gitea_urls: +# api: https://git.uuxo.net/api/v1 +# upload: https://git.uuxo.net/api/packages/{{ .Env.GITHUB_REPOSITORY_OWNER }}/generic/{{ .ProjectName }}/{{ .Version }} + +# Announce release (optional) +announce: + skip: true