git-contest 0.2.4 → 0.2.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (52) hide show
  1. checksums.yaml +8 -8
  2. data/.travis.yml +21 -3
  3. data/README.md +5 -0
  4. data/Rakefile +8 -0
  5. data/appveyor.yml +21 -0
  6. data/bin/git-contest +4 -33
  7. data/bin/git-contest-config +4 -241
  8. data/bin/git-contest-finish +4 -151
  9. data/bin/git-contest-init +4 -65
  10. data/bin/git-contest-list +4 -64
  11. data/bin/git-contest-rebase +4 -64
  12. data/bin/git-contest-start +4 -57
  13. data/bin/git-contest-submit +4 -183
  14. data/git-contest.gemspec +2 -1
  15. data/lib/contest/driver/base.rb +7 -4
  16. data/lib/git/contest/command_line.rb +10 -0
  17. data/lib/git/contest/command_line/command.rb +117 -0
  18. data/lib/git/contest/command_line/main_command.rb +56 -0
  19. data/lib/git/contest/command_line/sub_commands.rb +35 -0
  20. data/lib/git/contest/command_line/sub_commands/config_command.rb +258 -0
  21. data/lib/git/contest/command_line/sub_commands/finish_command.rb +161 -0
  22. data/lib/git/contest/command_line/sub_commands/init_command.rb +79 -0
  23. data/lib/git/contest/command_line/sub_commands/list_command.rb +89 -0
  24. data/lib/git/contest/command_line/sub_commands/rebase_command.rb +87 -0
  25. data/lib/git/contest/command_line/sub_commands/start_command.rb +77 -0
  26. data/lib/git/contest/command_line/sub_commands/submit_command.rb +220 -0
  27. data/lib/git/contest/common.rb +6 -6
  28. data/lib/git/contest/git.rb +160 -156
  29. data/lib/git/contest/version.rb +1 -1
  30. data/spec/command/t004_git_contest_submit_spec.rb +254 -0
  31. data/spec/command/t005_git_contest_branching_spec.rb +90 -0
  32. data/spec/command/t007_git_contest_start_spec.rb +95 -0
  33. data/spec/command/t008_git_contest_finish_spec.rb +171 -0
  34. data/spec/command/t009_git_contest_init_spec.rb +85 -0
  35. data/spec/command/t012_git_contest_list_spec.rb +123 -0
  36. data/spec/command/t013_git_contest_config_spec.rb +186 -0
  37. data/spec/{lib/contest/driver → contest_driver}/t001_aizu_online_judge_spec.rb +1 -1
  38. data/spec/{lib/contest/driver → contest_driver}/t002_codeforces_spec.rb +1 -1
  39. data/spec/{lib/contest/driver → contest_driver}/t003_uva_online_judge_spec.rb +1 -1
  40. data/spec/{lib/contest/driver → contest_driver}/t010_kattis_spec.rb +1 -1
  41. data/spec/{lib/contest/driver → contest_driver}/t011_utils_spec.rb +0 -0
  42. data/spec/spec_helper.rb +18 -12
  43. data/spec/t006_config_spec.rb +37 -31
  44. metadata +57 -33
  45. data/spec/bin/t004_git_contest_submit_spec.rb +0 -223
  46. data/spec/bin/t005_git_contest_branching_spec.rb +0 -70
  47. data/spec/bin/t007_git_contest_start_spec.rb +0 -88
  48. data/spec/bin/t008_git_contest_finish_spec.rb +0 -162
  49. data/spec/bin/t009_git_contest_init_spec.rb +0 -55
  50. data/spec/bin/t012_git_contest_list_spec.rb +0 -99
  51. data/spec/bin/t013_git_contest_config_spec.rb +0 -149
  52. data/spec/spec_list.txt +0 -1
