#!/bin/bash ################################################################################ # Update the local binary files with the uversion files from the fossil repo. ################################################################################ # # Exit Error Codes: # # The exit error codes are based on the /usr/include/sysexits.h for all # non-zero errors. An exit code of '0' is success. # ############################################################################### # # Bugs: # - Nothing to admit # ############################################################################### # # Todo: # - Removing files is a destructive and should be done only by request (-r) # - Add more Todo items # ############################################################################### ############################################################################### # Variables: Read-Only ######################################## # Error Codes readonly Error_Data=65 # EX_DATAERR readonly Error_NoInput=66 # EX_NOINPUT readonly Error_NoPerm=77 # EX_NOPERM readonly Error_TempFail=75 # EX_TEMPFAIL readonly Error_Unavailable=69 # EX_UNAVAILABLE readonly Error_Usage=64 # EX_USAGE ######################################## # Grep Exit Codes readonly Grep_Found_Match=0 readonly Grep_No_Match=1 readonly Grep_Error=2 ######################################## # Page Viewer if [ -n "`command -v less 2> /dev/null`" ] then readonly page_viewer="less --no-init --quit-if-one-screen" elif [ -n "`command -v more 2> /dev/null`" ] then readonly page_viewer="more" else readonly page_viewer="" fi ######################################## # Project Values readonly program_name=`basename $0` readonly project_path=$(fossil info | grep 'local-root:' | sed 's/.*: *\(.*\)[\/]/\1/') ############################################################################### # Variables: Internal action="" export_list=() file_list="" ############################################################################### # Variables: Arg Configurable category_list="" debug_mode="" dest_path="" dry_run="" make_dirs="" separator=":" ############################################################################### main() { for category in $category_list do export_list=( $(fossil uv ls | grep "$category$separator") ) done if [ -n "$dry_run" ] then action=ignore fi if [ -n "$debug_mode" ] then echo "--- Internally Defined Values ---" echo " page_viewer: \"$page_viewer\"" echo " program_name: \"$program_name\"" echo " project_path: \"$project_path\"" echo " action: \"$action\"" echo "" echo "--- Argument Based Values ---" echo " category_list: \"$category_list\"" echo " dest_path: \"$dest_path\"" echo " dry_run: \"$dry_run\"" echo " make_dirs: \"$make_dirs\"" echo " separator: \"$separator\"" exit $Error_TempFail fi if [ ! -d "$dest_path" ] then $action mkdir -p "$dest_path" fi remove_files update_files export_files } ################################################################################ # Parse the export name to get the file name get_export_file() { echo $1 | sed "s_.*$separator\(.*\)_\1_" } ################################################################################ # Do nothing and do it well ignore() { echo -n } ################################################################################ # Compare the currently existing files with the unversioned files (uv_list). # Any file that is not in the uv_list, should be removed unless those files are # in the fossil repo. This covers the use-case of unversioned files being # deleted or renamed. remove_files() { if [ ! -d "$dest_path" ] then return fi uv_list=$(fossil uv ls) for file_path in `find "$dest_path" -not -name '.*' -type f | sed 's|^\./||'` do # Get the path relative to the project root #file=${file_path:${#project_path}+1} # if $file_path is in the repo, skip it fossil finfo "$file_path" &> /dev/null if [ "$?" == "0" -o -L "$file_path" ] then continue fi # if $file is not in the $uv_list file=$(basename $file_path) echo $uv_list | grep "$separator$file" &> /dev/null if [ "$?" == "$Grep_No_Match" ] then echo "Removing \"$file_path\"" $action rm -f "$file_path" fi done } ################################################################################ # Check the unversioned file list to see if there are any missing files in the # check-out. If a file is missing, export it into existance. export_files() { if [ ! -d "$dest_path" ] then return fi for export_file in ${export_list[@]} do file=$(get_export_file $export_file) if [ ! -f "$file" ] then echo "Exporting \"$export_file\"" $action fossil uv export "$export_file" "$dest_path/$file" fi done } ################################################################################ # If an unversion file already exists, check the timestamps and if the file in # the repo is newer, replace the old file with a freshly exported file. update_files() { if [ ! -d "$dest_path" ] then return fi cd $dest_path for export_file in ${export_list[@]} do file=$(get_export_file $export_file) if [ -f "$file" ] then timestamp=$(fossil uv list --like "$export_file" | sed 's/.* \(....-..-.. ..:..:..\) .*/\1/') repo_time=$(date +"%s" -u -d "$timestamp") timestamp=$(stat --format="%y" $file) file_time=$(date +"%s" -u -d "$timestamp") if [ $repo_time -gt $file_time ] then echo "Updating \"$dest_path/$file\"" $action fossil uv export "$export_file" "$dest_path/$file" fi fi done cd - &> /dev/null } ################################################################################ show_usage() { if [ -n "$page_viewer" ] then usage | $page_viewer else usage fi } ################################################################################ usage() { cat <<- USAGE_END Usage: $program_name [Options] Category [Category [Category ...]] Dest Options: --debug Display the internal values that would be used to create the requested class then exit. -h, --help Show this message and exit. -n, --dry-run Only print what would be done. No file changes will be made. Note: The destination directory must exist to use this option. -p, --parents Make parent directories as needed. -s, --separator CHAR Specify the character that separates the category name from the file name in the repository. Default: ":" Arguments: Category All the files with this category will be exported from the repository. Multiple categories can be provided. Dest The location of where to place the exported files. This path can be absolute or relative to the project root directory. However, the destination MUST be somewhere within the project. USAGE_END } ############################################################################### # Parse the args arg_list=() while [ "$1" != "" ] do case $1 in --debug) debug_mode="yes" ;; -h|--help) show_usage exit 0 ;; -n|--dry-run) dry_run="yes" ;; -p|--parents) make_dirs="yes" ;; -s|--separator) shift separator="$1" ;; -*) echo "Error: Option \"$1\" is not supported." echo "Use \"-h\" for help:" echo " $program_name -h" exit $Error_Usage ;; *) arg_list+=($1) ;; esac shift done # Bash Array Syntax # ${array_name[@]} Get all elements # ${#array_name[@]} Count all elements # ${array_name[@]:I:N} Get N elements starting at I category_list=${arg_list[@]:0:${#arg_list[@]}-1} dest_path=${arg_list[@]:${#arg_list[@]}-1:1} ############################################################################### # Make sure that all required information is present and valid ### Category ### if [ -z "$category_list" ] then echo "Error: No categories were provided" echo "Use \"-h\" for help:" echo " $program_name -h" exit $Error_Usage fi uv_list=$(fossil uv ls) for category in $category_list do echo $uv_list | grep "$category$separator" &> /dev/null if [ "$?" == "$Grep_No_Match" ] then echo "Error: Category Not Found: $category" echo "Use \"-h\" for help:" echo " $program_name -h" exit $Error_Usage fi done ### Destination ### if [ -z "$dest_path" ] then echo "Error: No destination path was provided" echo "Use \"-h\" for help:" echo " $program_name -h" exit $Error_Usage fi if [[ $dest_path != /* ]] then dest_path="$project_path/$dest_path" fi dest_path=$(readlink --canonicalize $dest_path) echo $dest_path | grep "^$project_path" &> /dev/null if [ "$?" == "$Grep_No_Match" ] then echo "Error: Invalid destination, not within project root" echo "Use \"-h\" for help:" echo " $program_name -h" exit $Error_Usage fi if [ ! -d "$dest_path" -a -z "$make_dirs" ] then echo "Error: Invalid destination, \"$dest_path\" does not exist" echo "Use \"-h\" for help:" echo " $program_name -h" exit $Error_Usage fi ### Dry Run ### if [ ! -d "$dest_path" -a -n "$dry_run" ] then echo "Error: Destination must exist to use --dry-run (-n)" echo "Use \"-h\" for help:" echo " $program_name -h" exit $Error_Usage fi ############################################################################### # Everything is now ready main exit 0