Skip to content

Commit b66399d

Browse files
insomniusclaude
andcommitted
ci: modernize Go workflow with fmt/vet/build/tidy/generate gates
The previous workflow ran tests + coverage + an outdated golangci-lint. A handful of common drift categories slipped through: unformatted code, stale go.sum, stale mocks, compile-only-untested packages. Add explicit gates for each and bump every action version to the current supported major. - .github/workflows/general.yml: three jobs — verify (tidy, gofmt, vet, build, `go generate` diff), lint (golangci-lint v1.64.5), test (make test + goveralls). Concurrency cancels superseded runs. setup-go@v5 with module cache; checkout@v4; golangci-lint-action@v6. - .github/workflows/merge.yml + release.yml: bump to v4/v5 actions, switch to docker/login-action@v3 instead of piped echo, run on ubuntu-latest, drop the manual docker logout dance. - .github/dependabot.yml: weekly updates for gomod (patch group) and github-actions. Labels so filtering PRs is easy. - .golangci.yml: pin a conservative linter set (errcheck, gosimple, govet, ineffassign, staticcheck, unused, misspell, unconvert, gofmt, goimports) with local-prefixes for our import path. Test files exempt from errcheck/unused. Also gofmt four files that the new gate correctly flagged: two migration command files, the plugin_list command, and the topo-sort test. Alignment-only changes, no logic shift. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 3a1e90c commit b66399d

9 files changed

Lines changed: 223 additions & 40 deletions

File tree

.github/dependabot.yml

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
version: 2
2+
3+
updates:
4+
# Keep GitHub Actions pinned to latest stable releases.
5+
- package-ecosystem: github-actions
6+
directory: /
7+
schedule:
8+
interval: weekly
9+
open-pull-requests-limit: 5
10+
labels:
11+
- dependencies
12+
- github-actions
13+
14+
# Go module updates. Group patch-level upgrades so the queue stays sane;
15+
# reserve individual PRs for minor/major where review is warranted.
16+
- package-ecosystem: gomod
17+
directory: /
18+
schedule:
19+
interval: weekly
20+
open-pull-requests-limit: 10
21+
labels:
22+
- dependencies
23+
- go
24+
groups:
25+
go-patch-updates:
26+
update-types:
27+
- patch

.github/workflows/general.yml

Lines changed: 86 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,26 +6,102 @@ on:
66
pull_request:
77
branches: [master]
88

9+
# Cancel superseded runs on a branch so a force-push / amend doesn't waste CI.
10+
concurrency:
11+
group: ${{ github.workflow }}-${{ github.ref }}
12+
cancel-in-progress: true
13+
14+
permissions:
15+
contents: read
16+
917
jobs:
18+
verify:
19+
name: Verify
20+
runs-on: ubuntu-latest
21+
steps:
22+
- uses: actions/checkout@v4
23+
24+
- name: Set up Go
25+
uses: actions/setup-go@v5
26+
with:
27+
# Keep in sync with go.mod's `go` directive (currently 1.19). Using
28+
# stable runs the newest toolchain Go will accept while still
29+
# exercising the minimum language level declared in go.mod.
30+
go-version: stable
31+
check-latest: true
32+
cache: true
33+
34+
- name: Verify go.mod / go.sum are tidy
35+
run: |
36+
go mod tidy
37+
if ! git diff --quiet go.mod go.sum; then
38+
echo "::error::go.mod or go.sum is not tidy — run 'go mod tidy' locally and commit."
39+
git diff go.mod go.sum
40+
exit 1
41+
fi
42+
43+
- name: Check gofmt
44+
run: |
45+
unformatted=$(gofmt -l .)
46+
if [ -n "$unformatted" ]; then
47+
echo "::error::The following files are not gofmt'd:"
48+
echo "$unformatted"
49+
exit 1
50+
fi
51+
52+
- name: go vet
53+
run: go vet ./...
54+
55+
- name: go build
56+
run: go build ./...
57+
58+
- name: go generate produces no diff
59+
run: |
60+
go generate ./...
61+
if ! git diff --quiet; then
62+
echo "::error::go generate produced changes — regenerate mocks locally and commit."
63+
git status
64+
git diff
65+
exit 1
66+
fi
67+
68+
lint:
69+
name: Lint
70+
runs-on: ubuntu-latest
71+
steps:
72+
- uses: actions/checkout@v4
73+
74+
- name: Set up Go
75+
uses: actions/setup-go@v5
76+
with:
77+
go-version: stable
78+
check-latest: true
79+
cache: true
80+
81+
- name: golangci-lint
82+
uses: golangci/golangci-lint-action@v6
83+
with:
84+
# Pin a recent stable release; bump with intent, not passively.
85+
version: v1.64.5
86+
args: --timeout=5m
87+
1088
test:
1189
name: Test Coverage
1290
runs-on: ubuntu-latest
91+
needs: verify
1392
steps:
14-
- name: Set up Go 1.x
15-
uses: actions/setup-go@v3
16-
with:
17-
go-version: ^1.19
93+
- uses: actions/checkout@v4
1894