@@ -0,0 +1,220 @@
1
+ #
2
+ # git-contest-submit
3
+ # https://github.com/sh19910711/git-contest
4
+ #
5
+ # Copyright (c) 2013-2014 Hiroyuki Sano <sh19910711 at gmail.com>
6
+ # Licensed under the MIT-License.
7
+ #
8
+
9
+ module CommandLine
10
+
11
+ module SubCommands
12
+
13
+ class SubmitCommand < Command
14
+
15
+ def initialize(new_args, new_input_stream = STDIN)
16
+ super
17
+
18
+ # load sites
19
+ $config = get_config() || {}
20
+ $sites = {}
21
+ if $config.has_key? 'sites'
22
+ $sites = $config["sites"]
23
+ end
24
+
25
+ # load drivers
26
+ Contest::Driver::Utils.load_plugins
27
+
28
+ $drivers = {}
29
+ load_drivers
30
+ end
31
+
32
+ def define_options
33
+ opt_parser.on "-h", "--help", "help" do
34
+ usage
35
+ exit 0
36
+ end
37
+ end
38
+
39
+ def set_default_options
40
+ end
41
+
42
+ def run
43
+
44
+ # check options
45
+ sub_commands = $sites.keys
46
+
47
+ # detect site
48
+ unless has_next_token?
49
+ usage
50
+ exit 0
51
+ end
52
+
53
+ site = next_token.strip
54
+
55
+ unless $sites.has_key?(site)
56
+ if site != ""
57
+ puts "site not found"
58
+ else
59
+ usage
60
+ end
61
+ exit 0
62
+ end
63
+
64
+ # detect driver
65
+ driver_name = $sites[site]["driver"]
66
+
67
+ unless $drivers.has_key?(driver_name)
68
+ puts "driver not found"
69
+ exit
70
+ end
71
+
72
+ #
73
+ # Submit Start
74
+ #
75
+ driver = $drivers[driver_name].new(args)
76
+
77
+ $submit_info = {}
78
+
79
+ # set events
80
+ driver.on(
81
+ 'start',
82
+ Proc.new do
83
+ puts "@start: submit"
84
+ end
85
+ )
86
+
87
+ driver.on(
88
+ 'before_login',
89
+ Proc.new do
90
+ puts "@submit: logging in..."
91
+ end
92
+ )
93
+
94
+ driver.on(
95
+ 'after_login',
96
+ Proc.new do
97
+ puts "@submit: login ok"
98
+ end
99
+ )
100
+
101
+ driver.on(
102
+ 'before_submit',
103
+ Proc.new do |submit_info|
104
+ $submit_info = submit_info
105
+ puts "@submit: doing..."
106
+ end
107
+ )
108
+
109
+ driver.on(
110
+ 'after_submit',
111
+ Proc.new do
112
+ puts "@submit: done"
113
+ end
114
+ )
115
+
116
+ driver.on(
117
+ 'before_wait',
118
+ Proc.new do
119
+ print "@result: waiting..."
120
+ end
121
+ )
122
+
123
+ driver.on(
124
+ 'retry',
125
+ Proc.new do
126
+ print "."
127
+ end
128
+ )
129
+
130
+ driver.on(
131
+ 'after_wait',
132
+ Proc.new do |submission_info|
133
+ puts ""
134
+ next unless submission_info.is_a?(Hash)
135
+ puts ""
136
+ puts "@result: Submission Result"
137
+ puts " %s: %s" % ["submission id", "#{submission_info[:submission_id]}"]
138
+ puts " %s: %s" % ["status", "#{submission_info[:status]}"]
139
+ puts ""
140
+ if Git.contest_is_initialized
141
+ puts "@commit"
142
+ Git.do "add #{get_git_add_target($config["submit_rules"]["add"] || ".")}"
143
+ Git.do "commit --allow-empty -m \"#{submission_info[:result]}\""
144
+ end
145
+ end
146
+ )
147
+
148
+ driver.on(
149
+ 'finish',
150
+ Proc.new do
151
+ puts "@finish"
152
+ end
153
+ )
154
+
155
+ # global config
156
+ $config["submit_rules"] ||= {}
157
+ $config["file"] ||= {}
158
+
159
+ # set config
160
+ driver.config = $sites[site]
161
+ driver.config.merge! $config
162
+
163
+ # parse driver options
164
+ driver.options = driver.get_opts()
165
+
166
+ result = driver.submit()
167
+
168
+ end
169
+
170
+ private
171
+
172
+ # Load Drivers
173
+ #
174
+ def load_drivers
175
+ driver_names = $sites.keys().map {|key| $sites[key]["driver"] }
176
+ driver_names.uniq.each do |driver_name|
177
+ class_name = driver_name.clone
178
+ class_name.gsub!(/^[a-z]/) {|c| c.upcase }
179
+ class_name.gsub!(/(_)([a-z])/) {|c, b| $2.upcase }
180
+ $drivers[driver_name] = Contest::Driver.const_get "#{class_name}Driver"
181
+ end
182
+ end
183
+
184
+ #
185
+ # Command Utils
186
+ #
187
+ def usage
188
+ puts get_banner
189
+ return 0
190
+ end
191
+
192
+ def get_banner
193
+ res = ""
194
+ res += "usage: git contest submit <site>\n"
195
+ res += "\n"
196
+ res += "Available sites are:\n"
197
+ $sites.keys().each do |site|
198
+ if $drivers.has_key? $sites[site]["driver"]
199
+ driver = $drivers[$sites[site]["driver"]].new
200
+ res += " %-12s\t#{driver.get_desc}\n" % [site]
201
+ else
202
+ # TODO: driver not found
203
+ end
204
+ end
205
+ res += "\n"
206
+ res += "Try 'git contest submit <site> --help' for details.\n"
207
+ return res
208
+ end
209
+
210
+ def get_git_add_target rule
211
+ str = rule
212
+ str = str.gsub('${source}', $submit_info[:source])
213
+ str
214
+ end
215
+
216
+ end # SubmitCommand
217
+
218
+ end # SubCommands
219
+
220
+ end # CommandLine
@@ -17,12 +17,12 @@ def init
17
17
  end
