dev_flow 0.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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