diff --git a/.github/workflows/go-test.yml b/.github/workflows/go-test.yml index 4fba194..2e3071a 100644 --- a/.github/workflows/go-test.yml +++ b/.github/workflows/go-test.yml @@ -6,6 +6,15 @@ on: jobs: lint: name: Lint + # description: | + # Lint uses golangci-lint github action and lints only changes from master. + # + # It will run on the latest go version. + # + # We rely on golangci-lint to automatically disable linters that do not apply to the minimum required go version + # currently defined in go.mod. + # + # At this moment, .golangci.yml configuration files are not shared: each repository must have its configuration. runs-on: ubuntu-latest steps: - uses: actions/checkout@v5 @@ -19,11 +28,31 @@ jobs: with: version: latest only-new-issues: true - skip-cache: true + skip-cache: false + - name: Linting complete # <- report summary when we have good news to report + run: | + echo "### Your changes to the go code look good. 👍" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "> â„šī¸ INFO: we use [golangci-lint action](https://github.com/golangci/golangci-lint-action)" >> $GITHUB_STEP_SUMMARY + echo "> to lint any change to go code from master" >> $GITHUB_STEP_SUMMARY unit_tests: name: Unit tests + # description: | + # Run go unit tests run x6: linux, mac & windows on the 2 latest go versions. + # + # Run tests with the -race flag. + # + # Captures test coverage and uploads it to codecov.com. runs-on: ${{ matrix.os }} + outputs: + # NOTE(fredbi): as of Aug. 2025, there is no way to have github actions declare outputs dynamically + test_result_oldstable_ubuntu_latest: "${{ steps.goreport.outputs.test_result_oldstable_ubuntu_latest }}" + test_result_oldstable_macos_latest: "${{ steps.goreport.outputs.test_result_oldstable_macos_latest }}" + test_result_oldstable_windows_latest: "${{ steps.goreport.outputs.test_result_oldstable_windows_latest }}" + test_result_stable_ubuntu_latest: "${{ steps.goreport.outputs.test_result_stable_ubuntu_latest }}" + test_result_stable_macos_latest: "${{ steps.goreport.outputs.test_result_stable_macos_latest }}" + test_result_stable_windows_latest: "${{ steps.goreport.outputs.test_result_stable_windows_latest }}" strategy: matrix: @@ -35,12 +64,34 @@ jobs: with: go-version: '${{ matrix.go_version }}' check-latest: true - cache: true + cache: true # <- cache key is go.sum - uses: actions/checkout@v5 + - name: Run unit tests + # description: | + # runs unit tests with test coverage assuming that this repository contains only one module, + # with a go.mod located at the root. + id: gotest + shell: bash + run: | + go test -v \ + -race \ + -coverprofile="coverage-${{ matrix.os }}.${{ matrix.go_version }}.out" \ + -covermode=atomic \ + -coverpkg=$(go list ./... |paste -sd ",") \ + ./... + + - name: Report test + if: ${{ success() || failure() }} + id: goreport shell: bash - run: go test -v -race -coverprofile="coverage-${{ matrix.os }}.${{ matrix.go_version }}.out" -covermode=atomic -coverpkg=$(go list)/... ./... + run: | + icon="✅" + if [[ "${{ steps.gotest.outcome }}" != "success" ]] ; then + icon="đŸšĢ" + fi + echo "test_result_${{ matrix.go_version }}_$(echo "${{ matrix.os }}"|tr '-' '_')='| ${{ matrix.go_version }} | ${{ matrix.os }} | ${icon} ${{ steps.gotest.outcome }} |'" >> "$GITHUB_OUTPUT" - name: Upload coverage to codecov uses: codecov/codecov-action@v5 @@ -48,13 +99,50 @@ jobs: files: './coverage-${{ matrix.os }}.${{ matrix.go_version }}.out' flags: '${{ matrix.go_version }}-${{ matrix.os }}' fail_ci_if_error: false - verbose: true + verbose: false all_tests: - needs: [ unit_tests ] name: All tests + # description: | + # This job regroups all tests launched as a matrix, so we may define a branch protection rule + # just on that job rather than each matrix job independently. + needs: [ unit_tests ] # <- requires all unit_tests jobs to be successful. + env: + test_result: '${{ join(needs.unit_tests.outputs.*) }}' # <- this is a comma-separated list of quoted strings runs-on: ubuntu-latest steps: - name: Tests complete run: | - echo "All tests completed" + echo "### All tests completed. 👍" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "> â„šī¸ INFO: we run unit tests in 6 different configurations of OS and go version." >> $GITHUB_STEP_SUMMARY + echo "> At this moment, we don't run architecture-specific test runs" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "| go version | OS | Result |" >> $GITHUB_STEP_SUMMARY + echo "|------------|----|--------|" >> $GITHUB_STEP_SUMMARY + declare -a lines + eval "lines=($(echo $test_result|tr ',' ' '))" + for line in "${lines[@]}";do echo "${line}" ; done >> $GITHUB_STEP_SUMMARY + + some_failed_tests: + name: Some tests have failed + # description: | + # This job is only here to report a summary of failed tests in the github actions UI. + needs: [ unit_tests ] # <- requires all unit_tests jobs to be completed, but not all where successfull. + if: ${{ failure() }} + env: + test_result: '${{ join(needs.unit_tests.outputs.*) }}' + runs-on: ubuntu-latest + steps: + - name: Tests failed + run: | + echo "### Some tests have failed. đŸšĢ" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "> â„šī¸ INFO: testing halted on first encountered failure." >> $GITHUB_STEP_SUMMARY + echo "> Some configurations may not have been tested yet." >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "| go version | OS | Result |" >> $GITHUB_STEP_SUMMARY + echo "|------------|----|--------|" >> $GITHUB_STEP_SUMMARY + declare -a lines + eval "lines=($(echo $test_result|tr ',' ' '))" + for line in "${lines[@]}";do echo "${line}" ; done >> $GITHUB_STEP_SUMMARY diff --git a/.github/workflows/local-auto-merge.yml b/.github/workflows/local-auto-merge.yml new file mode 100644 index 0000000..4cbf332 --- /dev/null +++ b/.github/workflows/local-auto-merge.yml @@ -0,0 +1,15 @@ +name: Dependabot auto-merge + +# This workflow mimics how a go-openapi repo would typically invoke the common workflow. + +on: pull_request + + +permissions: + contents: write + pull-requests: write + +jobs: + dependabot: + uses: ./.github/workflows/auto-merge.yml + secrets: inherit diff --git a/.github/workflows/local-go-test.yml b/.github/workflows/local-go-test.yml new file mode 100644 index 0000000..259a504 --- /dev/null +++ b/.github/workflows/local-go-test.yml @@ -0,0 +1,17 @@ +name: go test + +# This workflow mimics how a go-openapi repo would typically invoke the common workflow. + +on: + push: + tags: + - v* + branches: + - master + + pull_request: + +jobs: + test: + uses: ./.github/workflows/go-test.yml + secrets: inherit diff --git a/.golangci.yml b/.golangci.yml new file mode 100644 index 0000000..568ce16 --- /dev/null +++ b/.golangci.yml @@ -0,0 +1,75 @@ +version: "2" +linters: + default: all + disable: + - cyclop + - depguard + - errchkjson + - errorlint + - exhaustruct + - forcetypeassert + - funlen + - gochecknoglobals + - gochecknoinits + - gocognit + - godot + - godox + - gosmopolitan + - inamedparam + - intrange # disabled while < go1.22 + - ireturn + - lll + - musttag + - nestif + - nlreturn + - nonamedreturns + - noinlineerr + - paralleltest + - recvcheck + - testpackage + - thelper + - tparallel + - unparam + - varnamelen + - whitespace + - wrapcheck + - wsl + - wsl_v5 + settings: + dupl: + threshold: 200 + goconst: + min-len: 2 + min-occurrences: 3 + gocyclo: + min-complexity: 45 + exclusions: + generated: lax + presets: + - comments + - common-false-positives + - legacy + - std-error-handling + paths: + - third_party$ + - builtin$ + - examples$ +formatters: + enable: + - gofmt + - goimports + exclusions: + generated: lax + paths: + - third_party$ + - builtin$ + - examples$ +issues: + # Maximum issues count per one linter. + # Set to 0 to disable. + # Default: 50 + max-issues-per-linter: 0 + # Maximum count of issues with the same text. + # Set to 0 to disable. + # Default: 3 + max-same-issues: 0 diff --git a/README.md b/README.md index 52aec57..0d268cd 100644 --- a/README.md +++ b/README.md @@ -6,3 +6,30 @@ Common CI workflows and setup for go-openapi repos. * shared dependabot config NOTE: at this moment, it is difficult to share the golangci-lint config, so that one is not shared yet. + +## Motivation + +It took a while, but we eventually managed to align all checks, tests and dependabot rules declared in the +family of go-openapi repos. + +Now we'd like to be able to maintain, enrich and improve these checks without worrying too much about +the burden to replicate it about a dozen times. + +## Contemplated enhancements + +* [x] enrich github actions UI with a job summary +* [] version common workflows, so we can limit the impact of a change +* [] verify that go.sum cache for tests works (should be enabled) +* [] share mono repo workflows (see github.com/go-openapi/swag/.github/workflows) +* [] manage somehow to share golangci config (with local merge) +* [] manage somehow to share / replicate dependabot config +* [] add markdown linting for docs +* [] golangci-lint: check valid PR comments etc +* [] add spellcheck for docs (and code?) +* [] use non-blocking, scheduled, proactive full linting to check for the impact of new linters, new go versions etc +* [] (possibility) take over hugo & doc gen part from go-swagger +* [] (possibility) take over release part from go-swagger +* [] produce hugo github page with all latest tagged versions (incl. mono repo) +* [] add bot to filter PRs, issues +* [] check with github API that all repo settings (branch protection rules, etc) are identical +* [] comment PRs and issues diff --git a/audit/README.md b/audit/README.md new file mode 100644 index 0000000..eb5eadf --- /dev/null +++ b/audit/README.md @@ -0,0 +1,3 @@ +# Audit + +Audit of github repos to check for inconsistencies. diff --git a/doc/README.md b/doc/README.md new file mode 100644 index 0000000..19577ee --- /dev/null +++ b/doc/README.md @@ -0,0 +1,3 @@ +# doc + +Documentation check and generation tools. diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..51c159d --- /dev/null +++ b/go.mod @@ -0,0 +1,11 @@ +module github.com/go-openapi/ci-workflows + +go 1.24.2 + +require github.com/stretchr/testify v1.10.0 + +require ( + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..713a0b4 --- /dev/null +++ b/go.sum @@ -0,0 +1,10 @@ +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/sample/pkg/pkg.go b/sample/pkg/pkg.go new file mode 100644 index 0000000..6a54077 --- /dev/null +++ b/sample/pkg/pkg.go @@ -0,0 +1,6 @@ +// Package pkg exercises CI pipelines. +package pkg + +func Pkg() string { + return "" +} diff --git a/sample/pkg/pkg_test.go b/sample/pkg/pkg_test.go new file mode 100644 index 0000000..6979d22 --- /dev/null +++ b/sample/pkg/pkg_test.go @@ -0,0 +1,11 @@ +package pkg + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestPkg(t *testing.T) { + assert.Empty(t, Pkg()) +} diff --git a/sample/sample.go b/sample/sample.go new file mode 100644 index 0000000..d38e9a3 --- /dev/null +++ b/sample/sample.go @@ -0,0 +1,6 @@ +// Package sample is used to exercise CI pipelines. +package sample + +func Sample() int { + return 1 +} diff --git a/sample/sample_test.go b/sample/sample_test.go new file mode 100644 index 0000000..b4f2063 --- /dev/null +++ b/sample/sample_test.go @@ -0,0 +1,19 @@ +package sample + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestSample(t *testing.T) { + /* + if runtime.GOOS == "windows" { + require.Equal(t, 0, Sample()) // make the test fail on windows + + return + } + + */ + require.Equal(t, 1, Sample()) +}