uses knowledge of current dir to pick app dir
[feisty_meow.git] / scripts / site_avenger / shared_site_mgr.sh
1 #!/bin/bash
2
3 # Author: Kevin Wentworth
4 # Author: Chris Koeritz
5
6 # This contains a bunch of reusable functions that help out in managing websites.
7
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.
11
12 source "$FEISTY_MEOW_SCRIPTS/core/launch_feisty_meow.sh"
13
14 export SSM_LOG_FILE="$TMP/$(logname)-siteavenger-script.log"
15
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"
23 fi
24
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"
28
29 # configure feisty revision control to ignore vendor folders.
30 export NO_CHECKIN_VENDOR=true
31
32 # tests that the main storage folder for apps exists.
33 function check_apps_root()
34 {
35   local appdir="$1"; shift
36   if [ ! -d "$appdir" ]; then
37     echo "$(date_stringer): Creating the apps directory: $appdir" >> "$SSM_LOG_FILE"
38     mkdir "$appdir"
39     exit_on_error "Making apps directory when not already present"
40   fi
41 }
42
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()
50 {
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
56     echo "
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.
60 "
61     return 1
62   fi
63   # loop upwards in dir hierarchy to find the name.
64   while true; do
65     local currdir="$(\pwd)"
66     if [ "$currdir" == "/" ]; then
67       # we climbed out of all subdirs.  this is a failure case.
68       return 1
69     fi
70     # get the base part of our name to check on success.
71     local base="$(basename "$currdir")"
72     if [ "$base" == "$dir_name_sought" ]; then
73       # yes, that is the right name.  success case.  save our result.
74       export PARENT_DIR_FOUND="$currdir"
75       return 0
76     fi
77   done
78 }
79
80 # tries to find an appropriate config file for the application.
81 function locate_config_file()
82 {
83   local app_dirname="$1"; shift
84
85   local configfile="$THISDIR/config/${app_dirname}.app"
86   echo "$(date_stringer): config file guessed?: $configfile" >> "$SSM_LOG_FILE"
87   if [ ! -f "$configfile" ]; then
88     # this is not a good config file.  we can't auto-guess the config.
89     echo -e "$(date_stringer): 
90 There is no specific site configuration file in:
91   $configfile
92 We will continue onward using the default and hope that this project follows
93 the standard pattern for cakephp projects." >> "$SSM_LOG_FILE"
94     # we'll pull in the default config file we set earlier; this will
95     # reinitialize some variables based on the app name.
96   else
97     # they gave us a valid config file.  let's try using it.
98     export SITE_MANAGEMENT_CONFIG_FILE="$configfile"
99   fi
100
101   # try to load the config.
102   source "$SITE_MANAGEMENT_CONFIG_FILE"
103   exit_on_error "loading site management configuration from: $SITE_MANAGEMENT_CONFIG_FILE"
104
105   return 0
106 }
107
108 # this function will seek out top-level directories in the target directory passed in.
109 # if there is only one directory, then it is returned (in the app_dirname variable).
110 # otherwise, the user is asked which directory to use.
111 # important: this sets a global variable app_dirname to the application's directory name.
112 function find_app_folder()
113 {
114   local appsdir="$1"; shift
115
116   # throw away any prior value so no confusion arises.
117   unset app_dirname
118   
119   # count number of directories...  if exactly one, then choose it.
120   numdirs=$(count_directories "$appsdir")
121
122   if [ $numdirs -eq 0 ]; then
123     sep
124     echo "There are no directories in the application directory:"
125     echo "  $appsdir"
126     echo "Please create a directory for the site storage, based on the application"
127     echo "name that you want to work on.  Or you can just pass the directory name"
128     echo "on the command line, e.g.:"
129     echo "  $(basename $0) turtle"
130     sep
131     exit 1
132   elif [ $numdirs -eq 1 ]; then
133     app_dirname="$(basename $(find "$appsdir" -mindepth 1 -maxdepth 1 -type d) )"
134     exit_on_error "Guessing application folder"
135   else
136     # there's more than one folder in apps...
137
138     # if we can find an avenger5 directory above our current PWD, then that might tell us our name.
139     if find_named_parent_dir "avenger5"; then
140       # we can grab a name above the avenger5 location.  let's try that.
141       app_dirname="$(basename "$(dirname $PARENT_DIR_FOUND)" )"
142     else
143       # well, we couldn't guess a directory based on our current location,
144       # so ask the user to choose.
145       # Reference: https://askubuntu.com/questions/1705/how-can-i-create-a-select-menu-in-a-shell-script
146       holdps3="$PS3"
147       PS3='Please pick a folder for site initialization: '
148       options=( $(find "$appsdir" -mindepth 1 -maxdepth 1 -type d -exec basename {} ';') "Quit")
149       select app_dirname in "${options[@]}"; do
150         case $app_dirname in
151           "Quit") echo ; echo "Quitting from the script."; return 1; ;;
152           *) echo ; echo "You picked folder '$app_dirname'" ; break; ;;
153         esac
154       done
155       if [ -z "$app_dirname" ]; then
156         echo "The folder was not provided.  This script needs a directory name"
157         echo "within which to initialize the site."
158         return 1
159       fi
160       PS3="$holdps3"
161     fi
162   fi
163   test_app_folder "$appsdir" "$app_dirname"
164   exit_on_error "Testing application folder: $app_dirname"
165
166   echo "Application folder is: $app_dirname"
167   return 0
168 }
169
170 # ensures that the app directory name is valid and then loads the config
171 # for the app (either via a specific file or using the defaults).
172 function test_app_folder()
173 {
174   local appsdir="$1"; shift
175   local dir="$1"; shift
176
177   local combo="$appsdir/$dir"
178
179   if [ ! -d "$combo" ]; then
180     echo "$(date_stringer): Creating app directory: $combo" >> "$SSM_LOG_FILE"
181     mkdir "$combo"
182     exit_on_error "Making application directory when not already present"
183   fi
184
185   locate_config_file "$dir"
186 }
187
188 # eases some permissions to enable apache to write log files and do other shopkeeping.
189 function fix_site_perms()
190 {
191   local app_dir="$1"; shift
192
193   local site_dir="$app_dir/$CHECKOUT_DIR_NAME"
194
195   if [ -f "$site_dir/bin/cake" ]; then
196     sudo chmod -R a+rx "$site_dir/bin/cake"
197     exit_on_error "Enabling execute bit on cake binary"
198   fi
199
200   if [ -d "$site_dir/logs" ]; then
201     sudo chmod -R g+w "$site_dir/logs"
202     exit_on_error "Enabling group write on site's Logs directory"
203   fi
204
205   if [ -d "$site_dir/tmp" ]; then
206     sudo chmod -R g+w "$site_dir/tmp"
207     exit_on_error "Enabling group write on site's tmp directory"
208   fi
209 }
210
211 # tosses out any cached object data that originated from the database.
212 function clear_orm_cache()
213 {
214   local site_dir="$1"; shift
215
216   if [ -f "$site_dir/bin/cake" ]; then
217     # flush any cached objects from db.
218     "$site_dir/bin/cake" orm_cache clear
219     exit_on_error "Clearing ORM cache"
220   fi
221 }
222
223 # updates the revision control repository passed in.  this expects that the
224 # repo will live in a folder called "checkout_dirname" under the app path,
225 # which is the standard for our deployed sites.
226 # important: this also sets a global variable called site_store_path to the full
227 # path of the application.
228 function update_repo()
229 {
230   local full_app_dir="$1"; shift
231   local checkout_dirname="$1"; shift
232   local repo_root="$1"; shift
233   local repo_name="$1"; shift
234
235 echo "$(date_stringer): here are parms in update repo:" >> "$SSM_LOG_FILE"
236 echo "$(date_stringer): $(var full_app_dir checkout_dirname repo_root repo_name)" >> "$SSM_LOG_FILE"
237
238   # forget any prior value, since we are going to validate the path.
239   unset site_store_path
240
241   pushd "$full_app_dir" &>/dev/null
242   exit_on_error "Switching to our app dir '$full_app_dir'"
243
244   local complete_path="$full_app_dir/$checkout_dirname"
245
246   # see if the checkout directory exits.  the repo_found variable is set to
247   # non-empty if we find it and it's a valid git repo.
248   repo_found=
249   if [ -d "$checkout_dirname" ]; then
250     # checkout directory exists, so let's check it.
251     pushd "$checkout_dirname" &>/dev/null
252     exit_on_error "Switching to our checkout directory: $checkout_dirname"
253
254     # ask for repository name (without .git).
255     if git rev-parse --git-dir > /dev/null 2>&1; then
256       # this is a valid git repo.
257       repo_found=yes
258     fi
259  
260     # we don't consider the state of having the dir exist but the repo be wrong as good.
261     if [ -z "$repo_found" ]; then
262       echo "There is a problem; this folder is not a valid repository:"
263       echo "  $full_app_dir"
264       echo "This script cannot continue unless the git repository is valid."
265       exit 1
266     fi
267     popd &>/dev/null
268   fi
269
270   if [ ! -z "$repo_found" ]; then
271     # a repository was found, so update the version here and leave.
272     echo "Repository $repo_name exists.  Updating it."
273     rgetem
274     exit_on_error "Recursive checkout on: $complete_path"
275   else
276     # clone the repo since it wasn't found.
277     echo "Cloning repository $repo_name now."
278     git clone "$repo_root/$repo_name.git" $checkout_dirname
279     exit_on_error "Git clone of repository: $repo_name"
280   fi
281
282 #not doing this here since powerup uses this and has no sudo.
283   #fix_site_perms "$complete_path"
284
285 #unused?
286   # construct the full path to where the app will actually live.
287   site_store_path="$complete_path"
288
289   popd &>/dev/null
290 }
291
292 # this function goes to the directory specified and makes it right with
293 # composer install.  this is as opposed to composer update, which could
294 # change the state. 
295 function composer_repuff()
296 {
297   local site_store_path="$1"; shift
298
299   pushd "$site_store_path" &>/dev/null
300   exit_on_error "Switching to our app dir '$site_store_path'"
301
302   echo "Updating site with composer..."
303
304   composer -n install
305   exit_on_error "Composer installation step on '$site_store_path'."
306   echo "Site updated."
307
308 #hmmm: argh global
309   dir="$site_store_path/$CHECKOUT_DIR_NAME/vendor/siteavenger/avcore"
310   if [ -d "$dir" ]; then
311     echo "Running avcore database migrations..."
312     logfile="$TMP/problem-avcore_db_migration-$(date_stringer).log"
313     ./bin/cake migrations migrate -p Avcore &>"$logfile"
314     if [ $? -ne 0 ]; then
315       echo "** FAILED: Database migrations for avcore.  Check log file in: $logfile"
316       # we keep going, because some sites aren't ready for this yet.
317     else
318       \rm "$logfile"
319       echo "Database for avcore migrated."
320     fi
321   fi
322
323   clear_orm_cache
324
325   popd &>/dev/null
326 }
327
328 # this function creates the links needed to make the site function properly given our
329 # various dependencies and infrastructure.
330 function create_site_links()
331 {
332   local site_store_path="$1"; shift
333   local theme_name="$1"; shift
334
335   echo "Creating symbolic links for site assets..."
336
337   # jump into the site path so we can start making relative links.
338   pushd "$site_store_path" &>/dev/null
339   exit_on_error "Switching to our app dir '$site_store_path'"
340
341   pushd webroot &>/dev/null
342
343   # remove all symlinks that might plague us.
344   find . -maxdepth 1 -type l -exec rm -f {} ';'
345   exit_on_error "Cleaning out links in webroot"
346
347   # link in the avcore plugin.
348   make_safe_link "../vendor/siteavenger/avcore/webroot" avcore
349
350   # make the link for our theme as a lower-case version of the theme.
351   themelower=${theme_name,,}
352   make_safe_link "../plugins/$theme_name/webroot" "$themelower"
353
354   # link in any favicon files.
355   if [ -d "../plugins/$theme_name/favicon" ]; then
356     local fave
357     for fave in "../plugins/$theme_name/favicon"/*; do
358       make_safe_link "$fave" .
359     done
360   fi
361
362   # get back out of webroot.
363   popd &>/dev/null
364
365   # hop up a level above where we had been.
366   pushd .. &>/dev/null
367
368   # link 'public' to webroot.
369   if [ -L public ]; then
370     # public is a symlink.
371     \rm public
372     exit_on_error "Removing public directory symlink"
373   elif [ -d public ]; then
374     # public is a folder with default files.
375 #hmmm: is that safe?
376     \rm -rf public
377     exit_on_error "Removing public directory and contents"
378   fi
379
380   # create the main 'public' symlink
381 #hmmm: argh global
382   make_safe_link $CHECKOUT_DIR_NAME/webroot public
383   exit_on_error "Creating link to webroot called 'public'"
384
385 #hmmm: public/$themelower/im will be created automatically by system user with appropriate permissions
386
387   echo Created symbolic links.
388
389   popd &>/dev/null
390   popd &>/dev/null
391 }
392
393 # fetches composer to make sure it's up to date.
394 # (if powerup runs, composer install doesn't update git origin.)
395 function update_composer_repository()
396 {
397   local site_store_path="$1"; shift
398
399   pushd "$site_store_path" &>/dev/null
400
401   if git config remote.composer.url &>/dev/null; then
402     git pull composer
403     echo "Updated the composer repository."
404   else
405     echo "No composer repository was found for updating."
406   fi
407 }
408
409 # fixes the ownership for a site avenger or php application.
410 # this almost certainly will require sudo capability, if there are any ownership problems
411 # that need to be resolved.
412 function fix_appdir_ownership()
413 {
414   local appsdir="$1"; shift
415   local dir="$1"; shift
416
417   local combo="$appsdir/$dir"
418
419   # go with the default user running the script.
420   user_name="$USER"
421   if [ ! -z "$user_name" -a "$user_name" != "root" ]; then
422     echo "$(date_stringer): Chowning the app folder to be owned by: $user_name" >> "$SSM_LOG_FILE"
423 #hmmm: have to hope for now for standard group named after user 
424     sudo chown -R "$user_name:$user_name" "$combo"
425     exit_on_error "Chowning $combo to be owned by $user_name"
426   else
427     echo "$(date_stringer): user name failed checks for chowning, was found as '$user_name'" >> "$SSM_LOG_FILE"
428   fi
429
430   # 
431 #probably not enough for path!
432   fix_site_perms "$combo"
433 }
434
435 # Jumps to an application directory given the app name.  If no app name is
436 # given, it will show a menu to pick the app.
437 function switch_to()
438 {
439   # check for parameters.
440   app_dirname="$1"; shift
441
442   check_apps_root "$BASE_APPLICATION_PATH"
443
444   # find proper webroot where the site will be initialized.
445   if [ -z "$app_dirname" ]; then
446     # no dir was passed, so guess it.
447     find_app_folder "$BASE_APPLICATION_PATH"
448   else
449     test_app_folder "$BASE_APPLICATION_PATH" "$app_dirname"
450   fi
451   if [ $? -ne 0 ]; then
452     echo "Could not locate the application directory: ${app_dirname}"
453     return 1
454   fi
455
456   # where we expect to find our checkout folder underneath.
457   full_app_dir="$BASE_APPLICATION_PATH/$app_dirname"
458
459   pushd $full_app_dir/$CHECKOUT_DIR_NAME
460 #redundant if pushd  pwd
461 }
462