Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
216 changes: 216 additions & 0 deletions recipe/provision-fedora.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,216 @@
<?php

namespace Deployer;

require __DIR__ . '/provision-fedora/databases.php';
require __DIR__ . '/provision-fedora/nodejs.php';
require __DIR__ . '/provision-fedora/php.php';
require __DIR__ . '/provision-fedora/user.php';
require __DIR__ . '/provision-fedora/website.php';

use Deployer\Task\Context;

use function Deployer\Support\parse_home_dir;

add('recipes', ['provision-fedora']);

// Fedora release number, like: 43, 44, etc.
// As only Fedora 43/44 are supported for provision should be one of those.
set('fedora_version', function () {
return run('rpm -E %fedora');
});

desc('Provision the server');
task('provision', [
'provision:check',
'provision:configure',
'provision:update',
'provision:upgrade',
'provision:install',
'provision:ssh',
'provision:firewall',
'provision:user',
'provision:php',
'provision:node',
'provision:databases',
'provision:composer',
'provision:server',
'provision:website',
'provision:verify',
]);

// Default user to use for provisioning.
set('provision_user', 'root');

desc('Checks pre-required state');
task('provision:check', function () {
set('remote_user', get('provision_user'));

$release = run('cat /etc/os-release');
['NAME' => $name, 'VERSION_ID' => $version] = parse_ini_string($release);
if ($name !== 'Fedora Linux') {
warning('!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!');
warning('!! !!');
warning('!! Only Fedora Linux is supported! !!');
warning('!! !!');
warning('!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!');
if (!askConfirmation(' Do you want to continue? (Not recommended)', false)) {
throw new \RuntimeException('Provision aborted due to incompatible OS.');
}
}
// Also only version 43 and newer are supported.
if (version_compare($version, '43', '<')) {
warning("Fedora $version is not supported. Use Fedora 43 or newer.");
if (!askConfirmation(' Do you want to continue? (Not recommended)', false)) {
throw new \RuntimeException('Provision aborted due to incompatible OS.');
}
}
})->oncePerNode();

desc('Collects required params');
task('provision:configure', function () {
set('remote_user', get('provision_user'));

$params = [
'sudo_password',
'domain',
'public_path',
'db_type',
];
$dbparams = [
'db_user',
'db_name',
'db_password',
];

$showCode = false;

foreach ($params as $name) {
if (!Context::get()->getConfig()->hasOwn($name)) {
$showCode = true;
}
get($name);
}

if (get('db_type') !== 'none') {
foreach ($dbparams as $name) {
if (!Context::get()->getConfig()->hasOwn($name)) {
$showCode = true;
}
get($name);
}
}

if ($showCode) {
$code = "\n\n<comment>====== Configuration Start ======</comment>";
$code .= "\nhost(<info>'{{alias}}'</info>)";
$codeParams = $params;
if (get('db_type') !== 'none') {
$codeParams = array_merge($codeParams, $dbparams);
}
foreach ($codeParams as $name) {
$code .= "\n ->set(<info>'$name'</info>, <info>'" . get($name) . "'</info>)";
}
$code .= ";\n";
$code .= "<comment>====== Configuration End ======</comment>\n\n";
writeln($code);
}
});


desc('Adds repositories and update');
task('provision:update', function () {
set('remote_user', get('provision_user'));

// Update before installing anything
run('dnf -y makecache');

// Pre-requisites
run('dnf install -y curl gnupg2 dnf-plugins-core');

// Caddy
run('dnf copr enable -y @caddy/caddy');

// Update
run('dnf -y makecache');
})
->oncePerNode()
->verbose();

desc('Upgrades all packages');
task('provision:upgrade', function () {
set('remote_user', get('provision_user'));
run('dnf upgrade -y', timeout: 900);
})
->oncePerNode()
->verbose();

