Skip to content

Commit

Permalink
Refactor provision recipe
Browse files Browse the repository at this point in the history
Introduce a few helper function to simplify recipes.
Introduce new node configuration recipe.
  • Loading branch information
antonmedv committed Oct 19, 2024
1 parent 68b9aa4 commit 4161ac1
Show file tree
Hide file tree
Showing 8 changed files with 237 additions and 140 deletions.
123 changes: 4 additions & 119 deletions recipe/provision.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
require __DIR__ . '/provision/databases.php';
require __DIR__ . '/provision/nodejs.php';
require __DIR__ . '/provision/php.php';
require __DIR__ . '/provision/user.php';
require __DIR__ . '/provision/website.php';

use Deployer\Task\Context;
Expand All @@ -27,12 +28,12 @@
'provision:install',
'provision:ssh',
'provision:firewall',
'provision:deployer',
'provision:server',
'provision:user',
'provision:php',
'provision:node',
'provision:databases',
'provision:composer',
'provision:npm',
'provision:server',
'provision:website',
'provision:verify',
]);
Expand Down Expand Up @@ -169,67 +170,6 @@
->verbose()
->oncePerNode();

desc('Configures a server');
task('provision:server', function () {
run('usermod -a -G www-data caddy');
$html = <<<'HTML'
<!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="48" height="38" viewBox="0 0 243 193">
<title>Deployer</title>
<g fill="none" fill-rule="evenodd">
<path fill="#0CF" d="M242.781.39L.207 101.653l83.606 21.79z"/>
<path fill="#00B3E0" d="M97.555 186.363l14.129-50.543L242.78.39 83.812 123.442l13.743 62.922"/>
<path fill="#0084A6" d="M97.555 186.363l33.773-39.113-19.644-11.43-14.13 50.543"/>
<path fill="#0CF" d="M131.328 147.25l78.484 45.664L242.782.391 111.683 135.82l19.644 11.429"/>
</g>
</svg>
<h1>Not Found</h1>
<p>The requested URL was not found on this server.</p>
</main>
</body>
</html>
HTML;
run("mkdir -p /var/dep/html");
run("echo $'$html' > /var/dep/html/404.html");
})->oncePerNode();

desc('Configures the ssh');
task('provision:ssh', function () {
run("sed -i 's/PasswordAuthentication .*/PasswordAuthentication no/' /etc/ssh/sshd_config");
Expand All @@ -241,61 +181,6 @@
}
})->oncePerNode();

set('sudo_password', function () {
return askHiddenResponse(' Password for sudo: ');
});

// Specify which key to copy to server.
// Set to `false` to disable copy of key.
set('ssh_copy_id', '~/.ssh/id_rsa.pub');

desc('Setups a deployer user');
task('provision:deployer', function () {
if (test('id deployer >/dev/null 2>&1')) {
// TODO: Check what created deployer user configured correctly.
// TODO: Update sudo_password of deployer user.
// TODO: Copy ssh_copy_id to deployer ssh dir.
info('deployer user already exist');
} else {
run('useradd deployer');
run('mkdir -p /home/deployer/.ssh');
run('mkdir -p /home/deployer/.deployer');
run('adduser deployer sudo');

run('chsh -s /bin/bash deployer');
run('cp /root/.profile /home/deployer/.profile');
run('cp /root/.bashrc /home/deployer/.bashrc');

// Make color prompt.
run("sed -i 's/#force_color_prompt=yes/force_color_prompt=yes/' /home/deployer/.bashrc");

$password = run("mkpasswd -m sha-512 '%secret%'", ['secret' => get('sudo_password')]);
run("usermod --password '%secret%' deployer", ['secret' => $password]);

if (!empty(get('ssh_copy_id'))) {
$file = parse_home_dir(get('ssh_copy_id'));
if (!file_exists($file)) {
info('Configure path to your public key.');
writeln("");
writeln(" set(<info>'ssh_copy_id'</info>, <info>'~/.ssh/id_rsa.pub'</info>);");
writeln("");
$file = ask(' Specify path to your public ssh key: ', '~/.ssh/id_rsa.pub');
}
run('echo "$KEY" >> /root/.ssh/authorized_keys', ['env' => ['KEY' => file_get_contents(parse_home_dir($file))]]);
}
run('cp /root/.ssh/authorized_keys /home/deployer/.ssh/authorized_keys');
run('ssh-keygen -f /home/deployer/.ssh/id_rsa -t rsa -N ""');

run('chown -R deployer:deployer /home/deployer');
run('chmod -R 755 /home/deployer');
run('chmod 700 /home/deployer/.ssh/id_rsa');

run('usermod -a -G www-data deployer');
run('usermod -a -G caddy deployer');
run('groups deployer');
}
})->oncePerNode();

