From 9700dafbd435583211f8de76527d305e011ae6d1 Mon Sep 17 00:00:00 2001 From: Bart Trojanowski Date: Sun, 17 May 2026 11:20:55 -0400 Subject: [PATCH 01/12] changes for nix build --- CMakeLists.txt | 19 +++++-- flake.nix | 139 ++++++++++++++++++++++++++------------------- src/CMakeLists.txt | 17 +++++- 3 files changed, 109 insertions(+), 66 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index def2761..378c70a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -14,13 +14,20 @@ option(WIP_STATIC "Build a fully static binary" OFF) include(FetchContent) include(CheckCXXSourceCompiles) -FetchContent_Declare( - spdlog - GIT_REPOSITORY https://github.com/gabime/spdlog.git - GIT_TAG v1.15.3 -) +# use spdlog from system if possible +find_package(spdlog 1.15 QUIET CONFIG) -FetchContent_MakeAvailable(spdlog) +if(spdlog_FOUND) + message(STATUS "Using system spdlog ${spdlog_VERSION}") +else() + + FetchContent_Declare( + spdlog + GIT_REPOSITORY https://github.com/gabime/spdlog.git + GIT_TAG v1.15.3 + ) + FetchContent_MakeAvailable(spdlog) +endif() # Code coverage options — applied AFTER FetchContent so that third-party # dependencies (spdlog, fmt, …) are NOT instrumented. Mixing gcov versions diff --git a/flake.nix b/flake.nix index 123e95d..d545353 100644 --- a/flake.nix +++ b/flake.nix @@ -1,70 +1,93 @@ { - description = "git-wip — Work In Progress branch manager"; + description = "git-wip — Work In Progress branch manager"; - inputs = { - nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; - flake-utils.url = "github:numtide/flake-utils"; - }; + inputs = { + nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; + flake-utils.url = "github:numtide/flake-utils"; + }; - outputs = { self, nixpkgs, flake-utils }: - flake-utils.lib.eachDefaultSystem (system: - let - pkgs = nixpkgs.legacyPackages.${system}; - in - { - devShells.default = pkgs.mkShell { - name = "git-wip-dev"; + outputs = { self, nixpkgs, flake-utils }: + flake-utils.lib.eachDefaultSystem (system: + let + pkgs = nixpkgs.legacyPackages.${system}; + in + { + packages.default = pkgs.stdenv.mkDerivation { + pname = "git-wip"; + version = "unstable-${self.shortRev or self.dirtyShortRev or "dirty"}"; - # Build tools and dependencies - # Nix name ↔ apt name - # cmake ↔ cmake - # ninja ↔ ninja-build - # pkg-config ↔ pkg-config - # gnumake ↔ make - # gcc / stdenv ↔ gcc / g++ - # clang-tools ↔ clangd - # clang ↔ clang - # libgit2 ↔ libgit2-dev - # gtest ↔ googletest / libgmock-dev / libgtest-dev - # git ↔ git - packages = with pkgs; [ - # build system - cmake - ninja - pkg-config - gnumake + src = self; - # compilers - gcc - clang - clang-tools # provides clangd + nativeBuildInputs = with pkgs; [ + cmake + ninja + pkg-config + git + ]; - # runtime library (required at link time) - libgit2 + buildInputs = with pkgs; [ + libgit2 + libgit2.dev + spdlog + openssl + openssl.dev + pcre2 + libssh2 + zlib + ]; - # test framework - gtest + PKG_CONFIG_PATH = with pkgs; lib.makeSearchPath "lib/pkgconfig" [ + openssl.dev libgit2 pcre2 libssh2 zlib + ]; - # version control (needed by cmake FetchContent and tests) - git +# Better phase for patching shebangs + postPatch = '' + patchShebangs cmake/GitVersion.sh + ''; - # python is used by test/runner.py - python3 - ]; +# Pre-generate version header + preConfigure = '' + echo "=== Generating git-wip version header for Nix build ===" + mkdir -p build + ./cmake/GitVersion.sh GIT_WIP_ build/git_wip_version.h + ''; - # Ensure pkg-config can find libgit2 - PKG_CONFIG_PATH = "${pkgs.libgit2}/lib/pkgconfig"; + cmakeFlags = [ + "-DCMAKE_BUILD_TYPE=Release" + "-DBUILD_TESTING=OFF" + "-DUSE_GIT_WIP_VERSION_H=${placeholder "source"}/build/git_wip_version.h" + ]; - shellHook = '' - echo "git-wip dev shell" - echo " compiler: $(c++ --version | head -1)" - echo " cmake: $(cmake --version | head -1)" - echo " libgit2: $(pkg-config --modversion libgit2)" - echo "" - echo " build: make" - echo " test: make test" - ''; - }; - } - ); + buildPhase = '' + make -j$NIX_BUILD_CORES + ''; + + installPhase = '' + make install PREFIX=$out + ''; + + meta = with pkgs.lib; { + description = "git-wip — Work In Progress branch manager"; + homepage = "https://github.com/bartman/git-wip"; + license = licenses.gpl2Only; + platforms = platforms.linux ++ platforms.darwin; + }; + }; + + devShells.default = pkgs.mkShell { + name = "git-wip-dev"; + packages = with pkgs; [ + cmake ninja pkg-config gnumake + gcc clang clang-tools + libgit2 gtest git python3 + ]; + PKG_CONFIG_PATH = "${pkgs.libgit2}/lib/pkgconfig"; + shellHook = '' + echo "git-wip dev shell ready" + echo " build: make" + echo " test: make test" + echo " install (local): make install" + ''; + }; + }); } diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 23efe7c..1153d9a 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,6 +1,16 @@ # Generate version header -include(${CMAKE_SOURCE_DIR}/cmake/GitVersion.cmake) -gitversion_generate(PREFIX GIT_WIP_ OUTPUT ${CMAKE_BINARY_DIR}/git_wip_version.h) +set(VERSION_HEADER "") + +if(DEFINED USE_GIT_WIP_VERSION_H AND EXISTS "${USE_GIT_WIP_VERSION_H}") + set(VERSION_HEADER "${USE_GIT_WIP_VERSION_H}") + message(STATUS "Using pre-generated version header: ${VERSION_HEADER}") +else() + # Normal development path (local make, dev shell, etc.) + message(STATUS "No pre-generated header found → generating with GitVersion.sh") + include(${CMAKE_SOURCE_DIR}/cmake/GitVersion.cmake) + gitversion_generate(PREFIX GIT_WIP_ OUTPUT ${CMAKE_BINARY_DIR}/git_wip_version.h) + set(VERSION_HEADER "${CMAKE_BINARY_DIR}/git_wip_version.h") +endif() add_executable(git-wip color.cpp @@ -15,6 +25,9 @@ add_executable(git-wip # Ensure the executable is rebuilt when the version header changes add_dependencies(git-wip gitversion) +# make sure the verison is visible to the compiler +target_include_directories(git-wip PRIVATE ${CMAKE_BINARY_DIR}) + install(TARGETS git-wip RUNTIME DESTINATION bin ) From 9cd496b0a2ee42573a8962bafa5cfc349103b152 Mon Sep 17 00:00:00 2001 From: Bart Trojanowski Date: Mon, 18 May 2026 09:40:45 -0400 Subject: [PATCH 02/12] adding package.nix --- default.nix | 3 ++ flake.nix | 61 +--------------------------------------- nix/package.nix | 75 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 79 insertions(+), 60 deletions(-) create mode 100644 default.nix create mode 100644 nix/package.nix diff --git a/default.nix b/default.nix new file mode 100644 index 0000000..5df5cff --- /dev/null +++ b/default.nix @@ -0,0 +1,3 @@ +{ pkgs ? import {} }: + +pkgs.callPackage ./nix/package.nix { } diff --git a/flake.nix b/flake.nix index d545353..e45238a 100644 --- a/flake.nix +++ b/flake.nix @@ -12,66 +12,7 @@ pkgs = nixpkgs.legacyPackages.${system}; in { - packages.default = pkgs.stdenv.mkDerivation { - pname = "git-wip"; - version = "unstable-${self.shortRev or self.dirtyShortRev or "dirty"}"; - - src = self; - - nativeBuildInputs = with pkgs; [ - cmake - ninja - pkg-config - git - ]; - - buildInputs = with pkgs; [ - libgit2 - libgit2.dev - spdlog - openssl - openssl.dev - pcre2 - libssh2 - zlib - ]; - - PKG_CONFIG_PATH = with pkgs; lib.makeSearchPath "lib/pkgconfig" [ - openssl.dev libgit2 pcre2 libssh2 zlib - ]; - -# Better phase for patching shebangs - postPatch = '' - patchShebangs cmake/GitVersion.sh - ''; - -# Pre-generate version header - preConfigure = '' - echo "=== Generating git-wip version header for Nix build ===" - mkdir -p build - ./cmake/GitVersion.sh GIT_WIP_ build/git_wip_version.h - ''; - - cmakeFlags = [ - "-DCMAKE_BUILD_TYPE=Release" - "-DBUILD_TESTING=OFF" - "-DUSE_GIT_WIP_VERSION_H=${placeholder "source"}/build/git_wip_version.h" - ]; - - buildPhase = '' - make -j$NIX_BUILD_CORES - ''; - - installPhase = '' - make install PREFIX=$out - ''; - - meta = with pkgs.lib; { - description = "git-wip — Work In Progress branch manager"; - homepage = "https://github.com/bartman/git-wip"; - license = licenses.gpl2Only; - platforms = platforms.linux ++ platforms.darwin; - }; + packages.default = pkgs.callPackage ./nix/package.nix { }; devShells.default = pkgs.mkShell { diff --git a/nix/package.nix b/nix/package.nix new file mode 100644 index 0000000..eb39f0c --- /dev/null +++ b/nix/package.nix @@ -0,0 +1,75 @@ +{ stdenv +, cmake +, ninja +, pkg-config +, git +, libgit2 +, spdlog +, openssl +, pcre2 +, libssh2 +, zlib +}: + +stdenv.mkDerivation { + pname = "git-wip"; + version = "unstable-${self.shortRev or self.dirtyShortRev or "dirty"}"; + + src = self; + + nativeBuildInputs = with pkgs; [ + cmake + ninja + pkg-config + git + ]; + + buildInputs = with pkgs; [ + libgit2 + libgit2.dev + spdlog + openssl + openssl.dev + pcre2 + libssh2 + zlib + ]; + + PKG_CONFIG_PATH = with pkgs; lib.makeSearchPath "lib/pkgconfig" [ + openssl.dev libgit2 pcre2 libssh2 zlib + ]; + +# Better phase for patching shebangs + postPatch = '' + patchShebangs cmake/GitVersion.sh + ''; + +# Pre-generate version header + preConfigure = '' + echo "=== Generating git-wip version header for Nix build ===" + mkdir -p build + ./cmake/GitVersion.sh GIT_WIP_ build/git_wip_version.h + ls -l build/git_wip_version.h + ''; + + cmakeFlags = [ + "-DCMAKE_BUILD_TYPE=Release" + "-DBUILD_TESTING=OFF" + "-DUSE_GIT_WIP_VERSION_H=${placeholder "source"}/build/git_wip_version.h" + ]; + + buildPhase = '' + make -j$NIX_BUILD_CORES + ''; + + installPhase = '' + make install PREFIX=$out + ''; + + meta = with pkgs.lib; { + description = "git-wip — Work In Progress branch manager"; + homepage = "https://github.com/bartman/git-wip"; + license = licenses.gpl2Only; + platforms = platforms.linux ++ platforms.darwin; + }; +} From 9c598c886589a0d4e75827ff65dc25b6dbbc4ca2 Mon Sep 17 00:00:00 2001 From: Bart Trojanowski Date: Mon, 18 May 2026 09:44:39 -0400 Subject: [PATCH 03/12] wip --- flake.nix | 1 + nix/package.nix | 12 ++++++------ 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/flake.nix b/flake.nix index e45238a..1baf023 100644 --- a/flake.nix +++ b/flake.nix @@ -13,6 +13,7 @@ in { packages.default = pkgs.callPackage ./nix/package.nix { + #version = "unstable-${self.shortRev or self.dirtyShortRev or "dirty"}"; }; devShells.default = pkgs.mkShell { diff --git a/nix/package.nix b/nix/package.nix index eb39f0c..196a788 100644 --- a/nix/package.nix +++ b/nix/package.nix @@ -13,18 +13,18 @@ stdenv.mkDerivation { pname = "git-wip"; - version = "unstable-${self.shortRev or self.dirtyShortRev or "dirty"}"; + version = "unstable-"; - src = self; + src = ./..; - nativeBuildInputs = with pkgs; [ + nativeBuildInputs = with stdenv; [ cmake ninja pkg-config git ]; - buildInputs = with pkgs; [ + buildInputs = with stdenv; [ libgit2 libgit2.dev spdlog @@ -35,7 +35,7 @@ stdenv.mkDerivation { zlib ]; - PKG_CONFIG_PATH = with pkgs; lib.makeSearchPath "lib/pkgconfig" [ + PKG_CONFIG_PATH = with stdenv; lib.makeSearchPath "lib/pkgconfig" [ openssl.dev libgit2 pcre2 libssh2 zlib ]; @@ -66,7 +66,7 @@ stdenv.mkDerivation { make install PREFIX=$out ''; - meta = with pkgs.lib; { + meta = with stdenv.lib; { description = "git-wip — Work In Progress branch manager"; homepage = "https://github.com/bartman/git-wip"; license = licenses.gpl2Only; From 9189eba8ba9be383bc849cbe13dc5cd78ea842fd Mon Sep 17 00:00:00 2001 From: Bart Trojanowski Date: Mon, 18 May 2026 09:48:30 -0400 Subject: [PATCH 04/12] wip --- flake.nix | 1 + nix/package.nix | 25 +++++-------------------- 2 files changed, 6 insertions(+), 20 deletions(-) diff --git a/flake.nix b/flake.nix index 1baf023..51930ed 100644 --- a/flake.nix +++ b/flake.nix @@ -13,6 +13,7 @@ in { packages.default = pkgs.callPackage ./nix/package.nix { + inherit pkgs; #version = "unstable-${self.shortRev or self.dirtyShortRev or "dirty"}"; }; diff --git a/nix/package.nix b/nix/package.nix index 196a788..274aabb 100644 --- a/nix/package.nix +++ b/nix/package.nix @@ -1,4 +1,5 @@ { stdenv +, pkgs , cmake , ninja , pkg-config @@ -17,34 +18,18 @@ stdenv.mkDerivation { src = ./..; - nativeBuildInputs = with stdenv; [ - cmake - ninja - pkg-config - git - ]; + nativeBuildInputs = [ cmake ninja pkg-config git ]; - buildInputs = with stdenv; [ - libgit2 - libgit2.dev - spdlog - openssl - openssl.dev - pcre2 - libssh2 - zlib - ]; + buildInputs = [ libgit2 libgit2.dev spdlog openssl openssl.dev pcre2 libssh2 zlib ]; - PKG_CONFIG_PATH = with stdenv; lib.makeSearchPath "lib/pkgconfig" [ + PKG_CONFIG_PATH = with pkgs; lib.makeSearchPath "lib/pkgconfig" [ openssl.dev libgit2 pcre2 libssh2 zlib ]; -# Better phase for patching shebangs postPatch = '' patchShebangs cmake/GitVersion.sh ''; -# Pre-generate version header preConfigure = '' echo "=== Generating git-wip version header for Nix build ===" mkdir -p build @@ -66,7 +51,7 @@ stdenv.mkDerivation { make install PREFIX=$out ''; - meta = with stdenv.lib; { + meta = with pkgs.lib; { description = "git-wip — Work In Progress branch manager"; homepage = "https://github.com/bartman/git-wip"; license = licenses.gpl2Only; From 2919ba62a67ee604ef5bf9194f70cda57e1460e4 Mon Sep 17 00:00:00 2001 From: Bart Trojanowski Date: Mon, 18 May 2026 09:56:23 -0400 Subject: [PATCH 05/12] wip --- nix/package.nix | 1 + 1 file changed, 1 insertion(+) diff --git a/nix/package.nix b/nix/package.nix index 274aabb..b9345a6 100644 --- a/nix/package.nix +++ b/nix/package.nix @@ -35,6 +35,7 @@ stdenv.mkDerivation { mkdir -p build ./cmake/GitVersion.sh GIT_WIP_ build/git_wip_version.h ls -l build/git_wip_version.h + cat build/git_wip_version.h ''; cmakeFlags = [ From a2254a35153778767867dad447fa2fe521083331 Mon Sep 17 00:00:00 2001 From: Bart Trojanowski Date: Mon, 18 May 2026 10:18:54 -0400 Subject: [PATCH 06/12] wip --- flake.nix | 2 +- nix/package.nix | 10 +++++++--- src/CMakeLists.txt | 13 +++++++++---- 3 files changed, 17 insertions(+), 8 deletions(-) diff --git a/flake.nix b/flake.nix index 51930ed..361fdef 100644 --- a/flake.nix +++ b/flake.nix @@ -14,7 +14,7 @@ { packages.default = pkgs.callPackage ./nix/package.nix { inherit pkgs; - #version = "unstable-${self.shortRev or self.dirtyShortRev or "dirty"}"; + version = "unstable-${self.shortRev or self.dirtyShortRev or "dirty"}"; }; devShells.default = pkgs.mkShell { diff --git a/nix/package.nix b/nix/package.nix index b9345a6..46874e2 100644 --- a/nix/package.nix +++ b/nix/package.nix @@ -10,11 +10,12 @@ , pcre2 , libssh2 , zlib +, version ? "unstable" }: stdenv.mkDerivation { pname = "git-wip"; - version = "unstable-"; + inherit version; src = ./..; @@ -33,15 +34,18 @@ stdenv.mkDerivation { preConfigure = '' echo "=== Generating git-wip version header for Nix build ===" mkdir -p build - ./cmake/GitVersion.sh GIT_WIP_ build/git_wip_version.h + cat > build/git_wip_version.h <<'EOF' +#pragma once +#define GIT_WIP_VERSION ${builtins.toJSON version} +EOF ls -l build/git_wip_version.h cat build/git_wip_version.h + cmakeFlagsArray+=("-DUSE_GIT_WIP_VERSION_H=$PWD/build/git_wip_version.h") ''; cmakeFlags = [ "-DCMAKE_BUILD_TYPE=Release" "-DBUILD_TESTING=OFF" - "-DUSE_GIT_WIP_VERSION_H=${placeholder "source"}/build/git_wip_version.h" ]; buildPhase = '' diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 1153d9a..b3e679b 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,15 +1,23 @@ # Generate version header set(VERSION_HEADER "") +set(VERSION_HEADER_DIR "${CMAKE_BINARY_DIR}") if(DEFINED USE_GIT_WIP_VERSION_H AND EXISTS "${USE_GIT_WIP_VERSION_H}") set(VERSION_HEADER "${USE_GIT_WIP_VERSION_H}") + get_filename_component(VERSION_HEADER_DIR "${VERSION_HEADER}" DIRECTORY) message(STATUS "Using pre-generated version header: ${VERSION_HEADER}") + add_custom_target(gitversion DEPENDS "${VERSION_HEADER}") else() + if(DEFINED USE_GIT_WIP_VERSION_H) + message(WARNING "USE_GIT_WIP_VERSION_H was set but does not exist: ${USE_GIT_WIP_VERSION_H}") + endif() + # Normal development path (local make, dev shell, etc.) message(STATUS "No pre-generated header found → generating with GitVersion.sh") include(${CMAKE_SOURCE_DIR}/cmake/GitVersion.cmake) gitversion_generate(PREFIX GIT_WIP_ OUTPUT ${CMAKE_BINARY_DIR}/git_wip_version.h) set(VERSION_HEADER "${CMAKE_BINARY_DIR}/git_wip_version.h") + set(VERSION_HEADER_DIR "${CMAKE_BINARY_DIR}") endif() add_executable(git-wip @@ -25,15 +33,12 @@ add_executable(git-wip # Ensure the executable is rebuilt when the version header changes add_dependencies(git-wip gitversion) -# make sure the verison is visible to the compiler -target_include_directories(git-wip PRIVATE ${CMAKE_BINARY_DIR}) - install(TARGETS git-wip RUNTIME DESTINATION bin ) target_include_directories(git-wip PRIVATE - ${CMAKE_BINARY_DIR} + ${VERSION_HEADER_DIR} ${LIBGIT2_INCLUDE_DIRS} ) From 93b99ef8c4a58991e45e5a96918076a069267a66 Mon Sep 17 00:00:00 2001 From: Bart Trojanowski Date: Mon, 18 May 2026 11:12:53 -0400 Subject: [PATCH 07/12] wip --- VERSION | 1 + cmake/GitVersion.sh | 35 ++++++++++++++++++++++++++--- flake.nix | 13 ++++++++++- tag-new-release.sh | 55 +++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 100 insertions(+), 4 deletions(-) create mode 100644 VERSION create mode 100755 tag-new-release.sh diff --git a/VERSION b/VERSION new file mode 100644 index 0000000..a85e614 --- /dev/null +++ b/VERSION @@ -0,0 +1 @@ +v0.3 diff --git a/cmake/GitVersion.sh b/cmake/GitVersion.sh index 69f0a20..d64ee80 100755 --- a/cmake/GitVersion.sh +++ b/cmake/GitVersion.sh @@ -1,6 +1,16 @@ #!/usr/bin/env bash -# GitVersion.sh - Generate version header from git describe +# GitVersion.sh - Generate version header from VERSION file + git metadata. +# # Usage: GitVersion.sh PREFIX OUTPUT +# +# Output version string format: +# {VERSION}-{YYYYMMDD}-g{HASH}[-dirty] when .git is available +# {VERSION}-unknown when .git is not available +# +# VERSION is read from the VERSION file at the top of the source tree. +# YYYYMMDD is the committer date of HEAD. +# HASH is the short hash of HEAD. +# -dirty is appended if the working tree has uncommitted changes. set -e @@ -12,8 +22,27 @@ if [ -z "$PREFIX" ] || [ -z "$OUTPUT" ]; then exit 1 fi -# Get git describe output -DESCRIBE="$(git describe --tags --dirty=-dirty 2>/dev/null || echo "unknown")" +# Locate the source tree (the directory containing the VERSION file). This +# script normally runs with CWD = source root (set by CMake), but be defensive. +SRC_DIR="${SRC_DIR:-$PWD}" +if [ ! -f "$SRC_DIR/VERSION" ]; then + SRC_DIR="$(cd "$(dirname "$0")/.." && pwd)" +fi + +VERSION="$(cat "$SRC_DIR/VERSION" 2>/dev/null || echo unknown)" + +if git -C "$SRC_DIR" rev-parse --git-dir >/dev/null 2>&1; then + DATE="$(git -C "$SRC_DIR" log -1 --format=%cd --date=format:%Y%m%d)" + HASH="$(git -C "$SRC_DIR" rev-parse --short HEAD)" + DIRTY="" + if ! git -C "$SRC_DIR" diff --quiet 2>/dev/null \ + || ! git -C "$SRC_DIR" diff --cached --quiet 2>/dev/null; then + DIRTY="-dirty" + fi + DESCRIBE="${VERSION}-${DATE}-g${HASH}${DIRTY}" +else + DESCRIBE="${VERSION}-unknown" +fi # Generate temporary output file OUTPUT_TMP="${OUTPUT}.tmp" diff --git a/flake.nix b/flake.nix index 361fdef..e765368 100644 --- a/flake.nix +++ b/flake.nix @@ -10,11 +10,22 @@ flake-utils.lib.eachDefaultSystem (system: let pkgs = nixpkgs.legacyPackages.${system}; + + # Read the canonical version from the committed VERSION file + # and combine it with flake-provided metadata to produce a + # string of the same shape as cmake/GitVersion.sh: + # {VERSION}-{YYYYMMDD}-g{HASH}[-dirty] + baseVersion = pkgs.lib.fileContents ./VERSION; + # self.lastModifiedDate is "YYYYMMDDHHMMSS" — take the date. + buildDate = builtins.substring 0 8 (self.lastModifiedDate or "00000000"); + shortHash = self.shortRev or self.dirtyShortRev or "unknown"; + dirtySuffix = if self ? rev then "" else "-dirty"; + gitWipVersion = "${baseVersion}-${buildDate}-g${shortHash}${dirtySuffix}"; in { packages.default = pkgs.callPackage ./nix/package.nix { inherit pkgs; - version = "unstable-${self.shortRev or self.dirtyShortRev or "dirty"}"; + version = gitWipVersion; }; devShells.default = pkgs.mkShell { diff --git a/tag-new-release.sh b/tag-new-release.sh new file mode 100755 index 0000000..9f09e4c --- /dev/null +++ b/tag-new-release.sh @@ -0,0 +1,55 @@ +#!/usr/bin/env bash +# tag-new-release.sh - Tag a new release +# +# Usage: tag-new-release.sh +# +# 1. Validates that is strictly greater (per `sort -V`) than both +# the current contents of the VERSION file and the most recent annotated +# git tag. +# 2. Writes to the VERSION file. +# 3. Commits the VERSION file with message "Release ". +# 4. Creates an annotated tag `git tag -a -m `. + +set -euo pipefail + +die() { echo "error: $*" >&2; exit 1; } + +[ $# -eq 1 ] || die "usage: $0 " +NEW="$1" + +# Run from repo root +cd "$(dirname "$0")" + +[ -d .git ] || die "not at the top of a git repo" +[ -f VERSION ] || die "VERSION file missing" + +CUR_FILE="$(cat VERSION)" +CUR_TAG="$(git tag --list --sort=-v:refname | head -n1 || true)" + +# Strict-greater check via `sort -V`: NEW must sort strictly after the other. +strictly_greater() { + local new="$1" other="$2" + [ -n "$other" ] || return 0 # nothing to compare against + [ "$new" != "$other" ] || return 1 + local top + top="$(printf '%s\n%s\n' "$new" "$other" | sort -V | tail -n1)" + [ "$top" = "$new" ] +} + +strictly_greater "$NEW" "$CUR_FILE" \ + || die "version '$NEW' is not strictly greater than VERSION file '$CUR_FILE'" +strictly_greater "$NEW" "$CUR_TAG" \ + || die "version '$NEW' is not strictly greater than most recent tag '$CUR_TAG'" + +# Refuse to release with a dirty tree (besides VERSION itself, which we are +# about to write). +if ! git diff --quiet || ! git diff --cached --quiet; then + die "working tree has uncommitted changes; commit or stash them first" +fi + +echo "$NEW" > VERSION +git add VERSION +git commit -m "Release $NEW" +git tag -a -m "$NEW" "$NEW" + +echo "Tagged release $NEW (commit $(git rev-parse --short HEAD))" From 41761a9cce47c6134ceb13830490df860c6d7dbe Mon Sep 17 00:00:00 2001 From: Bart Trojanowski Date: Mon, 18 May 2026 11:25:15 -0400 Subject: [PATCH 08/12] agents update --- AGENTS.md | 87 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 87 insertions(+) diff --git a/AGENTS.md b/AGENTS.md index c69de22..e2f16f9 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -26,6 +26,93 @@ The Neovim Lua plugin supports the following configuration options (set via `opt Async execution uses Neovim's `vim.system` with `on_exit` callback for non-blocking saves. +## Versioning + +The `git-wip --version` string is generated at build time and embedded in the +binary as the C string macro `GIT_WIP_VERSION` (defined in +`build/git_wip_version.h`, consumed in `src/main.cpp`). + +### Version string format + +``` +{VERSION}-{YYYYMMDD}-g{HASH}[-dirty] +``` + +- `VERSION` — contents of the committed `VERSION` file at the top of the + source tree (currently `v0.3`). This is the canonical, human-managed + version and is bumped by `tag-new-release.sh`. +- `YYYYMMDD` — committer date of `HEAD` (for `make`) or + `self.lastModifiedDate` truncated to a date (for `nix build`). +- `HASH` — short hash of `HEAD` / `self.shortRev`. +- `-dirty` — appended when the working tree has uncommitted changes (`make` + path) or when the flake source is dirty (Nix path: `self.rev` absent). + +Special fallback for `make` builds with no `.git` directory available: + +``` +{VERSION}-unknown +``` + +The format intentionally does **not** match `git describe`'s +`vX.Y--g` shape, because `git describe` cannot run +inside the Nix sandbox (`.git` is stripped from the source tree). The +date+hash form is monotonic, encodes the commit, and works identically under +all three build paths. + +### Files involved + +| File | Role | +|---|---| +| `VERSION` | Source of truth. One line, e.g. `v0.3`. Committed. | +| `tag-new-release.sh` | Bumps `VERSION`, commits, creates annotated tag. | +| `cmake/GitVersion.sh` | Generates `git_wip_version.h` for `make` builds. Reads `VERSION`, queries git. | +| `cmake/GitVersion.cmake` | CMake wrapper around `GitVersion.sh`. Adds custom command + target `gitversion`. | +| `src/CMakeLists.txt` | Wires `gitversion` into the `git-wip` executable. Honours `USE_GIT_WIP_VERSION_H=` to use a pre-generated header (Nix path). | +| `flake.nix` | Computes the version string from `lib.fileContents ./VERSION`, `self.lastModifiedDate`, and `self.shortRev` / `self.dirtyShortRev`. Passes it to `nix/package.nix`. | +| `nix/package.nix` | In `preConfigure`, writes `build/git_wip_version.h` from the injected `version` argument and points CMake at it via `-DUSE_GIT_WIP_VERSION_H=...`. | + +### Build paths and what they produce + +| Build invocation | Where version comes from | Example output | +|---|---|---| +| `make` (Debian / NixOS dev shell) | `cmake/GitVersion.sh` runs `git log -1 --format=%cd` + `git rev-parse --short HEAD` | `v0.3-20260518-g93b99ef` | +| `make` with no `.git` (extracted tarball) | `cmake/GitVersion.sh` fallback | `v0.3-unknown` | +| `nix build` (flake) | `flake.nix` computes from `self.*`, passed via `package.nix` `preConfigure` | `v0.3-20260518-g93b99ef` | + +All three paths produce byte-identical output for the same clean commit. + +### Cutting a release + +``` +./tag-new-release.sh v0.4 +``` + +The script: +1. Refuses to run on a dirty tree. +2. Validates `v0.4` is strictly greater (per `sort -V`) than both the current + `VERSION` file contents and the most recent git tag. +3. Writes `v0.4` to `VERSION`. +4. Commits with message `Release v0.4`. +5. Creates annotated tag `git tag -a -m v0.4 v0.4`. + +After running the script, push commit + tag with `git push --follow-tags`. + +### Implementation notes + +- **No use of `git describe`.** The previous scheme used it; the rewrite + avoids it because (a) it cannot run in the Nix sandbox, and (b) it is + unstable when a tarball is extracted without `.git`. +- **`self.revCount` is intentionally unused.** The GitHub flake fetcher + (`github:` URL) does not populate it; the date+hash scheme works under + both `github:` and `git+https://` fetchers without conditional logic. +- **Dirty detection in Nix** relies on `self ? rev`. A dirty flake source + sets `dirtyRev`/`dirtyShortRev` but not `rev`, so the check + `if self ? rev then "" else "-dirty"` is correct. +- **`USE_GIT_WIP_VERSION_H`** is a CMake variable, not an env var. The + Nix derivation appends `-DUSE_GIT_WIP_VERSION_H=$PWD/build/git_wip_version.h` + to `cmakeFlagsArray` in `preConfigure`. When set and the file exists, + `src/CMakeLists.txt` skips the `GitVersion.sh` invocation entirely. + ## Test Infrastructure ### test/cli/lib.sh From 95c26ed5e5cc68579d141d8a4cd2bbaa2f499c98 Mon Sep 17 00:00:00 2001 From: Bart Trojanowski Date: Mon, 18 May 2026 11:36:50 -0400 Subject: [PATCH 09/12] nix github build --- .github/workflows/ci.yml | 45 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8051499..64eefc8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -236,6 +236,51 @@ jobs: build-a/Testing/ retention-days: 7 + nix: + name: Nix (dev shell + flake build) + + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v6 + + - name: Install Nix + uses: cachix/install-nix-action@v31 + with: + extra_nix_config: | + experimental-features = nix-command flakes + accept-flake-config = true + + - name: Configure git identity (needed by tests that create commits) + run: | + git config --global user.email "ci@github-actions" + git config --global user.name "GitHub Actions" + git config --global init.defaultBranch master + + - name: Build & test inside dev shell (make / make test) + run: | + nix develop --command bash -c ' + set -e + make + make test + ' + + - name: Build via flake (nix build) + run: nix build --print-build-logs + + - name: Verify --version of nix-built binary + run: | + set -e + ./result/bin/git-wip --version + # Sanity-check: must start with the contents of the VERSION file. + ver="$(./result/bin/git-wip --version)" + base="$(cat VERSION)" + case "$ver" in + "$base"-*) echo "OK: version '$ver' starts with '$base-'" ;; + *) echo "FAIL: version '$ver' does not start with '$base-'" >&2; exit 1 ;; + esac + coverage: name: Coverage (debian:stable / gcc / Debug) From d101794dae6396d4936936e4dac1515269a9bbb5 Mon Sep 17 00:00:00 2001 From: Bart Trojanowski Date: Mon, 18 May 2026 11:42:44 -0400 Subject: [PATCH 10/12] agents update --- AGENTS.md | 57 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/AGENTS.md b/AGENTS.md index e2f16f9..f0dc359 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -113,6 +113,63 @@ After running the script, push commit + tag with `git push --follow-tags`. to `cmakeFlagsArray` in `preConfigure`. When set and the file exists, `src/CMakeLists.txt` skips the `GitVersion.sh` invocation entirely. +## Continuous Integration + +`.github/workflows/ci.yml` defines three jobs: + +### `build` (matrix) + +Distro × compiler × build-type matrix. Runs in distro-native containers on +the `ubuntu-latest` runner: + +| Distro | Compilers | Build types | Static | +|---|---|---|---| +| `debian:stable` | gcc, clang | Release, Debug | yes | +| `ubuntu:24.04` | gcc, clang | Release, Debug | yes | +| `fedora:latest` | gcc, clang | Release, Debug | no | +| `archlinux:latest` | gcc, clang | Release, Debug | no | + +For each cell: `dependencies.sh` installs deps, `make BUILD=build-so` builds +dynamic, `make BUILD=build-so test` runs tests; then (only on distros where +libgit2.a is available) it repeats with `STATIC=1 BUILD=build-a`. On +failure, test artifacts from `build-so/` and `build-a/` are uploaded. + +### `nix` (single job, no matrix) + +Runs on stock `ubuntu-latest` (no container). Installs Nix via +`cachix/install-nix-action@v31` with `nix-command` + `flakes` enabled. +Compiler and dependency versions are pinned by the flake, so a matrix is +unnecessary. Steps: + +1. **Dev shell make build** — `nix develop --command bash -c 'make && make test'`. + Exercises the `cmake/GitVersion.sh` path with the flake's pinned toolchain. +2. **Flake build** — `nix build --print-build-logs`. Exercises the + `flake.nix` → `package.nix` → `preConfigure` version-injection path. +3. **Version sanity check** — runs `./result/bin/git-wip --version` and asserts + the output starts with `-`. Catches regressions in + either the flake's version computation or `package.nix`'s header generation. + +This job is the canonical guard against the three known regressions in the +Nix path: (a) `git describe` creeping back in, (b) `.git` being needed inside +the sandbox, (c) `VERSION` not being read. + +### `coverage` + +Single job on `debian:stable` / gcc / Debug. Runs `make TYPE=Debug coverage` +and uploads the resulting `coverage.info` to Codecov. + +### Why no `nixos/nix` container + +GitHub Actions' `container:` mechanism requires a node-capable image to host +the runner agent and the JavaScript-based actions (`actions/checkout`, +`actions/cache`, `actions/upload-artifact`). The official `nixos/nix` image +is intentionally minimal and lacks `node` and an FHS layout, breaking those +actions. The supported pattern — used by NixOS/nix itself — is to run on +stock `ubuntu-latest` and install Nix via `cachix/install-nix-action`. +Functionally this is equivalent to a NixOS container for our purposes +because the flake's `mkShell` and `mkDerivation` pin their toolchain +independently of the host distro. + ## Test Infrastructure ### test/cli/lib.sh From 17a6d58e6145e4446e2e1455ba55815e85804245 Mon Sep 17 00:00:00 2001 From: Bart Trojanowski Date: Mon, 18 May 2026 12:04:10 -0400 Subject: [PATCH 11/12] nix instructions updated --- README.md | 67 +++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 65 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index c76153c..ce506ca 100644 --- a/README.md +++ b/README.md @@ -83,11 +83,15 @@ Equivalent to `git wip save "WIP"`. ### `git wip [--version | -v | version]` -Show the version string (from `git describe --tags --dirty=-dirty` at build time). +Show the version string baked in at build time. Format: +`{VERSION}-{YYYYMMDD}-g{HASH}[-dirty]`, where `VERSION` comes from the +committed `VERSION` file, `YYYYMMDD` is the commit date, and `HASH` is the +short commit hash. `-dirty` is appended if the working tree had uncommitted +changes at build time. ``` $ git wip --version -v0.2-83-g95a6648-dirty +v0.3-20260518-g93b99ef ``` ### `git wip save [] [options] [-- ...]` @@ -248,6 +252,65 @@ Or copy the binary manually: $ cp build/src/git-wip ~/bin/ ``` +### NixOS + +`git-wip` ships a flake (`flake.nix`) and a self-contained package definition +(`nix/package.nix`). To install it system-wide on NixOS **without touching +your existing `flake.nix`**, drop a `git-wip.nix` file next to your +`configuration.nix` and add it to `imports`. + +In `configuration.nix`: + +```nix +imports = [ + # ...your other imports... + ./git-wip.nix +]; +``` + +In `git-wip.nix`: + +```nix +{ config, pkgs, lib, ... }: + +let + # Pin to the most recent release. Update both `ref` and `rev` when bumping. + # `rev` must be the full 40-character commit hash; look it up with: + # git ls-remote https://github.com/bartman/git-wip.git refs/tags/v0.3 + src = builtins.fetchGit { + url = "https://github.com/bartman/git-wip.git"; + ref = "refs/tags/v0.3"; + rev = "0000000000000000000000000000000000000000"; # replace with v0.3's full sha + }; + + # Reproduce the version string shape used by the upstream flake: + # {VERSION}-{YYYYMMDD}-g{HASH} + baseVersion = lib.fileContents (src + "/VERSION"); + buildDate = builtins.substring 0 8 (src.lastModifiedDate or "00000000"); + shortHash = src.shortRev or "unknown"; + version = "${baseVersion}-${buildDate}-g${shortHash}"; + + git-wip = pkgs.callPackage (src + "/nix/package.nix") { + inherit pkgs version; + }; +in +{ + environment.systemPackages = [ git-wip ]; +} +``` + +Apply with `sudo nixos-rebuild switch`, then verify: + +```sh +$ git-wip --version +v0.3-20260518-g93b99ef +``` + +To upgrade to a newer release later, bump `ref` to the new tag (e.g. +`refs/tags/v0.4`) and replace `rev` with the full hash of that tag's commit. +Leaving the `rev` value invalid will cause Nix to error out — useful, since +it forces you to consciously pin each release. + --- ## Editor integration From 625dc2fc80c5c0404c061d489936a69e6159e972 Mon Sep 17 00:00:00 2001 From: Bart Trojanowski Date: Mon, 18 May 2026 12:04:26 -0400 Subject: [PATCH 12/12] Release v0.4 --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index a85e614..1811f96 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -v0.3 +v0.4