desc('Installs packages');
task('provision:install', function () {
set('remote_user', get('provision_user'));
$packages = [
'acl',
'caddy',
'curl',
'fail2ban',
'firewalld',
'gcc',
'gcc-c++',
'git',
'make',
'memcached',
'ncdu',
'nodejs',
'openssh-server',
'pcre-devel',
'pkgconf-pkg-config',
'policycoreutils-python-utils',
'python3',
'sendmail',
'sqlite',
'sqlite-devel',
'unzip',
'util-linux',
'valkey',
'whois',
];
run('dnf install -y ' . implode(' ', $packages), timeout: 900);

run('systemctl enable --now caddy');
run('systemctl enable --now valkey');
run('systemctl enable --now memcached');
run('systemctl enable --now fail2ban');
})
->verbose()
->oncePerNode();

desc('Configures the ssh');
task('provision:ssh', function () {
set('remote_user', get('provision_user'));
run("sed -i 's/PasswordAuthentication .*/PasswordAuthentication no/' /etc/ssh/sshd_config");
run('ssh-keygen -A');
run('systemctl enable sshd');
run('systemctl restart sshd');
if (test('[ ! -d /root/.ssh ]')) {
run('mkdir -p /root/.ssh');
run('touch /root/.ssh/authorized_keys');
}
})->oncePerNode();

desc('Setups a firewall');
task('provision:firewall', function () {
set('remote_user', get('provision_user'));
run('systemctl enable --now firewalld');
run('firewall-cmd --permanent --add-service=ssh');
run('firewall-cmd --permanent --add-service=http');
run('firewall-cmd --permanent --add-service=https');
run('firewall-cmd --reload');
})->oncePerNode();

desc('Verifies what provision was successful');
task('provision:verify', function () {
fetch('{{domain}}', 'get', [], null, $info, true);
if ($info['http_code'] === 404) {
info("provisioned successfully!");
}
});
51 changes: 51 additions & 0 deletions recipe/provision-fedora/404.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>404 Not Found</title>
<style>
body {
-moz-osx-font-smoothing: grayscale;
-webkit-font-smoothing: antialiased;
align-content: center;
background: #343434;
color: #fff;
display: grid;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
font-size: 20px;
justify-content: center;
margin: 0;
min-height: 100vh;
}

main {
padding: 0 30px;
}

svg {
animation: 2s ease-in-out infinite hover;
}

@keyframes hover {
0%, 100% {
transform: translateY(0)
}
50% {
transform: translateY(-8px)
}
}
</style>
</head>
<body>
<main>
<svg width="68" viewBox="0 0 48 40" xmlns="http://www.w3.org/2000/svg">
<path
d="M41.8253 0.269788C44.8519 -0.884712 47.9239 1.84927 47.1284 4.98944L39.3219 35.8043C38.615 38.5948 35.2805 39.7475 33.0012 37.9892L19.9291 27.905C17.8765 26.3215 17.8492 23.2338 19.8735 21.6144L35.3856 9.20469C36.2481 8.51467 37.5067 8.65451 38.1967 9.51704C38.8868 10.3796 38.7469 11.6381 37.8844 12.3282L22.8693 24.3402C22.6163 24.5427 22.6197 24.9286 22.8762 25.1266L34.8414 34.3568C35.1263 34.5766 35.5431 34.4325 35.6315 34.0837L43.0145 4.94009C43.1139 4.54757 42.7299 4.20583 42.3516 4.35014L5.41328 18.44C4.96316 18.6117 4.99234 19.2581 5.4561 19.3885L12.954 21.4973C14.4463 21.917 15.5617 23.1612 15.8166 24.6903L17.2908 33.5357C17.3714 34.0193 18.0276 34.11 18.2364 33.6664L18.8254 32.4148C19.2957 31.4154 20.4872 30.9865 21.4866 31.4568C22.486 31.9271 22.915 33.1186 22.4446 34.118L21.1735 36.8193C19.5032 40.3685 14.2535 39.6429 13.6086 35.7737L11.9232 25.6611C11.8913 25.4699 11.7519 25.3144 11.5654 25.2619L2.9172 22.8296C-0.792895 21.7862 -1.02636 16.6153 2.5746 15.2417L41.8253 0.269788Z"
fill="white"/>
</svg>
<h1>Not Found</h1>
<p>The requested URL was not found on this server.</p>
</main>
</body>
</html>
25 changes: 25 additions & 0 deletions recipe/provision-fedora/Caddyfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{{domain}} {
root * {{deploy_path}}/current/{{public_path}}
encode zstd gzip
file_server
php_fastcgi * unix//run/php-fpm/www.sock {
resolve_root_symlink
}

