Automated update

This commit is contained in:
klein panic
2025-02-01 14:08:09 -05:00
parent 9dbf850d20
commit 22626c7ff4

View File

@@ -1,21 +1,19 @@
#!/usr/bin/env bash #!/usr/bin/env bash
set -euo pipefail
IFS=$'\n\t'
####################################### # Declare the security variable
# Security Check security_variable=2 # Change this value as needed (0, 1, or 2)
#######################################
security_variable=2 # Allowed values: 0, 1, or 2
# Check the value of the security variable
if [ "$security_variable" -eq 0 ]; then if [ "$security_variable" -eq 0 ]; then
echo "Error: security_variable is set to 0. Exiting the script." echo "Error: security_variable is set to 0. Exiting the script."
exit 1 exit 1
fi fi
# If the security variable is 2, continue without any conditions
if [ "$security_variable" -eq 2 ]; then if [ "$security_variable" -eq 2 ]; then
echo "security_variable is set to 2. Running the script without further checks." echo "security_variable is set to 2. Running the script without further checks."
else else
# When security_variable==1, only run on Friday. # If the security variable is 1, check if today is Friday
DAY_OF_WEEK=$(date +%A) DAY_OF_WEEK=$(date +%A)
if [ "$DAY_OF_WEEK" = "Friday" ]; then if [ "$DAY_OF_WEEK" = "Friday" ]; then
echo "Today is Friday. Continuing with the script." echo "Today is Friday. Continuing with the script."
@@ -25,47 +23,44 @@ else
fi fi
fi fi
####################################### # Check if required commands are available
# Dependency Checks for cmd in git nmcli msmtp ssh scp gh; do
#######################################
for cmd in git nmcli msmtp ssh scp gh timeout; do
if ! command -v "$cmd" &>/dev/null; then if ! command -v "$cmd" &>/dev/null; then
echo "$cmd is required but not installed. Exiting." echo "$cmd is required but not installed. Exiting."
exit 1 exit 1
fi fi
done done
#######################################
# Configuration # Configuration
#######################################
TXT_FILE="/home/klein/.config/setup/autogitupdate.txt" TXT_FILE="/home/klein/.config/setup/autogitupdate.txt"
CREDENTIALS_FILE="$HOME/.config/setup/credentials.config" CREDENTIALS_FILE="$HOME/.config/setup/credentials.config"
REPORT="/tmp/git_update_report.txt" # Full detailed report REPORT="/tmp/git_update_report.txt" # Full detailed report
SMS_REPORT="/tmp/git_update_sms.txt" # Minimal SMS summary SMS_REPORT="/tmp/git_update_sms.txt" # Minimal SMS summary
SMS_CHAR_LIMIT=160 # Character limit for SMS messages SMS_CHAR_LIMIT=160 # Character limit for SMS messages
SSH_DIR="/home/klein/reports" SSH_DIR="/home/klein/reports"
BRANCH_NAME="main" # Default branch name
# Check for the existence of the configuration file
if [ ! -f "$TXT_FILE" ]; then if [ ! -f "$TXT_FILE" ]; then
echo "Error: Configuration file '$TXT_FILE' not found. Exiting the script." echo "Error: Configuration file '$TXT_FILE' not found. Exiting the script."
exit 1 exit 1
fi fi
# Check if the credentials file exists
if [ ! -f "$CREDENTIALS_FILE" ]; then if [ ! -f "$CREDENTIALS_FILE" ]; then
echo "Error: Credentials file '$CREDENTIALS_FILE' not found. Exiting the script." echo "Error: Credentials file '$CREDENTIALS_FILE' not found. Exiting the script."
exit 1 exit 1
fi fi
# Source the credentials. # Source the credentials from the config file
source "$CREDENTIALS_FILE" source "$CREDENTIALS_FILE"
# Ensure required credentials are set. # Validate that required variables are set
if [ -z "${EMAIL:-}" ] || [ -z "${SSH_HOST:-}" ] || [ -z "${PHONE_NUMBER:-}" ]; then if [ -z "$EMAIL" ] || [ -z "$SSH_HOST" ] || [ -z "$PHONE_NUMBER" ]; then
echo "Error: Missing required credentials (EMAIL, SSH_HOST, or PHONE_NUMBER) in '$CREDENTIALS_FILE'. Exiting." echo "Error: Missing required credentials (EMAIL, SSH_HOST, or PHONE_NUMBER) in '$CREDENTIALS_FILE'. Exiting the script."
exit 1 exit 1
fi fi
# Ensure report files exist and have strict permissions. # Ensure the report files exist and have the correct permissions
ensure_file() { ensure_file() {
local file="$1" local file="$1"
local permissions="$2" local permissions="$2"
@@ -76,75 +71,96 @@ ensure_file() {
ensure_file "$REPORT" 600 ensure_file "$REPORT" 600
ensure_file "$SMS_REPORT" 600 ensure_file "$SMS_REPORT" 600
####################################### # Function to check WiFi connection and internet availability
# Helper Functions
#######################################
# Check WiFi connection and Internet availability.
check_wifi() { check_wifi() {
nmcli -t -f ACTIVE,SSID dev wifi | grep -q "^yes" && ping -c 1 8.8.8.8 &>/dev/null nmcli -t -f ACTIVE,SSID dev wifi | grep -q "^yes" && ping -c 1 8.8.8.8 &>/dev/null
return $? return $?
} }
# Read the repository directories from TXT_FILE.
get_repo_dirs() { get_repo_dirs() {
local valid_dirs=() local valid_dirs=()
while IFS= read -r repo_dir; do while IFS= read -r repo_dir; do
# Trim whitespace and skip empty lines or comment lines. # Trim whitespace and skip empty lines
repo_dir=$(echo "$repo_dir" | xargs) repo_dir=$(echo "$repo_dir" | xargs)
if [[ -z "$repo_dir" || "$repo_dir" == \#* ]]; then if [[ -z "$repo_dir" || "$repo_dir" == \#* ]]; then
continue continue
fi fi
# Only add directories that exist and contain a .git folder. # Check if the directory is valid and contains a .git folder
if [ -d "$repo_dir" ] && [ -d "$repo_dir/.git" ]; then if [ -d "$repo_dir" ] && [ -d "$repo_dir/.git" ]; then
valid_dirs+=("$repo_dir") valid_dirs+=("$repo_dir")
fi fi
done < "$TXT_FILE" done < "$TXT_FILE"
# Output each valid repository on its own line.
printf "%s\n" "${valid_dirs[@]}" # Output only the valid directories without any additional text
echo "${valid_dirs[@]}"
} }
# Create a GitHub repository from a local repository. # Function to create GitHub repo if it doesn't exist and push changes
create_github_repo() { create_github_repo() {
local repo_name="$1" local repo_name="$1"
local repo_dir="$2" local repo_dir="$2"
local repo_number="$3" local repo_number="$3"
echo "Attempting to create GitHub repo for repo $repo_number ($repo_name)" >> "$REPORT" echo "Attempting to create GitHub repo for repo $repo_number ($repo_name)" >> "$REPORT"
# The GH command uses the absolute path as source. # Use GitHub CLI to create the repo
if gh repo create "$repo_name" --public --source="$repo_dir" --remote=origin --push; then if gh repo create "$repo_name" --public --source="$repo_dir" --remote=origin --push; then
echo "Successfully created GitHub repository #$repo_number: $repo_name" >> "$REPORT" echo "Successfully created GitHub repository #$repo_number: $repo_name" >> "$REPORT"
echo "Repo #$repo_number $repo_name: GHS" >> "$SMS_REPORT" echo "Repo #$repo_number $repo_name: GHS" >> "$SMS_REPORT"
echo "Success GHS" echo "Success GHS"
# Check if the repository has existing commits
if [ -d "$repo_dir/.git" ] && [ "$(git -C "$repo_dir" rev-parse HEAD)" ]; then
echo "Repository #$repo_number has existing commits. Attempting to add remote and push." >> "$REPORT"
# Add the GitHub remote if not already added
git remote add origin "git@github.com:kleinpanic/$repo_name.git" 2>/dev/null || true
# Set the main branch if necessary
git branch -M main
# Push to GitHub
if git push -u origin main; then
echo "Existing repository #$repo_number pushed successfully to GitHub." >> "$REPORT"
echo "Repo #$repo_number: PS" >> "$SMS_REPORT"
echo "Repo PS"
else
echo "Failed to push the existing repository #$repo_number to GitHub." >> "$REPORT"
echo "Repo #$repo_number: PF" >> "$SMS_REPORT"
echo "Repo PF"
fi
fi
return 0
else else
echo "Failed to create GitHub repository #$repo_number: $repo_name" >> "$REPORT" echo "Failed to create GitHub repository #$repo_number: $repo_name" >> "$REPORT"
echo "Repo #$repo_number: GCF" >> "$SMS_REPORT" echo "Repo #$repo_number: GCF" >> "$SMS_REPORT"
echo "GCF Repo" echo "GCF Repo"
return 1 return 1
fi fi
return 0
} }
# Check if a remote GitHub origin exists. # Function to check if a remote GitHub origin exists
check_github_remote() { check_github_remote() {
if ! remote_url=$(git remote get-url origin 2>/dev/null); then if ! git remote get-url origin &>/dev/null; then
echo "No 'origin' remote found." >> "$REPORT" echo "No 'origin' remote found." >> "$REPORT"
return 1 return 1
fi fi
echo "Debug: Found remote URL: $remote_url" >> "$REPORT"
local remote_url
remote_url=$(git remote get-url origin)
if [[ "$remote_url" != *github.com* ]]; then if [[ "$remote_url" != *github.com* ]]; then
echo "'origin' remote does not point to GitHub." >> "$REPORT" echo "'origin' remote does not point to GitHub." >> "$REPORT"
return 1 return 1
fi fi
local repo_name repo_owner full_repo_name local repo_name
repo_name=$(basename "$remote_url" .git) repo_name=$(basename "$remote_url" .git)
local repo_owner
repo_owner=$(basename "$(dirname "$remote_url")") repo_owner=$(basename "$(dirname "$remote_url")")
full_repo_name="$repo_owner/$repo_name" local full_repo_name="$repo_owner/$repo_name"
echo "Checking if GitHub repository '$full_repo_name' exists..." >> "$REPORT" echo "Checking if GitHub repository '$full_repo_name' exists..." >> "$REPORT"
if ! gh repo view "$full_repo_name" &>/dev/null; then if ! gh repo view "$full_repo_name" &>/dev/null; then
echo "GitHub repository '$full_repo_name' does not exist or is not accessible." >> "$REPORT" echo "GitHub repository '$full_repo_name' does not exist or is not accessible." >> "$REPORT"
return 1 return 1
@@ -153,7 +169,7 @@ check_github_remote() {
return 0 return 0
} }
# Check if the repository is up-to-date. # Function to check if the repository is up-to-date
is_repo_up_to_date() { is_repo_up_to_date() {
git remote update &>/dev/null git remote update &>/dev/null
if git status -uno | grep -q "Your branch is up to date"; then if git status -uno | grep -q "Your branch is up to date"; then
@@ -165,8 +181,7 @@ is_repo_up_to_date() {
fi fi
} }
# Verify that the repositorys .git folder is valid. # Function to check if the .git folder is valid
# (This function does not treat an empty repository as an error.)
is_valid_git_repo() { is_valid_git_repo() {
local repo_dir="$1" local repo_dir="$1"
@@ -175,6 +190,7 @@ is_valid_git_repo() {
return 1 return 1
fi fi
# Check for essential .git files
for file in HEAD config; do for file in HEAD config; do
if [ ! -f "$repo_dir/.git/$file" ]; then if [ ! -f "$repo_dir/.git/$file" ]; then
echo "Error: Missing '$file' in '.git' directory of '$repo_dir'. Skipping this repository." >> "$REPORT" echo "Error: Missing '$file' in '.git' directory of '$repo_dir'. Skipping this repository." >> "$REPORT"
@@ -182,53 +198,36 @@ is_valid_git_repo() {
fi fi
done done
# Do not fail here if no commits exist; we'll handle it later. # Ensure the repository has at least one commit
if ! git -C "$repo_dir" rev-parse HEAD &>/dev/null; then
echo "Error: The repository in '$repo_dir' appears to be corrupt or does not have any commits." >> "$REPORT"
return 1
fi
return 0 return 0
} }
# Logging function to write timestamped messages. # A simple logging function to write timestamped messages
log_msg() { log_msg() {
local msg="$1" local msg="$1"
echo "$(date '+%Y-%m-%d %H:%M:%S') - $msg" >> "$REPORT" echo "$(date '+%Y-%m-%d %H:%M:%S') - $msg" >> "$REPORT"
} }
#######################################
# Core Update Function
#######################################
update_repo() { update_repo() {
local repo_dir="$1" local repo_dir="$1"
local repo_number="$2" local repo_number="$2"
cd "$repo_dir" || { log_msg "Repo #$repo_number: Could not change directory to $repo_dir"; return 1; } cd "$repo_dir" || return 1
log_msg "Processing repository #$repo_number at $repo_dir" log_msg "Processing repository #$repo_number at $repo_dir"
# Validate the repository structure. # Validate the .git folder.
if ! is_valid_git_repo "$repo_dir"; then if ! is_valid_git_repo "$repo_dir"; then
log_msg "Repo #$repo_number: Invalid repository structure. Skipping." log_msg "Repo #$repo_number: Invalid repository. Skipping."
echo "Invalid .git" >> "$SMS_REPORT" echo "Invalid .git" >> "$SMS_REPORT"
return 1 return 1
fi fi
# If no commit exists, create an initial empty commit. # Check for GitHub remote; try creating if absent.
if ! git rev-parse HEAD &>/dev/null; then
log_msg "Repo #$repo_number: No commits found. Creating an initial empty commit."
if ! git commit --allow-empty -m "Initial commit"; then
log_msg "Repo #$repo_number: Failed to create an initial commit."
echo "Repo #$repo_number: No commit" >> "$SMS_REPORT"
return 1
fi
fi
# Check for detached HEAD state.
local current_branch
current_branch=$(git rev-parse --abbrev-ref HEAD)
if [ "$current_branch" == "HEAD" ]; then
log_msg "Repo #$repo_number: Detached HEAD state. Skipping repository."
echo "Repo #$repo_number: Detached HEAD" >> "$SMS_REPORT"
return 1
fi
# Check for a valid GitHub remote.
local remote_available=true local remote_available=true
if ! check_github_remote; then if ! check_github_remote; then
log_msg "Repo #$repo_number: No valid GitHub remote found." log_msg "Repo #$repo_number: No valid GitHub remote found."
@@ -237,18 +236,18 @@ update_repo() {
local repo_name local repo_name
repo_name=$(basename "$repo_dir") repo_name=$(basename "$repo_dir")
create_github_repo "$repo_name" "$repo_dir" "$repo_number" create_github_repo "$repo_name" "$repo_dir" "$repo_number"
# Recheck the remote. # Recheck the remote
if check_github_remote; then if check_github_remote; then
remote_available=true remote_available=true
else else
log_msg "Repo #$repo_number: Failed to create remote repository." log_msg "Repo #$repo_number: Failed to create remote repository."
fi fi
else else
log_msg "Repo #$repo_number: No WiFi. Will commit locally only." log_msg "Repo #$repo_number: No WiFi. Committing locally only."
fi fi
fi fi
# Check if there are any local changes. # Check for any local changes (staged, unstaged, or untracked)
if [ -z "$(git status --porcelain)" ]; then if [ -z "$(git status --porcelain)" ]; then
log_msg "Repo #$repo_number: No local changes detected." log_msg "Repo #$repo_number: No local changes detected."
if [ "$remote_available" = false ]; then if [ "$remote_available" = false ]; then
@@ -256,13 +255,14 @@ update_repo() {
else else
echo "Repo #$repo_number: NC-GE" >> "$SMS_REPORT" echo "Repo #$repo_number: NC-GE" >> "$SMS_REPORT"
fi fi
echo "NC-GE"
return 0 return 0
fi fi
# Stage all changes. # Stage all changes.
git add -A git add -A
# Commit changes (overriding PGP signing requirement for automation). # Commit changes; override PGP signing requirement for automation.
if git -c commit.gpgSign=false commit -m "Automated update"; then if git -c commit.gpgSign=false commit -m "Automated update"; then
log_msg "Repo #$repo_number: Local commit succeeded." log_msg "Repo #$repo_number: Local commit succeeded."
else else
@@ -271,53 +271,51 @@ update_repo() {
return 0 return 0
fi fi
# If a remote exists and WiFi is available, attempt a pull with rebase. # If a remote exists and WiFi is available, perform a pull with rebase.
if [ "$remote_available" = true ] && check_wifi; then if [ "$remote_available" = true ] && check_wifi; then
log_msg "Repo #$repo_number: Attempting pull --rebase on branch '$current_branch'." log_msg "Repo #$repo_number: Attempting pull --rebase."
if timeout 60s git pull --rebase --no-edit origin "$current_branch"; then # Use a timeout (60 seconds in this example) and --no-edit to avoid interactive prompts.
if timeout 60s git pull --rebase --no-edit origin main; then
log_msg "Repo #$repo_number: Rebase succeeded." log_msg "Repo #$repo_number: Rebase succeeded."
else else
log_msg "Repo #$repo_number: Rebase failed; aborting and attempting merge fallback." log_msg "Repo #$repo_number: Rebase timed out or encountered conflicts; aborting rebase."
git rebase --abort || log_msg "Repo #$repo_number: Failed to abort rebase cleanly." git rebase --abort
if timeout 60s git pull --no-edit origin "$current_branch"; then echo "Repo #$repo_number: Rebase Conflict" >> "$SMS_REPORT"
log_msg "Repo #$repo_number: Merge fallback succeeded." echo "MC"
else return 1
log_msg "Repo #$repo_number: Merge fallback failed. Manual intervention required."
echo "Repo #$repo_number: Rebase/Merge Conflict" >> "$SMS_REPORT"
return 1
fi
fi fi
else else
log_msg "Repo #$repo_number: Skipping pull/rebase (no remote or no internet)." log_msg "Repo #$repo_number: Skipping pull/rebase (no remote or no internet)."
fi fi
# Push changes if remote is available and internet is active. # Push changes if a remote is available and there is internet.
if [ "$remote_available" = true ] && check_wifi; then if [ "$remote_available" = true ] && check_wifi; then
log_msg "Repo #$repo_number: Attempting push on branch '$current_branch'." log_msg "Repo #$repo_number: Attempting push."
if timeout 60s git push origin "$current_branch"; then if timeout 60s git push origin main; then
log_msg "Repo #$repo_number: Push succeeded." log_msg "Repo #$repo_number: Push succeeded."
echo "Repo #$repo_number: P" >> "$SMS_REPORT" echo "Repo #$repo_number: P" >> "$SMS_REPORT"
echo "P"
else else
log_msg "Repo #$repo_number: Push timed out or failed." log_msg "Repo #$repo_number: Push timed out or failed."
echo "Repo #$repo_number: PF" >> "$SMS_REPORT" echo "Repo #$repo_number: PF" >> "$SMS_REPORT"
echo "PF"
return 1 return 1
fi fi
else else
log_msg "Repo #$repo_number: No internet; changes committed locally." log_msg "Repo #$repo_number: No internet; changes committed locally."
echo "Repo #$repo_number: LC" >> "$SMS_REPORT" echo "Repo #$repo_number: LC" >> "$SMS_REPORT"
echo "LC"
fi fi
} }
####################################### # Start of the script
# Main Script Execution
#######################################
echo "Starting auto git update script on $(date)" > "$REPORT" echo "Starting auto git update script on $(date)" > "$REPORT"
echo "(G-R), $(date)" > "$SMS_REPORT" echo "(G-R), $(date)" > "$SMS_REPORT"
# Read valid repository directories into an array. # Fetch valid repositories
mapfile -t repo_dirs < <(get_repo_dirs) repo_dirs=($(get_repo_dirs))
# Initialize counters for SMS summary. # Counters for SMS summary report
pushed_count=0 pushed_count=0
no_change_count=0 no_change_count=0
local_commit_count=0 local_commit_count=0
@@ -327,6 +325,7 @@ created_count=0
no_github_repo_count=0 no_github_repo_count=0
total_repos=${#repo_dirs[@]} total_repos=${#repo_dirs[@]}
# Loop through each valid directory
repo_number=1 repo_number=1
for repo_dir in "${repo_dirs[@]}"; do for repo_dir in "${repo_dirs[@]}"; do
echo "Updating repository #$repo_number: $repo_dir" echo "Updating repository #$repo_number: $repo_dir"
@@ -343,40 +342,45 @@ for repo_dir in "${repo_dirs[@]}"; do
repo_number=$((repo_number + 1)) repo_number=$((repo_number + 1))
done done
# If the SMS report exceeds the character limit, generate a concise summary. # Check if the detailed SMS report exceeds the SMS character limit
sms_detailed_content=$(cat "$SMS_REPORT") sms_detailed_content=$(cat "$SMS_REPORT")
if [ "${#sms_detailed_content}" -gt "$SMS_CHAR_LIMIT" ]; then if [ "${#sms_detailed_content}" -gt "$SMS_CHAR_LIMIT" ]; then
DATE=$(date "+%Y-%m-%d %H:%M:%S") DATE=$(date "+%Y-%m-%d %H:%M:%S")
echo "Concat output activated"
sms_summary="$DATE. Pushed: $pushed_count/$total_repos, No Change: $no_change_count/$total_repos, Locally Committed: $local_commit_count/$total_repos, Conflicts: $conflict_count/$total_repos, Errors: $error_count/$total_repos, No GitHub Repo: $no_github_repo_count/$total_repos, Created: $created_count/$total_repos" sms_summary="$DATE. Pushed: $pushed_count/$total_repos, No Change: $no_change_count/$total_repos, Locally Committed: $local_commit_count/$total_repos, Conflicts: $conflict_count/$total_repos, Errors: $error_count/$total_repos, No GitHub Repo: $no_github_repo_count/$total_repos, Created: $created_count/$total_repos"
echo "$sms_summary" > "$SMS_REPORT" echo "$sms_summary" > "$SMS_REPORT"
fi fi
# Function to send an SMS/text notification.
send_text() { send_text() {
/usr/bin/msmtp -a default -t <<EOF /usr/bin/msmtp -a default -t <<EOF
To: $PHONE_NUMBER To: $PHONE_NUMBER
From: $EMAIL From: $EMAIL
Subject: Git Update Report Subject: $SUBJECT
$(cat "$SMS_REPORT") $(cat "$SMS_REPORT")
EOF EOF
return $? return $?
} }
# Function to copy the detailed report to a remote host via SSH.
copy_report_ssh() { copy_report_ssh() {
# Ensure SSH_DIR is defined
if [ -z "$SSH_DIR" ]; then if [ -z "$SSH_DIR" ]; then
echo "Error: SSH_DIR is not set. Please define it in your script." echo "Error: SSH_DIR is not set. Please define it in your script."
return 1 return 1
fi fi
# Create the remote directory if it doesn't exist
ssh "$SSH_HOST" "mkdir -p $SSH_DIR" ssh "$SSH_HOST" "mkdir -p $SSH_DIR"
# Transfer the report file to the specified directory
rsync -avz --progress "$REPORT" "$SSH_HOST:$SSH_DIR/" rsync -avz --progress "$REPORT" "$SSH_HOST:$SSH_DIR/"
return $? return $?
} }
if check_wifi; then if check_wifi; then
echo "WiFi is connected. Attempting to send text message and perform SSH transfer..." echo "WiFi is connected. Attempting to send text message and SSH transfer..."
send_text && echo "Text message sent successfully." || echo "Failed to send text message." send_text && echo "Text message sent successfully." || echo "Failed to send text message."
if copy_report_ssh; then if copy_report_ssh; then
echo "SSH transfer succeeded." echo "SSH transfer succeeded."
rm -f "$REPORT" rm -f "$REPORT"
@@ -386,4 +390,3 @@ if check_wifi; then
else else
echo "No WiFi connection detected. Changes committed locally." echo "No WiFi connection detected. Changes committed locally."
fi fi