#!/bin/bash # # Pod Management Task Script for Docker or Podman. # # Do not run `./pm-task` directly, since the `cd` command will not work in this # way. Instead, run in `source pm-task` pattern. There is already an alias for # this in the `startup.sh` script, after running that script, simply run 'pm' to # use this script. # # To work with Podman, simply make `docker` an aliase to the `podman` command. # Image building with docker compose. function subBuild() { if [ -f compose.yaml ] || [ -f docker-compose.yml ] || [ -f container-compose.yml ]; then ls -lah export RANDOM_UUID=`uuid` docker compose build read -ei Y -p "Restart container(s)? " confirm if [[ $confirm =~ ^[Yy]$ ]]; then docker compose down && docker compose up -d fi # read -e -p "Restart nginx service in container? " confirm # if [[ $confirm =~ ^[Yy]$ ]]; then # docker exec -d nginx service nginx reload # fi # Assume nginx is always in place. docker exec -d nginx service nginx reload else echo 'Cannot build (niether compose.yaml, docker-compose.yml nor container-compose.yml is found).' fi } # Get config file path function getConfigFile() { echo "$HOME/.config/pmtask.conf" } # Read docker-workspace from config file, or return empty string function getWorkspaceFromConfig() { config_file=$(getConfigFile) if [ -f "$config_file" ]; then # Parse INI file for docker-workspace option (skip comments and sections) while IFS= read -r line || [ -n "$line" ]; do # Skip empty lines, comments, and section headers case "$line" in ''|'#'*|'['*) continue ;; esac # Parse key=value key=$(echo "$line" | cut -d'=' -f1 | tr -d ' ') value=$(echo "$line" | cut -d'=' -f2- | tr -d ' ') if [ "$key" = "docker-workspace" ]; then echo "$value" return 0 fi done < "$config_file" fi return 1 } # Save workspace to config file function saveWorkspaceToConfig() { config_file=$(getConfigFile) config_dir=$(dirname "$config_file") # Create config directory if it doesn't exist if [ ! -d "$config_dir" ]; then mkdir -p "$config_dir" fi # Check if config file exists and has docker-workspace if [ -f "$config_file" ]; then # Update existing entry temp_file=$(mktemp) found=0 while IFS= read -r line || [ -n "$line" ]; do case "$line" in docker-workspace=*) echo "docker-workspace=$1" found=1 ;; *) echo "$line" ;; esac done < "$config_file" > "$temp_file" # Append if not found if [ "$found" -eq 0 ]; then echo "docker-workspace=$1" >> "$temp_file" fi mv "$temp_file" "$config_file" else echo "docker-workspace=$1" > "$config_file" fi } # List available projects in a workspace and let user choose one. # Sets PROJECT_LIST (newline-separated) and PROJECT_COUNT globals function subListProjects() { workspace="$1" PROJECT_LIST="" PROJECT_COUNT=0 # Get list of subdirectories (excluding special dirs) if [ -d "$workspace" ]; then for dir in "$workspace"/*/; do [ -d "$dir" ] || continue bname=$(basename "$dir") # Skip hidden dirs and README* dirs case "$bname" in '.'|'..'|'#'*|''|' '*) continue ;; README*) continue ;; esac PROJECT_COUNT=$((PROJECT_COUNT + 1)) if [ -z "$PROJECT_LIST" ]; then PROJECT_LIST="$bname" else PROJECT_LIST="$PROJECT_LIST $bname" fi done fi } function subGotoWorkspace() { workspace="" # If no argument provided, list projects and let user choose if [ -z "$1" ]; then # Get workspace from config or prompt user workspace=$(getWorkspaceFromConfig) if [ -z "$workspace" ]; then # No config, prompt user for workspace echo "No workspace configured." echo "Enter workspace path (e.g., $HOME/docker or /mnt/workspace/podman): " read -r workspace if [ -z "$workspace" ]; then echo "Workspace path cannot be empty." exit 1 fi # Save to config saveWorkspaceToConfig "$workspace" fi # Validate workspace exists if [ ! -d "$workspace" ]; then echo "Workspace directory does not exist: $workspace" exit 1 fi PROJECT_LIST="" PROJECT_COUNT=0 subListProjects "$workspace" if [ "$PROJECT_COUNT" -eq 0 ]; then echo "No projects found in $workspace" cd "$workspace" return 0 fi echo "Available projects in $workspace:" count=1 echo "$PROJECT_LIST" | while read -r project; do echo " [$count] $project" count=$((count + 1)) done echo " [0] Cancel" read -r choice case "$choice" in ''|*[!0-9]*) echo "Invalid choice. Canceling." cd "$workspace" return 0 ;; esac if [ "$choice" -eq 0 ]; then echo "Canceled." cd "$workspace" return 0 fi if [ "$choice" -gt "$PROJECT_COUNT" ]; then echo "Invalid choice. Canceling." cd "$workspace" return 0 fi # Get the selected project selected=$(echo "$PROJECT_LIST" | sed -n "${choice}p") workspace="$workspace/$selected" else # Workspace provided via config, append project name workspace=$(getWorkspaceFromConfig) if [ -z "$workspace" ]; then # No config, prompt user echo "No workspace configured." echo "Enter workspace path: " read -r workspace saveWorkspaceToConfig "$workspace" fi workspace="$workspace/$1" fi cd "$workspace" } # Application logs streaming (single log). function subLogs() { docker logs -f $POD # # $1 is $2 of the script. # if [ $1 ]; then # # ELABORATE # docker exec -it $1 tail -s 5 -f /var/www/app/storage/logs/laravel.log # else # docker exec -it $POD tail -s 5 -f /var/www/app/storage/logs/laravel.log # fi } # Application logs streaming (daily log). function subLogsDaily() { # $1 is $2 of the script. if [ $1 ]; then # ELABORATE docker exec -it $1 tail -s 5 -f /var/www/app/storage/logs/laravel-$TODAY.log else docker exec -it $POD tail -s 5 -f /var/www/app/storage/logs/laravel-$TODAY.log fi } # Reload service in container (hard-coded, limited usage). function subReload() { case $POD in nginx) docker exec -it $POD service nginx reload ;; *) # docker exec -it $POD service apache2 reload # No longer using Apache. docker exec -it $POD service nginx reload ;; esac } function subRestart() { docker compose down && docker compose up -d } # Container shell access. function subShell() { # $1 is $2 of the script. if [ $1 ]; then docker exec -it $1 bash else docker exec -it $POD bash fi } # Help messages (keep at bottom). ---------------------------------------------- function subHelp() { echo "Usage: pm-task [...]" echo "Commands:" echo " build, rebuild Build the Docker Compose project." echo " daily, today Stream daily logs for the current project." echo " down, stop Stop and remove containers." echo " logs, log Stream logs for the current project." echo " ps Watch container stats." echo " reload Reload the service in the current project." echo " restart Restart the Docker Compose project." echo " stats Display container resource usage statistics." echo " up, start Start the Docker Compose project." echo " bash|rsh|sh Access a shell in the running container." echo " (no command) List available projects and choose one to navigate to." } # Task if [ $1 ]; then # Assume the host is running podman when alias docket='podman' exists. if [ $(type -t docker) == "alias" ]; then COMMAND='podman' fi # Assumed container is named after the directory name. TASK=$1 TODAY=$(date +%Y-%m-%d) POD=${PWD##*/} case $TASK in bash|rsh|sh) subShell $2 ;; build|rebuild) subBuild ;; daily|today) subLogsDaily $2 ;; down|stop) docker compose down ;; logs|log) subLogs $2 ;; ps) # Because `watch` has no idea of the alias. eval "watch -n 10 $COMMAND ps" ;; reload) subReload ;; restart) if [ $2 ]; then subGotoWorkspace $2 fi subRestart $2 ;; stats) docker stats ;; up|start) docker compose up -d ;; upgrade) docker compose pull && docker compose up -d --remove-orphans --force-recreate ;; --help) subHelp ;; *) subGotoWorkspace $TASK esac # Go to the workspace when no argument is provided. else echo "Usage: pm-task [...]" echo "Run 'pm-task --help' for more information." subGotoWorkspace fi