dev_flow 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,252 @@
1
+ module DevFlow
2
+
3
+ class App
4
+ attr_accessor :config, :roadmap, :logger, :command, :git, :members, :waiting
5
+
6
+ def initialize config, command
7
+ @config, @commnad = config, command
8
+
9
+ # initialize logger
10
+ @logger = Logger.new(STDOUT)
11
+ @logger.level = config[:verbose] ? Logger::INFO : Logger::WARN
12
+ @logger.formatter = proc {|severity, datetime, progname, msg| "#{msg.to_s}\n"}
13
+
14
+ # initialize git console
15
+ @git = DevFlow::Girc.new 'git', config[:verbose]
16
+ error "Please use dw in a git directory" unless @git.in_git_dir?
17
+
18
+ # load configurations
19
+ if @config[:members_file] and File.exists? (@config[:members_file])
20
+ info "Load member information form #{@config[:members_file]}"
21
+ @config = @config.merge(YAML.load(File.open(@config[:members_file], 'r:utf-8').read))
22
+ else
23
+ warn "No member file to load"
24
+ end
25
+
26
+ if @config[:local_config] and File.exists? (@config[:local_config])
27
+ info "Load local configuration from #{@config[:local_config]}"
28
+ @config = @config.merge(YAML.load(File.open(@config[:local_config], 'r:utf-8').read))
29
+ end
30
+
31
+ # load roadmap, reload config
32
+ if @config[:roadmap] and File.exists? (@config[:roadmap])
33
+ info "Load roadmap from #{@config[:roadmap]}"
34
+ @roadmap = RoadMap.new(@config[:roadmap], @config).parse
35
+ @config = @roadmap.config
36
+
37
+ error "No leader defined for your porject!" unless @config['leader']
38
+ end
39
+
40
+ # convert member list to member name=>object hash
41
+ @members = Hash.new
42
+ @config["members"].each do |name, ary|
43
+ @members[name] = Member.new(name, *ary)
44
+ end
45
+ error "No known members defined!" unless all_member_names.size > 0
46
+
47
+ if @config["whoami"]
48
+ error "You (#{user_name}) are not in the known member list. You may use 'dw init' to setup the working environment." unless all_member_names.include? @config["whoami"]
49
+ end
50
+
51
+ # suggest user to take those tasks
52
+ @waiting = Hash.new
53
+ end
54
+
55
+ # log message handler
56
+ # ------------------------------
57
+ def error msg
58
+ @logger.fatal ("[ERROR] " + msg).bold.red
59
+ exit
60
+ end
61
+
62
+ def warn msg
63
+ @logger.warn ("[WARN] " + msg).yellow
64
+ end
65
+
66
+ def info msg
67
+ @logger.info "[INFO] " + msg
68
+ end
69
+
70
+ # helper function
71
+ # ------------------------------
72
+ def all_member_names
73
+ @members.keys
74
+ end
75
+
76
+ def user_name
77
+ wi = @config["whoami"]
78
+ @members[wi] ? @members[wi].display_name : wi
79
+ end
80
+
81
+ def leader_name
82
+ @members[@config["leader"]].display_name
83
+ end
84
+
85
+ def task
86
+ @roadmap.tasks.each do |task|
87
+ return task if task.branch_name == @git.current_branch
88
+ end
89
+ nil
90
+ end
91
+
92
+ def in_trunk?
93
+ %w[master develop staging production].include? @git.current_branch
94
+ end
95
+
96
+ def in_release?
97
+ task and task.is_release?
98
+ end
99
+
100
+ def i_am_leader?
101
+ @config["whoami"] and @config["leader"] == @config["whoami"]
102
+ end
103
+
104
+ def i_am_moderator?
105
+ @config["whoami"] and @config["moderator"] == @config["whoami"]
106
+ end
107
+
108
+ def i_am_supervisor?
109
+ @config["whoami"] and @config["supervisor"] == @config["whoami"]
110
+ end
111
+
112
+ def i_have_power?
113
+ [@config["leader"], @config["supervisor"], @config["moderator"]].include? @config["whoami"]
114
+ end
115
+
116
+ def tasks_for_close
117
+ @roadmap.tasks.select {|task| task.progress == 99}
118
+ end
119
+
120
+ # display informations
121
+ # -----------------------
122
+ def hr; "-"*76 end
123
+ def hrh; hr.bold end
124
+ def hrb; "="*76 end
125
+ def hrbh; hrb.bold end
126
+
127
+ def hello
128
+ puts hrbh
129
+ puts "Hello, #{user_name.bold}."
130
+ puts "This is the DevFlow console, version: " + VERSION
131
+ puts hrh
132
+ puts "You are on branch #{@git.current_branch.bold.green}" if @git.current_branch
133
+ puts "You task is: #{self.task.display_name.bold}" if self.task
134
+ puts "You are the #{'leader'.bold} of the project." if self.i_am_leader?
135
+ puts "You are the #{'moderator'.bold} of the project." if self.i_am_moderator?
136
+ puts "You are the #{'supervisor'.bold} of the project." if self.i_am_supervisor?
137
+ end
138
+
139
+ def display_close_waiting
140
+ return false unless self.tasks_for_close.size > 0
141
+ puts "There have tasks marked completed and need you to review it:"
142
+ i = 0
143
+ self.tasks_for_close.each do |task|
144
+ i += 1
145
+ if @git.wd_clean?
146
+ puts task.as_title i.to_s
147
+ @waiting[i] = task
148
+ else
149
+ puts task.as_title " "
150
+ end
151
+ end
152
+ end
153
+
154
+ def display_tasks
155
+ i = 0
156
+ j = 0
157
+ remain = 0
158
+ @roadmap.tasks.each do |task|
159
+ next if task.parent and task.parent.is_completed?
160
+ next if task.is_pending? or task.is_deleted?
161
+ if i > 31 # only show 16 task lines at most
162
+ remain += 1
163
+ next
164
+ end
165
+
166
+ header = nil
167
+ header = '+'.bold.green if task.is_completed?
168
+ header = '-' unless header or task.is_workable?
169
+ unless header
170
+ j += 1
171
+ header = j.to_s.bold
172
+ header = ' ' unless @git.wd_clean?
173
+ @waiting[j] = task if @git.wd_clean?
174
+ end
175
+
176
+ puts task.as_title(header)
177
+ i += 1
178
+ end
179
+ puts "There #{remain.to_s.bold} more tasks not show here." if remain > 0
180
+ end
181
+
182
+ # interactive methods with git remote server
183
+ # ------------------------------------------------------
184
+ def ask_rebase force = false
185
+ return false if @config[:offline]
186
+
187
+ unless force
188
+ print "Rebase your wokring directory? [Y/n]:".bold.yellow
189
+ ans = STDIN.gets.chomp!
190
+ return false if ans == 'n'
191
+ end
192
+
193
+ # do the rebase:
194
+ if @config["git_remote"]
195
+ info "Rebase you working directory from #{@config["git_remote"]}/devleop"
196
+ @git.rebase! @config["git_remote"], 'develop'
197
+ else
198
+ info "Git remote not defined, skip rebase."
199
+ end
200
+ end
201
+
202
+ # switch to other branch
203
+ def switch_to! branch
204
+ if @git.branches.include? branch
205
+ info "Switch to branch #{branch}"
206
+ `git checkout #{branch}`
207
+ else
208
+ info "Switch to new branch #{branch}"
209
+ `git checkout -b #{branch}`
210
+ end
211
+ end
212
+
213
+ def upload_progress! task, progress, is_complete = false
214
+ current_branch = @git.current_branch
215
+
216
+ switch_to! 'develop' unless current_branch == 'develop'
217
+
218
+ info "Rewrite #{@config[:roadmap]} file"
219
+ @roadmap.rewrite! task.ln => progress
220
+
221
+ info "Set progress of #{task.display_name} to #{progress}"
222
+ `git commit -am 'update progress of task #{task.branch_name} to #{progress}'`
223
+ `git push #{@config[:git_remote]} develop` if @config[:git_remote]
224
+
225
+ # if this is a complete update, do not switch back
226
+ unless (is_complete or current_branch == 'develop')
227
+ switch_to! current_branch
228
+ `git merge develop`
229
+ `git push #{@config[:git_remote]} #{current_branch}` if @config[:git_remote]
230
+ end
231
+ end
232
+
233
+ def new_version branch_name
234
+ if branch_name =~ /^release\_v/
235
+ return branch_name.gsub('release_v', 'version-')
236
+ elsif branch_name =~ /^hotfix\_/
237
+ last_v = ''
238
+ `git tag`.split("\n").each do |t|
239
+ if /^version\-\d+\.\d+\.(?<nm_>\d+)$/ =~ t # with fix number
240
+ last_v = t.gsub(/\d+$/, (nm_.to_i + 1).to_s)
241
+ elsif /^version\-\d+\.\d+$/ =~ t # without fix number
242
+ last_v = t + '.1'
243
+ end
244
+ end
245
+ return last_v
246
+ else
247
+ return ''
248
+ end
249
+ end
250
+
251
+ end # class
252
+ end
@@ -0,0 +1,20 @@
1
+ module DevFlow
2
+ class Cleanup < App
3
+
4
+ def process!
5
+ completed_branches = Array.new
6
+ @roadmap.tasks.each {|t| completed_branches << t.branch_name if t.is_completed?}
7
+
8
+ @git.branches.each do |t|
9
+ if completed_branches.include? t
10
+ print "delete completed branch #{t}? [Y/n]:"
11
+ ans = STDIN.gets.chomp!
12
+ unless ans == 'n'
13
+ `git branch -d #{t}`
14
+ end
15
+ end
16
+ end
17
+ end
18
+
19
+ end # class
20
+ end
@@ -0,0 +1,69 @@
1
+ module DevFlow
2
+ class Close < App
3
+
4
+ def process!
5
+ self.hello
6
+
7
+ error "Only leader (#{leader_name.bold}) can close a branch." unless i_am_leader?
8
+
9
+ # whether I am working on a proper task branch
10
+ current_task = self.task
11
+ error "Not on a known task branch. Can not continue." unless current_task
12
+
13
+ if current_task and (in_release? or current_task.branch_name =~ /^hotfix\_/)
14
+ error "Use command 'release' to close a release/hotfix branch." unless @config[:release]
15
+ else
16
+ error "Use command 'close' to close a non-release branch." if @config[:release]
17
+ end
18
+
19
+ if in_release? and @config[:release]
20
+ error "You should use release only on branches just completed." unless current_task and current_task.progress == 99
21
+ end
22
+
23
+
24
+ self.ask_rebase true # force rebase
25
+ puts hr
26
+
27
+ # commit you current branch and push
28
+ progress = 100
29
+ message = ARGV[2] || "close the branch by set progress to 100."
30
+ message = "[close] " + message
31
+
32
+ info "commit progress"
33
+ `git commit -am '#{message}'`
34
+ if @config[:git_remote]
35
+ info "push your progress to remote server"
36
+ `git push #{@config[:git_remote]} #{current_task.branch_name}`
37
+ end
38
+
39
+ # goto develop branch and merge
40
+ `git checkout develop`
41
+ rslt = `git merge --no-ff #{current_task.branch_name}`
42
+ error "Not fast forward merge failed: #{rslt}" unless $?.success?
43
+
44
+ # rewrite progress in ROADMAP file under develop trunk
45
+ upload_progress! current_task, progress, true
46
+
47
+ # merge into the master
48
+ if @config[:release]
49
+ info "Merge the release branch into master trunk"
50
+ `git checkout master`
51
+ `git merge --no-ff develop`
52
+ tag = new_version current_task.branch_name
53
+ if tag
54
+ info "Tag your release as #{tag}"
55
+ `git tag #{tag}`
56
+ end
57
+ info "Push your change to remote server"
58
+ `git push #{@config[:git_remote]} --tags master` if @config[:git_remote]
59
+
60
+ puts "Now your are on branch #{'master'.bold.red}"
61
+ puts "You may want to review and test the program again and then switch back to develop trunk."
62
+ end
63
+
64
+ info "Delete closed branch #{current_task.branch_name}"
65
+ `git branch -d #{current_task.branch_name}`
66
+ end
67
+
68
+ end # class
69
+ end
@@ -0,0 +1,42 @@
1
+ module DevFlow
2
+ class Complete < App
3
+
4
+ def process!
5
+ self.hello
6
+
7
+ # whether I am working on a proper task branch
8
+ current_task = self.task
9
+ error "Not on a known task branch. Can not continue." unless current_task
10
+
11
+ unless current_task.resources.include?(@config[:whoami])
12
+ if i_have_power?
13
+ role = 'supervisor' if i_am_supervisor?
14
+ role = 'moderator' if i_am_moderator?
15
+ role = 'leader' if i_am_leader?
16
+ warn "You are complete the task as a #{role}"
17
+ else
18
+ error "You are not in the resource list for that task."
19
+ end
20
+ end
21
+
22
+ self.ask_rebase true # force rebase
23
+ puts hr
24
+
25
+ # commit you current branch and push
26
+ progress = 99
27
+ message = ARGV[2] || "complete your branch by set progress to 99."
28
+ message = "[complete] " + message
29
+
30
+ info "Commit your progress"
31
+ `git commit -am '#{message}'`
32
+ if @config[:git_remote]
33
+ info "push your progress to remote server"
34
+ `git push #{@config[:git_remote]} #{current_task.branch_name}`
35
+ end
36
+
37
+ # rewrite progress in ROADMAP file under develop trunk
38
+ upload_progress! current_task, progress, true
39
+ end
40
+
41
+ end # class
42
+ end
@@ -0,0 +1,103 @@
1
+ module DevFlow
2
+ class Info < App
3
+
4
+ def process!
5
+ self.hello
6
+
7
+ current_task = self.task
8
+ self.ask_rebase if current_task or in_trunk?
9
+
10
+ puts hr
11
+
12
+ # if i am the leader and there are closed branches, warn:
13
+ if i_am_leader? and tasks_for_close.size > 0
14
+ display_close_waiting
15
+ else
16
+ display_tasks
17
+ end
18
+ puts hrh
19
+
20
+ if @git.wd_clean?
21
+ # if work directory is clean, ready to switch
22
+ if i_am_leader? and in_release? # concentrate
23
+ puts "You are in a release branch, please release it as soon as possible."
24
+ else # otherwise show switch options
25
+ puts "You switch to other branches:".bold.yellow
26
+ puts "Type #{0.to_s.bold} to switch to develop trunk." unless @git.current_branch == 'develop'
27
+ puts "Simply press enter to keep working on the current branch."
28
+ print @waiting.keys.join(", ") + ":"
29
+
30
+ ans = STDIN.gets.chomp!
31
+ if ans == 0.to_s
32
+ switch_to! 'develop'
33
+ elsif @waiting[ans.to_i]
34
+ switch_to! @waiting[ans.to_i].branch_name
35
+
36
+ # if the task not started yet, update progress
37
+ upload_progress!(@waiting[ans.to_i], 10) unless @waiting[ans.to_i].progress > 0
38
+ else
39
+ error "Invalid input #{ans}. Can not continue." if ans and ans.size > 0
40
+ end
41
+ end
42
+ else # if the wd is not clean
43
+
44
+ if current_task and in_release?
45
+ if i_am_leader?
46
+ puts "You are in a release branch, if you are ready to release it, use:"
47
+ puts " $ dw release".bold.blue
48
+ else
49
+ puts "WARN: You are on a release branch, only leader can edit release branches.".yellow
50
+ puts "Please undo you edit. Do not push your edition to the remote server."
51
+ end
52
+ exit
53
+ end
54
+
55
+ if current_task and current_task.progress == 99
56
+ if i_am_leader
57
+ puts "You are in a branch marked complete. Please test and review the code, and close it by:"
58
+ puts " $ dw close".bold.blue
59
+ puts "Or reject the branch by issue:"
60
+ puts " $ dw progress 60".bold.blue
61
+ else
62
+ puts "WARN: You are on a completed branch, call the leader (#{leader_name.bold}) to close it.".yellow
63
+ end
64
+ exit
65
+ end
66
+
67
+ if current_task
68
+ unless current_task.resources.include? @config["whoami"]
69
+ puts "WARN: You are editing a task not assigned to you.".yellow
70
+ end
71
+
72
+ puts "You are encouraged to push your progress often by:"
73
+ puts " $ dw progress 40 'git commit message'".bold.blue
74
+ puts "Or use the short version:"
75
+ puts " $ dw pg 40 'git commit message'".bold.blue
76
+ puts "If you fully implemented and tested the code, issue:"
77
+ puts " $ dw complete"
78
+ puts "Then #{'do not forget'.bold.red} inform the leader (#{leader_name.bold})."
79
+ else
80
+ puts "You are not on any non-branches. You may commit all you changes and"
81
+ puts "switch to develop trunk before the next move:"
82
+ puts " $ git commit -am 'git commit message'".bold.blue
83
+ puts " $ git checkout develop".bold.blue
84
+ end
85
+
86
+ if in_trunk?
87
+ if @git.current_branch == 'develop' and i_have_power?
88
+ puts "You should only edit ROADMAP files on develop trunk, when you done, use:"
89
+ puts " $ dw update-roadmap".bold.blue
90
+ puts "Or use the short version:"
91
+ puts " $ dw ur".bold.blue
92
+ else
93
+ warn "Avoid directly edit files under trunk branches."
94
+ puts "Please undo you change and switch to work on a task branch."
95
+ end
96
+ end
97
+
98
+ end
99
+ hrb
100
+ end
101
+
102
+ end # class
103
+ end
@@ -0,0 +1,87 @@
1
+ module DevFlow
2
+ class Init < App
3
+
4
+ def process!
5
+ local_configuration = {}
6
+ if File.exists? @config[:local_config]
7
+ local_configuration = YAML.load(File.open(@config[:local_config], 'r:utf-8').read) || {}
8
+ end
9
+
10
+ # find the current user
11
+ sugguest = @config["whoami"] if @config["whoami"]
12
+ unless all_member_names.include? sugguest
13
+ info "use system 'whoami' command to find user name"
14
+ begin
15
+ suggest = `whoami`
16
+ end
17
+ info "found #{suggest}" if all_member_names.include? sugguest
18
+ end
19
+
20
+ unless all_member_names.include? sugguest
21
+ info "use git config to find user name"
22
+ sugguest = @git.config["user.email"].gsub(/\@.+$/, '') if @git.config["user.email"]
23
+ info "found #{suggest}" if all_member_names.include? sugguest
24
+ end
25
+
26
+ # ask the user for the user name
27
+ puts "Tell us who you are: ".bold.yellow
28
+ msg = all_member_names.join(", ")
29
+ if all_member_names.include? sugguest
30
+ msg += " [#{sugguest}]"
31
+ end
32
+
33
+ print msg + ":"
34
+ ans = STDIN.gets.chomp!
35
+ ans = sugguest unless ans.size > 0
36
+ error "Unknown member! Can not continue." unless all_member_names.include? ans
37
+
38
+ # find the default git remote server
39
+ @config["whoami"] = ans
40
+ info "Welcome #{self.user_name.bold}!"
41
+
42
+ remotes = @git.remote_list
43
+ error "You need to set at least one remote git server to interact with!" unless remotes.size > 0
44
+
45
+ puts "Which remote git server to use?".bold.yellow
46
+ msg = remotes.join ", "
47
+
48
+ suggest = @config["git_remote"] || 'origin'
49
+ msg += " [#{suggest}]" if remotes.include? suggest
50
+ print msg + ":"
51
+
52
+ ans2 = STDIN.gets.chomp!
53
+ ans2 = suggest unless ans2.size > 0
54
+ error "You must define a valid git remote server" unless remotes.include? ans2
55
+
56
+ # write out to the local configuration file
57
+ info "write contents to local configuration file"
58
+ write_local_config(local_configuration.merge({"whoami" => ans, "git_remote" => ans2}))
59
+ add_to_gitignore()
60
+ end
61
+
62
+ def write_local_config hash
63
+ wh = File.open(@config[:local_config], 'w:utf-8')
64
+ YAML.dump(hash, wh)
65
+ wh.close
66
+ end
67
+
68
+ def add_to_gitignore
69
+ had = false
70
+ if File.exists?(".gitignore")
71
+ fh = File.open(".gitignore")
72
+ fh.each do |line|
73
+ had = true if line =~ /#{@config[:local_config]}/
74
+ end
75
+ fh.close
76
+ end
77
+
78
+ unless had
79
+ info "add local configuration file to gitignore list"
80
+ wh = File.open(".gitignore", 'a')
81
+ wh.puts @config[:local_config]
82
+ wh.close
83
+ end
84
+ end
85
+
86
+ end # class
87
+ end
@@ -0,0 +1,35 @@
1
+ module DevFlow
2
+ class Progress < App
3
+
4
+ def process!
5
+ self.hello
6
+
7
+ # whether I am working on a proper task branch
8
+ current_task = self.task
9
+ error "Not on a known task branch. Can not continue." unless current_task
10
+
11
+ self.ask_rebase
12
+ puts hr
13
+
14
+ # commit you current branch and push
15
+ progress = ARGV[1]
16
+ progress = progress.to_i if progress
17
+ unless (progress and progress > 0 and progress < 99)
18
+ error "invalid progress. Use percentage between 1 to 98."
19
+ end
20
+ message = ARGV[2] || "update progress to #{progress}"
21
+ message = "[progress] " + message
22
+
23
+ info "commit your progress"
24
+ `git commit -am '#{message}'`
25
+ if @config[:git_remote]
26
+ info "push your progress to remote server"
27
+ `git push #{@config[:git_remote]} #{current_task.branch_name}`
28
+ end
29
+
30
+ # rewrite progress in ROADMAP file under develop trunk
31
+ upload_progress! current_task, progress
32
+ end
33
+
34
+ end # class
35
+ end
@@ -0,0 +1,17 @@
1
+ module DevFlow
2
+ class Ur < App
3
+
4
+ def process!
5
+ error "Not on develop trunk" unless @git.current_branch == 'develop'
6
+ error "Only leader/moderator and supervisor can edit ROADMAP" unless i_have_power?
7
+ #p @git.modified_files
8
+ error "No change detected on #{@config[:roadmap]}" unless @git.modified_files.include? File.expand_path(@config[:roadmap])
9
+
10
+ `git add .`
11
+ `git commit -am 'update roadmap'`
12
+ info "Push your change to the remote server"
13
+ `git push #{@config[:git_remote]} develop` if @config[:git_remote]
14
+ end
15
+
16
+ end # class
17
+ end