mirror of
https://github.com/MeshCore-Beacon/beacon-server.git
synced 2026-10-22 13:02:26 +00:00
feat: add auto db migration in binary
This commit is contained in:
5 files changed
+85
-36
No files matched your search
+3
-6
@@ -1,5 +1,5 @@
|
||||
# Copyright 2026 Beacon Contributors
|
||||
# SPDX-License-Identifier: agpl
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
FROM golang:1.26-alpine AS builder
|
||||
WORKDIR /app
|
||||
@@ -9,10 +9,7 @@ COPY . .
|
||||
RUN go build -o beacon ./cmd/beacon
|
||||
|
||||
FROM alpine:3.19
|
||||
RUN apk add --no-cache ca-certificates tzdata postgresql-client
|
||||
RUN apk add --no-cache ca-certificates tzdata
|
||||
WORKDIR /app
|
||||
COPY --from=builder /app/beacon .
|
||||
COPY db/migrations/ ./migrations/
|
||||
COPY .build/docker-entrypoint.sh .
|
||||
RUN chmod +x docker-entrypoint.sh
|
||||
ENTRYPOINT ["./docker-entrypoint.sh"]
|
||||
CMD ["./beacon"]
|
||||
@@ -1,30 +0,0 @@
|
||||
#!/bin/sh
|
||||
# Copyright 2026 Beacon Contributors
|
||||
# SPDX-License-Identifier: agpl
|
||||
|
||||
set -e
|
||||
|
||||
if [ -n "$POSTGRES_DSN" ]; then
|
||||
echo "Running database migrations..."
|
||||
|
||||
psql "$POSTGRES_DSN" -c "CREATE TABLE IF NOT EXISTS schema_migrations (
|
||||
filename TEXT PRIMARY KEY,
|
||||
applied_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);"
|
||||
|
||||
for f in /app/migrations/*.sql; do
|
||||
name=$(basename "$f")
|
||||
already=$(psql "$POSTGRES_DSN" -tAc "SELECT 1 FROM schema_migrations WHERE filename = '$name'")
|
||||
if [ "$already" != "1" ]; then
|
||||
echo " Applying $name..."
|
||||
psql "$POSTGRES_DSN" -f "$f"
|
||||
psql "$POSTGRES_DSN" -c "INSERT INTO schema_migrations (filename) VALUES ('$name');"
|
||||
else
|
||||
echo " Skipping $name (already applied)"
|
||||
fi
|
||||
done
|
||||
|
||||
echo "Migrations complete."
|
||||
fi
|
||||
|
||||
exec ./beacon "$@"
|
||||
@@ -114,6 +114,10 @@ func main() {
|
||||
}
|
||||
defer pool.Close()
|
||||
|
||||
if err := db.RunMigrations(ctx, pool); err != nil {
|
||||
log.Fatalf("migrations failed: %v", err)
|
||||
}
|
||||
|
||||
store := db.New(pool)
|
||||
|
||||
// ── Redis cache layer ────────────────────────────────────────────────────
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
// Copyright 2026 Beacon Contributors
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
package db
|
||||
|
||||
import (
|
||||
"context"
|
||||
"embed"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/jackc/pgx/v5/pgxpool"
|
||||
)
|
||||
|
||||
//go:embed migrations/*.sql
|
||||
var migrationFiles embed.FS
|
||||
|
||||
func RunMigrations(ctx context.Context, pool *pgxpool.Pool) error {
|
||||
_, err := pool.Exec(ctx, `
|
||||
CREATE TABLE IF NOT EXISTS schema_migrations (
|
||||
filename TEXT PRIMARY KEY,
|
||||
applied_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
)
|
||||
`)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create schema_migrations: %w", err)
|
||||
}
|
||||
|
||||
entries, err := fs.ReadDir(migrationFiles, "migrations")
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to read migrations: %w", err)
|
||||
}
|
||||
|
||||
sort.Slice(entries, func(i, j int) bool {
|
||||
return entries[i].Name() < entries[j].Name()
|
||||
})
|
||||
|
||||
for _, entry := range entries {
|
||||
if !strings.HasSuffix(entry.Name(), ".sql") {
|
||||
continue
|
||||
}
|
||||
var already bool
|
||||
err := pool.QueryRow(
|
||||
ctx,
|
||||
"SELECT EXISTS(SELECT 1 FROM schema_migrations WHERE filename = $1)",
|
||||
entry.Name(),
|
||||
).Scan(&already)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to check migration %s: %w", entry.Name(), err)
|
||||
}
|
||||
if already {
|
||||
continue
|
||||
}
|
||||
|
||||
sql, err := migrationFiles.ReadFile("migrations/" + entry.Name())
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to read migration %s: %w", entry.Name(), err)
|
||||
}
|
||||
|
||||
if _, err := pool.Exec(ctx, string(sql)); err != nil {
|
||||
return fmt.Errorf("failed to apply migration %s: %w", entry.Name(), err)
|
||||
}
|
||||
|
||||
if _, err := pool.Exec(
|
||||
ctx,
|
||||
"INSERT INTO schema_migrations (filename) VALUES ($1)",
|
||||
entry.Name(),
|
||||
); err != nil {
|
||||
return fmt.Errorf("failed to record migration %s: %w", entry.Name(), err)
|
||||
}
|
||||
|
||||
fmt.Printf("applied migration: %s\n", entry.Name())
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
File renamed without changes.
Reference in new issue
Block a user