Class: Git::Daily::Release

Inherits:
Command
  • Object
show all
Defined in:
lib/git-daily/command/release.rb

Direct Known Subclasses

Hotfix

Constant Summary collapse

SUBCOMMAND =
{
  "open" => :open,
  "list" => :list,
  "sync" => :sync,
  "close" => :close
}

Instance Method Summary collapse

Methods inherited from Command

branches, clean?, current_branch, develop, has_branch?, has_remote_branch?, logurl, master, merged_branches, pull_request_url, release_branches, remote, remote_branch, remotes

Constructor Details

#initializeRelease

Returns a new instance of Release.



16
17
18
19
20
# File 'lib/git-daily/command/release.rb', line 16

def initialize
  @base_branch = Command.develop
  @branch_prefix = "release"
  @merge_to = [Command.master, Command.develop]
end

Instance Method Details

#closeObject



106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
# File 'lib/git-daily/command/release.rb', line 106

def close
  rel_branches = Command.release_branches(@branch_prefix)
  if rel_branches.empty?
    raise "#{@branch_prefix} branch not found. abort."
  end
  rel_branch = rel_branches.shift
  remote = Command.remote

  merge_branches.each do |branch_name|

    branches = `git branch`.split("\n").select{|v| v[/#{branch_name}/]}
    merge_branch = branches[0][/^\*/] ? branches[0][2 .. -1] : branches[0].strip
    next unless merge_branch

    if remote
      puts "first, fetch remotes"
      `git fetch --all`
      puts "diff check"

      diff_branch1 = "#{rel_branch}..#{remote}/#{rel_branch}"
      diff_branch2 = "#{remote}/#{rel_branch}..#{rel_branch}"
      diff1 = `git diff #{diff_branch1}`
      diff2 = `git diff #{diff_branch2}`
      unless diff1.empty? or diff1.empty?
        $stderr.puts "There are some diff between local and $remote, run release sync first."
        raise "abort"
      end
    end

    $stderr.puts "checkout #{merge_branch} and merge #{rel_branch} to #{merge_branch}"
    `git checkout #{merge_branch}`
    if remote
      r = `git daily pull`
      puts r
      unless $? == 0
        $stderr.puts "pull failed"
        raise "abort"
      end
    end

    merged_branches = Command.merged_branches
    unless merged_branches.find {|v| v == rel_branch}
      r = `git merge --no-ff #{rel_branch}`
      puts r
      unless $? == 0
        $stderr.puts "merge failed"
        raise "abort"
      end
    end

    puts "push #{merge_branch} to #{remote}"
    `git checkout #{merge_branch}`
    r = `git push #{remote} #{merge_branch}`
    puts r
    unless $? == 0
      $stderr.puts "push failed"
      raise "abort"
    end
  end

  puts "delete branch: #{rel_branch}"
  r = `git branch -d #{rel_branch}`
  puts r
  unless $? == 0
    $stderr.puts "failed to delete local #{rel_branch}"
    raise "abort"
  end
  if remote
    r = `git push #{remote} :#{rel_branch}`
    puts r
    unless $? == 0
      $stderr.puts "failed to delete #{remote}'s #{rel_branch}"
      raise "abort"
    end
  end

  base = @base_branch
  `git checkout #{base}`
  puts "#{@branch_prefix} closed"

end

#helpObject



22
23
24
# File 'lib/git-daily/command/release.rb', line 22

def help
  "release\tOperation daily release"
end

#listObject



284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
# File 'lib/git-daily/command/release.rb', line 284

def list
  release_branch = Command.release_branches(@branch_prefix)
  if release_branch.empty?
    raise "#{@branch_prefix} branch not found. abort."
  end

  current_branch = Command.current_branch

  remote = Command.remote
  master_branch = if remote
                    Command.remote_branch(remote, Command.master)
                  else
                    Command.master
                  end

  puts "first, fetch remotes"
  `git fetch --all`

  revs = []
  rev_ids = `git rev-list --no-merges #{master_branch}..#{current_branch}`.split(/\n/)
  rev_ids.each do |rev_id|
    rev = {}
    rev[:id] = rev_id
    rev[:add_files] = []
    rev[:mod_files] = []

    logs = `git show #{rev_id}`
    file = nil
    logs.each_line do |line|
      case line
      when /^Author: .+\<([^@]+)@([^>]+)>/
        rev[:author] = $1
      when /^diff --git a\/([^ ]+) /
        file = $1
      when /^new file mode/
        rev[:add_files] << file
      when /^index/
        rev[:mod_files] << file
      end
    end

    revs << rev
  end

  mod_files = []
  add_files = []
  revs.each do |rev|
    mod_files += rev[:add_files]
    mod_files += rev[:mod_files]
  end
  mod_files.sort!.uniq!
  add_files.sort!.uniq!

  if revs.size > 0
    puts "Commit list:"
    revs.each do |rev|
      puts "\t#{rev[:id]} = #{rev[:author]}"
    end
    puts
  end

  if add_files.size > 0
    puts "Added files:"
    add_files.each do |file|
      puts "\t#{file}"
    end
    puts
  end

  if mod_files.size > 0
    puts "Modified files:"
    mod_files.each do |file|
      puts "\t#{file}"
    end
    puts
  end

  if Command.logurl and revs.size > 0
    authors = {}
    revs.each do |rev|
      authors[rev[:author]] = [] unless authors[rev[:author]]
      authors[rev[:author]] << rev[:id]
    end

    logurl = Command.logurl
    puts "Author list:"
    authors.keys.sort.each do |key|
      puts "\t#{key}:"
      authors[key].each do |id|
        puts sprintf("\t#{logurl}", id)
      end
      puts
    end
  end

  if Command.pull_request_url
    puts 'Pull Requests: '

    urlBase = Command.pull_request_url
    merges = `git log --merges --pretty=format:'%<(30)%s | %an' #{master_branch}..#{current_branch}`.split(/\n/)

    merges.each do |merge|
      merge.match(/^Merge pull request #(?<id>[1-9][0-9]*) .+$/) do |match|
        url = sprintf(urlBase,  match[:id])
        puts "\t#{url} | #{match[0]}"
      end
    end
  end
end

#merge_branchesObject



102
103
104
# File 'lib/git-daily/command/release.rb', line 102

def merge_branches
  @merge_to
end

#openObject



41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
# File 'lib/git-daily/command/release.rb', line 41

def open
  cur_branch = Command.current_branch
  base_branch = @base_branch
  if cur_branch != base_branch
    raise "currently not on #{base_branch} but on #{cur_branch}"
  end

  rel_branches = Command.release_branches(@branch_prefix)
  unless rel_branches.empty?
    raise "release process (on local) is not closed, so cannot open release\n    release branches: #{rel_branches.join(',')}"
  end

  remote = Command.remote
  if remote
    puts "first, fetch remotes"
    `git fetch --all`

    rels = `git branch -a --no-color`.split(/\n/).select { |b| b[/remotes\/#{remote}\/#{@branch_prefix}/]}
    unless rels.empty?
      raise "release process (on local) is not closed, so cannot open release\n    release branches: #{rels.join(',')}"
    end
  end

  new_release_branch = "#{@branch_prefix}/#{DateTime.now.strftime("%Y%m%d-%H%M")}"
  puts "Confirm: create branch #{new_release_branch} from #{cur_branch} ?"
  print " [yN] : "
  confirm = $stdin.gets
  unless confirm.strip[/^[Yy]/]
    raise "abort"
  end

  # merge current branch
  if remote
    puts "merge #{cur_branch} branch from remote"
    r = `git merge #{remote}/#{cur_branch}`
    unless $? == 0
      $stderr.puts "merge failed"
      $stderr.puts r
      raise "abort"
    end
  end

  # create release branch
  puts "create release branch: #{new_release_branch}"
  r = `git branch #{new_release_branch}`
  if remote
    puts "push to remote: #{remote}"
    r = `git push #{remote} #{new_release_branch}`
    puts r
    unless $? == 0
      $stderr.puts "push failed"
      r = `git branch -d #{new_release_branch}`
      $stderr.puts r
      $stderr.puts "rollback (delete branch)"
    end
  end

  `git checkout #{new_release_branch}`
  puts "release opened"
end

#runObject



26
27
28
29
30
31
32
33
34
35
36
37
38
39
# File 'lib/git-daily/command/release.rb', line 26

def run
  unless Command.clean?
    raise "git status is not clean"
  end

  subcommand = ARGV.shift
  if SUBCOMMAND.has_key?(subcommand)
    return send(SUBCOMMAND[subcommand])
  else
    $stderr.puts "please specify release subcommand"
    $stderr.puts usage
    exit 1
  end
end

#syncObject

Sync release



189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
# File 'lib/git-daily/command/release.rb', line 189

def sync
  remote = Command.remote
  unless remote
    raise "remote not found. abort."
  end
  cur = Command.current_branch
  develop = Command.develop

  puts "first, fetch remotes"
  `git fetch --all`
  puts "cleanup remote"
  `git remote prune #{remote}`

  rel = nil
  rel_branches = Command.release_branches(@branch_prefix)
  rel = rel_branches.shift if rel_branches.size == 1

  r_closed = false
  r_rel_branch = nil
  r_rel_branches = `git branch -a`.split(/\n/).select { |a| a[/remotes\/#{remote}\/#{@branch_prefix}/] }
  r_rel_branches.map! { |b| b.strip }
  if r_rel_branches.size == 1
    r_rel_branch = r_rel_branches.shift[/remotes\/#{remote}\/(.*)/, 1]
  else
    if r_rel_branches.empty?
      r_closed = true
    else
      raise "there are a number of remote release branches"
    end
  end

  if rel
    if r_rel_branch != rel
      r_closed = true
    end

    if r_closed
      puts "release closed! so cleanup local release branch"
      puts "checkout #{develop}"
      `git checkout #{develop}`
      r = `git daily pull`
      puts r
      puts "delete #{rel}"
      r = `git branch -d #{rel}`
      puts r
      unless $? == 0
        $stderr.puts "branch delete failed"
        $stderr.puts "    git branch delete failed, please manually"
      end

      if r_rel_branch != rel
        $stderr.puts "Closed old release branch"
        $stderr.puts "Please retry 'release sync'"
      end
      puts "sync to release close"
      return
    end

    if cur != rel
      puts "checkout #{rel}"
      `git checkout #{rel}`
      cur = Command.current_branch
    end

    puts "git pull"
    r = `git daily pull`
    puts r
    unless $? == 0
      raise "abort"
    end

    puts "git push"
    r = `git daily push`
    puts r
    unless $? == 0
      $stderr.puts "failed to push to remote"
      raise "abort"
    end
  else
    if r_closed
      puts "sync completed (nothing to do)"
      return
    end

    puts "checkout and traking #{r_rel_branch}"
    r = `git checkout #{r_rel_branch}`
    puts r
    unless $? == 0
      $stderr.puts "failed to checkout"
      raise "abort"
    end
    puts "start to tracking release branch"
  end
end

#usageObject



394
395
396
397
398
399
400
401
# File 'lib/git-daily/command/release.rb', line 394

def usage
  "Usage: git daily release open\\t: Open daily-release process\n git daily release list\\t: Show release list\n git daily release sync\\t: Sync current opened release process\n git daily release close\\t: Close to daily-release process\n"
end