mirror of
https://github.com/easingthemes/ssh-deploy.git
synced 2024-11-13 22:38:04 +00:00
[engine] use NodeJS instead of Docker
This commit is contained in:
parent
3c2d851430
commit
81256f2671
19
.gitignore
vendored
Normal file
19
.gitignore
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
### Node template
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
|
||||
# Diagnostic reports (https://nodejs.org/api/report.html)
|
||||
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
|
||||
|
||||
# Dependency directories
|
||||
node_modules/
|
||||
|
||||
# Optional npm cache directory
|
||||
.npm
|
||||
|
||||
# dotenv environment variables file
|
||||
.env
|
||||
.env.test
|
||||
|
16
Dockerfile
16
Dockerfile
@ -1,16 +0,0 @@
|
||||
FROM debian:9.5-slim
|
||||
|
||||
# Label
|
||||
LABEL "com.github.actions.name"="ssh deploy"
|
||||
LABEL "com.github.actions.description"="For deploying code over ssh"
|
||||
LABEL "com.github.actions.icon"="truck"
|
||||
LABEL "com.github.actions.color"="green"
|
||||
|
||||
LABEL "repository"="http://github.com/easingthemes/ssh-deploy"
|
||||
LABEL "homepage"="https://github.com/easingthemes/ssh-deploy"
|
||||
LABEL "maintainer"="Dragan Filipovic <info@frontenddot.com>"
|
||||
|
||||
# Copy entrypoint
|
||||
ADD entrypoint.sh /entrypoint.sh
|
||||
RUN chmod +x /entrypoint.sh
|
||||
ENTRYPOINT ["/entrypoint.sh"]
|
27
action.yaml
Normal file
27
action.yaml
Normal file
@ -0,0 +1,27 @@
|
||||
name: 'ssh deploy'
|
||||
description: 'For deploying code over ssh'
|
||||
inputs:
|
||||
SSH_PRIVATE_KEY: # Private Key
|
||||
description: 'Private Key'
|
||||
required: true
|
||||
REMOTE_HOST:
|
||||
description: 'Remote host'
|
||||
required: true
|
||||
REMOTE_USER:
|
||||
description: 'Remote user'
|
||||
required: true
|
||||
SOURCE:
|
||||
description: 'Source directory'
|
||||
default: ''
|
||||
TARGET:
|
||||
description: 'Target directory'
|
||||
default: '/home/REMOTE_USER/'
|
||||
outputs:
|
||||
status:
|
||||
description: 'Status'
|
||||
runs:
|
||||
using: 'node10'
|
||||
main: 'dist/index.js'
|
||||
branding:
|
||||
color: 'green'
|
||||
icon: 'truck'
|
646
dist/index.js
vendored
Executable file
646
dist/index.js
vendored
Executable file
@ -0,0 +1,646 @@
|
||||
#!/usr/bin/env node
|
||||
module.exports =
|
||||
/******/ (function(modules, runtime) { // webpackBootstrap
|
||||
/******/ "use strict";
|
||||
/******/ // The module cache
|
||||
/******/ var installedModules = {};
|
||||
/******/
|
||||
/******/ // The require function
|
||||
/******/ function __webpack_require__(moduleId) {
|
||||
/******/
|
||||
/******/ // Check if module is in cache
|
||||
/******/ if(installedModules[moduleId]) {
|
||||
/******/ return installedModules[moduleId].exports;
|
||||
/******/ }
|
||||
/******/ // Create a new module (and put it into the cache)
|
||||
/******/ var module = installedModules[moduleId] = {
|
||||
/******/ i: moduleId,
|
||||
/******/ l: false,
|
||||
/******/ exports: {}
|
||||
/******/ };
|
||||
/******/
|
||||
/******/ // Execute the module function
|
||||
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
|
||||
/******/
|
||||
/******/ // Flag the module as loaded
|
||||
/******/ module.l = true;
|
||||
/******/
|
||||
/******/ // Return the exports of the module
|
||||
/******/ return module.exports;
|
||||
/******/ }
|
||||
/******/
|
||||
/******/
|
||||
/******/ __webpack_require__.ab = __dirname + "/";
|
||||
/******/
|
||||
/******/ // the startup function
|
||||
/******/ function startup() {
|
||||
/******/ // Load entry module and return exports
|
||||
/******/ return __webpack_require__(676);
|
||||
/******/ };
|
||||
/******/
|
||||
/******/ // run startup
|
||||
/******/ return startup();
|
||||
/******/ })
|
||||
/************************************************************************/
|
||||
/******/ ({
|
||||
|
||||
/***/ 129:
|
||||
/***/ (function(module) {
|
||||
|
||||
module.exports = require("child_process");
|
||||
|
||||
/***/ }),
|
||||
|
||||
/***/ 243:
|
||||
/***/ (function(module, __unusedexports, __webpack_require__) {
|
||||
|
||||
"use strict";
|
||||
|
||||
|
||||
var exec = __webpack_require__(129).exec;
|
||||
var execSync = __webpack_require__(129).execSync;
|
||||
var fs = __webpack_require__(747);
|
||||
var path = __webpack_require__(622);
|
||||
var access = fs.access;
|
||||
var accessSync = fs.accessSync;
|
||||
var constants = fs.constants || fs;
|
||||
|
||||
var isUsingWindows = process.platform == 'win32'
|
||||
|
||||
var fileNotExists = function(commandName, callback){
|
||||
access(commandName, constants.F_OK,
|
||||
function(err){
|
||||
callback(!err);
|
||||
});
|
||||
};
|
||||
|
||||
var fileNotExistsSync = function(commandName){
|
||||
try{
|
||||
accessSync(commandName, constants.F_OK);
|
||||
return false;
|
||||
}catch(e){
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
var localExecutable = function(commandName, callback){
|
||||
access(commandName, constants.F_OK | constants.X_OK,
|
||||
function(err){
|
||||
callback(null, !err);
|
||||
});
|
||||
};
|
||||
|
||||
var localExecutableSync = function(commandName){
|
||||
try{
|
||||
accessSync(commandName, constants.F_OK | constants.X_OK);
|
||||
return true;
|
||||
}catch(e){
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
var commandExistsUnix = function(commandName, cleanedCommandName, callback) {
|
||||
|
||||
fileNotExists(commandName, function(isFile){
|
||||
|
||||
if(!isFile){
|
||||
var child = exec('command -v ' + cleanedCommandName +
|
||||
' 2>/dev/null' +
|
||||
' && { echo >&1 ' + cleanedCommandName + '; exit 0; }',
|
||||
function (error, stdout, stderr) {
|
||||
callback(null, !!stdout);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
localExecutable(commandName, callback);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
var commandExistsWindows = function(commandName, cleanedCommandName, callback) {
|
||||
if (/[\x00-\x1f<>:"\|\?\*]/.test(commandName)) {
|
||||
callback(null, false);
|
||||
return;
|
||||
}
|
||||
var child = exec('where ' + cleanedCommandName,
|
||||
function (error) {
|
||||
if (error !== null){
|
||||
callback(null, false);
|
||||
} else {
|
||||
callback(null, true);
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
var commandExistsUnixSync = function(commandName, cleanedCommandName) {
|
||||
if(fileNotExistsSync(commandName)){
|
||||
try {
|
||||
var stdout = execSync('command -v ' + cleanedCommandName +
|
||||
' 2>/dev/null' +
|
||||
' && { echo >&1 ' + cleanedCommandName + '; exit 0; }'
|
||||
);
|
||||
return !!stdout;
|
||||
} catch (error) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return localExecutableSync(commandName);
|
||||
}
|
||||
|
||||
var commandExistsWindowsSync = function(commandName, cleanedCommandName, callback) {
|
||||
if (/[\x00-\x1f<>:"\|\?\*]/.test(commandName)) {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
var stdout = execSync('where ' + cleanedCommandName, {stdio: []});
|
||||
return !!stdout;
|
||||
} catch (error) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
var cleanInput = function(s) {
|
||||
if (/[^A-Za-z0-9_\/:=-]/.test(s)) {
|
||||
s = "'"+s.replace(/'/g,"'\\''")+"'";
|
||||
s = s.replace(/^(?:'')+/g, '') // unduplicate single-quote at the beginning
|
||||
.replace(/\\'''/g, "\\'" ); // remove non-escaped single-quote if there are enclosed between 2 escaped
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
if (isUsingWindows) {
|
||||
cleanInput = function(s) {
|
||||
var isPathName = /[\\]/.test(s);
|
||||
if (isPathName) {
|
||||
var dirname = '"' + path.dirname(s) + '"';
|
||||
var basename = '"' + path.basename(s) + '"';
|
||||
return dirname + ':' + basename;
|
||||
}
|
||||
return '"' + s + '"';
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = function commandExists(commandName, callback) {
|
||||
var cleanedCommandName = cleanInput(commandName);
|
||||
if (!callback && typeof Promise !== 'undefined') {
|
||||
return new Promise(function(resolve, reject){
|
||||
commandExists(commandName, function(error, output) {
|
||||
if (output) {
|
||||
resolve(commandName);
|
||||
} else {
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
if (isUsingWindows) {
|
||||
commandExistsWindows(commandName, cleanedCommandName, callback);
|
||||
} else {
|
||||
commandExistsUnix(commandName, cleanedCommandName, callback);
|
||||
}
|
||||
};
|
||||
|
||||
module.exports.sync = function(commandName) {
|
||||
var cleanedCommandName = cleanInput(commandName);
|
||||
if (isUsingWindows) {
|
||||
return commandExistsWindowsSync(commandName, cleanedCommandName);
|
||||
} else {
|
||||
return commandExistsUnixSync(commandName, cleanedCommandName);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/***/ }),
|
||||
|
||||
/***/ 250:
|
||||
/***/ (function(module, __unusedexports, __webpack_require__) {
|
||||
|
||||
"use strict";
|
||||
|
||||
|
||||
var spawn = __webpack_require__(129).spawn
|
||||
var util = __webpack_require__(669)
|
||||
|
||||
var escapeSpaces = function(path) {
|
||||
if (typeof path === 'string') {
|
||||
return path.replace(/\b\s/g, '\\ ')
|
||||
} else {
|
||||
return path
|
||||
}
|
||||
}
|
||||
|
||||
var escapeSpacesInOptions = function(options) {
|
||||
// Escape paths in the src, dest, include, exclude, and excludeFirst arguments
|
||||
;['src', 'dest', 'include', 'exclude', 'excludeFirst'].forEach(function(
|
||||
optionKey
|
||||
) {
|
||||
var option = options[optionKey]
|
||||
if (typeof option === 'string') {
|
||||
options[optionKey] = escapeSpaces(option)
|
||||
} else if (Array.isArray(option) === true) {
|
||||
options[optionKey] = option.map(escapeSpaces)
|
||||
}
|
||||
})
|
||||
|
||||
return options
|
||||
}
|
||||
|
||||
module.exports = function(options, callback) {
|
||||
options = options || {}
|
||||
options = util._extend({}, options)
|
||||
options = escapeSpacesInOptions(options)
|
||||
|
||||
var platform = options.platform || process.platform // Enable process.platform to be mocked in options for testing
|
||||
var isWin = platform === 'win32'
|
||||
|
||||
if (typeof options.src === 'undefined') {
|
||||
throw new Error("'src' directory is missing from options")
|
||||
}
|
||||
|
||||
if (typeof options.dest === 'undefined') {
|
||||
throw new Error("'dest' directory is missing from options")
|
||||
}
|
||||
|
||||
var dest = options.dest
|
||||
|
||||
if (typeof options.host !== 'undefined') {
|
||||
dest = options.host + ':' + options.dest
|
||||
}
|
||||
|
||||
if (!Array.isArray(options.src)) {
|
||||
options.src = [options.src]
|
||||
}
|
||||
|
||||
var args = [].concat(options.src)
|
||||
|
||||
args.push(dest)
|
||||
|
||||
// [rsync failed on windows, copying persmissions](https://github.com/jedrichards/rsyncwrapper/issues/28)
|
||||
// [set chmod flag by default on Windows](https://github.com/jedrichards/rsyncwrapper/pull/29)
|
||||
var chmodArg = (options.args || []).find(function(arg) {
|
||||
return arg.match(/--chmod=/)
|
||||
})
|
||||
if (isWin && !chmodArg) {
|
||||
args.push('--chmod=ugo=rwX')
|
||||
}
|
||||
|
||||
if (typeof options.host !== 'undefined' || options.ssh) {
|
||||
args.push('--rsh')
|
||||
var rshCmd = 'ssh'
|
||||
if (typeof options.port !== 'undefined') {
|
||||
rshCmd += ' -p ' + options.port
|
||||
}
|
||||
if (typeof options.privateKey !== 'undefined') {
|
||||
rshCmd += ' -i ' + options.privateKey
|
||||
}
|
||||
if (typeof options.sshCmdArgs !== 'undefined') {
|
||||
rshCmd += ' ' + options.sshCmdArgs.join(' ')
|
||||
}
|
||||
args.push(rshCmd)
|
||||
}
|
||||
|
||||
if (options.recursive === true) {
|
||||
args.push('--recursive')
|
||||
}
|
||||
|
||||
if (options.times === true) {
|
||||
args.push('--times')
|
||||
}
|
||||
|
||||
if (options.syncDest === true || options.deleteAll === true) {
|
||||
args.push('--delete')
|
||||
args.push('--delete-excluded')
|
||||
}
|
||||
|
||||
if (options.syncDestIgnoreExcl === true || options.delete === true) {
|
||||
args.push('--delete')
|
||||
}
|
||||
|
||||
if (options.dryRun === true) {
|
||||
args.push('--dry-run')
|
||||
args.push('--verbose')
|
||||
}
|
||||
|
||||
if (
|
||||
typeof options.excludeFirst !== 'undefined' &&
|
||||
util.isArray(options.excludeFirst)
|
||||
) {
|
||||
options.excludeFirst.forEach(function(value, index) {
|
||||
args.push('--exclude=' + value)
|
||||
})
|
||||
}
|
||||
|
||||
if (typeof options.include !== 'undefined' && util.isArray(options.include)) {
|
||||
options.include.forEach(function(value, index) {
|
||||
args.push('--include=' + value)
|
||||
})
|
||||
}
|
||||
|
||||
if (typeof options.exclude !== 'undefined' && util.isArray(options.exclude)) {
|
||||
options.exclude.forEach(function(value, index) {
|
||||
args.push('--exclude=' + value)
|
||||
})
|
||||
}
|
||||
|
||||
switch (options.compareMode) {
|
||||
case 'sizeOnly':
|
||||
args.push('--size-only')
|
||||
break
|
||||
case 'checksum':
|
||||
args.push('--checksum')
|
||||
break
|
||||
}
|
||||
|
||||
if (typeof options.args !== 'undefined' && util.isArray(options.args)) {
|
||||
args = [...new Set([...args, ...options.args])]
|
||||
}
|
||||
|
||||
args = [...new Set(args)]
|
||||
|
||||
var noop = function() {}
|
||||
var onStdout = options.onStdout || noop
|
||||
var onStderr = options.onStderr || noop
|
||||
|
||||
var cmd = 'rsync '
|
||||
args.forEach(function(arg) {
|
||||
if (arg.substr(0, 4) === 'ssh ') {
|
||||
arg = '"' + arg + '"'
|
||||
}
|
||||
cmd += arg + ' '
|
||||
})
|
||||
cmd = cmd.trim()
|
||||
|
||||
if (options.noExec) {
|
||||
callback(null, null, null, cmd)
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
var stdout = ''
|
||||
var stderr = ''
|
||||
// Launch cmd in a shell just like Node's child_process.exec() does:
|
||||
// see https://github.com/joyent/node/blob/937e2e351b2450cf1e9c4d8b3e1a4e2a2def58bb/lib/child_process.js#L589
|
||||
var child
|
||||
if (isWin) {
|
||||
child = spawn('cmd.exe', ['/s', '/c', '"' + cmd + '"'], {
|
||||
windowsVerbatimArguments: true,
|
||||
stdio: [process.stdin, 'pipe', 'pipe'],
|
||||
})
|
||||
} else {
|
||||
child = spawn('/bin/sh', ['-c', cmd])
|
||||
}
|
||||
|
||||
child.stdout.on('data', function(data) {
|
||||
onStdout(data)
|
||||
stdout += data
|
||||
})
|
||||
|
||||
child.stderr.on('data', function(data) {
|
||||
onStderr(data)
|
||||
stderr += data
|
||||
})
|
||||
|
||||
child.on('exit', function(code) {
|
||||
var err = null
|
||||
if (code !== 0) {
|
||||
err = new Error('rsync exited with code ' + code)
|
||||
err.code = code
|
||||
}
|
||||
callback(err, stdout, stderr, cmd)
|
||||
})
|
||||
} catch (err) {
|
||||
callback(err, null, null, cmd)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/***/ }),
|
||||
|
||||
/***/ 428:
|
||||
/***/ (function(module, __unusedexports, __webpack_require__) {
|
||||
|
||||
var exec = __webpack_require__(129).exec;
|
||||
|
||||
var commandline={
|
||||
get:getString,
|
||||
run:runCommand
|
||||
};
|
||||
|
||||
function runCommand(command){
|
||||
//return refrence to the child process
|
||||
return exec(
|
||||
command
|
||||
);
|
||||
}
|
||||
|
||||
function getString(command,callback){
|
||||
//return refrence to the child process
|
||||
return exec(
|
||||
command,
|
||||
(
|
||||
function(){
|
||||
return function(err,data,stderr){
|
||||
if(!callback)
|
||||
return;
|
||||
|
||||
callback(err, data, stderr);
|
||||
}
|
||||
}
|
||||
)(callback)
|
||||
);
|
||||
}
|
||||
|
||||
module.exports=commandline;
|
||||
|
||||
|
||||
/***/ }),
|
||||
|
||||
/***/ 622:
|
||||
/***/ (function(module) {
|
||||
|
||||
module.exports = require("path");
|
||||
|
||||
/***/ }),
|
||||
|
||||
/***/ 669:
|
||||
/***/ (function(module) {
|
||||
|
||||
module.exports = require("util");
|
||||
|
||||
/***/ }),
|
||||
|
||||
/***/ 676:
|
||||
/***/ (function(__unusedmodule, __unusedexports, __webpack_require__) {
|
||||
|
||||
const fs = __webpack_require__(747);
|
||||
const path = __webpack_require__(622);
|
||||
const commandExists = __webpack_require__(677);
|
||||
const nodeCmd = __webpack_require__(428);
|
||||
const nodeRsync = __webpack_require__(250);
|
||||
|
||||
const { REMOTE_HOST, REMOTE_USER, SSH_PRIVATE_KEY, DEPLOY_KEY_NAME, SOURCE, TARGET, ARGS, GITHUB_WORKSPACE, HOME } = process.env;
|
||||
console.log('GITHUB_WORKSPACE', GITHUB_WORKSPACE);
|
||||
|
||||
const sshDeploy = (() => {
|
||||
const rsync = ({ privateKey, src, dest, args }) => {
|
||||
console.log(`Starting Rsync Action: ${src} to ${dest}`);
|
||||
|
||||
try {
|
||||
// RSYNC COMMAND
|
||||
nodeRsync({ src, dest, args, privateKey, ssh: true, sshCmdArgs: ['-o StrictHostKeyChecking=no'], recursive: true }, (error, stdout, stderr, cmd) => {
|
||||
if (error) {
|
||||
console.error('⚠️ Rsync error', error.message);
|
||||
process.abort();
|
||||
} else {
|
||||
console.log("✅ Rsync finished.", stdout);
|
||||
}
|
||||
});
|
||||
} catch (err) {
|
||||
console.error(`⚠️ An error happened:(.`, err.message, err.stack);
|
||||
process.abort();
|
||||
}
|
||||
};
|
||||
|
||||
const init = ({
|
||||
src,
|
||||
dest,
|
||||
args,
|
||||
host = 'localhost',
|
||||
username,
|
||||
privateKeyContent
|
||||
}) => {
|
||||
validateRsync(() => {
|
||||
const privateKey = addSshKey(privateKeyContent, DEPLOY_KEY_NAME ||'deploy_key');
|
||||
|
||||
const remoteDest = username + '@' + host + ':' + dest;
|
||||
|
||||
rsync({ privateKey, src, dest: remoteDest, args });
|
||||
});
|
||||
};
|
||||
|
||||
const validateDir = (dir) => {
|
||||
if (!fs.existsSync(dir)){
|
||||
console.log(`Creating ${dir} dir in `, GITHUB_WORKSPACE);
|
||||
fs.mkdirSync(dir);
|
||||
} else {
|
||||
console.log(`${dir} dir exist`);
|
||||
}
|
||||
};
|
||||
|
||||
const validateFile = (filePath) => {
|
||||
if (!fs.existsSync(filePath)){
|
||||
console.log(`Creating ${filePath} file in `, GITHUB_WORKSPACE);
|
||||
try {
|
||||
fs.writeFileSync(filePath, '', {
|
||||
encoding: 'utf8',
|
||||
mode: 0o600
|
||||
});
|
||||
} catch (e) {
|
||||
console.error('⚠️ writeFileSync error', filePath, e.message);
|
||||
process.abort();
|
||||
}
|
||||
} else {
|
||||
console.log(`${filePath} file exist`);
|
||||
}
|
||||
};
|
||||
|
||||
const addSshKey = (key, name) => {
|
||||
const sshDir = path.join(HOME || __dirname, '.ssh');
|
||||
const filePath = path.join(sshDir, name);
|
||||
|
||||
validateDir(sshDir);
|
||||
validateFile(sshDir + '/known_hosts');
|
||||
|
||||
try {
|
||||
fs.writeFileSync(filePath, key, {
|
||||
encoding: 'utf8',
|
||||
mode: 0o600
|
||||
});
|
||||
} catch (e) {
|
||||
console.error('⚠️ writeFileSync error', filePath, e.message);
|
||||
process.abort();
|
||||
}
|
||||
|
||||
console.log('✅ Ssh key added to `.ssh` dir ', filePath);
|
||||
|
||||
return filePath;
|
||||
};
|
||||
|
||||
const validateRsync = (callback = () => {}) => {
|
||||
const rsyncCli = commandExists.sync('rsync');
|
||||
|
||||
if (!rsyncCli) {
|
||||
nodeCmd.get(
|
||||
'sudo apt-get --no-install-recommends install rsync',
|
||||
function(err, data, stderr){
|
||||
if (err) {
|
||||
console.log('⚠️ Rsync installation failed ', err.message);
|
||||
process.abort();
|
||||
} else {
|
||||
console.log('✅ Rsync installed. \n', data, stderr);
|
||||
callback();
|
||||
}
|
||||
}
|
||||
);
|
||||
} else {
|
||||
callback();
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
init
|
||||
}
|
||||
})();
|
||||
|
||||
const validateInputs = (inputs) => {
|
||||
const validInputs = inputs.filter(input => {
|
||||
if (!input) {
|
||||
console.error(`⚠️ ${input} is mandatory`);
|
||||
}
|
||||
|
||||
return input;
|
||||
});
|
||||
|
||||
if (validInputs.length !== inputs.length) {
|
||||
process.abort();
|
||||
}
|
||||
};
|
||||
|
||||
const run = () => {
|
||||
validateInputs([SSH_PRIVATE_KEY, REMOTE_HOST, REMOTE_USER]);
|
||||
|
||||
sshDeploy.init({
|
||||
src: GITHUB_WORKSPACE + '/' + SOURCE || '',
|
||||
dest: TARGET || '/home/' + REMOTE_USER + '/',
|
||||
args: [ARGS] || false,
|
||||
host: REMOTE_HOST,
|
||||
username: REMOTE_USER,
|
||||
privateKeyContent: SSH_PRIVATE_KEY,
|
||||
});
|
||||
};
|
||||
|
||||
run();
|
||||
|
||||
|
||||
|
||||
|
||||
/***/ }),
|
||||
|
||||
/***/ 677:
|
||||
/***/ (function(module, __unusedexports, __webpack_require__) {
|
||||
|
||||
module.exports = __webpack_require__(243);
|
||||
|
||||
|
||||
/***/ }),
|
||||
|
||||
/***/ 747:
|
||||
/***/ (function(module) {
|
||||
|
||||
module.exports = require("fs");
|
||||
|
||||
/***/ })
|
||||
|
||||
/******/ });
|
@ -1,13 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
set -eu
|
||||
|
||||
# Set deploy key
|
||||
SSH_PATH="$HOME/.ssh"
|
||||
mkdir "$SSH_PATH"
|
||||
echo "$DEPLOY_KEY" > "$SSH_PATH/deploy_key"
|
||||
chmod 600 "$SSH_PATH/deploy_key"
|
||||
|
||||
|
||||
# Do deployment
|
||||
sh -c "rsync $ARGS -e 'ssh -i $SSH_PATH/deploy_key -o StrictHostKeyChecking=no' $GITHUB_WORKSPACE/$SOURCE $TARGET"
|
29
package-lock.json
generated
Normal file
29
package-lock.json
generated
Normal file
@ -0,0 +1,29 @@
|
||||
{
|
||||
"name": "ssh-deploy",
|
||||
"version": "2.0.0",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
"@zeit/ncc": {
|
||||
"version": "0.20.5",
|
||||
"resolved": "https://registry.npmjs.org/@zeit/ncc/-/ncc-0.20.5.tgz",
|
||||
"integrity": "sha512-XU6uzwvv95DqxciQx+aOLhbyBx/13ky+RK1y88Age9Du3BlA4mMPCy13BGjayOrrumOzlq1XV3SD/BWiZENXlw==",
|
||||
"dev": true
|
||||
},
|
||||
"command-exists": {
|
||||
"version": "1.2.8",
|
||||
"resolved": "https://registry.npmjs.org/command-exists/-/command-exists-1.2.8.tgz",
|
||||
"integrity": "sha512-PM54PkseWbiiD/mMsbvW351/u+dafwTJ0ye2qB60G1aGQP9j3xK2gmMDc+R34L3nDtx4qMCitXT75mkbkGJDLw=="
|
||||
},
|
||||
"node-cmd": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/node-cmd/-/node-cmd-3.0.0.tgz",
|
||||
"integrity": "sha1-OP/3CkqqT2WdID61eGJzcBjiT28="
|
||||
},
|
||||
"rsyncwrapper": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/rsyncwrapper/-/rsyncwrapper-3.0.1.tgz",
|
||||
"integrity": "sha512-fkGmeEJRbKveT/6bBqTVzzHS1wtbGQwL6qnwT/+1AtMAsEV5dX1fSAiOJVZrDOnVsOr2lFl8ga1MZLoHekV3yg=="
|
||||
}
|
||||
}
|
||||
}
|
32
package.json
Normal file
32
package.json
Normal file
@ -0,0 +1,32 @@
|
||||
{
|
||||
"name": "ssh-deploy",
|
||||
"version": "2.0.0",
|
||||
"description": "This GitHub Action deploys specific directory from `GITHUB_WORKSPACE` to a folder on a server via rsync over ssh.",
|
||||
"main": "src/index.js",
|
||||
"dependencies": {
|
||||
"command-exists": "1.2.8",
|
||||
"node-cmd": "3.0.0",
|
||||
"rsyncwrapper": "3.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@zeit/ncc": "^0.20.5"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "ncc build ./src/index.js -o dist"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/easingthemes/ssh-deploy.git"
|
||||
},
|
||||
"keywords": [
|
||||
"deploy",
|
||||
"ssh",
|
||||
"rsync"
|
||||
],
|
||||
"author": "Dragan Filipovic",
|
||||
"license": "ISC",
|
||||
"bugs": {
|
||||
"url": "https://github.com/easingthemes/ssh-deploy/issues"
|
||||
},
|
||||
"homepage": "https://github.com/easingthemes/ssh-deploy#readme"
|
||||
}
|
151
src/index.js
Normal file
151
src/index.js
Normal file
@ -0,0 +1,151 @@
|
||||
#!/usr/bin/env node
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const commandExists = require('command-exists');
|
||||
const nodeCmd = require('node-cmd');
|
||||
const nodeRsync = require('rsyncwrapper');
|
||||
|
||||
const { REMOTE_HOST, REMOTE_USER, SSH_PRIVATE_KEY, DEPLOY_KEY_NAME, SOURCE, TARGET, ARGS, GITHUB_WORKSPACE, HOME } = process.env;
|
||||
console.log('GITHUB_WORKSPACE', GITHUB_WORKSPACE);
|
||||
|
||||
const sshDeploy = (() => {
|
||||
const rsync = ({ privateKey, src, dest, args }) => {
|
||||
console.log(`Starting Rsync Action: ${src} to ${dest}`);
|
||||
|
||||
try {
|
||||
// RSYNC COMMAND
|
||||
nodeRsync({ src, dest, args, privateKey, ssh: true, sshCmdArgs: ['-o StrictHostKeyChecking=no'], recursive: true }, (error, stdout, stderr, cmd) => {
|
||||
if (error) {
|
||||
console.error('⚠️ Rsync error', error.message);
|
||||
process.abort();
|
||||
} else {
|
||||
console.log("✅ Rsync finished.", stdout);
|
||||
}
|
||||
});
|
||||
} catch (err) {
|
||||
console.error(`⚠️ An error happened:(.`, err.message, err.stack);
|
||||
process.abort();
|
||||
}
|
||||
};
|
||||
|
||||
const init = ({
|
||||
src,
|
||||
dest,
|
||||
args,
|
||||
host = 'localhost',
|
||||
username,
|
||||
privateKeyContent
|
||||
}) => {
|
||||
validateRsync(() => {
|
||||
const privateKey = addSshKey(privateKeyContent, DEPLOY_KEY_NAME ||'deploy_key');
|
||||
|
||||
const remoteDest = username + '@' + host + ':' + dest;
|
||||
|
||||
rsync({ privateKey, src, dest: remoteDest, args });
|
||||
});
|
||||
};
|
||||
|
||||
const validateDir = (dir) => {
|
||||
if (!fs.existsSync(dir)){
|
||||
console.log(`Creating ${dir} dir in `, GITHUB_WORKSPACE);
|
||||
fs.mkdirSync(dir);
|
||||
} else {
|
||||
console.log(`${dir} dir exist`);
|
||||
}
|
||||
};
|
||||
|
||||
const validateFile = (filePath) => {
|
||||
if (!fs.existsSync(filePath)){
|
||||
console.log(`Creating ${filePath} file in `, GITHUB_WORKSPACE);
|
||||
try {
|
||||
fs.writeFileSync(filePath, '', {
|
||||
encoding: 'utf8',
|
||||
mode: 0o600
|
||||
});
|
||||
} catch (e) {
|
||||
console.error('⚠️ writeFileSync error', filePath, e.message);
|
||||
process.abort();
|
||||
}
|
||||
} else {
|
||||
console.log(`${filePath} file exist`);
|
||||
}
|
||||
};
|
||||
|
||||
const addSshKey = (key, name) => {
|
||||
const sshDir = path.join(HOME || __dirname, '.ssh');
|
||||
const filePath = path.join(sshDir, name);
|
||||
|
||||
validateDir(sshDir);
|
||||
validateFile(sshDir + '/known_hosts');
|
||||
|
||||
try {
|
||||
fs.writeFileSync(filePath, key, {
|
||||
encoding: 'utf8',
|
||||
mode: 0o600
|
||||
});
|
||||
} catch (e) {
|
||||
console.error('⚠️ writeFileSync error', filePath, e.message);
|
||||
process.abort();
|
||||
}
|
||||
|
||||
console.log('✅ Ssh key added to `.ssh` dir ', filePath);
|
||||
|
||||
return filePath;
|
||||
};
|
||||
|
||||
const validateRsync = (callback = () => {}) => {
|
||||
const rsyncCli = commandExists.sync('rsync');
|
||||
|
||||
if (!rsyncCli) {
|
||||
nodeCmd.get(
|
||||
'sudo apt-get --no-install-recommends install rsync',
|
||||
function(err, data, stderr){
|
||||
if (err) {
|
||||
console.log('⚠️ Rsync installation failed ', err.message);
|
||||
process.abort();
|
||||
} else {
|
||||
console.log('✅ Rsync installed. \n', data, stderr);
|
||||
callback();
|
||||
}
|
||||
}
|
||||
);
|
||||
} else {
|
||||
callback();
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
init
|
||||
}
|
||||
})();
|
||||
|
||||
const validateInputs = (inputs) => {
|
||||
const validInputs = inputs.filter(input => {
|
||||
if (!input) {
|
||||
console.error(`⚠️ ${input} is mandatory`);
|
||||
}
|
||||
|
||||
return input;
|
||||
});
|
||||
|
||||
if (validInputs.length !== inputs.length) {
|
||||
process.abort();
|
||||
}
|
||||
};
|
||||
|
||||
const run = () => {
|
||||
validateInputs([SSH_PRIVATE_KEY, REMOTE_HOST, REMOTE_USER]);
|
||||
|
||||
sshDeploy.init({
|
||||
src: GITHUB_WORKSPACE + '/' + SOURCE || '',
|
||||
dest: TARGET || '/home/' + REMOTE_USER + '/',
|
||||
args: [ARGS] || ['-rltgoDzvO'],
|
||||
host: REMOTE_HOST,
|
||||
username: REMOTE_USER,
|
||||
privateKeyContent: SSH_PRIVATE_KEY,
|
||||
});
|
||||
};
|
||||
|
||||
run();
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user