From cce156e037cf0e04db479c4eea768c0a933ae4b2 Mon Sep 17 00:00:00 2001 From: Antonio Ospite Date: Mon, 5 Jun 2017 15:13:28 +0200 Subject: [PATCH 1/1] Initial import --- .gitignore | 2 + Makefile | 35 +++++++++ README.txt | 15 ++++ TODO | 7 ++ drin.in | 67 +++++++++++++++++ drin.rst | 102 ++++++++++++++++++++++++++ libexec/bootstrap.sh | 180 ++++++++++++++++++++++++++++++++++++++++++++++ libexec/clean.sh | 58 +++++++++++++++ libexec/create-profile.sh | 116 ++++++++++++++++++++++++++++++ libexec/new.sh | 121 +++++++++++++++++++++++++++++++ 10 files changed, 703 insertions(+) create mode 100644 .gitignore create mode 100644 Makefile create mode 100644 README.txt create mode 100644 TODO create mode 100644 drin.in create mode 100644 drin.rst create mode 100755 libexec/bootstrap.sh create mode 100755 libexec/clean.sh create mode 100755 libexec/create-profile.sh create mode 100755 libexec/new.sh diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0c56601 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +drin +drin.1 diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..49a32a6 --- /dev/null +++ b/Makefile @@ -0,0 +1,35 @@ +# Packagers may want to override this! +prefix ?= /usr/local + +LIBEXEC_DIR ?= $(prefix)/share/drin/libexec +BIN_DIR := $(prefix)/bin +MAN_DIR := $(prefix)/share/man + +.PHONY: all local installdocs install clean + +all: drin + +drin: drin.in + sed -e 's#@libexec@#$(LIBEXEC_DIR)#g' $< > $@ \ + && chmod +x $@ + +# Useful during development to run the script from the working directory +local: LIBEXEC_DIR := $(PWD)/libexec +local: clean drin + touch drin.in + +installdocs: drin.1 + install -d $(DESTDIR)$(MAN_DIR)/man1 + install -m644 drin.1 $(DESTDIR)$(MAN_DIR)/man1 + +install: drin installdocs + install -d $(DESTDIR)$(LIBEXEC_DIR) + install -m755 libexec/*.sh $(DESTDIR)$(LIBEXEC_DIR) + install -d $(DESTDIR)$(BIN_DIR) + install -m755 $< $(DESTDIR)$(BIN_DIR) + +clean: + rm -f drin drin.1 + +%.1: %.rst + rst2man $< > $@ diff --git a/README.txt b/README.txt new file mode 100644 index 0000000..b9d03c9 --- /dev/null +++ b/README.txt @@ -0,0 +1,15 @@ +Helper commands to create and install new Drupal projects. +See the 'drin.rst' file for more info. + +To test the commands locally, execute `make local` and run `./drin`. + +Dependencies: + - bash + - composer + - git + - sudo + +The shell script template was inspired by +http://agateau.com/2014/template-for-shell-based-command-line-scripts/ + +Copyright (C) 2017 Antonio Ospite diff --git a/TODO b/TODO new file mode 100644 index 0000000..e77aa2a --- /dev/null +++ b/TODO @@ -0,0 +1,7 @@ +- add bash-completion files to autocomplete subcommands names + +And maybe: + - add a --destdir= option to the 'create-profile' command + - in the 'new' command, set up a local composer repository like shown in + https://github.com/drupal-composer/drupal-project/issues/249 + - add a --vanilla option to the 'new' command to skip any patching diff --git a/drin.in b/drin.in new file mode 100644 index 0000000..4ba7583 --- /dev/null +++ b/drin.in @@ -0,0 +1,67 @@ +#!/bin/bash +# drin - helper tools to initialize Drupal projects +# +# Copyright (C) 2017 Antonio Ospite +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +set -e + +PROGNAME=$(basename $0) +LIBEXEC="@libexec@" + +usage() { + cat <&2 && exit 1; } + +while [ $# -gt 0 ]; +do + case "$1" in + -h|--help) + usage + exit 0 + ;; + -*) + echo "Error: Unknown option '${1}'" 1>&2 + ;; + *) + subcommand="${LIBEXEC}/${1}.sh" + if [ ! -x "$subcommand" ]; then + echo "Error: '${1}' is not a known subcommand." 1>&2 + echo "Run '${PROGNAME} --help' for a list of known subcommands." 1>&2 + exit 1 + else + shift + exec $subcommand "$@" + fi + ;; + esac + shift +done diff --git a/drin.rst b/drin.rst new file mode 100644 index 0000000..ed21253 --- /dev/null +++ b/drin.rst @@ -0,0 +1,102 @@ +====== + drin +====== + +----------------- +Drupal init tools +----------------- + +:Author: Antonio Ospite +:Date: 2017-06-20 +:Copyright: GPLv2+ +:Manual section: 1 +:Manual group: General Commands Manual + +SYNOPSIS +======== + +*drin* [sub-command] + +DESCRIPTION +=========== + +Helper commands to create and install new Drupal projects. + +When setting up a new Drupal project with drupal-composer/drupal-project drush +and drupal-console are not available yet and some repetitive tasks can use +a nicer command line interface. + +These scripts are especially useful when setting up projects in user web +directories[1]. + +[1] http://httpd.apache.org/docs/current/howto/public_html.html + + +OPTIONS +======= + +Available options: + + **-h**, **--help** + this help + + +Available sub-commands: + + ``new`` `[-h|--help]` `` `[composer options (e.g. --devel)]` + Create a new Drupal project in the `destdir` directory. + + ``bootstrap`` `[--devel|-h|--help]"` + Bootstrap a Drupal project, using settings from a `bootstrap.conf` file. + + ``clean`` `[-h|--help]` + Cleanup the project, removing all the installed files. + + ``create-profile`` `[-h|--help]` `` `<machine_name>` + Create an installation profile from the installed project. + + +EXAMPLES OF USE +=============== + +Create and install a new Drupal project: + +:: + + cd ~/public_html + drin new drupal_test_site + cd drupal_test_site + $EDITOR bootstrap.conf + drin bootstrap --devel + + +Create an installation profile from the currently installed project: + +:: + + drin create-profile "Test Profile" test_profile + + +Clean and rebuild the whole project to verify that installing from scratch works: + +:: + + drin clean + drin bootstrap + + +SEE ALSO +======== + +* drupal-composer/drupal-project: https://github.com/drupal-composer/drupal-project +* drush: https://github.com/drush-ops/drush +* drupal-console: https://github.com/hechoendrupal/drupal-console + +.. _drupal-composer/drupal-project: https://github.com/drupal-composer/drupal-project +.. _drush: https://github.com/drush-ops/drush +.. _drupal-console: https://github.com/hechoendrupal/drupal-console + +BUGS +==== + +None known. diff --git a/libexec/bootstrap.sh b/libexec/bootstrap.sh new file mode 100755 index 0000000..3b52f26 --- /dev/null +++ b/libexec/bootstrap.sh @@ -0,0 +1,180 @@ +#!/bin/bash +# Bootstrap a Drupal project +# +# Copyright (C) 2017 Antonio Ospite <ao2@ao2.it> +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +set -e + +usage() { + cat <<EOF +usage: drin $(basename $0 .sh) [--devel|-h|--help] + +Bootstrap a Drupal project, using settings from a 'bootstrap.conf' file. + +Options: + --devel install drupal/devel and use a settings.local.php file + -h, --help display this usage message and exit + +EOF +} + +while [ $# -gt 0 ]; +do + case "$1" in + -h|--help) + usage + exit 0 + ;; + --devel) + DEVEL_MODE="true" + ;; + -*) + echo "Error: Unknown option '${1}'" 1>&2 + ;; + esac + shift +done + +. bootstrap.conf + +declare -p DB_NAME +declare -p DB_USER +declare -p DB_PASS + +declare -p ACCOUNT_NAME +declare -p ACCOUNT_PASS +declare -p ACCOUNT_MAIL + +declare -p SITE_NAME +declare -p SITE_MAIL +declare -p SITE_BASE_PATH + +declare -p TRUSTED_HOSTS + +declare -p WEB_SERVER_GROUP + +[ "x$INSTALLATION_PROFILE" = "x" ] && { echo "INSTALLATION_PROFILE not specified, using the \"standard\" profile!"; INSTALLATION_PROFILE="standard"; } + +if [ "x$MYSQL_ROOT_PASSWORD" = "x" ]; +then + read -s -p "MySQL root password: " MYSQL_ROOT_PASSWORD + echo +fi + +SITE_LOCAL_PATH="${PWD}/web" + +command -v composer &> /dev/null || { echo "Aborting, 'composer' not available." 1>&2; exit 1; } +command -v git &> /dev/null || { echo "Aborting, 'git' not available." 1>&2; exit 1; } + +[ -d "$SITE_LOCAL_PATH" ] || composer install + +# TODO check if the commands are available +DRUSH="${PWD}/vendor/bin/drush" +DRUPAL_CONSOLE="${PWD}/vendor/bin/drupal" + +[ -x "$DRUSH" ] || { echo "Aborting, '$DRUSH' not available." 1>&2; exit 1; } +[ -x "$DRUPAL_CONSOLE" ] || { echo "Aborting, '$DRUPAL_CONSOLE' not available." 1>&2; exit 1; } + +# This becomes unnecessary if the installation profile gets pulled in by +# composer.json, like suggested in +# https://github.com/drupal-composer/drupal-project/issues/249 +if ! echo $INSTALLATION_PROFILE | egrep -q "^(minimal|standard)$"; +then + if [ -d $SITE_LOCAL_PATH/profiles/$INSTALLATION_PROFILE ]; + then + echo "Installation profile '$INSTALLATION_PROFILE' already there." + else + cp -a $INSTALLATION_PROFILE $SITE_LOCAL_PATH/profiles + fi +fi + +pushd "$SITE_LOCAL_PATH" + +$DRUSH --verbose --yes \ + site-install \ + --db-su=root \ + --db-su-pw="$MYSQL_ROOT_PASSWORD" \ + --db-url="mysql://${DB_USER}:${DB_PASS}@localhost/${DB_NAME}" \ + --site-name="$SITE_NAME" \ + --site-mail="$SITE_MAIL" \ + --account-name="$ACCOUNT_NAME" \ + --account-pass="$ACCOUNT_PASS" \ + --account-mail="$ACCOUNT_MAIL" \ + "$INSTALLATION_PROFILE" + +if $DRUSH pm-info --fields=status locale | grep -q enabled; +then + # This is necessary for multi-language sites, it fixes some issues like: + # "The Translation source field needs to be installed." + $DRUSH --yes entity-updates + + # Update translations of contrib modules + $DRUSH --yes locale-update +fi + +# This fixes permissions when installing under $HOME/public_html/ +chmod 775 sites/default/files +sudo chgrp -R "$WEB_SERVER_GROUP" sites/default/files + +[ -d ../config/sync ] && sudo chgrp -R "$WEB_SERVER_GROUP" ../config/sync + +# Enables clean URLs +sed -i "s@# RewriteBase /drupal\$@RewriteBase ${SITE_BASE_PATH}@" .htaccess + +chmod 644 sites/default/settings.php + +# Add some basic settings to settings.php +if ! grep -q "^\\\$settings\['trusted_host_patterns'\] =" sites/default/settings.php; +then + echo "\$settings['trusted_host_patterns'] = [" >> sites/default/settings.php + for host in "${TRUSTED_HOSTS[@]}" + do + echo " '^${host}\$'," >> sites/default/settings.php + done + echo "];" >> sites/default/settings.php +fi + +if [ "$DEVEL_MODE" = "true" ]; +then + # NOTE: don't run composer under web/ but in the project dir + composer --working-dir=../ require drupal/devel + $DRUSH --yes en devel + + chmod 755 sites/default + cp sites/example.settings.local.php sites/default/settings.local.php + chmod 444 sites/default/settings.local.php + chmod 555 sites/default + + if ! grep -q "^include \$app_root . '/' . \$site_path . '/settings.local.php';" sites/default/settings.php; + then + echo "include \$app_root . '/' . \$site_path . '/settings.local.php';" >> sites/default/settings.php + fi +fi + +chmod 444 sites/default/settings.php + +# If using a git checkout of Drupal core, set up a diff alias. +# +# This is useful because the Automated Test infrastructure of drupal.org does +# not expect patches to be created from a split core directory. +if [ -d core/.git ]; +then + git -C core/ config --local alias.core-diff "diff --src-prefix=a/core/ --dst-prefix=b/core/" + echo "Added a 'git core-diff' to the drupal/core repository clone." + echo "This command helps creating core patches ready for upstream." +fi + +popd diff --git a/libexec/clean.sh b/libexec/clean.sh new file mode 100755 index 0000000..34ef55e --- /dev/null +++ b/libexec/clean.sh @@ -0,0 +1,58 @@ +#!/bin/bash +# Clean up a Drupal project directory +# +# Copyright (C) 2017 Antonio Ospite <ao2@ao2.it> +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +set -e + +usage() { + cat <<EOF +usage: drin $(basename $0 .sh) [-h|--help] + +Cleanup the project, removing all the installed files. + +Options: + -h, --help display this usage message and exit +EOF +} + +while [ $# -gt 0 ]; +do + case "$1" in + -h|--help) + usage + exit 0 + ;; + -*) + echo "Error: Unknown option '${1}'" 1>&2 + ;; + esac + shift +done + +CONFIRMATION_STRING="YESIAMSURE" + +echo "WARNING! This removes any files in the config/ web/ and vendor/ directories." +echo "Are you sure you want to continue?" +echo +read -p "Type ${CONFIRMATION_STRING} to confirm: " INPUT + +run() { + echo $* + $* +} + +[ "$INPUT" = "$CONFIRMATION_STRING" ] && run sudo rm -rf config/ web/ vendor/ composer.lock diff --git a/libexec/create-profile.sh b/libexec/create-profile.sh new file mode 100755 index 0000000..96020b3 --- /dev/null +++ b/libexec/create-profile.sh @@ -0,0 +1,116 @@ +#!/bin/bash +# Create a Drupal installation profile +# +# Copyright (C) 2017 Antonio Ospite <ao2@ao2.it> +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +set -e + +usage() { + cat <<EOF +usage: drin $(basename $0 .sh) [-h|--help] <title> <machine_name> + +Create an installation profile from the installed project. + +Options: + -h, --help display this usage message and exit +EOF +} + +while [ $# -gt 0 ]; +do + case "$1" in + -h|--help) + usage + exit 0 + ;; + -*) + echo "Error: Unknown option '${1}'" 1>&2 + ;; + *) + break + ;; + esac + shift +done + + +[ "x$1" = "x" -o "x$2" = "x" ] && { usage 1>&2; exit 1; } + +PROFILE_TITLE="$1" +PROFILE_MACHINE_NAME="$2" + +WEB_ROOT="${PWD}/web" + +[ -d "$WEB_ROOT" ] || { echo "Aborting, run this command from the Drupal project directory." 1>&2; exit 1; } + +PROJECT_ROOT="$PWD" + +DRUPAL_CONSOLE="${PROJECT_ROOT}/vendor/bin/drupal" +DRUSH="${PROJECT_ROOT}/vendor/bin/drush" + +[ -x "$DRUSH" ] || { echo "Aborting, '$DRUSH' not available." 1>&2; exit 1; } +[ -x "$DRUPAL_CONSOLE" ] || { echo "Aborting, '$DRUPAL_CONSOLE' not available." 1>&2; exit 1; } + +[ -d "${PROJECT_ROOT}/${PROFILE_MACHINE_NAME}" ] && { echo "Aborting, ${PROJECT_ROOT}/${PROFILE_MACHINE_NAME} already exists." 1>&2; exit 1; } + +pushd "$WEB_ROOT" + +# The list of modules and themes could also be obtained by exporting the +# configuration first and then looking at: config/install/core.extension.yml +# like this: +# +# sed -e '/module:/,/^[^ ]/!d;//d' -e 's/^[ ]*\(.*\):.*$/\1/' config/install/core.extension.yml +# +# or +# +# $DRUPAL_CONSOLE yaml:get:value "$PWD/$PROFILE_MACHINE_NAME/config/install/core.extension.yml" dependencies +# +# However getting them before exporting the configuration and generating the +# profile is cleaner. +# +ENABLED_MODULES="$($DRUSH pm-list --type=module --status=enabled --pipe | tr '\n' ',')" +ENABLED_THEMES="$($DRUSH pm-list --type=theme --status=enabled --pipe | tr '\n' ',')" + +rm -rf "${WEB_ROOT}/profiles/$PROFILE_MACHINE_NAME" +$DRUPAL_CONSOLE generate:profile \ + --profile="$PROFILE_TITLE" \ + --machine-name="$PROFILE_MACHINE_NAME" \ + --description="Drupal installation profile for $PROFILE_TITLE" \ + --dependencies=$ENABLED_MODULES \ + --themes=$ENABLED_THEMES \ + --no-interaction +cp -a "${WEB_ROOT}/profiles/${PROFILE_MACHINE_NAME}" "$PROJECT_ROOT" + +# Basically do what's suggested in the "Configuration" section here: +# https://www.drupal.org/docs/8/creating-distributions/how-to-write-a-drupal-8-installation-profile +$DRUPAL_CONSOLE config:export --directory="${PROJECT_ROOT}/${PROFILE_MACHINE_NAME}/config/install" --remove-uuid --remove-config-hash +rm "${PROJECT_ROOT}/${PROFILE_MACHINE_NAME}/config/install/core.extension.yml" + +# The reference to the core version could be removed, but this is not strictly necessary +#find "${PROJECT_ROOT}/${PROFILE_MACHINE_NAME}/config/install" -type f -exec sed -i -e '/^_core: { }/d' {} \; + +# Since the profile generated by `$DRUPAL_CONSOLE generate:profile` calls in +# the standard profile, some duplicated config files could be removed in the +# new profile, but that's not strictly necessary either. +#fdupes -f -1 "${WEB_ROOT}/core/profiles/standard/config/install/" "${PROJECT_ROOT}/${PROFILE_MACHINE_NAME}/config/install/" | xargs rm + +# Export the default content if the default_content module is there +if echo $ENABLED_MODULES | grep -q default_content; +then + $DRUSH default-content-export-references --folder="${PROJECT_ROOT}/${PROFILE_MACHINE_NAME}/content" node +fi + +popd diff --git a/libexec/new.sh b/libexec/new.sh new file mode 100755 index 0000000..012c3cb --- /dev/null +++ b/libexec/new.sh @@ -0,0 +1,121 @@ +#!/bin/bash +# Create a new Drupal project +# +# Copyright (C) 2017 Antonio Ospite <ao2@ao2.it> +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +set -e + +usage() { + cat <<EOF +usage: drin $(basename $0 .sh) [-h|--help] <destdir> + +Create a new Drupal project in the 'destdir' directory. + +Options: + -h, --help display this usage message and exit +EOF +} + +while [ $# -gt 0 ]; +do + case "$1" in + -h|--help) + usage + exit 0 + ;; + -*) + echo "Error: Unknown option '${1}'" 1>&2 + ;; + *) + break + ;; + esac + shift +done + +[ "x$1" = "x" ] && { usage 1>&2; exit 1; } + +[ -d "$1" ] && { echo "Aborting, project directory already exists." 1>&2; exit 1; } + +DESTDIR="$1" + +command -v composer &> /dev/null || { echo "Aborting, 'composer' not available." 1>&2; exit 1; } +command -v git &> /dev/null || { echo "Aborting, 'git' not available." 1>&2; exit 1; } + +# Create a new project keeping the VCS metadata so it's easier to bring in +# updates to drupal-composer/drupal-project itself. +echo "Creating a new Drupal project..." +composer create-project drupal-composer/drupal-project:8.x-dev@dev "$DESTDIR" --keep-vcs --stability dev --no-interaction + +pushd "$DESTDIR" + +git remote rename origin upstream +git checkout -b master +echo >> .gitignore +echo "# Ignore the configuration for the bootstrap script" >> .gitignore +echo "bootstrap.conf" >> .gitignore + +# Add some patches, use sed until composer can do that from the command line +# (e.g. composer config ...) +sed -i -e 's@"extra": {@"extra": {\ + "patches-file": "composer.patches.json",@' composer.json + +cat > composer.patches.json <<EOF +{ + "patches": { + "drupal/core": { + "drupal-do_not_disable_MultiViews_htaccess": "https://www.drupal.org/files/issues/drupal-do_not_disable_MultiViews_htaccess-2619250-24.patch" + } + } +} +EOF + +# Apply the patches +composer update + +# Add a template config file for the bootstrap script +cat > bootstrap.conf <<EOF +#DB_NAME="drupal_test_database" +#DB_USER="drupal_test_user" +#DB_PASS="drupal_test_password" + +#ACCOUNT_NAME="admin" +#ACCOUNT_PASS="admin" +#ACCOUNT_MAIL="admin@example.com" + +#SITE_NAME="example" +#SITE_MAIL="admin@example.com" + +#SITE_BASE_PATH="/~${USER}/$(basename "${PWD}")/web" + +#WEB_SERVER_GROUP="www-data" + +#INSTALLATION_PROFILE="standard" + +#TRUSTED_HOSTS=( +# "localhost" +# "ip4-localhost" +# "ip6-localhost" +# ) + +#MYSQL_ROOT_PASSWORD="password" +EOF + +echo +echo "Uncomment and customize the values in the bootstrap.conf file." +echo "Make sure that access to '$PWD' is restricted by the web server." + +popd -- 2.1.4