18
18
 
19
19
  def init_global
20
- $git_contest_home = File.expand_path(ENV['GIT_CONTEST_HOME'] || "~/.git-contest")
21
- $git_contest_config = File.expand_path(ENV['GIT_CONTEST_CONFIG'] || "#{$git_contest_home}/config.yml")
22
- if git_do_no_echo 'branch'
23
- $MASTER = git_do 'config --get git.contest.branch.master'
24
- $PREFIX = git_do 'config --get git.contest.branch.prefix'
25
- $ORIGIN = git_do 'config --get git.contest.origin'
20
+ $git_contest_home = File.expand_path(ENV['GIT_CONTEST_HOME'] || File.join("~", ".git-contest"))
21
+ $git_contest_config = File.expand_path(ENV['GIT_CONTEST_CONFIG'] || File.join($git_contest_home, "config.yml"))
22
+ if Git.do_no_echo 'branch'
23
+ $MASTER = Git.do 'config --get git.contest.branch.master'
24
+ $PREFIX = Git.do 'config --get git.contest.branch.prefix'
25
+ $ORIGIN = Git.do 'config --get git.contest.origin'
26
26
  if $ORIGIN == ''
27
27
  $ORIGIN = 'origin'
28
28
  end
@@ -5,185 +5,189 @@
5
5
  # Licensed under the MIT-License.
6
6
  #
7
7
 
8
- def git_do(*args)
9
- puts "git #{args.join(' ')}" if ENV['GIT_CONTEST_DEBUG'] == 'ON'
10
- return `git #{args.join(' ')} 2>&1`.strip
11
- end
8
+ module Git
12
9
 
13
- # use return value
14
- def git_do_no_echo(*args)
15
- puts "git #{args.join(' ')}" if ENV['GIT_CONTEST_DEBUG'] == 'ON'
16
- system "git #{args.join(' ')} >/dev/null 2>&1"
17
- end
10
+ def self.do(*args)
11
+ puts "git #{args.join(' ')}" if ENV['GIT_CONTEST_DEBUG'] == 'ON'
12
+ `git #{args.join(' ')} 2>&1`.strip
13
+ end
18
14
 
