build(dev): add compose and air hot reload
- commit
- 63f8a7f0b837606433f75ea6cca64250b222dd14
- author
- brice · · 2026-10-10 11:55 UTC
- parent
- 470cf12
- signature
- GPG Verified
CD88F9E564E0102646FB6077A1A07C7C7C3E349D· arch-gpg Verified for the committer email
4 files · +245 -5
A .air.toml +25 -0
| @@ -0,0 +1,25 @@ | |||
| 1 | + | # Hot reload for `docker compose up` (see compose.yaml). Run from the module root. | |
| 2 | + | root = "." | |
| 3 | + | tmp_dir = "tmp" | |
| 4 | + | ||
| 5 | + | [build] | |
| 6 | + | # templ files are generated when the module pins templ as a tool (web UI); | |
| 7 | + | # until then this is a no-op, so the same config works before and after. | |
| 8 | + | pre_cmd = ["if grep -q 'a-h/templ' go.mod; then go tool templ generate; fi"] | |
| 9 | + | cmd = "go build -o ./tmp/ermite ./cmd/ermite" | |
| 10 | + | entrypoint = ["./tmp/ermite", "serve"] | |
| 11 | + | include_ext = ["go", "templ"] | |
| 12 | + | exclude_dir = ["gen", "tmp", "scripts", "proto", ".git", ".worktrees"] | |
| 13 | + | # *_templ.go is the output of templ generate: a .templ change triggers the | |
| 14 | + | # rebuild, the generated Go file does not trigger a second one. | |
| 15 | + | exclude_regex = ["_test\\.go", "_templ\\.go"] | |
| 16 | + | # Let the server finish in-flight requests, then stop it. | |
| 17 | + | send_interrupt = true | |
| 18 | + | kill_delay = "2s" | |
| 19 | + | delay = 300 | |
| 20 | + | ||
| 21 | + | [log] | |
| 22 | + | time = false | |
| 23 | + | ||
| 24 | + | [misc] | |
| 25 | + | clean_on_exit = true | |
| @@ -0,0 +1,25 @@ | |||||
| 1 | + | # Hot reload for `docker compose up` (see compose.yaml). Run from the module root. | |||
| 2 | + | root = "." | |||
| 3 | + | tmp_dir = "tmp" | |||
| 4 | + | ||||
| 5 | + | [build] | |||
| 6 | + | # templ files are generated when the module pins templ as a tool (web UI); | |||
| 7 | + | # until then this is a no-op, so the same config works before and after. | |||
| 8 | + | pre_cmd = ["if grep -q 'a-h/templ' go.mod; then go tool templ generate; fi"] | |||
| 9 | + | cmd = "go build -o ./tmp/ermite ./cmd/ermite" | |||
| 10 | + | entrypoint = ["./tmp/ermite", "serve"] | |||
| 11 | + | include_ext = ["go", "templ"] | |||
| 12 | + | exclude_dir = ["gen", "tmp", "scripts", "proto", ".git", ".worktrees"] | |||
| 13 | + | # *_templ.go is the output of templ generate: a .templ change triggers the | |||
| 14 | + | # rebuild, the generated Go file does not trigger a second one. | |||
| 15 | + | exclude_regex = ["_test\\.go", "_templ\\.go"] | |||
| 16 | + | # Let the server finish in-flight requests, then stop it. | |||
| 17 | + | send_interrupt = true | |||
| 18 | + | kill_delay = "2s" | |||
| 19 | + | delay = 300 | |||
| 20 | + | ||||
| 21 | + | [log] | |||
| 22 | + | time = false | |||
| 23 | + | ||||
| 24 | + | [misc] | |||
| 25 | + | clean_on_exit = true | |||
A compose.yaml +37 -0
| @@ -0,0 +1,37 @@ | |||
| 1 | + | # Development server with hot reload: `docker compose up`, then http://localhost:7341. | |
| 2 | + | # The module is built on its own (no go.work), like CI. Private modules bnema.dev/ermite-* | |
| 3 | + | # are fetched from git.bnema.dev, which the container reaches through the host's DNS and routes. | |
| 4 | + | services: | |
| 5 | + | ermite: | |
| 6 | + | build: | |
| 7 | + | dockerfile_inline: | | |
| 8 | + | FROM golang:1.27-alpine | |
| 9 | + | RUN apk add --no-cache git git-daemon | |
| 10 | + | working_dir: /src | |
| 11 | + | command: ["go", "tool", "air", "-c", ".air.toml"] | |
| 12 | + | ports: | |
| 13 | + | - "127.0.0.1:7341:7341" | |
| 14 | + | environment: | |
| 15 | + | CGO_ENABLED: "0" | |
| 16 | + | GOWORK: "off" | |
| 17 | + | GOTOOLCHAIN: local | |
| 18 | + | GOPRIVATE: bnema.dev/ermite* | |
| 19 | + | # The mounted .git of a worktree points outside the container. | |
| 20 | + | GOFLAGS: -buildvcs=false | |
| 21 | + | ERMITE_DATA_DIR: /data | |
| 22 | + | ERMITE_ADDR: ":7341" | |
| 23 | + | ERMITE_PUBLIC_URL: http://localhost:7341 | |
| 24 | + | # Development placeholder, not a secret: the port is bound to the loopback. | |
| 25 | + | ERMITE_ADMIN_TOKEN: dev-only-admin-token-do-not-use-0123456789 | |
| 26 | + | ERMITE_LOG_FORMAT: console | |
| 27 | + | ERMITE_LOG_LEVEL: debug | |
| 28 | + | volumes: | |
| 29 | + | - .:/src | |
| 30 | + | - go-mod:/go/pkg/mod | |
| 31 | + | - go-build:/root/.cache/go-build | |
| 32 | + | - data:/data | |
| 33 | + | ||
| 34 | + | volumes: | |
| 35 | + | go-mod: | |
| 36 | + | go-build: | |
| 37 | + | data: | |
| @@ -0,0 +1,37 @@ | |||||
| 1 | + | # Development server with hot reload: `docker compose up`, then http://localhost:7341. | |||
| 2 | + | # The module is built on its own (no go.work), like CI. Private modules bnema.dev/ermite-* | |||
| 3 | + | # are fetched from git.bnema.dev, which the container reaches through the host's DNS and routes. | |||
| 4 | + | services: | |||
| 5 | + | ermite: | |||
| 6 | + | build: | |||
| 7 | + | dockerfile_inline: | | |||
| 8 | + | FROM golang:1.27-alpine | |||
| 9 | + | RUN apk add --no-cache git git-daemon | |||
| 10 | + | working_dir: /src | |||
| 11 | + | command: ["go", "tool", "air", "-c", ".air.toml"] | |||
| 12 | + | ports: | |||
| 13 | + | - "127.0.0.1:7341:7341" | |||
| 14 | + | environment: | |||
| 15 | + | CGO_ENABLED: "0" | |||
| 16 | + | GOWORK: "off" | |||
| 17 | + | GOTOOLCHAIN: local | |||
| 18 | + | GOPRIVATE: bnema.dev/ermite* | |||
| 19 | + | # The mounted .git of a worktree points outside the container. | |||
| 20 | + | GOFLAGS: -buildvcs=false | |||
| 21 | + | ERMITE_DATA_DIR: /data | |||
| 22 | + | ERMITE_ADDR: ":7341" | |||
| 23 | + | ERMITE_PUBLIC_URL: http://localhost:7341 | |||
| 24 | + | # Development placeholder, not a secret: the port is bound to the loopback. | |||
| 25 | + | ERMITE_ADMIN_TOKEN: dev-only-admin-token-do-not-use-0123456789 | |||
| 26 | + | ERMITE_LOG_FORMAT: console | |||
| 27 | + | ERMITE_LOG_LEVEL: debug | |||
| 28 | + | volumes: | |||
| 29 | + | - .:/src | |||
| 30 | + | - go-mod:/go/pkg/mod | |||
| 31 | + | - go-build:/root/.cache/go-build | |||
| 32 | + | - data:/data | |||
| 33 | + | ||||
| 34 | + | volumes: | |||
| 35 | + | go-mod: | |||
| 36 | + | go-build: | |||
| 37 | + | data: | |||
M go.mod +18 -1
| @@ -3,6 +3,7 @@ module bnema.dev/ermite | |||
| 3 | 3 | go 1.27.1 | |
| 4 | 4 | ||
| 5 | 5 | tool ( | |
| 6 | + | github.com/air-verse/air | |
| 6 | 7 | github.com/bnema/hexcheck/cmd/hexcheck | |
| 7 | 8 | github.com/bufbuild/buf/cmd/buf | |
| 8 | 9 | github.com/vektra/mockery/v3 | |
| @@ -38,8 +39,14 @@ require ( | |||
| 38 | 39 | cel.dev/cel-go v0.32.0 // indirect | |
| 39 | 40 | cel.dev/expr v0.25.3 // indirect | |
| 40 | 41 | connectrpc.com/otelconnect v0.9.0 // indirect | |
| 42 | + | dario.cat/mergo v1.0.2 // indirect | |
| 41 | 43 | github.com/Microsoft/go-winio v0.6.2 // indirect | |
| 44 | + | github.com/air-verse/air v1.67.4 // indirect | |
| 45 | + | github.com/andybalholm/brotli v1.2.0 // indirect | |
| 42 | 46 | github.com/antlr4-go/antlr/v4 v4.13.1 // indirect | |
| 47 | + | github.com/bep/godartsass/v2 v2.5.0 // indirect | |
| 48 | + | github.com/bep/golibsass v1.2.0 // indirect | |
| 49 | + | github.com/bits-and-blooms/bitset v1.24.5 // indirect | |
| 43 | 50 | github.com/bnema/hexcheck v0.3.1 // indirect | |
| 44 | 51 | github.com/brunoga/deep v1.3.1 // indirect | |
| 45 | 52 | github.com/bufbuild/buf v1.73.0 // indirect | |
| @@ -47,7 +54,7 @@ require ( | |||
| 47 | 54 | github.com/bufbuild/protoplugin v0.0.0-20260414125817-25d1d281b46b // indirect | |
| 48 | 55 | github.com/cespare/xxhash/v2 v2.3.0 // indirect | |
| 49 | 56 | github.com/cli/browser v1.3.0 // indirect | |
| 50 | - | github.com/clipperhouse/uax29/v2 v2.2.0 // indirect | |
| 57 | + | github.com/clipperhouse/uax29/v2 v2.6.0 // indirect | |
| 51 | 58 | github.com/containerd/errdefs v1.0.0 // indirect | |
| 52 | 59 | github.com/containerd/errdefs/pkg v0.3.0 // indirect | |
| 53 | 60 | github.com/cpuguy83/go-md2man/v2 v2.0.7 // indirect | |
| @@ -56,19 +63,24 @@ require ( | |||
| 56 | 63 | github.com/docker/docker-credential-helpers v0.9.9 // indirect | |
| 57 | 64 | github.com/docker/go-connections v0.8.1 // indirect | |
| 58 | 65 | github.com/docker/go-units v0.5.0 // indirect | |
| 66 | + | github.com/fatih/color v1.18.0 // indirect | |
| 59 | 67 | github.com/fatih/structs v1.1.0 // indirect | |
| 60 | 68 | github.com/felixge/httpsnoop v1.1.0 // indirect | |
| 61 | 69 | github.com/fsnotify/fsnotify v1.9.0 // indirect | |
| 62 | 70 | github.com/go-logr/logr v1.4.4 // indirect | |
| 63 | 71 | github.com/go-logr/stdr v1.2.2 // indirect | |
| 64 | 72 | github.com/go-viper/mapstructure/v2 v2.5.0 // indirect | |
| 73 | + | github.com/gobwas/glob v0.2.3 // indirect | |
| 65 | 74 | github.com/gofrs/flock v0.13.1 // indirect | |
| 75 | + | github.com/gohugoio/hashstructure v0.6.0 // indirect | |
| 76 | + | github.com/gohugoio/hugo v0.164.0 // indirect | |
| 66 | 77 | github.com/google/go-containerregistry v0.22.1 // indirect | |
| 67 | 78 | github.com/google/uuid v1.6.0 // indirect | |
| 68 | 79 | github.com/huandu/xstrings v1.5.0 // indirect | |
| 69 | 80 | github.com/inconshreveable/mousetrap v1.1.0 // indirect | |
| 70 | 81 | github.com/jdx/go-netrc v1.0.0 // indirect | |
| 71 | 82 | github.com/jedib0t/go-pretty/v6 v6.7.8 // indirect | |
| 83 | + | github.com/joho/godotenv v1.5.1 // indirect | |
| 72 | 84 | github.com/klauspost/compress v1.20.0 // indirect | |
| 73 | 85 | github.com/klauspost/pgzip v1.2.6 // indirect | |
| 74 | 86 | github.com/knadh/koanf/maps v0.1.2 // indirect | |
| @@ -88,6 +100,8 @@ require ( | |||
| 88 | 100 | github.com/moby/moby/client v0.6.0 // indirect | |
| 89 | 101 | github.com/opencontainers/go-digest v1.0.0 // indirect | |
| 90 | 102 | github.com/opencontainers/image-spec v1.1.1 // indirect | |
| 103 | + | github.com/pelletier/go-toml v1.9.5 // indirect | |
| 104 | + | github.com/pelletier/go-toml/v2 v2.4.3 // indirect | |
| 91 | 105 | github.com/petermattis/goid v0.0.0-20260820044319-269ab09b5261 // indirect | |
| 92 | 106 | github.com/quic-go/qpack v0.6.0 // indirect | |
| 93 | 107 | github.com/quic-go/quic-go v0.62.0 // indirect | |
| @@ -98,8 +112,11 @@ require ( | |||
| 98 | 112 | github.com/segmentio/asm v1.2.1 // indirect | |
| 99 | 113 | github.com/segmentio/encoding v0.5.4 // indirect | |
| 100 | 114 | github.com/sirupsen/logrus v1.10.2 // indirect | |
| 115 | + | github.com/spf13/afero v1.15.0 // indirect | |
| 116 | + | github.com/spf13/cast v1.10.0 // indirect | |
| 101 | 117 | github.com/spf13/pflag v1.0.10 // indirect | |
| 102 | 118 | github.com/stretchr/objx v0.5.3 // indirect | |
| 119 | + | github.com/tdewolff/parse/v2 v2.8.12 // indirect | |
| 103 | 120 | github.com/tetratelabs/wazero v1.12.0 // indirect | |
| 104 | 121 | github.com/tidwall/btree v1.8.1 // indirect | |
| 105 | 122 | github.com/vektra/mockery/v3 v3.8.0 // indirect | |
| @@ -3,6 +3,7 @@ module bnema.dev/ermite | |||||
| 3 | go 1.27.1 | 3 | go 1.27.1 | ||
| 4 | 4 | ||||
| 5 | tool ( | 5 | tool ( | ||
| 6 | + | github.com/air-verse/air | |||
| 6 | github.com/bnema/hexcheck/cmd/hexcheck | 7 | github.com/bnema/hexcheck/cmd/hexcheck | ||
| 7 | github.com/bufbuild/buf/cmd/buf | 8 | github.com/bufbuild/buf/cmd/buf | ||
| 8 | github.com/vektra/mockery/v3 | 9 | github.com/vektra/mockery/v3 | ||
| @@ -38,8 +39,14 @@ require ( | |||||
| 38 | cel.dev/cel-go v0.32.0 // indirect | 39 | cel.dev/cel-go v0.32.0 // indirect | ||
| 39 | cel.dev/expr v0.25.3 // indirect | 40 | cel.dev/expr v0.25.3 // indirect | ||
| 40 | connectrpc.com/otelconnect v0.9.0 // indirect | 41 | connectrpc.com/otelconnect v0.9.0 // indirect | ||
| 42 | + | dario.cat/mergo v1.0.2 // indirect | |||
| 41 | github.com/Microsoft/go-winio v0.6.2 // indirect | 43 | github.com/Microsoft/go-winio v0.6.2 // indirect | ||
| 44 | + | github.com/air-verse/air v1.67.4 // indirect | |||
| 45 | + | github.com/andybalholm/brotli v1.2.0 // indirect | |||
| 42 | github.com/antlr4-go/antlr/v4 v4.13.1 // indirect | 46 | github.com/antlr4-go/antlr/v4 v4.13.1 // indirect | ||
| 47 | + | github.com/bep/godartsass/v2 v2.5.0 // indirect | |||
| 48 | + | github.com/bep/golibsass v1.2.0 // indirect | |||
| 49 | + | github.com/bits-and-blooms/bitset v1.24.5 // indirect | |||
| 43 | github.com/bnema/hexcheck v0.3.1 // indirect | 50 | github.com/bnema/hexcheck v0.3.1 // indirect | ||
| 44 | github.com/brunoga/deep v1.3.1 // indirect | 51 | github.com/brunoga/deep v1.3.1 // indirect | ||
| 45 | github.com/bufbuild/buf v1.73.0 // indirect | 52 | github.com/bufbuild/buf v1.73.0 // indirect | ||
| @@ -47,7 +54,7 @@ require ( | |||||
| 47 | github.com/bufbuild/protoplugin v0.0.0-20260414125817-25d1d281b46b // indirect | 54 | github.com/bufbuild/protoplugin v0.0.0-20260414125817-25d1d281b46b // indirect | ||
| 48 | github.com/cespare/xxhash/v2 v2.3.0 // indirect | 55 | github.com/cespare/xxhash/v2 v2.3.0 // indirect | ||
| 49 | github.com/cli/browser v1.3.0 // indirect | 56 | github.com/cli/browser v1.3.0 // indirect | ||
| 50 | - | github.com/clipperhouse/uax29/v2 v2.2.0 // indirect | 57 | + | github.com/clipperhouse/uax29/v2 v2.6.0 // indirect |
| 51 | github.com/containerd/errdefs v1.0.0 // indirect | 58 | github.com/containerd/errdefs v1.0.0 // indirect | ||
| 52 | github.com/containerd/errdefs/pkg v0.3.0 // indirect | 59 | github.com/containerd/errdefs/pkg v0.3.0 // indirect | ||
| 53 | github.com/cpuguy83/go-md2man/v2 v2.0.7 // indirect | 60 | github.com/cpuguy83/go-md2man/v2 v2.0.7 // indirect | ||
| @@ -56,19 +63,24 @@ require ( | |||||
| 56 | github.com/docker/docker-credential-helpers v0.9.9 // indirect | 63 | github.com/docker/docker-credential-helpers v0.9.9 // indirect | ||
| 57 | github.com/docker/go-connections v0.8.1 // indirect | 64 | github.com/docker/go-connections v0.8.1 // indirect | ||
| 58 | github.com/docker/go-units v0.5.0 // indirect | 65 | github.com/docker/go-units v0.5.0 // indirect | ||
| 66 | + | github.com/fatih/color v1.18.0 // indirect | |||
| 59 | github.com/fatih/structs v1.1.0 // indirect | 67 | github.com/fatih/structs v1.1.0 // indirect | ||
| 60 | github.com/felixge/httpsnoop v1.1.0 // indirect | 68 | github.com/felixge/httpsnoop v1.1.0 // indirect | ||
| 61 | github.com/fsnotify/fsnotify v1.9.0 // indirect | 69 | github.com/fsnotify/fsnotify v1.9.0 // indirect | ||
| 62 | github.com/go-logr/logr v1.4.4 // indirect | 70 | github.com/go-logr/logr v1.4.4 // indirect | ||
| 63 | github.com/go-logr/stdr v1.2.2 // indirect | 71 | github.com/go-logr/stdr v1.2.2 // indirect | ||
| 64 | github.com/go-viper/mapstructure/v2 v2.5.0 // indirect | 72 | github.com/go-viper/mapstructure/v2 v2.5.0 // indirect | ||
| 73 | + | github.com/gobwas/glob v0.2.3 // indirect | |||
| 65 | github.com/gofrs/flock v0.13.1 // indirect | 74 | github.com/gofrs/flock v0.13.1 // indirect | ||
| 75 | + | github.com/gohugoio/hashstructure v0.6.0 // indirect | |||
| 76 | + | github.com/gohugoio/hugo v0.164.0 // indirect | |||
| 66 | github.com/google/go-containerregistry v0.22.1 // indirect | 77 | github.com/google/go-containerregistry v0.22.1 // indirect | ||
| 67 | github.com/google/uuid v1.6.0 // indirect | 78 | github.com/google/uuid v1.6.0 // indirect | ||
| 68 | github.com/huandu/xstrings v1.5.0 // indirect | 79 | github.com/huandu/xstrings v1.5.0 // indirect | ||
| 69 | github.com/inconshreveable/mousetrap v1.1.0 // indirect | 80 | github.com/inconshreveable/mousetrap v1.1.0 // indirect | ||
| 70 | github.com/jdx/go-netrc v1.0.0 // indirect | 81 | github.com/jdx/go-netrc v1.0.0 // indirect | ||
| 71 | github.com/jedib0t/go-pretty/v6 v6.7.8 // indirect | 82 | github.com/jedib0t/go-pretty/v6 v6.7.8 // indirect | ||
| 83 | + | github.com/joho/godotenv v1.5.1 // indirect | |||
| 72 | github.com/klauspost/compress v1.20.0 // indirect | 84 | github.com/klauspost/compress v1.20.0 // indirect | ||
| 73 | github.com/klauspost/pgzip v1.2.6 // indirect | 85 | github.com/klauspost/pgzip v1.2.6 // indirect | ||
| 74 | github.com/knadh/koanf/maps v0.1.2 // indirect | 86 | github.com/knadh/koanf/maps v0.1.2 // indirect | ||
| @@ -88,6 +100,8 @@ require ( | |||||
| 88 | github.com/moby/moby/client v0.6.0 // indirect | 100 | github.com/moby/moby/client v0.6.0 // indirect | ||
| 89 | github.com/opencontainers/go-digest v1.0.0 // indirect | 101 | github.com/opencontainers/go-digest v1.0.0 // indirect | ||
| 90 | github.com/opencontainers/image-spec v1.1.1 // indirect | 102 | github.com/opencontainers/image-spec v1.1.1 // indirect | ||
| 103 | + | github.com/pelletier/go-toml v1.9.5 // indirect | |||
| 104 | + | github.com/pelletier/go-toml/v2 v2.4.3 // indirect | |||
| 91 | github.com/petermattis/goid v0.0.0-20260820044319-269ab09b5261 // indirect | 105 | github.com/petermattis/goid v0.0.0-20260820044319-269ab09b5261 // indirect | ||
| 92 | github.com/quic-go/qpack v0.6.0 // indirect | 106 | github.com/quic-go/qpack v0.6.0 // indirect | ||
| 93 | github.com/quic-go/quic-go v0.62.0 // indirect | 107 | github.com/quic-go/quic-go v0.62.0 // indirect | ||
| @@ -98,8 +112,11 @@ require ( | |||||
| 98 | github.com/segmentio/asm v1.2.1 // indirect | 112 | github.com/segmentio/asm v1.2.1 // indirect | ||
| 99 | github.com/segmentio/encoding v0.5.4 // indirect | 113 | github.com/segmentio/encoding v0.5.4 // indirect | ||
| 100 | github.com/sirupsen/logrus v1.10.2 // indirect | 114 | github.com/sirupsen/logrus v1.10.2 // indirect | ||
| 115 | + | github.com/spf13/afero v1.15.0 // indirect | |||
| 116 | + | github.com/spf13/cast v1.10.0 // indirect | |||
| 101 | github.com/spf13/pflag v1.0.10 // indirect | 117 | github.com/spf13/pflag v1.0.10 // indirect | ||
| 102 | github.com/stretchr/objx v0.5.3 // indirect | 118 | github.com/stretchr/objx v0.5.3 // indirect | ||
| 119 | + | github.com/tdewolff/parse/v2 v2.8.12 // indirect | |||
| 103 | github.com/tetratelabs/wazero v1.12.0 // indirect | 120 | github.com/tetratelabs/wazero v1.12.0 // indirect | ||
| 104 | github.com/tidwall/btree v1.8.1 // indirect | 121 | github.com/tidwall/btree v1.8.1 // indirect | ||
| 105 | github.com/vektra/mockery/v3 v3.8.0 // indirect | 122 | github.com/vektra/mockery/v3 v3.8.0 // indirect | ||