From 8fe1f923fbaa9c6fa849dc4d2d6ca62b9f212bd4 Mon Sep 17 00:00:00 2001 From: Tarik02 Date: Sat, 15 Aug 2026 12:22:42 +0300 Subject: [PATCH 1/4] feat: add nix package --- .github/workflows/check.yml | 20 +++++++ .github/workflows/nix-pnpm-deps.yml | 90 +++++++++++++++++++++++++++++ README.md | 6 ++ flake.lock | 45 +++++++++++++++ flake.nix | 35 +++++++++++ nix/package.nix | 88 ++++++++++++++++++++++++++++ 6 files changed, 284 insertions(+) create mode 100644 .github/workflows/nix-pnpm-deps.yml create mode 100644 flake.lock create mode 100644 flake.nix create mode 100644 nix/package.nix diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index f49f833..784a1d7 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -12,6 +12,26 @@ concurrency: cancel-in-progress: true jobs: + nix: + name: nix + runs-on: ubuntu-latest + permissions: + contents: read + steps: + - uses: actions/checkout@v6.0.3 + + - uses: cachix/install-nix-action@v31 + + - name: check upstream source revision + shell: bash + run: | + submodule_rev=$(git ls-tree HEAD upstream-t3code | awk '{ print $3 }') + input_rev=$(nix flake metadata --json | jq -r '.locks.nodes."upstream-t3code".locked.rev') + test "$submodule_rev" = "$input_rev" + + - name: build + run: nix build + check: name: ${{ matrix.name }} runs-on: ubuntu-latest diff --git a/.github/workflows/nix-pnpm-deps.yml b/.github/workflows/nix-pnpm-deps.yml new file mode 100644 index 0000000..c01b256 --- /dev/null +++ b/.github/workflows/nix-pnpm-deps.yml @@ -0,0 +1,90 @@ +name: nix pnpm dependency hash + +on: + push: + branches: + - master + +permissions: + contents: read + +concurrency: + group: nix-pnpm-dependency-hash-${{ github.ref }} + cancel-in-progress: false + +jobs: + update: + name: check pnpm dependency hash + runs-on: ubuntu-latest + timeout-minutes: 30 + permissions: + contents: write + pull-requests: write + steps: + - id: app-token + name: create github app token + uses: actions/create-github-app-token@v3.2.0 + with: + client-id: ${{ vars.APP_CLIENT_ID }} + private-key: ${{ secrets.APP_PRIVATE_KEY }} + permission-contents: write + permission-pull-requests: write + + - name: checkout master + uses: actions/checkout@v6.0.3 + with: + ref: master + token: ${{ steps.app-token.outputs.token }} + + - name: install nix + uses: cachix/install-nix-action@v31 + + - id: update + name: update pnpm dependency hash + shell: bash + run: | + nix run github:Mic92/nix-update/cf68051e7b7e08de6c707484cdcd1a53feb48e05 -- \ + t3code-cli \ + --flake \ + --version=skip \ + --override-filename nix/package.nix \ + --build + + if git diff --quiet -- nix/package.nix; then + echo "changed=false" >> "$GITHUB_OUTPUT" + else + echo "changed=true" >> "$GITHUB_OUTPUT" + fi + + - id: pull-request + name: create or update dependency hash pull request + uses: peter-evans/create-pull-request@v8 + with: + token: ${{ steps.app-token.outputs.token }} + base: master + branch: automation/nix-pnpm-dependency-hash-master + delete-branch: true + add-paths: nix/package.nix + commit-message: update nix pnpm dependency hash + title: update nix pnpm dependency hash + body: | + ## What changed + + - Updated the Nix pnpm dependency hash for the current lockfile. + + ## Why + + The committed hash did not match the dependency store generated from `pnpm-lock.yaml`. + + ## Checklist + + - [x] This PR is small and focused + - [x] I explained what changed and why + + - name: fail on dependency hash drift + if: steps.update.outputs.changed == 'true' + env: + PULL_REQUEST_URL: ${{ steps.pull-request.outputs.pull-request-url }} + run: | + echo "::error::The Nix pnpm dependency hash was stale. Repair PR: $PULL_REQUEST_URL" + exit 1 diff --git a/README.md b/README.md index d96dfca..8a7aa11 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,12 @@ npm install --global t3code-cli This installs the `t3cli` command globally. +On NixOS or another system with Nix installed: + +```sh +nix profile install github:tarik02-org/t3code-cli +``` + ## Quick Start ```sh diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..ede16a5 --- /dev/null +++ b/flake.lock @@ -0,0 +1,45 @@ +{ + "nodes": { + "nixpkgs": { + "locked": { + "lastModified": 1786599213, + "narHash": "sha256-yNJd40f11EzXBjSByCB7IPpeFFAdeoSKKM67dGkfFoU=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "0e251e24a4f24e036a084b6b4b2d2491af4167f4", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "nixpkgs": "nixpkgs", + "upstream-t3code": "upstream-t3code" + } + }, + "upstream-t3code": { + "flake": false, + "locked": { + "lastModified": 1786361740, + "narHash": "sha256-qZi9hMGzqpmnpqvvVtsQvkZIiVqTgOMWv1y15MiSAYg=", + "owner": "pingdotgg", + "repo": "t3code", + "rev": "3b72d17cbca691f0b64e6d4a10c9e349f42873a5", + "type": "github" + }, + "original": { + "owner": "pingdotgg", + "repo": "t3code", + "rev": "3b72d17cbca691f0b64e6d4a10c9e349f42873a5", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..5f02424 --- /dev/null +++ b/flake.nix @@ -0,0 +1,35 @@ +{ + description = "T3 Code CLI"; + + inputs = { + nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; + + upstream-t3code = { + url = "github:pingdotgg/t3code/3b72d17cbca691f0b64e6d4a10c9e349f42873a5"; + flake = false; + }; + }; + + outputs = + { + nixpkgs, + self, + upstream-t3code, + }: + let + system = "x86_64-linux"; + pkgs = import nixpkgs { inherit system; }; + in + { + packages.${system} = rec { + t3code-cli = pkgs.callPackage ./nix/package.nix { + src = self; + upstreamSrc = upstream-t3code; + }; + + default = t3code-cli; + }; + + formatter.${system} = pkgs.nixfmt; + }; +} diff --git a/nix/package.nix b/nix/package.nix new file mode 100644 index 0000000..b8e9908 --- /dev/null +++ b/nix/package.nix @@ -0,0 +1,88 @@ +{ + autoPatchelfHook, + cacert, + fetchPnpmDeps, + installShellFiles, + lib, + libsecret, + makeWrapper, + nodejs_24, + pnpm_10, + pnpmConfigHook, + runCommand, + src, + stdenv, + upstreamSrc, + writableTmpDirAsHomeHook, +}: + +let + nodejs = nodejs_24; + pnpm = pnpm_10; + version = (builtins.fromJSON (builtins.readFile "${src}/package.json")).version; + sourceWithUpstream = runCommand "t3code-cli-${version}-source" { } '' + cp --recursive --no-preserve=mode ${src} $out + chmod --recursive u+w $out + rm -rf $out/upstream-t3code + cp --recursive --no-preserve=mode ${upstreamSrc} $out/upstream-t3code + ''; +in +stdenv.mkDerivation (finalAttrs: { + pname = "t3code-cli"; + inherit version; + src = sourceWithUpstream; + strictDeps = true; + + pnpmDeps = fetchPnpmDeps { + inherit (finalAttrs) pname version src; + inherit pnpm; + fetcherVersion = 4; + hash = "sha256-2bwrSxiSH8/S/JaB9aqNpNEmzLo5rSoIawIk8FONMMI="; + }; + + nativeBuildInputs = [ + autoPatchelfHook + installShellFiles + makeWrapper + nodejs + pnpm + pnpmConfigHook + writableTmpDirAsHomeHook + ]; + + buildInputs = [ libsecret ]; + + noAuditTmpdir = true; + SSL_CERT_FILE = "${cacert}/etc/ssl/certs/ca-bundle.crt"; + + buildPhase = '' + runHook preBuild + pnpm build + runHook postBuild + ''; + + installPhase = '' + runHook preInstall + + app="$out/libexec/t3code-cli" + mkdir -p "$out/bin" + pnpm --filter t3code-cli --config.inject-workspace-packages=true \ + deploy --prod --offline "$app" + makeWrapper ${lib.getExe nodejs} "$out/bin/t3cli" \ + --add-flags "$app/dist/bin.js" + installShellCompletion --cmd t3cli \ + --bash <($out/bin/t3cli --completions bash) \ + --fish <($out/bin/t3cli --completions fish) \ + --zsh <($out/bin/t3cli --completions zsh) + + runHook postInstall + ''; + + meta = { + description = "Non-interactive CLI for T3 Code"; + homepage = "https://github.com/tarik02-org/t3code-cli"; + license = lib.licenses.mit; + mainProgram = "t3cli"; + platforms = [ "x86_64-linux" ]; + }; +}) From b1b0e7d8ee234140b0894a6fb8fd27842760d7b6 Mon Sep 17 00:00:00 2001 From: Tarik02 Date: Sat, 15 Aug 2026 12:31:30 +0300 Subject: [PATCH 2/4] ci: report stale nix hashes on pull requests --- .github/workflows/nix-pnpm-deps.yml | 75 ++++++++++------------------- 1 file changed, 26 insertions(+), 49 deletions(-) diff --git a/.github/workflows/nix-pnpm-deps.yml b/.github/workflows/nix-pnpm-deps.yml index c01b256..af162a3 100644 --- a/.github/workflows/nix-pnpm-deps.yml +++ b/.github/workflows/nix-pnpm-deps.yml @@ -1,46 +1,29 @@ name: nix pnpm dependency hash on: - push: - branches: - - master + pull_request: permissions: contents: read + pull-requests: write concurrency: - group: nix-pnpm-dependency-hash-${{ github.ref }} - cancel-in-progress: false + group: nix-pnpm-dependency-hash-${{ github.event.pull_request.number }} + cancel-in-progress: true jobs: - update: + check: name: check pnpm dependency hash runs-on: ubuntu-latest timeout-minutes: 30 - permissions: - contents: write - pull-requests: write steps: - - id: app-token - name: create github app token - uses: actions/create-github-app-token@v3.2.0 - with: - client-id: ${{ vars.APP_CLIENT_ID }} - private-key: ${{ secrets.APP_PRIVATE_KEY }} - permission-contents: write - permission-pull-requests: write - - - name: checkout master - uses: actions/checkout@v6.0.3 - with: - ref: master - token: ${{ steps.app-token.outputs.token }} + - uses: actions/checkout@v6.0.3 - name: install nix uses: cachix/install-nix-action@v31 - id: update - name: update pnpm dependency hash + name: calculate pnpm dependency hash shell: bash run: | nix run github:Mic92/nix-update/cf68051e7b7e08de6c707484cdcd1a53feb48e05 -- \ @@ -54,37 +37,31 @@ jobs: echo "changed=false" >> "$GITHUB_OUTPUT" else echo "changed=true" >> "$GITHUB_OUTPUT" + { + echo 'The Nix pnpm dependency hash is stale. Apply this change to the pull request:' + echo + echo '```diff' + git diff -- nix/package.nix + echo '```' + } > "$RUNNER_TEMP/nix-pnpm-dependency-hash.md" fi - - id: pull-request - name: create or update dependency hash pull request - uses: peter-evans/create-pull-request@v8 + - name: post proposed hash change + if: steps.update.outputs.changed == 'true' + uses: marocchino/sticky-pull-request-comment@v2 with: - token: ${{ steps.app-token.outputs.token }} - base: master - branch: automation/nix-pnpm-dependency-hash-master - delete-branch: true - add-paths: nix/package.nix - commit-message: update nix pnpm dependency hash - title: update nix pnpm dependency hash - body: | - ## What changed - - - Updated the Nix pnpm dependency hash for the current lockfile. + header: nix-pnpm-dependency-hash + path: ${{ runner.temp }}/nix-pnpm-dependency-hash.md - ## Why - - The committed hash did not match the dependency store generated from `pnpm-lock.yaml`. - - ## Checklist - - - [x] This PR is small and focused - - [x] I explained what changed and why + - name: remove resolved hash comment + if: steps.update.outputs.changed == 'false' + uses: marocchino/sticky-pull-request-comment@v2 + with: + header: nix-pnpm-dependency-hash + delete: true - name: fail on dependency hash drift if: steps.update.outputs.changed == 'true' - env: - PULL_REQUEST_URL: ${{ steps.pull-request.outputs.pull-request-url }} run: | - echo "::error::The Nix pnpm dependency hash was stale. Repair PR: $PULL_REQUEST_URL" + echo '::error::The Nix pnpm dependency hash is stale. Apply the change from the pull request comment.' exit 1 From 37456fefbb1be470063f43b1a4f9c315f4d2e2a3 Mon Sep 17 00:00:00 2001 From: Tarik02 Date: Sat, 15 Aug 2026 12:40:44 +0300 Subject: [PATCH 3/4] feat: support nix on linux and macos --- .github/workflows/check.yml | 23 ++++++++++------- .github/workflows/nix-pnpm-deps.yml | 19 +++++++++----- flake.lock | 17 +++++++++++++ flake.nix | 39 +++++++++++++++++++++-------- nix/package.nix | 13 +++++++--- 5 files changed, 82 insertions(+), 29 deletions(-) diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index 784a1d7..e07c981 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -13,22 +13,27 @@ concurrency: jobs: nix: - name: nix - runs-on: ubuntu-latest + name: nix (${{ matrix.system }}) + runs-on: ${{ matrix.runner }} permissions: contents: read + strategy: + fail-fast: false + matrix: + include: + - system: x86_64-linux + runner: ubuntu-latest + - system: aarch64-linux + runner: ubuntu-24.04-arm + - system: x86_64-darwin + runner: macos-15-intel + - system: aarch64-darwin + runner: macos-15 steps: - uses: actions/checkout@v6.0.3 - uses: cachix/install-nix-action@v31 - - name: check upstream source revision - shell: bash - run: | - submodule_rev=$(git ls-tree HEAD upstream-t3code | awk '{ print $3 }') - input_rev=$(nix flake metadata --json | jq -r '.locks.nodes."upstream-t3code".locked.rev') - test "$submodule_rev" = "$input_rev" - - name: build run: nix build diff --git a/.github/workflows/nix-pnpm-deps.yml b/.github/workflows/nix-pnpm-deps.yml index af162a3..6a292af 100644 --- a/.github/workflows/nix-pnpm-deps.yml +++ b/.github/workflows/nix-pnpm-deps.yml @@ -13,7 +13,7 @@ concurrency: jobs: check: - name: check pnpm dependency hash + name: check nix package inputs runs-on: ubuntu-latest timeout-minutes: 30 steps: @@ -23,9 +23,16 @@ jobs: uses: cachix/install-nix-action@v31 - id: update - name: calculate pnpm dependency hash + name: calculate nix package fixes shell: bash run: | + submodule_rev=$(git ls-tree HEAD upstream-t3code | awk '{ print $3 }') + sed -i -E \ + "s#github:pingdotgg/t3code/[0-9a-f]{40}#github:pingdotgg/t3code/$submodule_rev#" \ + flake.nix + grep -Fq "url = \"github:pingdotgg/t3code/$submodule_rev\";" flake.nix + nix flake update upstream-t3code + nix run github:Mic92/nix-update/cf68051e7b7e08de6c707484cdcd1a53feb48e05 -- \ t3code-cli \ --flake \ @@ -33,15 +40,15 @@ jobs: --override-filename nix/package.nix \ --build - if git diff --quiet -- nix/package.nix; then + if git diff --quiet -- flake.nix flake.lock nix/package.nix; then echo "changed=false" >> "$GITHUB_OUTPUT" else echo "changed=true" >> "$GITHUB_OUTPUT" { - echo 'The Nix pnpm dependency hash is stale. Apply this change to the pull request:' + echo 'The Nix source pin or pnpm dependency hash is stale. Apply this change to the pull request:' echo echo '```diff' - git diff -- nix/package.nix + git diff -- flake.nix flake.lock nix/package.nix echo '```' } > "$RUNNER_TEMP/nix-pnpm-dependency-hash.md" fi @@ -63,5 +70,5 @@ jobs: - name: fail on dependency hash drift if: steps.update.outputs.changed == 'true' run: | - echo '::error::The Nix pnpm dependency hash is stale. Apply the change from the pull request comment.' + echo '::error::The Nix package inputs are stale. Apply the change from the pull request comment.' exit 1 diff --git a/flake.lock b/flake.lock index ede16a5..b78a9e4 100644 --- a/flake.lock +++ b/flake.lock @@ -16,9 +16,26 @@ "type": "github" } }, + "nixpkgs-darwin": { + "locked": { + "lastModified": 1786527240, + "narHash": "sha256-OLtJPnSXcRy79Rf7BhYaMeXAVF625FpLcd3+Svq641Y=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "e0c84f9d0ad137f076dc957494f5b39885597d4f", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixpkgs-26.05-darwin", + "repo": "nixpkgs", + "type": "github" + } + }, "root": { "inputs": { "nixpkgs": "nixpkgs", + "nixpkgs-darwin": "nixpkgs-darwin", "upstream-t3code": "upstream-t3code" } }, diff --git a/flake.nix b/flake.nix index 5f02424..b15ce7a 100644 --- a/flake.nix +++ b/flake.nix @@ -3,6 +3,7 @@ inputs = { nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; + nixpkgs-darwin.url = "github:NixOS/nixpkgs/nixpkgs-26.05-darwin"; upstream-t3code = { url = "github:pingdotgg/t3code/3b72d17cbca691f0b64e6d4a10c9e349f42873a5"; @@ -13,23 +14,41 @@ outputs = { nixpkgs, + nixpkgs-darwin, self, upstream-t3code, }: let - system = "x86_64-linux"; - pkgs = import nixpkgs { inherit system; }; + systems = [ + "x86_64-linux" + "aarch64-linux" + "x86_64-darwin" + "aarch64-darwin" + ]; + nixpkgsFor = system: if system == "x86_64-darwin" then nixpkgs-darwin else nixpkgs; in { - packages.${system} = rec { - t3code-cli = pkgs.callPackage ./nix/package.nix { - src = self; - upstreamSrc = upstream-t3code; - }; + packages = nixpkgs.lib.genAttrs systems ( + system: + let + pkgs = import (nixpkgsFor system) { inherit system; }; + in + rec { + t3code-cli = pkgs.callPackage ./nix/package.nix { + src = self; + upstreamSrc = upstream-t3code; + }; - default = t3code-cli; - }; + default = t3code-cli; + } + ); - formatter.${system} = pkgs.nixfmt; + formatter = nixpkgs.lib.genAttrs systems ( + system: + let + pkgs = import (nixpkgsFor system) { inherit system; }; + in + pkgs.nixfmt + ); }; } diff --git a/nix/package.nix b/nix/package.nix index b8e9908..f18ec75 100644 --- a/nix/package.nix +++ b/nix/package.nix @@ -41,16 +41,16 @@ stdenv.mkDerivation (finalAttrs: { }; nativeBuildInputs = [ - autoPatchelfHook installShellFiles makeWrapper nodejs pnpm pnpmConfigHook writableTmpDirAsHomeHook - ]; + ] + ++ lib.optional stdenv.hostPlatform.isLinux autoPatchelfHook; - buildInputs = [ libsecret ]; + buildInputs = lib.optional stdenv.hostPlatform.isLinux libsecret; noAuditTmpdir = true; SSL_CERT_FILE = "${cacert}/etc/ssl/certs/ca-bundle.crt"; @@ -83,6 +83,11 @@ stdenv.mkDerivation (finalAttrs: { homepage = "https://github.com/tarik02-org/t3code-cli"; license = lib.licenses.mit; mainProgram = "t3cli"; - platforms = [ "x86_64-linux" ]; + platforms = [ + "x86_64-linux" + "aarch64-linux" + "x86_64-darwin" + "aarch64-darwin" + ]; }; }) From 958c35b7a91d37fe1e9a5a8d3292598f842c5274 Mon Sep 17 00:00:00 2001 From: Tarik02 Date: Sun, 16 Aug 2026 03:02:51 +0300 Subject: [PATCH 4/4] docs: move nix installation down --- README.md | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 8a7aa11..7b2b2ba 100644 --- a/README.md +++ b/README.md @@ -10,12 +10,6 @@ npm install --global t3code-cli This installs the `t3cli` command globally. -On NixOS or another system with Nix installed: - -```sh -nix profile install github:tarik02-org/t3code-cli -``` - ## Quick Start ```sh @@ -45,6 +39,14 @@ Install it with: npx skills add tarik02-org/t3code-cli ``` +## Nix + +On NixOS or another system with Nix installed: + +```sh +nix profile install github:tarik02-org/t3code-cli +``` + ## Authentication `t3cli` stores multiple named auth environments in `~/.config/t3cli/config.json` (or `$XDG_CONFIG_HOME/t3cli/config.json`). Tokens are encrypted at rest with AES-256-GCM; the master key is stored in the OS keyring when available, otherwise in `~/.config/t3cli/key`.