269 lines
9.0 KiB
Bash
Executable File
269 lines
9.0 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
|
|
# Build and optionally configure a Docker sandbox interactively.
|
|
|
|
set -o errexit
|
|
set -o nounset
|
|
set -o pipefail
|
|
|
|
readonly KIT_PATH="$HOME/.sbx/kits/hypoport.ai"
|
|
readonly TEMPLATE='127.0.0.1:62735/finmas.de/mux033/opencode-ops:latest'
|
|
|
|
require_command() {
|
|
local command_name="$1"
|
|
local message="$2"
|
|
|
|
if ! command -v "$command_name" >/dev/null 2>&1; then
|
|
printf '%s\n' "$message" >&2
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
ask_yes_no() {
|
|
local prompt="$1"
|
|
local answer
|
|
|
|
while true; do
|
|
read -r -p "$prompt [y/n] " answer || exit 0
|
|
case "$answer" in
|
|
[Yy]) return 0 ;;
|
|
[Nn]) return 1 ;;
|
|
*) printf 'Please answer yes or no.\n' ;;
|
|
esac
|
|
done
|
|
}
|
|
|
|
prompt_component() {
|
|
local result_var="$1"
|
|
local label="$2"
|
|
shift 2
|
|
local options=("$@" custom)
|
|
local choice custom_value
|
|
|
|
while true; do
|
|
printf '%s:\n' "$label"
|
|
select choice in "${options[@]}"; do
|
|
case "$choice" in
|
|
custom)
|
|
read -r -p "Custom $label: " custom_value || exit 0
|
|
if [[ -n "$custom_value" ]]; then
|
|
printf -v "$result_var" '%s' "$custom_value"
|
|
return 0
|
|
fi
|
|
printf 'The custom value cannot be empty.\n'
|
|
;;
|
|
'') printf 'Please select one of the listed options.\n' ;;
|
|
*)
|
|
printf -v "$result_var" '%s' "$choice"
|
|
return 0
|
|
;;
|
|
esac
|
|
done
|
|
done
|
|
}
|
|
|
|
main() {
|
|
require_command sbx 'We cannot build a Docker sbx image because sbx does not seem to be installed.'
|
|
require_command jq 'We cannot list existing Docker sbx sandboxes because jq does not seem to be installed.'
|
|
|
|
local build_from_scratch
|
|
if ask_yes_no 'Build from scratch?'; then
|
|
build_from_scratch=true
|
|
else
|
|
build_from_scratch=false
|
|
fi
|
|
|
|
local sandbox_json
|
|
if ! sandbox_json=$(sbx ls --json); then
|
|
printf 'Unable to retrieve the existing Docker sbx sandboxes.\n' >&2
|
|
exit 1
|
|
fi
|
|
|
|
if ! jq -s -e 'length == 1 and (.[0].sandboxes | type == "array")' \
|
|
<<< "$sandbox_json" >/dev/null; then
|
|
printf 'The output from sbx ls --json was not the expected JSON format.\n' >&2
|
|
exit 1
|
|
fi
|
|
|
|
local -a sandbox_names
|
|
mapfile -t sandbox_names < <(jq -r '.sandboxes[].name' <<< "$sandbox_json")
|
|
|
|
local base_sandbox_name next_version selected_sandbox
|
|
local -a current_workspace_paths=()
|
|
local -a workspace_paths=()
|
|
|
|
if [[ "$build_from_scratch" == true ]]; then
|
|
prompt_component sandbox_prefix 'Sandbox prefix' 'finmas-gmbh' 'private'
|
|
prompt_component sandbox_purpose 'Sandbox purpose' 'git-work' 'generic-research'
|
|
prompt_component sandbox_suffix 'Sandbox suffix' 'finmas-api' 'private-api'
|
|
base_sandbox_name="${sandbox_prefix}-${sandbox_purpose}-${sandbox_suffix}"
|
|
next_version=1
|
|
else
|
|
require_command fzf 'We cannot select an existing Docker sbx image because fzf does not seem to be installed.'
|
|
if ((${#sandbox_names[@]} == 0)); then
|
|
printf 'No existing Docker sbx sandboxes were found.\n' >&2
|
|
exit 1
|
|
fi
|
|
|
|
if ! selected_sandbox=$(printf '%s\n' "${sandbox_names[@]}" | fzf \
|
|
--prompt='Select Docker sbx image: ' --height=40% --reverse); then
|
|
printf 'No Docker sbx image was selected.\n'
|
|
exit 0
|
|
fi
|
|
|
|
mapfile -t current_workspace_paths < <(
|
|
jq -r --arg name "$selected_sandbox" \
|
|
'.sandboxes[] | select(.name == $name) | .workspaces[]?' \
|
|
<<< "$sandbox_json"
|
|
)
|
|
|
|
if [[ "$selected_sandbox" =~ ^(.*)-v([0-9]+)$ ]]; then
|
|
base_sandbox_name="${BASH_REMATCH[1]}"
|
|
next_version=$((10#${BASH_REMATCH[2]} + 1))
|
|
else
|
|
base_sandbox_name="$selected_sandbox"
|
|
next_version=1
|
|
fi
|
|
fi
|
|
|
|
local new_sandbox_name="${base_sandbox_name}-v${next_version}"
|
|
|
|
if [[ "$build_from_scratch" == true &&
|
|
( "$sandbox_prefix" != 'finmas-gmbh' || "$sandbox_suffix" == 'private-api' ) ]]; then
|
|
workspace_paths=("$HOME/docker-sbx/empty")
|
|
else
|
|
workspace_paths=("$HOME/Documents/git")
|
|
fi
|
|
|
|
if [[ "$build_from_scratch" == true ]]; then
|
|
current_workspace_paths=("${workspace_paths[@]}")
|
|
fi
|
|
|
|
printf 'Workspace directories in the current sandbox:\n'
|
|
if ((${#current_workspace_paths[@]} == 0)); then
|
|
printf 'No workspace directories were reported.\n'
|
|
else
|
|
printf '%s\n' "${current_workspace_paths[@]}"
|
|
fi
|
|
|
|
if ask_yes_no 'Do you want to add additional workspace directories?'; then
|
|
local workspace_path
|
|
while true; do
|
|
read -r -p 'Additional workspace path (press Enter when finished): ' workspace_path || exit 0
|
|
[[ -z "$workspace_path" ]] && break
|
|
workspace_paths+=("$workspace_path")
|
|
done
|
|
fi
|
|
|
|
printf 'Creating Docker sbx image: %s\n' "$new_sandbox_name"
|
|
if ! sbx create opencode \
|
|
--name "$new_sandbox_name" \
|
|
--kit "$KIT_PATH" \
|
|
--template "$TEMPLATE" \
|
|
"${workspace_paths[@]}"; then
|
|
printf 'Unable to create Docker sbx image: %s\n' "$new_sandbox_name" >&2
|
|
exit 1
|
|
fi
|
|
|
|
local tmp='' api_key
|
|
if [[ "$build_from_scratch" == false ]]; then
|
|
tmp=$(mktemp -d) || {
|
|
printf 'Unable to create a temporary directory.\n' >&2
|
|
exit 1
|
|
}
|
|
trap 'rm -rf "${tmp:?}"' EXIT
|
|
|
|
if ! sbx cp "${selected_sandbox}:/home/agent/.local" "$tmp"; then
|
|
printf 'Unable to copy /home/agent/.local from Docker sbx image: %s\n' \
|
|
"$selected_sandbox" >&2
|
|
exit 1
|
|
fi
|
|
|
|
if ! sbx cp "$tmp/.local" "${new_sandbox_name}:/home/agent/"; then
|
|
printf 'Unable to copy the temporary .local directory into Docker sbx image: %s\n' \
|
|
"$new_sandbox_name" >&2
|
|
exit 1
|
|
fi
|
|
fi
|
|
|
|
if ! read -rsp 'HYPOPORT_API_KEY: ' api_key; then
|
|
printf '\nUnable to read the HYPOPORT_API_KEY.\n' >&2
|
|
exit 1
|
|
fi
|
|
printf '\n'
|
|
|
|
if ! printf '%s\n' "$api_key" |
|
|
sbx exec "$new_sandbox_name" bash -c \
|
|
'umask 077; read -r api_key; printf "export HYPOPORT_API_KEY=%q\n" "$api_key" > /etc/sandbox-persistent.sh'; then
|
|
unset api_key
|
|
printf 'Unable to store the HYPOPORT_API_KEY in Docker sbx image: %s\n' \
|
|
"$new_sandbox_name" >&2
|
|
exit 1
|
|
fi
|
|
unset api_key
|
|
|
|
if ((${#sandbox_names[@]} == 0)) || ! ask_yes_no 'Do you want to delete existing Docker sbx sandboxes?'; then
|
|
return 0
|
|
fi
|
|
|
|
printf 'Existing Docker sbx sandboxes:\n'
|
|
local index
|
|
for index in "${!sandbox_names[@]}"; do
|
|
printf ' %d) %s\n' "$((index + 1))" "${sandbox_names[index]}"
|
|
done
|
|
|
|
local selection selection_part range_start range_end
|
|
local valid_selection index sandbox_name
|
|
declare -A selected_indices=()
|
|
while true; do
|
|
read -r -p 'Sandboxes to delete (for example, 1-3 or 1, 2, 7; press Enter to cancel): ' selection || return 0
|
|
[[ -z "$selection" ]] && return 0
|
|
|
|
selected_indices=()
|
|
valid_selection=true
|
|
IFS=',' read -ra selection_parts <<< "$selection"
|
|
for selection_part in "${selection_parts[@]}"; do
|
|
selection_part="${selection_part//[[:space:]]/}"
|
|
if [[ "$selection_part" =~ ^([0-9]+)-([0-9]+)$ ]]; then
|
|
range_start=$((10#${BASH_REMATCH[1]}))
|
|
range_end=$((10#${BASH_REMATCH[2]}))
|
|
if ((range_start < 1 || range_start > range_end || range_end > ${#sandbox_names[@]})); then
|
|
valid_selection=false
|
|
break
|
|
fi
|
|
for ((index = range_start; index <= range_end; index++)); do
|
|
selected_indices[$index]=1
|
|
done
|
|
elif [[ "$selection_part" =~ ^[0-9]+$ ]]; then
|
|
index=$((10#$selection_part))
|
|
if ((index < 1 || index > ${#sandbox_names[@]})); then
|
|
valid_selection=false
|
|
break
|
|
fi
|
|
selected_indices[$index]=1
|
|
else
|
|
valid_selection=false
|
|
break
|
|
fi
|
|
done
|
|
|
|
if [[ "$valid_selection" != true || ${#selected_indices[@]} -eq 0 ]]; then
|
|
printf 'Please enter valid sandbox numbers and ranges from 1 to %d.\n' "${#sandbox_names[@]}"
|
|
continue
|
|
fi
|
|
|
|
for ((index = 1; index <= ${#sandbox_names[@]}; index++)); do
|
|
if [[ ${selected_indices[$index]:-} == 1 ]]; then
|
|
sandbox_name="${sandbox_names[index - 1]}"
|
|
printf 'Deleting Docker sbx sandbox: %s\n' "$sandbox_name"
|
|
if ! sbx rm --force "$sandbox_name"; then
|
|
printf 'Unable to delete Docker sbx sandbox: %s\n' "$sandbox_name" >&2
|
|
fi
|
|
fi
|
|
done
|
|
return 0
|
|
done
|
|
}
|
|
|
|
main "$@"
|