3 # Author: Chris Koeritz
4 # Author: Kevin Wentworth
6 # This contains a bunch of reusable functions that help out in managing websites.
8 # This script is sourced, and relies on the value of THISDIR, which should
9 # point at the directory where the site management scripts are stored,
10 # especially this one.
12 source "$FEISTY_MEOW_SCRIPTS/core/launch_feisty_meow.sh"
14 export SSM_LOG_FILE="$TMP/$(fm_username)-siteavenger-script.log"
16 # configure feisty revision control to ignore vendor folders.
17 export NO_CHECKIN_VENDOR=true
19 # handles the computation of the base application path and the app dir name.
20 # this expects to be passed the application directory name, but it will attempt to
21 # do something intelligent if no name is passed in.
22 function autoconfigure_paths()
24 export app_dirname="$1"; shift
26 if [ -z "$app_dirname" ]; then
27 echo "$(date_stringer): Guessing application dir from local folder."
28 app_dirname="$(basename $(\pwd))"
29 export BASE_APPLICATION_PATH="$(dirname $(\pwd))"
30 echo "calculated application dir of '$app_dirname' and"
31 echo "a base app path of '$BASE_APPLICATION_PATH'"
34 # get our configuration loaded, if we know the config file.
35 # if there is none, we will use our default version.
36 export SITE_MANAGEMENT_CONFIG_FILE
37 if [ -z "$SITE_MANAGEMENT_CONFIG_FILE" ]; then
38 SITE_MANAGEMENT_CONFIG_FILE="$THISDIR/config/default.app"
39 echo "$(date_stringer): Site management config file was not set. Using default:" >> "$SSM_LOG_FILE"
40 echo "$(date_stringer): $SITE_MANAGEMENT_CONFIG_FILE" >> "$SSM_LOG_FILE"
43 # load in at least the default version to get us moving.
44 source "$SITE_MANAGEMENT_CONFIG_FILE"
45 exit_on_error "loading site management configuration from: $SITE_MANAGEMENT_CONFIG_FILE"
48 echo "after site config file sourced, app dirname now '$app_dirname' and"
49 echo "base app path now '$BASE_APPLICATION_PATH'"
53 # tests that the main storage folder for apps exists.
54 # the parameter passed in should be the application directory name (app_dirname), without
55 # any additional path components. the script will attempt to auto-configure the application
56 # base path (above the project folder with app_dirname) and get all the other path variables
58 function check_apps_root()
60 local temp_app_dirname="$1"; shift
62 echo new call to auto conf func...
63 autoconfigure_paths "$temp_app_dirname"
64 echo after call to auto conf func...
66 if [ -z "$BASE_APPLICATION_PATH" ]; then
67 echo fix this: we had no base app path, what to do now?
71 if [ ! -d "$BASE_APPLICATION_PATH" ]; then
72 echo "$(date_stringer): Creating the apps directory: $BASE_APPLICATION_PATH" >> "$SSM_LOG_FILE"
73 mkdir "$BASE_APPLICATION_PATH"
74 exit_on_error "Making apps directory when not already present"
78 #hmmm: extract to core somewhere...
79 # locates a parent directory of a certain name, if possible. returns success
80 # (as zero) if the directory was found, and failure if there was no parent
81 # named as requested. sets a global variable PARENT_DIR_FOUND to the full
82 # directory name that matched, including the name being sought (but omitting
83 # any deeper directories than that).
84 function find_named_parent_dir()
86 local dir_name_sought="$1"; shift
87 # clear any previous global result.
88 unset PARENT_DIR_FOUND
89 # check for degenerate case of parameter count.
90 if [ -z "$dir_name_sought" ]; then
92 find_named_parent_dir: requires a directory name parameter, which will be
93 sought out above the current directory. the return value indicates whether
94 the requested name was found or not.
98 # signal a failure by default with our return value.
100 # loop upwards in dir hierarchy to find the name.
102 local currdir="$(\pwd)"
103 if [ "$currdir" == "/" ]; then
104 # we climbed out of all subdirs. this is a failure case.
108 # get the base part of our name to check on success.
109 local base="$(basename "$currdir")"
110 if [ "$base" == "$dir_name_sought" ]; then
111 # yes, that is the right name. success case. save our result.
112 export PARENT_DIR_FOUND="$currdir"
116 # hop up a directory.
120 # rollback any directories we pushed.
121 while popd &>/dev/null; do true; done
126 # tries to find an appropriate config file for the application.
127 function locate_config_file()
129 local app_dirname="$1"; shift
131 local configfile="$THISDIR/config/${app_dirname}.app"
132 echo "$(date_stringer): config file guessed?: $configfile" >> "$SSM_LOG_FILE"
133 if [ ! -f "$configfile" ]; then
134 # this is not a good config file. we can't auto-guess the config.
135 echo -e "$(date_stringer):
136 There is no specific site configuration file in:
138 We will continue onward using the default and hope that this project follows
139 the standard pattern for cakephp projects." >> "$SSM_LOG_FILE"
140 # we'll pull in the default config file we set earlier; this will
141 # reinitialize some variables based on the app name.
143 # they gave us a valid config file. let's try using it.
144 export SITE_MANAGEMENT_CONFIG_FILE="$configfile"
147 # try to load the config.
148 source "$SITE_MANAGEMENT_CONFIG_FILE"
149 exit_on_error "loading site management configuration from: $SITE_MANAGEMENT_CONFIG_FILE"
154 # this function will seek out top-level directories in the target directory passed in.
155 # if there is only one directory, then it is returned (in the app_dirname variable).
156 # otherwise, the user is asked which directory to use.
157 # important: this sets a global variable app_dirname to the application's directory name.
158 function find_app_folder()
160 local appsdir="$1"; shift
162 # throw away any prior value so no confusion arises.
165 # count number of directories... if exactly one, then choose it.
166 numdirs=$(count_directories "$appsdir/")
168 if [ $numdirs -eq 0 ]; then
170 echo "There are no directories in the application directory:"
172 echo "Please create a directory for the site storage, based on the application"
173 echo "name that you want to work on. Or you can just pass the directory name"
174 echo "on the command line, e.g.:"
175 echo " $(basename $0) turtle"
178 elif [ $numdirs -eq 1 ]; then
179 # one directory in apps, so we'll pick that one.
180 app_dirname="$(basename $(find "$appsdir" -follow -mindepth 1 -maxdepth 1 -type d) )"
181 exit_on_error "Guessing application folder"
183 # there's more than one folder in apps...
185 # make sure we're allowed to auto-guess the folder name from our current dir.
186 if [ -z "$NO_AUTOMATIC_FOLDER_GUESS" ]; then
187 # if we can find the special checkout directory name above our current PWD, then that
188 # might tell us our project name.
189 if find_named_parent_dir "$CHECKOUT_DIR_NAME"; then
190 # we can grab a name above the checkout dir name location. let's try that.
191 app_dirname="$(basename "$(dirname $PARENT_DIR_FOUND)" )"
194 # flag maintenance, to avoid hosing other commands by leaving this set.
195 unset NO_AUTOMATIC_FOLDER_GUESS
197 # well, we couldn't guess a directory based on our current location,
198 # so ask the user to choose.
199 # Reference: https://askubuntu.com/questions/1705/how-can-i-create-a-select-menu-in-a-shell-script
201 PS3='Please pick a folder for site initialization: '
202 options=( $(find "$appsdir" -follow -mindepth 1 -maxdepth 1 -type d -exec basename {} ';') "Quit")
203 select app_dirname in "${options[@]}"; do
205 "Quit") echo ; echo "Quitting from the script."; return 1; ;;
206 *) echo ; echo "You picked folder '$app_dirname'" ; break; ;;
209 if [ -z "$app_dirname" ]; then
210 echo "The folder was not provided. This script needs a directory name"
211 echo "within which to initialize the site."
217 test_app_folder "$appsdir" "$app_dirname"
218 exit_on_error "Testing application folder: $app_dirname"
220 echo "Application folder is: $app_dirname"
224 # ensures that the app directory name is valid and then loads the config
225 # for the app (either via a specific file or using the defaults).
226 function test_app_folder()
228 local appsdir="$1"; shift
229 local dir="$1"; shift
231 local combo="$appsdir/$dir"
232 if [ "$dir" == " " ]; then
233 # trickery here means we don't expect an intermediate directory component.
237 if [ ! -d "$combo" ]; then
238 # the directory wasn't there yet, so we will auto-create it. this should
239 # hopefully be the right decision usually.
240 echo "$(date_stringer): Creating app directory: $combo" >> "$SSM_LOG_FILE"
242 exit_on_error "Making application directory when not already present"
244 # the directory does exist. let's test out a theory that it might not be
245 # an official site avenger style folder, in which case we need to patch a
246 # variable to set expectations.
247 if [ ! -d "$combo/$CHECKOUT_DIR_NAME" ]; then
248 echo "Dropping expectation for intermediate checkout directory name."
249 CHECKOUT_DIR_NAME=" "
253 echo yo modulopius on the variables:
254 var combo CHECKOUT_DIR_NAME
256 locate_config_file "$dir"
259 # eases some permissions to enable apache to write log files and do other shopkeeping.
260 function fix_site_perms()
262 local site_dir="$1"; shift
264 if [ -f "$site_dir/bin/cake" ]; then
265 sudo chmod -R a+rx "$site_dir/bin/cake"
266 exit_on_error "Enabling execute bit on cake binary"
269 if [ -d "$site_dir/logs" ]; then
270 sudo chmod -R g+w "$site_dir/logs"
271 exit_on_error "Enabling group write on site's Logs directory"
274 if [ -d "$site_dir/tmp" ]; then
275 sudo chmod -R g+w "$site_dir/tmp"
276 exit_on_error "Enabling group write on site's tmp directory"
280 # tosses out any cached object data that originated from the database.
281 function clear_orm_cache()
283 local site_dir="$1"; shift
285 if [ -f "$site_dir/bin/cake" ]; then
286 # flush any cached objects from db.
287 "$site_dir/bin/cake" orm_cache clear
288 exit_on_error "Clearing ORM cache"
292 # checks that the directory provided is a valid git repository.
293 function is_valid_git_repo()
295 local complete_path="$1"; shift
297 # see if the directory even exists.
298 if [ ! -d "$complete_path" ]; then
299 # nope, that's not a git repo since it's not even there.
304 # directory exists, so let's test it out.
305 pushd "$complete_path" &>/dev/null
306 exit_on_error "Switching to directory for check out: $complete_path"
308 # ask for repository name (without .git).
309 if git rev-parse --git-dir > /dev/null 2>&1; then
310 # this is a valid git repo.
315 # no, this is not a valid git repository.
320 # updates the revision control repository passed in. this expects that the
321 # repo will live in a folder called "checkout_dirname" under the app path,
322 # which is the standard for deployed site avenger sites. if that directory is
323 # missing, then we assume a checkout of the top-level repository instead.
324 # important: this also sets a global variable called site_store_path to the full
325 # path of the application.
326 function update_repo()
328 local full_app_dir="$1"; shift
329 local checkout_dirname="$1"; shift
330 local repo_root="$1"; shift
331 local repo_name="$1"; shift
333 echo "$(date_stringer): here are parms in update repo:" >> "$SSM_LOG_FILE"
334 echo "$(date_stringer): $(var full_app_dir checkout_dirname repo_root repo_name)" >> "$SSM_LOG_FILE"
336 # forget any prior value, since we are going to validate the path.
337 unset site_store_path
339 # pushd "$full_app_dir" &>/dev/null
340 # exit_on_error "Switching to our app dir '$full_app_dir'"
342 local complete_path="$full_app_dir"
343 if [ ! "$checkout_dirname" == " " ]; then
344 # make the full path using the non-empty checkout dir name.
345 complete_path+="/$checkout_dirname"
348 echo set complete_path: $complete_path
349 # store the local version into our special global.
350 site_store_path="$complete_path"
352 # check out the directory to see if it's a git repository.
353 if ! is_valid_git_repo "$complete_path"; then
354 if [ -d "$complete_path" ]; then
355 # we don't consider the state of having the dir exist but the repo be wrong as good.
356 echo "There is a problem; this folder is not a valid repository:"
357 echo " $complete_path"
358 echo "This script cannot continue unless the git repository is valid."
361 # okay, so the directory doesn't even exist. that means we will try to
362 # clone the project anew.
363 mkdir "$complete_path"
364 exit_on_error "Making project directory prior to new clone: $complete_path"
365 pushd "$complete_path/.." &>/dev/null
366 exit_on_error "Switching to parent directory prior to new clone: $complete_path/.."
367 echo "Cloning repository $repo_name now."
368 git clone "$repo_root/$repo_name.git" $checkout_dirname
369 exit_on_error "Git clone of repository: $repo_name"
373 # a repository was found, so update the version here and leave.
374 pushd "$complete_path" &>/dev/null
375 exit_on_error "Switching to directory for repo update: $complete_path"
376 echo "Repository $repo_name exists. Updating it."
377 git pull --tags --all
378 exit_on_error "Recursive checkout on: $complete_path"
382 # this function goes to the directory specified and makes it right with
383 # composer install. this is as opposed to composer update, which could
385 function composer_repuff()
387 local site_store_path="$1"; shift
389 pushd "$site_store_path" &>/dev/null
390 exit_on_error "Switching to our app dir '$site_store_path'"
392 echo "Updating site with composer..."
395 exit_on_error "Composer installation step on '$site_store_path'."
398 #hmmm: untested, had wrong path here and was never being run.
399 dir="vendor/siteavenger/avcore"
400 if [ -d "$dir" ]; then
401 echo "Running avcore database migrations..."
402 logfile="$TMP/problem-avcore_db_migration-$(date_stringer).log"
403 ./bin/cake migrations migrate -p Avcore &>"$logfile"
404 if [ $? -ne 0 ]; then
405 echo "** FAILED: Database migrations for avcore. Check log file in: $logfile"
406 # we keep going, because some sites aren't ready for this yet.
409 echo "Database for avcore migrated."
418 # this function creates the links needed to make the site function properly given our
419 # various dependencies and infrastructure.
420 function create_site_links()
422 local site_store_path="$1"; shift
423 local theme_name="$1"; shift
425 echo "Creating symbolic links for site assets..."
427 # jump into the site path so we can start making relative links.
428 pushd "$site_store_path" &>/dev/null
429 exit_on_error "Switching to our app dir '$site_store_path'"
431 pushd webroot &>/dev/null
432 exit_on_error "Switching to our webroot dir"
434 # remove all symlinks that might plague us.
435 find . -maxdepth 1 -type l -exec rm -f {} ';'
436 exit_on_error "Cleaning out links in webroot"
438 # link in the avcore plugin.
439 make_safe_link "../vendor/siteavenger/avcore/webroot" avcore
441 # make the link for our theme as a lower-case version of the theme.
442 themelower=${theme_name,,}
443 make_safe_link "../plugins/$theme_name/webroot" "$themelower"
445 # link in any favicon files.
446 if [ -d "../plugins/$theme_name/favicon" ]; then
448 for fave in "../plugins/$theme_name/favicon"/*; do
449 make_safe_link "$fave" .
453 # get back out of webroot.
456 # hop up a level above where we had been. this is a level above the
457 # site store path, which will not be appropriate for all projects, so
458 # we must tread carefully.
461 # we only do the following linking exercises when we are sure this is a
462 # site avenger style application. otherwise we would be creating links
463 # above our own heads, sort of.
464 if [ -d "$CHECKOUT_DIR_NAME" ]; then
465 # link 'public' to webroot.
466 if [ -L public ]; then
467 # public is a symlink.
469 exit_on_error "Removing public directory symlink"
470 elif [ -d public ]; then
471 # public is a folder with default files.
474 exit_on_error "Removing public directory and contents"
477 # create the main 'public' symlink
479 make_safe_link $CHECKOUT_DIR_NAME/webroot public
480 exit_on_error "Creating link to webroot called 'public'"
481 #hmmm: public/$themelower/im will be created automatically by system user with appropriate permissions
484 echo "Skipping 'public' link for project without '$CHECKOUT_DIR_NAME' folder."
490 echo Created symbolic links.
493 # fetches composer to make sure it's up to date.
494 # (if powerup runs, composer install doesn't update git origin.)
495 function update_composer_repository()
497 local site_store_path="$1"; shift
499 pushd "$site_store_path" &>/dev/null
501 if git config remote.composer.url &>/dev/null; then
503 echo "Updated the composer repository."
505 echo "No composer repository was found for updating."
509 # fixes the ownership for a site avenger or php application.
510 # this almost certainly will require sudo capability, if there are any ownership problems
511 # that need to be resolved.
512 function fix_appdir_ownership()
514 local appsdir="$1"; shift
515 local dir="$1"; shift
517 local combo="$appsdir/$dir"
519 # go with the default user running the script.
521 if [ ! -z "$user_name" -a "$user_name" != "root" ]; then
522 echo "$(date_stringer): Chowning the app folder to be owned by: $user_name" >> "$SSM_LOG_FILE"
523 #hmmm: have to hope for now for standard group named after user
524 sudo chown -R "$user_name:$user_name" "$combo"
525 exit_on_error "Chowning $combo to be owned by $user_name"
527 echo "$(date_stringer): user name failed checks for chowning, was found as '$user_name'" >> "$SSM_LOG_FILE"
530 #hmmm: is this variable set by this point? it's the right thing to pass down there anyway.
531 fix_site_perms "$site_store_path"
534 # Jumps to an application directory given the app name. If no app name is
535 # given, it will show a menu to pick the app.
538 # check for parameters.
539 app_dirname="$1"; shift
541 check_apps_root "$app_dirname"
543 # find proper webroot where the site will be initialized.
544 if [ -z "$app_dirname" ]; then
545 # no dir was passed, so guess it.
546 export NO_AUTOMATIC_FOLDER_GUESS=true
547 find_app_folder "$BASE_APPLICATION_PATH"
549 test_app_folder "$BASE_APPLICATION_PATH" "$app_dirname"
551 if [ $? -ne 0 ]; then
552 if [ "$app_dirname" != "Quit" ]; then
553 echo "Could not locate the application directory: ${app_dirname}"
558 # where we expect to find our checkout folder underneath.
559 full_app_dir="$BASE_APPLICATION_PATH/$app_dirname"
561 pushd $full_app_dir/$CHECKOUT_DIR_NAME
562 #redundant if pushd pwd