1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
10 |
|
11 | CONFIG = "ggg"
|
12 |
|
13 | LOGS_LINES = 40
|
14 | COMMIT_HISTORY = 5
|
15 | VERSION = require("./package.json").version
|
16 |
|
17 | {exec} = require "child_process"
|
18 | fs = require 'fs'
|
19 | path = require 'path'
|
20 |
|
21 | program = require 'commander'
|
22 |
|
23 | MainConfig = require "./lib/MainConfig"
|
24 | Layer = require "./lib/Layer"
|
25 |
|
26 | parseTargetAndProcessName = (arg) ->
|
27 | [target, processName] = arg.split ':'
|
28 | {target, processName}
|
29 |
|
30 | program
|
31 | .version(VERSION)
|
32 | .option("-l, --local <user>", "deploy locally for bootstrapping")
|
33 | .option("-n, --noPlugin", "disable plugins")
|
34 |
|
35 | program
|
36 | .command("init")
|
37 | .description("creates a ggg.js config file for you")
|
38 | .action ->
|
39 | init finish
|
40 |
|
41 |
|
42 | program
|
43 | .command("deploy <name> [branch]")
|
44 | .description("deploys a branch (defaults to origin/master) to named target")
|
45 | .action (name, branch) ->
|
46 | getLayer name, (err, layer) ->
|
47 | return finish err if err?
|
48 |
|
49 | branch = branch || "origin/master"
|
50 |
|
51 | layer.deploy branch, finish
|
52 |
|
53 | runProcessSpecificCommandOnLayer = (command) ->
|
54 | (name) ->
|
55 | {target, processName} = parseTargetAndProcessName name
|
56 | getLayer target, (err, layer) ->
|
57 | return finish err if err?
|
58 | if processName
|
59 | layer[command] processName, finish
|
60 | else
|
61 | layer[command] finish
|
62 |
|
63 | program
|
64 | .command("restart <name:process>")
|
65 | .description("restarts all processes associated with target.\nIf process is provided, restarts only the named process.\n`ggg restart prod` would restart all processes under the prod target\n`ggg restart prod:web` would restart only the `web` process in the `prod` target.")
|
66 | .action runProcessSpecificCommandOnLayer('restart')
|
67 |
|
68 | program
|
69 | .command("start <name:process>")
|
70 | .description("starts all processes associated with target. if process is provided, starts only the named process")
|
71 | .action runProcessSpecificCommandOnLayer('start')
|
72 |
|
73 | program
|
74 | .command("stop <name:process>")
|
75 | .description("stops all processes associated with name. if process is provided, stops only the named process")
|
76 | .action runProcessSpecificCommandOnLayer('stop')
|
77 |
|
78 | program
|
79 | .command("logs <name:process>")
|
80 | .description("Logs #{LOGS_LINES} lines from target and process. When no process is supplied, defaults to the first process.")
|
81 | .option("-l, --lines <num>", "the number of lines to log")
|
82 | .action (name) ->
|
83 | {target, processName} = parseTargetAndProcessName name
|
84 | getLayer target, (err, layer) ->
|
85 | return finish err if err?
|
86 | lines = program.lines || LOGS_LINES
|
87 |
|
88 | if processName
|
89 | layer.serverLogs lines, processName, finish
|
90 | else
|
91 | layer.serverLogs lines, finish
|
92 |
|
93 | program
|
94 | .command("history <name>")
|
95 | .description("Shows a history of #{COMMIT_HISTORY} last commits deployed")
|
96 | .option("-r, --revisions <num>", "the number of commits to show")
|
97 | .action (name) ->
|
98 | getLayer name, (err, layer) ->
|
99 | return finish err if err?
|
100 | revisions = program.args.revisions || COMMIT_HISTORY
|
101 | layer.commitHistory revisions, finish
|
102 |
|
103 | program
|
104 | .command("command <name> <command>")
|
105 | .description("run a command over ssh in the root of your project directory")
|
106 | .action (name, command) ->
|
107 | getLayer name, (err, layer) ->
|
108 | return finish err if err?
|
109 | layer.runCommand command, finish
|
110 |
|
111 | program
|
112 | .command("list")
|
113 | .description("lists all deploy targets")
|
114 | .action ->
|
115 | getConfigRepo (err, repoName, mainConfig) ->
|
116 | return finish err if err?
|
117 |
|
118 | list mainConfig, finish
|
119 |
|
120 | program
|
121 | .command("servers <name>")
|
122 | .description("lists deploy server locations")
|
123 | .action (name) ->
|
124 | getLayer name, finish
|
125 |
|
126 | program
|
127 | .command("help")
|
128 | .description("display this help")
|
129 | .action ->
|
130 | console.log program.helpInformation()
|
131 | finish()
|
132 |
|
133 | program
|
134 | .command("*")
|
135 | .action ->
|
136 | finish new Error "bad command!"
|
137 |
|
138 |
|
139 |
|
140 |
|
141 | init = (cb) ->
|
142 | initConfigContent = """
|
143 | // example ggg.js. Delete what you don't need
|
144 | module.exports = {
|
145 |
|
146 | // services
|
147 | // can either be a string or an object with mutiple processes to start up
|
148 | start: "node app.js",
|
149 | /* or
|
150 | start: {
|
151 | web: 'node app.js',
|
152 | worker 'node worker.js',
|
153 | montior: 'node monitor.js'
|
154 | },
|
155 | */
|
156 |
|
157 | // install
|
158 | install: "npm install",
|
159 |
|
160 | // cron jobs (from your app folder)
|
161 | cron: {
|
162 | someTask: { time: "0 3 * * *", command: "node sometask.js"},
|
163 | },
|
164 |
|
165 | // servers to deploy to
|
166 | servers: {
|
167 | dev: "deploy@dev.mycompany.com",
|
168 | staging: ["deploy@staging.mycompany.com", "deploy@staging2.mycompany.com"],
|
169 | prod: {
|
170 | hosts: ["deploy@mycompany.com", "deploy@backup.mycompany.com"],
|
171 | cron: {
|
172 | someTask: {time: "0 3 * * *", command: "node sometask.js"},
|
173 | anotherTask: {time: "0 3 * * *", command: "node secondTask.js"}
|
174 | },
|
175 | start: "prodstart app.js"
|
176 | }
|
177 | }
|
178 | }
|
179 | """
|
180 |
|
181 | console.log "GOGOGO INITIALIZING!"
|
182 | console.log "*** Written to ggg.js ***"
|
183 | console.log initConfigContent
|
184 |
|
185 | fs.writeFile mainConfigPath() + ".js", initConfigContent, cb
|
186 |
|
187 | list = (mainConfig, cb) ->
|
188 | console.log "GOGOGO servers (see ggg.js)"
|
189 | console.log " - " + mainConfig.getLayerNames().join("\n - ")
|
190 |
|
191 |
|
192 |
|
193 |
|
194 | reponame = (dir, cb) ->
|
195 | exec "git config --get remote.origin.url", {cwd:dir}, (err, stdout, stderr) ->
|
196 | if err?
|
197 | cb null, path.basename(dir)
|
198 | else
|
199 | url = stdout.replace("\n","")
|
200 | cb null, path.basename(url).replace(".git","")
|
201 |
|
202 |
|
203 |
|
204 | mainConfigPath = -> path.join process.cwd(), CONFIG
|
205 |
|
206 |
|
207 | getConfigRepo = (cb) ->
|
208 | reponame process.cwd(), (err, repoName) ->
|
209 | return cb err if err?
|
210 | MainConfig.loadFromFile mainConfigPath(), (err, mainConfig) ->
|
211 | if err
|
212 | errString = "Bad gogogo config file, ggg.js. Run 'gogogo init' to" +
|
213 | " create one. Err=#{err.message}"
|
214 | return cb new Error errString
|
215 |
|
216 | cb null, repoName, mainConfig
|
217 |
|
218 |
|
219 | getLayer = (name, cb) ->
|
220 | getConfigRepo (err, repoName, mainConfig) ->
|
221 | return cb err if err?
|
222 |
|
223 | layerConfig = mainConfig.getLayerByName name
|
224 | if !layerConfig then return cb new Error("Invalid layer Name: #{name}")
|
225 |
|
226 | if program.noPlugin
|
227 | layerConfig.plugins = null
|
228 | mainConfig.disablePlugins()
|
229 |
|
230 | if program.local
|
231 | layerConfig.hosts = ["#{program.local}@localhost"]
|
232 |
|
233 | layer = new Layer name, layerConfig, repoName, mainConfig, program.local
|
234 | layer.on "error", (err) -> return cb err
|
235 | layer.on "ready", ->
|
236 | cb null, layer
|
237 |
|
238 |
|
239 | finish = (err) ->
|
240 | if err?
|
241 | console.log "!!! " + err.message
|
242 | console.log "stack follows:\n\n #{err.stack}"
|
243 | process.exit 1
|
244 | console.log "OK"
|
245 |
|
246 |
|
247 | module.exports = program
|