1. Identify your needs
Requirements should be identified before development, which can be summarized as the following according to common front-end deployment processes:Define requirements for automated deployment according to the deployment process:
2. Preparation before development
2.1 Importing dependent Modules
Since you need to implement file compression, simulate form submission, loading effect, friendly prompt effect, file reading and writing, and file uploading, you need at least the following modules:
- Compressing module (supports compressed folders and zip compression)
- The child_process module (which can be used to create a child process that calls shell commands in js)
- Fs module (can be used to interact with the file system)
- Form-data module (combine the name and value of form elements to realize the serialization of form data, thus reducing the splicing of form elements and improving work efficiency)
- Ora module (loading effects in command line environment and ICONS showing various states)
- Chalk module (modify the style of the string in the console, including font style, font color, background color)
- HTTP module (create HTTP server, client)
2.2 How to implement the specification
For reasonable demand to realize the decoupling and logic clear/flexible, need to pay attention to the overall program logic, select encapsulates related functions, and freedom in the main program scheduling (flexible, closed, modify relevant function call), and to give prompt execution by the current function, to ensure the integrity of functions and abnormal hints.
Here is the completion of the program function to build the sorting work, the following into the project implementation.
3. Function realization
3.1 Packaged Build
Use the child_process module to call shell commands for a local packaged build, where env represents different build environments and is available from different execution scripts.
Json scripts can be configured as follows: “upload:server”: “node build/deploy.js –sit”.
const env = process.argv[process.argv.length - 1].replace(The '-'.' '); // Get build script environment parameters
childProcess.exec(`npm run build:${env}`.function(error, stdout, stderr) {
if (error) {
console.log('exec error: ' + error);
}
compress();
});
Copy the code
3.2 Local Compressed Files and Function Prompts
After the completion of project packaging and construction, perform function prompt and compression operations. Compressing and ORA modules are used here for zip compression and functional prompt of product folders
const spinner = ora('Start packing... ').start();
spinner.text = 'Packing done, start compression';
// Compress the command
// The first parameter is the directory to be packed, and the second parameter is the file name to be packed
compressing.zip
.compressDir('web/'.'web.tar.gz')
.then(() = > {
setTimeout(() = > {
spinner.text = 'Compression complete, upload started';
upload();
}, 300);
})
.catch(handleError);
Copy the code
3.3 Uploading compressed Files to the File server
Setting up a file server:
How to build a simple file server
Instructions:
- The form-data Form simulates file upload
- Send a POST request to upload a file to the server
- Get interface returns code for downloading files uploaded to the file server
<form action="/ishare/profile" method="post" enctype="multipart/form-data">
<input type="file" name="avatar" />
<button type="submit">submit</button>
</form>
Copy the code
- The Entype property in the form form can be used to control how the form data is encoded before being sent
- Multipart /form-data does not encode characters and is used to send binary files. The other two types (Text /plain, Application/X-www-form-urlencoded) cannot be used to send files.
- Type =file Uploads files
- Name Is used to obtain the file name
The general implementation process is as follows:
var FormData = require('form-data');
var formData = new FormData();
formData.append(
'avatar',
fs.createReadStream(path.resolve(__dirname, '.. /web.tar.gz')));//'file' is the key accepted by the server
var headers = formData.getHeaders(); // This can't be less
var request = http.request(
{
method: 'post'.host: config.remotePath,
path: '/ishare/profile'.headers: headers
},
function(res) {
var str = ' ';
res.on('data'.function(buffer) {
str += buffer; // Use string concatenation
});
res.on('end'.() = > {
if (str) {
const result = JSON.parse(str);
const uploaded = result.uploaded;
const fileCode = uploaded.substring(uploaded.indexOf('/') + 1);
spinner.text = 'Upload complete, start fetching file from server'; getFile(fileCode); }}); }); formData.pipe(request);Copy the code
3.4 Obtaining uploaded Files from the Remote server
After the compressed file is uploaded to the file server, the interface returns fileCode to invoke the interface service deployed on the remote server.
Interface services mainly do the following things:
- Avoid unwarranted server connection operations and reduce open walls and unstable factors
- Download the uploaded file to the specified location on the server
- Configurati on Configures the address of the server and target file directory
- The original files are not backed up again
- Delete a static resource file directory
- Unzip the new zip file
3.5 Obtain fileCode and sessionTick(to prevent users from invoking the interface maliciously) and download the file to the specified location on the server
app.get('/getFile'.function(req, res) {
const fileCode = req.query.code;
const sessionTick = req.query.sessionTick;
getFileByCode({fileCode: fileCode,sessionTick: sessionTick}, res);
});
async function getFileByCode(obj, res) {
if(obj.sessionTick && obj.sessionTick ! = ='522314cc-f038-4abf-bb36-adc912e506e2') {
res.msg = 'sessionTick missed or error';
res.code = '0';
res.status = 200;
res.json(res);
return
}
console.log('sessionTick missed');
await dirExists(`. /${config.filePath}`);
let httpStream = request({
method: 'GET'.url: `http://${config.remotePath}/ishare/${obj.fileCode}`
});
let writeStream = fs.createWriteStream(`. /${config.filePath}/${config.fileName}`);
// Connect Readable and Writable
httpStream.pipe(writeStream);
let totalLength = 0;
// When the response for the first HTTP request is obtained
httpStream.on('response'.response= > {
console.log('response headers is: ', response.headers);
});
httpStream.on('data'.chunk= > {
totalLength += chunk.length;
console.log('recevied data size: ' + totalLength + 'KB');
});
// Download complete
writeStream.on('close'.() = > {
console.log('download finished');
executeShell(res);
});
}
Copy the code
DirExists is used to check whether the specified path exists and create it if it does not
/** * If the path does not exist, create *@param {string} * / dir path
async function dirExists(dir) {
let isExists = await getStat(dir)
// Return true if the path is not a file
if (isExists && isExists.isDirectory()) {
return true
} else if (isExists) {
// If the path exists but the file, return false
return false
}
// If the path does not exist
let tempDir = path.parse(dir).dir // Get the parent path
If the parent directory does not exist, the code will continue to loop until the directory exists
let status = await dirExists(tempDir)
let mkdirStatus
if (status) {
mkdirStatus = await mkdir(dir)
}
return mkdirStatus
}
/** * Read path information *@param {string} Path the path * /
function getStat(path) {
return new Promise((resolve, reject) = > {
fs.stat(path, (err, stats) = > {
if (err) {
resolve(false);
} else{ resolve(stats); }}); }); }/** * Create path *@param {string} * / dir path
function mkdir(dir) {
return new Promise((resolve, reject) = > {
fs.mkdir(dir, err= > {
if (err) {
resolve(false);
} else {
resolve(true); }}); }); }Copy the code
3.6 Run deploy.sh to back up front-end resources of the Server
The executeShell function is mainly used to execute the shell command sh deploy.sh to back up original files using the target file directory
async function executeShell(res) {
await dirExists(`${config.projectPath}/${config.projectName}/${config.filePath}`);
childprocess.exec(`sh deploy.sh ${config.projectName} ${config.filePath} ${config.fileName}`.function(error, stdout, stderr) {
if (error) {
result.msg = 'An error message is displayed when the deploy.sh script is executed.${error}`;
result.code = '1';
result.status = 500;
res.json(result);
console.log('exec error: ' + error);
} else {
console.log('Execute script deploy.sh to complete'); copy(res); }}); }Copy the code
The deploy.sh code is implemented as follows:
#! /bin/sh
nowtime=$(date "+%Y%m%d")
echo $nowtime
cd /home/bankdplyop/${1}
rm -rf ${3}
tar -cf ${2}$nowtime.tar.gz ${2}
Copy the code
Where 1, {1}, 1, {2}, and 3 are the parameters to be transmitted when executing the shell command. The command executed here is shdeploy.sh{3} is the parameters to be transmitted when executing the shell command. Sh 3 Indicates the parameters passed when the shell command is executed. The following command is executed :shdeploy.sh{config.projectname} config.filePath{config.filepath} config.filePath{config.filename}
The config file is used to freely configure the target file address, the project source file address, and some remote server related parameters
module.exports = {
sit: {
remotePath: '29.2.221.176'.// Directory of the remote server to upload
filePath: 'web'.fileName: 'web.tar.gz'.projectName: 'iris_front'.projectPath: '/home/bankdplyop'.host: '0.0.0.0'.// Remote host
port: 3000 // Server port number}};Copy the code
3.7 Deleting the Static Resource File directory and decompressing the downloaded file
- Copy the downloaded files to the location of the project to be deployed
- Run the replace.sh script
- Prompt user publish complete, please test
async function copy(res) {
const destFilePath = path.resolve(__dirname, `${config.projectPath}/${config.projectName}/${config.fileName}`);
const filePath = path.resolve(__dirname, `. /${config.filePath}/${config.fileName}`);
await dirExists(`${config.projectPath}/${config.projectName}`);
fs.writeFile(destFilePath, fs.readFileSync(filePath), function(err) {
if (err) {
result.msg = 'File copy failed';
result.code = '1';
result.status = 500;
res.json(result);
};
executeReplaceShell(res);
});
}
Copy the code
ExecuteReplaceShell (res) deletes and decompresses files when the copy is complete
async function executeReplaceShell(res) {
console.log(
'replace shell start: ' +
`${config.projectName}/${config.filePath}/${config.fileName}`
)
try {
childprocess.exec(
`sh replace.sh ${config.projectName} ${config.filePath} ${config.fileName} `.function (error, stdout, stderr) {
if (error) {
result.msg = 'replace script replace.sh with error message:${error}`
result.code = '1'
result.status = 500
res.json(result)
} else {
result.msg = 'Publish complete, please test'
result.code = '0'
result.status = 200
res.json(result)
}
}
)
} catch (e) {
console.log(e)
}
}
Copy the code
The replace.sh code is implemented as follows:
cd /home/bankdplyop/${1}
ls ${2}
rm -rf ${2}
unzip ${3}
Copy the code
Where 1, {1}, 1, {2}, and 3 refer to the parameters passed when executing the shell command. The command executed here is shreplace.sh{3} refers to the parameters passed when executing the shell command. Sh replace.sh 3 refers to the argument passed when executing the shell command, Run the following command :shreplace.sh{config.projectname} config.filePath{config.filepath} config.filePath{config.filename}
This completes the automated release process.
use
Run the project root directory directly
npm run upload:server
Copy the code
conclusion
Front-end automatic deployment is a very meaningful function, remember at the beginning of the code upload server, all need to be manually operated, really is no trouble, I hope interested students, if the company does not have a complete set of automatic deployment process, you can play by yourself. There are still a lot of points that can be improved, I hope you can exchange more, put forward some valuable opinions.