Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .github/workflows/assay-index.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ permissions:

env:
ASSAY_CACHE: /home/runner/.cache/assay
# Must match GO_TAGS in services/tms/Taskfile.yml. assay shells out to go
# test, and a differing tag set shares no build cache with the other jobs.
GOFLAGS: -tags=nomsgpack

jobs:
index:
Expand Down
3 changes: 3 additions & 0 deletions .github/workflows/assay-select.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ permissions:

env:
ASSAY_CACHE: /home/runner/.cache/assay
# Must match GO_TAGS in services/tms/Taskfile.yml. assay shells out to go
# test, and a differing tag set shares no build cache with the other jobs.
GOFLAGS: -tags=nomsgpack

jobs:
select:
Expand Down
11 changes: 8 additions & 3 deletions .github/workflows/test-tms.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,11 @@ permissions:
contents: read

env:
# Must match GO_TAGS in services/tms/Taskfile.yml. 769 of 832 packages depend
# on gin, so a job that builds under a different tag set shares no build cache
# with the others. Steps passing -tags on the command line override this
# rather than merging, so they spell nomsgpack out themselves.
GOFLAGS: -tags=nomsgpack
# Both test harnesses reuse this server, so the migrated template database is
# built once for the whole run instead of once per package.
TRENOVA_TEST_POSTGRES_DSN: postgres://postgres:postgres@localhost:5432/trenova_test?sslmode=disable
Expand Down Expand Up @@ -131,7 +136,7 @@ jobs:
printf 'integration packages: %d\n' "${#packages[@]}"
# Cap package parallelism: every process creates databases on the one
# shared server, and the runner only has two cores anyway.
go test -p 4 -tags=integration -coverprofile=coverage-integration.out \
go test -p 4 -tags=integration,nomsgpack -coverprofile=coverage-integration.out \
-covermode=atomic "${packages[@]}"

- name: Upload integration coverage artifact
Expand Down Expand Up @@ -270,7 +275,7 @@ jobs:
working-directory: ./services/tms
run: |
set -euo pipefail
go run github.com/swaggo/swag/cmd/swag@v1.16.6 init -g ./cmd/cli/main.go -o ./docs --parseInternal --parseDependency
go run github.com/swaggo/swag/cmd/swag@v1.16.6 init -g ./cmd/cli/main.go -o ./docs --parseInternal --parseDependency --outputTypes json,yaml
go run ./cmd/openapi-postprocess
if ! git diff --quiet -- docs; then
echo "::error::OpenAPI spec is stale - run 'task docs-generate' and commit"
Expand Down Expand Up @@ -344,4 +349,4 @@ jobs:

- name: Run TMS race tests
working-directory: ./services/tms
run: go test -p 4 -race -tags=integration ./...
run: go test -p 4 -race -tags=integration,nomsgpack ./...
5 changes: 5 additions & 0 deletions .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@ version: "2"
run:
go: "1.26"
tests: false
# Must match GO_TAGS in services/tms/Taskfile.yml. Linting under a different
# tag set type-checks a different build of almost every package and discards
# the build cache the other commands share.
build-tags:
- nomsgpack
linters:
default: none
enable:
Expand Down
2 changes: 1 addition & 1 deletion deploy/Dockerfile.tms
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ COPY services/tms/ ./services/tms/

WORKDIR /build/services/tms
ENV CGO_ENABLED=1
RUN go build -tags musl -ldflags="-s -w" -o /build/trenova ./cmd/cli
RUN go build -tags musl,nomsgpack -ldflags="-s -w" -o /build/trenova ./cmd/cli

FROM alpine:3.21

Expand Down
2 changes: 1 addition & 1 deletion docs/engineering/generated-artifacts.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ go generate ./internal/infrastructure/database/seeder/... # pkg/seedhel
go generate ./internal/api/graphql/projection/... # internal/api/graphql/projection/specs_gen.go
go generate ./internal/infrastructure/database/reportcatalog/... # pkg/reportcatalog/catalog_gen.go

go run github.com/swaggo/swag/cmd/swag@v1.16.6 init -g ./cmd/cli/main.go -o ./docs --parseInternal --parseDependency
go run github.com/swaggo/swag/cmd/swag@v1.16.6 init -g ./cmd/cli/main.go -o ./docs --parseInternal --parseDependency --outputTypes json,yaml
go run ./cmd/openapi-postprocess # then: git diff --quiet -- docs
```

Expand Down
11 changes: 10 additions & 1 deletion services/tms/.air.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,16 @@ root = "."
tmp_dir = "tmp"

[build]
cmd = "go build -tags nofitz -o ./tmp/trenova-cli.exe ./cmd/cli"
# -N -l (no optimisation, no inlining) takes a resolver-edit reload from ~25s
# to ~17s. It is scoped to first-party packages rather than `all=` so the
# stdlib and runtime stay optimised, and it applies only here -- `task
# build-cli`, CI and the release image are all unaffected.
#
# Two costs: the reloaded server runs unoptimised code, so do not benchmark
# against it, and these flags key a separate build cache, so the first reload
# after adopting them rebuilds every first-party package once (~160s).
# Drop the -gcflags argument to revert.
cmd = "go build -tags nofitz,nomsgpack -gcflags='github.com/emoss08/trenova/...=-N -l' -o ./tmp/trenova-cli.exe ./cmd/cli"
bin = "./tmp/trenova-cli"
delay = 100
exclude_dir = ["assets", "tmp", "vendor", "ui", "platform", "microservices"]
Expand Down
27 changes: 20 additions & 7 deletions services/tms/Taskfile.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,24 @@ vars:
TEST_DB_CONTAINER: trenova-integration-postgres
TEST_DB_PORT: "55432"
TEST_POSTGRES_DSN: "postgres://postgres:postgres@localhost:55432/trenova_test?sslmode=disable"
# nomsgpack drops gin's MsgPack binding, which nothing here uses. It is the
# only thing pulling in ugorji/go/codec, a 20s compile on the critical path.
#
# go-fitz needs libmupdf, which has no pure-Go build and no bundled Windows DLL.
# Windows dev builds drop it so the CLI starts; Linux/macOS/Docker keep PDF support.
# Override with `task build-cli GO_TAGS=` to force it back on.
GO_TAGS: '{{if eq OS "windows"}}nofitz{{end}}'
# Override with `task build-cli GO_TAGS=nomsgpack` to force PDF support back on.
GO_TAGS: 'nomsgpack{{if eq OS "windows"}},nofitz{{end}}'
GO_TAG_FLAG: '{{if .GO_TAGS}}-tags {{.GO_TAGS}}{{end}}'
# Commands that pass -tags on the command line override GOFLAGS rather than
# merging with it, so they have to spell the base tags out themselves.
GO_TAGS_INTEGRATION: '{{if .GO_TAGS}}{{.GO_TAGS}},{{end}}integration'