19
- #
20
- def git_contest_is_initialized
21
- git_contest_has_master_configured &&
22
- git_contest_has_prefix_configured &&
23
- git_do('config --get git.contest.branch.master') != git_do('config --get git.contest.branch.develop')
24
- end
25
-
26
- def git_contest_has_master_configured
27
- master = (git_do 'config --get git.contest.branch.master').strip
28
- master != '' && git_local_branches().include?(master)
29
- end
30
-
31
- def git_contest_has_develop_configured
32
- develop = (git_do 'config --get git.contest.branch.develop').strip
33
- develop != '' && git_local_branches().include?(develop)
34
- end
35
-
36
- def git_contest_has_prefix_configured
37
- git_do_no_echo 'config --get git.contest.branch.prefix'
38
- end
39
-
40
- def git_contest_resolve_nameprefix name, prefix
41
- if git_local_branch_exists "#{prefix}/#{name}"
42
- return name
43
- end
44
- branches = git_local_branches().select {|branch| branch.start_with? "#{prefix}/#{name}" }
45
- if branches.size == 0
46
- abort "No branch matches prefix '#{name}'"
47
- else
48
- if branches.size == 1
49
- return branches[0][prefix.length..-1]
50
- else
51
- abort "Multiple branches match prefix '#{name}'"
52
- end
15
+ # use return value
16
+ def self.do_no_echo(*args)
17
+ puts "git #{args.join(' ')}" if ENV['GIT_CONTEST_DEBUG'] == 'ON'
18
+ `git #{args.join(' ')} 2>&1`
19
+ $?.success?
53
20
  end
54
- end
55
21
 
56
- #
57
- def git_remote_branch_exists(branch_name)
58
- git_remote_branches().include?(branch_name)
59
- end
60
-
61
- def git_local_branch_exists(branch_name)
62
- git_local_branches().include?(branch_name)
63
- end
64
-
65
- def git_branch_exists(branch_name)
66
- git_all_branches().include?(branch_name)
67
- end
68
-
69
- def git_remote_branches
70
- cmd_ret = git_do 'branch -r --no-color'
71
- cmd_ret.lines.map {|line|
72
- line.gsub(/^[*]?\s*/, '').gsub(/\s*$/, '').strip
73
- }
74
- end
75
-
76
- def git_local_branches
77
- cmd_ret = git_do 'branch --no-color'
78
- cmd_ret.lines.map {|line|
79
- line.gsub(/^[*]?\s*/, '').gsub(/\s*$/, '').strip
80
- }
81
- end
82
-
83
- def git_all_branches
84
- cmd_ret1 = git_do 'branch --no-color'
85
- cmd_ret2 = git_do 'branch -r --no-color'
86
- lines = ( cmd_ret1 + cmd_ret2 ).lines
87
- lines.map {|line|
88
- line.gsub(/^[*]?\s*/, '').gsub(/\s*$/, '').strip
89
- }
90
- end
91
-
92
- def git_current_branch
93
- ret = git_do('branch --no-color').lines
94
- ret = ret.grep /^\*/
95
- ret[0].gsub(/^[* ] /, '').strip
96
- end
97
-
98
- def git_is_clean_working_tree
99
- if ! git_do_no_echo 'diff --no-ext-diff --ignore-submodules --quiet --exit-code'
100
- return 1
101
- elsif ! git_do_no_echo 'diff-index --cached --quiet --ignore-submodules HEAD --'
102
- return 2
103
- else
104
- return 0
105
- end
106
- end
107
-
108
- def git_repo_is_headless
109
- ! git_do_no_echo 'rev-parse --quiet --verify HEAD'
110
- end
22
+ #
23
+ def self.contest_is_initialized
24
+ Git.contest_has_master_configured &&
25
+ Git.contest_has_prefix_configured &&
26
+ Git.do('config --get git.contest.branch.master') != Git.do('config --get git.contest.branch.develop')
27
+ end
111
28
 
