实现项目发布自动化

背景:公司开发项目发布系统

需求:    

        1.实现本地文件或者git/svn上的文件上传(ftp)到服务器
        2.错误实时返回到前台
        3.文件备份回滚

简单分析场景:
    1.下载到本地时,如果本地存在,则会报错,故需要在下载之前删除本地文件夹
        判断存在?执行删除
        或者直接删除,捕获错误忽略(try)
    2.只有在上一步成功过后才可以读取文件夹,所以需不需要判断文件夹存在情况呢?
        还是判断一下吧!可能有直接输入文件夹上传,没有 下载 一步
    3.读取文件列表 (此为同步进行) 成功失败后执行响应操作
    4.以上成功。连接ftp。
    5.以上成功。首先创建一个备份'_back'的文件夹,作为项目名称 也就是 name_back
    6.以上成功。上传文件。适时返回进度,在上传完成(未出错)
    7.以上成功。将项目名称改为 name_getTime() 。此处可能出错,因为文件可能不存在,捕获错误,忽略。
    8.以上成功。讲上传的项目 name_back 重命名为 name
    9.关闭连接,完成。
    10. 回滚。需要传入 服务器端的 项目地址 以及 版本识别符

说明:
    1.所有的失败(错误)均需要处理,防止服务崩溃
    2.所有输入的文件夹路径前后不能有 '/'
    3.对于有ftp连接的需要在失败或者错误或者结束业务的时候关闭 避免超时报错,服务器崩溃
    4.针对回滚,考虑往数据库中存入数据,结合 uuid 识别项目
技术:nodejs + node-ftp

实现:
    1.下载远程代码
        通过命令node调取命令行实现下载(svn命令行需要安装subversion)
        依赖 child_process 实现命令行调取 exec = require('child_process').exec 
        异步执行,需要结合 promise 返回状态

        参数说明:    
            cmd 执行的命令行 (我执行多条命令的时候出错,然后没有去找解决的办法,妈的没有时间)
            {} 配置项。设置下载的超时时间,以及下载的buffer(小了可能报错)
            cb 回调处理
        exec(cmd, {
            encoding: 'utf8',
            timeout: 0,
            maxBuffer: 10000 * 1024, // 默认 200 * 1024
            killSignal: 'SIGTERM'
        }, function(err, stdout, stderr) {
            if (err) {
                console.log('下载文件%s出错%s',cmd,err)
            } else {
                cb()               
            }
        });

    2.读取本地文件(如果本地文件夹上传直接读取)

        //递归读取文件 
        //参数 path 读取的文件夹 相对路径是相对当前的服务(server index)路径,绝对路径带盘符
        function readFile(path) {
              files = fs.readdirSync(path); //需要用到同步读取
             files.forEach(walk);

              function walk(file) {
                states = fs.statSync(path + '/' + file);
                if (states.isDirectory()) {
                      readFile(path + '/' + file);
                } else {
                      _fileList.push(path + '/' + file);
                }
              }
        }
         try {
            readFile(path);
               resolve(fileList) //读取文件夹是同步api
          } catch (err) {
            reject(err)
          }


    3.实现ftp的远程连接
        需要依赖 node-ftp (npm install)
        Client = require('ftp')

        var ftp_client = new Client()
        ftp_client.on('ready', function() {
            console.log('ftp连接建立成功');
            //成功的执行
            cb(file_client)
        });
        ftp_client.on('error', function(err) {
            console.log('ftp连接建立失败',error);
            //失败的回调
            cb(err)
            return;
        });

   

    4.根据文件列表,远程创建(调用nodejs的api好像不能远程创建,他只能创建在该服务运行的电脑)
        使用ftp-node 的api
        参数:     ftp_client  ftp的实例
                fileList 文件列表
                serverPath 服务器端的相对目录
        还需要参数就是文件列表
        准备操作:通过文件列表,获得所有的文件夹路径;路径数组进行去重.
            hash快速去重:
                function uniqArray(arr) {
                    var hash = {},
                          len = arr.length,
                         result = [];

                    for (var i = 0; i < len; i++) {
                          if (!hash[arr[i]]) {
                            hash[arr[i]] = true;
                            result.push(arr[i]);
                          }
                    }
                    return result;
                }

        创建文件夹:

            var pre = new Promise(function(resolve, reject) {
                  // 建立相应的文件夹
                  async.each(fileList, function(item, cb) {
                    var _path = serverPath + "/" + item;
                    ftp_client.mkdir(_path, true, function(err) { //参数 true 表示已有文件夹不报错
                          if (err) {
                            console.log('创建文件夹%s出错%s', _path, err)
                            reject(err)
                         } else {
                               ++file_num;
                            if (file_len == file_num) { //这一步很重要!!! 分别是文件的总数量,和当前数量
                                 resolve()
                            }
                          }
                    });
                })
            })

    5.上传文件
        参数:     ftp_client  ftp的实例
                fileList 文件列表
                serverPath 服务器端的相对目录
        上传:
            var pre = new Promise(function(resolve, reject) {
                  async.each(fileList, function(item, cb) {
                    ftp_client.put(item, serverPath + "/" + item, function(err) {
                          if (err) {
                            console.log('上传文件%s出错:%s', item, err)
                               reject(err)
                          } else {
                                ++file_num;
                            if (file_len == file_num) { //同上,很重要
                                  resolve(ftp_client)
                            }
                          }
                    })
                  })
            })

    6.回滚实现
        很抱歉,暂时没有找到copy到指定文件夹的快捷方法
        暂时使用的是重命名,感觉挺快的。

        参数:
            /**
             * file_client
             * from 备份文件夹名称
             * to 项目文件夹名称
             */
        1.调用方法建立连接
        2.参照文件夹操作


配合的文件夹操作:
    依赖 fs 系统
    1.删除本地文件夹

        function deleteFloderLocal(path) {
            var files = [];
               if (fs.existsSync(path)) {
                files = fs.readdirSync(path)
                files.forEach(function(file, index) {
                    var curPath = path + "/" + file;
                    if (fs.statSync(curPath).isDirectory()) {
                        deleteFloderLocal(curPath);
                    } else {
                        fs.unlinkSync(curPath);
                    }
                });
                fs.rmdirSync(path);
            }
        }
        //判断本地文件夹是否存在
        function fsExistsSync(path) {
            try {
                fs.accessSync(path, fs.F_OK);
            } catch (e) {
                console.log(e)
                return false;
            }
            return true;
        }

    2.删除服务器端文件夹

        function deleteFloderServer(path, ftp_client) {
            var pre = new Promise(function(resolve, reject) {
                ftp_client.rmdir(path, true, function(err) { 
                    if (err) {
                        console.log('删除目录失败:', err);
                    };
                    resolve();
                    console.log('删除目录' + path + "成功!")
                })
            })
            return pre
        }

        说明:    1.rmdir true 参数说明文件夹非空时仍可以移除
                2.所有的不要含有中文,我再操作的时候含有中文报错了

    3.备份文件夹
        说白了就是重命名,很显然只能保留一个,此处的备份是考虑到上传文件可能破坏,以后回滚
        远程的,不要跟我讲node api
        function copyFloderServer(path,lab, ftp_client) {
            console.log('备份文件开始...')
            ftp_client.rename(path, path + lab, function(err) {
                if (err) {
                    console.log('备份目录失败:', err);
                    return
                };
                console.log('文件备份/恢复完成!')
            })
        }

说明:这个是临时接手node的一个需求,从0到0.1的新手还是有很多事情要做,很多地方考虑不周,敬请指出,感谢。

转载于:https://my.oschina.net/u/3565026/blog/1507349

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值