mirror of
				https://github.com/easingthemes/ssh-deploy.git
				synced 2025-10-30 23:30:09 +08:00 
			
		
		
		
	Add option to execute script before and after rsync
This commit is contained in:
		
							parent
							
								
									0e7d8019db
								
							
						
					
					
						commit
						5dfe8ebb5a
					
				
							
								
								
									
										12
									
								
								.eslintrc.js
									
									
									
									
									
								
							
							
						
						
									
										12
									
								
								.eslintrc.js
									
									
									
									
									
								
							| @ -12,14 +12,14 @@ module.exports = { | ||||
|     SharedArrayBuffer: 'readonly' | ||||
|   }, | ||||
|   parserOptions: { | ||||
|     ecmaVersion: 2018, | ||||
|     ecmaVersion: 2018 | ||||
|   }, | ||||
|   rules: { | ||||
|     "comma-dangle": [ | ||||
|       "error", | ||||
|       "never" | ||||
|     'comma-dangle': [ | ||||
|       'error', | ||||
|       'never' | ||||
|     ], | ||||
|     "no-console": "off", | ||||
|     "object-curly-newline": "off" | ||||
|     'no-console': 'off', | ||||
|     'object-curly-newline': 'off' | ||||
|   } | ||||
| }; | ||||
|  | ||||
							
								
								
									
										9
									
								
								.github/workflows/e2e.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										9
									
								
								.github/workflows/e2e.yml
									
									
									
									
										vendored
									
									
								
							| @ -2,7 +2,7 @@ name: e2e Test | ||||
| 
 | ||||
| on: | ||||
|   push: | ||||
|     branches: [ 'main' ] | ||||
|     branches: [ 'feature/ssh-cmd' ] | ||||
| 
 | ||||
| env: | ||||
|   TEST_HOST_DOCKER: ./test | ||||
| @ -55,7 +55,7 @@ jobs: | ||||
|           cat index.html | ||||
| 
 | ||||
|       - name: e2e Test published ssh-deploy action | ||||
|         uses: easingthemes/ssh-deploy@v3.1.0 | ||||
|         uses: easingthemes/ssh-deploy@feature/ssh-cmd | ||||
|         with: | ||||
|           # SSH_PRIVATE_KEY: $EXAMPLE_SSH_PRIVATE_KEY | ||||
|           # REMOTE_HOST: $EXAMPLE_REMOTE_HOST1 | ||||
| @ -64,6 +64,9 @@ jobs: | ||||
|           SOURCE: "test_project/" | ||||
|           TARGET: "/var/www/html/" | ||||
|           EXCLUDE: "/dist/, /node_modules/" | ||||
|           SCRIPT: | | ||||
|           SCRIPT_BEFORE: | | ||||
|             whoami | ||||
|             ls -al | ||||
|           SCRIPT_AFTER: | | ||||
|             whoami | ||||
|             ls -al | ||||
|  | ||||
| @ -107,13 +107,13 @@ jobs: | ||||
| 
 | ||||
| ## Issues | ||||
| 
 | ||||
| This is a Github Action wrapping `rsync` via `ssh`. Only issues with action functionality can be fixed here. | ||||
| This is a GitHub Action wrapping `rsync` via `ssh`. Only issues with action functionality can be fixed here. | ||||
| 
 | ||||
| Almost 95% of the issues are related to wrong SSH connection or `rsync` params and permissions. | ||||
| This issues are not related to the action itself. | ||||
| These issues are not related to the action itself. | ||||
| 
 | ||||
| - Check manually your ssh connection from your client before opening a bug report. | ||||
| - Check `rsync` params for your usecase. Default params are not going to be enough wor everyone, it highly depends on your setup. | ||||
| - Check `rsync` params for your use-case. Default params are not going to be enough wor everyone, it highly depends on your setup. | ||||
| - Check manually your rsync command from your client before opening a bug report. | ||||
| 
 | ||||
| I've added e2e test for this action. | ||||
|  | ||||
							
								
								
									
										10
									
								
								action.yml
									
									
									
									
									
								
							
							
						
						
									
										10
									
								
								action.yml
									
									
									
									
									
								
							| @ -31,10 +31,14 @@ inputs: | ||||
