feat(conga,dev): worker and scheduler reports, summer dev runs serve
- queue:work and schedule:run print a startup summary and a shutdown report on a terminal, log them as records otherwise, and return nil after a clean stop - schedule:run --once ends with 'Ran N due entries (M failed)' - summer dev runs bin/<app> serve with the forwarded global options, reports through Output, stops the old server before starting the new one and prints a dev report on Ctrl+C (exit 0) - lagoon.DatabaseLabel describes a DSN without its secrets - Long help and examples for queue:work, schedule:run, dev, docs:serve
This commit is contained in:
@@ -202,7 +202,7 @@ The `serve` command also opens the database through [lagoon](../lagoon/README.md
|
||||
## Dependencies
|
||||
|
||||
- SummerCMS modules: [backpack](../backpack/README.md), [bonfire](../bonfire/README.md), [bouncer](../bouncer/README.md), [cabana](../cabana/README.md), [compass](../compass/README.md), [conga](../conga/README.md) (the in-process job worker of `serve`), [cooler](../cooler/README.md) (the shared rate-limit store `serve` installs), [diary](../diary/README.md) (the `http` and `serve` component loggers), [lagoon](../lagoon/README.md) (including `lagoon/attach`), [pact](../pact/README.md), [party](../party/README.md), [towel](../towel/README.md), [wire](../wire/README.md).
|
||||
- Third-party: `gocloud.dev/blob` (uploads bucket opened by `serve`), `github.com/goccy/go-yaml` (api-docs spec conversion), `github.com/jackc/pgx/v5/pgconn` (the database host and name in the startup summary).
|
||||
- Third-party: `gocloud.dev/blob` (uploads bucket opened by `serve`), `github.com/goccy/go-yaml` (api-docs spec conversion).
|
||||
- Standard library: `bytes`, `context`, `crypto/rand`, `embed`, `encoding/base64`, `encoding/json`, `errors`, `fmt`, `html/template`, `log/slog`, `math`, `mime`, `net`, `net/http`, `net/netip`, `net/url`, `os`, `os/signal`, `path/filepath`, `regexp`, `sort`, `strconv`, `strings`, `sync`, `sync/atomic`, `syscall`, `time`, `unicode/utf8`.
|
||||
- Tests additionally use `github.com/testcontainers/testcontainers-go` (Redis and Postgres containers).
|
||||
|
||||
|
||||
@@ -155,11 +155,11 @@ func TestServeSummarySmoke(t *testing.T) {
|
||||
t.Fatalf("summary leaks %q:\n%s\n%s", secret, got, logs.String())
|
||||
}
|
||||
}
|
||||
if databaseFact("host=db port=5432 dbname=acme user=u password=p") != "db:5432/acme" {
|
||||
t.Errorf("keyword DSN = %q", databaseFact("host=db port=5432 dbname=acme user=u password=p"))
|
||||
if lagoon.DatabaseLabel("host=db port=5432 dbname=acme user=u password=p") != "db:5432/acme" {
|
||||
t.Errorf("keyword DSN = %q", lagoon.DatabaseLabel("host=db port=5432 dbname=acme user=u password=p"))
|
||||
}
|
||||
if databaseFact("postgres://u:p@[::1:bad") != "configured" {
|
||||
t.Errorf("unparsable DSN = %q, want configured", databaseFact("postgres://u:p@[::1:bad"))
|
||||
if lagoon.DatabaseLabel("postgres://u:p@[::1:bad") != "configured" {
|
||||
t.Errorf("unparsable DSN = %q, want configured", lagoon.DatabaseLabel("postgres://u:p@[::1:bad"))
|
||||
}
|
||||
|
||||
probe := []bonfire.Command{{
|
||||
|
||||
@@ -18,7 +18,6 @@ import (
|
||||
"git.golem15.com/golem15/summercms/modules/diary"
|
||||
"git.golem15.com/golem15/summercms/modules/lagoon"
|
||||
"git.golem15.com/golem15/summercms/modules/party"
|
||||
"github.com/jackc/pgx/v5/pgconn"
|
||||
)
|
||||
|
||||
// serveLog is the logger of serve's startup summary and shutdown report.
|
||||
@@ -108,7 +107,7 @@ func buildFacts(app *backpack.App, in factInputs) summaryFacts {
|
||||
Environment: "unknown",
|
||||
Addr: in.addr,
|
||||
URL: serveURL(in.addr),
|
||||
Database: databaseFact(configString(app, "database.dsn")),
|
||||
Database: lagoon.DatabaseLabel(configString(app, "database.dsn")),
|
||||
Pending: len(in.pending),
|
||||
MigrateCmd: in.bin + " migrate",
|
||||
Mail: driverFact(app, "mail.driver", "memory"),
|
||||
@@ -155,31 +154,6 @@ func driverFact(app *backpack.App, key, def string) string {
|
||||
return def
|
||||
}
|
||||
|
||||
// databaseFact is host:port/name parsed from dsn with pgconn.ParseConfig,
|
||||
// never the user or password. A DSN that does not parse is "configured",
|
||||
// an empty one "not configured".
|
||||
func databaseFact(dsn string) string {
|
||||
dsn = strings.TrimSpace(dsn)
|
||||
if dsn == "" {
|
||||
return "not configured"
|
||||
}
|
||||
cfg, err := pgconn.ParseConfig(dsn)
|
||||
if err != nil || cfg.Host == "" {
|
||||
return "configured"
|
||||
}
|
||||
host := cfg.Host
|
||||
if strings.HasPrefix(host, "/") {
|
||||
host = "socket " + host
|
||||
} else if strings.Contains(host, ":") {
|
||||
host = "[" + host + "]"
|
||||
}
|
||||
fact := host + ":" + strconv.Itoa(int(cfg.Port))
|
||||
if cfg.Database != "" {
|
||||
fact += "/" + cfg.Database
|
||||
}
|
||||
return fact
|
||||
}
|
||||
|
||||
// migrations is the migration line: "up to date", "N pending — run ..." or
|
||||
// "unknown".
|
||||
func (f summaryFacts) migrations() (string, bool) {
|
||||
|
||||
Reference in New Issue
Block a user