Merge branch 'master' of gitbox.bradina.localdomain:branderson/dotfiles
This commit is contained in:
@@ -12,3 +12,4 @@ config/icons
|
||||
config/VSCodium
|
||||
dependencies/dotfiles-local
|
||||
packages/*.local
|
||||
.claude
|
||||
|
||||
Executable
+209
@@ -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
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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" },
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -11,6 +11,8 @@ npm
|
||||
tree-sitter-cli
|
||||
ripgrep
|
||||
shellcheck
|
||||
bubblewrap
|
||||
socat
|
||||
|
||||
# Agent sandboxing
|
||||
bubblewrap
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
nvim
|
||||
neovim
|
||||
coreutils
|
||||
tree
|
||||
ag
|
||||
@@ -10,3 +10,6 @@ fortune
|
||||
tmuxinator
|
||||
python
|
||||
pipx
|
||||
tree-sitter-cli
|
||||
ripgrep
|
||||
shellcheck
|
||||
|
||||
Reference in New Issue
Block a user