Sources & Checksums

Tasks can declare source file patterns. gogo uses these for incremental builds — skipping execution when nothing has changed.

Checksum Mode (sources only)

When only sources is set, gogo computes a SHA256 checksum of all matching files and skips execution if nothing changed since the last run:

tasks:
  build:
    cmd: go build -o myapp ./...
    sources:
      - "**/*.go"
      - go.mod
      - go.sum

On the first run, the task executes and the checksum is stored in .gogo/checksum/. On subsequent runs, gogo recomputes the checksum and skips the task if it matches.

Timestamp Mode (sources + generates)

When both sources and generates are set, gogo uses timestamp comparison instead. The task is skipped only when all output files exist and are newer than all source files:

tasks:
  build:
    cmd: go build -o bin/myapp ./...
    sources:
      - "**/*.go"
      - go.mod
    generates:
      - bin/myapp

This avoids checksum storage and matches traditional make-style incremental builds.

Status Mode (status:)

When file fingerprints can’t answer “is this already done?”, give the task status: commands — shell probes of the desired end state. The task is skipped when every status command exits 0:

tasks:
  create-bucket:
    status:
      - aws s3api head-bucket --bucket my-app-artifacts
    cmd: aws s3 mb s3://my-app-artifacts

  install-tools:
    status:
      - which golangci-lint
      - which goimports
    cmds:
      - go install github.com/golangci/golangci-lint/v2/cmd/golangci-lint@latest
      - go install golang.org/x/tools/cmd/goimports@latest

This suits idempotent operations — creating cloud resources, installing tools, running database migrations — where the world, not a file tree, holds the answer.

Semantics:

Not to be confused with preconditions, which invert the meaning: a failing precondition aborts with an error (“you may not run”), while a failing status command simply means the task needs to run.

Glob Patterns

Non-recursive patterns are matched with Go’s filepath.Glob, which supports *, ?, and […] character classes within a single path segment:

Pattern Matches
*.go Go files in the task directory
cmd/*.go Go files in the cmd directory
go.mod The go.mod file

Recursive Patterns (**)

A pattern containing ** triggers a recursive walk. gogo’s matcher is a small superset of filepath.Glob rather than a full doublestar implementation — keep these rules in mind:

Pattern Matches
**/*.go All .go files in any subdirectory (basename matches *.go)
**/*.proto All .proto files recursively
vendor/** Every file under vendor/
internal/**/*.go All .go files under internal/ (basename matches *.go)

Because matching after ** is basename-only, a pattern like **/foo/*.go doesn’t constrain foo to be the direct parent directory — it just looks for files whose basename matches foo/*.go, which won’t match anything. Express that constraint as foo/**/*.go instead, anchored to a known prefix.

Presets

The same source list — **/*.go, go.mod, go.sum — is repeated on every Go build/lint/test task. Source presets let you name a list once and reference it by name:

sources:
  lint: [go, .golangci.yml]    # composes the built-in `go` preset with one literal

tasks:
  build:
    cmd: go build ./...
    sources: go                # short form: a single name
  test:
    cmd: go test ./...
    sources: go
  lint:
    cmd: golangci-lint run
    sources: lint              # references the user-defined preset above
  format:
    cmd: goimports -w .
    sources: "**/*.go"         # plain globs still work

A sources: entry that has no glob characters (*, ?, [, ], /, \) is looked up in the preset map first; if no preset matches, it’s treated as a literal path. So go.mod and .golangci.yml continue to work as bare filenames.

Built-in Presets

gogo ships with two built-ins. User-defined entries with the same name win.

Preset Expands to
go **/*.go, go.mod, go.sum
go-vendored the go preset, plus vendor/**

Composition

Presets can reference other presets — the resolver expands recursively, deduplicates, and rejects cycles:

sources:
  go-strict: [go, .golangci.yml, .editorconfig]
  ci: [go-strict, scripts/**]

Overriding a Built-in

Define a preset with the same name to replace the built-in:

sources:
  # This project doesn't track go.sum.
  go: ["**/*.go", "go.mod"]

Checksum Storage

Checksums are stored in .gogo/checksum/ relative to the task file directory. You should add .gogo/ to your .gitignore:

# .gitignore
.gogo/

Up-to-Date Output

When a task is skipped, gogo prints:

[build] up to date
Edit this page on GitHub