Shun's org notes
Welcome! I'm Shun, a novice software engineer. Here I publish my Org notes to share cool things I discover along the way.
I'm a daily driver of Nix & Emacs; consequently, most of my notes revolve around these two ecosystems.
Find me at: SourceHut, GitHub, or me@shunueda.org.
(macOS) Suppress annoying `xcrun` popup
macOS puts a stub at /usr/bin/python3 (and friends) that shells out to xcrun to find the real binary. If Xcode Command Line Tools aren't installed, this triggers an annoying GUI popup - which I really don't want since I manage everything through Nix devShells and avoid impure global installs.
I looked at the manpage, and found that xcrun respects the envvar DEVELOPER_DIR, and wondered if pointing this envvar to a stub xcrun could suppress the popup.
let
# Suppress annoying `xcrun` popups
stubDeveloperDir = "${pkgs.linkFarm "stub-developer-dir" [
{
name = "usr/bin/xcrun";
path = pkgs.writeShellScript "stub-xcrun" ''
>&2 echo "$1: command not found"
exit 127
'';
}
]}";
in
...
environment = {
shells = [ pkgs.bash ];
variables = {
DEVELOPER_DIR = stubDeveloperDir;
};
};
launchd.user.envVariables = {
DEVELOPER_DIR = stubDeveloperDir;
};
And voila!
$ python3 python3: command not found
ghq 🤝 project.el
I use ghq for remote repository management, and I make it integrate with project.el's project switcher (C-x p p) like this:
(use-package project :config ;; Populate the project switcher list from `ghq` (dolist (project (split-string (shell-command-to-string "ghq list --full-path") "\n" t)) (project--remember-dir (file-name-as-directory project)))
Learning Emacs & Nix
Very helpful tutorials that helped me get started:
TIL: Sieve (mail filtering language)
I use Fastmail for my emails, and today I found that you can set a custom mail filtering rule using language called Sieve.
Apparently there is also a protocol to manage Sieve script, but Fastmail doesn't support it :( I thought of writing a Just script or a Terraform provider, but too bad.
However: Fastmail supports JMAP API (!) so if I really wanted to, I can use SieveScript/set - but I'm too lazy. JMAP is crazytown.
Side note, Fastmail is awesome. Highly recommend.
(macOS) Manage display resolution via CLI
There seem to be no official way to change display resolution from the cli on macOS, but p00ya/displaymode worked really well for me. I've fixed few stuffs & Nixified it, so feel free to try it out:
nix run github:shunueda/monorepo#displaymode
I put this in Home Manager's activation:
home = {
activation = {
# Darwin-specific activation script
darwin = lib.mkIf pkgs.stdenv.hostPlatform.isDarwin (
lib.hm.dag.entryAfter [ "writeBoundary" ] ''
# Set display resolution, mine is 14-inch.
${lib.getExe pkgs.displaymode} t 1800 1169
''
);
};
};
p00ya/displaymode is licensed under the Apache-2.0 license.
Using S3 as a Nix cache
Cachix is too expensive so I use S3 (well, technically Cloudflare R2 because it's S3-compatible and egress-free) as a Nix cache, with a following script:
#!/usr/bin/env bash set -euo pipefail signkey=$(mktemp) trap 'rm -f $signkey' EXIT echo "$NIX_CACHE_SIGNING_KEY" >"$signkey" nix store sign --key-file "$signkey" --all nix path-info --all | grep -v -E '\.(drv|check|drv\.chroot|lock)$' | xargs -n 50 -P 8 sudo -i nix copy --to "$NIX_CACHE_SUBSTITUTER"
Intended for use in CI. Signs everything is the store, filters things we're interested in, and nix copy (in chunks to avoid command being too long) to the backend. Pretty straight forward but works well for me.
Side note: if you're using S3 as a Nix store, check this out: anteriorcore/terraform-aws-s3-nix-lru-cache. It uses S3 access logs to GC. Pretty neat.
Does anyone find Google's AI search useful?
I've never found Google's "AI Overview" particularly helpful. It's been more distracting than useful, and worse still, there seems to be no way to turn it off.
Since then, I've switched to DuckDuckGo's “No AI” search at https://noai.duckduckgo.com/, and that has worked well for me.
Here's how to configure it in Home Manager. I use Librewolf, but this should work the same way in Firefox:
librewolf = {
enable = true;
policies = {
GenerativeAI.Enabled = false; # Bonus: turn off other Firefox AI features
};
profiles.<my-profile> = {
search = {
force = true;
default = "ddg-noai";
engines = {
"ddg-noai" = {
urls = [
{
template = "https://noai.duckduckgo.com/";
params = [ (lib.nameValuePair "q" "{searchTerms}") ];
}
];
definedAliases = [ "@noai" ];
};
};
};
...
};
...
};
histsync: sync shell history with pass
Atuin is super cool: it let's you sync shell history across machine, but few issues for me:
- Requires a server. I distrust managed public servers and don't want to host my own.
- Heavy LLM usage for development. I have nothing against LLMs but I try to avoid them.
I use password-store (pass), so why don't I leverage this:
bash = {
enable = true;
# ...
initExtra = ''
histsync() {
# Append currently history to HISTFILE
history -a
# Entry name in pass - I separate history by host
local passentry="ShellHistories/$HOSTNAME"
# Merge HISTFILE and pass entries, remove duplicates, update HISTFILE
(cat "$HISTFILE" 2>/dev/null; pass show "$passentry" 2>/dev/null) |
awk '!a[$0]++' |
${pkgs.moreutils}/bin/sponge "$HISTFILE"
# Update pass entry with the merged history
<"$HISTFILE" pass insert -mf "$passentry"
# Reload history for current session
history -c
history -r
}
# ...
'';
};
Note: the merge logic will not work if you use timestamps.
password-store: nerd snipe quagmire
I've been using Apple's Passwords.app, but password-store (pass) seemed very cool, and since I already did the heavy-lifting of setting up the YubiKey, I decided to migrate.
Another reason that convinced me: Emacs supports pass as an auth-source natively! Here is a sample:
(auth-source-pass-enable)
(auth-source-pass-get 'secret "foo/bar")
You can also use password-store.el to interact with pass via Emacs.
Setting up my YubiKey
To manage my GPG key, I set up my YubiKey (5C NFC). This guide was extremely helpful: https://github.com/drduh/YubiKey-Guide.
One note: if you're using ed25519 for your signing key, make sure to set KEY_TYPE=cv25519 before creating the encryption (E) subkey - GPG will complain otherwise.