From 79aa58cfc2f539edef7bbf866d0a03593fd0c45e Mon Sep 17 00:00:00 2001 From: Jakob Ankarhem Date: Sat, 5 Sep 2026 14:17:22 +0200 Subject: [PATCH] feat(colemak): Install Colemak keylayout via nix (#79) * feat(colemak): Install Colemak keylayout via nix Replace the manual curl step in the README with a home-manager activation script that copies a hash-pinned fetchurl of the layout into ~/Library/Keyboard Layouts. Copied rather than symlinked since macOS does not reliably pick up symlinks there. * chore(git): Fetch from github, push to knot and github origin now fetches only from github (where PRs merge) while keeping the dual push to knot and github, so pulls are always current and a plain git push after git pull syncs both. Also adds the missing fetch refspec - without it git fetch origin never populated origin/* remote-tracking refs, so pull relied on FETCH_HEAD. * fix(ci): Pass github_token to claude review action claude-code-action unconditionally exchanges an OIDC token with api.anthropic.com for a GitHub App token unless a github_token input is provided. The job deliberately withholds id-token: write, so every run failed at setup. Pass the job's comment-only GITHUB_TOKEN instead; comments post as github-actions[bot] rather than the Claude app. * fix(ci): Grant claude review id-token instead of token override claude-code-action exchanges an OIDC token with api.anthropic.com for an app token (comments post as the Claude app). Fine to grant id-token: write since the job only runs for trusted authors (OWNER/MEMBER/COLLABORATOR). Reverts the github_token override. * fix(colemak): Replace symlink target instead of writing through it rm -f before cp so a pre-existing symlink at the layout path is replaced rather than written through (BSD cp has no --remove-destination). Also corrects the claude-review containment comment: the agent's credential is the app installation token, which the job permissions block does not scope. --- .gitconfig | 4 +++- .github/workflows/review.yml | 14 +++++++++----- README.md | 11 +++++------ modules/colemak.nix | 26 ++++++++++++++++++++++++++ 4 files changed, 43 insertions(+), 12 deletions(-) diff --git a/.gitconfig b/.gitconfig index bd77cf6..3433231 100644 --- a/.gitconfig +++ b/.gitconfig @@ -1,6 +1,8 @@ # git remote remove origin # git config --local include.path ../.gitconfig [remote "origin"] - url = git@knot.ankarhem.dev:ankarhem.dev/nix-config + # fetch from github (canonical, has PR merges), push to both knot and github + url = git@github.com:ankarhem/nix-config + fetch = +refs/heads/*:refs/remotes/origin/* pushurl = git@knot.ankarhem.dev:ankarhem.dev/nix-config pushurl = git@github.com:ankarhem/nix-config diff --git a/.github/workflows/review.yml b/.github/workflows/review.yml index 13f433e..0fe8177 100644 --- a/.github/workflows/review.yml +++ b/.github/workflows/review.yml @@ -73,12 +73,14 @@ jobs: !startsWith(github.event.pull_request.head.ref, 'auto/') && contains(fromJSON('["OWNER", "MEMBER", "COLLABORATOR"]'), github.event.pull_request.author_association || '') runs-on: ubuntu-latest - # Comment-only access, deliberately without id-token: write — a yolo agent - # over PR content should not be able to mint OIDC tokens. permissions: contents: read pull-requests: write issues: write + # claude-code-action exchanges an OIDC token with api.anthropic.com for + # an app token (posts comments as the Claude app). OK to grant: the job + # only runs for trusted authors (OWNER/MEMBER/COLLABORATOR). + id-token: write steps: - name: Checkout repository uses: actions/checkout@v7 @@ -99,7 +101,9 @@ jobs: plugin_marketplaces: "https://github.com/anthropics/claude-code.git" plugins: "code-review@claude-code-plugins" prompt: "/code-review:code-review --comment" - # Yolo: skip Claude's internal tool-approval prompts. Actual GitHub - # access is capped by this job's permissions (comment only, no - # contents write, no id-token). + # Yolo: skip Claude's internal tool-approval prompts. Containment is + # the trusted-author gate plus the Claude app installation's + # permissions — the agent's GitHub credential is the app installation + # token from the OIDC exchange, which the job's permissions block + # does not cap (that only scopes GITHUB_TOKEN, unused here). claude_args: '--dangerously-skip-permissions' diff --git a/README.md b/README.md index a79bd78..5228471 100644 --- a/README.md +++ b/README.md @@ -8,12 +8,11 @@ The user account / home map folder must be named `ankarhem`. 1. Install Rosetta 2 `softwareupdate --install-rosetta --agree-to-license` 2. Install nix using [determinate systems](https://github.com/DeterminateSystems/nix-installer). ❗NOT the determinate distribution -3. Install colemak `curl -O --output-dir ~/Library/Keyboard\ Layouts/ https://colemak.com/pub/mac/Colemak.keylayout` -4. Install git `xcode-select --install` -5. Replace default colemak System Preferences > Keyboard > Input Sources -6. Add access token to `~/.config/nix/nix.conf`. `mkdir -p ~/.config/nix && echo "access-tokens = github.com=ghp_*****" > ~/.config/nix/nix.conf` -7. Bootstrap darwin build `nix run --extra-experimental-features flakes --extra-experimental-features nix-command nix-darwin -- switch --flake .#mbp` -8. Change shell with `chsh -s /run/current-system/sw/bin/fish` +3. Install git `xcode-select --install` +4. Add access token to `~/.config/nix/nix.conf`. `mkdir -p ~/.config/nix && echo "access-tokens = github.com=ghp_*****" > ~/.config/nix/nix.conf` +5. Bootstrap darwin build `nix run --extra-experimental-features flakes --extra-experimental-features nix-command nix-darwin -- switch --flake .#mbp` +6. Replace default colemak System Preferences > Keyboard > Input Sources (the layout is installed by the flake, log out and back in for it to show up) +7. Change shell with `chsh -s /run/current-system/sw/bin/fish` ### Important diff --git a/modules/colemak.nix b/modules/colemak.nix index 12d7fde..529f4d9 100644 --- a/modules/colemak.nix +++ b/modules/colemak.nix @@ -25,6 +25,8 @@ }; flake.modules.darwin.colemak = { + home-manager.sharedModules = [ inputs.self.modules.homeManager.colemak ]; + system.keyboard.enableKeyMapping = true; system.keyboard.remapCapsLockToEscape = true; system.keyboard.userKeyMapping = [ @@ -35,4 +37,28 @@ } ]; }; + + flake.modules.homeManager.colemak = + { pkgs, lib, ... }: + let + colemakKeylayout = pkgs.fetchurl { + url = "https://colemak.com/pub/mac/Colemak.keylayout"; + hash = "sha256-dASDHgkNCm8aPNNg7pvUhJh7sx5aCExI8Ea8dim3ZZU="; + }; + in + { + # macOS only picks up real files in ~/Library/Keyboard Layouts (not + # symlinks), so copy the pinned keylayout instead of linking it. + home.activation.colemakLayout = lib.hm.dag.entryAfter [ "writeBoundary" ] '' + layout="$HOME/Library/Keyboard Layouts/Colemak.keylayout" + if ! cmp -s "${colemakKeylayout}" "$layout"; then + $DRY_RUN_CMD mkdir -p "$(dirname "$layout")" + # rm first so a pre-existing symlink target isn't written through + # (BSD cp has no --remove-destination), and read-only files are replaced + $DRY_RUN_CMD rm -f "$layout" + $DRY_RUN_CMD cp "${colemakKeylayout}" "$layout" + $DRY_RUN_CMD chmod 644 "$layout" + fi + ''; + }; } -- 2.54.0