builder-web/bin/migrations/builder_migrations.ml
Reynir Björnsson bde3baec46 Refactor migrations and don't enable foreign keys
Each migration is, for the most part, a module that exposes expected
database version numbers, command identifier and documentation. This
results in all information about the migration and rollback are
found in the module itself, and builder_migrations.ml only has to
reference the module.

Some migrations require foreign keys constraints are disabled. It is not
possible to enable or disable foreign key constraints inside a
transaction.
2021-06-10 12:08:14 +02:00

123 lines
4.1 KiB
OCaml

open Rresult.R.Infix
type action = Fpath.t -> Caqti_blocking.connection ->
(unit, [ Caqti_error.call_or_retrieve | `Wrong_version of int32 * int64 | `Msg of string ]) result
module type MIGRATION = sig
val new_version : int64
val old_version : int64
val identifier : string
val migrate_doc : string
val rollback_doc : string
val migrate : action
val rollback : action
end
let pp_error ppf = function
| #Caqti_error.load_or_connect | #Caqti_error.call_or_retrieve as e ->
Caqti_error.pp ppf e
| `Wrong_version (application_id, user_version) ->
Format.fprintf ppf "wrong version { application_id: %ld, user_version: %Ld }"
application_id user_version
| `Msg m ->
Format.fprintf ppf "%s" m
let or_die exit_code = function
| Ok r -> r
| Error e ->
Format.eprintf "Database error: %a" pp_error e;
exit exit_code
let do_database_action action () datadir =
let datadir = Fpath.v datadir in
let dbpath = Fpath.(datadir / "builder.sqlite3") in
Logs.debug (fun m -> m "Connecting to database...");
let ((module Db : Caqti_blocking.CONNECTION) as conn) =
Caqti_blocking.connect
(Uri.make ~scheme:"sqlite3" ~path:(Fpath.to_string dbpath) ~query:["create", ["false"]] ())
|> or_die 1
in
Logs.debug (fun m -> m "Connected!");
let r =
Db.start () >>= fun () ->
Logs.debug (fun m -> m "Started database transaction");
match action datadir conn with
| Ok () ->
Logs.debug (fun m -> m "Committing database transaction");
Db.commit ()
| Error _ as e ->
Logs.debug (fun m -> m "Rolling back database transaction");
Db.rollback () >>= fun () ->
e
in
or_die 2 r
let help man_format migrations = function
| None -> `Help (man_format, None)
| Some migration ->
if List.mem migration migrations
then `Help (man_format, Some migration)
else `Error (true, "Unknown migration: " ^ migration)
let datadir =
let doc = "data directory containing builder.sqlite3 and data files" in
Cmdliner.Arg.(value &
opt dir "/var/db/builder-web/" &
info ~doc ["datadir"])
let setup_log =
let setup_log level =
Logs.set_level level;
Logs.set_reporter (Logs_fmt.reporter ~dst:Format.std_formatter ());
in
Cmdliner.Term.(const setup_log $ Logs_cli.level ())
let actions (module M : MIGRATION) =
let c s = s ^ "-" ^ M.identifier in
let v doc from_ver to_ver = Printf.sprintf "%s (DB version %Ld -> %Ld)" doc from_ver to_ver in
[
(Cmdliner.Term.(const do_database_action $ const M.migrate $ setup_log $ datadir),
Cmdliner.Term.info ~doc:(v M.migrate_doc M.old_version M.new_version)
(c "migrate"));
(Cmdliner.Term.(const do_database_action $ const M.rollback $ setup_log $ datadir),
Cmdliner.Term.info ~doc:(v M.rollback_doc M.new_version M.old_version)
(c "rollback"));
]
let f20210308 =
let doc = "Remove broken builds as fixed in commit a57798f4c02eb4d528b90932ec26fb0b718f1a13. \
Note that the files on disk have to be removed manually." in
Cmdliner.Term.(const do_database_action $ const M20210308.fixup $ setup_log $ datadir),
Cmdliner.Term.info ~doc "fixup-2021-03-08"
let help_cmd =
let topic =
let doc = "Migration to get help on" in
Cmdliner.Arg.(value & pos 0 (some string) None & info ~doc ~docv:"MIGRATION" [])
in
let doc = "Builder migration help" in
Cmdliner.Term.(ret (const help $ man_format $ choice_names $ topic)),
Cmdliner.Term.info ~doc "help"
let default_cmd =
let doc = "Builder migration command" in
Cmdliner.Term.(ret (const help $ man_format $ choice_names $ const None)),
Cmdliner.Term.info ~doc "builder-migrations"
let () =
Cmdliner.Term.eval_choice
default_cmd
(List.concat [
[ help_cmd ];
actions (module M20210126);
actions (module M20210202);
actions (module M20210216);
actions (module M20210218);
[ f20210308 ];
actions (module M20210427);
actions (module M20210531);
actions (module M20210602);
actions (module M20210608);
actions (module M20210609);
])
|> Cmdliner.Term.exit