From 0d418b6baa96d9cd74bee43ccc9d05159c287f64 Mon Sep 17 00:00:00 2001 From: eric Date: Wed, 4 Mar 2026 05:37:45 +0100 Subject: [PATCH] feat: add release scripts --- .gitignore | 3 +- .pre-commit-config.yaml | 1 - flake.nix | 147 ++++++++++++++++++++++++++++++++++++---- 3 files changed, 135 insertions(+), 16 deletions(-) delete mode 120000 .pre-commit-config.yaml diff --git a/.gitignore b/.gitignore index 1c6f3fa..636b09a 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ -.pre-commit-config.yaml \ No newline at end of file +.pre-commit-config.yaml +.direnv \ No newline at end of file diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml deleted file mode 120000 index ae04f76..0000000 --- a/.pre-commit-config.yaml +++ /dev/null @@ -1 +0,0 @@ -/nix/store/041b74mcfyxy04kjz4pcmra2k3nzh5di-pre-commit-config.json \ No newline at end of file diff --git a/flake.nix b/flake.nix index 95d36e0..fea997d 100644 --- a/flake.nix +++ b/flake.nix @@ -27,6 +27,8 @@ in { lib = { + + # ── mkDevShell ─────────────────────────────────────────────────────── mkDevShell = { system, @@ -43,7 +45,6 @@ # formatterSettings = treefmt-nix settings.formatter attrset # e.g. { shfmt.options = [ "-i" "2" "-s" "-w" ]; } features ? { }, - # features = opt-in lib-managed behaviours # features.oxfmt = true → adds pkgs.oxfmt + pkgs.oxlint, enables oxfmt in treefmt }: let @@ -82,12 +83,6 @@ entry = "${pkgs.gitleaks}/bin/gitleaks protect --staged"; pass_filenames = false; }; - tests = { - enable = true; - entry = "echo 'No tests defined yet.'"; - pass_filenames = false; - stages = [ "pre-push" ]; - }; } // additionalHooks; }; @@ -136,9 +131,126 @@ ''; }; }; + + # ── mkRelease ──────────────────────────────────────────────────────── + mkRelease = + { + system, + readVersion, + # Shell string — must print current version to stdout: "x.y.z" or "x.y.z-channel.N" + # Example: + # readVersion = ''cat "$ROOT_DIR/VERSION"''; + writeVersion, + # Shell string — env vars available: BASE_VERSION, CHANNEL, PRERELEASE_NUM, FULL_VERSION + # Example: + # writeVersion = ''echo "$FULL_VERSION" > "$ROOT_DIR/VERSION"''; + postVersion ? "", + # Shell string — runs after writeVersion and versionFiles, before git add. + # Same env vars available. + versionFiles ? [ ], + # List of { path, template } attrsets. + # template is a Nix function: version -> string + # The content is fully rendered by Nix at eval time — no shell interpolation needed. + # Example: + # versionFiles = [ + # { + # path = "src/version.ts"; + # template = version: ''export const APP_VERSION = "${version}" as const;''; + # } + # { + # path = "internal/version/version.go"; + # template = version: '' + # package version + # + # const Version = "${version}" + # ''; + # } + # ]; + channels ? [ + "alpha" + "beta" + "rc" + "internal" + ], + # Valid release channels beyond "stable". Validated at runtime. + extraRuntimeInputs ? [ ], + # Extra packages available in the release script's PATH. + }: + let + pkgs = import nixpkgs { inherit system; }; + channelList = pkgs.lib.concatStringsSep " " channels; + + # Version files are fully rendered by Nix at eval time. + # The shell only writes the pre-computed strings — no shell interpolation in templates. + versionFilesScript = pkgs.lib.concatMapStrings ( + f: + let + # We can't call f.template here since FULL_VERSION is a runtime value. + # Instead we pass the path and use a shell heredoc with the template + # rendered at runtime via the VERSION env vars. + renderedContent = f.template "$FULL_VERSION"; + in + '' + mkdir -p "$(dirname "${f.path}")" + cat > "${f.path}" << 'NIXEOF' + ${renderedContent} + NIXEOF + log "Generated version file: ${f.path}" + '' + ) versionFiles; + + script = + builtins.replaceStrings + [ + "__CHANNEL_LIST__" + "__VERSION_FILES__" + "__READ_VERSION__" + "__WRITE_VERSION__" + "__POST_VERSION__" + ] + [ + channelList + versionFilesScript + readVersion + writeVersion + postVersion + ] + (builtins.readFile ./packages/release/release.sh); + in + pkgs.writeShellApplication { + name = "release"; + runtimeInputs = + with pkgs; + [ + git + gnugrep + gawk + gnused + coreutils + ] + ++ extraRuntimeInputs; + text = script; + }; + }; - # Dogfood: this repo's own dev shell using the lib above + # ── packages ──────────────────────────────────────────────────────────── + packages = forAllSystems ( + system: + let + pkgs = import nixpkgs { inherit system; }; + in + { + # Expose a no-op release package for the lib repo itself (dogfood) + release = self.lib.mkRelease { + inherit system; + readVersion = ''cat "$ROOT_DIR/VERSION"''; + writeVersion = ''echo "$FULL_VERSION" > "$ROOT_DIR/VERSION"''; + }; + } + ); + + # ── devShells ─────────────────────────────────────────────────────────── devShells = forAllSystems ( system: let @@ -146,15 +258,12 @@ env = self.lib.mkDevShell { inherit system; extraPackages = with pkgs; [ - nixfmt - shfmt + nixfmt-rfc-style gitlint gitleaks + shfmt + self.packages.${system}.release ]; - formatters = { - shfmt.enable = true; - }; - tools = [ { name = "Nix"; @@ -170,6 +279,7 @@ } ); + # ── checks ────────────────────────────────────────────────────────────── checks = forAllSystems ( system: let @@ -180,6 +290,15 @@ } ); + # ── formatter ─────────────────────────────────────────────────────────── formatter = forAllSystems (system: (self.lib.mkDevShell { inherit system; }).formatter); + + # ── templates ─────────────────────────────────────────────────────────── + templates = { + default = { + path = ./template; + description = "Product repo using devshell-lib"; + }; + }; }; }