fix(deploynotify): read commit SHA from Coolify SOURCE_COMMIT runtime env

Baking SOURCE_COMMIT as a Docker build arg never worked: Coolify exposes it
as a runtime env var, not a build arg, so gitSHA was always empty and the owner
DM was skipped. Read SOURCE_COMMIT from the container env at startup (falling
back to the ldflags-baked gitSHA for local builds), forward it via the compose
environment, and drop the dead build-arg baking.
This commit is contained in:
2026-06-28 23:08:17 +07:00
parent 1fa549fa18
commit 0abd33cb42
6 changed files with 55 additions and 22 deletions
+3 -2
View File
@@ -23,8 +23,9 @@ ADMIN_IDS=
# Only the twentyq module needs this. Leave blank to disable that command.
GEMINI_API_KEY=
# SOURCE_COMMIT is injected as a build arg by Coolify (it sets it automatically)
# for the deploynotify owner DM. Leave unset for local `docker compose up` —
# SOURCE_COMMIT is injected into the container at runtime by Coolify (a
# predefined env var holding the commit SHA); read at startup for the
# deploynotify owner DM. Leave unset for local `docker compose up` —
# deploynotify just stays silent.
# SOURCE_COMMIT=
+4 -8
View File
@@ -6,15 +6,11 @@ RUN go mod download
COPY . .
# SOURCE_COMMIT is baked into the binary so internal/deploynotify can DM the
# owner once per new version (parity with the Makefile build). Coolify exposes
# the commit SHA as the SOURCE_COMMIT build arg automatically — no manual
# wiring needed. For a manual build, pass it with
# --build-arg SOURCE_COMMIT=$(git rev-parse --short HEAD)
# When unset, deploynotify treats the empty SHA as "stay silent".
ARG SOURCE_COMMIT=""
# The deploy-notify commit SHA comes from the SOURCE_COMMIT runtime env that
# Coolify injects into the container (see docker-compose.yml), not from a build
# arg — Coolify does not pass build args here. The binary is built plain.
RUN CGO_ENABLED=0 GOOS=linux go build \
-ldflags="-s -w -X main.gitSHA=${SOURCE_COMMIT}" \
-ldflags="-s -w" \
-o /out/server \
./cmd/server
+17 -4
View File
@@ -36,11 +36,22 @@ import (
"github.com/tiennm99/miti99bot/internal/telegram"
)
// gitSHA is populated at build time via `-ldflags "-X main.gitSHA=<sha>"`
// (see Makefile). Empty value means the binary was built without that flag —
// deploynotify treats it as a signal to stay silent.
// gitSHA is the local-build fallback, populated via `-ldflags "-X
// main.gitSHA=<sha>"` (see Makefile). On Coolify the commit comes from the
// SOURCE_COMMIT runtime env instead (resolveCommitSHA prefers it). Empty from
// both sources means deploynotify stays silent.
var gitSHA string
// resolveCommitSHA returns the commit identifier for the deploy notification.
// Coolify injects SOURCE_COMMIT into the container environment at runtime, so
// prefer it; fall back to the ldflags-baked gitSHA for local builds.
func resolveCommitSHA(envSourceCommit string) string {
if s := strings.TrimSpace(envSourceCommit); s != "" {
return s
}
return gitSHA
}
// factories is the static module catalog. Adding a new module is a one-line
// change here. Lives in main rather than the modules package to avoid an
// import cycle (modules → util → modules).
@@ -157,7 +168,7 @@ func main() {
Bot: b,
Store: deploynotify.NewStore(provider.Collection("deploynotify")),
OwnerID: cfg.BotOwnerID,
GitSHA: gitSHA,
GitSHA: resolveCommitSHA(cfg.SourceCommit),
})
handler := server.New()
@@ -260,6 +271,7 @@ func buildProvider(ctx context.Context, cfg config) (storage.Provider, func(), e
type config struct {
Port string
TelegramBotToken string
SourceCommit string // Coolify-injected commit SHA (runtime env) for deploynotify
GeminiAPIKey string
GoldPriceAPIURL string
GoldFXAPIURL string
@@ -299,6 +311,7 @@ func loadConfig() config {
return config{
Port: port,
TelegramBotToken: envMap["TELEGRAM_BOT_TOKEN"],
SourceCommit: envMap["SOURCE_COMMIT"],
GeminiAPIKey: envMap["GEMINI_API_KEY"],
GoldPriceAPIURL: envMap["GOLD_PRICE_API_URL"],
GoldFXAPIURL: envMap["GOLD_FX_API_URL"],
+22
View File
@@ -7,6 +7,28 @@ import (
"github.com/tiennm99/miti99bot/internal/storage"
)
func TestResolveCommitSHA(t *testing.T) {
prev := gitSHA
defer func() { gitSHA = prev }()
// Coolify runtime env wins over the baked fallback.
gitSHA = "baked"
if got := resolveCommitSHA(" runtime-sha "); got != "runtime-sha" {
t.Errorf("env present: got %q, want trimmed runtime-sha", got)
}
// Empty env falls back to the ldflags-baked value.
if got := resolveCommitSHA(""); got != "baked" {
t.Errorf("env empty: got %q, want baked fallback", got)
}
// Neither source set → empty (deploynotify stays silent).
gitSHA = ""
if got := resolveCommitSHA(" "); got != "" {
t.Errorf("both empty: got %q, want empty", got)
}
}
func TestFactoriesIncludesGoldAndCoin(t *testing.T) {
catalog := factories()
if catalog["gold"] == nil {
+4 -4
View File
@@ -2,10 +2,6 @@ services:
bot:
build:
context: .
args:
# Coolify sets SOURCE_COMMIT automatically; passed so deploynotify DMs
# the owner on each new version. Optional — empty SHA just stays silent.
SOURCE_COMMIT: ${SOURCE_COMMIT:-}
# Or pin a prebuilt image instead of building:
# image: ghcr.io/tiennm99/miti99bot:latest
restart: unless-stopped
@@ -20,6 +16,10 @@ services:
ADMIN_IDS: ${ADMIN_IDS} # CSV of admin Telegram user ids
# --- Optional ---
GEMINI_API_KEY: ${GEMINI_API_KEY} # only the twentyq module needs it
# Coolify injects SOURCE_COMMIT (commit SHA) at deploy time; passed to the
# container so deploynotify DMs the owner once per new version. Empty (e.g.
# local `docker compose up` without it) just stays silent.
SOURCE_COMMIT: ${SOURCE_COMMIT:-}
# Storage auto-selects mongodb because MONGO_URL is set — no KV_PROVIDER.
# The in-process cron scheduler runs by default — no CRON_MODE.
# PORT defaults to 8080 (internal health server) — omit unless overriding.
+5 -4
View File
@@ -88,10 +88,11 @@ SSM/AWS lookup that fails with no AWS creds and bricks startup), `KV_PROVIDER`,
bot token; a second poller gets HTTP 409, and a second in-process scheduler
double-fires crons. Prefer **stop-first redeploys** so two containers never
overlap near a cron time.
5. **Build arg for deploynotify:** Coolify sets `SOURCE_COMMIT` automatically
and the compose build forwards it, so the owner gets the "new version" DM
with no manual wiring. Without it, `deploynotify` stays silent (no crash) —
but you lose that notification.
5. **deploynotify commit SHA:** Coolify injects `SOURCE_COMMIT` (a predefined
runtime env var) into the container, and the compose `environment` forwards
it, so the bot reads it at startup and DMs the owner the "new version"
notice — no manual wiring. Without it, `deploynotify` stays silent (no
crash) — but you lose that notification.
6. **Health check:** use Coolify's HTTP monitor against `GET /` (returns
`text/plain` `miti99bot ok`). Do **not** use a compose `healthcheck` — the
distroless image has no shell/curl and `cmd/server` has no `-healthcheck`