feat: db-tool: update hook realization

This commit is contained in:
2026-06-06 18:17:16 +00:00
parent 2856ca1d98
commit 3299daf061
6 changed files with 62 additions and 82 deletions
+26 -3
View File
@@ -115,19 +115,42 @@ in {
hectic = let hectic = let
versionString = lib.fileContents ./hook/sql/HECTIC_VERSION; versionString = lib.fileContents ./hook/sql/HECTIC_VERSION;
static = path: { inherit path; sql = builtins.readFile path; }; static = path: { inherit path; sql = builtins.readFile path; };
templated = path: { templated = path: let
sql = builtins.replaceStrings sql = builtins.replaceStrings
[ "@HECTIC_VERSION@" ] [ "@HECTIC_VERSION@" ]
[ versionString ] [ versionString ]
(builtins.readFile path); (builtins.readFile path);
};
in { in {
inherit sql;
path = builtins.toFile (builtins.baseNameOf (toString path)) sql;
};
in rec {
inherit versionString; inherit versionString;
version = templated ./hook/sql/hectic-version.sql; version = templated ./hook/sql/hectic-version.sql;
secret = static ./hook/sql/hectic-secret.sql; secret = static ./hook/sql/hectic-secret.sql;
migration = static ./hook/sql/hectic-migration.sql; migration = static ./hook/sql/hectic-migration.sql;
inheritance = static ./hook/sql/hectic-inheritance.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`. # Back-compat alias. Prefer `self.lib.hectic.inheritance`.
+13 -18
View File
@@ -7,17 +7,12 @@
# #
# Idempotent: each SQL file uses IF NOT EXISTS / CREATE OR REPLACE. # 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: # Usage:
# apply_hectic_bundle <PGURL> [<DOTENV_CONTENT>] # apply_hectic_bundle <PGURL> [<DOTENV_CONTENT>]
# #
# If DOTENV_CONTENT is non-empty, it is loaded into hectic.secret via # If DOTENV_CONTENT is non-empty, it is loaded into hectic.secret via
# hectic.load_secrets_from_env() after the bundle is applied. # hectic.load_secrets_from_env() after the bundle is applied.
# SQL file paths are substituted by Nix evaluation time.
apply_hectic_bundle() { apply_hectic_bundle() {
pgurl="${1:-}" pgurl="${1:-}"
@@ -28,22 +23,22 @@ apply_hectic_bundle() {
return 3 return 3
fi fi
for var in HECTIC_VERSION_SQL HECTIC_SECRET_SQL HECTIC_MIGRATION_SQL HECTIC_INHERITANCE_SQL; do set -- \
eval "val=\${$var:-}" "@HECTIC_VERSION_SQL@" \
if [ -z "$val" ]; then "@HECTIC_SECRET_SQL@" \
printf '%s\n' "apply-hectic-bundle: $var not set" >&2 "@HECTIC_MIGRATION_SQL@" \
return 3 "@HECTIC_INHERITANCE_SQL@"
fi
if [ ! -r "$val" ]; then for sql_path do
printf '%s\n' "apply-hectic-bundle: $var not readable: $val" >&2 if [ ! -r "$sql_path" ]; then
printf '%s\n' "apply-hectic-bundle: SQL file not readable: $sql_path" >&2
return 1 return 1
fi fi
done done
psql "$pgurl" -v ON_ERROR_STOP=1 -f "$HECTIC_VERSION_SQL" || return 1 for sql_path do
psql "$pgurl" -v ON_ERROR_STOP=1 -f "$HECTIC_SECRET_SQL" || return 1 psql "$pgurl" -v ON_ERROR_STOP=1 -f "$sql_path" || return 1
psql "$pgurl" -v ON_ERROR_STOP=1 -f "$HECTIC_MIGRATION_SQL" || return 1 done
psql "$pgurl" -v ON_ERROR_STOP=1 -f "$HECTIC_INHERITANCE_SQL" || return 1
if [ -n "$env_content" ]; then if [ -n "$env_content" ]; then
# Dollar-quote with $ps_env$ tag to preserve all content verbatim. # Dollar-quote with $ps_env$ tag to preserve all content verbatim.
+18 -21
View File
@@ -42,23 +42,26 @@ that already matches.
```nix ```nix
self.lib.hectic = { self.lib.hectic = {
versionString; # e.g. "0.1.0" versionString; # e.g. "0.1.0"
version = { sql; }; # templated version = { sql; path; }; # templated
secret = { sql; path; }; secret = { sql; path; };
migration = { sql; path; }; migration = { sql; path; };
inheritance = { 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 `.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 file to pass to `psql -f`. `version.path` is generated at Nix
materialized version of `version.sql` must do evaluation time from the templated SQL; the other `*.path` entries point at the
`pkgs.runCommand "hectic-version.sql" { text = self.lib.hectic.version.sql; passAsFile = ["text"]; } ''cp "$textPath" "$out"''`). verbatim source files in the store.
## Shell helper (`apply-hectic-bundle.sh`) ## Shell helper (`apply-hectic-bundle.sh`)
`lib/hook/apply-hectic-bundle.sh` is a dash-compatible helper sourced by both `lib/hook/apply-hectic-bundle.sh` is a dash-compatible helper template.
`migrator` and `db-tool`. Public entry point: `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 ```sh
apply_hectic_bundle <PGURL> [<DOTENV_CONTENT>] apply_hectic_bundle <PGURL> [<DOTENV_CONTENT>]
@@ -70,25 +73,19 @@ apply_hectic_bundle <PGURL> [<DOTENV_CONTENT>]
dollar-quoted (`$ps_env$`) string so secret values cannot terminate the dollar-quoted (`$ps_env$`) string so secret values cannot terminate the
literal. literal.
Required environment (paths to the SQL files): 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.
- `HECTIC_VERSION_SQL` External consumers that do not want to source the helper can still invoke
- `HECTIC_SECRET_SQL` `psql -f` against `self.lib.hectic.bundleFiles` or the individual
- `HECTIC_MIGRATION_SQL` `self.lib.hectic.*.path` entries directly.
- `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.
## Adding a new SQL file ## Adding a new SQL file
1. Add `lib/hook/sql/hectic-<name>.sql`. 1. Add `lib/hook/sql/hectic-<name>.sql`.
2. Wire it into `lib/default.nix` under `lib.hectic.<name>`. 2. Wire it into `lib/default.nix` under `lib.hectic.<name>`.
3. Inject `HECTIC_<NAME>_SQL` in both `package/migrator/default.nix` and 3. Add its `.path` to `lib.hectic.bundleFiles` in the correct order.
`package/db-tool/default.nix`. 4. Add a matching placeholder/replacement in `lib.hectic.applyBundleScript` and
4. Append a `psql -f "$HECTIC_<NAME>_SQL"` step to update `lib/hook/apply-hectic-bundle.sh` to apply the file.
`lib/hook/apply-hectic-bundle.sh` in the correct order.
5. Bump `HECTIC_VERSION` if the new content changes existing semantics. 5. Bump `HECTIC_VERSION` if the new content changes existing semantics.
6. Update tests in `test/package/migrator/test/postgresql/init-hectic-bundle/` 6. Update tests in `test/package/migrator/test/postgresql/init-hectic-bundle/`
and `test/package/db-tool/test/postgresql/hydrate-hook/`. and `test/package/db-tool/test/postgresql/hydrate-hook/`.
+1 -3
View File
@@ -210,15 +210,13 @@ directly against the paths exposed by `self.lib.hectic.*.path`:
```nix ```nix
services.postgresql.initialScript = pkgs.writeText "hectic-init.sql" '' services.postgresql.initialScript = pkgs.writeText "hectic-init.sql" ''
\i ${self.lib.hectic.version.path}
\i ${self.lib.hectic.secret.path} \i ${self.lib.hectic.secret.path}
\i ${self.lib.hectic.migration.path} \i ${self.lib.hectic.migration.path}
\i ${self.lib.hectic.inheritance.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 ## Exit Codes
| Code | Meaning | | Code | Meaning |
+2 -21
View File
@@ -1,30 +1,12 @@
{ dash, hectic, postgresql_17, neovim, openssh, coreutils, gawk, lib, runCommand, self }: { dash, hectic, postgresql_17, neovim, openssh, coreutils, gawk, lib, runCommand, self }:
let let
shell = "${dash}/bin/dash"; shell = "${dash}/bin/dash";
hecticInheritanceSqlPath = ../../lib/hook/sql/hectic-inheritance.sql;
hecticInheritance = runCommand "hectic-inheritance" { } '' hecticInheritance = runCommand "hectic-inheritance" { } ''
mkdir -p "$out/share/hectic" 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 applyBundle = self.lib.hectic.applyBundleScript;
# 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;
mkDatabase = mkDatabase =
{ postgresql ? postgresql_17 }: { postgresql ? postgresql_17 }:
@@ -44,7 +26,6 @@ let
${builtins.readFile hectic.helpers.posix-shell.quote} ${builtins.readFile hectic.helpers.posix-shell.quote}
${builtins.readFile hectic.helpers.posix-shell.pager_or_cat} ${builtins.readFile hectic.helpers.posix-shell.pager_or_cat}
${builtins.readFile hectic.helpers.posix-shell.with_closed_fds} ${builtins.readFile hectic.helpers.posix-shell.with_closed_fds}
${hecticEnv}
${applyBundle} ${applyBundle}
${builtins.readFile ./database.sh} ${builtins.readFile ./database.sh}
''; '';
+2 -16
View File
@@ -1,4 +1,4 @@
{ dash, hectic, sqlite, postgresql_17, gawk, runCommand, self }: { dash, hectic, sqlite, postgresql_17, gawk, self }:
let let
shell = "${dash}/bin/dash"; shell = "${dash}/bin/dash";
bashOptions = [ bashOptions = [
@@ -6,20 +6,7 @@ let
"nounset" "nounset"
]; ];
hecticVersionSqlFile = runCommand "hectic-version.sql" { applyBundle = self.lib.hectic.applyBundleScript;
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;
migrator = hectic.writeShellApplication { migrator = hectic.writeShellApplication {
inherit shell bashOptions; inherit shell bashOptions;
@@ -28,7 +15,6 @@ let
text = '' text = ''
${builtins.readFile hectic.helpers.posix-shell.log} ${builtins.readFile hectic.helpers.posix-shell.log}
${hecticEnv}
${applyBundle} ${applyBundle}
${builtins.readFile ./migrator.sh} ${builtins.readFile ./migrator.sh}
''; '';