git-contest 0.2.4 → 0.2.5

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.
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