19-
- uses: actions/checkout@v3
95+
- name: Set up Go
96+
uses: actions/setup-go@v5
97+
with:
98+
go-version: stable
99+
check-latest: true
100+
cache: true
20101

21102
- name: Install goveralls
22103
run: go install github.com/mattn/goveralls@latest
23104

24-
- name: Lint
25-
uses: golangci/golangci-lint-action@v3
26-
with:
27-
version: v1.50.1
28-
29105
- name: Unit Test
30106
run: make test
31107

.github/workflows/merge.yml

Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,36 @@
11
name: Publish Docker Latest
2+
23
on:
34
push:
45
branches:
56
- master
7+
8+
concurrency:
9+
group: ${{ github.workflow }}-${{ github.ref }}
10+
cancel-in-progress: false
11+
12+
permissions:
13+
contents: read
14+
615
jobs:
716
push_to_registry:
817
name: Push Docker image to Docker Hub Registry
9-
runs-on: ubuntu-20.04
18+
runs-on: ubuntu-latest
1019
steps:
11-
- name: Check out the repo
12-
uses: actions/checkout@v3
20+
- uses: actions/checkout@v4
1321

14-
- name: Set up Go 1.x
15-
uses: actions/setup-go@v3
22+
- name: Set up Go
23+
uses: actions/setup-go@v5
1624
with:
17-
go-version: ^1.19
18-
19-
- name: Logout first
20-
run: docker logout
25+
go-version: stable
26+
check-latest: true
27+
cache: true
2128

22-
- name: Login to docker hub registry
23-
run: echo ${{ secrets.DOCKER_PASSWORD }} | docker login docker.io -u ${{ secrets.DOCKER_USERNAME }} --password-stdin
29+
- name: Login to Docker Hub
30+
uses: docker/login-action@v3
31+
with:
32+
username: ${{ secrets.DOCKER_USERNAME }}
33+
password: ${{ secrets.DOCKER_PASSWORD }}
2434

2535
- name: Build binary
2636
run: make build_linux

.github/workflows/release.yml

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,31 @@
11
name: Publish Docker image
2+
23
on:
34
release:
45
types: [published]
6+
7+
permissions:
8+
contents: read
9+
510
jobs:
611
push_to_registry:
712
name: Push Docker image to Docker Hub Registry
8-
runs-on: ubuntu-20.04
13+
runs-on: ubuntu-latest
914
steps:
10-
- name: Check out the repo
11-
uses: actions/checkout@v2
15+
- uses: actions/checkout@v4
16+
17+
- name: Set up Go
18+
uses: actions/setup-go@v5
19+
with:
20+
go-version: stable
21+
check-latest: true
22+
cache: true
1223

13-
- name: Login to docker hub registry
14-
run: echo ${{ secrets.DOCKER_PASSWORD }} | docker login docker.io -u ${{ secrets.DOCKER_USERNAME }} --password-stdin
24+
- name: Login to Docker Hub
25+
uses: docker/login-action@v3
26+
with:
27+
username: ${{ secrets.DOCKER_USERNAME }}
28+
password: ${{ secrets.DOCKER_PASSWORD }}
1529

1630
- name: Build binary
1731
run: make build_linux

.golangci.yml

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
# golangci-lint configuration for altair.
2+
# Docs: https://golangci-lint.run/usage/configuration/
3+
# Keep this file conservative: enable what catches real bugs, avoid style
4+
# nitpicks that churn diffs without catching defects.
5+
6+
run:
7+
timeout: 5m
8+
9+
linters:
10+
# Start from the default set, then add a handful that have paid for
11+
# themselves in real Go codebases.
12+
disable-all: false
13+
enable:
14+
- errcheck # unchecked errors
15+
- gosimple # simplifiable expressions
16+
- govet # go vet, also runs in CI but belt-and-suspenders
17+
- ineffassign # useless assignments
18+
- staticcheck # wide-ranging static analysis
19+
- unused # unused code
20+
- misspell # English typos (e.g. the max_iddle_connection story)
21+
- unconvert # unnecessary type conversions
22+
- gofmt # formatting
23+
- goimports # import grouping/sorting
24+
25+
linters-settings:
26+
misspell:
27+
locale: US
28+
goimports:
29+
local-prefixes: github.com/kodefluence/altair
30+
31+
issues:
32+
# Default is 50; raise so we see the full failure list in CI rather than
33+
# a truncated sample.
34+
max-issues-per-linter: 0
35+
max-same-issues: 0
36+
37+
# Exclude generated code.
38+
exclude-dirs:
39+
- mock
40+
exclude-files:
41+
- ".*\\.pb\\.go$"
42+
- ".*_mock\\.go$"
43+
44+
# Don't fail builds on lint issues in test files that use fixture structs
45+
# whose fields aren't all exercised — common in table-driven tests.
46+
exclude-rules:
47+
- path: _test\.go
48+
linters:
49+
- errcheck
50+
- unused

