@@ -6,28 +6,100 @@
 
 set -e
 
+# ---- Global constants ----
+
+declare -r DEBOPS_DATA_HOME="${XDG_DATA_HOME:-$HOME/.local/share}/debops"
+declare -r DEBOPS_CONFIG=".debops.cfg"
+declare -r DEBOPS_INVENTORY="inventory"
+declare -r SCRIPT_NAME="$(basename ${0})"
+declare -r SECRET_SUFFIX=".secret"
+
+
+# ---- Configuration variables ----
+
 SKEL_DIRS=(
-  "ansible/inventory/group_vars/all"
-  "ansible/inventory/host_vars"
+  "ansible/${DEBOPS_INVENTORY}/group_vars/all"
+  "ansible/${DEBOPS_INVENTORY}/host_vars"
   "ansible/playbooks"
 )
 
+
+# ---- Functions ----
+
+# Find specified file or directory in parent dir (if not specified, finds $DEBOPS_CONFIG)
+# Source: https://unix.stackexchange.com/questions/13464
+find_up () {
+  local name=${1:-$DEBOPS_CONFIG}
+  local slashes="${PWD//[^\/]/}"
+  local directory="${PWD}"
+
+  for (( n=${#slashes}; n>0; --n )) ; do
+    test -e "${directory}/${name}" && echo "$(readlink -f ${directory}/${name})" && return
+    directory="$directory/.."
+  done
+}
+
+# Display error message and exit
+error_msg () {
+  local severity="${2:-Error}"
+  local message="${1}"
+
+  echo >&2 "${SCRIPT_NAME}: ${severity}: ${message}"
+  set +e
+  [[ "${severity}" == "Error" ]] && exit 1
+  set -e
+}
+
+# Check if required commands exist
+require_commands () {
+  for name in ${@} ; do
+    if ! type ${name} > /dev/null 2>&1 ; then
+      error_msg "${name}: command not found"
+    fi
+  done
+}
+
+
+# ---- DebOps environment setup ----
+
+# Specify name of new project directory
 project="${1:-$PWD}"
 
-if [ -f "$project/.debops.cfg" ]; then
-  echo >&2 "$project is already a DebOps project" ; exit 1
+# Find DebOps configuration file
+debops_config="$(find_up)"
+
+# Check if project dir already exists
+new_project="$(find_up ${project})"
+
+# Find root of the DebOps project dir
+[ -n "${debops_config}" ] && debops_root="$(dirname ${debops_config})"
+
+# Exit if we are in a DebOps project dir and nested project would be created
+if [ -n "${debops_root}" ] ; then
+  if [ "${debops_root}" = "${new_project}" ] ; then
+    error_msg "You are inside ${new_project} project already"
+  else
+    [ -z "${project}" ] && error_msg "Cannot create nested project in ${debops_root}"
+  fi
 fi
 
+# Exit if DebOps configuration file has been found
+[ -r "${project}/${DEBOPS_CONFIG}" ] && error_msg "${project} is already a DebOps project"
+
+
+# ---- Main script ----
+
 for skel_dir in "${SKEL_DIRS[@]}"; do
   if [ ! -d "$project/$skel_dir" ]; then
     mkdir -p "$project/$skel_dir"
   fi
 done
 
-touch "$project/.debops.cfg"
+touch "$project/${DEBOPS_CONFIG}"
 
 gitignore_content=$(cat <<EOF
-ansible/inventory.secret
+ansible/${DEBOPS_INVENTORY}${SECRET_SUFFIX}
+${DEBOPS_INVENTORY}${SECRET_SUFFIX}
 
 
 # Created by http://www.gitignore.io
@@ -90,7 +162,7 @@ echo "$gitignore_content" >> "$project/.gitignore"
 
 # Swap in different hosts file content depending on the host's OS/distro
 valid_debops_controller=0
-hosts_file="ansible/inventory/hosts"
+hosts_file="ansible/${DEBOPS_INVENTORY}/hosts"
 
 if [ $(uname) = "Linux" ]; then
   distro=$(lsb_release -si)
@@ -134,7 +206,7 @@ fi
 
 echo "$hosts_content" >> "$project/$hosts_file"
 
-apt_lxc_file="ansible/inventory/group_vars/all/apt_lxc.yml"
+apt_lxc_file="ansible/${DEBOPS_INVENTORY}/group_vars/all/apt_lxc.yml"
 
 apt_lxc_content=$(cat <<EOF
 # Occasionally Debian's CDN is a bit flaky. If you get 404s trying to access