log {
output file {{deploy_path}}/log/access.log {
mode 0644
}
}

handle_errors {
@404 {
expression {http.error.status_code} == 404
}
rewrite @404 /404.html
encode zstd gzip
file_server {
root /var/deployer
}
}
}
72 changes: 72 additions & 0 deletions recipe/provision-fedora/databases.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
<?php

namespace Deployer;

set('db_type', function () {
$supportedDbTypes = [
'none',
'mariadb',
'postgresql',
];
return askChoice(' What DB to install? ', $supportedDbTypes, 0);
});

set('db_name', function () {
return ask(' DB name: ', 'prod');
});

set('db_user', function () {
return ask(' DB user: ', 'deployer');
});

set('db_password', function () {
return askHiddenResponse(' DB password: ');
});

// PGDG ships a much more complete/current set of PostgreSQL major versions
// than Fedora's own repositories, so provision:postgresql relies on it.
set('postgresql_version', function () {
return ask(' What PostgreSQL version to install? ', '17', ['13', '14', '15', '16', '17', '18']);
});

desc('Provision databases');
task('provision:databases', function () {
set('remote_user', get('provision_user'));

$dbType = get('db_type');
if ($dbType === 'none') {
return;
}
invoke('provision:' . $dbType);
})
->limit(1);

desc('Provision MariaDB');
task('provision:mariadb', function () {
run('dnf install -y mariadb-server', timeout: 900);
run('systemctl enable --now mariadb');
run("mysql --user=\"root\" -e \"CREATE USER IF NOT EXISTS '{{db_user}}'@'0.0.0.0' IDENTIFIED BY '%db_password%';\"", secrets: ['db_password' => get('db_password')]);
run("mysql --user=\"root\" -e \"CREATE USER IF NOT EXISTS '{{db_user}}'@'%' IDENTIFIED BY '%db_password%';\"", secrets: ['db_password' => get('db_password')]);
run("mysql --user=\"root\" -e \"GRANT ALL PRIVILEGES ON *.* TO '{{db_user}}'@'0.0.0.0' WITH GRANT OPTION;\"");
run("mysql --user=\"root\" -e \"GRANT ALL PRIVILEGES ON *.* TO '{{db_user}}'@'%' WITH GRANT OPTION;\"");
run("mysql --user=\"root\" -e \"FLUSH PRIVILEGES;\"");
run("mysql --user=\"root\" -e \"CREATE DATABASE IF NOT EXISTS {{db_name}} character set UTF8mb4 collate utf8mb4_bin;\"");
});

desc('Provision PostgreSQL');
task('provision:postgresql', function () {
$version = get('postgresql_version');

// Fedora's own repos only ship a single PostgreSQL version. PGDG gives us
// a properly maintained, versioned package instead.
run('dnf install -y https://download.postgresql.org/pub/repos/yum/reporpms/F-{{fedora_version}}-x86_64/pgdg-fedora-repo-latest.noarch.rpm');
run('dnf -y -q module disable postgresql', nothrow: true);

run("dnf install -y postgresql$version-server postgresql$version-contrib", timeout: 900);
run("/usr/pgsql-$version/bin/postgresql-$version-setup initdb", nothrow: true);
run("systemctl enable --now postgresql-$version");

run("sudo -u postgres /usr/pgsql-$version/bin/psql <<< $'CREATE DATABASE {{db_name}};'");
run("sudo -u postgres /usr/pgsql-$version/bin/psql <<< $'CREATE USER {{db_user}} WITH ENCRYPTED PASSWORD \'%db_password%\';'", secrets: ['db_password' => get('db_password')]);
run("sudo -u postgres /usr/pgsql-$version/bin/psql <<< $'GRANT ALL PRIVILEGES ON DATABASE {{db_name}} TO {{db_user}};'");
});
Loading