From 51d9682cf50dd9d154a1d7d19f27fd2a580d460d Mon Sep 17 00:00:00 2001 From: bo0tzz Date: Thu, 20 Nov 2025 21:43:09 +0100 Subject: [PATCH 1/2] add cigogne support --- README.md | 3 ++- priv/cigogne.toml | 2 ++ ...se_tables.sql => 20250825210434-add_m25_base_tables.sql} | 6 ++++++ 3 files changed, 10 insertions(+), 1 deletion(-) create mode 100644 priv/cigogne.toml rename priv/migrations/{20250825210434_add_m25_base_tables.sql => 20250825210434-add_m25_base_tables.sql} (95%) diff --git a/README.md b/README.md index 8149221..7b955e0 100644 --- a/README.md +++ b/README.md @@ -99,7 +99,8 @@ The required SQL statements will be printed to the console for use with other mi tools like [`dbmate`](https://github.com/amacneil/dbmate). Alternatively, you can apply the migrations directly using the `--apply` flag. -Support for [Cigogne](https://github.com/Billuc/cigogne) is in the works. +When using [Cigogne](https://github.com/Billuc/cigogne), you can include the migrations +into your project by running `gleam run -m cigogne include --name m25`. ## Development diff --git a/priv/cigogne.toml b/priv/cigogne.toml new file mode 100644 index 0000000..9f5adaa --- /dev/null +++ b/priv/cigogne.toml @@ -0,0 +1,2 @@ +[migrations] +migration_folder = "migrations" diff --git a/priv/migrations/20250825210434_add_m25_base_tables.sql b/priv/migrations/20250825210434-add_m25_base_tables.sql similarity index 95% rename from priv/migrations/20250825210434_add_m25_base_tables.sql rename to priv/migrations/20250825210434-add_m25_base_tables.sql index b4df5a9..f599301 100644 --- a/priv/migrations/20250825210434_add_m25_base_tables.sql +++ b/priv/migrations/20250825210434-add_m25_base_tables.sql @@ -1,3 +1,4 @@ +--- migration:up create schema if not exists m25; create table if not exists m25.job ( @@ -49,3 +50,8 @@ create table if not exists m25.version ( version timestamptz not null primary key, created_at timestamptz not null default now() ); + +--- migration:down +drop schema m25; + +--- migration:end From 36c6ec74fe79ff66080baf73cabc255d081eb257 Mon Sep 17 00:00:00 2001 From: bo0tzz Date: Fri, 21 Nov 2025 12:37:00 +0100 Subject: [PATCH 2/2] use cigogne to get migrations --- gleam.toml | 1 + manifest.toml | 3 ++ src/m25/internal/cli.gleam | 94 +++++++++++++------------------------- 3 files changed, 37 insertions(+), 61 deletions(-) diff --git a/gleam.toml b/gleam.toml index 03bc54a..59ff748 100644 --- a/gleam.toml +++ b/gleam.toml @@ -24,6 +24,7 @@ simplifile = ">= 2.3.0 and < 3.0.0" gtempo = ">= 7.2.2 and < 8.0.0" glint = ">= 1.2.1 and < 2.0.0" argv = ">= 1.0.2 and < 2.0.0" +cigogne = ">= 5.0.4 and < 6.0.0" [dev-dependencies] gleeunit = ">= 1.0.0 and < 2.0.0" diff --git a/manifest.toml b/manifest.toml index d04c9e1..c1ab052 100644 --- a/manifest.toml +++ b/manifest.toml @@ -4,6 +4,7 @@ packages = [ { name = "argv", version = "1.0.2", build_tools = ["gleam"], requirements = [], otp_app = "argv", source = "hex", outer_checksum = "BA1FF0929525DEBA1CE67256E5ADF77A7CDDFE729E3E3F57A5BDCAA031DED09D" }, { name = "backoff", version = "1.1.6", build_tools = ["rebar3"], requirements = [], otp_app = "backoff", source = "hex", outer_checksum = "CF0CFFF8995FB20562F822E5CC47D8CCF664C5ECDC26A684CBE85C225F9D7C39" }, + { name = "cigogne", version = "5.0.4", build_tools = ["gleam"], requirements = ["argv", "envoy", "gleam_crypto", "gleam_erlang", "gleam_otp", "gleam_stdlib", "gleam_time", "pog", "simplifile", "splitter", "tom"], otp_app = "cigogne", source = "hex", outer_checksum = "2138362840E0773213C5811DD6FB43B59E44F0DA3DF9FF2882223C789207C201" }, { name = "envoy", version = "1.0.2", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "envoy", source = "hex", outer_checksum = "95FD059345AA982E89A0B6E2A3BF1CF43E17A7048DCD85B5B65D3B9E4E39D359" }, { name = "eval", version = "1.0.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "eval", source = "hex", outer_checksum = "264DAF4B49DF807F303CA4A4E4EBC012070429E40BE384C58FE094C4958F9BDA" }, { name = "exception", version = "2.1.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "exception", source = "hex", outer_checksum = "329D269D5C2A314F7364BD2711372B6F2C58FA6F39981572E5CA68624D291F8C" }, @@ -34,6 +35,7 @@ packages = [ { name = "pog", version = "4.1.0", build_tools = ["gleam"], requirements = ["exception", "gleam_erlang", "gleam_otp", "gleam_stdlib", "gleam_time", "pgo"], otp_app = "pog", source = "hex", outer_checksum = "E4AFBA39A5FAA2E77291836C9683ADE882E65A06AB28CA7D61AE7A3AD61EBBD5" }, { name = "simplifile", version = "2.3.0", build_tools = ["gleam"], requirements = ["filepath", "gleam_stdlib"], otp_app = "simplifile", source = "hex", outer_checksum = "0A868DAC6063D9E983477981839810DC2E553285AB4588B87E3E9C96A7FB4CB4" }, { name = "snag", version = "1.1.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "snag", source = "hex", outer_checksum = "7E9F06390040EB5FAB392CE642771484136F2EC103A92AE11BA898C8167E6E17" }, + { name = "splitter", version = "1.2.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "splitter", source = "hex", outer_checksum = "3DFD6B6C49E61EDAF6F7B27A42054A17CFF6CA2135FF553D0CB61C234D281DD0" }, { name = "squirrel", version = "4.2.0", build_tools = ["gleam"], requirements = ["argv", "envoy", "eval", "filepath", "glam", "gleam_community_ansi", "gleam_crypto", "gleam_json", "gleam_regexp", "gleam_stdlib", "gleam_time", "glexer", "justin", "mug", "non_empty_list", "pog", "simplifile", "term_size", "tom", "tote", "youid"], otp_app = "squirrel", source = "hex", outer_checksum = "4FE8428187DF4A9A7E5F918D6AD68856ECC0D773F8F782EF95E03753F65CCB3D" }, { name = "term_size", version = "1.0.1", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "term_size", source = "hex", outer_checksum = "D00BD2BC8FB3EBB7E6AE076F3F1FF2AC9D5ED1805F004D0896C784D06C6645F1" }, { name = "tom", version = "2.0.0", build_tools = ["gleam"], requirements = ["gleam_stdlib", "gleam_time"], otp_app = "tom", source = "hex", outer_checksum = "74D0C5A3761F7A7D06994755D4D5AD854122EF8E9F9F76A3E7547606D8C77091" }, @@ -43,6 +45,7 @@ packages = [ [requirements] argv = { version = ">= 1.0.2 and < 2.0.0" } +cigogne = { version = ">= 5.0.4 and < 6.0.0" } filepath = { version = ">= 1.1.2 and < 2.0.0" } gleam_erlang = { version = ">= 1.0.0 and < 2.0.0" } gleam_json = { version = ">= 3.0.1 and < 4.0.0" } diff --git a/src/m25/internal/cli.gleam b/src/m25/internal/cli.gleam index f17f004..8a377eb 100644 --- a/src/m25/internal/cli.gleam +++ b/src/m25/internal/cli.gleam @@ -1,8 +1,8 @@ import argv -import filepath +import cigogne +import cigogne/config import gleam/bool import gleam/dynamic/decode -import gleam/erlang/application import gleam/erlang/process import gleam/io import gleam/list @@ -15,10 +15,7 @@ import gleam/time/calendar import gleam/time/timestamp import glint import pog -import simplifile import snag -import tempo -import tempo/datetime fn add_error_context(error: String, context: String) { context <> ": " <> error @@ -77,74 +74,49 @@ fn get_current_version( } } -fn get_migrations() -> Result(List(String), String) { - use priv <- result.try( - application.priv_directory("m25") - |> result.replace_error("Couldn't get priv directory"), - ) - - let assert Ok(directory) = - [priv, "migrations"] - |> list.reduce(filepath.join) - - simplifile.get_files(directory) - |> result.map_error(fn(err) { - err - |> string.inspect - |> add_error_context("Unable to get files in priv directory") - }) -} - -fn version_from_filename(filename: String) -> timestamp.Timestamp { - let assert Ok(filename) = filename |> filepath.split |> list.last - let assert Ok(version_string) = filename |> string.split("_") |> list.first - // gtempo requires an offset to be specified, so add one manually - let assert Ok(version) = - datetime.parse(version_string <> "Z", tempo.Custom("YYYYMMDDHHmmssZ")) - datetime.to_timestamp(version) -} - fn get_migrations_to_apply( current_version: option.Option(timestamp.Timestamp), ) -> Result(option.Option(String), String) { - use files <- result.try( - get_migrations() - |> result.map_error(add_error_context( - _, - "Failed to get migrations for m25 tables", - )), + use config <- result.try( + config.get("m25") + |> result.map_error(fn(err) { + err + |> string.inspect + |> add_error_context("Failed to get migrations configuration") + }), + ) + + use engine <- result.try( + cigogne.create_engine(config) + |> result.map_error(fn(err) { + err + |> string.inspect + |> add_error_context("Failed to create migrations engine") + }), ) - let files_to_apply = case current_version { - option.None -> files + let migrations = cigogne.get_all_migrations(engine) + + let migrations_to_apply = case current_version { + option.None -> migrations option.Some(current_version) -> { - files - |> list.filter(fn(file) { - timestamp.compare(version_from_filename(file), current_version) - == order.Gt + migrations + |> list.filter(fn(migration) { + timestamp.compare(migration.timestamp, current_version) == order.Gt }) } } // Check if there are files to apply - use <- bool.guard( - case files_to_apply { - [] -> True - _ -> False - }, - Ok(option.None), - ) - use migration_sql <- result.try( - files_to_apply - |> list.try_map(simplifile.read) - |> result.map(string.join(_, "\n")) - |> result.map_error(fn(err) { - add_error_context(string.inspect(err), "Failed to read migrations") - }), - ) + use <- bool.guard(list.is_empty(migrations_to_apply), Ok(option.None)) + + let migration_sql = + migrations_to_apply + |> list.flat_map(fn(m) { m.queries_up }) + |> string.join("\n") - let assert Ok(latest_migration) = list.last(files_to_apply) - let new_version = version_from_filename(latest_migration) + let assert Ok(latest_migration) = list.last(migrations_to_apply) + let new_version = latest_migration.timestamp let new_version_sql = "insert into m25.version (version) values (timestamptz '" <> {