From 4e0da6aa34cf4bf55697891eadce8addeeb98243 Mon Sep 17 00:00:00 2001 From: Birol Bilgin Date: Tue, 30 Sep 2025 14:02:17 +0200 Subject: [PATCH] Add GitHub workflows - Add test-go.yaml workflow for building and testing every commit in PRs - Add lint-go.yaml workflow for Go module checks, commit signature verification, and linting - Add build-all target to Makefile for building all packages - Update golangci-lint configuration for v2.4.0 compatibility - Fix process death detection in GitHub Actions runners Signed-off-by: Birol Bilgin --- .github/workflows/lint-go.yaml | 79 ++++++++++++++++++++++ .github/workflows/test-go.yaml | 117 +++++++++++++++++++++++++++++++++ .golangci.yml | 19 ++---- Makefile | 11 +++- pkg/server/helper_test.go | 3 +- scripts/cli-test.sh | 4 +- 6 files changed, 213 insertions(+), 20 deletions(-) create mode 100644 .github/workflows/lint-go.yaml create mode 100644 .github/workflows/test-go.yaml diff --git a/.github/workflows/lint-go.yaml b/.github/workflows/lint-go.yaml new file mode 100644 index 0000000..3a1abb3 --- /dev/null +++ b/.github/workflows/lint-go.yaml @@ -0,0 +1,79 @@ +name: Lint + +on: + pull_request: {} + push: + branches: + - main + +permissions: read-all + +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.event.after }} + cancel-in-progress: true + +jobs: + go-mod-tidy: + name: Check Golang Modules + runs-on: ubuntu-latest + steps: + - name: Install Go + uses: actions/setup-go@v6 + with: + go-version: 1.25 + + - name: Checkout code + uses: actions/checkout@v5 + with: + persist-credentials: false + + - name: Check module vendoring + run: | + go mod tidy + go mod vendor + test -z "$(git status --porcelain)" || \ + (echo "::error::modules files is not up to date, please run 'go mod tidy && go mod vendor', re-submit changes"; exit 1) + + verify-signatures: + name: Verify Commit Signatures + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v5 + with: + fetch-depth: 0 + persist-credentials: false + + - name: Verify commit signed-off + run: | + set -eu -o pipefail + COMMITS=$(git rev-list ${{ github.event.pull_request.base.sha }}..${{ github.event.pull_request.head.sha }}) + for commit in $COMMITS; do + commit_msg=$(git log --format=%B -n 1 $commit) + if ! echo "$commit_msg" | grep -qE "^Signed-off-by: .+ <.+@.+>$"; then + echo "::error::Commit $commit is missing proper Signed-off-by line (format: Signed-off-by: Name )" + exit 1 + fi + done + + golangci: + name: Lint Code + runs-on: ubuntu-latest + steps: + - name: Install Go + uses: actions/setup-go@v6 + with: + go-version: 1.25 + + - name: Checkout code + uses: actions/checkout@v5 + with: + persist-credentials: false + + - name: golangci-lint + uses: golangci/golangci-lint-action@v8 + with: + version: v2.4.0 + skip-cache: true + args: "--verbose --modules-download-mode=vendor" + diff --git a/.github/workflows/test-go.yaml b/.github/workflows/test-go.yaml new file mode 100644 index 0000000..7789330 --- /dev/null +++ b/.github/workflows/test-go.yaml @@ -0,0 +1,117 @@ +name: Build and Test + +on: + pull_request: {} + push: + branches: + - main + +permissions: read-all + +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.event.after }} + cancel-in-progress: true + +jobs: + build_commits: + name: Build All + runs-on: ubuntu-latest + timeout-minutes: 20 + steps: + - name: Checkout code + uses: actions/checkout@v5 + with: + ref: ${{ github.event.pull_request.head.sha }} + fetch-depth: 0 + persist-credentials: false + + - name: Install Golang + uses: actions/setup-go@v6 + with: + go-version: 1.25 + cache: false + + - name: Build for every commit + run: | + set -eu -o pipefail + COMMITS=$(git rev-list ${{ github.event.pull_request.base.sha }}..${{ github.event.pull_request.head.sha }}) + for commit in $COMMITS ; do + git checkout $commit || exit 1 + go build ./... + done + + - name: Failed build commit + if: ${{ failure() }} + run: | + echo "::error::Build failed for commit $(git --no-pager log -n 1 --format=reference)" + + test_commits: + name: Unit Tests + runs-on: ubuntu-latest + timeout-minutes: 20 + steps: + - name: Checkout code + uses: actions/checkout@v5 + with: + ref: ${{ github.event.pull_request.head.sha }} + fetch-depth: 0 + persist-credentials: false + + - name: Install Golang + uses: actions/setup-go@v6 + with: + go-version: 1.25 + cache: false + + - name: Run unit test for every commit + run: | + set -eu -o pipefail + COMMITS=$(git rev-list ${{ github.event.pull_request.base.sha }}..${{ github.event.pull_request.head.sha }}) + for commit in $COMMITS ; do + git checkout $commit || exit 1 + make test + done + + - name: Failed test commit + if: ${{ failure() }} + run: | + echo "::error::Test failed for commit $(git --no-pager log -n 1 --format=reference)" + + test_cli_commits: + name: CLI Tests + runs-on: ubuntu-latest + timeout-minutes: 20 + steps: + - name: Checkout code + uses: actions/checkout@v5 + with: + ref: ${{ github.event.pull_request.head.sha }} + fetch-depth: 0 + persist-credentials: false + + - name: Install Golang + uses: actions/setup-go@v6 + with: + go-version: 1.25 + cache: false + + - name: Run CLI tests for every commit + run: | + set -eu -o pipefail + COMMITS=$(git rev-list ${{ github.event.pull_request.base.sha }}..${{ github.event.pull_request.head.sha }}) + for commit in $COMMITS ; do + git checkout $commit || exit 1 + make cli-test clean + done + + - name: Failed CLI test commit + if: ${{ failure() }} + run: | + echo "::error::CLI test failed for commit $(git --no-pager log -n 1 --format=reference)" + + - name: Upload server files + if: ${{ failure() }} + uses: actions/upload-artifact@v4 + with: + name: cli-test-files + path: tmp/ diff --git a/.golangci.yml b/.golangci.yml index 7aca596..45213bc 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -1,21 +1,15 @@ -version: 2 - +version: "2" run: - timeout: 10m + modules-download-mode: readonly + issues-exit-code: 1 tests: true - -output: - format: colored-line-number - print-issued-lines: true - print-linter-name: true - + timeout: 10m formatters: enable: - gofmt - goimports - linters: - disable-all: true + default: none enable: - depguard - err113 @@ -72,7 +66,6 @@ linters: enable: - nilness gosec: - exclude-generated: true includes: - G101 # Look for hard coded credentials - G112 # Potential Slowloris Attack because ReadHeaderTimeout is not configured @@ -83,8 +76,6 @@ linters: line-length: 140 misspell: locale: US - unused: - check-exported: false exclusions: generated: lax presets: diff --git a/Makefile b/Makefile index 706e46d..e72d198 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -.PHONY: build test lint clean check help all build-hos build-hosd cli-test +.PHONY: build test lint clean check help all build-hos build-hosd build-all cli-test # Default target all: build @@ -6,6 +6,10 @@ all: build # Build commands build: build-hos build-hosd +build-all: + @echo "Building all packages..." + go build ./... + build-hos: @echo "Building hos client..." go build -o bin/hos ./cmd/hos @@ -17,12 +21,12 @@ build-hosd: # Test commands test: @echo "Running tests..." - go test -v ./... + go test ./... # Code quality commands lint: @echo "Running golangci-lint..." - golangci-lint run + golangci-lint run --verbose --modules-download-mode=vendor # Clean up clean: @@ -42,6 +46,7 @@ check: test cli-test lint help: @echo "Available commands:" @echo " build - Build both hos and hosd binaries" + @echo " build-all - Build all packages" @echo " build-hos - Build only hos client" @echo " build-hosd - Build only hosd server" @echo " test - Run unit tests" diff --git a/pkg/server/helper_test.go b/pkg/server/helper_test.go index 145d362..684b7bf 100644 --- a/pkg/server/helper_test.go +++ b/pkg/server/helper_test.go @@ -180,8 +180,7 @@ func newTestClient(s *Server, t *testing.T, users ...string) *clientT { } func (c *clientT) do(user, method, path string, data any, header map[string]string) (*http.Response, error) { - ctx, cancel := context.WithTimeout(context.Background(), time.Second) - defer cancel() + ctx := context.Background() cfns := []clientFunc{headers(header)} if data != nil { diff --git a/scripts/cli-test.sh b/scripts/cli-test.sh index a2c1004..418a54b 100755 --- a/scripts/cli-test.sh +++ b/scripts/cli-test.sh @@ -111,7 +111,6 @@ start_server() { local server_pid=$! echo "$server_pid" > "$pid_file" - # Wait for server to be ready using health check local max_wait=30 local wait_count=0 @@ -119,6 +118,8 @@ start_server() { log_info "Waiting for server to be ready at $health_url..." + # Lets disable exit on error while checking for server status + set +e while [[ $wait_count -lt $max_wait ]]; do # Check if process is still running if ! kill -0 "$server_pid" 2>/dev/null; then @@ -135,6 +136,7 @@ start_server() { sleep 1 ((wait_count++)) done + set -e log_error "Server at $address did not become ready within ${max_wait} seconds" return 1