flake/modules/home/terminal/hr/hr.sh
2026-01-21 15:41:03 +00:00

310 lines
7.5 KiB
Bash

# Set default PROJECT_ID if not already set
if [[ -z "$PROJECT_ID" ]]; then
export PROJECT_ID="mk2-test"
fi
_hr_usage() {
echo "Usage: hr <command>"
echo "Commands:"
echo " switch Switch PROJECT_ID between mk2-test and mk2-prod"
echo " call Call a Cloud Run service route"
echo " cf Call a Cloud Function"
echo " init py Initialize a python devenv environment (git-ignored)"
echo " freeze Freeze dependencies to requirements.txt"
}
_hr_init_py() {
echo "Initializing python devenv..."
# 1. Init devenv
if [ -f .gitignore ]; then
cp .gitignore .gitignore.bak
fi
if command -v devenv >/dev/null; then
devenv init
echo "Direnv allowed"
else
echo "Error: devenv not found in path."
return 1
fi
if [ -f .gitignore.bak ]; then
mv .gitignore.bak .gitignore
elif [ -f .gitignore ]; then
rm .gitignore
fi
# 2. Replace devenv.nix
cat <<EOF >devenv.nix
{pkgs, ...}: {
packages = [ pkgs.google-cloud-sdk ];
languages.python = {
enable = true;
venv.enable = true;
uv = {
enable = true;
sync.enable = false;
};
};
# We use the named index "google" defined in uv.toml
env.UV_INDEX_GOOGLE_USERNAME = "oauth2accesstoken";
env.PROJECT_ID = "mk2-test";
enterShell = ''
if ! gcloud auth print-access-token >/dev/null 2>&1; then
echo "⚠️ gcloud not authenticated. Run 'gcloud auth login' to access Google Artifact Registry."
else
export UV_INDEX_GOOGLE_PASSWORD=\$(gcloud auth print-access-token)
fi
uv sync
'';
}
EOF
cat <<EOF >uv.toml
[[index]]
name = "google"
url = "https://europe-west1-python.pkg.dev/mk2-prod/python-packages/simple/"
EOF
# 3. Add to local git exclude
if git rev-parse --git-dir >/dev/null 2>&1; then
EXCLUDE_FILE=$(git rev-parse --git-path info/exclude)
mkdir -p "$(dirname "$EXCLUDE_FILE")"
IGNORES=(
".devenv*"
".direnv"
"devenv.nix"
"devenv.yaml"
"devenv.lock"
"uv.lock"
"uv.toml"
".envrc"
)
for file in "${IGNORES[@]}"; do
if ! grep -Fxq "$file" "$EXCLUDE_FILE" 2>/dev/null; then
echo "$file" >>"$EXCLUDE_FILE"
echo "Added $file to local git exclude ($EXCLUDE_FILE)"
fi
done
else
echo "Warning: Not a git repository. Skipping git ignore setup."
fi
# 4. Allow direnv
if command -v direnv >/dev/null; then
direnv allow
echo "Direnv allowed"
else
echo "Error: direnv not found in path."
return 1
fi
}
_hr_freeze() {
local extra_index_url="https://europe-west1-python.pkg.dev/mk2-prod/python-packages/simple/"
# Install the auth plugin and keyring CLI
uv pip install keyrings.google-artifactregistry-auth==1.1.2 keyring
# Install project dependencies using the subprocess keyring provider
uv pip install --no-cache -e .[test] --extra-index-url "${extra_index_url}" --keyring-provider subprocess
# Generate requirements.txt
echo "--extra-index-url ${extra_index_url}" >requirements.txt
uv pip freeze --exclude-editable >>requirements.txt
}
_hr_add_json_field() {
local json="$1"
local key="$2"
local value="$3"
local jq_opt="--arg"
# Check if explicit boolean
if [[ "$value" == "true" || "$value" == "false" ]]; then
jq_opt="--argjson"
# Check if number (integer or float, no leading zeros unless just 0)
elif [[ "$value" =~ ^-?(0|[1-9][0-9]*)(\.[0-9]+)?$ ]]; then
jq_opt="--argjson"
# Check if object or array
elif [[ "$value" == "["* || "$value" == "{"* ]]; then
if echo "$value" | jq empty >/dev/null 2>&1; then
jq_opt="--argjson"
else
# Warn to stderr, but proceed as string
echo "Warning: Value for '$key' looks like JSON but is invalid. Treating as string." >&2
fi
fi
# Apply the value at the path defined by the key (dot-notation supported)
# paths like items.0.id are converted to ["items", 0, "id"]
echo "$json" | jq --arg k "$key" $jq_opt v "$value" \
'setpath($k | split(".") | map(if test("^[0-9]+$") then tonumber else . end); $v)'
}
_hr_call() {
local route_arg="$1"
shift
if [[ -z "$route_arg" ]]; then
echo "Usage: hr call <route-name>[/path] <options>"
return 1
fi
local service_name
local url_path
if [[ "$route_arg" == */* ]]; then
service_name="${route_arg%%/*}"
url_path="/${route_arg#*/}"
else
service_name="$route_arg"
url_path=""
fi
local project_number
if [[ "$PROJECT_ID" == "mk2-prod" ]]; then
project_number="1013087376822"
else
project_number="322048751601"
fi
if ! command -v jq >/dev/null; then
echo "Error: jq is required but not installed."
return 1
fi
local json_payload="{}"
while [[ $# -gt 0 ]]; do
if [[ "$1" == -* ]]; then
local key="${1#-}"
if [[ -z "$2" || "$2" == -* ]]; then
echo "Error: Missing value for option $key"
return 1
fi
local value="$2"
json_payload=$(_hr_add_json_field "$json_payload" "$key" "$value")
shift 2
else
echo "Error: Unexpected argument '$1'"
return 1
fi
done
local url="https://${service_name}-${project_number}.europe-west1.run.app${url_path}"
echo "Calling $url..."
echo "$json_payload"
curl "$url" \
-H "Authorization: bearer $(gcloud auth print-identity-token)" \
-H "Content-Type: application/json" \
-d "$json_payload"
}
_hr_cf() {
local function_name="$1"
shift
if [[ -z "$function_name" ]]; then
echo "Usage: hr cf <function-name> <options>"
return 1
fi
if ! command -v jq >/dev/null; then
echo "Error: jq is required but not installed."
return 1
fi
local json_payload="{}"
while [[ $# -gt 0 ]]; do
if [[ "$1" == -* ]]; then
local key="${1#-}"
if [[ -z "$2" || "$2" == -* ]]; then
echo "Error: Missing value for option $key"
return 1
fi
local value="$2"
json_payload=$(_hr_add_json_field "$json_payload" "$key" "$value")
shift 2
else
echo "Error: Unexpected argument '$1'"
return 1
fi
done
local url="https://europe-west1-${PROJECT_ID}.cloudfunctions.net/${function_name}"
echo "Calling $url..."
echo "$json_payload"
curl "$url" \
-H "Authorization: bearer $(gcloud auth print-identity-token)" \
-H "Content-Type: application/json" \
-d "$json_payload"
}
hr() {
if [[ $# -eq 0 ]]; then
_hr_usage
return 1
fi
local command="$1"
shift
if [[ "$command" == "switch" ]]; then
if [[ -z "$1" ]]; then
# Toggle between test and prod
if [[ "$PROJECT_ID" == "mk2-test" ]]; then
export PROJECT_ID="mk2-prod"
echo "Switched PROJECT_ID to mk2-prod"
else
export PROJECT_ID="mk2-test"
echo "Switched PROJECT_ID to mk2-test"
fi
elif [[ "$1" == "test" ]]; then
export PROJECT_ID="mk2-test"
echo "Set PROJECT_ID to mk2-test"
elif [[ "$1" == "prod" ]]; then
export PROJECT_ID="mk2-prod"
echo "Set PROJECT_ID to mk2-prod"
else
echo "Usage: hr switch [test|prod]"
return 1
fi
return 0
fi
# Run original logic in a subshell to preserve set -e behavior without affecting current shell
(
set -e
# Restore arguments for processing
set -- "$command" "$@"
if [ "$1" = "init" ] && [ "$2" = "py" ]; then
_hr_init_py
elif [ "$1" = "freeze" ]; then
_hr_freeze
elif [ "$1" = "call" ]; then
shift
_hr_call "$@"
elif [ "$1" = "cf" ]; then
shift
_hr_cf "$@"
else
_hr_usage
exit 1
fi
)
}