├── .github ├── dependabot.yml └── workflows │ ├── test.yml │ └── upload_assets.yml ├── .gitignore ├── .goreleaser.yml ├── Dockerfile ├── LICENSE ├── README.md ├── go.mod ├── images ├── linter.png └── pr.png ├── main.go └── main_test.go /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "gomod" 4 | directory: "/" 5 | schedule: 6 | interval: daily 7 | commit-message: 8 | prefix: "[gomod] " 9 | - package-ecosystem: "github-actions" 10 | directory: "/" 11 | schedule: 12 | interval: daily 13 | commit-message: 14 | prefix: "[gh-actions] " 15 | - package-ecosystem: "docker" 16 | directory: "/" 17 | schedule: 18 | interval: daily 19 | commit-message: 20 | prefix: "[docker] " 21 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | on: 2 | push: 3 | branches: 4 | - master 5 | pull_request: 6 | branches: 7 | - master 8 | 9 | name: run tests 10 | jobs: 11 | lint: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - name: Checkout code 15 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 16 | with: 17 | fetch-depth: 0 18 | 19 | - name: Install Go 20 | uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0 21 | with: 22 | go-version-file: go.mod 23 | 24 | - name: Run linters 25 | uses: golangci/golangci-lint-action@4afd733a84b1f43292c63897423277bb7f4313a9 # v8.0.0 26 | with: 27 | problem-matchers: true 28 | args: --issues-exit-code=0 --output.sarif.path linter-results.sarif # we expect some findings, but for this demo just continue 29 | 30 | - name: Upload SARIF to Code Scanning 31 | uses: github/codeql-action/upload-sarif@ff0a06e83cb2de871e5a09832bc6a81e7276941f # v3.28.18 32 | with: 33 | sarif_file: ./linter-results.sarif 34 | 35 | test: 36 | runs-on: ubuntu-latest 37 | steps: 38 | - name: Checkout code 39 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 40 | with: 41 | fetch-depth: 0 42 | 43 | - name: Install Go 44 | uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0 45 | with: 46 | go-version-file: go.mod 47 | 48 | - name: Verify go version 49 | run: | 50 | echo "GOVERSION=$(go version)" >> $GITHUB_ENV 51 | go version 52 | 53 | - name: Run tests 54 | run: go test -v -covermode=count -coverprofile=coverage.out 55 | 56 | - name: Coveralls 57 | uses: coverallsapp/github-action@648a8eb78e6d50909eff900e4ec85cab4524a45b # v2.3.6 58 | with: 59 | github-token: ${{ secrets.github_token }} 60 | file: coverage.out 61 | format: golang -------------------------------------------------------------------------------- /.github/workflows/upload_assets.yml: -------------------------------------------------------------------------------- 1 | on: 2 | push: 3 | tags: 4 | - 'v*' 5 | 6 | env: 7 | # name of the resulting binary and the docker-image. must match name 8 | # in .goreleaser.yml 9 | BINARY: my-app 10 | 11 | name: Upload release assets after tagging 12 | jobs: 13 | build: 14 | name: create assets 15 | runs-on: ubuntu-latest 16 | steps: 17 | - name: Checkout code 18 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 19 | with: 20 | fetch-depth: 0 21 | 22 | - name: Install Go 23 | uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0 24 | with: 25 | go-version-file: go.mod 26 | 27 | - name: Run GoReleaser 28 | uses: goreleaser/goreleaser-action@9c156ee8a17a598857849441385a2041ef570552 # v6.3.0 29 | with: 30 | version: v2 31 | args: release --clean 32 | env: 33 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 34 | 35 | - name: Upload binary artifact 36 | uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 37 | with: 38 | name: binary 39 | path: dist/go-ci-demo_linux_amd64_v1/${{ env.BINARY }} 40 | retention-days: 1 41 | 42 | docker-image: 43 | needs: build 44 | env: 45 | REGISTRY: ghcr.io 46 | IMAGE_NAME: ${{ github.repository }} 47 | 48 | name: create docker image 49 | runs-on: ubuntu-latest 50 | steps: 51 | - name: Checkout code 52 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 53 | with: 54 | fetch-depth: 0 55 | 56 | - name: Set up Docker Buildx 57 | uses: docker/setup-buildx-action@b5ca514318bd6ebac0fb2aedd5d36ec1b5c232a2 # v3.10.0 58 | with: 59 | driver-opts: image=moby/buildkit:latest 60 | #buildkitd-flags: --debug 61 | 62 | - name: Log in to the Container registry 63 | uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3.4.0 64 | with: 65 | registry: ${{ env.REGISTRY }} 66 | username: ${{ github.actor }} 67 | password: ${{ secrets.GITHUB_TOKEN }} 68 | 69 | # Download the binary artifact from the build job 70 | - name: Download binary artifact 71 | uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 72 | with: 73 | name: binary 74 | path: ./docker-build 75 | 76 | - name: Prepare docker build 77 | run: | 78 | cp Dockerfile ./docker-build 79 | chmod 755 ./docker-build/${{ env.BINARY }} 80 | 81 | - name: Get tag info for image label 82 | id: tag 83 | run: | 84 | TAG=$(git describe --tags) 85 | echo "tag=$TAG" >> $GITHUB_OUTPUT 86 | 87 | - name: Build and push Docker image 88 | uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6.18.0 89 | with: 90 | context: ./docker-build 91 | build-args: | 92 | binary=${{ env.BINARY }} 93 | push: true 94 | tags: | 95 | ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }} 96 | ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.tag.outputs.tag }} 97 | cache-from: type=gha 98 | cache-to: type=gha,mode=max 99 | 100 | - name: Tag and push latest 101 | if: steps.tag.outputs.tag != 'v99.9.9' 102 | uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6.18.0 103 | with: 104 | context: ./docker-build 105 | build-args: | 106 | binary=${{ env.BINARY }} 107 | push: true 108 | tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest 109 | cache-from: type=gha 110 | no-cache: false 111 | pull: false -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | golang-ci-template-github-actions 3 | *zip 4 | *.out 5 | dist/ -------------------------------------------------------------------------------- /.goreleaser.yml: -------------------------------------------------------------------------------- 1 | # This is an example .goreleaser.yml file with some sensible defaults. 2 | # Make sure to check the documentation at https://goreleaser.com 3 | version: 2 4 | before: 5 | hooks: 6 | - go mod tidy 7 | project_name: go-ci-demo 8 | builds: 9 | - binary: my-app 10 | env: 11 | - CGO_ENABLED=0 12 | dir: . 13 | ldflags: 14 | - -s -w 15 | - -X "main.BuildVersion={{.Tag}}" 16 | goos: 17 | - linux 18 | - darwin 19 | - windows 20 | - freebsd 21 | archives: 22 | - files: 23 | - README.md 24 | - LICENSE 25 | name_template: >- 26 | {{- .ProjectName }}_ 27 | {{- title .Os }}_ 28 | {{- if eq .Arch "amd64" }}x86_64 29 | {{- else if eq .Arch "386" }}i386 30 | {{- else }}{{ .Arch }}{{ end }} 31 | {{- if .Arm }}v{{ .Arm }}{{ end -}} 32 | checksum: 33 | name_template: 'checksums.txt' 34 | snapshot: 35 | version_template: "{{ incpatch .Version }}-next" 36 | changelog: 37 | sort: asc 38 | filters: 39 | exclude: 40 | - '^docs:' 41 | - '^test:' -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM gcr.io/distroless/static-debian12 2 | ARG binary 3 | LABEL maintainer="Jan Delgado " 4 | 5 | COPY ./$binary /app 6 | ENTRYPOINT ["/app"] -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019-2020 Jan Delgado, jdelgado[at]gmx.net 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # golang ci template using github actions 2 | 3 | [![Build Status](https://github.com/jandelgado/golang-ci-template-github-actions/workflows/run%20tests/badge.svg)](https://github.com/jandelgado/golang-ci-template-github-actions/actions?workflow=run%20tests) 4 | [![Coverage Status](https://coveralls.io/repos/github/jandelgado/golang-ci-template-github-actions/badge.svg?branch=master)](https://coveralls.io/github/jandelgado/golang-ci-template-github-actions?branch=master) 5 | [![Go Report Card](https://goreportcard.com/badge/github.com/jandelgado/golang-ci-template-github-actions)](https://goreportcard.com/report/github.com/jandelgado/golang-ci-template-github-actions) 6 | 7 | 8 | 9 | * [Info](#info) 10 | * [Go-Version](#go-version) 11 | * [Dependabot](#dependabot) 12 | * [Creating a release](#creating-a-release) 13 | * [Linting & Test](#linting--test) 14 | * [Linter](#linter) 15 | * [Test](#test) 16 | * [Author](#author) 17 | 18 | 19 | 20 | ## Info 21 | 22 | This repository serves as a template for github-actions integrated go projects. 23 | It consists of a `hello, world!` like example in source file [main.go](main.go) 24 | which gets compiled into the binary `my-app` using goreleaser. The CI is 25 | configured to run 26 | [golangci-linter](https://github.com/golangci/golangci-lint-action) on the code, 27 | before the unit tests are executed. Test coverage is uploaded to coveralls.io. 28 | 29 | [goreleaser](https://github.com/goreleaser/goreleaser) is used to create the 30 | final multi-plattform assets, which are automatically uploaded to the 31 | [release](https://github.com/jandelgado/golang-ci-template-github-actions/releases/latest). 32 | The [release-process](#creating-a-release) is triggered by pushing a git tag to 33 | the repository. 34 | 35 | Finally, a docker image is built, which gets published to 36 | [ghcr.io](https://github.com/jandelgado/golang-ci-template-github-actions/pkgs/container/my-app). 37 | Run it with 38 | 39 | ```console 40 | $ docker run --rm ghcr.io/jandelgado/my-app:latest 41 | hello, world! 42 | ``` 43 | 44 | ## Go-Version 45 | 46 | The go version to use is configured in `go.mod` with the `toolchain` directive. 47 | When building locally, set `GOTOOLCHAIN` to `auto` to automatically install 48 | the configured toolchain (introduced with go 1.21). 49 | 50 | ## Dependabot 51 | 52 | We use [dependabot](https://docs.github.com/en/code-security/dependabot) to 53 | both keep the go dependencies as well as the used github action up-to-date. 54 | The configuration can be found [here](.github/dependabot.yml). 55 | 56 | ## Creating a release 57 | 58 | A new release is created by creating a git tag and pushing it, e.g.: 59 | 60 | ```console 61 | $ git tag -a "v1.2.3" -m "this is release v1.2.3" 62 | $ git push origin v1.2.3 63 | ``` 64 | 65 | The push of the new tag triggers the CI, which uses goreleaser with 66 | [this configuration](.goreleaser.yml) to 67 | 68 | * build multi-platform release artifacts 69 | * create a new release 70 | * upload the artifacts, which are then available on the [releases page](/jandelgado/golang-ci-template-github-actions/releases). 71 | 72 | Finally, a docker image is built using the previously built artefacts. The image 73 | is published to 74 | [ghcr.io](https://github.com/jandelgado/golang-ci-template-github-actions/pkgs/container/golang-ci-template-github-actions). 75 | 76 | To run goreleaser locally, start the tool with `gorelaser build --snapshot --clean`. 77 | 78 | ## Linting & Test 79 | 80 | ### Linter 81 | 82 | [golangci-linter](https://github.com/golangci/golangci-lint-action) is 83 | configured for code-linting. The report is uploaded so that linting results 84 | are visible in the MR: 85 | 86 | ![pr screenshot](images/linter.png) 87 | 88 | ### Test 89 | 90 | We use the 91 | [coveralls-github-action](https://github.com/coverallsapp/github-action) to 92 | upload the golang coverage to coveralls. 93 | 94 | Don't forget to enable `Leave comments (x)` in coveralls, under 95 | `repo settings` > `pull request alerts`, so that the coveralls-action posts a comment 96 | with the test coverage to affected pull requests: 97 | 98 | ![pr screenshot](images/pr.png) 99 | 100 | ## Author 101 | 102 | (c) copyright 2021-2025 by Jan Delgado, License MIT -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/jandelgado/golang-ci-template-github-actions 2 | 3 | // minimal go version that must be used 4 | go 1.23 5 | 6 | // build with the go version automatically fi GOTOOLCHAIN=auto 7 | // is set 8 | toolchain go1.24.1 9 | -------------------------------------------------------------------------------- /images/linter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jandelgado/golang-ci-template-github-actions/0045f66e8df03d4dd4f242190eeb151f62e6ae6b/images/linter.png -------------------------------------------------------------------------------- /images/pr.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jandelgado/golang-ci-template-github-actions/0045f66e8df03d4dd4f242190eeb151f62e6ae6b/images/pr.png -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | // will be set by linker during build 6 | var BuildVersion = "(version)" 7 | 8 | // Hello just says hello 9 | func Hello() error { 10 | fmt.Println("hello, world!") 11 | return nil 12 | } 13 | 14 | func main() { 15 | Hello() // return value intentionally not checked to trigger linter 16 | fmt.Printf("version: %s\n", BuildVersion) 17 | } -------------------------------------------------------------------------------- /main_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func ExampleHello() { 4 | Hello() 5 | // Output: 6 | // hello, world! 7 | } 8 | --------------------------------------------------------------------------------