module/migration/controller/command/migrate_force.go

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,13 @@ func NewMigrateForce(runner *usecase.Runner) *MigrateForce {
2121
return &MigrateForce{runner: runner, version: -1}
2222
}
2323

24-
func (*MigrateForce) Use() string { return "migrate:force" }
25-
func (*MigrateForce) Short() string { return "Force-set a plugin's migration version (clears dirty state)" }
26-
func (*MigrateForce) Example() string { return "altair plugin migrate:force --plugin oauth --version 3" }
24+
func (*MigrateForce) Use() string { return "migrate:force" }
25+
func (*MigrateForce) Short() string {
26+
return "Force-set a plugin's migration version (clears dirty state)"
27+
}
28+
func (*MigrateForce) Example() string {
29+
return "altair plugin migrate:force --plugin oauth --version 3"
30+
}
2731

2832
func (m *MigrateForce) ModifyFlags(flags *pflag.FlagSet) {
2933
flags.StringVar(&m.plugin, "plugin", "", "Plugin whose schema_version to force")

module/migration/usecase/source_test.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,11 @@ func TestMaxSourceVersion(t *testing.T) {
1717
{
1818
name: "finds the highest numeric prefix in a standard migration set",
1919
fs: fstest.MapFS{
20-
"migrations/mysql/1_create_users.up.sql": {Data: []byte("")},
20+
"migrations/mysql/1_create_users.up.sql": {Data: []byte("")},
2121
"migrations/mysql/1_create_users.down.sql": {Data: []byte("")},
22-
"migrations/mysql/7_add_index.up.sql": {Data: []byte("")},
23-
"migrations/mysql/7_add_index.down.sql": {Data: []byte("")},
24-
"migrations/mysql/3_alter_table.up.sql": {Data: []byte("")},
22+
"migrations/mysql/7_add_index.up.sql": {Data: []byte("")},
23+
"migrations/mysql/7_add_index.down.sql": {Data: []byte("")},
24+
"migrations/mysql/3_alter_table.up.sql": {Data: []byte("")},
2525
},
2626
path: "migrations/mysql",
2727
want: 7,

module/plugin_list/controller/command/list.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,10 @@ func NewList(registry []module.Plugin, appBearer core.AppBearer, pluginBearer co
2727
return &List{registry: registry, appBearer: appBearer, pluginBearer: pluginBearer, runner: runner}
2828
}
2929

30-
func (*List) Use() string { return "list" }
31-
func (*List) Short() string { return "List every plugin compiled into the binary and its activation state" }
30+
func (*List) Use() string { return "list" }
31+
func (*List) Short() string {
32+
return "List every plugin compiled into the binary and its activation state"
33+
}
3234
func (*List) Example() string { return "altair plugin list" }
3335

3436
func (*List) ModifyFlags(flags *pflag.FlagSet) {}

plugin/runner_test.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,12 @@ type stubPlugin struct {
1313
deps []string
1414
}
1515

16-
func (s *stubPlugin) Name() string { return s.name }
17-
func (s *stubPlugin) DependsOn() []string { return s.deps }
16+
func (s *stubPlugin) Name() string { return s.name }
17+
func (s *stubPlugin) DependsOn() []string { return s.deps }
1818
func (s *stubPlugin) Migrations(module.PluginContext) []module.MigrationSet { return nil }
19-
func (s *stubPlugin) Load(module.PluginContext) error { return nil }
20-
func (s *stubPlugin) LoadCommand(module.PluginContext) error { return nil }
21-
func (s *stubPlugin) SampleConfig() []byte { return nil }
19+
func (s *stubPlugin) Load(module.PluginContext) error { return nil }
20+
func (s *stubPlugin) LoadCommand(module.PluginContext) error { return nil }
21+
func (s *stubPlugin) SampleConfig() []byte { return nil }
2222

2323
func names(plugins []module.Plugin) []string {
2424
out := make([]string, len(plugins))

0 commit comments

Comments
 (0)