# 769 of 832 packages depend on gin, so a go command that misses the tag set
# rebuilds and re-caches almost the whole tree under a second configuration.
# Setting GOFLAGS here covers every go invocation in this file, including ones
# added later.
env:
GOFLAGS: '{{if .GO_TAGS}}-tags={{.GO_TAGS}}{{end}}'

tasks:
default:
Expand Down Expand Up @@ -84,7 +97,7 @@ tasks:
set -euo pipefail
packages=$(git grep -l 'go:build integration' -- '*_test.go' \
| xargs -n1 dirname | sort -u | sed 's|^|./|')
go test -p 4 -tags=integration $packages
go test -p 4 -tags={{.GO_TAGS_INTEGRATION}} $packages

test-all:
desc: Run all tests (unit + integration)
Expand All @@ -93,7 +106,7 @@ tasks:
TRENOVA_TEST_POSTGRES_DSN: "{{.TEST_POSTGRES_DSN}}"
APP_ENV: test
cmds:
- go test -p 4 -tags=integration ./...
- go test -p 4 -tags={{.GO_TAGS_INTEGRATION}} ./...

test-db-up:
desc: Start the shared Postgres the integration suite runs against
Expand Down Expand Up @@ -134,7 +147,7 @@ tasks:
TRENOVA_TEST_POSTGRES_DSN: "{{.TEST_POSTGRES_DSN}}"
APP_ENV: test
cmds:
- go test -p 4 -tags=integration -coverprofile=coverage.out ./...
- go test -p 4 -tags={{.GO_TAGS_INTEGRATION}} -coverprofile=coverage.out ./...
- go tool cover -html=coverage.out -o coverage.html

test-short:
Expand Down Expand Up @@ -261,14 +274,14 @@ tasks:
docs-generate:
desc: Generate Swagger docs for the TMS API
cmds:
- go run github.com/swaggo/swag/cmd/swag@v1.16.6 init -g ./cmd/cli/main.go -o ./docs --parseInternal --parseDependency
- go run github.com/swaggo/swag/cmd/swag@v1.16.6 init -g ./cmd/cli/main.go -o ./docs --parseInternal --parseDependency --outputTypes json,yaml
- go run ./cmd/openapi-postprocess

docs-generate-check:
desc: Check the generated OpenAPI spec is up to date
cmds:
- |
go run github.com/swaggo/swag/cmd/swag@v1.16.6 init -g ./cmd/cli/main.go -o ./docs --parseInternal --parseDependency
go run github.com/swaggo/swag/cmd/swag@v1.16.6 init -g ./cmd/cli/main.go -o ./docs --parseInternal --parseDependency --outputTypes json,yaml
go run ./cmd/openapi-postprocess
if git diff --quiet -- docs; then
echo "OpenAPI spec is up to date"
Expand Down
18 changes: 10 additions & 8 deletions services/tms/cmd/cli/db/db_create_seed.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,23 @@ import (
"os"
"os/exec"
"path/filepath"
"regexp"
"strconv"
"strings"
"text/template"

"github.com/fatih/color"
"github.com/samber/lo"
"github.com/spf13/cobra"
"golang.org/x/tools/imports"
)

var (
seedDev bool
seedTest bool
)

var seedNamePattern = regexp.MustCompile(`^[A-Za-z][A-Za-z0-9_]*$`)

var createSeedCmd = &cobra.Command{
Use: "create-seed <name>",
Short: "Create a new database seed",
Expand All @@ -36,6 +38,12 @@ func init() {

func runCreateSeed(cmd *cobra.Command, args []string) error {
seedName := args[0]
if !seedNamePattern.MatchString(seedName) {
return fmt.Errorf(
"invalid seed name %q: must start with a letter and contain only letters, digits and underscores",
seedName,
)
}

var targetDir string
var environments string
Expand Down Expand Up @@ -74,13 +82,7 @@ func runCreateSeed(cmd *cobra.Command, args []string) error {

formatted, err := format.Source([]byte(content))
if err != nil {
formatted, err = imports.Process(filepath, []byte(content), nil)
if err != nil {
color.Yellow("⚠ Could not format seed file: %v", err)
formatted = []byte(content)
}
} else {
formatted, _ = imports.Process(filepath, formatted, nil)
return fmt.Errorf("format seed content: %w", err)
}

if err := os.WriteFile(filepath, formatted, 0o644); err != nil {
Expand Down
Loading
Loading