112
- #
113
- # 0: same
114
- # 1: first branch needs ff
115
- # 2: second branch needs ff
116
- # 3: branch needs merge
117
- # 4: there is no merge
118
- #
119
- def git_compare_branches first, second
120
- commit1 = git_do "rev-parse \"#{first}\""
121
- commit2 = git_do "rev-parse \"#{second}\""
122
- if commit1 != commit2
123
- if git_do_no_echo("merge-base \"#{commit1}\" \"#{commit2}\"") > 0
124
- return 4
29
+ def self.contest_has_master_configured
30
+ master = (Git.do 'config --get git.contest.branch.master').strip
31
+ master != '' && Git.local_branches().include?(master)
32
+ end
33
+
34
+ def self.contest_has_develop_configured
35
+ develop = (Git.do 'config --get git.contest.branch.develop').strip
36
+ develop != '' && Git.local_branches().include?(develop)
37
+ end
38
+
39
+ def self.contest_has_prefix_configured
40
+ Git.do_no_echo 'config --get git.contest.branch.prefix'
41
+ end
42
+
43
+ def self.contest_resolve_nameprefix name, prefix
44
+ if Git.local_branch_exists "#{prefix}/#{name}"
45
+ return name
46
+ end
47
+ branches = Git.local_branches().select {|branch| branch.start_with? "#{prefix}/#{name}" }
48
+ if branches.size == 0
49
+ abort "No branch matches prefix '#{name}'"
125
50
  else
126
- base = git_do "merge-base \"#{commit1}\" \"#{commit2}\""
127
- if commit1 == base
128
- return 1
129
- elsif commit2 == base
130
- return 2
51
+ if branches.size == 1
52
+ return branches[0][prefix.length..-1]
131
53
  else
132
- return 3
54
+ abort "Multiple branches match prefix '#{name}'"
133
55
  end
134
56
  end
135
- else
136
- return 0
137
57
  end
138
- end
139
58
 
140
- def require_branch(branch)
141
- if ! git_all_branches().include?(branch)
142
- abort "Branch #{branch} does not exist."
59
+ #
60
+ def self.remote_branch_exists(branch_name)
61
+ Git.remote_branches().include?(branch_name)
62
+ end
63
+
64
+ def self.local_branch_exists(branch_name)
65
+ Git.local_branches().include?(branch_name)
143
66
  end
144
- end
145
67
 
146
- def require_branch_absent(branch)
147
- if git_all_branches().include?(branch)
148
- abort "Branch #{branch} already exists. Pick another name."
68
+ def self.branch_exists(branch_name)
69
+ Git.all_branches().include?(branch_name)
149
70
  end
150
- end
151
71
 
152
- def require_clean_working_tree
153
- ret = git_is_clean_working_tree
154
- if ret == 1
155
- abort "fatal: Working tree contains unstaged changes. Aborting."
72
+ def self.remote_branches
73
+ cmd_ret = Git.do 'branch -r --no-color'
74
+ cmd_ret.lines.map {|line|
75
+ line.gsub(/^[*]?\s*/, '').gsub(/\s*$/, '').strip
76
+ }
156
77
  end
157
- if ret == 2
158
- abort "fatal: Index contains uncommited changes. Aborting."
78
+
79
+ def self.local_branches
80
+ cmd_ret = Git.do 'branch --no-color'
81
+ cmd_ret.lines.map {|line|
82
+ line.gsub(/^[*]?\s*/, '').gsub(/\s*$/, '').strip
83
+ }
159
84
  end
160
- end
161
85
 
162
- def require_local_branch branch
163
- if ! git_local_branch_exists branch
164
- abort "fatal: Local branch '#{branch}' does not exist and is required."
86
+ def self.all_branches
87
+ cmd_ret1 = Git.do 'branch --no-color'
88
+ cmd_ret2 = Git.do 'branch -r --no-color'
89
+ lines = ( cmd_ret1 + cmd_ret2 ).lines
90
+ lines.map {|line|
91
+ line.gsub(/^[*]?\s*/, '').gsub(/\s*$/, '').strip
92
+ }
165
93
  end
166
- end
167
94
 
