diff --git a/.claude/settings.json b/.claude/settings.json new file mode 100644 index 0000000..e46cd4a --- /dev/null +++ b/.claude/settings.json @@ -0,0 +1,15 @@ +{ + "hooks": { + "PostToolUse": [ + { + "matcher": "Write|Edit|MultiEdit", + "hooks": [ + { + "type": "command", + "command": "command -v comment-checker >/dev/null 2>&1 && exec comment-checker || { command -v direnv >/dev/null 2>&1 && exec direnv exec \"${PWD:-.}\" comment-checker; } || exit 0" + } + ] + } + ] + } +} \ No newline at end of file diff --git a/.envrc b/.envrc new file mode 100644 index 0000000..3550a30 --- /dev/null +++ b/.envrc @@ -0,0 +1 @@ +use flake diff --git a/.gitignore b/.gitignore index 0fe729e..388f177 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,7 @@ npm/bin dist dist-types *.tsbuildinfo + +# nix/direnv artifacts (regenerated; flake-profile is a GC root for the devshell) +/result +/.direnv/ diff --git a/flake.nix b/flake.nix index ef16220..22b3add 100644 --- a/flake.nix +++ b/flake.nix @@ -21,21 +21,76 @@ overlays = [ (import rust-overlay) ]; }; in f pkgs); + version = "0.1.5"; + mkCommentChecker = pkgs: + let + target = { + "x86_64-linux" = "x86_64-unknown-linux-gnu"; + "aarch64-linux" = "aarch64-unknown-linux-gnu"; + "x86_64-darwin" = "x86_64-apple-darwin"; + "aarch64-darwin" = "aarch64-apple-darwin"; + }.${pkgs.system} or (throw "unsupported system ${pkgs.system}"); + hash = { + "x86_64-unknown-linux-gnu" = "sha256-d/Xl2VZqnB+lFNkdtglY7N/nY6CxhgQG+arGL7FmCME="; + "aarch64-unknown-linux-gnu" = "sha256-vP0Ss8eOOElpCrxryGiMn0WMBIEDtJe3LnB8FunZjok="; + "x86_64-apple-darwin" = "sha256-c0mJOCcz0Zt61Da/y/n3JTFGchWJQk8cBZ8EMVYx7e8="; + "aarch64-apple-darwin" = "sha256-C/f81qw86DXoZ6dL2rEt6z67IfYw09hG8iaa0vQOu2U="; + }.${target}; + src = pkgs.fetchurl { + url = "https://github.com/systemfsoftware/comment-checker/releases/download/v${version}/comment-checker-${target}"; + inherit hash; + }; + in pkgs.stdenv.mkDerivation { + pname = "comment-checker"; + inherit version src; + dontUnpack = true; + installPhase = '' + install -Dm755 $src $out/bin/comment-checker + ''; + meta = with pkgs.lib; { + description = "Claude Code PostToolUse hook that flags unnecessary comments"; + homepage = "https://github.com/systemfsoftware/comment-checker"; + license = licenses.asl20; + platforms = platforms.unix; + }; + }; + mkBwrap = pkgs: commentChecker: + pkgs.writeShellScriptBin "comment-checker" '' + extra="" + [ -e /lib ] && extra="$extra --ro-bind /lib /lib" + [ -e /lib64 ] && extra="$extra --ro-bind /lib64 /lib64" + exec ${pkgs.bubblewrap}/bin/bwrap \ + --ro-bind /nix/store /nix/store \ + --ro-bind /etc /etc \ + --ro-bind /usr /usr \ + $extra \ + --proc /proc --dev /dev --tmpfs /tmp \ + --unshare-net --die-with-parent \ + --ro-bind "$PWD" "$PWD" \ + --chdir "$PWD" \ + -- ${commentChecker}/bin/comment-checker "$@" + ''; in { + packages = forAllSystems (pkgs: + let + unwrapped = mkCommentChecker pkgs; + wrapped = mkBwrap pkgs unwrapped; + in { + comment-checker = unwrapped; + comment-checker-bwrap = wrapped; + default = wrapped; + }); + devShells = forAllSystems (pkgs: { default = pkgs.mkShell { packages = [ - # Rust toolchain derived directly from ./rust-toolchain.toml (pkgs.rust-bin.fromRustupToolchainFile ./rust-toolchain.toml) pkgs.cargo-mutants - - # Linker + C compiler: the cc crate compiles tree-sitter grammars. pkgs.gcc - - # JS toolchain: nodejs 24 (CI major), pnpm pinned by nixpkgs to - # 11.21.0 — identical to packageManager, so no corepack shim. pkgs.nodejs pkgs.pnpm + pkgs.bubblewrap + (mkBwrap pkgs (mkCommentChecker pkgs)) ]; }; }); diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 292be6e..16083bc 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,3 +1,4 @@ [toolchain] channel = "stable" +profile = "minimal" components = ["rustc", "cargo", "clippy", "rustfmt"] diff --git a/scripts/tools/create-github-release.ts b/scripts/tools/create-github-release.ts index 2caaf43..808ee8d 100755 --- a/scripts/tools/create-github-release.ts +++ b/scripts/tools/create-github-release.ts @@ -1,4 +1,4 @@ -#!/usr/bin/env -S deno run --allow-run=git,gh --allow-read --allow-env +#!/usr/bin/env -S deno run --allow-run=git,gh,tar --allow-read --allow-write --allow-env import { type Target, TARGETS_PATH } from '../lib/shared.ts' @@ -36,22 +36,37 @@ try { if (body) releaseNotes = body } catch { /* no changelog */ } -const binaryFiles = (await Promise.all(targets.map((t) => +const tarballs = (await Promise.all(targets.map((t) => firstExisting([ `release-assets/release-${t.suffix}/dist/release-tarball-${t.suffix}/comment-checker-${t.target}.tar.gz`, `release-assets/release-${t.suffix}/comment-checker-${t.target}.tar.gz`, `release-assets/comment-checker-${t.target}.tar.gz`, ]).then((found) => { if (!found) console.error(`create-github-release: missing tarball for ${t.target}`) - return found + return found ? ({ target: t, tarball: found } as const) : null }) -))).filter((p): p is string => p !== null) +))).filter((v): v is { target: Target; tarball: string } => v !== null) -if (binaryFiles.length !== targets.length) { - console.error(`create-github-release: expected ${targets.length} tarballs, found ${binaryFiles.length}`) +if (tarballs.length !== targets.length) { + console.error(`create-github-release: expected ${targets.length} tarballs, found ${tarballs.length}`) Deno.exit(1) } +await Deno.mkdir('release-assets/binaries', { recursive: true }) + +const binaries = await Promise.all(tarballs.map(async ({ target, tarball }) => { + const tmp = `release-assets/binaries/.tmp-${target.suffix}` + await Deno.mkdir(tmp, { recursive: true }) + const res = await new Deno.Command('tar', { args: ['-xzf', tarball, '-C', tmp] }).output() + if (!res.success) throw new Error(`tar -xzf ${tarball} failed with ${res.code}`) + const exe = target.bin.endsWith('.exe') + const outName = `comment-checker-${target.target}${exe ? '.exe' : ''}` + const outPath = `release-assets/binaries/${outName}` + await Deno.rename(`${tmp}/${target.bin}`, outPath) + await Deno.remove(tmp, { recursive: true }) + return outPath +})) + const tag = `v${version}` -await exec('gh', ['release', 'create', tag, ...binaryFiles, '--title', tag, '--notes', releaseNotes]) -console.log(`created GitHub release ${tag} with ${binaryFiles.length} binary tarball(s)`) +await exec('gh', ['release', 'create', tag, ...binaries, '--title', tag, '--notes', releaseNotes]) +console.log(`created GitHub release ${tag} with ${binaries.length} binaries`)