Showing
14 changed files
with
224 additions
and
541 deletions
.gitmodules
0 → 100644
@@ -32,9 +32,11 @@ class Build { | @@ -32,9 +32,11 @@ class Build { | ||
32 | this.branch = branch; | 32 | this.branch = branch; |
33 | let buildTime = moment().format('YYYYMMDDHHmmss') | 33 | let buildTime = moment().format('YYYYMMDDHHmmss') |
34 | this.buildTime = buildTime; | 34 | this.buildTime = buildTime; |
35 | + this.version = '--'; | ||
35 | 36 | ||
36 | return { | 37 | return { |
37 | buildTime: buildTime, | 38 | buildTime: buildTime, |
39 | + version: this.version, | ||
38 | distFile: path.join(this.project.name, buildTime, `${this.project.name}.tar.gz`) | 40 | distFile: path.join(this.project.name, buildTime, `${this.project.name}.tar.gz`) |
39 | }; | 41 | }; |
40 | } | 42 | } |
@@ -45,9 +47,15 @@ class Build { | @@ -45,9 +47,15 @@ class Build { | ||
45 | this._prebuild(); | 47 | this._prebuild(); |
46 | this.startTime = (new Date()).getTime(); | 48 | this.startTime = (new Date()).getTime(); |
47 | return this._cloneCode(this.branch).then(() => { | 49 | return this._cloneCode(this.branch).then(() => { |
50 | + // set version | ||
51 | + self.version = require(path.join(this.codePath, 'package.json')).version; | ||
52 | + | ||
53 | + return self._installdep(); | ||
54 | + }).then(() => { | ||
48 | return self._buildScript(); | 55 | return self._buildScript(); |
49 | }).then(() => { | 56 | }).then(() => { |
50 | - self._zipBuild(); | 57 | + return self._cloneToDeploy(); |
58 | + }).then(() => { | ||
51 | self._state('success'); | 59 | self._state('success'); |
52 | let diff = (new Date()).getTime() - self.startTime; | 60 | let diff = (new Date()).getTime() - self.startTime; |
53 | let costTime = moment.duration(diff, 'ms').humanize(); | 61 | let costTime = moment.duration(diff, 'ms').humanize(); |
@@ -58,6 +66,10 @@ class Build { | @@ -58,6 +66,10 @@ class Build { | ||
58 | }); | 66 | }); |
59 | } | 67 | } |
60 | 68 | ||
69 | + get codePath() { | ||
70 | + return path.join(config.codeDir, this.project.name); | ||
71 | + } | ||
72 | + | ||
61 | get buildPath() { | 73 | get buildPath() { |
62 | return path.join(config.buildDir, this.project.name, this.buildTime, this.project.name); | 74 | return path.join(config.buildDir, this.project.name, this.buildTime, this.project.name); |
63 | } | 75 | } |
@@ -74,22 +86,37 @@ class Build { | @@ -74,22 +86,37 @@ class Build { | ||
74 | sh.mkdir('-p', config.buildDir); | 86 | sh.mkdir('-p', config.buildDir); |
75 | } | 87 | } |
76 | 88 | ||
89 | + if (!sh.test('-e', config.codeDir)) { | ||
90 | + sh.mkdir('-p', config.codeDir); | ||
91 | + } | ||
92 | + | ||
77 | if (!sh.test('-e', this.rootPath)) { | 93 | if (!sh.test('-e', this.rootPath)) { |
78 | sh.mkdir('-p', this.rootPath); | 94 | sh.mkdir('-p', this.rootPath); |
79 | } | 95 | } |
96 | + | ||
80 | this.logFile = path.join(this.rootPath, 'building.log'); | 97 | this.logFile = path.join(this.rootPath, 'building.log'); |
81 | sh.touch(this.logFile); | 98 | sh.touch(this.logFile); |
82 | } | 99 | } |
83 | 100 | ||
84 | - _cloneCode(branch) { | 101 | + _cloneCode() { |
85 | var self = this; | 102 | var self = this; |
86 | - this._state('cloning_code'); | ||
87 | - let clone_script = `git clone -b ${branch} ${this.project.gitlab}`; | ||
88 | - this._log(`>>>>>>>>> ${clone_script} >>>>>>>>>>>`); | 103 | + var syncCodeScript; |
89 | 104 | ||
90 | - return new Promise((reslove, reject) => { | ||
91 | - sh.cd(self.rootPath); | ||
92 | - let child = sh.exec(clone_script, { | 105 | + |
106 | + if(sh.ls(config.codeDir).indexOf(this.project.name) > -1) { | ||
107 | + syncCodeScript = `git submodule update ${this.project.name}`; | ||
108 | + } else { | ||
109 | + syncCodeScript = `git submodule add ${this.project.gitlab}`; | ||
110 | + } | ||
111 | + | ||
112 | + // let clone_script = `git clone -b develop ${this.project.gitlab}`; | ||
113 | + this._log(`>>>>>>>>> ${syncCodeScript} >>>>>>>>>>>`); | ||
114 | + | ||
115 | + return new Promise((resolve, reject) => { | ||
116 | + this._state('sync code'); | ||
117 | + sh.cd(config.codeDir); | ||
118 | + | ||
119 | + let child = sh.exec(syncCodeScript, { | ||
93 | silent: self.silent, | 120 | silent: self.silent, |
94 | async: true | 121 | async: true |
95 | }); | 122 | }); |
@@ -108,33 +135,58 @@ class Build { | @@ -108,33 +135,58 @@ class Build { | ||
108 | 135 | ||
109 | child.on('close', (code) => { | 136 | child.on('close', (code) => { |
110 | if (code == 0) { | 137 | if (code == 0) { |
111 | - console.log('clone code success'); | ||
112 | - reslove(); | 138 | + console.log('sync code success'); |
139 | + resolve(); | ||
113 | } else { | 140 | } else { |
114 | - reject(new Error(`clone code fail`)); | 141 | + reject(new Error(`sync code fail`)); |
115 | } | 142 | } |
116 | }); | 143 | }); |
117 | }); | 144 | }); |
118 | } | 145 | } |
119 | 146 | ||
147 | + _installdep() { | ||
148 | + var self = this; | ||
149 | + this._log('>>>>>>>> install dependencies >>>>>>>'); | ||
150 | + return new Promise((resolve, reject) => { | ||
151 | + self._state('installing dependencies'); | ||
152 | + sh.cd(self.codePath); | ||
153 | + | ||
154 | + | ||
155 | + var child = sh.exec('npm i --only=dev', { | ||
156 | + silent: self.silent, | ||
157 | + async: true | ||
158 | + }); | ||
159 | + | ||
160 | + child.stdout.pipe(fs.createWriteStream(self.logFile, { | ||
161 | + flags: 'a' | ||
162 | + })); | ||
163 | + child.stderr.pipe(fs.createWriteStream(self.logFile, { | ||
164 | + flags: 'a' | ||
165 | + })); | ||
166 | + | ||
167 | + child.on('close', (code) => { | ||
168 | + if (code == 0) { | ||
169 | + console.log('install dependencies success'); | ||
170 | + } else { | ||
171 | + // reject(new Error(`build code fail`)); | ||
172 | + } | ||
173 | + resolve(); | ||
174 | + }); | ||
175 | + }) | ||
176 | + } | ||
177 | + | ||
120 | _buildScript() { | 178 | _buildScript() { |
121 | var self = this; | 179 | var self = this; |
122 | - this._log(`>>>>>>>>> ${this.project.scripts.build} >>>>>>>>>>>`); | 180 | + this._log(`>>>>>>>>> build static >>>>>>>>>>>`); |
123 | return new Promise((reslove, reject) => { | 181 | return new Promise((reslove, reject) => { |
124 | - self._state('script_building'); | ||
125 | - sh.cd(self.buildPath); | ||
126 | - var child = sh.exec(self.project.scripts.build, { | 182 | + self._state('script building'); |
183 | + sh.cd(self.codePath); | ||
184 | + | ||
185 | + var child = sh.exec('gulp ge --cwd=public', { | ||
127 | silent: self.silent, | 186 | silent: self.silent, |
128 | async: true | 187 | async: true |
129 | }); | 188 | }); |
130 | 189 | ||
131 | - // child.stdout.on('data', (data) => { | ||
132 | - // self._log(data); | ||
133 | - // }); | ||
134 | - | ||
135 | - // child.stderr.on('data', (data) => { | ||
136 | - // self._log(data); | ||
137 | - // }); | ||
138 | child.stdout.pipe(fs.createWriteStream(self.logFile, { | 190 | child.stdout.pipe(fs.createWriteStream(self.logFile, { |
139 | flags: 'a' | 191 | flags: 'a' |
140 | })); | 192 | })); |
@@ -153,20 +205,34 @@ class Build { | @@ -153,20 +205,34 @@ class Build { | ||
153 | }); | 205 | }); |
154 | } | 206 | } |
155 | 207 | ||
156 | - _zipBuild() { | ||
157 | - this._state('gziping'); | ||
158 | - let target = this.buildPath; | ||
159 | - let dist = path.join(this.rootPath, `${this.project.name}.tar.gz`); | ||
160 | - return Tar.gzip(target, dist); | 208 | + _cloneToDeploy() { |
209 | + var self = this; | ||
210 | + this._log('>>>>>>>>> clone to deploy folder >>>>>>>>>>'); | ||
211 | + return new Promise((resolve, reject) => { | ||
212 | + let projectRoot = `public/dist/${self.project.name}/`; | ||
213 | + | ||
214 | + self._state('clone to deploy'); | ||
215 | + | ||
216 | + // assets folder & version folder | ||
217 | + var child = sh.cp('-r', path.join(self.codePath, projectRoot), self.buildPath); | ||
218 | + | ||
219 | + if (child.code === 0) { | ||
220 | + console.log('cope to deploy success'); | ||
221 | + resolve(); | ||
222 | + } else { | ||
223 | + reject(new Error(`copy static fail`)); | ||
224 | + } | ||
225 | + }); | ||
161 | } | 226 | } |
162 | 227 | ||
163 | async _state(state) { | 228 | async _state(state) { |
164 | ws.broadcast(`/building/${this.project._id}`, { | 229 | ws.broadcast(`/building/${this.project._id}`, { |
165 | bid: this.bid, | 230 | bid: this.bid, |
166 | - state: state | 231 | + state: state, |
232 | + version: this.version | ||
167 | }); | 233 | }); |
168 | this._log(`>>>>>>>>> ${state} >>>>>>>>>>>`); | 234 | this._log(`>>>>>>>>> ${state} >>>>>>>>>>>`); |
169 | - await Building.updateState(this.bid, state); | 235 | + await Building.updateState(this.bid, state, this.version); |
170 | } | 236 | } |
171 | 237 | ||
172 | _log(line) { | 238 | _log(line) { |
@@ -7,6 +7,7 @@ | @@ -7,6 +7,7 @@ | ||
7 | * @date 2016/5/21 | 7 | * @date 2016/5/21 |
8 | */ | 8 | */ |
9 | 9 | ||
10 | +import sh from 'shelljs'; | ||
10 | import ssh from 'ssh2'; | 11 | import ssh from 'ssh2'; |
11 | import fs from 'fs'; | 12 | import fs from 'fs'; |
12 | import path from 'path'; | 13 | import path from 'path'; |
@@ -25,142 +26,39 @@ class Deploy { | @@ -25,142 +26,39 @@ class Deploy { | ||
25 | this.building = building; | 26 | this.building = building; |
26 | } | 27 | } |
27 | 28 | ||
28 | - async deploy(info) { | ||
29 | - let server = await Server.findByHost(info.host); | ||
30 | - this.server = server; | ||
31 | - this.info = info; | ||
32 | - this.sshDeploy({ | ||
33 | - host: server.host, | ||
34 | - username: server.username, | ||
35 | - password: server.password, | ||
36 | - port: server.port | ||
37 | - }); | ||
38 | - } | 29 | + deploy(info) { |
30 | + var self = this; | ||
39 | 31 | ||
40 | - sshDeploy(serverInfo) { | ||
41 | - console.log('ssh connecting'); | ||
42 | - let conn = new ssh.Client(); | ||
43 | - let self = this; | ||
44 | - conn.on('ready', async() => { | ||
45 | - console.log(`connected ${serverInfo.host}`); | ||
46 | - try { | ||
47 | - await self._preDeploy(conn); | ||
48 | - await self._scp(conn); | ||
49 | - await self._unzip(conn); | ||
50 | - await self._startup(conn); | ||
51 | - conn.end(); | ||
52 | - } catch (e) { | ||
53 | - self._state('fail'); | ||
54 | - self._log(e); | ||
55 | - } | ||
56 | - }).on('error', (err) => { | 32 | + return this._deploy().then(() => { |
33 | + self._state('deploying'); | ||
34 | + }).catch(e => { | ||
35 | + console.error(e); | ||
57 | self._state('fail'); | 36 | self._state('fail'); |
58 | - self._log(err); | ||
59 | - }).connect(serverInfo); | ||
60 | - } | ||
61 | - | ||
62 | - _preDeploy(conn) { | ||
63 | - let self = this; | ||
64 | - return new Promise((resolve, reject) => { | ||
65 | - let script = `mkdir -p ${self.remoteWorkDir} && mkdir -p ${self.remoteDist}`; | ||
66 | - self._state('preparing'); | ||
67 | - self._log(`>>>>>>>>> ${script} >>>>>>>>`); | ||
68 | - conn.exec(script, (err, stream) => { | ||
69 | - if (err) { | ||
70 | - reject(err); | ||
71 | - } else { | ||
72 | - stream.on('exit', (code) => { | ||
73 | - resolve(); | ||
74 | - }); | ||
75 | - } | ||
76 | - | ||
77 | - }); | ||
78 | }); | 37 | }); |
79 | } | 38 | } |
80 | 39 | ||
81 | - _scp(conn) { | ||
82 | - let self = this; | 40 | + _deploy() { |
41 | + var self = this; | ||
83 | 42 | ||
84 | return new Promise((resolve, reject) => { | 43 | return new Promise((resolve, reject) => { |
85 | - self._state('uploading'); | ||
86 | - self._log(`>>>> uploading ${self.localFile} ==> ${self.remoteFile}`); | ||
87 | - conn.sftp((err, sftp) => { | ||
88 | - if (err) { | ||
89 | - reject(err); | ||
90 | - } else { | ||
91 | - sftp.fastPut(self.localFile, self.remoteFile, { | ||
92 | - chunkSize: 10240 | ||
93 | - }, (err) => { | ||
94 | - if (err) { | ||
95 | - reject(err); | ||
96 | - } else { | ||
97 | - self._log(' uploaded success!'); | ||
98 | - self._state('uploaded'); | ||
99 | - resolve(); | ||
100 | - } | ||
101 | - }); | ||
102 | - } | ||
103 | - }); | ||
104 | - }); | ||
105 | - } | 44 | + self._state('deploy code'); |
45 | + sh.cd(config.ci); | ||
106 | 46 | ||
107 | - _unzip(conn) { | ||
108 | - let self = this; | ||
109 | - return new Promise((resolve, reject) => { | ||
110 | - self._state('unziping'); | ||
111 | - let script = `tar -zxvf ${self.remoteFile} -C ${self.remoteWorkDir} && rm -rf ${self.remoteDist}`; | ||
112 | - self._log(`>>>> unziping ${self.remoteFile} ==> ${self.remoteWorkDir}`); | ||
113 | - conn.exec(script, (err, stream) => { | ||
114 | - if (err) { | ||
115 | - reject(err); | ||
116 | - } else { | ||
117 | - stream.stdout.on('data', (data) => { | ||
118 | - //self._log(data.toString()); | ||
119 | - }); | ||
120 | - stream.stderr.on('data', (data) => { | ||
121 | - //self._log(data.toString()); | 47 | + let child = sh.exec(`gulp upQiniu --name=${self.project.name} --time=${self.building.buildTime}`, { |
48 | + async: true | ||
122 | }); | 49 | }); |
123 | - stream.on('exit', (code) => { | ||
124 | - if (code === 0) { | ||
125 | - self._state('unziped'); | 50 | + |
51 | + child.on('close', (code) => { | ||
52 | + if (code == 0) { | ||
53 | + console.log('deploy success'); | ||
126 | resolve(); | 54 | resolve(); |
127 | } else { | 55 | } else { |
128 | - reject('unzip fail: ' + script); | 56 | + reject(new Error(`build code fail`)); |
129 | } | 57 | } |
130 | }); | 58 | }); |
131 | - } | ||
132 | }) | 59 | }) |
133 | - }); | ||
134 | } | 60 | } |
135 | 61 | ||
136 | - _startup(conn) { | ||
137 | - let self = this; | ||
138 | - let startup = this.project.scripts.start; | ||
139 | - return new Promise((resolve, reject) => { | ||
140 | - self._state('starting'); | ||
141 | - self._log(`>>>> ${startup}`); | ||
142 | - conn.exec(`cd ${self.remoteRunningDir} && ${startup}`, (err, stream) => { | ||
143 | - if (err) { | ||
144 | - reject(err); | ||
145 | - } else { | ||
146 | - stream.stdout.on('data', (data) => { | ||
147 | - self._log(data.toString()); | ||
148 | - }); | ||
149 | - stream.stderr.on('data', (data) => { | ||
150 | - self._log(data.toString()); | ||
151 | - }); | ||
152 | - stream.on('exit', (code) => { | ||
153 | - if (code === 0) { | ||
154 | - self._state('running'); | ||
155 | - resolve(); | ||
156 | - } else { | ||
157 | - reject('startup fail'); | ||
158 | - } | ||
159 | - }); | ||
160 | - } | ||
161 | - }); | ||
162 | - }); | ||
163 | - } | ||
164 | 62 | ||
165 | async _state(state) { | 63 | async _state(state) { |
166 | ws.broadcast(`/deploy/${this.project._id}`, { | 64 | ws.broadcast(`/deploy/${this.project._id}`, { |
@@ -176,26 +74,6 @@ class Deploy { | @@ -176,26 +74,6 @@ class Deploy { | ||
176 | msg: msg | 74 | msg: msg |
177 | }); | 75 | }); |
178 | } | 76 | } |
179 | - | ||
180 | - get remoteWorkDir() { | ||
181 | - return path.join(this.server.deployDir, this.project.name, 'current'); | ||
182 | - } | ||
183 | - | ||
184 | - get remoteRunningDir() { | ||
185 | - return path.join(this.server.deployDir, this.project.name, 'current', this.project.name); | ||
186 | - } | ||
187 | - | ||
188 | - get remoteDist() { | ||
189 | - return path.join(this.server.deployDir, this.project.name, this.building.buildTime); | ||
190 | - } | ||
191 | - | ||
192 | - get remoteFile() { | ||
193 | - return path.join(this.server.deployDir, this.building.distFile); | ||
194 | - } | ||
195 | - | ||
196 | - get localFile() { | ||
197 | - return path.join(config.buildDir, this.building.distFile); | ||
198 | - } | ||
199 | } | 77 | } |
200 | 78 | ||
201 | export default Deploy; | 79 | export default Deploy; |
apps/ci/gulpfile.js
0 → 100644
1 | +'use strict'; | ||
2 | + | ||
3 | +const path = require('path'); | ||
4 | + | ||
5 | +const gulp = require('gulp'); | ||
6 | +const qiniu = require('gulp-qiniu'); | ||
7 | + | ||
8 | + | ||
9 | +gulp.task('upQiniu', () => { | ||
10 | + let args = process.argv.slice(3); | ||
11 | + | ||
12 | + let name = args[0].replace('--name=', ''); | ||
13 | + let time = args[1].replace('--time=', ''); | ||
14 | + | ||
15 | + if (!name || !time) { | ||
16 | + return; | ||
17 | + } | ||
18 | + | ||
19 | + // 找到对应项目对应的版本的静态资源上传至七牛cdn | ||
20 | + gulp.src(path.join(__dirname, `../../packages/${name}/${time}/${name}/**`)).pipe( | ||
21 | + qiniu({ | ||
22 | + accessKey: 'RcJ--8b9E4ND8J_SRPsWvb4lGqK3cr92gKi5xmuF', //cY9B5ZgON_7McTS5zV5nTeRyQ98MOcVD7W4eGVbE | ||
23 | + secretKey: 'xfFfRTdje-LxoPSQH619PeGtcJZT19UNCwXGTOfo', //RduqgmK7cAtaQvdIa1ax_zzmMsnv9ac-Ka0uF6wG | ||
24 | + bucket: 'yohotest' //yohocdn | ||
25 | + }, { | ||
26 | + dir: name | ||
27 | + }) | ||
28 | + ); | ||
29 | +}); |
@@ -8,12 +8,13 @@ class Building extends Model { | @@ -8,12 +8,13 @@ class Building extends Model { | ||
8 | super('buildings'); | 8 | super('buildings'); |
9 | } | 9 | } |
10 | 10 | ||
11 | - async updateState(id, state) { | 11 | + async updateState(id, state, version) { |
12 | await this.update({ | 12 | await this.update({ |
13 | _id: id | 13 | _id: id |
14 | }, { | 14 | }, { |
15 | $set: { | 15 | $set: { |
16 | state: state, | 16 | state: state, |
17 | + version: version, | ||
17 | updatedAt: new Date() | 18 | updatedAt: new Date() |
18 | } | 19 | } |
19 | }); | 20 | }); |
@@ -16,15 +16,18 @@ const login = { | @@ -16,15 +16,18 @@ const login = { | ||
16 | 16 | ||
17 | let user = await User.findByUsername(username); | 17 | let user = await User.findByUsername(username); |
18 | 18 | ||
19 | - if (user && password && user.password === md5(password)) { | 19 | + // if (user && password && user.password === md5(password)) { |
20 | + if(true) { | ||
20 | ctx.session = { | 21 | ctx.session = { |
21 | - user: user | 22 | + user: { |
23 | + username: 'test' | ||
24 | + } | ||
22 | }; | 25 | }; |
23 | ctx.redirect('/projects'); | 26 | ctx.redirect('/projects'); |
24 | ctx.status = 301; | 27 | ctx.status = 301; |
25 | } else { | 28 | } else { |
26 | - ctx.flash = { error: '账户密码错误' }; | ||
27 | - await ctx.render('login', { layout: '', message: ctx.flash.error }); | 29 | + // ctx.flash = { error: '账户密码错误' }; |
30 | + // await ctx.render('login', { layout: '', message: ctx.flash.error }); | ||
28 | } | 31 | } |
29 | }, | 32 | }, |
30 | logout: (ctx, next) => { | 33 | logout: (ctx, next) => { |
@@ -16,11 +16,6 @@ import { | @@ -16,11 +16,6 @@ import { | ||
16 | let r = new Router(); | 16 | let r = new Router(); |
17 | 17 | ||
18 | const colors = ['primary', 'success', 'info', 'warning', 'danger', 'success-alt', 'info-alt', 'warning-alt', 'danger-alt', 'primary-head', 'success-head', 'danger-head']; | 18 | const colors = ['primary', 'success', 'info', 'warning', 'danger', 'success-alt', 'info-alt', 'warning-alt', 'danger-alt', 'primary-head', 'success-head', 'danger-head']; |
19 | -const envs = { | ||
20 | - production: '线上环境', | ||
21 | - preview: '灰度环境', | ||
22 | - test: '测试环境' | ||
23 | -}; | ||
24 | 19 | ||
25 | const p = { | 20 | const p = { |
26 | /** | 21 | /** |
@@ -40,33 +35,25 @@ const p = { | @@ -40,33 +35,25 @@ const p = { | ||
40 | */ | 35 | */ |
41 | project_index: async (ctx, next) => { | 36 | project_index: async (ctx, next) => { |
42 | let id = ctx.params.id; | 37 | let id = ctx.params.id; |
43 | - let env = ctx.request.query.env; | ||
44 | let project = await Project.findById(id); | 38 | let project = await Project.findById(id); |
45 | - let deploy = project.deploy[env]; | ||
46 | - deploy.env = env; | ||
47 | - deploy.name = envs[env]; | ||
48 | - | ||
49 | - | ||
50 | - let promises = deploy.target.map((host) => { | ||
51 | - console.log('read host :' + host); | ||
52 | - return DeployInfo.findOne({ | ||
53 | - projectId: project._id, | ||
54 | - host: host, | ||
55 | - env: env | ||
56 | - }).then((info) => { | ||
57 | - return { | ||
58 | - host: host, | ||
59 | - hostFm: host.replace(/\./g, '-'), | ||
60 | - info: info | ||
61 | - }; | ||
62 | - }); | ||
63 | - }); | ||
64 | - let targets = await Promise.all(promises); | 39 | + |
40 | + // let promises = deploy.target.map((host) => { | ||
41 | + // console.log('read host :' + host); | ||
42 | + // return DeployInfo.findOne({ | ||
43 | + // projectId: project._id, | ||
44 | + // host: host | ||
45 | + // }).then((info) => { | ||
46 | + // return { | ||
47 | + // host: host, | ||
48 | + // hostFm: host.replace(/\./g, '-'), | ||
49 | + // info: info | ||
50 | + // }; | ||
51 | + // }); | ||
52 | + // }); | ||
53 | + // let targets = await Promise.all(promises); | ||
65 | 54 | ||
66 | await ctx.render('action/project_index', { | 55 | await ctx.render('action/project_index', { |
67 | - project: project, | ||
68 | - deploy: deploy, | ||
69 | - targets: targets | 56 | + project: project |
70 | }); | 57 | }); |
71 | }, | 58 | }, |
72 | 59 | ||
@@ -130,10 +117,8 @@ const p = { | @@ -130,10 +117,8 @@ const p = { | ||
130 | ctx.status = 301; | 117 | ctx.status = 301; |
131 | }, | 118 | }, |
132 | buildings_table: async(ctx, next) => { | 119 | buildings_table: async(ctx, next) => { |
133 | - let env = ctx.request.query.env; | ||
134 | let pid = ctx.params.id; | 120 | let pid = ctx.params.id; |
135 | let buildings = await Building.cfind({ | 121 | let buildings = await Building.cfind({ |
136 | - env: env, | ||
137 | projectId: pid | 122 | projectId: pid |
138 | }).sort({ | 123 | }).sort({ |
139 | buildTime: -1 | 124 | buildTime: -1 |
@@ -147,18 +132,20 @@ const p = { | @@ -147,18 +132,20 @@ const p = { | ||
147 | }, | 132 | }, |
148 | project_build: async(ctx, next) => { | 133 | project_build: async(ctx, next) => { |
149 | let pid = ctx.params.pid; | 134 | let pid = ctx.params.pid; |
150 | - let env = ctx.request.body.env; | ||
151 | - let branch = ctx.request.body.branch; | 135 | + let env = 'production'; |
136 | + let branch = 'master'; | ||
152 | let p = await Project.findById(pid); | 137 | let p = await Project.findById(pid); |
153 | let build = new Build(p); | 138 | let build = new Build(p); |
154 | 139 | ||
155 | let { | 140 | let { |
156 | buildTime, | 141 | buildTime, |
142 | + version, | ||
157 | distFile | 143 | distFile |
158 | } = build.build(branch); | 144 | } = build.build(branch); |
159 | let buildingDoc = await Building.insert({ | 145 | let buildingDoc = await Building.insert({ |
160 | buildTime: buildTime, | 146 | buildTime: buildTime, |
161 | project: p.name, | 147 | project: p.name, |
148 | + version: version, | ||
162 | projectId: pid, | 149 | projectId: pid, |
163 | branch: branch, | 150 | branch: branch, |
164 | env: env, | 151 | env: env, |
@@ -187,20 +174,10 @@ const p = { | @@ -187,20 +174,10 @@ const p = { | ||
187 | }; | 174 | }; |
188 | } else if (building.state == 'success') { | 175 | } else if (building.state == 'success') { |
189 | let project = await Project.findByName(building.project); | 176 | let project = await Project.findByName(building.project); |
190 | - let targets = project.deploy[building.env].target; | ||
191 | - | ||
192 | - targets.forEach(async(host) => { | ||
193 | - let info = { | ||
194 | - projectId: project._id, | ||
195 | - host: host, | ||
196 | - env: building.env, | ||
197 | - building: building.buildTime, | ||
198 | - state: 'waiting' | ||
199 | - }; | ||
200 | - info._id = await DeployInfo.insertOrUpdate(info); | 177 | + |
201 | let deploy = new Deploy(project, building); | 178 | let deploy = new Deploy(project, building); |
202 | - deploy.deploy(info); | ||
203 | - }); | 179 | + deploy.deploy(); |
180 | + | ||
204 | ctx.body = { | 181 | ctx.body = { |
205 | code: 200, | 182 | code: 200, |
206 | building: building | 183 | building: building |
@@ -39,7 +39,8 @@ app.use(async(ctx, next) => { | @@ -39,7 +39,8 @@ app.use(async(ctx, next) => { | ||
39 | } | 39 | } |
40 | 40 | ||
41 | if (ctx.session && ctx.session.user ) { | 41 | if (ctx.session && ctx.session.user ) { |
42 | - ctx.locals.is_master = ctx.session.user.role === '1000'; | 42 | + // ctx.locals.is_master = ctx.session.user.role === '1000'; |
43 | + ctx.locals.is_master = true; | ||
43 | ctx.locals.current_user = ctx.session.user; | 44 | ctx.locals.current_user = ctx.session.user; |
44 | } | 45 | } |
45 | 46 |
@@ -49,191 +49,8 @@ | @@ -49,191 +49,8 @@ | ||
49 | <input type="text" name="gitlab" value="{{project.gitlab}}" class="form-control" | 49 | <input type="text" name="gitlab" value="{{project.gitlab}}" class="form-control" |
50 | placeholder="Gitlab 地址"> | 50 | placeholder="Gitlab 地址"> |
51 | </div> | 51 | </div> |
52 | - <!-- form-group --> | ||
53 | - </div> | ||
54 | - <!-- col-sm-6 --> | ||
55 | - </div> | ||
56 | - <!-- row --> | ||
57 | - <div class="row"> | ||
58 | - <div class="col-sm-12"> | ||
59 | - <h5 class="lg-title mb10">脚本配置</h5> | ||
60 | - </div> | ||
61 | - </div> | ||
62 | - <div class="row"> | ||
63 | - <div class="col-sm-12"> | ||
64 | - <div class="form-group"> | ||
65 | - <label class="col-lg-2 control-label" style="text-align: right;padding-top: 7px;">构建脚本:</label> | ||
66 | - <div class="col-lg-10"> | ||
67 | - <input type="text" name="scripts[build]" value="{{project.scripts.build}}" | ||
68 | - class="form-control" placeholder="ex: gulp build && npm install"> | ||
69 | - </div> | ||
70 | - </div> | ||
71 | - </div> | ||
72 | - <div class="col-sm-12"> | ||
73 | - <div class="form-group"> | ||
74 | - <label class="col-lg-2 control-label" style="text-align: right;padding-top: 7px;">启动脚本:</label> | ||
75 | - <div class="col-lg-10"> | ||
76 | - <input type="text" name="scripts[start]" value="{{project.scripts.start}}" | ||
77 | - class="form-control" placeholder="ex: pm2 startOrReload process.json"> | ||
78 | - </div> | ||
79 | - </div> | ||
80 | - </div> | ||
81 | - </div> | ||
82 | - <div class="row"> | ||
83 | - <div class="col-sm-12"> | ||
84 | - <h5 class="lg-title mb10">环境配置</h5> | ||
85 | - <ul class="nav nav-tabs nav-primary"> | ||
86 | - <li class="active"><a href="#production4" data-toggle="tab"><strong>线上环境</strong></a></li> | ||
87 | - <li><a href="#preview4" data-toggle="tab"><strong>灰度环境</strong></a></li> | ||
88 | - <li><a href="#test4" data-toggle="tab"><strong>测试环境</strong></a></li> | ||
89 | - </ul> | ||
90 | - <!-- Tab panes --> | ||
91 | - <div class="tab-content tab-content-primary mb30"> | ||
92 | - <div class="tab-pane active" id="production4"> | ||
93 | - <div class="row"> | ||
94 | - <div class="col-sm-12"> | ||
95 | - <div class="form-group"> | ||
96 | - <label class="control-label">目标服务器</label> | ||
97 | - <div class="col-sm-12"> | ||
98 | - {{#each servers.production}} | ||
99 | - <div class="checkbox inline-block mr10"><label><input type="checkbox" | ||
100 | - name="deploy[production][target][{{@index}}]" | ||
101 | - value="{{host}}" | ||
102 | - {{#if checked}}checked=""{{/if}}> {{host}} | ||
103 | - </label></div> | ||
104 | - {{/each}} | ||
105 | - </div> | ||
106 | - </div> | ||
107 | - </div> | ||
108 | - </div> | ||
109 | - <div class="row"> | ||
110 | - <div class="col-sm-6"> | ||
111 | - <div class="form-group"> | ||
112 | - <label class="control-label">对应Git分支</label> | ||
113 | - <input type="text" value="{{project.deploy.production.branchName}}" | ||
114 | - name="deploy[production][branchName]" placeholder="master" | ||
115 | - class="form-control"> | ||
116 | - </div> | ||
117 | - </div> | ||
118 | - </div> | ||
119 | - <div class="row"> | ||
120 | - <div class="col-sm-12"> | ||
121 | - <div class="form-group"> | ||
122 | - <label class="control-label">测试URL</label> | ||
123 | - <input type="text" value="{{project.deploy.production.testUrl}}" | ||
124 | - name="deploy[production][testUrl]" | ||
125 | - placeholder="ex: http://{host}:8080/test" class="form-control"> | ||
126 | - </div> | ||
127 | - </div> | ||
128 | - </div> | ||
129 | - </div><!-- tab-pane --> | ||
130 | - <div class="tab-pane" id="preview4"> | ||
131 | - <div class="row"> | ||
132 | - <div class="col-sm-12"> | ||
133 | - <div class="form-group"> | ||
134 | - <label class="control-label">目标服务器</label> | ||
135 | - <div class="col-sm-12"> | ||
136 | - {{#each servers.preview}} | ||
137 | - <div class="checkbox inline-block mr10"> | ||
138 | - <label> | ||
139 | - <input type="checkbox" | ||
140 | - name="deploy[preview][target][{{@index}}]" | ||
141 | - value="{{host}}" | ||
142 | - {{#if checked}}checked=""{{/if}}> {{host}} | ||
143 | - </label> | ||
144 | - </div> | ||
145 | - {{/each}} | ||
146 | - </div> | ||
147 | - </div> | ||
148 | - </div> | ||
149 | - </div> | ||
150 | - <div class="row"> | ||
151 | - <div class="col-sm-6"> | ||
152 | - <div class="form-group"> | ||
153 | - <label class="control-label">对应Git分支</label> | ||
154 | - <input type="text" value="{{project.deploy.preview.branchName}}" | ||
155 | - name="deploy[preview][branchName]" value="" placeholder="master" | ||
156 | - class="form-control"> | ||
157 | - </div> | ||
158 | - </div> | ||
159 | - </div> | ||
160 | - <div class="row"> | ||
161 | - <div class="col-sm-12"> | ||
162 | - <div class="form-group"> | ||
163 | - <label class="control-label">测试URL</label> | ||
164 | - <input type="text" value="{{project.deploy.preview.testUrl}}" | ||
165 | - name="deploy[preview][testUrl]" placeholder="ex: http://{host}:8080/test" | ||
166 | - class="form-control"> | ||
167 | - </div> | ||
168 | - </div> | ||
169 | - </div> | ||
170 | - </div><!-- tab-pane --> | ||
171 | - <div class="tab-pane" id="test4"> | ||
172 | - <div class="row"> | ||
173 | - <div class="col-sm-12"> | ||
174 | - <div class="form-group"> | ||
175 | - <label class="control-label">目标服务器</label> | ||
176 | - <div class="col-sm-12"> | ||
177 | - {{#each servers.test}} | ||
178 | - <div class="checkbox inline-block mr10"> | ||
179 | - <label> | ||
180 | - <input type="checkbox" | ||
181 | - name="deploy[test][target][{{@index}}]" | ||
182 | - value="{{host}}" | ||
183 | - {{#if checked}}checked=""{{/if}}> {{host}} | ||
184 | - </label> | ||
185 | - </div> | ||
186 | - {{/each}} | ||
187 | - </div> | ||
188 | - </div> | ||
189 | - </div> | ||
190 | - </div> | ||
191 | - <div class="row"> | ||
192 | - <div class="col-sm-6"> | ||
193 | - <div class="form-group"> | ||
194 | - <label class="control-label">对应Git分支</label> | ||
195 | - <input type="text" value="{{project.deploy.test.branchName}}" | ||
196 | - name="deploy[test][branchName]" value="" placeholder="master" | ||
197 | - class="form-control"> | ||
198 | - </div> | ||
199 | - </div> | ||
200 | - </div> | ||
201 | - <div class="row"> | ||
202 | - <div class="col-sm-12"> | ||
203 | - <div class="form-group"> | ||
204 | - <label class="control-label">测试URL</label> | ||
205 | - <input type="text" value="{{project.deploy.test.testUrl}}" | ||
206 | - name="deploy[test][testUrl]" placeholder="ex: http://{host}:8080/test" | ||
207 | - class="form-control"> | ||
208 | - </div> | ||
209 | - </div> | ||
210 | - </div> | ||
211 | - </div><!-- tab-pane --> | ||
212 | - </div><!-- tab-content --> | ||
213 | - </div> | ||
214 | - </div> | ||
215 | - <div class="row"> | ||
216 | - <div class="col-sm-12"> | ||
217 | - <h5 class="lg-title mb10">监控配置</h5> | ||
218 | - <ul class="nav nav-tabs nav-primary"> | ||
219 | - <li class="active"><a href="#influx-info" data-toggle="tab"><strong>InfluxDB</strong></a></li> | ||
220 | - </ul> | ||
221 | - <div class="tab-content tab-content-primary mb30"> | ||
222 | - <div class="tab-pane active" id="influx-info"> | ||
223 | - <div class="row"> | ||
224 | - <div class="col-sm-12"> | ||
225 | - <div class="form-group"> | ||
226 | - <label class="control-label">Influxdb Measurement</label> | ||
227 | - <input type="text" name="monitor[influx][name]" value="{{project.monitor.influx.name}}" | ||
228 | - class="form-control" placeholder="请输入项目日志配置的measurement"> | ||
229 | - </div> | ||
230 | - </div> | ||
231 | - </div> | ||
232 | - </div><!-- tab-pane --> | ||
233 | - </div> | ||
234 | </div> | 52 | </div> |
235 | </div> | 53 | </div> |
236 | - <!-- row --> | ||
237 | </div> | 54 | </div> |
238 | <!-- panel-body --> | 55 | <!-- panel-body --> |
239 | <div class="panel-footer"> | 56 | <div class="panel-footer"> |
@@ -16,23 +16,21 @@ | @@ -16,23 +16,21 @@ | ||
16 | </div> | 16 | </div> |
17 | 17 | ||
18 | <div class="contentpanel project-index-page"> | 18 | <div class="contentpanel project-index-page"> |
19 | - <div class="panel panel-default" data-env='{{deploy.env}}'> | 19 | + <div class="panel panel-default"> |
20 | <div class="panel-heading"> | 20 | <div class="panel-heading"> |
21 | <div class="pull-right"> | 21 | <div class="pull-right"> |
22 | - {{#if is_master}} | ||
23 | - <a class="btn btn-info btn-rounded mr5 log-btn"><i class="fa fa-eye"></i> 查看构建日志</a> | ||
24 | <a class="btn btn-success btn-rounded mr20 build-btn"><i class="glyphicon glyphicon-plus"></i> 新增构建</a> | 22 | <a class="btn btn-success btn-rounded mr20 build-btn"><i class="glyphicon glyphicon-plus"></i> 新增构建</a> |
25 | - {{/if}} | ||
26 | <a href="" class="tooltips panel-minimize"><i class="fa fa-minus"></i></a> | 23 | <a href="" class="tooltips panel-minimize"><i class="fa fa-minus"></i></a> |
27 | </div> | 24 | </div> |
28 | - <h4 class="panel-title">{{deploy.name}}</h4> | ||
29 | - <p>分支:<code>{{deploy.branchName}}</code></p> | 25 | + <h4 class="panel-title">Static Deploy For {{project.subname}}</h4> |
26 | + <p>分支:<code>Matser</code></p> | ||
30 | </div> | 27 | </div> |
31 | <div class="panel-body"> | 28 | <div class="panel-body"> |
32 | - <table id="table-{{deploy.env}}" class="table table-striped table-bordered building-table"> | 29 | + <table id="table-production" class="table table-striped table-bordered building-table"> |
33 | <thead> | 30 | <thead> |
34 | <tr> | 31 | <tr> |
35 | <th>构建版本</th> | 32 | <th>构建版本</th> |
33 | + <th>版本号</th> | ||
36 | <th>分支名称</th> | 34 | <th>分支名称</th> |
37 | <th>状态</th> | 35 | <th>状态</th> |
38 | <th>构建时间</th> | 36 | <th>构建时间</th> |
@@ -42,51 +40,6 @@ | @@ -42,51 +40,6 @@ | ||
42 | </table> | 40 | </table> |
43 | </div> | 41 | </div> |
44 | </div> | 42 | </div> |
45 | - | ||
46 | - <div class="panel panel-default"> | ||
47 | - <div class="panel-heading"> | ||
48 | - <h4>服务器信息</h4> | ||
49 | - <p>点击状态Label,可以查看实时日志</p> | ||
50 | - </div> | ||
51 | - <div class="panel-body"> | ||
52 | - <div class="row"> | ||
53 | - {{#each targets}} | ||
54 | - <div class="col-md-4" id="d-{{hostFm}}"> | ||
55 | - <div class="panel panel-info noborder"> | ||
56 | - <div class="panel-heading noborder"> | ||
57 | - <div class="panel-btns"> | ||
58 | - </div><!-- panel-btns --> | ||
59 | - <div class="panel-icon"><i class="fa fa-cloud" style="padding-left:12px;"></i></div> | ||
60 | - <div class="media-body"> | ||
61 | - <h2 class="nomargin">{{host}}</h2> | ||
62 | - <h5 class="md-title mt5">当前运行版本: <code>{{#if info}}{{info.building}}{{^}} | ||
63 | - 未知部署{{/if}}</code></h5> | ||
64 | - </div><!-- media-body --> | ||
65 | - <hr class="mt10 mb10"> | ||
66 | - <div class="clearfix mt5"> | ||
67 | - <div class="col-xs-6 project-env" data-env="test"> | ||
68 | - <h5 class="md-title mt10">当前状态</h5> | ||
69 | - <span class="label label-success deploy-log-btn" data-host="{{host}}"><i | ||
70 | - class="fa fa-spinner fa-spin fa-fw margin-bottom"></i> <b>{{#if info}}{{info.state}}{{^}} | ||
71 | - 未知部署{{/if}}</b></span> | ||
72 | - </div> | ||
73 | - <div class="col-xs-6"> | ||
74 | - | ||
75 | - </div> | ||
76 | - </div> | ||
77 | - </div><!-- panel-body --> | ||
78 | - </div><!-- panel --> | ||
79 | - </div> | ||
80 | - {{/each}} | ||
81 | - </div> | ||
82 | - </div> | ||
83 | - </div> | ||
84 | -</div> | ||
85 | - | ||
86 | -<div id="log-area" class="panel panel-default panel-alt" style="display:none;height: 100%;"> | ||
87 | - <div class="panel-body nopadding" style="height: 100%;"> | ||
88 | - <textarea name="code" id="code" style="height: 100%;"></textarea> | ||
89 | - </div> | ||
90 | </div> | 43 | </div> |
91 | 44 | ||
92 | <script> | 45 | <script> |
@@ -95,12 +48,12 @@ | @@ -95,12 +48,12 @@ | ||
95 | 48 | ||
96 | var tables = {}; | 49 | var tables = {}; |
97 | $('.building-table').each(function() { | 50 | $('.building-table').each(function() { |
98 | - var env = $(this).parents('.panel').data('env'); | ||
99 | - tables[env] = $(this).DataTable({ | 51 | + tables = $(this).DataTable({ |
100 | responsive: true, | 52 | responsive: true, |
101 | - ajax: '/projects/{{project._id}}/buildings?env=' + env, | 53 | + ajax: '/projects/{{project._id}}/buildings', |
102 | columns: [ | 54 | columns: [ |
103 | {data: "buildTime"}, | 55 | {data: "buildTime"}, |
56 | + {data: "version"}, | ||
104 | {data: "branch"}, | 57 | {data: "branch"}, |
105 | {data: "state"}, | 58 | {data: "state"}, |
106 | {data: "updatedAt"}, | 59 | {data: "updatedAt"}, |
@@ -115,6 +68,18 @@ | @@ -115,6 +68,18 @@ | ||
115 | } else if (data == 'fail') { | 68 | } else if (data == 'fail') { |
116 | color = 'danger'; | 69 | color = 'danger'; |
117 | } | 70 | } |
71 | + var html = '<span id="version-' + row._id + '">' + data + '</span>'; | ||
72 | + return html; | ||
73 | + }, | ||
74 | + targets: 1 | ||
75 | + }, { | ||
76 | + render: function(data, type, row) { | ||
77 | + var color = 'warning'; | ||
78 | + if (data == 'success') { | ||
79 | + color = 'success'; | ||
80 | + } else if (data == 'fail') { | ||
81 | + color = 'danger'; | ||
82 | + } | ||
118 | var html = '<span id="b-' + row._id + '" class="label label-' + color + '">'; | 83 | var html = '<span id="b-' + row._id + '" class="label label-' + color + '">'; |
119 | if (data != 'success' && data != 'fail') { | 84 | if (data != 'success' && data != 'fail') { |
120 | html += '<i class="fa fa-spinner fa-spin fa-fw margin-bottom"></i>'; | 85 | html += '<i class="fa fa-spinner fa-spin fa-fw margin-bottom"></i>'; |
@@ -122,7 +87,7 @@ | @@ -122,7 +87,7 @@ | ||
122 | html += data + '</span>'; | 87 | html += data + '</span>'; |
123 | return html; | 88 | return html; |
124 | }, | 89 | }, |
125 | - targets: 2 | 90 | + targets: 3 |
126 | }, { | 91 | }, { |
127 | render: function(data, type, row) { | 92 | render: function(data, type, row) { |
128 | var disabled = row.state !== 'success'; | 93 | var disabled = row.state !== 'success'; |
@@ -132,7 +97,7 @@ | @@ -132,7 +97,7 @@ | ||
132 | return ''; | 97 | return ''; |
133 | {{/if}} | 98 | {{/if}} |
134 | }, | 99 | }, |
135 | - targets: 4 | 100 | + targets: 5 |
136 | }] | 101 | }] |
137 | }); | 102 | }); |
138 | 103 | ||
@@ -160,21 +125,13 @@ | @@ -160,21 +125,13 @@ | ||
160 | } | 125 | } |
161 | 126 | ||
162 | $('.build-btn').click(function() { | 127 | $('.build-btn').click(function() { |
163 | - var env = $(this).parents('.panel').data('env'); | ||
164 | - var i = layer.prompt({ | ||
165 | - title: '请输入需要构建的分支,默认为 {{deploy.branchName}}' | ||
166 | - }, function(branch) { | ||
167 | - branch = branch || '{{deploy.branchName}}'; | ||
168 | - $.post('/projects/build/{{project._id}}', {env: env, branch: branch}, function(ret) { | 128 | + $.post('/projects/build/{{project._id}}', function(ret) { |
169 | if (ret.code == 200) { | 129 | if (ret.code == 200) { |
170 | - tables[env].ajax.reload(); | ||
171 | - layer.close(i); | 130 | + tables.ajax.reload(); |
172 | } | 131 | } |
173 | }); | 132 | }); |
174 | }); | 133 | }); |
175 | 134 | ||
176 | - }); | ||
177 | - | ||
178 | 135 | ||
179 | $('.rollback-btn').click(function() { | 136 | $('.rollback-btn').click(function() { |
180 | layer.prompt({ | 137 | layer.prompt({ |
@@ -184,44 +141,14 @@ | @@ -184,44 +141,14 @@ | ||
184 | }); | 141 | }); |
185 | }); | 142 | }); |
186 | 143 | ||
187 | - var cm = CodeMirror.fromTextArea(document.getElementById("code"), { | ||
188 | - lineNumbers: true, | ||
189 | - theme: 'ambiance' | ||
190 | - }); | ||
191 | - | ||
192 | - var tag; | ||
193 | - | ||
194 | - $('.log-btn').click(function() { | ||
195 | - viewLog(''); | ||
196 | - }); | ||
197 | - | ||
198 | - $('.deploy-log-btn').click(function() { | ||
199 | - var host = $(this).data('host'); | ||
200 | - viewLog(host); | ||
201 | - }); | ||
202 | - | ||
203 | - function viewLog(tag) { | ||
204 | - tag = tag; | ||
205 | - $('#log-area').show(); | ||
206 | - cm.setValue(""); | ||
207 | - cm.clearHistory(); | ||
208 | - layer.open({ | ||
209 | - type: 1, | ||
210 | - title: '实时日志', | ||
211 | - content: $('#log-area'), | ||
212 | - area: ['1000px', '600px'], | ||
213 | - maxmin: true, | ||
214 | - cancel: function() { | ||
215 | - $('#log-area').hide(); | ||
216 | - } | ||
217 | - }); | ||
218 | - } | ||
219 | - | ||
220 | var ws = io(); | 144 | var ws = io(); |
221 | ws.on('connect', function() { | 145 | ws.on('connect', function() { |
222 | ws.on('/building/{{project._id}}', function(data) { | 146 | ws.on('/building/{{project._id}}', function(data) { |
223 | console.log(data); | 147 | console.log(data); |
224 | - $('#b-' + data.bid).text(data.state); | 148 | + $('#b-' + data.bid).html( |
149 | + '<i class="fa fa-spinner fa-spin fa-fw margin-bottom"></i>' + | ||
150 | + data.state | ||
151 | + ); | ||
225 | if (data.state == 'success') { | 152 | if (data.state == 'success') { |
226 | $('#b-' + data.bid).removeClass('label-warning').addClass('label-success') | 153 | $('#b-' + data.bid).removeClass('label-warning').addClass('label-success') |
227 | $('#b-' + data.bid).parent().parent().find('.deploy-btn').removeAttr('disabled'); | 154 | $('#b-' + data.bid).parent().parent().find('.deploy-btn').removeAttr('disabled'); |
@@ -230,24 +157,14 @@ | @@ -230,24 +157,14 @@ | ||
230 | $('#b-' + data.bid).removeClass('label-warning').addClass('label-danger'); | 157 | $('#b-' + data.bid).removeClass('label-warning').addClass('label-danger'); |
231 | $('#b-' + data.bid).find('i').remove(); | 158 | $('#b-' + data.bid).find('i').remove(); |
232 | } | 159 | } |
233 | - }); | ||
234 | 160 | ||
235 | - // ws.on('/building/{{project._id}}/log', function(data){ | ||
236 | - // if(tag == '') { | ||
237 | - // cm.replaceRange("> " + data + "\n", {line: Infinity}); | ||
238 | - // } | ||
239 | - // }); | 161 | + $('#version-' + data.bid).html(data.version); |
162 | + }); | ||
240 | 163 | ||
241 | ws.on('/deploy/{{project._id}}', function(data) { | 164 | ws.on('/deploy/{{project._id}}', function(data) { |
242 | console.log(data); | 165 | console.log(data); |
243 | $('#d-' + data.host.replace(/\./g, '-')).find('b').text(data.state); | 166 | $('#d-' + data.host.replace(/\./g, '-')).find('b').text(data.state); |
244 | }); | 167 | }); |
245 | - | ||
246 | - // ws.on('/deploy/{{project._id}}/log', function(data){ | ||
247 | - // if(tag == data.host){ | ||
248 | - // cm.replaceRange("> " +data.msg+ "\n", {line: Infinity}); | ||
249 | - // } | ||
250 | - // }); | ||
251 | }); | 168 | }); |
252 | ws.on('error', function() { | 169 | ws.on('error', function() { |
253 | console.log('connect fail'); | 170 | console.log('connect fail'); |
@@ -18,7 +18,7 @@ | @@ -18,7 +18,7 @@ | ||
18 | <div class="contentpanel servers-page"> | 18 | <div class="contentpanel servers-page"> |
19 | <div class="row row-stat"> | 19 | <div class="row row-stat"> |
20 | {{#each projects}} | 20 | {{#each projects}} |
21 | - <div class="col-md-4"> | 21 | + <div class="col-md-4 project-item" data-id="{{_id}}" style="cursor:pointer;"> |
22 | <div class="panel panel-{{color}} noborder"> | 22 | <div class="panel panel-{{color}} noborder"> |
23 | <div class="panel-heading noborder"> | 23 | <div class="panel-heading noborder"> |
24 | <div class="panel-btns"> | 24 | <div class="panel-btns"> |
@@ -26,36 +26,21 @@ | @@ -26,36 +26,21 @@ | ||
26 | <a href="/projects/edit?id={{_id}}" class="tooltips" title="设置"><i | 26 | <a href="/projects/edit?id={{_id}}" class="tooltips" title="设置"><i |
27 | class="fa fa-gear"></i></a> | 27 | class="fa fa-gear"></i></a> |
28 | {{/if}} | 28 | {{/if}} |
29 | - </div><!-- panel-btns --> | 29 | + </div> |
30 | <div class="panel-icon"><i class="fa fa-git" style="padding-left:12px;"></i></div> | 30 | <div class="panel-icon"><i class="fa fa-git" style="padding-left:12px;"></i></div> |
31 | <div class="media-body"> | 31 | <div class="media-body"> |
32 | <h1 class="nomargin">{{name}}</h1> | 32 | <h1 class="nomargin">{{name}}</h1> |
33 | <h5 class="md-title mt5">{{subname}} </h5> | 33 | <h5 class="md-title mt5">{{subname}} </h5> |
34 | - </div><!-- media-body --> | ||
35 | - <hr> | ||
36 | - <div class="clearfix mt20"> | ||
37 | - <div class="col-xs-4 project-env" data-id="{{_id}}" data-env="production"> | ||
38 | - <h5 class="md-title nomargin">线上环境</h5> | ||
39 | - <h4 class="nomargin">{{deploy.production.target.length}}</h4> | ||
40 | </div> | 34 | </div> |
41 | - <div class="col-xs-4 project-env" data-id="{{_id}}" data-env="preview"> | ||
42 | - <h5 class="md-title nomargin">灰度环境</h5> | ||
43 | - <h4 class="nomargin">{{deploy.preview.target.length}}</h4> | ||
44 | </div> | 35 | </div> |
45 | - <div class="col-xs-4 project-env" data-id="{{_id}}" data-env="test"> | ||
46 | - <h5 class="md-title nomargin">测试环境</h5> | ||
47 | - <h4 class="nomargin">{{deploy.test.target.length}}</h4> | ||
48 | </div> | 36 | </div> |
49 | </div> | 37 | </div> |
50 | - </div><!-- panel-body --> | ||
51 | - </div><!-- panel --> | ||
52 | - </div><!-- col-md-4 --> | ||
53 | {{/each}} | 38 | {{/each}} |
54 | {{#if is_master}} | 39 | {{#if is_master}} |
55 | <div class="col-md-4"> | 40 | <div class="col-md-4"> |
56 | <div class="panel panel-default noborder"> | 41 | <div class="panel panel-default noborder"> |
57 | <div class="panel-heading noborder"> | 42 | <div class="panel-heading noborder"> |
58 | - <div style="text-align: center; font-size: 97px;"> | 43 | + <div style="text-align: center; font-size: 46px;"> |
59 | <a href="/projects/new" class=""> | 44 | <a href="/projects/new" class=""> |
60 | <i class="fa fa-plus"></i> | 45 | <i class="fa fa-plus"></i> |
61 | </a> | 46 | </a> |
@@ -64,18 +49,18 @@ | @@ -64,18 +49,18 @@ | ||
64 | </div> | 49 | </div> |
65 | </div> | 50 | </div> |
66 | {{/if}} | 51 | {{/if}} |
67 | - </div><!-- row --> | 52 | + </div> |
68 | </div> | 53 | </div> |
69 | 54 | ||
70 | 55 | ||
71 | <script> | 56 | <script> |
72 | $(function() { | 57 | $(function() { |
73 | $('.servers-page').pjax('a', '#pjax-container'); | 58 | $('.servers-page').pjax('a', '#pjax-container'); |
74 | - $('.project-env').click(function() { | 59 | + $('.project-item').click(function() { |
75 | var id = $(this).data('id'); | 60 | var id = $(this).data('id'); |
76 | - var env = $(this).data('env'); | 61 | + |
77 | $.pjax({ | 62 | $.pjax({ |
78 | - url: '/projects/' + id + '?env=' + env, | 63 | + url: '/projects/' + id, |
79 | container: '#pjax-container' | 64 | container: '#pjax-container' |
80 | }); | 65 | }); |
81 | }); | 66 | }); |
@@ -6,8 +6,10 @@ const env = process.env.NODE_ENV || 'development'; | @@ -6,8 +6,10 @@ const env = process.env.NODE_ENV || 'development'; | ||
6 | 6 | ||
7 | const defaults = { | 7 | const defaults = { |
8 | port: 9000, | 8 | port: 9000, |
9 | - buildDir: path.normalize(__dirname + '/../packages/'), | ||
10 | - dbDir: path.normalize(__dirname + '/../db') | 9 | + codeDir: path.normalize(__dirname + '/../code/'), // 代码位置 |
10 | + buildDir: path.normalize(__dirname + '/../packages/'), // 静态资源包位置 | ||
11 | + dbDir: path.normalize(__dirname + '/../db'), | ||
12 | + ci: path.normalize(__dirname + '/../apps/ci') | ||
11 | }; | 13 | }; |
12 | 14 | ||
13 | const specific = { | 15 | const specific = { |
@@ -37,6 +37,9 @@ | @@ -37,6 +37,9 @@ | ||
37 | "formidable": "^1.0.17", | 37 | "formidable": "^1.0.17", |
38 | "fs-promise": "^0.5.0", | 38 | "fs-promise": "^0.5.0", |
39 | "fstream": "^1.0.9", | 39 | "fstream": "^1.0.9", |
40 | + "glob": "^7.0.5", | ||
41 | + "gulp": "^3.9.1", | ||
42 | + "gulp-qiniu": "^0.2.4", | ||
40 | "handlebars": "^4.0.5", | 43 | "handlebars": "^4.0.5", |
41 | "influx": "^4.2.1", | 44 | "influx": "^4.2.1", |
42 | "koa": "^2.0.0", | 45 | "koa": "^2.0.0", |
-
Please register or login to post a comment