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.