Continuous Integration (CI) has become an essential part of modern software development, and for good reason. It ensures code quality, speeds up development, and catches potential issues early. However, you can get started without an elaborate CI setup. Even a minimal CI setup can significantly improve your workflow. Here’s why every project should have at least minimal CI and how to implement it effectively using GitHub Actions.
What Constitutes Minimal CI?
For a project to benefit from CI without excessive complexity, it should include the following essential components:
1. Project Compilation: Verify that the codebase is always in a buildable state.
2. Unit Test Execution: Ensure the core functionality works as expected.
3. Static Code Analysis: Catch bugs and enforce coding standards before they become an issue.
Why GitHub Actions?
GitHub Actions offers a simple yet powerful CI/CD platform that integrates seamlessly with GitHub repositories. Its ease of use makes it accessible even for developers without extensive DevOps experience. In fact, with just a few workflows, you can have a robust CI pipeline for small projects.
For example, for my small open-source Go library GFSM, three workflows—build, static-check, and publish—are sufficient.
Setting Up Your Workflows
1. Build Workflow
The build workflow ensures your code compiles and passes tests on every
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: prepare-env
uses: ./.github/env
- name: Build
run: go build -v ./...
- name: Test
run: go test -v ./...
2. Static Code Analysis
Static analysis helps maintain code quality by catching potential issues early. The best Go linter at the moment – staticcheck, provides an excellent integration with GitHub actions out of the box. Like the build workflow, this workflow runs on every
ci:
name: "Run CI"
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
with:
fetch-depth: 1
- name: prepare-env
uses: ./.github/env
- uses: dominikh/staticcheck-action@v1
with:
version: "latest"
3. Publish Workflow
Publishing a new version to pkg.go.dev ensures that users always have access to the latest updates. The tricky part is triggering the pkg.go.dev proxy to update its cache for new versions. On comparison with
push:
tags:
- 'v[0-9]+.[0-9]+.[0-9]+'
jobs:
publish:
name: "pkg.go.dev publishing"
runs-on: ubuntu-latest
steps:
- name: Publishing new version
run: |
curl https://sum.golang.org/lookup/github.com/astavonin/gfsm@${{ github.ref_name }}
Reusable Composite Actions
Both the
using: composite
steps:
- name: Setup Go
uses: actions/setup-go@v5
with:
go-version: 1.23.x
- name: Generate
shell: bash
run: |
go get golang.org/x/tools/cmd/stringer@latest
go install golang.org/x/tools/cmd/stringer@latest
go generate ./...
This composite action ensures consistency and simplifies workflow maintenance.
Looking Ahead
Go currently relies on the
Final Thoughts
Implementing minimal CI with GitHub Actions requires minimal effort but yields significant benefits. By automating builds, tests, and static analysis, you can ensure your project maintains high quality and is always ready for deployment. For small projects, this setup is a no-brainer, and GitHub Actions makes it easy to get started.