Quick Install

One command is all you need to start a new website.
wget -qO - https://codeberg.org/vhs/after-dark/raw/branch/trunk/bin/install | sh

Run the above command in a terminal emulator after installing Hugo to start a new website in 5-10 seconds. Hugo version 0.51 or greater required.


After Dark includes a portable installation script for quick set-up. Please install Hugo 0.51 or greater before running:

Expand to view script
  1#!/bin/sh
  2#
  3# Copyright (C) 2019  VHS <vhsdev@tutanota.com>
  4#
  5# This file is part of After Dark.
  6#
  7# After Dark is free software: you can redistribute it and/or modify
  8# it under the terms of the GNU Affero General Public License as published
  9# by the Free Software Foundation, either version 3 of the License, or
 10# (at your option) any later version.
 11#
 12# After Dark is distributed in the hope that it will be useful,
 13# but WITHOUT ANY WARRANTY; without even the implied warranty of
 14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 15# GNU Affero General Public License for more details.
 16#
 17# You should have received a copy of the GNU Affero General Public License
 18# along with this program.  If not, see <https://www.gnu.org/licenses/>.
 19#
 20
 21set -eu
 22
 23validate_hugo() {
 24   # Exit with error if hugo is not installed
 25   if ! hash hugo 2>/dev/null; then
 26      echo "Error: After Dark requires Hugo version 0.51 or greater" >&2
 27      exit 1
 28   fi
 29
 30   # Exit with error if not minimum required hugo version
 31   re="v(0\d*\.([5-9][1-9]|[6-9])|[1-9]).*"
 32   if ! hugo version | grep -qE "$re"; then
 33      echo "Error: After Dark requires Hugo version 0.51 or greater" >&2
 34      exit 1
 35   fi
 36}
 37
 38confirm_intent() {
 39   while true; do
 40      printf "Do you wish to install After Dark? (y/n) "
 41      read -r yn </dev/tty
 42      case $yn in
 43      [Yy]*)
 44         break
 45         ;;
 46      [Nn]*) exit ;;
 47      *) echo "Please answer yes or no." ;;
 48      esac
 49   done
 50}
 51
 52prompt_site_name() {
 53   printf "Please enter a name for your site: "
 54   read -r response </dev/tty
 55   SITE_NAME=$(echo "$response" | tr -cd '[:alnum:] [:space:]' | tr -s '[:space:]')
 56   while true; do
 57      printf "You entered %s. Is that correct? (y/n) " "\"$SITE_NAME\""
 58      read -r yn </dev/tty
 59      case $yn in
 60      [Yy]*)
 61         break
 62         ;;
 63      [Nn]*)
 64         prompt_site_name
 65         break
 66         ;;
 67      *) echo "Please answer yes or no." ;;
 68      esac
 69   done
 70}
 71
 72format_dir_name() {
 73   echo "$1" | tr '[:upper:]' '[:lower:]' | tr ' ' '-' | tr -cd '[:alnum:]._-'
 74}
 75
 76prompt_install_dir() {
 77   default_dir=$(format_dir_name "$SITE_NAME")
 78   printf "Choose an installation directory name (%s): " "$default_dir"
 79   read -r response </dev/tty
 80   if [ "$response" = "" ]; then
 81      SITE_DIR=$default_dir
 82   elif [ "${response%"${response#?}"}" = "/" ]; then
 83      echo "Directory name not allowed. Proceeding with default..."
 84      SITE_DIR=$default_dir
 85   else
 86      formatted_input_dir=$(format_dir_name "$response")
 87      if [ "$formatted_input_dir" = "" ]; then
 88         echo "Directory name not allowed. Proceeding with default..."
 89         SITE_DIR=$default_dir
 90      else
 91         SITE_DIR=$formatted_input_dir
 92      fi
 93   fi
 94   while true; do
 95      printf "Installation directory name set to %s. Is that correct? (y/n) " "\"$SITE_DIR\""
 96      read -r yn </dev/tty
 97      case $yn in
 98      [Yy]*)
 99         break
100         ;;
101      [Nn]*)
102         prompt_install_dir
103         break
104         ;;
105      *) echo "Please answer yes or no." ;;
106      esac
107   done
108}
109
110create_site_dir() {
111   SITE_DIR_ABS="$PWD/$SITE_DIR"
112   mkdir "$SITE_DIR"
113}
114
115create_site() {
116   echo "Creating a new Hugo site ..."
117   hugo new site "$SITE_DIR" 1>/dev/null
118   cd "$SITE_DIR" || exit 1
119}
120
121download_theme() {
122   echo "Downloading the latest version of After Dark ..."
123   LATEST_META=$(wget -qO - https://registry.npmjs.org/after-dark/latest)
124   vers=$(echo "$LATEST_META" | grep -oE "\"version\".*[^,]*," | cut -d ',' -f1 | cut -d ':' -f2 | tr -d '" ')
125   mkdir -p themes/after-dark
126   wget -qO - https://registry.npmjs.org/after-dark/-/after-dark-"$vers".tgz | tar --strip-components=1 -xz -C themes/after-dark
127   echo "Version $vers downloaded to $SITE_DIR/themes/after-dark"
128}
129
130download_module() {
131   [ -z "$1" ] && {
132      echo "Error: Attempt to download undefined module" >&2
133      exit 1
134   }
135   echo "Downloading $1 module for After Dark ..."
136   meta=$(wget -qO - https://registry.npmjs.org/"$1"/latest)
137   vers=$(echo "$meta" | grep -oE "\"version\".*[^,]*," | cut -d ',' -f1 | cut -d ':' -f2 | tr -d '" ')
138   mkdir -p themes/"$1"
139   wget -qO - https://registry.npmjs.org/"$1"/-/"$1"-"$vers".tgz | tar --strip-components=1 -xz -C themes/"$1"
140   echo "Version $vers downloaded to $SITE_DIR/themes/$1"
141}
142
143configure_theme() {
144   echo "Configuring basic After Dark theme settings ..."
145   tee "config.toml" >/dev/null <<TOML
146baseurl = "https://domain.example" # Controls base URL sitewide
147languageCode = "en-US" # Controls site language
148title = "$SITE_NAME" # Homepage title and page title suffix
149paginate = 11 # Number of posts to show before paginating
150copyright = "Copyright &copy; Copyright Owner. Licensed under <a target=\"_blank\" rel=\"external noopener license\" href=\"https://creativecommons.org/licenses/by-nd/4.0/\">CC-BY-ND-4.0</a>." # Optional, remove to suppress copyright notices
151
152# Controls default theme and theme components
153theme = [
154  "fractal-forest", # OBSD
155  "after-dark" # AGPL-3.0-or-later
156]
157
158disableLiveReload = false # Optional, set true to disable live reload
159enableRobotsTXT = true # Suggested, enable robots.txt file
160sectionPagesMenu = "main" # Enable menu system for lazy bloggers
161
162[markup.goldmark.renderer]
163  unsafe = true # Optional, allows HTML inside your CommonMark content
164[markup.tableOfContents]
165  startLevel = 1 # Suggested, draws TOC using all heading levels
166  endLevel = 6 # Suggested, draws TOC using all heading levels
167[markup.highlight]
168  noClasses = false # Suggested, used for custom syntax highlighting
169
170[params]
171  description = "" # Suggested, controls default description meta
172  author = "" # Optional, controls author name display on posts
173  hide_author = false # Optional, set true to hide author name on posts
174  disable_csp = false # Optional, set true to disable content security policy
175  images = [
176    "https://source.unsplash.com/collection/983219/2000x1322"
177  ] # Suggested, controls default Open Graph images
178
179[params.layout.menu.main]
180  hidden = true # Optional, set false or remove to show section menu
181
182[params.layout.footer]
183  hidden = false # Optional, set true to hide footer
184
185[params.modules.fractal_forest]
186  enabled = true # Optional, set false to disable module
187  decoders = ["bpgdec8a"] # Optional, 8-bit javascript decoder with animation
188TOML
189}
190
191update_archetypes() {
192   echo "Updating the default content archetype ..."
193   rm -f archetypes/default.md
194   cp themes/after-dark/archetypes/default.md archetypes
195}
196
197create_welcome_post() {
198   echo "Creating welcome post ..."
199   hugo new post/welcome.md 1>/dev/null
200}
201
202generate_help_docs() {
203   echo "Generating help documentation ..."
204   THEME_PATH=themes/after-dark
205   meta_path="$THEME_PATH"/data/npm
206   mkdir -p "$meta_path" && echo "$LATEST_META" | tr '\r\n' ' ' >"$meta_path"/latest.json
207   cd "$THEME_PATH"/docs && mkdir themes && ln -s ../.. "$THEME_PATH"
208   hugo new validate.md --kind validate 1>/dev/null
209}
210
211echo "Welcome to the After Dark quick installer. Press CTRL-C at any time to abort."
212
213confirm_intent
214validate_hugo
215prompt_site_name
216prompt_install_dir
217create_site_dir
218create_site
219download_theme
220update_archetypes
221download_module "fractal-forest"
222configure_theme
223create_welcome_post
224generate_help_docs
225
226YELLOW='\033[0;33m'
227NC='\033[0m'
228
229echo "${YELLOW}Installation successful!${NC}"
230echo "Site created in $SITE_DIR_ABS"
231echo "Serve your site with \"hugo serve --buildDrafts --navigateToChanged\""
232echo "Thank you for choosing After Dark."

Script has been tested on GNU/Linux, BSD (Darwin) and Windows via Cmder.

Warning: Examine scripts downloaded from the Internet before running them.

Here are three methods for downloading and running:

  1. Download and pipe to sh directly:

    wget -qO - https://codeberg.org/vhs/after-dark/raw/branch/trunk/bin/install | sh
  2. Download into new file, chmod and execute:

    curl -O https://cdn.jsdelivr.net/npm/after-dark@latest/bin/install && \
    chmod +x install && ./install
  3. From canonical git clone:

    # clone source and change to source directory
    git clone https://codeberg.org/vhs/after-dark.git && cd "$_"
    
    # use npm cli to get the release hash
    echo "${$(npm run integrity)#*sha512-}"
    
    # run quick install after validating
    ./bin/install

Script should complete in 5-10 seconds resulting in a sample site and help docs:

After Dark screenshots
After Dark Quick Install running to completion in Terminal on Deepin Manjaro.

Your new site will be called flying-toasters. Change it to the name of your project anytime you like. Access site by navigating to https://localhost:1313.

Multi-site Configuration (Advanced)

After Dark enables multi-site management from a single installation. To manage multiple websites use the -c and -d flags to specify the content and destination directories, respectively.

For example, to generate an audio site using the current After Dark installation create an executable script to generate the site:

flying-toasters/bin/gen-audio-site
#!/bin/sh
hugo -c sites/audio -d public/static.domain.example

Where audio contains the content for that site:

├── layouts
├── sites
│   └── audio
│       ├── audiobooks
│       │   ├── gaining-currency.md
│       │   └── the-power-of-now.md
│       └── clips
│           └── war-of-the-worlds.md
├── static

And public contains a folder for each site:

public
└── static.domain.example
    ├── categories
    │   └── index.xml
    ├── audiobooks
    │   └── index.html
    ├── clips
    │   └── index.html
    ├── css
    ├── index.html
    ├── index.xml
    ├── js
    ├── sitemap.xml
    └── tags
        └── index.xml

And create another script to serve the content for editing:

flying-toasters/bin/serve-audio-site
#!/bin/sh
hugo -c sites/audio

Each subdirectory of public then becomes an independent, deployable website and exact copy save for destination content generated.

Tip: For additional flexibility type hugo --help and modify your scripts using the --theme and --config flags.

Multi-site is perfect for maintaining a consistent look-and-feel across multiple domain origins while limiting the need to run the Upgrade Script for each site.