From 4616bd64ddb49d659ddb517ea5aadf2b98698e21 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 6 Feb 2026 01:29:19 +0000 Subject: [PATCH] Add embedded PostgreSQL support via --embedded-postgres flag Co-authored-by: unknwon <2946214+unknwon@users.noreply.github.com> --- cmd/gogs/web.go | 17 ++++++ go.mod | 4 +- go.sum | 6 ++ internal/embeddedpg/embeddedpg.go | 93 +++++++++++++++++++++++++++++++ 4 files changed, 119 insertions(+), 1 deletion(-) create mode 100644 internal/embeddedpg/embeddedpg.go diff --git a/cmd/gogs/web.go b/cmd/gogs/web.go index 558d3c1f3..4744e2e80 100644 --- a/cmd/gogs/web.go +++ b/cmd/gogs/web.go @@ -30,6 +30,7 @@ import ( "gogs.io/gogs/internal/conf" "gogs.io/gogs/internal/context" "gogs.io/gogs/internal/database" + "gogs.io/gogs/internal/embeddedpg" "gogs.io/gogs/internal/form" "gogs.io/gogs/internal/osutil" "gogs.io/gogs/internal/route" @@ -54,6 +55,7 @@ and it takes care of all the other things for you`, Flags: []cli.Flag{ stringFlag("port, p", "3000", "Temporary port number to prevent conflict"), stringFlag("config, c", "", "Custom configuration file path"), + boolFlag("embedded-postgres", "Use embedded PostgreSQL database"), }, } @@ -160,6 +162,21 @@ func newMacaron() *macaron.Macaron { } func runWeb(c *cli.Context) error { + var localPg *embeddedpg.LocalPostgres + + if c.Bool("embedded-postgres") { + localPg = embeddedpg.Initialize(conf.WorkDir()) + if err := localPg.Launch(); err != nil { + log.Fatal("Failed to launch embedded postgres: %v", err) + } + defer func() { + if err := localPg.Shutdown(); err != nil { + log.Error("Failed to shutdown embedded postgres: %v", err) + } + }() + localPg.ConfigureGlobalDatabase() + } + err := route.GlobalInit(c.String("config")) if err != nil { log.Fatal("Failed to initialize application: %v", err) diff --git a/go.mod b/go.mod index 925577c88..aa04d9f48 100644 --- a/go.mod +++ b/go.mod @@ -83,6 +83,7 @@ require ( github.com/djherbis/nio/v3 v3.0.1 // indirect github.com/dustin/go-humanize v1.0.1 // indirect github.com/fatih/color v1.18.0 // indirect + github.com/fergusstrange/embedded-postgres v1.33.0 // indirect github.com/getsentry/sentry-go v0.27.0 // indirect github.com/go-asn1-ber/asn1-ber v1.5.8-0.20250403174932-29230038a667 // indirect github.com/go-logr/logr v1.2.3 // indirect @@ -107,7 +108,7 @@ require ( github.com/klauspost/compress v1.18.0 // indirect github.com/kr/pretty v0.3.1 // indirect github.com/kr/text v0.2.0 // indirect - github.com/lib/pq v1.10.2 // indirect + github.com/lib/pq v1.10.9 // indirect github.com/mattn/go-colorable v0.1.14 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-runewidth v0.0.19 // indirect @@ -131,6 +132,7 @@ require ( github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/saintfish/chardet v0.0.0-20120816061221-3af4cd4741ca // indirect github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf // indirect + github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 // indirect go.bobheadxi.dev/streamline v1.2.1 // indirect go.opentelemetry.io/otel v1.11.0 // indirect go.opentelemetry.io/otel/trace v1.11.0 // indirect diff --git a/go.sum b/go.sum index 1398200bd..89db1e797 100644 --- a/go.sum +++ b/go.sum @@ -91,6 +91,8 @@ github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaB github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= +github.com/fergusstrange/embedded-postgres v1.33.0 h1:ka8vmRpm4IDsES7NPXQ/NThAp1fc/f+crcXYjCW7wK0= +github.com/fergusstrange/embedded-postgres v1.33.0/go.mod h1:w0YvnCgf19o6tskInrOOACtnqfVlOvluz3hlNLY7tRk= github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE= github.com/frankban/quicktest v1.14.3/go.mod h1:mgiwOwqx65TmIk1wJ6Q7wvnVMocbUorkibMOrVTHZps= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= @@ -290,6 +292,8 @@ github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.10.2 h1:AqzbZs4ZoCBp+GtejcpCpcxM3zlSMx29dXbUSeVtJb8= github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= +github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= +github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/lunny/log v0.0.0-20160921050905-7887c61bf0de/go.mod h1:3q8WtuPQsoRbatJuy3nvq/hRSvuBJrHHr+ybPPiNvHQ= github.com/lunny/nodb v0.0.0-20160621015157-fc1ef06ad4af/go.mod h1:Cqz6pqow14VObJ7peltM+2n3PWOz7yTrfUuGbVFkzN0= github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= @@ -456,6 +460,8 @@ github.com/unknwon/paginater v0.0.0-20170405233947-45e5d631308e h1:Qf3QQl/zmEbWD github.com/unknwon/paginater v0.0.0-20170405233947-45e5d631308e/go.mod h1:TBwoao3Q4Eb/cp+dHbXDfRTrZSsj/k7kLr2j1oWRWC0= github.com/urfave/cli v1.22.17 h1:SYzXoiPfQjHBbkYxbew5prZHS1TOLT3ierW8SYLqtVQ= github.com/urfave/cli v1.22.17/go.mod h1:b0ht0aqgH/6pBYzzxURyrM4xXNgsoT/n2ZzwQiEhNVo= +github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 h1:nIPpBwaJSVYIxUFsDv3M8ofmx9yWTog9BfvIu0q41lo= +github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8/go.mod h1:HUYIGzjTL3rfEspMxjDjgmT5uz5wzYJKVo23qUhYTos= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/ziutek/mymysql v1.5.4 h1:GB0qdRGsTwQSBVYuVShFBKaXSnSnYYC2d9knnE1LHFs= diff --git a/internal/embeddedpg/embeddedpg.go b/internal/embeddedpg/embeddedpg.go new file mode 100644 index 000000000..a5b0de075 --- /dev/null +++ b/internal/embeddedpg/embeddedpg.go @@ -0,0 +1,93 @@ +package embeddedpg + +import ( + "fmt" + "os" + "path/filepath" + "time" + + "github.com/cockroachdb/errors" + embpg "github.com/fergusstrange/embedded-postgres" + log "unknwon.dev/clog/v2" + + "gogs.io/gogs/internal/conf" +) + +// LocalPostgres wraps embedded PostgreSQL server functionality. +type LocalPostgres struct { + srv *embpg.EmbeddedPostgres + baseDir string + tcpPort uint32 + database string + user string + pass string +} + +// Initialize creates a LocalPostgres with default settings based on workDir. +func Initialize(workDir string) *LocalPostgres { + storageBase := filepath.Join(workDir, "data", "local-postgres") + return &LocalPostgres{ + baseDir: storageBase, + tcpPort: 15432, + database: "gogs", + user: "gogs", + pass: "gogs", + } +} + +// Launch starts the embedded PostgreSQL server and blocks until ready. +func (pg *LocalPostgres) Launch() error { + log.Info("Launching local PostgreSQL server...") + + if err := os.MkdirAll(pg.baseDir, 0700); err != nil { + return errors.Wrap(err, "mkdir local postgres base") + } + + opts := embpg.DefaultConfig(). + Username(pg.user). + Password(pg.pass). + Database(pg.database). + Port(pg.tcpPort). + DataPath(pg.baseDir). + RuntimePath(filepath.Join(pg.baseDir, "rt")). + BinariesPath(filepath.Join(pg.baseDir, "bin")). + CachePath(filepath.Join(pg.baseDir, "dl")). + StartTimeout(45 * time.Second). + Logger(os.Stderr) + + pg.srv = embpg.NewDatabase(opts) + + if err := pg.srv.Start(); err != nil { + return errors.Wrap(err, "launch embedded pg") + } + + log.Info("Local PostgreSQL ready on port %d", pg.tcpPort) + return nil +} + +// Shutdown stops the embedded PostgreSQL server gracefully. +func (pg *LocalPostgres) Shutdown() error { + if pg.srv == nil { + return nil + } + + log.Info("Shutting down local PostgreSQL...") + if err := pg.srv.Stop(); err != nil { + return errors.Wrap(err, "shutdown embedded pg") + } + + log.Info("Local PostgreSQL shutdown complete") + return nil +} + +// ConfigureGlobalDatabase modifies global conf.Database to point to this instance. +func (pg *LocalPostgres) ConfigureGlobalDatabase() { + conf.Database.Type = "postgres" + conf.Database.Host = fmt.Sprintf("localhost:%d", pg.tcpPort) + conf.Database.Name = pg.database + conf.Database.User = pg.user + conf.Database.Password = pg.pass + conf.Database.SSLMode = "disable" + + log.Trace("Global database configured for local PostgreSQL") +}