Checking Code Quality in Cosmos
…including highly opinionated stuff that people I respect might disagree with
Steps 1-8 are below, and cover the easy, boring stuff that everyone should attend to regardless.
- the chain should specify the latest version of go in go.mod – this ensures that all validators are using the latest version of go. Long-run this means that the chain won’t rando apphash for stuff like the crypto changes between 1.18 and 1.19.
Alos devs have latest. use it. Currently, this means that a new chain should be using go v1.20. This means it may be necessary to bump the golang in one of the older proto-builder containers, that you can find here:
- lint with golangci-lint
if you enable gofumpt (usually by replacing gofmt and goimports) in .golangci.yml, then you can fumpt and fix common issues like:
golangci-lint run ./… --fix
now your editor warns more. Typicially you’ll want a .golangci.yml file that includes:
run:
tests: true
timeout: 10m
sort-results: true
allow-parallel-runners: true
skip-dirs:
- "legacy_ibc_testing"
- "testutil"
linters:
disable-all: true
enable:
- depguard
- dogsled
- exportloopref
- goconst
- gocritic
- gofumpt
- gosec
- gosimple
- govet
- ineffassign
- misspell
- nakedret
- nolintlint
- staticcheck
- revive
- stylecheck
- typecheck
- thelper
- unconvert
- unused
issues:
gofumpt:
module-path: github.com/cosmos/interchain-security
extra-rules: true
exclude-rules:
- text: "Use of weak random number generator"
linters:
- gosec
- text: "leading space"
linters:
- nolintlint
max-issues-per-linter: 10000
max-same-issues: 10000
linters-settings:
gocritic:
disabled-checks:
- appendAssign
dogsled:
max-blank-identifiers: 3
maligned:
# print struct with more effective memory layout or not, false by default
suggest-new: true
nolintlint:
allow-unused: false
allow-leading-space: true
require-explanation: false
require-specific: false
By specifying extra in gofumpt, you are asking it to format the code even more tightly. By giving it the module path, it can see the module paths.
4) Module Versions
4) go.mod: versions
I have the current versions of ecosystem modules burned into working memory
- tendermint v0.34.28 (if sdk 46 or 45) or v0.37.1 (if sdk 47)
- cosmos-sdk or v0.46.12 or v0.47.2 – you should bias towards the newer release for a new chain
- iavl v0.20.0
- cosmos-proto alpha8
- ibc-go v6.1.0 if on sdk 46 and ibc-go 7.0.0 if on sdk 47
6) Annotate replace statements in go.mod
like this:
replace (
// osmosis-patched wasmd.
github.com/CosmWasm/wasmd => github.com/osmosis-labs/wasmd v0.31.0-osmo-v16
// dragonberry
github.com/confio/ics23/go => github.com/cosmos/cosmos-sdk/ics23/go v0.8.0
// Our cosmos-sdk branch is: https://github.com/osmosis-labs/cosmos-sdk, current branch: v16.x. Direct commit link: https://github.com/osmosis-labs/cosmos-sdk/commit/43c58d9061e3b8e0f06c3d9efef8c728800ab554
github.com/cosmos/cosmos-sdk => github.com/osmosis-labs/cosmos-sdk v0.45.1-0.20230326212251-7a2cf2993434
// N.B. v0.19.5 contains a breaking change to the IAVL API
github.com/cosmos/iavl v0.19.5 => github.com/cosmos/iavl v0.19.4
// use cosmos-compatible protobufs
github.com/gogo/protobuf => github.com/regen-network/protobuf v1.3.3-alpha.regen.1
// Informal Tendermint fork
github.com/tendermint/tendermint => github.com/informalsystems/tendermint v0.34.24
// use grpc compatible with cosmos protobufs
google.golang.org/grpc => google.golang.org/grpc v1.33.2
)
You should annotate the replace statements so that you and others checking your chain know where they refer to specifically, and why they are there.
5) Dependabot
As your chain matures, you will want to upgrade it to the latest ecosystem modules. Dependabot is a tool that makes that easier by making those PR’s for you. Watch out, as it may sometimes suggest breaking changes. If that happens, you’ll want to figure out if the suggested changes will be beneficial to you or not.
# To get started with Dependabot version updates, you'll need to specify which
# package ecosystems to update and where the package manifests are located.
# Please see the documentation for all configuration options:
# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
version: 2
updates:
- package-ecosystem: "gomod" # See documentation for possible values
directory: "/" # Location of package manifests
schedule:
interval: "daily"
- package-ecosystem: "github-actions" # See documentation for possible values
directory: "/" # Location of package manifests
schedule:
interval: "daily"
Thats what I recommend nearly universally.
- Don’t let green checkmarks lie to you
- use the standard github action for golangci-lint https://github.com/golangci/golangci-lint-action…
- run tests in ci
- build in ci
- containerize in ci
- sdk&gaia had lying greens. it hacks optic nerve. you feel safe. They took a long time to fix because the teams said “look, the checkmark was green” but in fact neither repo had been linted in a long time because CI has malfunctioned and the green check mark is a lie
- don’t use a diff as conditional in ci
7) Check over app.go carefully
In app.go we “wire” a chain together. You should check this file very carefully, and you SHOULD use upstream sources to help you to verify your work.
8) less is more
is kinda god tier here. You can do less in the chain, and more there, and it really does work out very well. No matter your goals, minimize the code (usually can import what you need & splice) viz:
9) Essential CI Jobs
The CI jobs that you’ll want at minimum are:
- golangci-lint: GitHub - golangci/golangci-lint-action: Official GitHub action for golangci-lint from its authors
- run tests: GitHub - mvdan/github-actions-golang: GitHub Actions as CI for Go
- Build your docker container
- Publish your protobuf files to the buf registry
- test the build
- codeql for static analysis