|     description: "An array of folder to exclude" | ||||
|     required: false | ||||
|     default: "" | ||||
|   SCRIPT: | ||||
|     description: "Script to run on host machine" | ||||
|   SCRIPT_BEFORE: | ||||
|     description: "Script to run on host machine before rsync" | ||||
|     required: false | ||||
|     default: "ls" | ||||
|     default: "echo 'Before rsync'" | ||||
|   SCRIPT_AFTER: | ||||
|     description: "Script to run on host machine after rsync" | ||||
|     required: false | ||||
|     default: "echo 'After rsync'" | ||||
| outputs: | ||||
|   status: | ||||
|     description: "Status" | ||||
|  | ||||
							
								
								
									
										2
									
								
								dist/index.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								dist/index.js
									
									
									
									
										vendored
									
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										68
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										68
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							| @ -1,16 +1,16 @@ | ||||
| { | ||||
|   "name": "@draganfilipovic/ssh-deploy", | ||||
|   "version": "3.0.1", | ||||
|   "version": "3.1.0", | ||||
|   "lockfileVersion": 2, | ||||
|   "requires": true, | ||||
|   "packages": { | ||||
|     "": { | ||||
|       "name": "@draganfilipovic/ssh-deploy", | ||||
|       "version": "3.0.1", | ||||
|       "version": "3.1.0", | ||||
|       "license": "MIT", | ||||
|       "dependencies": { | ||||
|         "command-exists": "^1.2.9", | ||||
|         "rsyncwrapper": "^3.0.1" | ||||
|         "rsyncwrapper": "^3.0.1", | ||||
|         "which": "^3.0.0" | ||||
|       }, | ||||
|       "devDependencies": { | ||||
|         "@vercel/ncc": "^0.36.0", | ||||
| @ -301,11 +301,6 @@ | ||||
|       "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", | ||||
|       "dev": true | ||||
|     }, | ||||
|     "node_modules/command-exists": { | ||||
|       "version": "1.2.9", | ||||
|       "resolved": "https://registry.npmjs.org/command-exists/-/command-exists-1.2.9.tgz", | ||||
|       "integrity": "sha512-LTQ/SGc+s0Xc0Fu5WaKnR0YiygZkm9eKFvyS+fRsU7/ZWFF8ykFM6Pc9aCVf1+xasOOZpO3BAVgVrKvsqKHV7w==" | ||||
|     }, | ||||
|     "node_modules/concat-map": { | ||||
|       "version": "0.0.1", | ||||
|       "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", | ||||
| @ -332,6 +327,21 @@ | ||||
|         "node": ">= 8" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/cross-spawn/node_modules/which": { | ||||
|       "version": "2.0.2", | ||||
|       "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", | ||||
|       "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", | ||||
|       "dev": true, | ||||
|       "dependencies": { | ||||
|         "isexe": "^2.0.0" | ||||
|       }, | ||||
|       "bin": { | ||||
|         "node-which": "bin/node-which" | ||||
|       }, | ||||
|       "engines": { | ||||
|         "node": ">= 8" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/debug": { | ||||
|       "version": "4.3.4", | ||||
|       "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", | ||||
| @ -1280,8 +1290,7 @@ | ||||
|     "node_modules/isexe": { | ||||
|       "version": "2.0.0", | ||||
|       "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", | ||||
|       "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", | ||||
|       "dev": true | ||||
|       "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" | ||||
|     }, | ||||
|     "node_modules/js-sdsl": { | ||||
|       "version": "4.2.0", | ||||
| @ -1923,18 +1932,17 @@ | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/which": { | ||||
|       "version": "2.0.2", | ||||
|       "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", | ||||
|       "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", | ||||
|       "dev": true, | ||||
|       "version": "3.0.0", | ||||
|       "resolved": "https://registry.npmjs.org/which/-/which-3.0.0.tgz", | ||||
|       "integrity": "sha512-nla//68K9NU6yRiwDY/Q8aU6siKlSs64aEC7+IV56QoAuyQT2ovsJcgGYGyqMOmI/CGN1BOR6mM5EN0FBO+zyQ==", | ||||
|       "dependencies": { | ||||
|         "isexe": "^2.0.0" | ||||
|       }, | ||||
|       "bin": { | ||||
|         "node-which": "bin/node-which" | ||||
|         "node-which": "bin/which.js" | ||||
|       }, | ||||
|       "engines": { | ||||
|         "node": ">= 8" | ||||
|         "node": "^14.17.0 || ^16.13.0 || >=18.0.0" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/which-boxed-primitive": { | ||||
| @ -2188,11 +2196,6 @@ | ||||
|       "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", | ||||
|       "dev": true | ||||
|     }, | ||||
|     "command-exists": { | ||||
|       "version": "1.2.9", | ||||
|       "resolved": "https://registry.npmjs.org/command-exists/-/command-exists-1.2.9.tgz", | ||||
|       "integrity": "sha512-LTQ/SGc+s0Xc0Fu5WaKnR0YiygZkm9eKFvyS+fRsU7/ZWFF8ykFM6Pc9aCVf1+xasOOZpO3BAVgVrKvsqKHV7w==" | ||||
|     }, | ||||
|     "concat-map": { | ||||
|       "version": "0.0.1", | ||||
|       "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", | ||||
| @ -2214,6 +2217,17 @@ | ||||
|         "path-key": "^3.1.0", | ||||
|         "shebang-command": "^2.0.0", | ||||
|         "which": "^2.0.1" | ||||
|       }, | ||||
|       "dependencies": { | ||||
|         "which": { | ||||
|           "version": "2.0.2", | ||||
|           "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", | ||||
|           "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", | ||||
|           "dev": true, | ||||
|           "requires": { | ||||
|             "isexe": "^2.0.0" | ||||
|           } | ||||
|         } | ||||
|       } | ||||
|     }, | ||||
|     "debug": { | ||||
| @ -2912,8 +2926,7 @@ | ||||
|     "isexe": { | ||||
|       "version": "2.0.0", | ||||
|       "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", | ||||
|       "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", | ||||
|       "dev": true | ||||
|       "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" | ||||
|     }, | ||||
|     "js-sdsl": { | ||||
|       "version": "4.2.0", | ||||
| @ -3360,10 +3373,9 @@ | ||||
|       } | ||||
|     }, | ||||
|     "which": { | ||||
|       "version": "2.0.2", | ||||
|       "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", | ||||
|       "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", | ||||
|       "dev": true, | ||||
|       "version": "3.0.0", | ||||
|       "resolved": "https://registry.npmjs.org/which/-/which-3.0.0.tgz", | ||||
|       "integrity": "sha512-nla//68K9NU6yRiwDY/Q8aU6siKlSs64aEC7+IV56QoAuyQT2ovsJcgGYGyqMOmI/CGN1BOR6mM5EN0FBO+zyQ==", | ||||
|       "requires": { | ||||
|         "isexe": "^2.0.0" | ||||
|       } | ||||
|  | ||||
| @ -30,8 +30,8 @@ | ||||
|   }, | ||||
|   "homepage": "https://github.com/easingthemes/ssh-deploy#readme", | ||||
|   "dependencies": { | ||||
|     "command-exists": "^1.2.9", | ||||
|     "rsyncwrapper": "^3.0.1" | ||||
|     "rsyncwrapper": "^3.0.1", | ||||
|     "which": "^3.0.0" | ||||
|   }, | ||||
|   "devDependencies": { | ||||
|     "@vercel/ncc": "^0.36.0", | ||||
|  | ||||
| @ -1,38 +1,60 @@ | ||||
| const { existsSync, mkdirSync, writeFileSync } = require('fs'); | ||||
| 
 | ||||
| const { | ||||
|   GITHUB_WORKSPACE | ||||
| } = process.env; | ||||
| const { join } = require('path'); | ||||
| 
 | ||||
| const validateDir = (dir) => { | ||||
|   if (!existsSync(dir)) { | ||||
|     console.log(`[SSH] Creating ${dir} dir in `, GITHUB_WORKSPACE); | ||||
|   if (existsSync(dir)) { | ||||
|     console.log(`[SSH] ${dir} dir exist`); | ||||
|     return; | ||||
|   } | ||||
| 
 | ||||
|   console.log(`[SSH] Creating ${dir} dir in workspace root`); | ||||
|   mkdirSync(dir); | ||||
|   console.log('✅ [SSH] dir created.'); | ||||
|   } else { | ||||
|     console.log(`[SSH] ${dir} dir exist`); | ||||
|   } | ||||
| }; | ||||
| 
 | ||||
| const validateFile = (filePath) => { | ||||
|   if (!existsSync(filePath)) { | ||||
|     console.log(`[SSH] Creating ${filePath} file in `, GITHUB_WORKSPACE); | ||||
| const writeToFile = ({ dir, filename, content, isRequired }) => { | ||||
|   validateDir(dir); | ||||
|   const filePath = join(dir, filename); | ||||
| 
 | ||||
|   if (existsSync(filePath)) { | ||||
|     console.log(`[FILE] ${filePath} file exist`); | ||||
|     if (isRequired) { | ||||
|       throw new Error(`⚠️ [FILE] ${filePath} Required file exist, aborting ...`); | ||||
|     } | ||||
|     return; | ||||
|   } | ||||
| 
 | ||||
|   try { | ||||
|       writeFileSync(filePath, '', { | ||||
|     writeFileSync(filePath, content, { | ||||
|       encoding: 'utf8', | ||||
|       mode: 0o600 | ||||
|     }); | ||||
|       console.log('✅ [SSH] file created.'); | ||||
|   } catch (e) { | ||||
|       console.error('⚠️ [SSH] writeFileSync error', filePath, e.message); | ||||
|       process.abort(); | ||||
|     } | ||||
|   } else { | ||||
|     console.log(`[SSH] ${filePath} file exist`); | ||||
|     throw new Error(`⚠️[FILE] Writing to file error. filePath: ${filePath}, message:  ${e.message}`); | ||||
|   } | ||||
| }; | ||||
| 
 | ||||
| module.exports = { | ||||
|   validateDir, | ||||
|   validateFile | ||||
| const validateRequiredInputs = (inputs) => { | ||||
|   const inputKeys = Object.keys(inputs); | ||||
|   const validInputs = inputKeys.filter((inputKey) => { | ||||
|     const inputValue = inputs[inputKey]; | ||||
| 
 | ||||
|     if (!inputValue) { | ||||
|       console.error(`⚠️ [INPUTS] ${inputKey} is mandatory`); | ||||
|     } | ||||
| 
 | ||||
|     return inputValue; | ||||
|   }); | ||||
| 
 | ||||
|   if (validInputs.length !== inputKeys.length) { | ||||
|     throw new Error('⚠️ [INPUTS] Inputs not valid, aborting ...'); | ||||
|   } | ||||
| }; | ||||
| 
 | ||||
| const snakeToCamel = (str) => str.replace(/[^a-zA-Z0-9]+(.)/g, (m, chr) => chr.toUpperCase()); | ||||
| 
 | ||||
| module.exports = { | ||||
|   writeToFile, | ||||
|   validateRequiredInputs, | ||||
|   snakeToCamel | ||||
| }; | ||||
|  | ||||
							
								
								
									
										101
									
								
								src/index.js
									
									
									
									
									
								
							
							
						
						
									
										101
									
								
								src/index.js
									
									
									
									
									
								
							| @ -1,78 +1,37 @@ | ||||
| #!/usr/bin/env node
 | ||||
| const nodeRsync = require('rsyncwrapper'); | ||||
| 
 | ||||
| const { validateRsync, validateInputs } = require('./rsyncCli'); | ||||
| const { sshDeploy } = require('./rsyncCli'); | ||||
| const { remoteCmdBefore, remoteCmdAfter } = require('./remoteCmd'); | ||||
| const { addSshKey } = require('./sshKey'); | ||||
| 
 | ||||
| const { | ||||
|   REMOTE_HOST, REMOTE_USER, | ||||
|   REMOTE_PORT, SSH_PRIVATE_KEY, DEPLOY_KEY_NAME, | ||||
|   SOURCE, TARGET, ARGS, EXCLUDE, | ||||
|   GITHUB_WORKSPACE | ||||
| } = require('./inputs'); | ||||
| 
 | ||||
| const defaultOptions = { | ||||
|   ssh: true, | ||||
|   sshCmdArgs: ['-o StrictHostKeyChecking=no'], | ||||
|   recursive: true | ||||
| }; | ||||
| 
 | ||||
| console.log('GITHUB_WORKSPACE: ', GITHUB_WORKSPACE); | ||||
| console.log('REMOTE_HOST: ', process.env.REMOTE_HOST); | ||||
| console.log('REMOTE_USER: ', process.env.REMOTE_USER); | ||||
| 
 | ||||
| const sshDeploy = (() => { | ||||
|   const rsync = ({ privateKey, port, src, dest, args, exclude }) => { | ||||
|     console.log(`[Rsync] Starting Rsync Action: ${src} to ${dest}`); | ||||
|     if (exclude) console.log(`[Rsync] exluding folders ${exclude}`); | ||||
| 
 | ||||
|     try { | ||||
|       // RSYNC COMMAND
 | ||||
|       nodeRsync({ | ||||
|         src, dest, args, privateKey, port, excludeFirst: exclude, ...defaultOptions | ||||
|       }, (error, stdout, stderr, cmd) => { | ||||
|         if (error) { | ||||
|           console.error('⚠️ [Rsync] error: ', error.message); | ||||
|           console.log('⚠️ [Rsync] stderr: ', stderr); | ||||
|           console.log('⚠️ [Rsync] stdout: ', stdout); | ||||
|           console.log('⚠️ [Rsync] cmd: ', cmd); | ||||
|           process.abort(); | ||||
|         } else { | ||||
|           console.log('✅ [Rsync] finished.', stdout); | ||||
|         } | ||||
|       }); | ||||
|     } catch (err) { | ||||
|       console.error('⚠️ [Rsync] command error: ', err.message, err.stack); | ||||
|       process.abort(); | ||||
|     } | ||||
|   }; | ||||
| 
 | ||||
|   const init = ({ src, dest, args, host = 'localhost', port, username, privateKeyContent, exclude = [] }) => { | ||||
|     validateRsync(() => { | ||||
|       const privateKey = addSshKey(privateKeyContent, DEPLOY_KEY_NAME || 'deploy_key'); | ||||
|       const remoteDest = `${username}@${host}:${dest}`; | ||||
| 
 | ||||
|       rsync({ privateKey, port, src, dest: remoteDest, args, exclude }); | ||||
|     }); | ||||
|   }; | ||||
| 
 | ||||
|   return { | ||||
|     init | ||||
|   }; | ||||
| })(); | ||||
| const { validateRequiredInputs } = require('./helpers'); | ||||
| const inputs = require('./inputs'); | ||||
| 
 | ||||
| const run = () => { | ||||
|   validateInputs({ SSH_PRIVATE_KEY, REMOTE_HOST, REMOTE_USER }); | ||||
| 
 | ||||
|   sshDeploy.init({ | ||||
|     src: `${GITHUB_WORKSPACE}/${SOURCE || ''}`, | ||||
|     dest: TARGET || `/home/${REMOTE_USER}/`, | ||||
|     args: ARGS ? [ARGS] : ['-rltgoDzvO'], | ||||
|     host: REMOTE_HOST, | ||||
|     port: REMOTE_PORT || '22', | ||||
|     username: REMOTE_USER, | ||||
|     privateKeyContent: SSH_PRIVATE_KEY, | ||||
|     exclude: (EXCLUDE || '').split(',').map((item) => item.trim()) // split by comma and trim whitespace
 | ||||
|   const { | ||||
|     source, remoteUser, remoteHost, remotePort, | ||||
|     deployKeyName, sshPrivateKey, | ||||
|     args, exclude, | ||||
|     scriptBefore, scriptAfter, | ||||
|     sshServer | ||||
|   } = inputs; | ||||
|   // Validate required inputs
 | ||||
|   validateRequiredInputs({ sshPrivateKey, remoteHost, remoteUser }); | ||||
|   // Add SSH key
 | ||||
|   const privateKey = addSshKey(sshPrivateKey, deployKeyName); | ||||
|   // Check Script before
 | ||||
|   if (scriptBefore) { | ||||
|     remoteCmdBefore(scriptBefore); | ||||
|   } | ||||
|   // Check script after
 | ||||
|   let callback = () => {}; | ||||
|   if (scriptAfter) { | ||||
|     callback = (...result) => { | ||||
|       remoteCmdAfter(scriptAfter, result); | ||||
|     }; | ||||
|   } | ||||
|   /* eslint-disable object-property-newline */ | ||||
|   sshDeploy({ | ||||
|     source, sshServer, exclude, remotePort, | ||||
|     privateKey, args, callback | ||||
|   }); | ||||
| }; | ||||
| 
 | ||||
|  | ||||
| @ -1,11 +1,47 @@ | ||||
| const inputNames = ['REMOTE_HOST', 'REMOTE_USER', 'REMOTE_PORT', 'SSH_PRIVATE_KEY', 'DEPLOY_KEY_NAME', 'SOURCE', 'TARGET', 'ARGS', 'EXCLUDE']; | ||||
| const { snakeToCamel } = require('./helpers'); | ||||
| 
 | ||||
| const inputNames = [ | ||||
|   'REMOTE_HOST', 'REMOTE_USER', 'REMOTE_PORT', | ||||
|   'SSH_PRIVATE_KEY', 'DEPLOY_KEY_NAME', | ||||
|   'SOURCE', 'TARGET', 'ARGS', 'EXCLUDE', | ||||
|   'SCRIPT_BEFORE', 'SCRIPT_AFTER']; | ||||
| 
 | ||||
| const githubWorkspace = process.env.GITHUB_WORKSPACE; | ||||
| const remoteUser = process.env.REMOTE_USER; | ||||
| 
 | ||||
| const defaultInputs = { | ||||
|   source: '', // TODO
 | ||||
|   target: `/home/${remoteUser}/`, | ||||
|   exclude: '', // TODO
 | ||||
|   args: '-rltgoDzvO', // TODO
 | ||||
|   deployKeyName: 'deploy_key' | ||||
| }; | ||||
| 
 | ||||
| const inputs = { | ||||
|   GITHUB_WORKSPACE: process.env.GITHUB_WORKSPACE | ||||
|   githubWorkspace | ||||
| }; | ||||
| 
 | ||||
| inputNames.forEach((input) => { | ||||
|   inputs[input] = process.env[input] || process.env[`INPUT_${input}`]; | ||||
|   const inputName = snakeToCamel(input.toLowerCase()); | ||||
|   const inputVal = process.env[input] || process.env[`INPUT_${input}`]; | ||||
|   const validVal = inputVal === undefined ? defaultInputs[inputName] : inputVal; | ||||
|   let extendedVal = validVal; | ||||
|   // eslint-disable-next-line default-case
 | ||||
|   switch (inputName) { | ||||
|     case 'source': | ||||
|       extendedVal = `${githubWorkspace}/${validVal}`; | ||||
|       break; | ||||
|     case 'exclude': | ||||
|       extendedVal = validVal.split(',').map((item) => item.trim()); | ||||
|       break; | ||||
|     case 'args': | ||||
|       extendedVal = [validVal]; | ||||
|       break; | ||||
|   } | ||||
| 
 | ||||
|   inputs[inputName] = extendedVal; | ||||
| }); | ||||
| 
 | ||||
| inputs.sshServer = `${inputs.remoteUser}@${inputs.remoteHost}:${inputs.target}`; | ||||
| 
 | ||||
| module.exports = inputs; | ||||
|  | ||||
| @ -0,0 +1,27 @@ | ||||
| const { join } = require('path'); | ||||
| const { exec } = require('child_process'); | ||||
| 
 | ||||
| const { sshServer, githubWorkspace } = require('./inputs'); | ||||
| const { writeToFile } = require('./helpers'); | ||||
| 
 | ||||
| const remoteCmd = (cmd, label) => { | ||||
|   const localScriptPath = join(githubWorkspace, `local_ssh_script-${label}.sh`); | ||||
|   try { | ||||
|     writeToFile(localScriptPath, cmd); | ||||
| 
 | ||||
|     exec(`ssh ${sshServer} 'bash -s' < ${localScriptPath}`, (err, data, stderr) => { | ||||
|       if (err) { | ||||
|         console.log('⚠️ [CMD] Remote script failed. ', err.message); | ||||
|       } else { | ||||
|         console.log('✅ [CMD] Remote script executed. \n', data, stderr); | ||||
|       } | ||||
|     }); | ||||
|   } catch (err) { | ||||
|     console.log('⚠️ [CMD] Starting Remote script execution failed. ', err.message); | ||||
|   } | ||||
| }; | ||||
| 
 | ||||
| module.exports = { | ||||
|   remoteCmdBefore: (cmd) => remoteCmd(cmd, 'before'), | ||||
|   remoteCmdAfter: (cmd) => remoteCmd(cmd, 'after') | ||||
| }; | ||||
| @ -1,46 +1,78 @@ | ||||
| const { sync: commandExists } = require("command-exists"); | ||||
| const { exec, execSync } = require("child_process"); | ||||
| const { execSync } = require('child_process'); | ||||
| const which = require('which'); | ||||
| const nodeRsync = require('rsyncwrapper'); | ||||
| 
 | ||||
| const validateRsync = (callback = () => {}) => { | ||||
|   const rsyncCli = commandExists("rsync"); | ||||
| const validateRsync = async () => { | ||||
|   const rsyncCli = await which('rsync', { nothrow: true }); | ||||
|   execSync('rsync --version', { stdio: 'inherit' }); | ||||
|   if (rsyncCli) { | ||||
|     console.log('⚠️ [CLI] Rsync exists'); | ||||
|     execSync("rsync --version", { stdio: 'inherit' }); | ||||
|     return callback(); | ||||
|     execSync('rsync --version', { stdio: 'inherit' }); | ||||
|     return; | ||||
|   } | ||||
| 
 | ||||
|   console.log('⚠️ [CLI] Rsync doesn\'t exists. Start installation with "apt-get" \n'); | ||||
| 
 | ||||
|   exec("sudo apt-get update && sudo apt-get --no-install-recommends install rsync", (err, data, stderr) => { | ||||
|     if (err) { | ||||
|       console.log("⚠️ [CLI] Rsync installation failed. Aborting ... ", err.message); | ||||
|       process.abort(); | ||||
|     } else { | ||||
|       console.log("✅ [CLI] Rsync installed. \n", data, stderr); | ||||
|       callback(); | ||||
|   try { | ||||
|     execSync('sudo apt-get update && sudo apt-get --no-install-recommends install rsync', { stdio: 'inherit' }); | ||||
|     console.log('✅ [CLI] Rsync installed. \n'); | ||||
|   } catch (err) { | ||||
|     throw new Error(`⚠️ [CLI] Rsync installation failed. Aborting ... error: ${err.message}`); | ||||
|   } | ||||
|   }); | ||||
| }; | ||||
| 
 | ||||
| const validateInputs = (inputs) => { | ||||
|   const inputKeys = Object.keys(inputs); | ||||
|   const validInputs = inputKeys.filter((inputKey) => { | ||||
|     const inputValue = inputs[inputKey]; | ||||
| const rsyncCli = ({ | ||||
|   source, sshServer, exclude, remotePort, | ||||
|   privateKey, args, callback | ||||
| }) => { | ||||
|   console.log(`[Rsync] Starting Rsync Action: ${source} to ${sshServer}`); | ||||
|   if (exclude) console.log(`[Rsync] excluding folders ${exclude}`); | ||||
| 
 | ||||
|     if (!inputValue) { | ||||
|       console.error(`⚠️ [INPUTS] ${inputKey} is mandatory`); | ||||
|   const defaultOptions = { | ||||
|     ssh: true, | ||||
|     sshCmdArgs: ['-o StrictHostKeyChecking=no'], | ||||
|     recursive: true | ||||
|   }; | ||||
| 
 | ||||
|   try { | ||||
|     // RSYNC COMMAND
 | ||||
|     /* eslint-disable object-property-newline */ | ||||
|     nodeRsync({ | ||||
|       src: source, dest: sshServer, excludeFirst: exclude, port: remotePort, | ||||
|       privateKey, args, callback, | ||||
|       ...defaultOptions | ||||
|     }, (error, stdout, stderr, cmd) => { | ||||
|       if (error) { | ||||
|         console.error('⚠️ [Rsync] error: ', error.message); | ||||
|         console.log('⚠️ [Rsync] stderr: ', stderr); | ||||
|         console.log('⚠️ [Rsync] stdout: ', stdout); | ||||
|         console.log('⚠️ [Rsync] cmd: ', cmd); | ||||
|       } else { | ||||
|         console.log('✅ [Rsync] finished.', stdout); | ||||
|       } | ||||
| 
 | ||||
|     return inputValue; | ||||
|   }); | ||||
|       callback(error, stdout, stderr, cmd); | ||||
| 
 | ||||
|   if (validInputs.length !== inputKeys.length) { | ||||
|     console.error("⚠️ [INPUTS] Inputs not valid, aborting ..."); | ||||
|       if (error) { | ||||
|         process.abort(); | ||||
|       } | ||||
|     }); | ||||
|   } catch (err) { | ||||
|     console.error('⚠️ [Rsync] command error: ', err.message, err.stack); | ||||
|     process.abort(); | ||||
|   } | ||||
| }; | ||||
| 
 | ||||
| const sshDeploy = (params) => { | ||||
|   validateRsync | ||||
|     .then(() => { | ||||
|       rsyncCli(params); | ||||
|     }) | ||||
|     .catch((err) => { | ||||
|       throw err; | ||||
|     }); | ||||
| }; | ||||
| 
 | ||||
| module.exports = { | ||||
|   validateRsync, | ||||
|   validateInputs, | ||||
|   sshDeploy | ||||
| }; | ||||
|  | ||||
| @ -1,37 +1,20 @@ | ||||
| const { writeFileSync } = require('fs'); | ||||
| const { join } = require('path'); | ||||
| 
 | ||||
| const { | ||||
|   validateDir, | ||||
|   validateFile | ||||
| } = require('./helpers'); | ||||
| const { writeToFile } = require('./helpers'); | ||||
| 
 | ||||
| const { | ||||
|   HOME | ||||
| } = process.env; | ||||
| const addSshKey = (content, filename) => { | ||||
|   const { HOME } = process.env; | ||||
|   const dir = join(HOME || __dirname, '.ssh'); | ||||
|   const filePath = join(dir, filename); | ||||
| 
 | ||||
| const addSshKey = (key, name) => { | ||||
|   const sshDir = join(HOME || __dirname, '.ssh'); | ||||
|   const filePath = join(sshDir, name); | ||||
|   writeToFile({ dir, filename: 'known_hosts', content: '' }); | ||||
|   writeToFile({ dir, filename, content, isRequired: true }); | ||||
| 
 | ||||
|   validateDir(sshDir); | ||||
|   validateFile(`${sshDir}/known_hosts`); | ||||
| 
 | ||||
|   try { | ||||
|     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); | ||||
|   console.log('✅ Ssh key added to `.ssh` dir ', dir); | ||||
| 
 | ||||
|   return filePath; | ||||
| }; | ||||
| 
 | ||||
| module.exports = { | ||||
|   addSshKey | ||||
| } | ||||
| }; | ||||
|  | ||||
							
								
								
									
										13
									
								
								src/test.js
									
									
									
									
									
								
							
							
						
						
									
										13
									
								
								src/test.js
									
									
									
									
									
								
							| @ -1,13 +0,0 @@ | ||||
| console.log('||||||||||||||||||||||||||||||||||||||'); | ||||
| console.log('EXAMPLE_REMOTE_HOST: ', process.env.EXAMPLE_REMOTE_HOST); | ||||
| console.log('EXAMPLE_REMOTE_USER: ', process.env.EXAMPLE_REMOTE_USER); | ||||
| console.log('EXAMPLE_SSH_PRIVATE_KEY: ', process.env.EXAMPLE_SSH_PRIVATE_KEY); | ||||
| console.log('||||||||||||||||||||||||||||||||||||||'); | ||||
| console.log('EXAMPLE_REMOTE_HOST1: ', process.env.EXAMPLE_REMOTE_HOST1); | ||||
| console.log('EXAMPLE_REMOTE_USER1: ', process.env.EXAMPLE_REMOTE_USER1); | ||||
| console.log('EXAMPLE_SSH_PRIVATE_KEY1: ', process.env.EXAMPLE_SSH_PRIVATE_KEY1); | ||||
| console.log('||||||||||||||||||||||||||||||||||||||'); | ||||
| console.log('REMOTE_USER: ', process.env.REMOTE_USER); | ||||
| console.log('REMOTE_HOST: ', process.env.REMOTE_HOST); | ||||
| console.log('SSH_PRIVATE_KEY: ', process.env.SSH_PRIVATE_KEY); | ||||
| console.log('||||||||||||||||||||||||||||||||||||||'); | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user
	 Dragan Filipovic
						Dragan Filipovic