ficsit-cli-flake/cli/localregistry/migrations.go
mircearoata 3640e5e708
feat: client and server-only mods (#71)
* feat: local registry database migrations

* chore: store raw API responses in local registry

* feat: update ficsit-resolver

* feat: client and server-only mods

* fix: remove mods that no longer support the current target
2024-10-04 19:23:51 +02:00

106 lines
2.6 KiB
Go

package localregistry
import (
"database/sql"
"fmt"
)
var migrations = []func(*sql.Tx) error{
initialSetup,
addRequiredOnRemote,
}
func applyMigrations(db *sql.DB) error {
// user_version will store the 1-indexed migration that was last applied
var nextMigration int
err := db.QueryRow("PRAGMA user_version;").Scan(&nextMigration)
if err != nil {
return fmt.Errorf("failed to get user_version: %w", err)
}
for i := nextMigration; i < len(migrations); i++ {
err := applyMigration(db, i)
if err != nil {
return fmt.Errorf("failed to apply migration %d: %w", i, err)
}
}
return nil
}
func applyMigration(db *sql.DB, migrationIndex int) error {
tx, err := db.Begin()
if err != nil {
return fmt.Errorf("failed to start transaction: %w", err)
}
// Will noop if the transaction was committed
defer tx.Rollback() //nolint:errcheck
err = migrations[migrationIndex](tx)
if err != nil {
return err
}
_, err = tx.Exec(fmt.Sprintf("PRAGMA user_version = %d;", migrationIndex+1))
if err != nil {
return fmt.Errorf("failed to set user_version: %w", err)
}
err = tx.Commit()
if err != nil {
return fmt.Errorf("failed to commit transaction: %w", err)
}
return nil
}
func initialSetup(tx *sql.Tx) error {
// Create the initial user
_, err := tx.Exec(`
CREATE TABLE IF NOT EXISTS "versions" (
"id" TEXT NOT NULL PRIMARY KEY,
"mod_reference" TEXT NOT NULL,
"version" TEXT NOT NULL,
"game_version" TEXT NOT NULL
);
CREATE INDEX IF NOT EXISTS "mod_reference" ON "versions" ("mod_reference");
CREATE UNIQUE INDEX IF NOT EXISTS "mod_version" ON "versions" ("mod_reference", "version");
CREATE TABLE IF NOT EXISTS "dependencies" (
"version_id" TEXT NOT NULL,
"dependency" TEXT NOT NULL,
"condition" TEXT NOT NULL,
"optional" INT NOT NULL,
FOREIGN KEY ("version_id") REFERENCES "versions" ("id") ON DELETE CASCADE,
PRIMARY KEY ("version_id", "dependency")
);
CREATE TABLE IF NOT EXISTS "targets" (
"version_id" TEXT NOT NULL,
"target_name" TEXT NOT NULL,
"link" TEXT NOT NULL,
"hash" TEXT NOT NULL,
"size" INT NOT NULL,
FOREIGN KEY ("version_id") REFERENCES "versions" ("id") ON DELETE CASCADE,
PRIMARY KEY ("version_id", "target_name")
);
`)
if err != nil {
return fmt.Errorf("failed to create initial tables: %w", err)
}
return nil
}
func addRequiredOnRemote(tx *sql.Tx) error {
_, err := tx.Exec(`
ALTER TABLE "versions" ADD COLUMN "required_on_remote" INT NOT NULL DEFAULT 1;
`)
if err != nil {
return fmt.Errorf("failed to add required_on_remote column: %w", err)
}
return nil
}