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