Sorting found info files for discovery doesn't bring any advantages, removed.
[project/simplytest.git] / scripts / sources / common
1 ##
2 # This is the file all (build and destroy) scripts have in 'common'.
3 #
4 # It loads the config, provides many helping functions and chooses which
5 # project-build script to include depending on the projects type (see bottom).
6 ##
7
8 # Load config.
9 source "$DIRECTORY/config"
10
11 # Include prepare script.
12 source "$DIRECTORY/sources/prepare"
13
14 ##
15 # Reads a value from a .info file
16 #
17 # @param $1
18 #  Info filepath to read from.
19 # @param $2
20 #  Name of value to read.
21 s_info_file_get() {
22   # Make sure info files are unix encoded before reading them.
23   dos2unix -q "$1"
24   # Cut out the value.
25   grep -m 1 "$2[ ]*=" "$1" | sed 's/^ *//g' | sed -n "s/$2[^=]*=[ ]*//p" | sed "s/[\"|\']//g"
26 }
27
28 ##
29 # Reads all values from a .info file.
30 #
31 # @param $1
32 #  Info filepath to read from.
33 # @param $2
34 #  Name of value to read.
35 s_info_file_get_all() {
36   # Make sure info files are unix encoded before reading them.
37   dos2unix -q "$1"
38   # Cut out the values.
39   cat "$1" | grep "$2\[\][ ]*" | sed 's/^ *//g' | sed -n "s/$2[^=]*=[ ]*//p" | sed "s/[\"|\']//g"
40 }
41
42 ##
43 # Resets the owner and permission of the environment files.
44 #
45 # @param $1
46 #  The submission id.
47 s_reset_environment_files() {
48   # Make sure PHP upload tempdir exists and is writeable.
49   if [[ ! -d "/home/$S_ID/www/tmp" ]]; then
50     mkdir "/home/$S_ID/www/tmp"
51   fi
52   chmod 777 "/home/$S_ID/www/tmp"
53   # Make sure all files and directory have the correct group and user.
54   chown --recursive $1 "/home/$1/www"/*
55   chgrp --recursive nogroup "/home/$1/www"/*
56 }
57
58 ##
59 # Clones/Fetches a project in project cache.
60 #
61 # @param $1
62 #  Project shortname to fetch.
63 # @param $2
64 #  Git url to clone from.
65 # @param $3
66 #  Path to download it to.
67 s_fetch_project() {
68   # Clone if project is not in cache yet.
69   if [[ ! -r "$S_DRUSHCACHE/$1.git" ]]; then
70     timeout 500 git clone --mirror "$2" "$S_DRUSHCACHE/$1.git"
71   fi
72   timeout 350 git clone --reference "$S_DRUSHCACHE/$1.git" --recursive "$2" "$3" || s_ste 9
73   # Update repo by fetching newest changes.
74   cd "$3" || s_ste 9
75   git fetch || s_ste 9
76   git pull
77 }
78
79 ##
80 # Execute a SQL query.
81 #
82 # @param $1
83 #  SQL query to execute.
84 s_sqlquery() {
85   mysql -u $S_SQLUSR "-p$S_SQLPWD" -e "$1" || s_ste 10
86 }
87
88 ##
89 # Setup temporary database.
90 #
91 # @param $1
92 #  Name of database and user to setup for.
93 s_db_setup() {
94   s_sqlquery "#CREATE USER '$1'@'localhost' IDENTIFIED BY  '$1';"
95   s_sqlquery "GRANT USAGE ON * . * TO  '$1'@'localhost' IDENTIFIED BY  '$1' WITH MAX_QUERIES_PER_HOUR 0 MAX_CONNECTIONS_PER_HOUR 0 MAX_UPDATES_PER_HOUR 0 MAX_USER_CONNECTIONS 0 ;"
96   s_sqlquery "CREATE DATABASE IF NOT EXISTS $1 ;"
97   s_sqlquery "GRANT ALL PRIVILEGES ON $1 . * TO  '$1'@'localhost';"
98 }
99
100 ##
101 # Set a timeout to execute a command.
102 #
103 # @param $1
104 #  Command to execute.
105 # @param $2
106 #  Minutes until execution.
107 s_settimeout() {
108   # Create an ´at´ job but give an extra minute to compensate time
109   # differences with snippet script.
110   echo "$1" | at now + $(($2 + 1)) minutes
111 }
112
113 ##
114 # Checkout specified version and remove git.
115 #
116 # @param $1
117 #  Path to check the project out at.
118 # @param $2
119 #  Version to check out.
120 s_set_version() {
121   cd $1
122   timeout 350 git checkout $2 || s_ste 9
123   rm -r "$1/.git"
124 }
125
126 ##
127 # Tries to find out the project real filename.
128 #
129 # This is necessary to compensate things like
130 # shortname: cck -> filename: content.
131 #
132 # @param $1
133 #  Path to the projects directory.
134 s_get_project_filename() {
135   ls -l "$1" | grep -m 1 -o "[_a-zA-Z0-9]*.info$" | sed -n 's/.info//p'
136 }
137
138 ##
139 # Tries to find out the core version of a project.
140 #
141 # @param $1
142 #  Path to the project's info file to determine core version of.
143 #
144 # @return
145 #  The core version determined.
146 s_get_project_coreversion() {
147   s_info_file_get "$1" "core" | sed "s/\..*//g"
148 }
149
150 ##
151 # Tries to find out the core version in docroot.
152 #
153 # @todo find a more reliable way to determine a core version.
154 #
155 # @return
156 #  The core version determined, or "X" for unknown.
157 s_get_coreversion() {
158   # Check for Drupal 8.
159   if [ -e "/home/$S_ID/www/core/install.php" ] # Core files were moved from root to core/.
160   then
161     echo "8"
162   
163   # Check for Drupal 7.
164   elif [ -e "/home/$S_ID/www/authorize.php" ] # authorize.php for module ftp upload/installation was added.
165   then
166     echo "7"
167   
168   # Check for Drupal 6.
169   elif [ -e "/home/$S_ID/www/misc/tabledrag.js" ] # Tabledrag was added.
170   then
171     echo "6"
172   
173   # Unknown version.
174   else
175     echo "X"
176   fi
177 }
178
179 ##
180 # Installs a drupal 8 core in docroot.
181 #
182 # @param $1
183 #  Core project version.
184 # @param $2
185 #  Install profile to use.
186 s_install_drupal_8() {
187   # Setup directories and permissions.
188   mkdir "/home/$S_ID/www/sites/default/files"
189   mkdir "/home/$S_ID/www/sites/default/files/tmp"
190   cp "/home/$S_ID/www/sites/default/default.settings.php" "/home/$S_ID/www/sites/default/settings.php" || s_ste 10
191   chmod --recursive 555 "/home/$S_ID/www"
192   chmod --recursive 777 "/home/$S_ID/www/sites/default/files"
193   chmod --recursive 777 "/home/$S_ID/www/sites/default/settings.php"
194   chown --recursive $S_ID "/home/$S_ID/www"/* || s_ste 10
195   chgrp --recursive nogroup "/home/$S_ID/www"/* || s_ste 10
196   chown --recursive $S_ID "/home/$S_ID/www/.htaccess" || s_ste 10
197   chgrp --recursive nogroup "/home/$S_ID/www/.htaccess" || s_ste 10
198   # Drush's not ready for d8.
199 }
200
201 ##
202 # Installs a drupal 7 core in docroot.
203 #
204 # @param $1
205 #  Core project version.
206 # @param $2
207 #  Install profile to use.
208 s_install_drupal_7() {
209   # Setup directories and permissions.
210   mkdir "/home/$S_ID/www/sites/default/files"
211   mkdir "/home/$S_ID/www/sites/default/files/tmp"
212   cp "/home/$S_ID/www/sites/default/default.settings.php" "/home/$S_ID/www/sites/default/settings.php" || s_ste 10
213   chown --recursive $S_ID "/home/$S_ID/www"/*
214   chgrp --recursive nogroup "/home/$S_ID/www"/*
215
216   cd "/home/$S_ID/www" || s_ste 10
217
218   # Start drupal installation.
219   timeout 350 sudo -u $S_ID drush site-install $2 --locale=en -y --db-url=mysql://$S_ID:$S_ID@localhost/$S_ID --account-name=$S_DUSR --account-pass=$S_DPWD --account-mail="$S_ID-$S_DMAIL_SUFFIX" --site-mail="$S_ID-$S_DMAIL_SUFFIX" --site-name="Drupal $1" || s_ste 10
220
221   # Set temporary path to files dir.
222   timeout 350 sudo -u $S_ID drush vset --always-set file_temporary_path "sites/default/files/tmp"
223
224   chmod --recursive 555 "/home/$S_ID/www"
225   chmod --recursive 777 "/home/$S_ID/www/sites/default/files"
226   chown --recursive $S_ID "/home/$S_ID/www"/* || s_ste 10
227   chgrp --recursive nogroup "/home/$S_ID/www"/* || s_ste 10
228   chown --recursive $S_ID "/home/$S_ID/www/.htaccess" || s_ste 10
229   chgrp --recursive nogroup "/home/$S_ID/www/.htaccess" || s_ste 10
230 }
231
232 ##
233 # Installs a drupal 6 core in docroot.
234 #
235 # @param $1
236 #  Core project version.
237 # @param $2
238 #  Install profile to use.
239 s_install_drupal_6() {
240   mkdir "/home/$S_ID/www/sites/default/files"
241   mkdir "/home/$S_ID/www/sites/default/files/tmp"
242   cp "/home/$S_ID/www/sites/default/default.settings.php" "/home/$S_ID/www/sites/default/settings.php" || s_ste 10
243   cd "/home/$S_ID/www" || s_ste 10
244   chown --recursive $S_ID "/home/$S_ID/www"/*
245   chgrp --recursive nogroup "/home/$S_ID/www"/*
246
247   # Set mysql default settings for installation.
248   echo "\$db_url = 'mysql://$S_ID:$S_ID@localhost/$S_ID';" >> "/home/$S_ID/www/sites/default/settings.php"
249   # Start drupal installation.
250   timeout 350 sudo -u $S_ID  drush site-install $2 --locale=en -y --db-url=mysql://$S_ID:$S_ID@localhost/$S_ID --account-name=$S_DUSR --account-pass=$S_DPWD --account-mail="$S_ID-$S_DMAIL_SUFFIX" --site-mail="$S_ID-$S_DMAIL_SUFFIX" --site-name="Drupal $1" || s_ste 10
251
252   # Set temporary path to files dir.
253   timeout 350 sudo -u $S_ID  drush vset --always-set file_directory_temp "sites/default/files/tmp"
254
255   # Fixing PHP backwardcompatibility issue.
256   sed -i 's/return call_user_func_array($function, $args);/return call_user_func_array($function, \&$args);/g' "/home/$S_ID/www/includes/module.inc"
257
258   # Setup directories and permissions.
259   chmod --recursive 555 "/home/$S_ID/www"
260   chmod --recursive 777 "/home/$S_ID/www/sites/default/files"
261   chown --recursive $S_ID "/home/$S_ID/www"/* || s_ste 10
262   chgrp --recursive nogroup "/home/$S_ID/www"/* || s_ste 10
263   chown --recursive $S_ID "/home/$S_ID/www/.htaccess" || s_ste 10
264   chgrp --recursive nogroup "/home/$S_ID/www/.htaccess" || s_ste 10
265 }
266
267 ##
268 # Installs a drupal core in docroot.
269 #
270 # @param $1
271 #  Major core version.
272 # @param $2
273 #  Used core version.
274 # @param $3 (Optional)
275 #  Installation profile.
276 s_install_drupal() {
277   case "$1" in
278     # Install a drupal 8 core.
279     '8')
280         if [[ -z "$3" ]]; then
281           profile="standard"
282         else
283           profile=$3
284         fi
285         s_install_drupal_8 $2 $profile
286         ;;
287     # Install a drupal 7 core.
288     '7')
289         if [[ -z "$3" ]]; then
290           profile="standard"
291         else
292           profile=$3
293         fi
294         s_install_drupal_7 $2 $profile
295         ;;
296     # Install a drupal 6 core.
297     '6')
298         if [[ -z "$3" ]]; then
299           profile="default"
300         else
301           profile=$3
302         fi
303         s_install_drupal_6 $2 $profile
304         ;;
305     # Unknown version, do at least some basic setups.
306     *)
307         # Give crazy rights for crazy versions.
308         chmod --recursive 777 "/home/$S_ID/www"
309         chown --recursive $S_ID "/home/$S_ID/www"/*
310         chgrp --recursive nogroup "/home/$S_ID/www"/*
311   esac
312 }
313
314 ##
315 # Installs a drupal module.
316 #
317 # @param $1
318 #  Path to the modules files.
319 # @param $2
320 #  Project file-shortname.
321 # @param $3
322 #  Major core version.
323 s_install_module() {
324   case "$3" in
325     '8')
326         chown --recursive $S_ID "/home/$S_ID/www"/*
327         chgrp --recursive nogroup "/home/$S_ID/www"/*
328         ;;
329     '7')
330         chown --recursive $S_ID "/home/$S_ID/www"/*
331         chgrp --recursive nogroup "/home/$S_ID/www"/*
332         timeout 350 sudo -u $S_ID drush en $2 -y
333         ;;
334     '6')
335         chown --recursive $S_ID "/home/$S_ID/www"/*
336         chgrp --recursive nogroup "/home/$S_ID/www"/*
337         timeout 350 sudo -u $S_ID drush en $2 -y
338         ;;
339     *)
340         s_ste 8
341   esac
342 }
343
344 ##
345 # Installs a drupal theme.
346 #
347 # @param $1
348 #  Path to the theme files.
349 # @param $2
350 #  Project file-shortname.
351 # @param $3
352 #  Major core version.
353 s_install_theme() {
354   case "$3" in
355     '8')
356         chown --recursive $S_ID "/home/$S_ID/www"/*
357         chgrp --recursive nogroup "/home/$S_ID/www"/*
358         ;;
359     '7')
360         chown --recursive $S_ID "/home/$S_ID/www"/*
361         chgrp --recursive nogroup "/home/$S_ID/www"/*
362         timeout 350 sudo -u $S_ID drush en $2 -y
363         if [[ -n $(drush pm-list --pipe --status=enabled --no-core | grep "$2") ]]; then
364           timeout 350 sudo -u $S_ID drush vset theme_default $2 --yes --always-set
365         fi
366         ;;
367     '6')
368         chown --recursive $S_ID "/home/$S_ID/www"/*
369         chgrp --recursive nogroup "/home/$S_ID/www"/*
370         timeout 350 sudo -u $S_ID drush en $2 -y
371         if [[ -n $(drush pm-list --pipe --status=enabled --no-core | grep "$2") ]]; then
372           timeout 350 sudo -u $S_ID drush vset theme_default $2 --yes --always-set
373         fi
374         ;;
375     *)
376         s_ste 8
377   esac
378 }
379
380 ##
381 # Add infobar script snippet to docroot, it'll be automatically appended by php.ini.
382 #
383 # @todo ugly php code within shell script - uuhhh.
384 s_addsnippet() {
385   S_TIMESTAMP=$(date +%s)
386   echo "<?php
387     \$simplytest_snippet = array(
388       'id' => \"$S_ID\",
389       'created_timestamp' => $S_TIMESTAMP,
390       'timeout' => $S_TIMEOUT,
391       'admin_user' => \"$S_DUSR\",
392       'admin_password' => \"$S_DPWD\",
393       'mysql' => \"$S_ID\",
394       'project' => \"$S_PROJECT\",
395       'version' => \"$S_VERSION\",
396       'mail_suffix' => \"$S_DMAIL_SUFFIX\",
397     );
398     ?>" >> "/home/$S_ID/www/snippet.php"
399   cat "$DIRECTORY/snippet.php" >> "/home/$S_ID/www/snippet.php"
400 }
401
402 ##
403 # Rewrites all .info files in the specified directory.
404 #
405 # @param $1
406 #  Directory to rewrite all .info files from.
407 # @param $2
408 #  Version value to insert.
409 # @param $3
410 #  Project value to insert.
411 s_rewrite_info_files() {
412   # Find all info files within the path.
413   local info_files=$(find "$1" -name '*.info')
414   for info_file in $info_files; do
415     echo "
416
417 ; Information added by simplytest.me build script
418 version = \"$2\"
419 project = \"$3\"
420 " >> "$info_file"
421   done
422 }
423
424 ##
425 # Reads all dependencies out of the specified .info file
426 #
427 # @param $1
428 #  Info file to read dependencies of.
429 s_read_dependencies() {
430   s_info_file_get_all "$1" "dependencies"
431   s_info_file_get_all "$1" "test_dependencies"
432   s_info_file_get_all "$1" "simplytest_dependencies"
433   s_info_file_get "$1" "base theme"
434 }
435
436 ##
437 # Resolves all dependencies within the given path.
438 #
439 # @todo Version specific dependencies not fetched #1836012.
440 #
441 # @param $1
442 #  Root path, for searching resolved dependencies in.
443 # @param $2
444 #  Path to resolve dependencies for.
445 #
446 # For this function we need some static dependency container:
447 declare -A __RESOLVED_DEPENDENCIES
448 #
449 s_resolve_dependencies() {
450   cd "$2"
451   # Find all info files within the path.
452   local info_files=$(find . -name '*.info')
453   for info_file in $info_files; do
454     # Read out dependencies (dependencies = "", base theme = "").
455     local cur_dependencies=$(s_read_dependencies "$info_file")
456     # Iterate through all dependencies.
457     for cur_dependency in $cur_dependencies; do
458       # 1. Make sure it's not empty, 2. Make sure it's not resolved yet, 3. Version definitions aren't modules.
459       if [[ -n $cur_dependency && -z ${__RESOLVED_DEPENDENCIES[$cur_dependency]} && ${cur_dependency:0:1} != '(' ]]; then
460         cd "$1"
461         lg "$(basename $info_file) depends on \"$cur_dependency\""
462         # Mark current dependency as resolved.
463         __RESOLVED_DEPENDENCIES[$cur_dependency]="1"
464         # Dependency is not already resolved? (prevent infinite recursion).
465         if [[ -z $(find . -name "$cur_dependency.info") ]]; then
466           local cur_real_dependency=$cur_dependency
467           local cur_recursive_dependency=$cur_dependency
468           # Try to resolve dependency by project alias list (eg. content -> cck).
469           if [[ -n ${__PROJECT_ALIASES[$cur_dependency]} ]]; then
470             cur_dependency=${__PROJECT_ALIASES[$cur_dependency]}
471           fi
472           local result=$(timeout 350 drush dl --gitinfofile --pipe -y $cur_dependency)
473           # If that failed, the dependency is still missing, try to guess and download the main module.
474           # eg. entity_token -> doesn't exist ----> entity|_token -----> entity -> does exist!
475           if [[ -z "$result" ]]; then
476             cur_dependency=${cur_dependency%_*}
477             local cur_recursive_dependency=$cur_dependency
478             local result=$(timeout 350 drush dl --gitinfofile --pipe -y $cur_dependency)
479           fi
480           # If that succeeded, resolve dependencies of the dependency (recursion).
481           if [[ -n "$result" ]]; then
482             lg "-> Successfully resolved $cur_real_dependency (Project: $cur_dependency)."
483             s_resolve_dependencies "$1" $(dirname $(find "$1" -name "$cur_recursive_dependency.info" | sort | head -n 1))
484           fi
485         fi
486         # Back to root dir.
487         cd "$2"
488       fi
489     done
490   done
491 }
492
493 ##
494 # Tries to find a main info file within the given path.
495 #
496 # @param $1
497 #  Path to search within.
498 #
499 # @return
500 #  Relative path to the found info file.
501 s_discover_info_file() {
502   find "$1" -name '*.info' | head -n 1
503 }
504
505 ##
506 # Destructs the specified sandbox.
507 #
508 # @param $1
509 #  ID of the environment to destruct.
510 s_destruct() {
511   # We don't want to set any states within destruction.
512   s_ste() {
513     :
514   }
515
516   # Disable vhost.
517   lg "Disable vhost.."
518   a2dissite $S_ID
519
520   # Delete vhost.
521   lg "Delete vhost.."
522   rm -f "/etc/apache2/sites-available/$S_ID"
523
524   # Reload config.
525   lg "Reload apache config.."
526   service apache2 reload
527
528   # Remove user.
529   lg "Remove user.."
530   userdel --force --remove $S_ID
531
532   # Delete cgi wrapper.
533   lg "Delete CGI wrapper.."
534   rm -R /var/www/wrappers/$S_ID
535
536   # Delete mysql user and database.
537   lg "Cleanup database.."
538   s_sqlquery "DROP USER '$S_ID'@'localhost';"
539   s_sqlquery "DROP DATABASE $S_ID;"
540 }
541
542 ##
543 # Logs a message to stdout.
544 #
545 # @param $1
546 #  Message to log.
547 lg() {
548   echo "::: `date` :  $1"
549 }
550
551 ##
552 # Sets a progress state on simplytest.me.
553 #
554 # @param $1
555 #  State to set.
556 #   ENQUEUE           0
557 #   PREPARE           1
558 #   DOWNLOAD          2
559 #   INSTALLING        3
560 #   FINALIZE          4
561 #   FINISHED          5
562 #   TERMINATED        6
563 #   ERROR_SERVER      7
564 #   ERROR_PREPARE     8
565 #   ERROR_DOWNLOAD    9
566 #   ERROR_INSTALLING 10
567 s_ste() {
568   # Set the new state by querying the site's callback.
569   wget -qO /dev/null "$S_CALLBACK/$S_ID/$1"
570   # Destroy and exit on error state.
571   if [[ $1 -ge 7 ]]; then
572      echo "FAILED ON STATE $1"
573      s_destruct "$S_ID"
574      # Before sending the terminated state, we need some time
575      # to make sure the user was informed about the error state.
576      # @todo figure out a better solution here, maybe better
577      # error logging system
578      sleep 30
579      # Tell the server that a slot has become free.
580      wget -qO /dev/null "$S_CALLBACK/$S_ID/6"
581      exit 1
582   fi
583 }
584
585 # Figure out which type of project to set up and include the proper source.
586 case "$S_TYPE" in
587   'Drupal core')
588       lg "Building a drupal core project."
589       source "$DIRECTORY/sources/type-drupal-core"
590       ;;
591   'Module')
592       lg "Building a drupal module project."
593       source "$DIRECTORY/sources/type-module"
594       ;;
595   'Theme')
596       lg "Building a drupal theme project."
597       source "$DIRECTORY/sources/type-theme"
598       ;;
599   'Distribution')
600       lg "Building a drupal distribution project."
601       source "$DIRECTORY/sources/type-distribution"
602       ;;
603 esac