Seed from go-template-repo, renamed to simplexcalc

The template's files at a77fd30, without its history or LICENSE, after
script/rename simplexcalc.

Model: opus-5-5
This commit is contained in:
clawbot
2026-09-26 21:38:57 +00:00
parent e336f46e34
commit f8ce8cef83
79 changed files with 7131 additions and 0 deletions
+199
View File
@@ -0,0 +1,199 @@
package database
import (
"context"
"database/sql"
"errors"
"fmt"
"io/fs"
"log/slog"
"path"
"sort"
"strconv"
"strings"
)
// bootstrapVersion is 000.sql: the migration that creates the ledger
// the others are recorded in.
const bootstrapVersion = 0
// errBadMigrationName is returned for a schema file whose name does not
// start with a version number. It is a build-time mistake, not a
// runtime condition, and it fails startup rather than being skipped —
// a migration silently not applied is the failure mode this whole
// mechanism exists to prevent.
var errBadMigrationName = errors.New(
"migration filename does not start with a version number",
)
// ParseMigrationVersion extracts the leading integer from a migration
// filename: "001_widgets.sql" is version 1. Exported so that a project
// seeded from this template can validate its own schema directory in a
// test.
func ParseMigrationVersion(name string) (int, error) {
base := name
if i := strings.IndexAny(base, "_."); i > 0 {
base = base[:i]
}
version, err := strconv.Atoi(base)
if err != nil {
return 0, fmt.Errorf("%w: %q", errBadMigrationName, name)
}
return version, nil
}
// migrationSet is one embedded directory of numbered .sql migrations
// (000 bootstrap plus schema files).
type migrationSet struct {
fsys fs.FS
dir string
}
// collect returns the set's migration filenames sorted
// lexicographically, which is why they are zero-padded.
func (m migrationSet) collect() ([]string, error) {
entries, err := fs.ReadDir(m.fsys, m.dir)
if err != nil {
return nil, fmt.Errorf("failed to read schema directory: %w", err)
}
var migrations []string
for _, entry := range entries {
if !entry.IsDir() && strings.HasSuffix(entry.Name(), ".sql") {
migrations = append(migrations, entry.Name())
}
}
sort.Strings(migrations)
return migrations, nil
}
// bootstrap ensures the schema_migrations table exists by applying
// 000.sql if the table is missing.
func (m migrationSet) bootstrap(
ctx context.Context, db *sql.DB, log *slog.Logger,
) error {
var tableExists int
err := db.QueryRowContext(ctx,
"SELECT COUNT(*) FROM sqlite_master WHERE type='table' AND name='schema_migrations'",
).Scan(&tableExists)
if err != nil {
return fmt.Errorf("failed to check for migrations table: %w", err)
}
if tableExists > 0 {
return nil
}
content, err := fs.ReadFile(m.fsys, path.Join(m.dir, "000.sql"))
if err != nil {
return fmt.Errorf("failed to read bootstrap migration 000.sql: %w", err)
}
if log != nil {
log.Info("applying bootstrap migration", "version", bootstrapVersion)
}
_, err = db.ExecContext(ctx, string(content))
if err != nil {
return fmt.Errorf("failed to apply bootstrap migration: %w", err)
}
return nil
}
// applied reports whether the numbered migration has been recorded.
func (m migrationSet) applied(
ctx context.Context, db *sql.DB, version int,
) (bool, error) {
var count int
err := db.QueryRowContext(ctx,
"SELECT COUNT(*) FROM schema_migrations WHERE version = ?",
version,
).Scan(&count)
if err != nil {
return false, fmt.Errorf("failed to check migration status: %w", err)
}
return count > 0, nil
}
// applyOne reads, executes, and records one migration file.
func (m migrationSet) applyOne(
ctx context.Context, db *sql.DB, migration string, version int,
) error {
content, err := fs.ReadFile(m.fsys, path.Join(m.dir, migration))
if err != nil {
return fmt.Errorf("failed to read migration %s: %w", migration, err)
}
_, execErr := db.ExecContext(ctx, string(content))
if execErr != nil {
return fmt.Errorf("failed to apply migration %s: %w", migration, execErr)
}
_, recErr := db.ExecContext(ctx,
"INSERT INTO schema_migrations (version) VALUES (?)",
version,
)
if recErr != nil {
return fmt.Errorf("failed to record migration %s: %w", migration, recErr)
}
return nil
}
// apply runs all pending migrations of the set, in order. Idempotent:
// a second run over the same database applies nothing.
func (m migrationSet) apply(ctx context.Context, db *sql.DB, log *slog.Logger) error {
err := m.bootstrap(ctx, db, log)
if err != nil {
return err
}
migrations, err := m.collect()
if err != nil {
return err
}
for _, migration := range migrations {
version, parseErr := ParseMigrationVersion(migration)
if parseErr != nil {
return parseErr
}
done, checkErr := m.applied(ctx, db, version)
if checkErr != nil {
return checkErr
}
if done {
if log != nil {
log.Debug("migration already applied", "version", version)
}
continue
}
if log != nil {
log.Info("applying migration", "version", version)
}
applyErr := m.applyOne(ctx, db, migration, version)
if applyErr != nil {
return applyErr
}
if log != nil {
log.Info("migration applied successfully", "version", version)
}
}
return nil
}