Adopt golib/pg for migrations and pool construction
forgebot applied its schema by executing an inline DDL string on every boot, with no version tracking and no lock, so two API replicas starting together raced and the SQL had nowhere to grow. golib/pg already owns that mechanic for the estate; take it and keep owning the SQL. - Move the schema into migrations/0001_init.sql, embedded via migrations.FS. The DDL is verbatim. - The four legacy-status UPDATEs move into 0001 unchanged. Each reads only retired statuses (pending/failed/running/succeeded/cancelled) and writes only current ones, and no current status is a source, so replaying 0001 once against the live database is a no-op. A test pins that property. - Build the pool with pg.NewMigrated, LockName "forgebot-migrations": the advisory lock serializes replicas, schema_migrations records what ran, and a migration failure fails startup instead of half-migrating. database.New and apiserver.New take a context and logger for it. - Render the DSN with pg.DSN, which percent-escapes the credentials the fmt.Sprintf builder pasted in raw. LoadConfig still reads the environment itself: pg.DSNFromEnv has no defaults for user and database name, where forgebot defaults both to "forgebot", and would newly honour DATABASE_URL and PG*. The deployed DBHOST/DBPORT/DBUSER/DBPASS/DBNAME/DBSSL contract and its defaults are unchanged, and pinned by a test. - Plumb GOPRIVATE=git.unkin.net for the first cross-repo Go dependency: exported by the Makefile, set in both Dockerfiles and the woodpecker Go steps, documented in the README. - gofmt the four files that were already unformatted on main, so the pre-commit step can pass.
This commit is contained in:
@@ -0,0 +1,34 @@
|
||||
CREATE TABLE IF NOT EXISTS tasks (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
parent_task_id UUID REFERENCES tasks(id),
|
||||
command TEXT NOT NULL,
|
||||
skill TEXT NOT NULL DEFAULT '',
|
||||
repository TEXT NOT NULL,
|
||||
ref TEXT NOT NULL,
|
||||
issue_number INTEGER NOT NULL DEFAULT 0,
|
||||
pr_number INTEGER NOT NULL DEFAULT 0,
|
||||
comment_id BIGINT NOT NULL DEFAULT 0,
|
||||
body TEXT NOT NULL DEFAULT '',
|
||||
author TEXT NOT NULL,
|
||||
extra_tools TEXT[] NOT NULL DEFAULT '{}',
|
||||
status TEXT NOT NULL DEFAULT 'todo',
|
||||
pool_ref TEXT NOT NULL DEFAULT '',
|
||||
job_name TEXT NOT NULL DEFAULT '',
|
||||
result TEXT NOT NULL DEFAULT '',
|
||||
error_message TEXT NOT NULL DEFAULT '',
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
started_at TIMESTAMPTZ,
|
||||
completed_at TIMESTAMPTZ
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_tasks_status ON tasks(status);
|
||||
CREATE INDEX IF NOT EXISTS idx_tasks_repository ON tasks(repository);
|
||||
CREATE INDEX IF NOT EXISTS idx_tasks_parent ON tasks(parent_task_id);
|
||||
|
||||
-- Rename the pre-kanban statuses onto the current set. Each rewrite reads only
|
||||
-- retired values and writes only current ones, so it is a no-op on its second
|
||||
-- and later runs and on a fresh database.
|
||||
UPDATE tasks SET status = 'todo' WHERE status IN ('pending', 'failed');
|
||||
UPDATE tasks SET status = 'in_progress' WHERE status = 'running';
|
||||
UPDATE tasks SET status = 'done' WHERE status = 'succeeded';
|
||||
UPDATE tasks SET status = 'wontdo' WHERE status = 'cancelled';
|
||||
@@ -0,0 +1,11 @@
|
||||
// Package migrations embeds the SQL schema files so forgebot-api carries its
|
||||
// own schema and applies it at startup, with no externally mirrored copy to
|
||||
// drift out of sync.
|
||||
package migrations
|
||||
|
||||
import "embed"
|
||||
|
||||
// FS holds every numbered migration; lexical filename order is version order.
|
||||
//
|
||||
//go:embed *.sql
|
||||
var FS embed.FS
|
||||
Reference in New Issue
Block a user