diff --git a/lib/default.nix b/lib/default.nix index ab7a5a0d..afeaba58 100644 --- a/lib/default.nix +++ b/lib/default.nix @@ -115,19 +115,42 @@ in { hectic = let versionString = lib.fileContents ./hook/sql/HECTIC_VERSION; static = path: { inherit path; sql = builtins.readFile path; }; - templated = path: { + templated = path: let sql = builtins.replaceStrings [ "@HECTIC_VERSION@" ] [ versionString ] (builtins.readFile path); + in { + inherit sql; + path = builtins.toFile (builtins.baseNameOf (toString path)) sql; }; - in { + in rec { inherit versionString; version = templated ./hook/sql/hectic-version.sql; secret = static ./hook/sql/hectic-secret.sql; migration = static ./hook/sql/hectic-migration.sql; inheritance = static ./hook/sql/hectic-inheritance.sql; - applyBundleScript = ./hook/apply-hectic-bundle.sh; + bundleFiles = [ + version.path + secret.path + migration.path + inheritance.path + ]; + applyBundleScript = + builtins.replaceStrings + [ + "@HECTIC_VERSION_SQL@" + "@HECTIC_SECRET_SQL@" + "@HECTIC_MIGRATION_SQL@" + "@HECTIC_INHERITANCE_SQL@" + ] + [ + "${version.path}" + "${secret.path}" + "${migration.path}" + "${inheritance.path}" + ] + (builtins.readFile ./hook/apply-hectic-bundle.sh); }; # Back-compat alias. Prefer `self.lib.hectic.inheritance`. diff --git a/lib/hook/apply-hectic-bundle.sh b/lib/hook/apply-hectic-bundle.sh index 0d3c1e1d..3e1d5c05 100644 --- a/lib/hook/apply-hectic-bundle.sh +++ b/lib/hook/apply-hectic-bundle.sh @@ -7,17 +7,12 @@ # # Idempotent: each SQL file uses IF NOT EXISTS / CREATE OR REPLACE. # -# Required env (caller injects from Nix): -# HECTIC_VERSION_SQL - path to hectic-version.sql (substituted) -# HECTIC_SECRET_SQL - path to hectic-secret.sql -# HECTIC_MIGRATION_SQL - path to hectic-migration.sql -# HECTIC_INHERITANCE_SQL - path to hectic-inheritance.sql -# # Usage: # apply_hectic_bundle [] # # If DOTENV_CONTENT is non-empty, it is loaded into hectic.secret via # hectic.load_secrets_from_env() after the bundle is applied. +# SQL file paths are substituted by Nix evaluation time. apply_hectic_bundle() { pgurl="${1:-}" @@ -28,22 +23,22 @@ apply_hectic_bundle() { return 3 fi - for var in HECTIC_VERSION_SQL HECTIC_SECRET_SQL HECTIC_MIGRATION_SQL HECTIC_INHERITANCE_SQL; do - eval "val=\${$var:-}" - if [ -z "$val" ]; then - printf '%s\n' "apply-hectic-bundle: $var not set" >&2 - return 3 - fi - if [ ! -r "$val" ]; then - printf '%s\n' "apply-hectic-bundle: $var not readable: $val" >&2 + set -- \ + "@HECTIC_VERSION_SQL@" \ + "@HECTIC_SECRET_SQL@" \ + "@HECTIC_MIGRATION_SQL@" \ + "@HECTIC_INHERITANCE_SQL@" + + for sql_path do + if [ ! -r "$sql_path" ]; then + printf '%s\n' "apply-hectic-bundle: SQL file not readable: $sql_path" >&2 return 1 fi done - psql "$pgurl" -v ON_ERROR_STOP=1 -f "$HECTIC_VERSION_SQL" || return 1 - psql "$pgurl" -v ON_ERROR_STOP=1 -f "$HECTIC_SECRET_SQL" || return 1 - psql "$pgurl" -v ON_ERROR_STOP=1 -f "$HECTIC_MIGRATION_SQL" || return 1 - psql "$pgurl" -v ON_ERROR_STOP=1 -f "$HECTIC_INHERITANCE_SQL" || return 1 + for sql_path do + psql "$pgurl" -v ON_ERROR_STOP=1 -f "$sql_path" || return 1 + done if [ -n "$env_content" ]; then # Dollar-quote with $ps_env$ tag to preserve all content verbatim. diff --git a/lib/hook/sql/README.md b/lib/hook/sql/README.md index 229ca943..9a52f0f7 100644 --- a/lib/hook/sql/README.md +++ b/lib/hook/sql/README.md @@ -42,23 +42,26 @@ that already matches. ```nix self.lib.hectic = { versionString; # e.g. "0.1.0" - version = { sql; }; # templated + version = { sql; path; }; # templated secret = { sql; path; }; migration = { sql; path; }; inheritance = { sql; path; }; - applyBundleScript; # ./hook/apply-hectic-bundle.sh + bundleFiles; # ordered bundle file paths + applyBundleScript; # generated helper shell source with paths embedded }; ``` `.sql` is the file contents as a string. `.path` is the Nix store path of the -verbatim source (only available on non-templated entries; consumers needing a -materialized version of `version.sql` must do -`pkgs.runCommand "hectic-version.sql" { text = self.lib.hectic.version.sql; passAsFile = ["text"]; } ''cp "$textPath" "$out"''`). +materialized file to pass to `psql -f`. `version.path` is generated at Nix +evaluation time from the templated SQL; the other `*.path` entries point at the +verbatim source files in the store. ## Shell helper (`apply-hectic-bundle.sh`) -`lib/hook/apply-hectic-bundle.sh` is a dash-compatible helper sourced by both -`migrator` and `db-tool`. Public entry point: +`lib/hook/apply-hectic-bundle.sh` is a dash-compatible helper template. +`self.lib.hectic.applyBundleScript` is the generated shell source with concrete +SQL paths embedded at Nix evaluation time. `migrator` and `db-tool` splice that +shell source directly into their generated scripts. Public entry point: ```sh apply_hectic_bundle [] @@ -70,25 +73,19 @@ apply_hectic_bundle [] dollar-quoted (`$ps_env$`) string so secret values cannot terminate the literal. -Required environment (paths to the SQL files): - -- `HECTIC_VERSION_SQL` -- `HECTIC_SECRET_SQL` -- `HECTIC_MIGRATION_SQL` -- `HECTIC_INHERITANCE_SQL` - -`migrator` and `db-tool` set these via Nix at build time. External consumers -typically invoke `psql -f` against the paths directly instead of sourcing the -helper. +The SQL file paths are embedded into the helper at Nix evaluation time, so +callers only need to source the generated script and call the function. +External consumers that do not want to source the helper can still invoke +`psql -f` against `self.lib.hectic.bundleFiles` or the individual +`self.lib.hectic.*.path` entries directly. ## Adding a new SQL file 1. Add `lib/hook/sql/hectic-.sql`. 2. Wire it into `lib/default.nix` under `lib.hectic.`. -3. Inject `HECTIC__SQL` in both `package/migrator/default.nix` and - `package/db-tool/default.nix`. -4. Append a `psql -f "$HECTIC__SQL"` step to - `lib/hook/apply-hectic-bundle.sh` in the correct order. +3. Add its `.path` to `lib.hectic.bundleFiles` in the correct order. +4. Add a matching placeholder/replacement in `lib.hectic.applyBundleScript` and + update `lib/hook/apply-hectic-bundle.sh` to apply the file. 5. Bump `HECTIC_VERSION` if the new content changes existing semantics. 6. Update tests in `test/package/migrator/test/postgresql/init-hectic-bundle/` and `test/package/db-tool/test/postgresql/hydrate-hook/`. diff --git a/package/db-tool/README.md b/package/db-tool/README.md index b9030e84..801f5b8f 100644 --- a/package/db-tool/README.md +++ b/package/db-tool/README.md @@ -210,15 +210,13 @@ directly against the paths exposed by `self.lib.hectic.*.path`: ```nix services.postgresql.initialScript = pkgs.writeText "hectic-init.sql" '' + \i ${self.lib.hectic.version.path} \i ${self.lib.hectic.secret.path} \i ${self.lib.hectic.migration.path} \i ${self.lib.hectic.inheritance.path} ''; ``` -The version file (`self.lib.hectic.version`) is templated and only exposes -`.sql` (a string). Materialize it with `pkgs.writeText` if a path is needed. - ## Exit Codes | Code | Meaning | diff --git a/package/db-tool/default.nix b/package/db-tool/default.nix index a0bdf27c..9be4f4ab 100644 --- a/package/db-tool/default.nix +++ b/package/db-tool/default.nix @@ -1,30 +1,12 @@ { dash, hectic, postgresql_17, neovim, openssh, coreutils, gawk, lib, runCommand, self }: let shell = "${dash}/bin/dash"; - - hecticInheritanceSqlPath = ../../lib/hook/sql/hectic-inheritance.sql; - hecticInheritance = runCommand "hectic-inheritance" { } '' mkdir -p "$out/share/hectic" - cp ${hecticInheritanceSqlPath} "$out/share/hectic/hectic-inheritance.sql" + cp ${self.lib.hectic.inheritance.path} "$out/share/hectic/hectic-inheritance.sql" ''; - # Materialize the templated version SQL into the Nix store as a real file - # so it can be passed by path to psql -f (alongside the static siblings). - hecticVersionSqlFile = pkgs-writeText "hectic-version.sql" self.lib.hectic.version.sql; - pkgs-writeText = name: text: runCommand name { inherit text; passAsFile = [ "text" ]; } '' - cp "$textPath" "$out" - ''; - - hecticEnv = '' - HECTIC_VERSION_SQL=${hecticVersionSqlFile} - HECTIC_SECRET_SQL=${self.lib.hectic.secret.path} - HECTIC_MIGRATION_SQL=${self.lib.hectic.migration.path} - HECTIC_INHERITANCE_SQL=${self.lib.hectic.inheritance.path} - export HECTIC_VERSION_SQL HECTIC_SECRET_SQL HECTIC_MIGRATION_SQL HECTIC_INHERITANCE_SQL - ''; - - applyBundle = builtins.readFile self.lib.hectic.applyBundleScript; + applyBundle = self.lib.hectic.applyBundleScript; mkDatabase = { postgresql ? postgresql_17 }: @@ -44,7 +26,6 @@ let ${builtins.readFile hectic.helpers.posix-shell.quote} ${builtins.readFile hectic.helpers.posix-shell.pager_or_cat} ${builtins.readFile hectic.helpers.posix-shell.with_closed_fds} - ${hecticEnv} ${applyBundle} ${builtins.readFile ./database.sh} ''; diff --git a/package/migrator/default.nix b/package/migrator/default.nix index 9bc15656..1d51942c 100644 --- a/package/migrator/default.nix +++ b/package/migrator/default.nix @@ -1,4 +1,4 @@ -{ dash, hectic, sqlite, postgresql_17, gawk, runCommand, self }: +{ dash, hectic, sqlite, postgresql_17, gawk, self }: let shell = "${dash}/bin/dash"; bashOptions = [ @@ -6,20 +6,7 @@ let "nounset" ]; - hecticVersionSqlFile = runCommand "hectic-version.sql" { - text = self.lib.hectic.version.sql; - passAsFile = [ "text" ]; - } ''cp "$textPath" "$out"''; - - hecticEnv = '' - HECTIC_VERSION_SQL=${hecticVersionSqlFile} - HECTIC_SECRET_SQL=${self.lib.hectic.secret.path} - HECTIC_MIGRATION_SQL=${self.lib.hectic.migration.path} - HECTIC_INHERITANCE_SQL=${self.lib.hectic.inheritance.path} - export HECTIC_VERSION_SQL HECTIC_SECRET_SQL HECTIC_MIGRATION_SQL HECTIC_INHERITANCE_SQL - ''; - - applyBundle = builtins.readFile self.lib.hectic.applyBundleScript; + applyBundle = self.lib.hectic.applyBundleScript; migrator = hectic.writeShellApplication { inherit shell bashOptions; @@ -28,7 +15,6 @@ let text = '' ${builtins.readFile hectic.helpers.posix-shell.log} - ${hecticEnv} ${applyBundle} ${builtins.readFile ./migrator.sh} '';