195 lines
5.6 KiB
JavaScript
195 lines
5.6 KiB
JavaScript
const keygen = require('ssh-keygen-lite');
|
|
const fs = require('fs')
|
|
const simpleGit = require("simple-git")
|
|
const path = require("path")
|
|
|
|
/**
|
|
*
|
|
* @param {String} a
|
|
* @returns
|
|
*/
|
|
function resolvePath(a) {
|
|
a = a.replace("\\",path.sep)
|
|
return path.resolve(a)
|
|
}
|
|
|
|
require("dotenv").config()
|
|
|
|
const GIT_URI = process.env.GIT_URI
|
|
const GIT_BRANCH = process.env.GIT_BRANCH || "main"
|
|
|
|
const shouldGenerateKeys = process.env.GENERATE_KEYS || GIT_URI.startsWith("ssh://") || false
|
|
|
|
const PROJECT_DIR = resolvePath(__dirname + "\\project")
|
|
|
|
const KEY_DIR = resolvePath(__dirname + "\\keys")
|
|
|
|
const KEY_LOCATION = resolvePath(KEY_DIR + "\\deploy.pem")
|
|
const KEY_LOCATION_PUBLIC = resolvePath(KEY_LOCATION+".pub")
|
|
const KNOWN_HOSTS_FILE = resolvePath(__dirname + "\\known_hosts")
|
|
|
|
const KEY_FORMAT = "PEM"
|
|
const KEY_COMMENT = "nodejs-deploy"
|
|
|
|
const GIT_SSH_COMMAND = `ssh -o UserKnownHostsFile="${KNOWN_HOSTS_FILE}" -o StrictHostKeyChecking=no -i "${KEY_LOCATION}"`
|
|
//console.log(GIT_SSH_COMMAND)
|
|
|
|
async function readKeys() {
|
|
const FILE_ENC = "UTF-8"
|
|
const privateKey = fs.readFileSync(KEY_LOCATION,FILE_ENC)
|
|
const publicKey = fs.readFileSync(KEY_LOCATION_PUBLIC,FILE_ENC)
|
|
|
|
const keys = {
|
|
private: privateKey,
|
|
public: publicKey,
|
|
}
|
|
return keys;
|
|
}
|
|
|
|
async function tryGenerateKeys() {
|
|
if (!fs.existsSync(KEY_DIR)) fs.mkdirSync(KEY_DIR)
|
|
const exists = fs.existsSync(KEY_LOCATION);
|
|
if (exists) {
|
|
console.log("no need to generate key, already exists")
|
|
return;
|
|
}
|
|
if (!shouldGenerateKeys) {
|
|
console.log("shouldGenerateKeys is false, skipping key generation")
|
|
return;
|
|
}
|
|
console.log("Generating keys...")
|
|
const aPromise = new Promise(function(resolve, reject) {
|
|
keygen({
|
|
location: KEY_LOCATION,
|
|
type: 'rsa',
|
|
read: true,
|
|
force: false,
|
|
destroy: false,
|
|
comment: KEY_COMMENT,
|
|
//password: 'keypassword',
|
|
size: '4096',
|
|
format: KEY_FORMAT,
|
|
},
|
|
// If you omit this callback function, a Promise will be returned instead!
|
|
function onDoneCallback(err, out) {
|
|
// The error could be related to ssh-keygen binary or file system errors.
|
|
if (err) {
|
|
console.error(err)
|
|
reject(err);
|
|
return;
|
|
}
|
|
resolve(true);
|
|
},
|
|
);
|
|
})
|
|
return await aPromise;
|
|
}
|
|
|
|
async function gitMain() {
|
|
|
|
/**
|
|
* @type {import('simple-git').SimpleGit}
|
|
*/
|
|
const cloneGit = simpleGit(__dirname)
|
|
cloneGit.env('GIT_SSH_COMMAND', GIT_SSH_COMMAND)
|
|
|
|
if (!fs.existsSync(PROJECT_DIR)) {
|
|
await cloneGit.clone(GIT_URI, PROJECT_DIR)
|
|
}
|
|
/**
|
|
* @type {import('simple-git').SimpleGit}
|
|
*/
|
|
const git = simpleGit(PROJECT_DIR)
|
|
git.env('GIT_SSH_COMMAND', GIT_SSH_COMMAND)
|
|
const fetchResult = await git.fetch()
|
|
const localBranches = await git.branchLocal()
|
|
|
|
const statusResult = await git.status()
|
|
//console.log(statusResult)
|
|
if (statusResult.current != GIT_BRANCH && !localBranches.all.includes(GIT_BRANCH)) {
|
|
await git.checkoutBranch(GIT_BRANCH, `origin/${GIT_BRANCH}`)
|
|
} else {
|
|
await git.checkout(GIT_BRANCH)
|
|
}
|
|
const behind = statusResult.behind
|
|
if (behind > 0 ) {
|
|
console.log(`${behind} change behind, pulling...`)
|
|
await git.pull()
|
|
}
|
|
console.log(`Local version is up-to date with branch "${GIT_BRANCH}"`)
|
|
if (statusResult.files.length > 0) {
|
|
console.log("Locally changed Files: ",statusResult.files)
|
|
}
|
|
}
|
|
|
|
const { execSync, spawn, ChildProcess} = require('child_process')
|
|
/**
|
|
* @type {Array<ChildProcess>}
|
|
*/
|
|
|
|
const processes = []
|
|
|
|
async function ProjectCmd(cmd) {
|
|
//console.log(`executing "${cmd}"`)
|
|
const processPromise = new Promise(function(resolve, reject) {
|
|
const process = spawn(cmd, { stdio: 'inherit', cwd: PROJECT_DIR, shell:true });
|
|
processes.push(process)
|
|
|
|
process.on('close', (code) => {
|
|
//console.log(`command "${cmd}" exited with code ${code}`);
|
|
processes.splice(processes.indexOf(process),1)
|
|
resolve();
|
|
});
|
|
})
|
|
return await processPromise;
|
|
}
|
|
|
|
async function killProcesses() {
|
|
for (let index = 0; index < processes.length; index++) {
|
|
const process = processes[index];
|
|
console.log("killing process",process.pid)
|
|
await new Promise(function(resolve, reject) {
|
|
process.on('close', (code) => {
|
|
console.log(`process exited with code ${code}`);
|
|
resolve();
|
|
});
|
|
process.kill("SIGINT")
|
|
})
|
|
}
|
|
}
|
|
|
|
async function RunProject(commands = ["npm i","npm start"]) {
|
|
for (let index = 0; index < commands.length; index++) {
|
|
await ProjectCmd(commands[index])
|
|
}
|
|
}
|
|
|
|
async function main() {
|
|
const didGenerate = await tryGenerateKeys()
|
|
if (didGenerate) {
|
|
console.log("Keys have been placed in the keys directory.")
|
|
return
|
|
}
|
|
await gitMain()
|
|
|
|
let customCommands = process.env.COMMANDS
|
|
//console.log(customCommands)
|
|
if (customCommands) {
|
|
try {
|
|
customCommands = JSON.parse(customCommands)
|
|
} catch (err) {
|
|
console.error("couln't parse commands from .env")
|
|
console.log(err.message)
|
|
customCommands = undefined;
|
|
}
|
|
}
|
|
|
|
console.log("custom start commands",customCommands)
|
|
|
|
if (processes.length == 0) {
|
|
RunProject(customCommands)
|
|
}
|
|
}
|
|
|
|
main()
|
|
setInterval(main, 1000 * 60) // 5 minutes
|