Files
Ansible-Build/task
T
2025-02-25 12:02:46 +01:00

349 lines
12 KiB
Bash
Executable File

#!/bin/bash
function help() {
echo
echo "$1 <command> [options]"
echo
echo "commands:"
echo
cmd_width=23
opt_width=22
desc_width=70
printf "| %-${cmd_width}s | %-${opt_width}s | %-${desc_width}s |\n" "Command" "Option" "Description"
echo "|$(printf '%*s' $((cmd_width + 2)) '' | tr ' ' '-')|$(printf '%*s' $((opt_width + 2)) '' | tr ' ' '-')|$(printf '%*s' $((desc_width + 2)) '' | tr ' ' '-')|"
printf "| %-${cmd_width}s | %-${opt_width}s | %-${desc_width}s |\n" "generate-vault-password" "[vault_id] [password]" "Generate a password file for Ansible vault."
printf "| %-${cmd_width}s | %-${opt_width}s | %-${desc_width}s |\n" "convert-vault-file" "[vault_id] [path]" "Re-encrypt vault file as single strings."
printf "| %-${cmd_width}s | %-${opt_width}s | %-${desc_width}s |\n" "show-vault-file" "[vault_id] [path]" "Show content of encrypted strings or fully encrypted vault file."
printf "| %-${cmd_width}s | %-${opt_width}s | %-${desc_width}s |\n" "encrypt-string" "[vault_id] [key:value] -q" "Generate encrypted string with Ansible Vault."
printf "| %-${cmd_width}s | %-${opt_width}s | %-${desc_width}s |\n" "install" "" "Install Ansible and dependencies with uv."
printf "| %-${cmd_width}s | %-${opt_width}s | %-${desc_width}s |\n" "install-exo" "" "Install Exoscale CLI."
printf "| %-${cmd_width}s | %-${opt_width}s | %-${desc_width}s |\n" "upgrade" "" "Upgrade Ansible and dependencies with uv."
printf "| %-${cmd_width}s | %-${opt_width}s | %-${desc_width}s |\n" "llm-update" "[path][prompt]" "Feed role files with prompt to LLM and apply file changes."
printf "| %-${cmd_width}s | %-${opt_width}s | %-${desc_width}s |\n" "lint" "" "Lint Ansible roles and playbooks."
printf "| %-${cmd_width}s | %-${opt_width}s | %-${desc_width}s |\n" "galaxy-build" "" "Create tarball of collection."
printf "| %-${cmd_width}s | %-${opt_width}s | %-${desc_width}s |\n" "galaxy-publish" "" "Publish tarball to Ansible Galaxy."
printf "| %-${cmd_width}s | %-${opt_width}s | %-${desc_width}s |\n" "node-install" "" "Install build dependencies."
printf "| %-${cmd_width}s | %-${opt_width}s | %-${desc_width}s |\n" "node-dev" "" "Start vuepress development server."
printf "| %-${cmd_width}s | %-${opt_width}s | %-${desc_width}s |\n" "node-build" "" "Create vuepress build."
printf "| %-${cmd_width}s | %-${opt_width}s | %-${desc_width}s |\n" "node-serve-build" "" "Serve vuepress build."
printf "| %-${cmd_width}s | %-${opt_width}s | %-${desc_width}s |\n" "list-hosts" "[path]" "Lists all hosts of an inventory."
printf "| %-${cmd_width}s | %-${opt_width}s | %-${desc_width}s |\n" "version" "" "Show tool versions."
printf "| %-${cmd_width}s | %-${opt_width}s | %-${desc_width}s |\n" "source" "" "Source the Python virtual env."
printf "| %-${cmd_width}s | %-${opt_width}s | %-${desc_width}s |\n" "test-localhost" "[clean]" "Deploy to localhost. Pass option 'clean' to clean up."
printf "| %-${cmd_width}s | %-${opt_width}s | %-${desc_width}s |\n" "show-passwordless-sudo" "[username]" "Give instructions to setup passwordless sudo."
printf "| %-${cmd_width}s | %-${opt_width}s | %-${desc_width}s |\n" "llm-update" "[path]" "Feed role files with prompt to LLM and apply updates with git path."
echo
}
# Set env vars
GIT_BRANCH=$(git symbolic-ref --short -q HEAD)
LLM_MODEL=${LLM_MODEL:="deepseek-coder"}
if [[ -a ".env" ]]; then
export $(cat .env | sed 's/^#.*//g' | xargs)
fi
function generate-vault-password() {
if test -z "$1"; then echo "\$1 is empty"; exit; fi
if test -z "$2"; then echo "\$1 is empty"; exit; fi
export VAULT_ID=$1
export VAULTPASSWORD=$2
echo "$VAULTPASSWORD" > ".vault_pass_$VAULT_ID"
chmod 600 ".vault_pass_$VAULT_ID"
echo "Password file ".vault_pass_$VAULT_ID" created."
}
function encrypt-string() {
if test -z "$1"; then echo "\$1 is empty"; exit; fi
if test -z "$2"; then echo "\$2 is empty"; exit; fi
# Split multiline
STRING_KEY=$(echo "$2" | cut -d ':' -f 1)
STRING_VALUE=$(echo "$2" | cut -d ':' -f 2 | sed 's/^ *//')
if [ "$3" != "-q" ]; then
echo "Found key: $STRING_KEY"
echo "Encrypting value: $STRING_VALUE"
fi
ansible-vault encrypt_string --encrypt-vault-id="$1" --name "$STRING_KEY" "$STRING_VALUE"
}
function convert-vault-file() {
if test -z "$1"; then echo "\$1 is empty"; exit; fi
if test -z "$2"; then echo "\$2 is empty"; exit; fi
VAULT_ID=$1
FILE_PATH=$2
TEMP_FILE=$(mktemp)
# Decrypt the vault file
ansible-vault decrypt --output="$FILE_PATH" "$FILE_PATH"
# Process each key-value pair
yq eval '.' "$FILE_PATH" | while IFS= read -r LINE; do
KEY=$(echo "$LINE" | cut -d':' -f1)
VALUE=$(echo "$LINE" | cut -d':' -f2-)
VALUE=$(echo "$VALUE" | sed 's/^[[:space:]"]*//;s/[[:space:]"]*$//')
# Re-encrypt the key-value pair
ENCYRPTED=$(ansible-vault encrypt_string --encrypt-vault-id="$VAULT_ID" --name "$KEY" "$VALUE" 2>/dev/null)
echo "$ENCYRPTED" >> "$TEMP_FILE"
done
# Move temp file to vault file
mv "$TEMP_FILE" "$FILE_PATH"
echo "Converted file saved as $FILE_PATH"
}
show-vault-file() {
if test -z "$1"; then echo "\$1 is empty"; exit 1; fi
if test -z "$2"; then echo "\$2 is empty"; exit 1; fi
VAULT_ID="$1"
FILE_PATH="$2"
TEMP_FILE=$(mktemp)
TEMP_ENTRY_FILE=$(mktemp)
if head -n 1 "$FILE_PATH" | grep -q "^\$ANSIBLE_VAULT;1.2;AES256;$VAULT_ID"; then
ansible-vault decrypt --output="$TEMP_FILE" "$FILE_PATH"
else
WRITE_FINISHED=false
while IFS= read -r LINE; do
if [[ "$LINE" =~ "!vault" ]]; then
if [ "$WRITE_FINISHED" ] && [ -s "$TEMP_ENTRY_FILE" ]; then
ansible-vault decrypt "$TEMP_ENTRY_FILE"
KEY=$(echo "$LINE" | cut -d':' -f1)
VALUE=$(cat "$TEMP_ENTRY_FILE")
echo "$KEY: $VALUE" >> "$TEMP_FILE"
fi
: > "$TEMP_ENTRY_FILE"
WRITE_FINISHED=true
else
if [ "$WRITE_FINISHED" ]; then
echo "$LINE" | sed 's/^[[:space:]"]*//;s/[[:space:]"]*$//' >> "$TEMP_ENTRY_FILE"
fi
fi
done < "$FILE_PATH"
fi
batcat --paging=always "$TEMP_FILE"
rm -f "$TEMP_FILE"
rm -f "$TEMP_ENTRY_FILE"
}
function init-venv() {
if [ ! -d "venv$GIT_BRANCH" ]; then
echo "Init venv$GIT_BRANCH with $(uv --version)."
uv venv "venv$GIT_BRANCH"
fi
}
function activate-venv() {
echo "Source virtualenv venv$GIT_BRANCH."
source "venv$GIT_BRANCH/bin/activate"
}
function install() {
echo "Setup Ansible log file"
sudo touch /var/log/ansible.log
sudo chown $USER: /var/log/ansible.log
init-venv
activate-venv
echo "Install Ansible and Python dependencies"
uv pip install -r requirements.txt
ansible-galaxy collection install community.general
}
function install-exo() {
curl -fsSL https://raw.githubusercontent.com/exoscale/cli/master/install-latest.sh | sh
}
function upgrade() {
echo "Update Ansible"
uv pip install --upgrade ansible
}
function list-hosts() {
if test -z "$1"; then echo "\$1 is empty"; exit; fi
activate-venv
ansible -i $1 all --list-hosts
}
function version() {
activate-venv
uv --version
ansible --version
}
function show-passwordless-sudo() {
USER=${1:-"$USERNAME"}
echo "echo \"$USER ALL=(ALL) NOPASSWD: ALL\" >> /etc/sudoers"
}
function galaxy-build() {
activate-venv
VERSION=$(yq eval '.version' galaxy.yml)
NAMESPACE=$(yq eval '.namespace' galaxy.yml)
NAME=$(yq eval '.name' galaxy.yml)
ansible-galaxy collection build
mv $NAMESPACE-$NAME-$VERSION.tar.gz tmp/
}
function galaxy-publish() {
activate-venv
VERSION=$(yq eval '.version' galaxy.yml)
NAMESPACE=$(yq eval '.namespace' galaxy.yml)
NAME=$(yq eval '.name' galaxy.yml)
ansible-galaxy collection publish tmp/$NAMESPACE-$NAME-$VERSION.tar.gz --token "$GALAXY_API_TOKEN"
}
function test-localhost() {
activate-venv
version
if [ "$1" = "clean" ]; then
echo "Cleanup localhost"
ansible-playbook -i test plays/clean.yml -t "odoo,odoo_data,odoo_volume,\
postgres,postgres_data,postgres_volume,\
meilisearch,meilisearch_data,meilisearch_volume,\
pgadmin,pgadmin_data,pgadmin_volume,\
n8n,n8n_data,n8n_volume,\
nginx,nginx_data,nginx_volume,\
metabase,metabase_data,metabase_volume,\
prometheus,prometheus_data,prometheus_volume,\
grafana,grafana_data,grafana_volume,\
loki,loki_data,loki_volume,\
promtail,promtail_data,promatil_volume"
else
echo "Setup localhost"
ansible-playbook -i test plays/localhost.yml --skip-tags check
fi
}
# LLM Commands
function llm-update() {
if test -z "$1"; then
echo "\$1 is empty.";
exit 1;
fi
# Get files from path
FILES=$(find "$1" -type f \( -name "*.md" -o -name "*.yml" -o -name "*.yaml" -o -name "*.conf" -o -name "*.nginx" \))
echo -e "Loaded these files into prompt:\n\n$FILES\n"
# Prompt task description
if [ -z "$2" ]; then
read -p "Enter the task description: " TASK_DESCRIPTION
else
TASK_DESCRIPTION="$2"
fi
# Prepare the files content for prompt
FILE_CONTENTS=""
for FILE in $FILES; do
FILE_CONTENTS+="<<<$FILE>>>
$(cat "$FILE")
"
done
# Define prompt content
PROMPT_FILE="tmp/llm_update"
echo -e "\nWrite prompt to $PROMPT_FILE."
cat << EOF > "$PROMPT_FILE"
Look at the code files below and do the following:
$TASK_DESCRIPTION
Output all files that you need to change in full again, including your changes.
In the same format as I provide the files below. Under no circumstances output any other text,
no additional infos, no code formatting chars. Only the code in the given format.
Here are the files:
$FILE_CONTENTS
EOF
# Run the llm command
echo -e "Send prompt and wait for the response of the $LLM_MODEL LLM."
RESULT=$(cat "$PROMPT_FILE" | llm -m "$LLM_MODEL")
# Check if result is empty
if [ -z "$RESULT" ]; then
echo "No response from the model. Exiting."
exit 1
fi
# Save the result to a file
RESULT_FILE="tmp/llm_update_result"
echo "$RESULT" > "$RESULT_FILE"
echo -e "Saved response to $RESULT_FILE.\n"
# Show a preview of result file
less "$RESULT_FILE"
# Ask for confirmation before updating files
read -p "Do you want to apply these updates to the files? (y/n): " CONFIRM
if [[ ! "$CONFIRM" =~ ^[Yy]$ ]]; then
exit 0
fi
# Parse the response from the file and update the files directly
echo -e "Parsing the response and updating files...\n"
CURRENT_FILE=""
while IFS= read -r LINE; do
if [[ $LINE =~ ^"<<<"(.*)">>>" ]]; then
CURRENT_FILE="${BASH_REMATCH[1]}"
echo "Update file $CURRENT_FILE."
> "$CURRENT_FILE"
elif [[ -n $CURRENT_FILE ]]; then
echo "$LINE" >> "$CURRENT_FILE"
fi
done < "$RESULT_FILE"
}
if declare -f "$1" > /dev/null; then
"$1" "${@:2}"
else
case "$1" in
help)
help task
exit 1
;;
node-install)
npm install
;;
node-dev)
npm run dev
;;
node-build)
npm run build
;;
node-serve-build)
cd .vuepress/dist
npx serve
;;
lint)
activate-venv
ansible-lint --write --offline roles/*
;;
source)
activate-venv
;;
*)
echo "Unknown command: $1"
help task
exit 1
;;
esac
fi