build.js 4.89 KB
/**
 * 本地项目构建
 *  clone >> build >> zip
 */

'use strict';

import sh from 'shelljs';
import moment from 'moment';
import path from 'path';
import fsp from 'fs-promise';
import fs from 'fs';
import config from '../../config/config';
import Tar from './tar';
import ws from '../../lib/ws';
import {
    Building
} from '../models';

class Build {

    constructor(project) {
        this.project = project;
        this.state = 'waiting building';
        this.silent = true;
    }

    build(branch) {
        if (typeof config.buildDir === 'undefined') {
            throw new Error('please config the buildDir');
        }
        this.branch = branch;
        let buildTime = moment().format('YYYYMMDDHHmmss')
        this.buildTime = buildTime;

        return {
            buildTime: buildTime,
            distFile: path.join(this.project.name, buildTime, `${this.project.name}.tar.gz`)
        };
    }

    run(bid) {
        this.bid = bid;
        let self = this;
        this._prebuild();
        this.startTime = (new Date()).getTime();
        return this._cloneCode(this.branch).then(() => {
            return self._buildScript();
        }).then(() => {
            self._zipBuild();
            self._state('success');
            let diff = (new Date()).getTime() - self.startTime;
            let costTime = moment.duration(diff, 'ms').humanize();
            self._log(`\n\n========== build success. cost: ${costTime} =======================`)
        }).catch((e) => {
            console.error(e);
            self._state('fail');
        });
    }

    get buildPath() {
        return path.join(config.buildDir, this.project.name, this.buildTime, this.project.name);
    }

    get rootPath() {
        return path.join(config.buildDir, this.project.name, this.buildTime);
    }

    /**
     * do some folder check
     */
    _prebuild() {
        if (!sh.test('-e', config.buildDir)) {
            sh.mkdir('-p', config.buildDir);
        }

        if (!sh.test('-e', this.rootPath)) {
            sh.mkdir('-p', this.rootPath);
        }
        this.logFile = path.join(this.rootPath, 'building.log');
        sh.touch(this.logFile);
    }

    _cloneCode(branch) {
        var self = this;
        this._state('cloning_code');
        let clone_script = `git clone -b ${branch} ${this.project.gitlab}`;
        this._log(`>>>>>>>>> ${clone_script} >>>>>>>>>>>`);

        return new Promise((reslove, reject) => {
            sh.cd(self.rootPath);
            let child = sh.exec(clone_script, {
                silent: self.silent,
                async: true
            });

            child.stdout.pipe(fs.createWriteStream(self.logFile, {
                flags: 'a'
            }));

            child.stderr.pipe(fs.createWriteStream(self.logFile, {
                flags: 'a'
            }));

            // child.stderr.on('data', (data) => {
            //     self._log(data);
            // });

            child.on('close', (code) => {
                if (code == 0) {
                    console.log('clone code success');
                    reslove();
                } else {
                    reject(new Error(`clone code fail`));
                }
            });
        });
    }

    _buildScript() {
        var self = this;
        this._log(`>>>>>>>>> ${this.project.scripts.build} >>>>>>>>>>>`);
        return new Promise((reslove, reject) => {
            self._state('script_building');
            sh.cd(self.buildPath);
            var child = sh.exec(self.project.scripts.build, {
                silent: self.silent,
                async: true
            });

            // child.stdout.on('data', (data) => {
            //     self._log(data);
            // });

            // child.stderr.on('data', (data) => {
            //     self._log(data);
            // });
            child.stdout.pipe(fs.createWriteStream(self.logFile, {
                flags: 'a'
            }));
            child.stderr.pipe(fs.createWriteStream(self.logFile, {
                flags: 'a'
            }));

            child.on('close', (code) => {
                if (code == 0) {
                    console.log('build success');
                    reslove();
                } else {
                    reject(new Error(`build code fail`));
                }
            });
        });
    }

    _zipBuild() {
        this._state('gziping');
        let target = this.buildPath;
        let dist = path.join(this.rootPath, `${this.project.name}.tar.gz`);
        return Tar.gzip(target, dist);
    }

    async _state(state) {
        ws.broadcast(`/building/${this.project._id}`, {
            bid: this.bid,
            state: state
        });
        this._log(`>>>>>>>>> ${state} >>>>>>>>>>>`);
        await Building.updateState(this.bid, state);
    }

    _log(line) {
        // ws.broadcast(`/building/${this.project._id}/log`, line);
        fsp.appendFile(this.logFile, line + '\n');
    }
}

export default Build;