desc('Setups a firewall');
task('provision:firewall', function () {
run('ufw allow 22');
Expand Down
32 changes: 27 additions & 5 deletions recipe/provision/nodejs.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,34 @@

namespace Deployer;

// Node.js version from https://github.com/nodesource/distributions.
set('nodejs_version', 'node_23.x');
use function Deployer\Support\starts_with;

set('node_version', '23.x');

desc('Installs npm packages');
task('provision:npm', function () {
run('npm install -g fx zx pm2');
run('pm2 startup');
task('provision:node', function () {
if (has('nodejs_version')) {
throw new \RuntimeException('nodejs_version is deprecated, use node_version_version instead.');
}
$arch = run('uname -m');

if ($arch === 'arm' || starts_with($arch, 'armv7')) {
$filename = 'fnm-arm32';
} elseif (starts_with($arch, 'aarch') || starts_with($arch, 'armv8')) {
$filename = 'fnm-arm64';
} else {
$filename = 'fnm-linux';
}

$url = "https://github.com/Schniz/fnm/releases/latest/download/$filename.zip";
run("curl -sSL $url --output /tmp/$filename.zip");

run("unzip /tmp/$filename.zip -d /tmp");

run("mv /tmp/fnm /usr/local/bin/fnm");
run('chmod +x /usr/local/bin/fnm');

run('fnm install {{node_version}}');
appendToFile('~/.bashrc', 'eval "`fnm env`"');
})
->oncePerNode();
10 changes: 5 additions & 5 deletions recipe/provision/php.php
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,11 @@
run('apt-get install -y ' . implode(' ', $packages), ['env' => ['DEBIAN_FRONTEND' => 'noninteractive']]);

// Configure PHP-CLI
run("sudo sed -i 's/error_reporting = .*/error_reporting = E_ALL/' /etc/php/$version/cli/php.ini");
run("sudo sed -i 's/display_errors = .*/display_errors = On/' /etc/php/$version/cli/php.ini");
run("sudo sed -i 's/memory_limit = .*/memory_limit = 512M/' /etc/php/$version/cli/php.ini");
run("sudo sed -i 's/upload_max_filesize = .*/upload_max_filesize = 128M/' /etc/php/$version/cli/php.ini");
run("sudo sed -i 's/;date.timezone.*/date.timezone = UTC/' /etc/php/$version/cli/php.ini");
run("sed -i 's/error_reporting = .*/error_reporting = E_ALL/' /etc/php/$version/cli/php.ini");
run("sed -i 's/display_errors = .*/display_errors = On/' /etc/php/$version/cli/php.ini");
run("sed -i 's/memory_limit = .*/memory_limit = 512M/' /etc/php/$version/cli/php.ini");
run("sed -i 's/upload_max_filesize = .*/upload_max_filesize = 128M/' /etc/php/$version/cli/php.ini");
run("sed -i 's/;date.timezone.*/date.timezone = UTC/' /etc/php/$version/cli/php.ini");

// Configure PHP-FPM
run("sed -i 's/error_reporting = .*/error_reporting = E_ALL/' /etc/php/$version/fpm/php.ini");
Expand Down
85 changes: 85 additions & 0 deletions recipe/provision/user.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
<?php

namespace Deployer;


use function Deployer\Support\parse_home_dir;

set('sudo_password', function () {
return askHiddenResponse(' Password for sudo: ');
});


desc('Setups a deployer user');
task('provision:user', function () {
if (test('id deployer >/dev/null 2>&1')) {
// TODO: Check what created deployer user configured correctly.
// TODO: Update sudo_password of deployer user.
// TODO: Copy ssh_copy_id to deployer ssh dir.
info('deployer user already exist');
} else {
run('useradd deployer');
run('mkdir -p /home/deployer/.ssh');
run('mkdir -p /home/deployer/.deployer');
run('adduser deployer sudo');

run('chsh -s /bin/bash deployer');
run('cp /root/.profile /home/deployer/.profile');
run('cp /root/.bashrc /home/deployer/.bashrc');

// Make color prompt.
run("sed -i 's/#force_color_prompt=yes/force_color_prompt=yes/' /home/deployer/.bashrc");

$password = run("mkpasswd -m sha-512 '%secret%'", ['secret' => get('sudo_password')]);
run("usermod --password '%secret%' deployer", ['secret' => $password]);

// Copy root public key to deployer user so user can login without password.
run('cp /root/.ssh/authorized_keys /home/deployer/.ssh/authorized_keys');

// Create ssh key if not already exists.
run('ssh-keygen -f /home/deployer/.ssh/id_ed25519 -t ed25519 -N ""');

run('chown -R deployer:deployer /home/deployer');
run('chmod -R 755 /home/deployer');
run('chmod 700 /home/deployer/.ssh/id_ed25519');

run('usermod -a -G www-data deployer');
run('usermod -a -G caddy deployer');
run('groups deployer');
}
})->oncePerNode();


desc('Copy public key to remote server');
task('provision:ssh_copy_id', function () {
$defaultKeys = [
'~/.ssh/id_rsa.pub',
'~/.ssh/id_ed25519.pub',
'~/.ssh/id_ecdsa.pub',
'~/.ssh/id_dsa.pub',
];

$publicKeyContent = false;
foreach ($defaultKeys as $key) {
$file = parse_home_dir($key);
if (file_exists($file)) {
$publicKeyContent = file_get_contents($file);
break;
}
}

if (!$publicKeyContent) {
$publicKeyContent = ask(' Public key: ', false);

Check failure on line 72 in recipe/provision/user.php

View workflow job for this annotation

GitHub Actions / phpstan

Parameter #2 $default of function Deployer\ask expects string|null, false given.
}

if (!$publicKeyContent) {
info('Skipping public key copy as no public key was found or provided.');
return;
}

run('echo "$PUBLIC_KEY" >> /home/deployer/.ssh/authorized_keys', [
'env' => [
'PUBLIC_KEY' => $publicKeyContent,
],
]);
});
71 changes: 68 additions & 3 deletions recipe/provision/website.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,71 @@
return ask(' Public path: ', 'public');
});

desc('Configures a server');
task('provision:server', function () {
run('usermod -a -G www-data caddy');
$html = <<<'HTML'
<!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="48" height="38" viewBox="0 0 243 193">
<title>Deployer</title>
<g fill="none" fill-rule="evenodd">
<path fill="#0CF" d="M242.781.39L.207 101.653l83.606 21.79z"/>
<path fill="#00B3E0" d="M97.555 186.363l14.129-50.543L242.78.39 83.812 123.442l13.743 62.922"/>
<path fill="#0084A6" d="M97.555 186.363l33.773-39.113-19.644-11.43-14.13 50.543"/>
<path fill="#0CF" d="M131.328 147.25l78.484 45.664L242.782.391 111.683 135.82l19.644 11.429"/>
</g>
</svg>
<h1>Not Found</h1>
<p>The requested URL was not found on this server.</p>
</main>
</body>
</html>
HTML;
run("mkdir -p /var/deployer");
run("echo $'$html' > /var/deployer/404.html");
})->oncePerNode();

desc('Provision website');
task('provision:website', function () {
$restoreBecome = become('deployer');

run("[ -d {{deploy_path}} ] || mkdir -p {{deploy_path}}");
run("chown -R deployer:deployer {{deploy_path}}");

Expand Down Expand Up @@ -44,6 +107,8 @@
run("echo $'$caddyfile' > Caddyfile");
}

$restoreBecome();

if (!test("grep -q 'import {{deploy_path}}/Caddyfile' /etc/caddy/Caddyfile")) {
run("echo 'import {{deploy_path}}/Caddyfile' >> /etc/caddy/Caddyfile");
}
Expand All @@ -52,12 +117,12 @@
info("Website {{domain}} configured!");
})->limit(1);

desc('Shows caddy logs');
task('logs:caddy', function () {
desc('Shows access logs');
task('logs:access', function () {
run('tail -f {{deploy_path}}/log/access.log');
})->verbose();

desc('Shows caddy syslog');
task('logs:caddy:syslog', function () {
task('logs:caddy', function () {
run('sudo journalctl -u caddy -f');
})->verbose();
Loading

0 comments on commit 4161ac1

Please sign in to comment.