Skip to content

Instantly share code, notes, and snippets.

@VottusCode
Last active February 17, 2022 11:37
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save VottusCode/c947f4d2752f8dabae6db258adb78148 to your computer and use it in GitHub Desktop.
Save VottusCode/c947f4d2752f8dabae6db258adb78148 to your computer and use it in GitHub Desktop.
My serverfull deploy/setup scripts
/**
* Script that deploys the application to an FTP/SFTP server.
*/
const {params, credentials, deploy: deployConfig} = require("./utils")
const {exec} = require("better-exec");
const {Deploy} = require("@vottuscode/deploy-script");
const {SftpFileRemote} = require("@remotefull/file");
const {SshCmdRemote} = require("@remotefull/commander");
const deploy = new Deploy(
new SftpFileRemote(credentials),
new SshCmdRemote(credentials),
params.includes("only-cmds") ? {
commands: deployConfig.commands ?? []
} : deployConfig
);
const run = async () => {
if (!["only-cmds", "no-build"].some((p) => params.includes(p)))
await exec("yarn build", process.stdout);
await deploy.run();
}
run();
const posixPath = require("path/posix");
/**
* @param {string[]} params
*/
module.exports = (params) => {
const opts = {
setup: {
https: process.env.DEPLOY_HTTPS === "true",
hostname: process.env.DEPLOY_HOST ?? "example.com",
},
rootDir: __dirname,
remoteDir: "/var/www/example.com",
clearFolders: ["storage/temp"],
ensureFolders: ["storage/logs", "storage/temp", "storage/config"],
deletePaths: {
dirs: ["public/assets", "public/admin_assets", "src"],
files: ["public/index.php"],
},
upload: [
"src",
"public/assets",
"public/admin_assets",
"public/images",
"public/index.php",
"public/.htaccess",
"storage/config/local.neon",
"storage/config/services.neon",
"composer.json",
"composer.lock",
],
commands: [],
envFields: ["DATABASE_URL"],
onlyUpdateEnv: params.includes("env-only"),
skipPostCommands: params.includes("skip-post-commands"),
}
opts.commands = [
`/usr/bin/php /usr/local/bin/composer install -d ${opts.remoteDir}`,
...(opts.ensureFolders ?? []).map(
(f) => `chmod 777 ${posixPath.join(opts.remoteDir, f)}`
),
];
return opts;
};
/**
* Script that installs dependencies on a serverful remote.
*/
const {credentials, deploy, info, warn} = require("./utils");
// _apt -> https://gist.githubusercontent.com/VottusCode/45915f46bf2291c64a80cbd45203b44f/raw/355523e9bf44a485342afd446fa9c1f9fe3492be/apt-manager.js
const {AptManager} = require("./_apt");
const {SshCmdRemote} = require("@remotefull/commander");
const {SftpFileRemote} = require("@remotefull/file");
const cmd = new SshCmdRemote(credentials)
const file = new SftpFileRemote(credentials);
const apacheConfig = `
<VirtualHost *:80>
${deploy.setup.https ? `
RewriteEngine On
RewriteCond %{HTTPS} !=on
RewriteRule ^/?(.*) https://%{SERVER_NAME}/$1 [R,L]
</VirtualHost>
<VirtualHost *:443>
` : ""}
ServerName ${deploy.setup.hostname}
DocumentRoot "${deploy.remoteDir}/public"
AllowEncodedSlashes On
php_value upload_max_filesize 100M
php_value post_max_size 100M
<Directory "${deploy.remoteDir}/public">
Require all granted
AllowOverride all
</Directory>
${deploy.setup.https ? `
SSLEngine on
SSLCertificateFile /etc/letsencrypt/live/${deploy.setup.hostname}/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/${deploy.setup.hostname}/privkey.pem` : ""}
</VirtualHost>
`;
console.log(info("connecting..."))
cmd
.connect()
.then(file.connect.bind(file))
.then(async () => {
console.log(info("connected to remote."))
const apt = new AptManager(cmd, {})
for (const key of Reflect.ownKeys(Reflect.getPrototypeOf(apt)).filter(k => !["constructor", "executeAptCommand"].includes(k) && typeof apt[k] === "function")) {
const func = apt[key];
apt[key] = function (...args) {
console.log(info(`apt: ${key}`, args.length >= 1 ? "-" : "", ...args));
return func.bind(apt)(...args);
}
}
// Check whether the ondrej/php repository is present.
if (!(await apt.getRepositories()).some((r) => r.repository.includes("http://ppa.launchpad.net/ondrej/php"))) {
await apt.addRepository("ppa:ondrej/php", true);
}
await apt.update();
await apt.install([
"apache2 libapache2-mod-php8.1",
"php8.1 php8.1-mbstring php8.1-gd php8.1-curl",
"git"
].join(" "));
console.log(await cmd.execute("a2enmod rewrite ssl"));
console.log(await cmd.execute("phpenmod gd mbstring curl"));
if (!(await file.exists("/usr/local/bin/composer"))) {
console.log(info("installing composer..."))
console.log(await cmd.execute("curl -O https://getcomposer.org/installer"))
console.log(await cmd.execute("php installer --install-dir=/usr/local/bin --filename=composer"))
console.log(await cmd.execute("rm installer"))
}
const apacheConfigPath = `/etc/apache2/sites-available/${deploy.setup.hostname}.conf`;
if (await file.exists(apacheConfigPath)) {
console.log(warn(apacheConfigPath, "exists, skipping..."))
return;
}
console.log(info(`creating ${apacheConfigPath}...`))
await file.createFile(apacheConfigPath, apacheConfig);
console.log(await cmd.execute(`a2ensite ${deploy.setup.hostname}.conf`))
console.log(await cmd.execute("systemctl restart apache2"));
})
.then(cmd.disconnect.bind(cmd))
.then(file.disconnect.bind(file))
.then(() => console.log("done."))
const chalk = require("chalk")
const path = require("path")
// assuming the .env file is in a root door and the file is in a subdir
require("dotenv").config({ path: path.join(__dirname, "..", ".env") });
const args = process.argv.slice(2);
const params = args
.filter((arg) => arg.startsWith("--"))
.map((param) => param.substring("--".length, param.length));
const [
rawHost = process.env.FTP_HOST ?? "127.0.0.1",
username = process.env.FTP_USER ?? "root",
password = process.env.FTP_PASS ?? null
] = args.filter((arg) => !arg.startsWith("--"));
const [host, port = process.env.FTP_PORT ?? "22"] = rawHost.split(":");
const credentials = {host, port: Number(port), username, password};
const deploy = require("../deploy.config")(params)
const info = (...args) => chalk.blueBright(`(info)`, ...args)
const warn = (...args) => chalk.yellow(`(warn)`, ...args)
module.exports = {
args,
params,
credentials,
deploy,
info,
warn
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment