diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index f49f833..e07c981 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -12,6 +12,31 @@ concurrency: cancel-in-progress: true jobs: + nix: + 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: 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..6a292af --- /dev/null +++ b/.github/workflows/nix-pnpm-deps.yml @@ -0,0 +1,74 @@ +name: nix pnpm dependency hash + +on: + pull_request: + +permissions: + contents: read + pull-requests: write + +concurrency: + group: nix-pnpm-dependency-hash-${{ github.event.pull_request.number }} + cancel-in-progress: true + +jobs: + check: + name: check nix package inputs + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - uses: actions/checkout@v6.0.3 + + - name: install nix + uses: cachix/install-nix-action@v31 + + - id: update + 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 \ + --version=skip \ + --override-filename nix/package.nix \ + --build + + 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 source pin or pnpm dependency hash is stale. Apply this change to the pull request:' + echo + echo '```diff' + git diff -- flake.nix flake.lock nix/package.nix + echo '```' + } > "$RUNNER_TEMP/nix-pnpm-dependency-hash.md" + fi + + - name: post proposed hash change + if: steps.update.outputs.changed == 'true' + uses: marocchino/sticky-pull-request-comment@v2 + with: + header: nix-pnpm-dependency-hash + path: ${{ runner.temp }}/nix-pnpm-dependency-hash.md + + - 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' + run: | + echo '::error::The Nix package inputs are stale. Apply the change from the pull request comment.' + exit 1 diff --git a/README.md b/README.md index d96dfca..7b2b2ba 100644 --- a/README.md +++ b/README.md @@ -39,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`. diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..b78a9e4 --- /dev/null +++ b/flake.lock @@ -0,0 +1,62 @@ +{ + "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" + } + }, + "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" + } + }, + "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..b15ce7a --- /dev/null +++ b/flake.nix @@ -0,0 +1,54 @@ +{ + description = "T3 Code CLI"; + + 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"; + flake = false; + }; + }; + + outputs = + { + nixpkgs, + nixpkgs-darwin, + self, + upstream-t3code, + }: + let + 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 = 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; + } + ); + + 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 new file mode 100644 index 0000000..f18ec75 --- /dev/null +++ b/nix/package.nix @@ -0,0 +1,93 @@ +{ + 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 = [ + installShellFiles + makeWrapper + nodejs + pnpm + pnpmConfigHook + writableTmpDirAsHomeHook + ] + ++ lib.optional stdenv.hostPlatform.isLinux autoPatchelfHook; + + buildInputs = lib.optional stdenv.hostPlatform.isLinux 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" + "aarch64-linux" + "x86_64-darwin" + "aarch64-darwin" + ]; + }; +})