168
- def require_remote_branch branch
169
- if ! git_remote_branch_exists branch
170
- abort "fatal: Remote branch '#{branch}' does not exist and is required."
95
+ def self.current_branch
96
+ ret = Git.do('branch --no-color').lines
97
+ ret = ret.grep /^\*/
98
+ ret[0].gsub(/^[* ] /, '').strip
171
99
  end
172
- end
173
100
 
174
- def require_branches_equal local, remote
175
- require_local_branch local
176
- require_remote_branch remote
177
- ret = git_compare_branches local, remote
178
- if ret > 0
179
- puts "Branches '#{local}' and '#{remote}' have diverged."
180
- if ret == 1
181
- abort "And branch #{local} may be fast-forwarded."
182
- elsif ret == 2
183
- puts "And local branch #{local} is ahead of #{remote}"
101
+ def self.is_clean_working_tree
102
+ if ! Git.do_no_echo 'diff --no-ext-diff --ignore-submodules --quiet --exit-code'
103
+ return 1
104
+ elsif ! Git.do_no_echo 'diff-index --cached --quiet --ignore-submodules HEAD --'
105
+ return 2
184
106
  else
185
- abort "Branches need merging first."
107
+ return 0
108
+ end
109
+ end
110
+
111
+ def self.repo_is_headless
112
+ ! Git.do_no_echo 'rev-parse --quiet --verify HEAD'
113
+ end
114
+
115
+ #
116
+ # 0: same
117
+ # 1: first branch needs ff
118
+ # 2: second branch needs ff
119
+ # 3: branch needs merge
120
+ # 4: there is no merge
121
+ #
122
+ def self.compare_branches first, second
123
+ commit1 = Git.do "rev-parse \"#{first}\""
124
+ commit2 = Git.do "rev-parse \"#{second}\""
125
+ if commit1 != commit2
126
+ if Git.do_no_echo("merge-base \"#{commit1}\" \"#{commit2}\"")
127
+ return 4
128
+ else
129
+ base = Git.do "merge-base \"#{commit1}\" \"#{commit2}\""
130
+ if commit1 == base
131
+ return 1
132
+ elsif commit2 == base
133
+ return 2
134
+ else
135
+ return 3
136
+ end
137
+ end
138
+ else
139
+ return 0
140
+ end
141
+ end
142
+
143
+ def self.require_branch(branch)
144
+ if ! Git.all_branches().include?(branch)
145
+ abort "Branch #{branch} does not exist."
146
+ end
147
+ end
148
+
149
+ def self.require_branch_absent(branch)
150
+ if Git.all_branches().include?(branch)
151
+ abort "Branch #{branch} already exists. Pick another name."
152
+ end
153
+ end
154
+
155
+ def self.require_clean_working_tree
156
+ ret = Git.is_clean_working_tree
157
+ if ret == 1
158
+ abort "fatal: Working tree contains unstaged changes. Aborting."
159
+ end
160
+ if ret == 2
161
+ abort "fatal: Index contains uncommited changes. Aborting."
162
+ end
163
+ end
164
+
165
+ def self.require_local_branch branch
166
+ if ! Git.local_branch_exists branch
167
+ abort "fatal: Local branch '#{branch}' does not exist and is required."
168
+ end
169
+ end
170
+
171
+ def self.require_remote_branch branch
172
+ if ! Git.remote_branch_exists branch
173
+ abort "fatal: Remote branch '#{branch}' does not exist and is required."
174
+ end
175
+ end
176
+
177
+ def self.require_branches_equal local, remote
178
+ Git.require_local_branch local
179
+ Git.require_remote_branch remote
180
+ ret = Git.compare_branches local, remote
181
+ if ret > 0
182
+ puts "Branches '#{local}' and '#{remote}' have diverged."
183
+ if ret == 1
184
+ abort "And branch #{local} may be fast-forwarded."
185
+ elsif ret == 2
186
+ puts "And local branch #{local} is ahead of #{remote}"
187
+ else
188
+ abort "Branches need merging first."
189
+ end
186
190
  end
187
191
  end
188
- end
189
192
 
193
+ end # Git