diff options
| author | Trey Bastian <hello@treybastian.com> | 2026-04-09 20:59:08 +0100 |
|---|---|---|
| committer | Trey Bastian <hello@treybastian.com> | 2026-04-09 20:59:08 +0100 |
| commit | ed75014df6e75b44ee8a31bcedf53fda63fcba7e (patch) | |
| tree | 2e9924e5881b862eea4371a4064975c6db5722a3 | |
| parent | b0684c87580926fa00c221afcc4461ca812390a0 (diff) | |
added support for a description and testing out claude
| -rw-r--r-- | CLAUDE.md | 38 | ||||
| -rwxr-xr-x | git-shell-commands/new-project | 46 | ||||
| -rwxr-xr-x | git-shell-commands/new-repo | 30 | ||||
| -rwxr-xr-x | git-shell-commands/set-description | 51 |
4 files changed, 149 insertions, 16 deletions
diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..bf1e5df --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,38 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## Overview + +A personal collection of bash scripts for various purposes. Scripts use `set -euo pipefail` and have inline usage documentation. New scripts should be organized into subdirectories by purpose. + +## Current script categories + +### General structure +Each subdirectory groups scripts by deployment target or domain. Scripts are plain bash — no build step. + +### Git server scripts + +### `git-shell-commands/` +Invoked remotely via SSH (e.g. `ssh git@treybastian.com new-project myrepo`). The git shell restricts the user to only these named commands. Scripts run in the context of the home directory of the git user. + +- `new-project` — creates a bare repo; `--public` also writes `public.conf` and registers the repo on the public remote; `--description <desc>` sets the repo description on both servers +- `new-repo` — lower-level command that only creates a bare repo (called by `new-project` to register the mirror); accepts `--description <desc>` +- `make-public` — converts an existing private repo to public by writing `public.conf` and registering the mirror +- `set-description` — sets the description on an existing repo; if `public.conf` exists, also updates the description on the remote +- `no-interactive-login` — blocks interactive shell access for the git user + +### `git-hooks/` +Installed globally on the server via `git config --system core.hooksPath`. When `core.hooksPath` is set, git uses only that directory for hooks — repo-specific hooks are bypassed. This is solved by: + +- `hook-runner` — a passthrough script that checks if a repo-local hook exists at `$(pwd)/hooks/<hook-name>` and runs it. It is **symlinked** to `post-update`, `pre-recieve`, and `update` so those hooks delegate to repo-specific scripts. +- `post-receive` — the main hook; if `public.conf` exists in the repo, it mirror-pushes to the URL inside it, then also delegates to a repo-local `post-receive` hook if present. + +#### Public/private distinction +A repo is considered public if and only if it contains a `public.conf` file at its root. That file holds a single line: the remote URL to mirror to (e.g. `git@treybastian.com:repos/myrepo.git`). The `post-receive` hook reads this file to decide whether to mirror on push. + +#### Deployment +- `git-shell-commands/*` → the git user's `~/git-shell-commands/` directory on the server +- `git-hooks/*` → wherever `core.hooksPath` points (e.g. `/home/git/scripts/git-hooks`) + +Symlink `hook-runner` to the other hook names (`post-update`, `pre-recieve`, `update`) in the hooks directory after deploying. diff --git a/git-shell-commands/new-project b/git-shell-commands/new-project index 3d13e41..46c6e93 100755 --- a/git-shell-commands/new-project +++ b/git-shell-commands/new-project @@ -5,38 +5,52 @@ set -euo pipefail # private repos will just exist on the server # # USAGE: -# ssh git@<host> new-project <name> [--public] +# ssh git@<host> new-project <name> [--public] [--description <desc>] # # EXAMPLES: # ssh git@treybastian.com new-project myrepo # ssh git@treybastian.com new-project myrepo --public +# ssh git@treybastian.com new-project myrepo --public --description "my cool repo" REMOTE_BASE_URL="git@treybastian.com:repos" usage() { - echo "Usage: new-project <name> [--public]" + echo "Usage: new-project <name> [--public] [--description <desc>]" echo "" echo "Examples:" echo " new-project myrepo" echo " new-project myrepo --public" + echo " new-project myrepo --public --description \"my cool repo\"" exit 1 } -if [ "$#" -lt 1 ] || [ "$#" -gt 2 ]; then - echo "Error: expected 1 or 2 arguments, got $#" +if [ "$#" -lt 1 ]; then + echo "Error: expected at least 1 argument, got $#" usage fi PROJECT_NAME="$1" PUBLIC=false +DESCRIPTION="" +shift -if [ "$#" -eq 2 ]; then - if [ "$2" != "--public" ]; then - echo "Error: unknown flag '$2'" - usage - fi - PUBLIC=true -fi +while [ "$#" -gt 0 ]; do + case "$1" in + --public) + PUBLIC=true + shift + ;; + --description) + [ "$#" -ge 2 ] || { echo "Error: --description requires a value"; usage; } + DESCRIPTION="$2" + shift 2 + ;; + *) + echo "Error: unknown flag '$1'" + usage + ;; + esac +done if [[ "$PROJECT_NAME" != *.git ]]; then PROJECT_NAME="${PROJECT_NAME}.git" @@ -49,9 +63,17 @@ fi git --bare init "${PROJECT_NAME}" +if [ -n "$DESCRIPTION" ]; then + echo "$DESCRIPTION" > "${PROJECT_NAME}/description" +fi + if [ "$PUBLIC" = true ]; then echo "${REMOTE_BASE_URL}/${PROJECT_NAME}" > "${PROJECT_NAME}/public.conf" - ssh git@treybastian.com new-repo "repos/${PROJECT_NAME}" + REMOTE_ARGS=("repos/${PROJECT_NAME}") + if [ -n "$DESCRIPTION" ]; then + REMOTE_ARGS+=(--description "$DESCRIPTION") + fi + ssh git@treybastian.com new-repo "${REMOTE_ARGS[@]}" echo "public repo: ${REMOTE_BASE_URL}/${PROJECT_NAME}" fi diff --git a/git-shell-commands/new-repo b/git-shell-commands/new-repo index c2c454a..80f7ec9 100755 --- a/git-shell-commands/new-repo +++ b/git-shell-commands/new-repo @@ -3,27 +3,45 @@ set -euo pipefail # this script creates a bare git repository on the git server # # USAGE: -# ssh git@<host> new-repo <name> +# ssh git@<host> new-repo <name> [--description <desc>] # # EXAMPLES: # ssh git@treybastian.com new-repo myrepo # ssh git@treybastian.com new-repo myrepo.git +# ssh git@treybastian.com new-repo myrepo --description "my cool repo" usage() { - echo "Usage: new-repo <name>" + echo "Usage: new-repo <name> [--description <desc>]" echo "" echo "Examples:" echo " new-repo myrepo" echo " new-repo myrepo.git" + echo " new-repo myrepo --description \"my cool repo\"" exit 1 } -if [ "$#" -ne 1 ]; then - echo "Error: expected 1 argument, got $#" +if [ "$#" -lt 1 ]; then + echo "Error: expected at least 1 argument, got $#" usage fi PROJECT_NAME="$1" +DESCRIPTION="" +shift + +while [ "$#" -gt 0 ]; do + case "$1" in + --description) + [ "$#" -ge 2 ] || { echo "Error: --description requires a value"; usage; } + DESCRIPTION="$2" + shift 2 + ;; + *) + echo "Error: unknown flag '$1'" + usage + ;; + esac +done if [[ "$PROJECT_NAME" != *.git ]]; then PROJECT_NAME="${PROJECT_NAME}.git" @@ -36,6 +54,10 @@ fi git --bare init "$PROJECT_NAME" +if [ -n "$DESCRIPTION" ]; then + echo "$DESCRIPTION" > "${PROJECT_NAME}/description" +fi + echo "repo created: $PROJECT_NAME" echo "git url: ${USER}@${HOSTNAME}:${PROJECT_NAME}" diff --git a/git-shell-commands/set-description b/git-shell-commands/set-description new file mode 100755 index 0000000..a6e8a50 --- /dev/null +++ b/git-shell-commands/set-description @@ -0,0 +1,51 @@ +#!/bin/bash +set -euo pipefail +# this script sets the description of an existing bare git repository +# if public.conf exists the description is also set on the remote repository +# +# USAGE: +# ssh git@<host> set-description <name> <desc> +# +# EXAMPLES: +# ssh git@treybastian.com set-description myrepo "my cool repo" +# ssh git@treybastian.com set-description myrepo.git "my cool repo" + +usage() { + echo "Usage: set-description <name> <desc>" + echo "" + echo "Examples:" + echo " set-description myrepo \"my cool repo\"" + echo " set-description myrepo.git \"my cool repo\"" + exit 1 +} + +if [ "$#" -ne 2 ]; then + echo "Error: expected 2 arguments, got $#" + usage +fi + +PROJECT_NAME="$1" +DESCRIPTION="$2" + +if [[ "$PROJECT_NAME" != *.git ]]; then + PROJECT_NAME="${PROJECT_NAME}.git" +fi + +if [ ! -d "$PROJECT_NAME" ]; then + echo "Error: '$PROJECT_NAME' does not exist" + exit 1 +fi + +echo "$DESCRIPTION" > "${PROJECT_NAME}/description" +echo "description updated: $PROJECT_NAME" + +PUBLIC_FILE="${PROJECT_NAME}/public.conf" +if [ -f "$PUBLIC_FILE" ]; then + REPO_URL=$(cat "$PUBLIC_FILE" | tr -d '\n' | xargs) + REMOTE_HOST="${REPO_URL%%:*}" + REMOTE_PATH="${REPO_URL##*:}" + ssh "$REMOTE_HOST" set-description "$REMOTE_PATH" "$DESCRIPTION" + echo "description updated on remote: $REPO_URL" +fi + +# vim: filetype=bash |
