Why did you write this?
There is only one truth => laziness!!!
If you want to use it directly, github portal You don't have to look at the back - - remember to leave a star, the heart of your pen.
At present, iot project is mainly done. Due to historical reasons, there are many pure H5 sub-projects of react/vue on the platform. These projects must call some APIs exposed by APP, which makes it necessary to build and deploy the build package manually on the development server repeatedly during local development and debugging. Several projects, several rounds of testing, quite a bit tired. So think about writing a tool and automatically deploying it to the server after the yarn build package, so that you can omit: open ftp tool - > find the build directory - > find the deployment directory on the server - > backup - > paste the copy file, which is a tedious manual process to improve the efficiency of mo work (yu).
And with this, when jenkins builds automatically, the shell script for the project configuration can also have fewer lines of code.
How to achieve it?
Thought analysis
First, we analyze the results to be achieved.
-
Execute yarn build
Individuals like to use yarn, with npm students can replace themselves.
Assuming that all projects are built on the create react app || Vue cli scaffolding, if you have your own custom scaffolding, keep looking down. In this paper, create react app scaffolding as an example - Tool automatic trigger
- Log on to remote server, establish connection and backup original project
- copy all files from the local build directory (in this article, the build directory under the project root directory) and upload them to the remote server deployment directory
- Deployment complete, output prompt, close remote connection
Secondly, it analyses how to achieve the results:
- Writing tools in front-end projects, preferred node.js
- Tools need to be triggered automatically after the implementation of yarn build. Here we need to use the hook postbuild in npm scripts. It will do some finishing work after the implementation of build, which can be used to trigger deployment operations. For those who don't know much about npm scripts, you can refer to Ruan Yifeng npm scripts usage guide
- To establish and operate a connection with the server, ssh connection is needed, and third-party libraries are recommended. ssh2
- When backing up, it's better to add a specific time to the file name. For example: my_app.bak 2019-10-8-14:36:36. For convenience, use a lightweight time library directly moment
Start drying
-
Install ssh2, moment
yarn add ssh2 moment
-
For convenience, create deploy.js in the project root directory. This js file is the automated deployment tool written this time
deploy.js is not necessarily in the project root directory. If you are familiar with the way node looks for paths, you can put them under the path you specify.
-
Open the package.json file and add:
"postbuild": "yarn run deploy", "deploy": "node ./deploy.js",
At this point, if console. log ('- - deploy test -') is added to deploy.js, the console executes yarn build, you can see that after the completion of the build, yarn run deploy will continue to execute, and the final console output:
---deploy test--
- According to the ssh2 document, write the code to connect the server, backup files and upload files:
const path = require('path') const moment = require('moment') const util = require('util') const events = require('events') const Client = require('ssh2').Client const fs = require('fs') /*********************************************************************************/ /******************************Please manually configure the following contents *******************************************************************************/ /*********************************************************************************/ /** * Remote Server Configuration * @type {{password: string, port: number, host: string, username: string}} */ const server = { host: 'xxx.xxx.xxx.xxx', //Host ip port: 22, //SSH Connection Port username: 'xxxx', //User name password: 'xxxxxxx', //User login password } const baseDir = 'my_app'//Project catalogue const basePath = '/home'//Project Deployment Directory const bakDirName = baseDir + '.bak' + moment(new Date()).format('YYYY-M-D-HH:mm:ss')//Backup File Name const buildPath = path.resolve('./build')//Compiled file directories for local projects /*********************************************************************************/ /**********************************Configuration End***************************************************************************/ /*********************************************************************************/ function doConnect(server, then) { const conn = new Client() conn.on('ready', function () { then && then(conn) }).on('error', function (err) { console.error('connect error!', err) }).on('close', function () { conn.end() }).connect(server) } function doShell(server, cmd, then) { doConnect(server, function (conn) { conn.shell(function (err, stream) { if (err) throw err else { let buf = '' stream.on('close', function () { conn.end() then && then(err, buf) }).on('data', function (data) { buf = buf + data }).stderr.on('data', function (data) { console.log('stderr: ' + data) }) stream.end(cmd) } }) }) } function doGetFileAndDirList(localDir, dirs, files) { const dir = fs.readdirSync(localDir) for (let i = 0; i < dir.length; i++) { const p = path.join(localDir, dir[i]) const stat = fs.statSync(p) if (stat.isDirectory()) { dirs.push(p) doGetFileAndDirList(p, dirs, files) } else { files.push(p) } } } function Control() { events.EventEmitter.call(this) } util.inherits(Control, events.EventEmitter) const control = new Control() control.on('doNext', function (todos, then) { if (todos.length > 0) { const func = todos.shift() func(function (err, result) { if (err) { then(err) throw err } else { control.emit('doNext', todos, then) } }) } else { then(null) } }) function doUploadFile(server, localPath, remotePath, then) { doConnect(server, function (conn) { conn.sftp(function (err, sftp) { if (err) { then(err) } else { sftp.fastPut(localPath, remotePath, function (err, result) { conn.end() then(err, result) }) } }) }) } function doUploadDir(server, localDir, remoteDir, then) { let dirs = [] let files = [] doGetFileAndDirList(localDir, dirs, files) // Create a remote directory let todoDir = [] dirs.forEach(function (dir) { todoDir.push(function (done) { const to = path.join(remoteDir, dir.slice(localDir.length + 1)).replace(/[\\]/g, '/') const cmd = 'mkdir -p ' + to + '\r\nexit\r\n' console.log(`cmd::${cmd}`) doShell(server, cmd, done) })// end of push }) // Upload files let todoFile = [] files.forEach(function (file) { todoFile.push(function (done) { const to = path.join(remoteDir, file.slice(localDir.length + 1)).replace(/[\\]/g, '/') console.log('upload ' + to) doUploadFile(server, file, to, done) }) }) control.emit('doNext', todoDir, function (err) { if (err) { throw err } else { control.emit('doNext', todoFile, then) } }) } console.log('--------deploy config--------------') console.log(`The server host: ${server.host}`) console.log(`Project Folder: ${baseDir}`) console.log(`Project deployment and backup directory: ${basePath}`) console.log(`Folder name after backup: ${bakDirName}`) console.log('--------deploy start--------------') doShell(server, `mv ${basePath}/${baseDir} ${basePath}/${bakDirName}\nexit\n`) doUploadDir(server, buildPath, `${basePath}/${baseDir}`, () => console.log('--------deploy end--------------'))
Running result example
When using scripts to trigger, after running yarn build, the lifecycle hook postbuild is automatically triggered for deployment. This process first builds the packaged project locally to the configurable buildPath directory, then backs up my_app to my_app.bak2019-10-8-23:06:27 in the remote server xxxx.xxx.xxxxx/home, and finally uploads all the local buildPath directory files to / home/my_app to complete the deployment.
$ yarn run deploy $ node ./deploy.js --------deploy config-------------- //Server host: xxx. xxx. xxx. XXX //Project folder: my_app //Project deployment and backup directory: / home //Backup folder name: my_app.bak2019-10-8-23:06:27 --------deploy start-------------- cmd::mkdir -p /home/my_app/static exit cmd::mkdir -p /home/my_app/static/css exit cmd::mkdir -p /home/my_app/static/js exit cmd::mkdir -p /home/my_app/static/media exit upload /home/my_app/asset-manifest.json upload /home/my_app/favicon.ico upload /home/my_app/index.html upload /home/my_app/logo192.png upload /home/my_app/logo512.png upload /home/my_app/manifest.json upload /home/my_app/precache-manifest.20dc8cb74286fd491ca0a9fc9b07234a.js upload /home/my_app/robots.txt upload /home/my_app/service-worker.js upload /home/my_app/static/css/main.2cce8147.chunk.css upload /home/my_app/static/css/main.2cce8147.chunk.css.map upload /home/my_app/static/js/2.222d1515.chunk.js upload /home/my_app/static/js/2.222d1515.chunk.js.map upload /home/my_app/static/js/main.0782b2ff.chunk.js upload /home/my_app/static/js/main.0782b2ff.chunk.js.map upload /home/my_app/static/js/runtime~main.077bb605.js upload /home/my_app/static/js/runtime~main.077bb605.js.map upload /home/my_app/static/media/logo.5d5d9eef.svg --------deploy end-------------- Done in 16.58s.
epilogue
Project GitHub address: https://github.com/hello-jun/deploy
This tool can also be used independently. After a little modification, it can also be used to automatically deploy react native projects. If you are interested, you can try it on your own.~
Welcome to star, message and issue.
I hope this article will be helpful to you, and I wish you a happy working life.