noot
This commit is contained in:
parent
2e8dafded7
commit
46d7fbab1e
8
Makefile
8
Makefile
@ -1,6 +1,6 @@
|
||||
.PHONY: all install
|
||||
|
||||
SOURCES_LIBS:=$(shell find lib -type f)
|
||||
SOURCES_LIBS:=$(shell find src/lib -type f)
|
||||
SOURCES_SRC:=$(shell find src -type f )
|
||||
REPOTOOL_PATH ?= ${HOME}/repo
|
||||
|
||||
@ -15,8 +15,8 @@ src/lib/repotool/stdlib.sh: $(SOURCES_LIBS)
|
||||
bashly add --source . stdlib -f
|
||||
|
||||
dist/repotool: $(SOURCES_SRC)
|
||||
mkdir -p dist
|
||||
bashly generate
|
||||
mv repotool dist
|
||||
@mkdir -p dist
|
||||
@bashly generate
|
||||
@mv repotool dist
|
||||
|
||||
|
||||
|
||||
@ -3,3 +3,8 @@ stdlib:
|
||||
files:
|
||||
- source: "lib/stdlib.sh"
|
||||
target: "%{user_lib_dir}/repotool/stdlib.%{user_ext}"
|
||||
git:
|
||||
help: git for repotool
|
||||
files:
|
||||
- source: "lib/git.sh"
|
||||
target: "%{user_lib_dir}/repotool/git.%{user_ext}"
|
||||
|
||||
82
repotool.zsh
82
repotool.zsh
@ -19,33 +19,57 @@ _activate_hook() {
|
||||
return 1
|
||||
}
|
||||
|
||||
TOOL_BIN="$REPOTOOL_PATH/.bin/repotool"
|
||||
case "$1" in
|
||||
get | g)
|
||||
shift;
|
||||
response=$($TOOL_BIN get $@)
|
||||
if [[ $? != 0 ]] then;
|
||||
echo "failed to get repo with args $@"
|
||||
return $?
|
||||
_parse_json() {
|
||||
local response="$1"
|
||||
local key="$2"
|
||||
echo "$response" | jq -r ".$key"
|
||||
}
|
||||
|
||||
_handle_response() {
|
||||
local response="$1"
|
||||
|
||||
# Validate JSON
|
||||
if ! echo "$response" | jq . >/dev/null 2>&1; then
|
||||
# Not valid JSON, write to stderr
|
||||
echo "$response" >&2
|
||||
return
|
||||
fi
|
||||
declare -A obj
|
||||
for item in ${(z)response}; do
|
||||
parts=(${(s[=])item})
|
||||
# NOTE: zsh is 1 indexed arrays
|
||||
obj[${parts[1]}]=${parts[2]}
|
||||
done
|
||||
_activate_hook "before_cd" $response
|
||||
cd ${obj[dir]}
|
||||
_activate_hook "after_cd" $response
|
||||
;;
|
||||
'help' | "-h"| "-help" | "--help")
|
||||
echo <<EOF
|
||||
usage:
|
||||
repo get <repo-name>
|
||||
EOF
|
||||
;;
|
||||
"open")
|
||||
raw_url=$(git ls-remote --get-url | cut -d '@' -f2 | sed 's/:/\//1')
|
||||
xdg-open "https://${raw_url}"
|
||||
;;
|
||||
esac
|
||||
|
||||
# Check if response has an echo field
|
||||
local echo_msg=$(echo "$response" | jq -r '.echo // empty')
|
||||
if [[ -n "$echo_msg" ]]; then
|
||||
echo "$echo_msg"
|
||||
fi
|
||||
|
||||
# Get hook name from response
|
||||
local hook_name=$(echo "$response" | jq -r '.hook // empty')
|
||||
|
||||
# Handle before hook if hook name is provided
|
||||
if [[ -n "$hook_name" ]]; then
|
||||
_activate_hook "before_${hook_name}_cd" "$response"
|
||||
fi
|
||||
|
||||
# Check if response has a cd field
|
||||
local cd_path=$(echo "$response" | jq -r '.cd // empty')
|
||||
if [[ -n "$cd_path" ]]; then
|
||||
cd "$cd_path"
|
||||
fi
|
||||
|
||||
# Handle after hook if hook name is provided
|
||||
if [[ -n "$hook_name" ]]; then
|
||||
_activate_hook "after_${hook_name}_cd" "$response"
|
||||
fi
|
||||
}
|
||||
|
||||
TOOL_BIN="$REPOTOOL_PATH/.bin/repotool"
|
||||
|
||||
# Pass all arguments to repotool and handle response
|
||||
response=$($TOOL_BIN "$@")
|
||||
exit_code=$?
|
||||
|
||||
if [[ $exit_code != 0 ]]; then
|
||||
echo "Command failed with exit code $exit_code" >&2
|
||||
return $exit_code
|
||||
fi
|
||||
|
||||
_handle_response "$response"
|
||||
@ -19,6 +19,8 @@ commands:
|
||||
command: ["git"]
|
||||
perl:
|
||||
command: ["perl"]
|
||||
jq:
|
||||
command: ["jq"]
|
||||
args:
|
||||
- name: repo
|
||||
required: true
|
||||
@ -44,3 +46,40 @@ commands:
|
||||
allowed: ["ssh", "https", "http"]
|
||||
examples:
|
||||
- repo get tuxpa.in/a/repotool
|
||||
- name: worktree
|
||||
alias: [w, wt]
|
||||
help: get worktree path for current repo
|
||||
dependencies:
|
||||
git:
|
||||
command: ["git"]
|
||||
perl:
|
||||
command: ["perl"]
|
||||
jq:
|
||||
command: ["jq"]
|
||||
args:
|
||||
- name: name
|
||||
required: false
|
||||
help: Name of the worktree
|
||||
flags:
|
||||
- long: --list
|
||||
short: -l
|
||||
help: List existing worktrees for this repo
|
||||
- long: --root
|
||||
short: -r
|
||||
help: Return the root directory of the original repo
|
||||
examples:
|
||||
- repo worktree feature-branch
|
||||
- repo worktree -l
|
||||
- repo worktree -r
|
||||
- name: open
|
||||
alias: o
|
||||
help: open the current repository in web browser
|
||||
dependencies:
|
||||
git:
|
||||
command: ["git"]
|
||||
perl:
|
||||
command: ["perl"]
|
||||
jq:
|
||||
command: ["jq"]
|
||||
examples:
|
||||
- repo open
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
|
||||
#!/bin/bash
|
||||
linspect
|
||||
|
||||
local resp
|
||||
@ -21,22 +21,14 @@ local ssh_user;
|
||||
local http_user;
|
||||
local http_pass;
|
||||
|
||||
local domain
|
||||
local path
|
||||
#now extract the args we need
|
||||
# this is where we use perl
|
||||
if [[ $resp == 1 ]]; then
|
||||
# TODO: properly extract user and password, if exists.
|
||||
output=($(echo ${args[repo]} | ${regex} '/^https?:\/\/(?:.*?:)?(.*\..*?)\/(.*?)(\.git)?$/ && print "$1 $2" ' -))
|
||||
domain=${output[0]}
|
||||
path=${output[1]}
|
||||
fi
|
||||
# Parse the URL to get domain and path
|
||||
parsed_json=$(parse_git_url "${args[repo]}" "$regex" "${deps[jq]}")
|
||||
domain=$(echo "$parsed_json" | ${deps[jq]} -r '.domain')
|
||||
path=$(echo "$parsed_json" | ${deps[jq]} -r '.path')
|
||||
|
||||
if [[ $resp == 2 ]]; then
|
||||
# TODO: properly extract ssh user, if exists.
|
||||
output=($(echo ${args[repo]} | ${regex} '/^(?:.*?@)?(.*\..*?)(?::|\/)(.*?)(\.git)?$/ && print "$1 $2" ' -))
|
||||
domain=${output[0]}
|
||||
path=${output[1]}
|
||||
if [[ -z "$domain" ]] || [[ -z "$path" ]]; then
|
||||
echo "Failed to parse repository URL: ${args[repo]}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ ! -z "${args[--ssh-user]}" && -z "$ssh_user" ]]; then
|
||||
@ -109,6 +101,14 @@ if [[ ! -d .git ]]; then
|
||||
fi
|
||||
fi
|
||||
|
||||
echo dir=$target_dir domain=$domain path=$path repo_url=$repo_url cloned=$cloned
|
||||
# Output in JSON format
|
||||
${deps[jq]} -n \
|
||||
--arg cd "$target_dir" \
|
||||
--arg domain "$domain" \
|
||||
--arg path "$path" \
|
||||
--arg repo_url "$repo_url" \
|
||||
--arg cloned "$cloned" \
|
||||
--arg hook "get" \
|
||||
'{cd: $cd, domain: $domain, path: $path, repo_url: $repo_url, cloned: $cloned, hook: $hook}'
|
||||
|
||||
exit 0
|
||||
|
||||
82
src/lib/git.sh
Normal file
82
src/lib/git.sh
Normal file
@ -0,0 +1,82 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Parse a git URL into domain and path components
|
||||
# Usage: parse_git_url <url> <perl_regex_command> <jq_command>
|
||||
# Returns: JSON with domain and path fields
|
||||
parse_git_url() {
|
||||
local url="$1"
|
||||
local regex="$2"
|
||||
local jq="$3"
|
||||
|
||||
local domain=""
|
||||
local path=""
|
||||
local output
|
||||
|
||||
# Check if it's an HTTP(S) URL
|
||||
if [[ "$url" =~ ^https?:// ]]; then
|
||||
output=($(echo "$url" | $regex '/^https?:\/\/(?:.*?:)?(.*\..*?)\/(.*?)(\.git)?$/ && print "$1 $2" ' -))
|
||||
if [[ ${#output[@]} -eq 2 ]]; then
|
||||
domain=${output[0]}
|
||||
path=${output[1]}
|
||||
fi
|
||||
# Check if it's an SSH URL
|
||||
elif [[ "$url" =~ ^[^:]+@[^:]+: ]] || [[ "$url" =~ ^ssh:// ]]; then
|
||||
output=($(echo "$url" | $regex '/^(?:.*?@)?(.*\..*?)(?::|\/)(.*?)(\.git)?$/ && print "$1 $2" ' -))
|
||||
if [[ ${#output[@]} -eq 2 ]]; then
|
||||
domain=${output[0]}
|
||||
path=${output[1]}
|
||||
fi
|
||||
# Check if it's a bare domain path (e.g., gfx.cafe/oku/trade)
|
||||
else
|
||||
output=($(echo "$url" | $regex '/^(.*\..*?)\/(.*?)(\.git)?$/ && print "$1 $2" ' -))
|
||||
if [[ ${#output[@]} -eq 2 ]]; then
|
||||
domain=${output[0]}
|
||||
path=${output[1]}
|
||||
fi
|
||||
fi
|
||||
|
||||
# Return JSON
|
||||
$jq -n --arg domain "$domain" --arg path "$path" '{domain: $domain, path: $path}'
|
||||
}
|
||||
|
||||
# Get domain and path from current git repository's origin
|
||||
# Usage: parse_git_origin <perl_regex_command> <jq_command>
|
||||
# Returns: JSON with domain and path fields
|
||||
parse_git_origin() {
|
||||
local regex="$1"
|
||||
local jq="$2"
|
||||
|
||||
# Get origin URL
|
||||
local origin_url
|
||||
origin_url=$(git config --get remote.origin.url)
|
||||
|
||||
if [[ -z "$origin_url" ]]; then
|
||||
$jq -n '{domain: "", path: ""}'
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Parse the URL
|
||||
parse_git_url "$origin_url" "$regex" "$jq"
|
||||
}
|
||||
|
||||
# Validate if a URL is a valid git repository URL
|
||||
# Usage: valid_url <url>
|
||||
# Returns: 1 for HTTP(S), 2 for SSH, 3 for bare domain paths, -1 for invalid
|
||||
valid_url() {
|
||||
local url="$1"
|
||||
|
||||
if [[ "$url" =~ ^https?:// ]]; then
|
||||
echo "1"
|
||||
return 0
|
||||
elif [[ "$url" =~ ^[^:]+@[^:]+: ]] || [[ "$url" =~ ^ssh:// ]]; then
|
||||
echo "2"
|
||||
return 0
|
||||
elif [[ "$url" =~ ^[a-zA-Z0-9.-]+\.[a-zA-Z]+/[^/]+ ]]; then
|
||||
# Bare domain path like gfx.cafe/oku/trade
|
||||
echo "3"
|
||||
return 0
|
||||
else
|
||||
echo "-1"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
@ -1,47 +0,0 @@
|
||||
|
||||
|
||||
# ((git|ssh|http(s)?)?|(git@[\w\.]+))(:(//)?)([\w\.@\:/\-~]+)(\.git)?(/)?
|
||||
valid_url()
|
||||
{
|
||||
# matches https://<domain.tld>/<path>
|
||||
if [[ "$1" =~ ^https?://.*\..*/.*$ ]]; then
|
||||
echo '1'
|
||||
return 0
|
||||
fi
|
||||
# matches <user>@<domain.*?>:<path>
|
||||
if [[ "$1" =~ ^(.*?)(:|/)(.+)(\/.+)*$ ]]; then
|
||||
echo '2'
|
||||
return 0
|
||||
fi
|
||||
echo '-1'
|
||||
return 0
|
||||
}
|
||||
|
||||
lcat()
|
||||
{
|
||||
if [[ -z "$DEBUG_LOG" || "$DEBUG_LOG" == 0 ]]; then
|
||||
return 0
|
||||
fi
|
||||
cat $@ >&2
|
||||
}
|
||||
|
||||
lecho()
|
||||
{
|
||||
if [[ -z "$DEBUG_LOG" || "$DEBUG_LOG" == 0 ]]; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
echo $@ >&2
|
||||
}
|
||||
|
||||
linspect()
|
||||
{
|
||||
if [[ -z "$DEBUG_LOG" || "$DEBUG_LOG" == 0 ]]; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
inspect_args>&2
|
||||
}
|
||||
|
||||
|
||||
|
||||
49
src/open_command.sh
Executable file
49
src/open_command.sh
Executable file
@ -0,0 +1,49 @@
|
||||
linspect
|
||||
|
||||
# Check if we're in a git repository
|
||||
if ! git rev-parse --git-dir > /dev/null 2>&1; then
|
||||
echo "Error: Not in a git repository" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Get the remote URL
|
||||
raw_url=$(git ls-remote --get-url)
|
||||
if [[ -z "$raw_url" ]]; then
|
||||
echo "Error: No remote URL found" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Parse the URL to get domain and path
|
||||
local regex="${deps[perl]} -n -l -e"
|
||||
parsed_json=$(parse_git_url "$raw_url" "$regex" "${deps[jq]}")
|
||||
domain=$(echo "$parsed_json" | ${deps[jq]} -r '.domain')
|
||||
path=$(echo "$parsed_json" | ${deps[jq]} -r '.path')
|
||||
|
||||
if [[ -z "$domain" ]] || [[ -z "$path" ]]; then
|
||||
echo "Error: Unable to parse repository URL: $raw_url" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Construct the HTTPS URL
|
||||
https_url="https://${domain}/${path}"
|
||||
|
||||
# Detect the platform and open the URL
|
||||
if command -v xdg-open &> /dev/null; then
|
||||
# Linux
|
||||
xdg-open "$https_url" 2>/dev/null
|
||||
elif command -v open &> /dev/null; then
|
||||
# macOS
|
||||
open "$https_url" 2>/dev/null
|
||||
elif command -v start &> /dev/null; then
|
||||
# Windows
|
||||
start "$https_url" 2>/dev/null
|
||||
else
|
||||
echo "Error: Unable to detect platform open command" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Return JSON with echo message
|
||||
${deps[jq]} -n \
|
||||
--arg echo "Opening $https_url in browser..." \
|
||||
--arg hook "open" \
|
||||
'{echo: $echo, hook: $hook}'
|
||||
143
src/worktree_command.sh
Executable file
143
src/worktree_command.sh
Executable file
@ -0,0 +1,143 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Get the current directory (should be inside a git repo)
|
||||
if ! git rev-parse --git-dir > /dev/null 2>&1; then
|
||||
echo "Error: Not in a git repository" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Get the repository root
|
||||
repo_root=$(git rev-parse --show-toplevel)
|
||||
|
||||
# Handle --root flag
|
||||
if [[ "${args[--root]}" == "1" ]]; then
|
||||
# Check if we're in a worktree
|
||||
git_common_dir=$(git rev-parse --git-common-dir 2>/dev/null)
|
||||
if [[ "$git_common_dir" != ".git" && -d "$git_common_dir" ]]; then
|
||||
# We're in a worktree, get the main repo path
|
||||
main_repo=$(dirname "$git_common_dir")
|
||||
${deps[jq]} -n --arg cd "$main_repo" --arg hook "worktree" '{cd: $cd, hook: $hook}'
|
||||
else
|
||||
# We're in the main repo already
|
||||
${deps[jq]} -n --arg cd "$repo_root" --arg hook "worktree" '{cd: $cd, hook: $hook}'
|
||||
fi
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Parse the origin URL to get domain and path
|
||||
local regex="${deps[perl]} -n -l -e"
|
||||
parsed_json=$(parse_git_origin "$regex" "${deps[jq]}")
|
||||
domain=$(echo "$parsed_json" | ${deps[jq]} -r '.domain')
|
||||
path=$(echo "$parsed_json" | ${deps[jq]} -r '.path')
|
||||
|
||||
if [[ -z "$domain" ]] || [[ -z "$path" ]]; then
|
||||
echo "Error: Unable to parse repository origin URL" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Calculate the worktree base directory
|
||||
worktree_base="$REPOTOOL_PATH/worktree/$domain/$path"
|
||||
|
||||
# Check if name argument is "list" or "ls"
|
||||
if [[ "${args[name]}" == "list" ]] || [[ "${args[name]}" == "ls" ]]; then
|
||||
args[--list]="1"
|
||||
fi
|
||||
|
||||
# Handle --list flag
|
||||
if [[ "${args[--list]}" == "1" ]]; then
|
||||
echo "Worktrees for $domain/$path:" >&2
|
||||
|
||||
# Get all worktrees from git using porcelain format
|
||||
local found_any=false
|
||||
local wt_path=""
|
||||
local is_prunable=false
|
||||
|
||||
while IFS= read -r line; do
|
||||
# Parse git worktree list --porcelain output
|
||||
if [[ "$line" =~ ^worktree[[:space:]](.+)$ ]]; then
|
||||
# Process previous worktree if any
|
||||
if [[ -n "$wt_path" ]] && [[ "$wt_path" == "$worktree_base/"* ]]; then
|
||||
local wt_name=$(basename "$wt_path")
|
||||
if [[ -d "$wt_path" ]]; then
|
||||
echo " - $wt_name" >&2
|
||||
else
|
||||
if [[ "$is_prunable" == "true" ]]; then
|
||||
echo " - $wt_name (missing - prunable)" >&2
|
||||
else
|
||||
echo " - $wt_name (missing)" >&2
|
||||
fi
|
||||
fi
|
||||
found_any=true
|
||||
fi
|
||||
|
||||
# Start new worktree
|
||||
wt_path="${BASH_REMATCH[1]}"
|
||||
is_prunable=false
|
||||
elif [[ "$line" == "prunable gitdir file points to non-existent location" ]]; then
|
||||
is_prunable=true
|
||||
fi
|
||||
done < <(git worktree list --porcelain)
|
||||
|
||||
# Process the last worktree
|
||||
if [[ -n "$wt_path" ]] && [[ "$wt_path" == "$worktree_base/"* ]]; then
|
||||
local wt_name=$(basename "$wt_path")
|
||||
if [[ -d "$wt_path" ]]; then
|
||||
echo " - $wt_name" >&2
|
||||
else
|
||||
if [[ "$is_prunable" == "true" ]]; then
|
||||
echo " - $wt_name (missing - prunable)" >&2
|
||||
else
|
||||
echo " - $wt_name (missing)" >&2
|
||||
fi
|
||||
fi
|
||||
found_any=true
|
||||
fi
|
||||
|
||||
if [[ "$found_any" == "false" ]]; then
|
||||
echo " No worktrees found" >&2
|
||||
fi
|
||||
|
||||
# Return hook field only (no cd field) so we don't change dirs
|
||||
${deps[jq]} -n --arg hook "worktree.list" '{hook: $hook}'
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Get the worktree name from arguments
|
||||
worktree_name="${args[name]}"
|
||||
|
||||
# Check if name is provided (required for normal operation)
|
||||
if [[ -z "$worktree_name" ]]; then
|
||||
echo "Error: Missing required argument: name" >&2
|
||||
echo "Run 'repo worktree --help' for usage information" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Construct the worktree path
|
||||
worktree_path="$worktree_base/$worktree_name"
|
||||
|
||||
# Check if a prunable worktree exists at this path
|
||||
if git worktree list | grep -q "$worktree_path.*prunable"; then
|
||||
echo "Found prunable worktree at $worktree_path, cleaning up..." >&2
|
||||
git worktree prune
|
||||
fi
|
||||
|
||||
# Check if worktree already exists
|
||||
created="false"
|
||||
if ! git worktree list | grep -q "$worktree_path"; then
|
||||
# Create parent directories if they don't exist
|
||||
mkdir -p "$(dirname "$worktree_path")"
|
||||
|
||||
# Create the worktree
|
||||
git worktree add "$worktree_path" -b "$worktree_name" 2>/dev/null || git worktree add "$worktree_path" "$worktree_name" 2>/dev/null || git worktree add "$worktree_path"
|
||||
created="true"
|
||||
fi
|
||||
|
||||
# Output in JSON format
|
||||
${deps[jq]} -n \
|
||||
--arg cd "$worktree_path" \
|
||||
--arg domain "$domain" \
|
||||
--arg path "$path" \
|
||||
--arg worktree_name "$worktree_name" \
|
||||
--arg created "$created" \
|
||||
--arg hook "worktree" \
|
||||
'{cd: $cd, domain: $domain, path: $path, worktree_name: $worktree_name, created: $created, hook: $hook}'
|
||||
Loading…
Reference in New Issue
Block a user