nur/modules/web-apps/wordpress/app.nix
2021-11-22 21:39:44 +01:00

128 lines
4.4 KiB
Nix

with import ./utils.nix;
let
writeableDefault = {
appPaths = []; # list of paths in the app to make writeable
pkgPath = "_writeable"; # path to create in read-only package that stores original content of writeable paths
sysPath = required "writeable.sysPath" ''system path to store writeable data (e.g. "/var/lib/phpfpm/my-app")'';
owner = required "writeable.owner" "user which owns the writeable files";
};
in
{ callPackage
, lib
, runCommand
, writeScript
, writeText
, appConfig
, writeable ? writeableDefault
, ...
}:
let
# Merge the given writeable settings with the defaults.
writeable_ = writeableDefault // writeable;
# We only care about writeble paths if the app is mostly frozen.
writeablePaths = lib.optionals appConfig.freezeWordPress (
["wp-content/uploads"]
++ lib.optional (!appConfig.freezePlugins) "wp-content/plugins"
++ lib.optional (!appConfig.freezeThemes) "wp-content/themes"
++ writeable_.appPaths
);
wordpress = callPackage appConfig.wordpress {};
plugins = callPackage appConfig.plugins {};
themes = callPackage appConfig.themes {};
# The wp-config.php file.
wpConfigFile = writeText "wp-config.php" appConfig.wpConfig.rendered;
# Generates a list of paths in bash that can be looped over.
listOfPaths = lib.concatMapStringsSep " " (x: "'${x}'");
# Generates the bash command to install a path from source to destination.
# If the install is `frozen`, then we simply symlink, otherwise we copy.
installPath = isFrozen: from: to:
(if isFrozen then "ln -s" else "cp -r") + " " + ''"${from}" "${to}"'';
# The build script for the app.
# This will install WordPress, the wp-config, plugins, and themes.
# If any writeble paths were configured, this script will copy them to a another folder in the
# package and set up symlinks in their place the given writeable path on the system.
buildPackageAt = out: ''
mkdir -p $(dirname "${out}") # Make parent directory.
cp -r "${wordpress}" "${out}"
chmod -R +w "${out}"
${installPath appConfig.freezeWordPress wpConfigFile "${out}/wp-config.php"}
# Install themes.
rm -r "${out}/wp-content/themes"/* # remove bundled themes
${lib.concatMapStringsSep "\n" (x: installPath appConfig.freezeThemes x "${out}/wp-content/themes/${x.name}") themes}
# Install plugins.
rm -r "${out}/wp-content/plugins"/* # remove bundled plugins
${lib.concatMapStringsSep "\n" (x: installPath appConfig.freezePlugins x "${out}/wp-content/plugins/${x.name}") plugins}
# TODO: Support translations.
${lib.optionalString (writeablePaths != []) ''
# Make symlinks to writeable directories.
writeable_orig_dir="${out}/${writeable_.pkgPath}"
mkdir -p "$writeable_orig_dir"
for thing in ${listOfPaths writeablePaths}; do
original_thing="$writeable_orig_dir/$thing"
parent=$(dirname "$original_thing")
mkdir -p "$parent"
# Move any existing data to the frozen writeable dir or create empty directory there.
mv "${out}/$thing" "$parent" || mkdir -p "$original_thing"
ln -s "${writeable_.sysPath}/$thing" "${out}/$thing"
done
''}
'';
# Copy the original writeable contents of the package to a writeable dir.
initWriteablePathsFor = package: ''
mkdir -p "$out"
writeable_orig_dir="${package}/${writeable_.pkgPath}"
for thing in $( ls "$writeable_orig_dir" ); do
cp -r "$writeable_orig_dir/$thing" "$out"
done
'';
# Takes an existing script and makes a initialization script that only runs if the output path
# has not been built yet.
mkInitScript = script: writeScript "init-writeable-paths" ''
#!/bin/sh
out="${writeable_.sysPath}"
if [ ! -d "$out" ]; then
${script}
chown -R "${writeable_.owner}" "$out"
chmod -R 744 "$out"
else
echo Output directory already exists. Not building path: "$out"
fi
'';
in if appConfig.freezeWordPress
then rec {
# For a mostly frozen app, we install it as a package and set up writeable paths on first run.
initScript = mkInitScript (initWriteablePathsFor package);
package = runCommand "wordpress-app" {
preferLocalBuild = true;
} (buildPackageAt "$out");
}
else rec {
# For fully writeable app, we skip package installation and write the app directly to the
# writeable path on first run.
initScript = mkInitScript (buildPackageAt package);
package = writeable_.sysPath;
}