Merge branch 'master' of gitbox.bradina.localdomain:branderson/dotfiles

This commit is contained in:
2026-07-14 18:08:06 -07:00
11 changed files with 308 additions and 18 deletions
+1
View File
@@ -12,3 +12,4 @@ config/icons
config/VSCodium
dependencies/dotfiles-local
packages/*.local
.claude
Executable
+209
View File
@@ -0,0 +1,209 @@
#!/bin/bash
set -euo pipefail
GITEA_TOKEN_FILE="${GITEA_TOKEN_FILE:-$HOME/.config/gitea/token}"
function usage() {
echo "Usage: gitea-pr create [--title TITLE] [--body BODY] [--base BRANCH]"
echo " gitea-pr comments PR_NUMBER"
echo " gitea-pr merge PR_NUMBER"
echo ""
echo "Requires GITEA_SSH_HOST and GITEA_API set in the environment (e.g. in" \
"your shell profile), and a token at $GITEA_TOKEN_FILE (or GITEA_TOKEN" \
"set for this invocation only)."
}
function require_config() {
if [ -z "${GITEA_SSH_HOST:-}" ]; then
echo "GITEA_SSH_HOST is not set. Add 'export GITEA_SSH_HOST=<gitea ssh hostname>'" \
"(e.g. gitbox.bradina.localdomain) to ~/.profile.local" >&2
exit 1
fi
if [ -z "${GITEA_API:-}" ]; then
echo "GITEA_API is not set. Add 'export GITEA_API=https://<gitea host>/api/v1'" \
"(e.g. https://gitbox.branderson.io/api/v1) to ~/.profile.local" >&2
exit 1
fi
}
function require_token() {
if [ -z "${GITEA_TOKEN:-}" ] && [ -f "$GITEA_TOKEN_FILE" ]; then
GITEA_TOKEN=$(<"$GITEA_TOKEN_FILE")
fi
if [ -z "${GITEA_TOKEN:-}" ]; then
echo "No Gitea token found. Generate one in the Gitea web UI" \
"(Settings > Applications, scopes write:repository + write:issue)" \
"and save it to $GITEA_TOKEN_FILE (chmod 600), or set GITEA_TOKEN" \
"in the environment for this invocation only." >&2
exit 1
fi
}
function gitea_owner_repo() {
local remote_url host owner_repo
remote_url=$(git remote get-url origin 2>/dev/null) || {
echo "No 'origin' remote found in this repository" >&2
exit 1
}
host=$(echo "$remote_url" | sed -E 's#^[^@]+@([^:]+):.*#\1#')
if [ "$host" != "$GITEA_SSH_HOST" ]; then
echo "origin remote ($remote_url) is not on $GITEA_SSH_HOST; gitea-pr only supports this host" >&2
exit 1
fi
owner_repo=$(echo "$remote_url" | sed -E 's#^[^@]+@[^:]+:##; s#\.git$##')
echo "$owner_repo"
}
if [ "$#" -lt 1 ]; then
usage
exit 1
fi
subcommand="$1"
shift
case "$subcommand" in
create|comments|merge)
require_config
require_token
owner_repo=$(gitea_owner_repo)
owner="${owner_repo%%/*}"
repo="${owner_repo##*/}"
;;
*)
usage
exit 1
;;
esac
case "$subcommand" in
create)
title=""
body=""
base=""
while [ "$#" -gt 0 ]; do
case "$1" in
--title) title="$2"; shift 2 ;;
--body) body="$2"; shift 2 ;;
--base) base="$2"; shift 2 ;;
*) echo "Unknown option: $1" >&2; usage; exit 1 ;;
esac
done
body="**Summary generated by LLM agent**
${body}"
current_branch=$(git rev-parse --abbrev-ref HEAD)
if [ "$current_branch" = "HEAD" ]; then
echo "Detached HEAD: check out the feature branch before creating a PR" >&2
exit 1
fi
if [ -z "$base" ]; then
base=$(git symbolic-ref refs/remotes/origin/HEAD 2>/dev/null | sed 's#refs/remotes/origin/##' || true)
base="${base:-main}"
fi
response=$(curl -sS -w '\n%{http_code}' "${GITEA_API}/repos/${owner}/${repo}/pulls?state=open" \
-H "Authorization: token ${GITEA_TOKEN}")
http_code=$(echo "$response" | tail -1)
json=$(echo "$response" | sed '$d')
if [ "$http_code" -lt 200 ] || [ "$http_code" -ge 300 ]; then
echo "Failed to check for an existing PR (HTTP $http_code):" >&2
echo "$json" >&2
exit 1
fi
existing_url=$(echo "$json" | jq -r --arg head "$current_branch" --arg base "$base" \
'.[] | select(.head.ref == $head and .base.ref == $base) | .html_url' | head -1)
if [ -n "$existing_url" ]; then
echo "$existing_url"
exit 0
fi
if [ -z "$title" ]; then
title="$current_branch"
fi
response=$(curl -sS -w '\n%{http_code}' -X POST "${GITEA_API}/repos/${owner}/${repo}/pulls" \
-H "Authorization: token ${GITEA_TOKEN}" \
-H "Content-Type: application/json" \
-d "$(jq -n --arg head "$current_branch" --arg base "$base" --arg title "$title" --arg body "$body" \
'{head: $head, base: $base, title: $title, body: $body}')")
http_code=$(echo "$response" | tail -1)
json=$(echo "$response" | sed '$d')
if [ "$http_code" -lt 200 ] || [ "$http_code" -ge 300 ]; then
echo "Failed to create PR (HTTP $http_code):" >&2
echo "$json" >&2
exit 1
fi
echo "$json" | jq -r '.html_url'
;;
comments)
pr_number="${1:-}"
if [ -z "$pr_number" ]; then
echo "Usage: gitea-pr comments PR_NUMBER" >&2
exit 1
fi
echo "=== Issue comments ==="
response=$(curl -sS -w '\n%{http_code}' "${GITEA_API}/repos/${owner}/${repo}/issues/${pr_number}/comments" \
-H "Authorization: token ${GITEA_TOKEN}")
http_code=$(echo "$response" | tail -1)
json=$(echo "$response" | sed '$d')
if [ "$http_code" -lt 200 ] || [ "$http_code" -ge 300 ]; then
echo "Failed to fetch issue comments (HTTP $http_code):" >&2
echo "$json" >&2
exit 1
fi
echo "$json" | jq -r '.[] | "[\(.user.login) @ \(.created_at)]\n\(.body)\n"'
echo "=== Reviews ==="
response=$(curl -sS -w '\n%{http_code}' "${GITEA_API}/repos/${owner}/${repo}/pulls/${pr_number}/reviews" \
-H "Authorization: token ${GITEA_TOKEN}")
http_code=$(echo "$response" | tail -1)
reviews_json=$(echo "$response" | sed '$d')
if [ "$http_code" -lt 200 ] || [ "$http_code" -ge 300 ]; then
echo "Failed to fetch reviews (HTTP $http_code):" >&2
echo "$reviews_json" >&2
exit 1
fi
echo "$reviews_json" | jq -r '.[] | select(.body != "") | "[\(.user.login) @ \(.submitted_at) — \(.state)]\n\(.body)\n"'
echo "=== Inline review comments ==="
review_ids=$(echo "$reviews_json" | jq -r '.[] | select(.comments_count > 0) | .id')
for review_id in $review_ids; do
response=$(curl -sS -w '\n%{http_code}' \
"${GITEA_API}/repos/${owner}/${repo}/pulls/${pr_number}/reviews/${review_id}/comments" \
-H "Authorization: token ${GITEA_TOKEN}")
http_code=$(echo "$response" | tail -1)
json=$(echo "$response" | sed '$d')
if [ "$http_code" -lt 200 ] || [ "$http_code" -ge 300 ]; then
echo "Failed to fetch comments for review ${review_id} (HTTP $http_code):" >&2
echo "$json" >&2
exit 1
fi
echo "$json" | jq -r '.[] | "[\(.user.login) @ \(.path):\(.line // "?")]\n\(.body)\n"'
done
;;
merge)
pr_number="${1:-}"
if [ -z "$pr_number" ]; then
echo "Usage: gitea-pr merge PR_NUMBER" >&2
exit 1
fi
response=$(curl -sS -w '\n%{http_code}' -X POST "${GITEA_API}/repos/${owner}/${repo}/pulls/${pr_number}/merge" \
-H "Authorization: token ${GITEA_TOKEN}" \
-H "Content-Type: application/json" \
-d '{"Do": "merge"}')
http_code=$(echo "$response" | tail -1)
json=$(echo "$response" | sed '$d')
if [ "$http_code" -lt 200 ] || [ "$http_code" -ge 300 ]; then
echo "Failed to merge PR #${pr_number} (HTTP $http_code):" >&2
echo "$json" >&2
exit 1
fi
echo "Merged PR #${pr_number}"
;;
esac
+30
View File
@@ -0,0 +1,30 @@
{
"name": "Gruvbox Dark",
"base": "dark",
"overrides": {
"claude": "#d3869b",
"text": "#ebdbb2",
"inactive": "#665c54",
"subtle": "#7c6f64",
"muted": "#928374",
"success": "#b8bb26",
"error": "#fb4934",
"warning": "#fabd2f",
"link": "#83a598",
"highlight": "#fabd2f",
"selection": "#504945",
"diffAdded": "#b8bb26",
"diffRemoved": "#fb4934",
"promptBorder": "#504945",
"bashBorder": "#504945",
"mcpBorder": "#504945",
"planMode": "#83a598",
"spinner": "#d3869b",
"header": "#8ec07c",
"separator": "#3c3836",
"badge": "#fe8019",
"permission": "#fe8019",
"suggestion": "#8ec07c",
"fastMode": "#fabd2f"
}
}
+18 -13
View File
@@ -30,23 +30,28 @@ if vim.fn.has("nvim-0.12") == 0 then
os.exit(1)
end
local asset = "nvim-linux-" .. asset_arch
vim.api.nvim_echo({
local sysname = ((vim.loop.os_uname() or {}).sysname or ""):lower()
local is_mac = sysname == "darwin"
local asset = (is_mac and "nvim-macos-" or "nvim-linux-") .. asset_arch
local lines = {
{ "This config requires Neovim 0.12.0 or newer (found " .. current .. ", " .. machine .. ").\n\n", "ErrorMsg" },
{ "Install a current build -- locally, no root needed:\n", "None" },
{ "Install a current build into ~/.local (no root needed):\n", "None" },
{ " curl -LO https://github.com/neovim/neovim/releases/latest/download/" .. asset .. ".tar.gz\n", "None" },
{ " tar xzf " .. asset .. ".tar.gz\n", "None" },
{ " rm -rf ~/.local/nvim && mv " .. asset .. " ~/.local/nvim\n", "None" },
{ " mkdir -p ~/.local/bin && ln -sf ~/.local/nvim/bin/nvim ~/.local/bin/nvim\n", "None" },
{ " mkdir -p ~/.local && tar xzf " .. asset .. ".tar.gz --strip-components=1 -C ~/.local\n", "None" },
{ " rm " .. asset .. ".tar.gz\n", "None" },
{ " # then make sure ~/.local/bin is on your PATH\n\n", "Comment" },
{ "Or system-wide (needs root):\n", "None" },
{ " curl -LO https://github.com/neovim/neovim/releases/latest/download/" .. asset .. ".tar.gz\n", "None" },
{ " tar xzf " .. asset .. ".tar.gz\n", "None" },
{ " sudo rm -rf /opt/nvim && sudo mv " .. asset .. " /opt/nvim\n", "None" },
{ " sudo ln -sf /opt/nvim/bin/nvim /usr/local/bin/nvim\n", "None" },
{ " rm " .. asset .. ".tar.gz\n", "None" },
}, true, {})
}
if not is_mac then
vim.list_extend(lines, {
{ "Or system-wide (needs root):\n", "None" },
{ " curl -LO https://github.com/neovim/neovim/releases/latest/download/" .. asset .. ".tar.gz\n", "None" },
{ " sudo mkdir -p /opt/nvim && sudo tar xzf " .. asset .. ".tar.gz --strip-components=1 -C /opt/nvim\n", "None" },
{ " sudo ln -sf /opt/nvim/bin/nvim /usr/local/bin/nvim\n", "None" },
{ " rm " .. asset .. ".tar.gz\n", "None" },
})
end
vim.api.nvim_echo(lines, true, {})
vim.fn.getchar()
os.exit(1)
end
+1 -1
View File
@@ -11,7 +11,7 @@
"gitsigns.nvim": { "branch": "main", "commit": "eb60cc7b94c46005237fd34170d76f3a089a90aa" },
"gruvbox.nvim": { "branch": "main", "commit": "154eb5ff5b96d0641307113fa385eaf0d36d9796" },
"indent-blankline.nvim": { "branch": "master", "commit": "d28a3f70721c79e3c5f6693057ae929f3d9c0a03" },
"lazy.nvim": { "branch": "main", "commit": "306a05526ada86a7b30af95c5cc81ffba93fef97" },
"lazy.nvim": { "branch": "main", "commit": "85c7ff3711b730b4030d03144f6db6375044ae82" },
"lualine.nvim": { "branch": "master", "commit": "221ce6b2d999187044529f49da6554a92f740a96" },
"markdown-preview.nvim": { "branch": "master", "commit": "a923f5fc5ba36a3b17e289dc35dc17f66d0548ee" },
"mason-lspconfig.nvim": { "branch": "main", "commit": "47059d71b42d74b0a1e9f61c1d99d301039c3b5b" },
+10 -2
View File
@@ -1,11 +1,19 @@
local servers = { "basedpyright", "ruff" }
local npm_servers = { "ts_ls", "svelte", "html", "cssls", "bashls" }
if vim.fn.executable("npm") == 1 then
local function has_public_npm()
if vim.fn.executable("npm") ~= 1 then
return false
end
local result = vim.fn.system("npm config get registry")
return result:find("registry.npmjs.org") ~= nil
end
if has_public_npm() then
vim.list_extend(servers, npm_servers)
else
vim.notify(
"npm not found: skipping LSP servers " .. table.concat(npm_servers, ", "),
"public npm registry unavailable: skipping LSP servers " .. table.concat(npm_servers, ", "),
vim.log.levels.WARN
)
end
+19 -1
View File
@@ -30,8 +30,26 @@ return {
if vim.fn.executable("tree-sitter") == 1 then
require("nvim-treesitter").install(parsers)
else
local uname = vim.uv.os_uname()
local sysname = (uname.sysname or ""):lower()
local machine = uname.machine or ""
local is_mac = sysname == "darwin"
local install_cmd
if is_mac then
local arch_part = ({ x86_64 = "x64", amd64 = "x64", aarch64 = "arm64", arm64 = "arm64" })[machine] or "x64"
local asset = "tree-sitter-macos-" .. arch_part
install_cmd = " curl -L https://github.com/tree-sitter/tree-sitter/releases/latest/download/"
.. asset
.. ".gz | gunzip > ~/.local/bin/tree-sitter && chmod +x ~/.local/bin/tree-sitter"
else
install_cmd = " cargo install --root ~/.local tree-sitter-cli"
end
vim.notify(
"tree-sitter CLI not found: skipping install of missing parsers",
"tree-sitter CLI not found: skipping install of missing parsers.\n"
.. "Install into ~/.local/bin:\n"
.. install_cmd,
vim.log.levels.WARN
)
end
+13
View File
@@ -186,6 +186,19 @@ fi
# Tool options
export SHELLCHECK_OPTS="-e SC2029 -e SC2155"
# Limit claude-code: cap CPU/memory/process count so a runaway subprocess
# (or its children, e.g. from the Bash tool) can't overheat the laptop.
# Adjust the -p values if these limits are ever too tight.
if program_installed claude && program_installed systemd-run; then
claude() {
systemd-run --user --scope --quiet \
-p CPUQuota=50% \
-p MemoryMax=6G \
-p TasksMax=512 \
-- command claude "$@"
}
fi
# Syntax highlighting
if [ -f /usr/share/zsh-syntax-highlighting/zsh-syntax-highlighting.zsh ]; then
source /usr/share/zsh-syntax-highlighting/zsh-syntax-highlighting.zsh
+1
View File
@@ -40,6 +40,7 @@
# mkdir ~/dotfiles/config/themes ~/dotfiles/config/icons
# cp -r ~/dotfiles/dependencies/Gruvbox-GTK-Theme/themes/* ~/dotfiles/config/themes
# cp -r ~/dotfiles/dependencies/Gruvbox-GTK-Theme/icons/* ~/dotfiles/config/icons
# cp -r ~/dotfiles/config/claude/themes/* ~/.claude/themes/
# TODO: Customize lightdm
# Move some wallpaper assets into this repo
+2
View File
@@ -11,6 +11,8 @@ npm
tree-sitter-cli
ripgrep
shellcheck
bubblewrap
socat
# Agent sandboxing
bubblewrap
+4 -1
View File
@@ -1,4 +1,4 @@
nvim
neovim
coreutils
tree
ag
@@ -10,3 +10,6 @@ fortune
tmuxinator
python
pipx
tree-sitter-cli
ripgrep
shellcheck