285 lines
6.8 KiB
Markdown
285 lines
6.8 KiB
Markdown
---
|
|
layout: layouts/base.njk
|
|
---
|
|
|
|
# Patterns
|
|
|
|
The task file from the specification is very basic. Commands can also have parameters and call each other.
|
|
|
|
The following is a collection of more complex task file patterns.
|
|
|
|
## Set default parameter
|
|
|
|
Fallback to a default value for a parameter.
|
|
|
|
```bash
|
|
build() {
|
|
local platform="amd64"
|
|
if [[ -n "$1" ]]; then
|
|
platform="$1"
|
|
fi
|
|
```
|
|
|
|
## Ensure parameter is not empty
|
|
|
|
Check the first param and exit if it is empty.
|
|
|
|
```bash
|
|
deploy() {
|
|
if [[ -z "$1" ]]; then echo "\$1 is empty."; exit; fi
|
|
```
|
|
|
|
## Prompt for input
|
|
|
|
Use `read` to ask for inputs.
|
|
|
|
```bash
|
|
if [[ -z "$2" ]]; then
|
|
read -p 'Enter the task description: ' task_description
|
|
else
|
|
task_description="$2"
|
|
fi
|
|
```
|
|
|
|
## Static env vars
|
|
|
|
These vars are exported from the script and used in external tools.
|
|
|
|
```bash
|
|
CONFIGURATION_FILE=${CONFIGURATION_FILE:=file.conf}
|
|
GIT_BRANCH=$(git symbolic-ref --short -q HEAD)
|
|
```
|
|
|
|
## Updateable vars
|
|
|
|
This variables can be configured from the environment, but is used only internally.
|
|
|
|
```
|
|
container_engine=${CONTAINER_ENGINE:=docker}
|
|
compose_command=${COMPOSE_COMMAND:=docker compose}
|
|
```
|
|
|
|
## Conditional vars
|
|
|
|
Depending on the value of another var, this vars value changes.
|
|
|
|
```bash
|
|
if [[ "$container_engine" == "podman" ]]; then
|
|
compose_command="podman-compose"
|
|
fi
|
|
```
|
|
|
|
## Template with env vars
|
|
|
|
Create a parameterized file from a template. Requires `envsubst`.
|
|
|
|
```bash
|
|
template-with-env() {
|
|
echo "Template $CONFIGURATION_FILE"
|
|
|
|
export CONFIGURATION_1
|
|
export CONFIGURATION_2=${CONFIGURATION_2:=value}
|
|
|
|
envsubst < 'file.conf.template' > "$CONFIGURATION_FILE"
|
|
}
|
|
```
|
|
|
|
## Create Python virtual env
|
|
|
|
Initialize Python virtual env with uv.
|
|
|
|
```bash
|
|
init-venv() {
|
|
if [[ ! -d ".venv$GIT_BRANCH" ]]; then
|
|
echo "Init .venv$GIT_BRANCH with $(uv --version)."
|
|
uv venv ".venv$GIT_BRANCH"
|
|
fi
|
|
}
|
|
```
|
|
|
|
## Call a Python script
|
|
|
|
Run a Python script.
|
|
|
|
```bash
|
|
generate-password-hash() {
|
|
if [[ -z "$1" ]]; then echo "\$1 is empty."; exit; fi
|
|
export PASSWORD_PLAIN="$1"
|
|
bin/password-hash
|
|
}
|
|
```
|
|
|
|
The Python script: `bin/password-hash`
|
|
|
|
```python
|
|
#!/usr/bin/env python3
|
|
|
|
import os
|
|
from passlib.context import CryptContext
|
|
crypt_context = CryptContext(schemes=['pbkdf2_sha512', 'plaintext'], deprecated=['plaintext'])
|
|
|
|
password = os.environ.get('PASSWORD_PLAIN')
|
|
print(crypt_context.hash(password))
|
|
```
|
|
|
|
## Command with named parameters
|
|
|
|
Assuming you have a `docker-compose.yml` and would like to start selected or all containers.
|
|
|
|
```bash
|
|
start() {
|
|
if [[ "$1" =~ 'db' ]]; then
|
|
docker compose up -d db
|
|
fi
|
|
|
|
if [[ "$1" =~ 'admin' ]]; then
|
|
docker compose up -d admin
|
|
echo 'Open http://localhost:8000 url in your browser.'
|
|
fi
|
|
|
|
if [[ "$1" =~ 'odoo' ]]; then
|
|
docker compose up -d odoo
|
|
echo 'Open http://localhost:8069 url in your browser.'
|
|
fi
|
|
|
|
if [[ "$1" =~ 'mail' ]]; then
|
|
docker compose up -d mail
|
|
echo 'Open http://localhost:8025 url in your browser.'
|
|
fi
|
|
}
|
|
```
|
|
|
|
Start all containers with `task start` and selected with `task start db,admin`.
|
|
|
|
## Run commands in container
|
|
|
|
Use `docker exec -i` to run commands in a container.
|
|
|
|
```bash
|
|
drop-db() {
|
|
local database="$1"
|
|
if [[ -z "$database" ]]; then
|
|
database="example"
|
|
fi
|
|
|
|
docker exec -i db psql "postgres://odoo:odoo@localhost:5432/postgres" -c "SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE datname = '$database';"
|
|
docker exec -i db psql "postgres://odoo:odoo@localhost:5432/postgres" -c "DROP DATABASE \"$database\";"
|
|
}
|
|
```
|
|
|
|
## Loop over files and folders
|
|
|
|
Use `for` to loop over file, folders or arrays.
|
|
|
|
```bash
|
|
render() {
|
|
echo "Update index.html for all folders"
|
|
for folder in ./*; do
|
|
if [[ -f "$folder/README.md" ]]; then
|
|
cd "$folder" || exit
|
|
md2html README.md _site/index.html
|
|
cd .. || exit
|
|
fi
|
|
done
|
|
}
|
|
```
|
|
|
|
## Parse and convert a file
|
|
|
|
With this function you can split a file into multiple parts whenever a specific keyword is matching. In this example the keyword is `!vault`.
|
|
|
|
```bash
|
|
convert-vault-file() {
|
|
local file_path="$1"
|
|
local temp_file=$(mktemp)
|
|
local temp_part_file=$(mktemp)
|
|
local write_finished=false
|
|
local current_key=""
|
|
|
|
while IFS= read -r line; do
|
|
# Check for keyword
|
|
if [[ "$line" =~ "!vault" ]]; then
|
|
# Process previous vault entry if it exists
|
|
if [[ "$write_finished" = true ]] && [[ -n "$current_key" ]] && [[ -s "$temp_part_file" ]]; then
|
|
# Decrypt part file and write to assemble file
|
|
ansible-vault decrypt "$temp_part_file"
|
|
local value=$(cat "$temp_part_file")
|
|
echo "$current_key: $value" >> "$temp_file"
|
|
fi
|
|
|
|
# Set up for new vault entry
|
|
current_key=$(echo "$line" | cut -d':' -f1)
|
|
# Clear the file
|
|
: > "$temp_part_file"
|
|
# Flag as ready to write
|
|
write_finished=true
|
|
else
|
|
if [[ "$write_finished" = true ]]; then
|
|
# Pipe into part file
|
|
echo "$line" >> "$temp_part_file"
|
|
fi
|
|
done
|
|
done < "$file_path"
|
|
|
|
# Process the final vault entry
|
|
if [[ "$write_finished" = true ]] && [[ -n "$current_key" ]] && [[ -s "$temp_part_file" ]]; then
|
|
ansible-vault decrypt "$temp_part_file"
|
|
local value=$(cat "$temp_part_file")
|
|
echo "$current_key: $value" >> "$temp_file"
|
|
fi
|
|
|
|
# Output assembled file
|
|
cat "$temp_file"
|
|
# Cleanup temp files
|
|
rm -f "$temp_file"
|
|
rm -f "$temp_part_file"
|
|
}
|
|
```
|
|
|
|
## Process data
|
|
|
|
This pattern tries to match the worklow of a jupyter notebook. Create a folder with the data processing scripts:
|
|
|
|
```
|
|
scripts
|
|
├── 01_import-mail-data
|
|
├── 02_transform-mail-data
|
|
└── 03_export-mail-data
|
|
```
|
|
|
|
The following function lists the scripts and asks for the number of scripts to run. Every script until the entered number is concatenated and executed.
|
|
|
|
```bash
|
|
process-data() {
|
|
while true; do
|
|
local scripts=($(ls scripts))
|
|
|
|
echo -e "Available scripts:\n"
|
|
for index in "${!scripts[@]}"; do
|
|
echo "$((index+1)): ${scripts[$index]}"
|
|
done
|
|
echo ""
|
|
read -p "Enter the number of scripts to run (or 'q' to quit): " user_input
|
|
if [[ "$user_input" = "q" ]]; then
|
|
break
|
|
fi
|
|
|
|
local temp_file=$(mktemp)
|
|
for index in "${!scripts[@]}"; do
|
|
if (( $user_input >= $((index+1)) )); then
|
|
cat "scripts/${scripts[$index]}" >> "$temp_file"
|
|
echo >> "$temp_file"
|
|
fi
|
|
done
|
|
|
|
clear
|
|
echo -e "\nPython Output:\n\n---"
|
|
python "$temp_file"
|
|
local exit_code=$?
|
|
echo -e "---\n"
|
|
echo -e "Exit code: $exit_code\n"
|
|
rm "$temp_file"
|
|
done
|
|
}
|
|
```
|