3 # Author: Kevin Wentworth
4 # Author: Chris Koeritz
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/$(logname)-siteavenger-script.log"
16 # get our configuration loaded, if we know the config file.
17 # if there is none, we will use our default version.
18 export SITE_MANAGEMENT_CONFIG_FILE
19 if [ -z "$SITE_MANAGEMENT_CONFIG_FILE" ]; then
20 SITE_MANAGEMENT_CONFIG_FILE="$THISDIR/config/default.app"
21 echo "$(date_stringer): Site management config file was not set. Using default:" >> "$SSM_LOG_FILE"
22 echo "$(date_stringer): $SITE_MANAGEMENT_CONFIG_FILE" >> "$SSM_LOG_FILE"
25 # load in at least the default version to get us moving.
26 source "$SITE_MANAGEMENT_CONFIG_FILE"
27 exit_on_error "loading site management configuration from: $SITE_MANAGEMENT_CONFIG_FILE"
29 # configure feisty revision control to ignore vendor folders.
30 export NO_CHECKIN_VENDOR=true
32 # tests that the main storage folder for apps exists.
33 function check_apps_root()
35 local appdir="$1"; shift
36 if [ ! -d "$appdir" ]; then
37 echo "$(date_stringer): Creating the apps directory: $appdir" >> "$SSM_LOG_FILE"
39 exit_on_error "Making apps directory when not already present"
43 #hmmm: extract to core somewhere...
44 # locates a parent directory of a certain name, if possible. returns success
45 # (as zero) if the directory was found, and failure if there was no parent
46 # named as requested. sets a global variable PARENT_DIR_FOUND to the full
47 # directory name that matched, including the name being sought (but omitting
48 # any deeper directories than that).
49 function find_named_parent_dir()
51 local dir_name_sought="$1"; shift
52 # clear any previous global result.
53 unset PARENT_DIR_FOUND
54 # check for degenerate case of parameter count.
55 if [ -z "$dir_name_sought" ]; then
57 find_named_parent_dir: requires a directory name parameter, which will be
58 sought out above the current directory. the return value indicates whether
59 the requested name was found or not.
63 # signal a failure by default with our return value.
65 # loop upwards in dir hierarchy to find the name.
67 local currdir="$(\pwd)"
68 if [ "$currdir" == "/" ]; then
69 # we climbed out of all subdirs. this is a failure case.
73 # get the base part of our name to check on success.
74 local base="$(basename "$currdir")"
75 if [ "$base" == "$dir_name_sought" ]; then
76 # yes, that is the right name. success case. save our result.
77 export PARENT_DIR_FOUND="$currdir"
85 # rollback any directories we pushed.
86 while popd &>/dev/null; do true; done
91 # tries to find an appropriate config file for the application.
92 function locate_config_file()
94 local app_dirname="$1"; shift
96 local configfile="$THISDIR/config/${app_dirname}.app"
97 echo "$(date_stringer): config file guessed?: $configfile" >> "$SSM_LOG_FILE"
98 if [ ! -f "$configfile" ]; then
99 # this is not a good config file. we can't auto-guess the config.
100 echo -e "$(date_stringer):
101 There is no specific site configuration file in:
103 We will continue onward using the default and hope that this project follows
104 the standard pattern for cakephp projects." >> "$SSM_LOG_FILE"
105 # we'll pull in the default config file we set earlier; this will
106 # reinitialize some variables based on the app name.
108 # they gave us a valid config file. let's try using it.
109 export SITE_MANAGEMENT_CONFIG_FILE="$configfile"
112 # try to load the config.
113 source "$SITE_MANAGEMENT_CONFIG_FILE"
114 exit_on_error "loading site management configuration from: $SITE_MANAGEMENT_CONFIG_FILE"
119 # this function will seek out top-level directories in the target directory passed in.
120 # if there is only one directory, then it is returned (in the app_dirname variable).
121 # otherwise, the user is asked which directory to use.
122 # important: this sets a global variable app_dirname to the application's directory name.
123 function find_app_folder()
125 local appsdir="$1"; shift
127 # throw away any prior value so no confusion arises.
130 # count number of directories... if exactly one, then choose it.
131 numdirs=$(count_directories "$appsdir/")
133 if [ $numdirs -eq 0 ]; then
135 echo "There are no directories in the application directory:"
137 echo "Please create a directory for the site storage, based on the application"
138 echo "name that you want to work on. Or you can just pass the directory name"
139 echo "on the command line, e.g.:"
140 echo " $(basename $0) turtle"
143 elif [ $numdirs -eq 1 ]; then
144 # one directory in apps, so we'll pick that one.
145 app_dirname="$(basename $(find "$appsdir" -follow -mindepth 1 -maxdepth 1 -type d) )"
146 exit_on_error "Guessing application folder"
148 # there's more than one folder in apps...
150 # make sure we're allowed to auto-guess the folder name from our current dir.
151 if [ -z "$NO_AUTOMATIC_FOLDER_GUESS" ]; then
152 # if we can find an avenger5 directory above our current PWD, then that
153 # might tell us our name.
154 if find_named_parent_dir "avenger5"; then
155 # we can grab a name above the avenger5 location. let's try that.
156 app_dirname="$(basename "$(dirname $PARENT_DIR_FOUND)" )"
159 # flag maintenance, to avoid hosing other commands by leaving this set.
160 unset NO_AUTOMATIC_FOLDER_GUESS
162 # well, we couldn't guess a directory based on our current location,
163 # so ask the user to choose.
164 # Reference: https://askubuntu.com/questions/1705/how-can-i-create-a-select-menu-in-a-shell-script
166 PS3='Please pick a folder for site initialization: '
167 options=( $(find "$appsdir" -follow -mindepth 1 -maxdepth 1 -type d -exec basename {} ';') "Quit")
168 select app_dirname in "${options[@]}"; do
170 "Quit") echo ; echo "Quitting from the script."; return 1; ;;
171 *) echo ; echo "You picked folder '$app_dirname'" ; break; ;;
174 if [ -z "$app_dirname" ]; then
175 echo "The folder was not provided. This script needs a directory name"
176 echo "within which to initialize the site."
182 test_app_folder "$appsdir" "$app_dirname"
183 exit_on_error "Testing application folder: $app_dirname"
185 echo "Application folder is: $app_dirname"
189 # ensures that the app directory name is valid and then loads the config
190 # for the app (either via a specific file or using the defaults).
191 function test_app_folder()
193 local appsdir="$1"; shift
194 local dir="$1"; shift
196 local combo="$appsdir/$dir"
198 if [ ! -d "$combo" ]; then
199 echo "$(date_stringer): Creating app directory: $combo" >> "$SSM_LOG_FILE"
201 exit_on_error "Making application directory when not already present"
204 locate_config_file "$dir"
207 # eases some permissions to enable apache to write log files and do other shopkeeping.
208 function fix_site_perms()
210 local app_dir="$1"; shift
212 local site_dir="$app_dir/$CHECKOUT_DIR_NAME"
214 if [ -f "$site_dir/bin/cake" ]; then
215 sudo chmod -R a+rx "$site_dir/bin/cake"
216 exit_on_error "Enabling execute bit on cake binary"
219 if [ -d "$site_dir/logs" ]; then
220 sudo chmod -R g+w "$site_dir/logs"
221 exit_on_error "Enabling group write on site's Logs directory"
224 if [ -d "$site_dir/tmp" ]; then
225 sudo chmod -R g+w "$site_dir/tmp"
226 exit_on_error "Enabling group write on site's tmp directory"
230 # tosses out any cached object data that originated from the database.
231 function clear_orm_cache()
233 local site_dir="$1"; shift
235 if [ -f "$site_dir/bin/cake" ]; then
236 # flush any cached objects from db.
237 "$site_dir/bin/cake" orm_cache clear
238 exit_on_error "Clearing ORM cache"
242 # updates the revision control repository passed in. this expects that the
243 # repo will live in a folder called "checkout_dirname" under the app path,
244 # which is the standard for our deployed sites.
245 # important: this also sets a global variable called site_store_path to the full
246 # path of the application.
247 function update_repo()
249 local full_app_dir="$1"; shift
250 local checkout_dirname="$1"; shift
251 local repo_root="$1"; shift
252 local repo_name="$1"; shift
254 echo "$(date_stringer): here are parms in update repo:" >> "$SSM_LOG_FILE"
255 echo "$(date_stringer): $(var full_app_dir checkout_dirname repo_root repo_name)" >> "$SSM_LOG_FILE"
257 # forget any prior value, since we are going to validate the path.
258 unset site_store_path
260 pushd "$full_app_dir" &>/dev/null
261 exit_on_error "Switching to our app dir '$full_app_dir'"
263 local complete_path="$full_app_dir/$checkout_dirname"
265 # see if the checkout directory exits. the repo_found variable is set to
266 # non-empty if we find it and it's a valid git repo.
268 if [ -d "$checkout_dirname" ]; then
269 # checkout directory exists, so let's check it.
270 pushd "$checkout_dirname" &>/dev/null
271 exit_on_error "Switching to our checkout directory: $checkout_dirname"
273 # ask for repository name (without .git).
274 if git rev-parse --git-dir > /dev/null 2>&1; then
275 # this is a valid git repo.
279 # we don't consider the state of having the dir exist but the repo be wrong as good.
280 if [ -z "$repo_found" ]; then
281 echo "There is a problem; this folder is not a valid repository:"
282 echo " $full_app_dir"
283 echo "This script cannot continue unless the git repository is valid."
289 if [ ! -z "$repo_found" ]; then
290 # a repository was found, so update the version here and leave.
291 echo "Repository $repo_name exists. Updating it."
293 exit_on_error "Recursive checkout on: $complete_path"
295 # clone the repo since it wasn't found.
296 echo "Cloning repository $repo_name now."
297 git clone "$repo_root/$repo_name.git" $checkout_dirname
298 exit_on_error "Git clone of repository: $repo_name"
301 #not doing this here since powerup uses this and has no sudo.
302 #fix_site_perms "$complete_path"
305 # construct the full path to where the app will actually live.
306 site_store_path="$complete_path"
311 # this function goes to the directory specified and makes it right with
312 # composer install. this is as opposed to composer update, which could
314 function composer_repuff()
316 local site_store_path="$1"; shift
318 pushd "$site_store_path" &>/dev/null
319 exit_on_error "Switching to our app dir '$site_store_path'"
321 echo "Updating site with composer..."
324 exit_on_error "Composer installation step on '$site_store_path'."
328 dir="$site_store_path/$CHECKOUT_DIR_NAME/vendor/siteavenger/avcore"
329 if [ -d "$dir" ]; then
330 echo "Running avcore database migrations..."
331 logfile="$TMP/problem-avcore_db_migration-$(date_stringer).log"
332 ./bin/cake migrations migrate -p Avcore &>"$logfile"
333 if [ $? -ne 0 ]; then
334 echo "** FAILED: Database migrations for avcore. Check log file in: $logfile"
335 # we keep going, because some sites aren't ready for this yet.
338 echo "Database for avcore migrated."
347 # this function creates the links needed to make the site function properly given our
348 # various dependencies and infrastructure.
349 function create_site_links()
351 local site_store_path="$1"; shift
352 local theme_name="$1"; shift
354 echo "Creating symbolic links for site assets..."
356 # jump into the site path so we can start making relative links.
357 pushd "$site_store_path" &>/dev/null
358 exit_on_error "Switching to our app dir '$site_store_path'"
360 pushd webroot &>/dev/null
362 # remove all symlinks that might plague us.
363 find . -maxdepth 1 -type l -exec rm -f {} ';'
364 exit_on_error "Cleaning out links in webroot"
366 # link in the avcore plugin.
367 make_safe_link "../vendor/siteavenger/avcore/webroot" avcore
369 # make the link for our theme as a lower-case version of the theme.
370 themelower=${theme_name,,}
371 make_safe_link "../plugins/$theme_name/webroot" "$themelower"
373 # link in any favicon files.
374 if [ -d "../plugins/$theme_name/favicon" ]; then
376 for fave in "../plugins/$theme_name/favicon"/*; do
377 make_safe_link "$fave" .
381 # get back out of webroot.
384 # hop up a level above where we had been.
387 # link 'public' to webroot.
388 if [ -L public ]; then
389 # public is a symlink.
391 exit_on_error "Removing public directory symlink"
392 elif [ -d public ]; then
393 # public is a folder with default files.
396 exit_on_error "Removing public directory and contents"
399 # create the main 'public' symlink
401 make_safe_link $CHECKOUT_DIR_NAME/webroot public
402 exit_on_error "Creating link to webroot called 'public'"
404 #hmmm: public/$themelower/im will be created automatically by system user with appropriate permissions
406 echo Created symbolic links.
412 # fetches composer to make sure it's up to date.
413 # (if powerup runs, composer install doesn't update git origin.)
414 function update_composer_repository()
416 local site_store_path="$1"; shift
418 pushd "$site_store_path" &>/dev/null
420 if git config remote.composer.url &>/dev/null; then
422 echo "Updated the composer repository."
424 echo "No composer repository was found for updating."
428 # fixes the ownership for a site avenger or php application.
429 # this almost certainly will require sudo capability, if there are any ownership problems
430 # that need to be resolved.
431 function fix_appdir_ownership()
433 local appsdir="$1"; shift
434 local dir="$1"; shift
436 local combo="$appsdir/$dir"
438 # go with the default user running the script.
440 if [ ! -z "$user_name" -a "$user_name" != "root" ]; then
441 echo "$(date_stringer): Chowning the app folder to be owned by: $user_name" >> "$SSM_LOG_FILE"
442 #hmmm: have to hope for now for standard group named after user
443 sudo chown -R "$user_name:$user_name" "$combo"
444 exit_on_error "Chowning $combo to be owned by $user_name"
446 echo "$(date_stringer): user name failed checks for chowning, was found as '$user_name'" >> "$SSM_LOG_FILE"
450 #probably not enough for path!
451 fix_site_perms "$combo"
454 # Jumps to an application directory given the app name. If no app name is
455 # given, it will show a menu to pick the app.
458 # check for parameters.
459 app_dirname="$1"; shift
461 check_apps_root "$BASE_APPLICATION_PATH"
463 # find proper webroot where the site will be initialized.
464 if [ -z "$app_dirname" ]; then
465 # no dir was passed, so guess it.
466 export NO_AUTOMATIC_FOLDER_GUESS=true
467 find_app_folder "$BASE_APPLICATION_PATH"
469 test_app_folder "$BASE_APPLICATION_PATH" "$app_dirname"
471 if [ $? -ne 0 ]; then
472 if [ "$app_dirname" != "Quit" ]; then
473 echo "Could not locate the application directory: ${app_dirname}"
478 # where we expect to find our checkout folder underneath.
479 full_app_dir="$BASE_APPLICATION_PATH/$app_dirname"
481 pushd $full_app_dir/$CHECKOUT_DIR_NAME
482 #redundant if pushd pwd