flash_flow 1.3.1 → 1.3.2
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.
- checksums.yaml +4 -4
- data/.ruby-version +1 -1
- data/Gemfile.lock +18 -16
- data/README.md +1 -1
- data/bin/flash_flow +4 -0
- data/flash_flow.yml.erb.example +1 -0
- data/lib/flash_flow/branch_merger.rb +7 -4
- data/lib/flash_flow/cmd_runner.rb +1 -0
- data/lib/flash_flow/data/base.rb +2 -2
- data/lib/flash_flow/data/branch.rb +1 -0
- data/lib/flash_flow/data/collection.rb +4 -0
- data/lib/flash_flow/data/github.rb +6 -1
- data/lib/flash_flow/deploy.rb +108 -13
- data/lib/flash_flow/git.rb +23 -24
- data/lib/flash_flow/issue_tracker/pivotal.rb +2 -2
- data/lib/flash_flow/merge_master/merge_status.html.erb +5 -5
- data/lib/flash_flow/merge_master/status.rb +30 -6
- data/lib/flash_flow/notifier/hipchat.rb +9 -3
- data/lib/flash_flow/options.rb +2 -0
- data/lib/flash_flow/version.rb +1 -1
- data/test/lib/data/test_collection.rb +5 -5
- data/test/lib/merge_master/test_status.rb +25 -5
- data/test/lib/test_branch_merger.rb +2 -1
- data/test/lib/test_deploy.rb +6 -13
- metadata +3 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: de553df31b0a6a9dfd5740c63c8319d9709df4fb
|
|
4
|
+
data.tar.gz: 49eabf79d3b1d31d3df371ee18eedbe324fca399
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: a27253b1eb9e7bbc74347728049b29125ff4ebd92280fdcfb339f6e89bbc502fdbaca44093ef38fe977c36c4f70fb7e265b251a311c3825240be4d8d5d5e81c7
|
|
7
|
+
data.tar.gz: 11f3fe54cf130178f9a4e66fc78b3ed71f883f148946391303500c3cc5622b789d4a7fafd2ea5c700c84b9124605caee9af68133f5cee7f9c18d2536110b7c81
|
data/.ruby-version
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
2.1
|
|
1
|
+
2.3.1
|
data/Gemfile.lock
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
flash_flow (1.3.
|
|
4
|
+
flash_flow (1.3.2)
|
|
5
5
|
hipchat (~> 1.5)
|
|
6
6
|
octokit (~> 4.1)
|
|
7
7
|
pivotal-tracker (~> 0.5)
|
|
@@ -10,21 +10,21 @@ PATH
|
|
|
10
10
|
GEM
|
|
11
11
|
remote: https://rubygems.org/
|
|
12
12
|
specs:
|
|
13
|
-
addressable (2.
|
|
13
|
+
addressable (2.4.0)
|
|
14
14
|
builder (3.2.2)
|
|
15
15
|
byebug (3.5.1)
|
|
16
16
|
columnize (~> 0.8)
|
|
17
17
|
debugger-linecache (~> 1.2)
|
|
18
18
|
slop (~> 3.6)
|
|
19
19
|
columnize (0.9.0)
|
|
20
|
-
crack (0.4.
|
|
20
|
+
crack (0.4.3)
|
|
21
21
|
safe_yaml (~> 1.0.0)
|
|
22
22
|
debugger-linecache (1.2.0)
|
|
23
|
-
domain_name (0.5.
|
|
23
|
+
domain_name (0.5.20160310)
|
|
24
24
|
unf (>= 0.0.5, < 1.0.0)
|
|
25
25
|
faraday (0.9.2)
|
|
26
26
|
multipart-post (>= 1.2, < 3)
|
|
27
|
-
hipchat (1.5.
|
|
27
|
+
hipchat (1.5.3)
|
|
28
28
|
httparty
|
|
29
29
|
mimemagic
|
|
30
30
|
http-cookie (1.0.2)
|
|
@@ -33,26 +33,28 @@ GEM
|
|
|
33
33
|
json (~> 1.8)
|
|
34
34
|
multi_xml (>= 0.5.2)
|
|
35
35
|
json (1.8.3)
|
|
36
|
-
mime-types (2.99)
|
|
37
|
-
mimemagic (0.3.
|
|
38
|
-
mini_portile2 (2.
|
|
36
|
+
mime-types (2.99.2)
|
|
37
|
+
mimemagic (0.3.1)
|
|
38
|
+
mini_portile2 (2.1.0)
|
|
39
39
|
minitest (5.3.5)
|
|
40
40
|
minitest-stub_any_instance (1.0.1)
|
|
41
41
|
multi_xml (0.5.5)
|
|
42
42
|
multipart-post (2.0.0)
|
|
43
43
|
netrc (0.11.0)
|
|
44
|
-
nokogiri (1.6.
|
|
45
|
-
mini_portile2 (~> 2.
|
|
44
|
+
nokogiri (1.6.8)
|
|
45
|
+
mini_portile2 (~> 2.1.0)
|
|
46
|
+
pkg-config (~> 1.1.7)
|
|
46
47
|
nokogiri-happymapper (0.5.9)
|
|
47
48
|
nokogiri (~> 1.5)
|
|
48
|
-
octokit (4.
|
|
49
|
-
sawyer (~> 0.
|
|
49
|
+
octokit (4.3.0)
|
|
50
|
+
sawyer (~> 0.7.0, >= 0.5.3)
|
|
50
51
|
pivotal-tracker (0.5.13)
|
|
51
52
|
builder
|
|
52
53
|
crack
|
|
53
54
|
nokogiri (>= 1.5.5)
|
|
54
55
|
nokogiri-happymapper (>= 0.5.4)
|
|
55
56
|
rest-client (>= 1.8.0)
|
|
57
|
+
pkg-config (1.1.7)
|
|
56
58
|
rake (10.4.2)
|
|
57
59
|
rest-client (1.8.0)
|
|
58
60
|
http-cookie (>= 1.0.2, < 2.0)
|
|
@@ -60,13 +62,13 @@ GEM
|
|
|
60
62
|
netrc (~> 0.7)
|
|
61
63
|
ruby-graphviz (1.2.2)
|
|
62
64
|
safe_yaml (1.0.4)
|
|
63
|
-
sawyer (0.
|
|
64
|
-
addressable (
|
|
65
|
+
sawyer (0.7.0)
|
|
66
|
+
addressable (>= 2.3.5, < 2.5)
|
|
65
67
|
faraday (~> 0.8, < 0.10)
|
|
66
68
|
slop (3.6.0)
|
|
67
69
|
unf (0.1.4)
|
|
68
70
|
unf_ext
|
|
69
|
-
unf_ext (0.0.7.
|
|
71
|
+
unf_ext (0.0.7.2)
|
|
70
72
|
|
|
71
73
|
PLATFORMS
|
|
72
74
|
ruby
|
|
@@ -80,4 +82,4 @@ DEPENDENCIES
|
|
|
80
82
|
rake (> 0)
|
|
81
83
|
|
|
82
84
|
BUNDLED WITH
|
|
83
|
-
1.
|
|
85
|
+
1.12.5
|
data/README.md
CHANGED
|
@@ -147,7 +147,7 @@ When we first started using flash_flow, if your branch had a merge conflict you
|
|
|
147
147
|
the branch that you were conflicting with to be merged to master, merge master into your branch, and then try again to
|
|
148
148
|
get your code into the merge branch.
|
|
149
149
|
|
|
150
|
-
Then we discovered git rerere
|
|
150
|
+
Then we discovered `git rerere`, which is [the coolest feature of git that almost no one seems to have heard of](https://git-scm.com/blog/2010/03/08/rerere.html). Basically
|
|
151
151
|
what rerere does is remember how you resolved conflicts and auto-apply those patches when it notices the same conflicts.
|
|
152
152
|
|
|
153
153
|
If your branch has a conflict with the `merge_branch` flash_flow will look for a rerere patch and apply that if it
|
data/bin/flash_flow
CHANGED
|
@@ -26,6 +26,10 @@ case
|
|
|
26
26
|
FlashFlow::Resolve.new(FlashFlow::Config.configuration.git, FlashFlow::Config.configuration.branch_info_file, logger: FlashFlow::Config.configuration.logger).manual_instructions
|
|
27
27
|
when options[:merge_status]
|
|
28
28
|
FlashFlow::MergeMaster::Status.new(FlashFlow::Config.configuration.issue_tracker, FlashFlow::Config.configuration.branches, FlashFlow::Config.configuration.branch_info_file, FlashFlow::Config.configuration.git, logger: FlashFlow::Config.configuration.logger).status
|
|
29
|
+
when options[:merge_status_html]
|
|
30
|
+
FlashFlow::MergeMaster::Status.new(FlashFlow::Config.configuration.issue_tracker, FlashFlow::Config.configuration.branches, FlashFlow::Config.configuration.branch_info_file, FlashFlow::Config.configuration.git, logger: FlashFlow::Config.configuration.logger).status_html
|
|
31
|
+
when options[:release_branches]
|
|
32
|
+
FlashFlow::Deploy.new(options).run_release
|
|
29
33
|
else
|
|
30
34
|
FlashFlow::Deploy.new(options).run
|
|
31
35
|
FlashFlow::IssueTracker::Base.new(FlashFlow::Config.configuration.issue_tracker).stories_pushed
|
data/flash_flow.yml.erb.example
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
module FlashFlow
|
|
2
2
|
class BranchMerger
|
|
3
3
|
|
|
4
|
-
attr_reader :conflict_sha, :resolutions
|
|
4
|
+
attr_reader :conflict_sha, :resolutions, :result
|
|
5
5
|
|
|
6
6
|
def initialize(git, branch)
|
|
7
7
|
@git = git
|
|
@@ -9,15 +9,18 @@ module FlashFlow
|
|
|
9
9
|
end
|
|
10
10
|
|
|
11
11
|
def do_merge(rerere_forget)
|
|
12
|
-
|
|
12
|
+
if sha.nil?
|
|
13
|
+
@result = :deleted
|
|
14
|
+
return
|
|
15
|
+
end
|
|
13
16
|
|
|
14
17
|
@git.run("merge --no-ff #{@branch.remote}/#{@branch.ref}")
|
|
15
18
|
|
|
16
19
|
if @git.last_success? || try_rerere(rerere_forget)
|
|
17
|
-
|
|
20
|
+
@result = :success
|
|
18
21
|
else
|
|
19
22
|
@conflict_sha = merge_rollback
|
|
20
|
-
|
|
23
|
+
@result = :conflict
|
|
21
24
|
end
|
|
22
25
|
end
|
|
23
26
|
|
data/lib/flash_flow/data/base.rb
CHANGED
|
@@ -11,7 +11,7 @@ module FlashFlow
|
|
|
11
11
|
|
|
12
12
|
def_delegators :@collection, :add_story, :mergeable, :mark_deleted, :mark_success, :mark_failure,
|
|
13
13
|
:remove_from_merge, :add_to_merge, :failures, :successes, :removals, :set_resolutions,
|
|
14
|
-
:to_a, :
|
|
14
|
+
:to_a, :code_reviewed?, :branch_link
|
|
15
15
|
|
|
16
16
|
def initialize(branch_config, filename, git, opts={})
|
|
17
17
|
@git = git
|
|
@@ -58,4 +58,4 @@ module FlashFlow
|
|
|
58
58
|
end
|
|
59
59
|
end
|
|
60
60
|
end
|
|
61
|
-
end
|
|
61
|
+
end
|
|
@@ -165,6 +165,10 @@ module FlashFlow
|
|
|
165
165
|
branch
|
|
166
166
|
end
|
|
167
167
|
|
|
168
|
+
def code_reviewed?(branch)
|
|
169
|
+
@collection_instance.respond_to?(:code_reviewed?) ? @collection_instance.code_reviewed?(branch) : true
|
|
170
|
+
end
|
|
171
|
+
|
|
168
172
|
def can_ship?(branch)
|
|
169
173
|
@collection_instance.respond_to?(:can_ship?) ? @collection_instance.can_ship?(branch) : true
|
|
170
174
|
end
|
|
@@ -14,6 +14,7 @@ module FlashFlow
|
|
|
14
14
|
@unmergeable_label = config['unmergeable_label'] || 'unmergeable'
|
|
15
15
|
@do_not_merge_label = config['do_not_merge_label'] || 'do not merge'
|
|
16
16
|
@code_reviewed_label = config['code_reviewed_label'] || 'code reviewed'
|
|
17
|
+
@shippable_label = config['shippable_label'] || 'shippable'
|
|
17
18
|
end
|
|
18
19
|
|
|
19
20
|
def initialize_connection!(token)
|
|
@@ -63,10 +64,14 @@ module FlashFlow
|
|
|
63
64
|
add_label(branch.metadata['pr_number'], @unmergeable_label)
|
|
64
65
|
end
|
|
65
66
|
|
|
66
|
-
def
|
|
67
|
+
def code_reviewed?(branch)
|
|
67
68
|
has_label?(branch.metadata['pr_number'], @code_reviewed_label)
|
|
68
69
|
end
|
|
69
70
|
|
|
71
|
+
def can_ship?(branch)
|
|
72
|
+
has_label?(branch.metadata['pr_number'], @shippable_label)
|
|
73
|
+
end
|
|
74
|
+
|
|
70
75
|
def branch_link(branch)
|
|
71
76
|
branch.metadata['pr_url']
|
|
72
77
|
end
|
data/lib/flash_flow/deploy.rb
CHANGED
|
@@ -11,7 +11,9 @@ require 'flash_flow/shadow_repo'
|
|
|
11
11
|
module FlashFlow
|
|
12
12
|
class Deploy
|
|
13
13
|
|
|
14
|
+
class GitPushFailure < RuntimeError ; end
|
|
14
15
|
class OutOfSyncWithRemote < RuntimeError ; end
|
|
16
|
+
class UnmergeableBranch < RuntimeError ; end
|
|
15
17
|
|
|
16
18
|
def initialize(opts={})
|
|
17
19
|
@do_not_merge = opts[:do_not_merge]
|
|
@@ -24,12 +26,69 @@ module FlashFlow
|
|
|
24
26
|
@lock = Lock::Base.new(Config.configuration.lock)
|
|
25
27
|
@notifier = Notifier::Base.new(Config.configuration.notifier)
|
|
26
28
|
@data = Data::Base.new(Config.configuration.branches, Config.configuration.branch_info_file, @git, logger: logger)
|
|
29
|
+
|
|
30
|
+
@release_branches = parse_branches(opts[:release_branches])
|
|
27
31
|
end
|
|
28
32
|
|
|
29
33
|
def logger
|
|
30
34
|
@logger ||= FlashFlow::Config.configuration.logger
|
|
31
35
|
end
|
|
32
36
|
|
|
37
|
+
def parse_branches(user_branches)
|
|
38
|
+
branch_list = user_branches == ['ready'] ? shippable_branch_names : [user_branches].flatten.compact
|
|
39
|
+
|
|
40
|
+
branch_list.map { |b| Data::Branch.new('origin', @git.remotes_hash['origin'], b) }
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def run_release
|
|
44
|
+
check_version
|
|
45
|
+
check_repo
|
|
46
|
+
check_branches
|
|
47
|
+
puts "Merging these branches into #{@git.release_branch}:\n #{@release_branches.map(&:ref).join("\n ")}"
|
|
48
|
+
logger.info "\n\n### Beginning #{@local_git.merge_branch} merge ###\n\n"
|
|
49
|
+
|
|
50
|
+
begin
|
|
51
|
+
mergers, errors = [], []
|
|
52
|
+
|
|
53
|
+
@lock.with_lock do
|
|
54
|
+
@git.fetch(@git.merge_remote)
|
|
55
|
+
@git.in_original_merge_branch do
|
|
56
|
+
@git.initialize_rerere
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
@git.reset_temp_merge_branch
|
|
60
|
+
@git.in_temp_merge_branch do
|
|
61
|
+
merge_branches(@release_branches) do |branch, merger|
|
|
62
|
+
mergers << [branch, merger]
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
errors = mergers.select { |m| m.last.result != :success }
|
|
67
|
+
|
|
68
|
+
if errors.empty?
|
|
69
|
+
@git.copy_temp_to_branch(@git.release_branch)
|
|
70
|
+
@git.delete_temp_merge_branch
|
|
71
|
+
unless @git.push(@git.release_branch, false)
|
|
72
|
+
raise GitPushFailure.new("Unable to push to #{@git.release_branch}. See log for details.")
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
if errors.empty?
|
|
78
|
+
puts 'Success!'
|
|
79
|
+
else
|
|
80
|
+
raise UnmergeableBranch.new("The following branches didn't merge successfully:\n #{errors.map {|e| e.first.ref }.join("\n ")}")
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
logger.info "### Finished #{@git.release_branch} merge ###"
|
|
84
|
+
rescue Lock::Error, OutOfSyncWithRemote, UnmergeableBranch, GitPushFailure => e
|
|
85
|
+
puts 'Failure!'
|
|
86
|
+
puts e.message
|
|
87
|
+
ensure
|
|
88
|
+
@local_git.run("checkout #{@local_git.working_branch}")
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
|
|
33
92
|
def run
|
|
34
93
|
check_version
|
|
35
94
|
check_repo
|
|
@@ -47,14 +106,16 @@ module FlashFlow
|
|
|
47
106
|
|
|
48
107
|
@git.reset_temp_merge_branch
|
|
49
108
|
@git.in_temp_merge_branch do
|
|
50
|
-
merge_branches
|
|
109
|
+
merge_branches(@data.merged_branches.mergeable) do |branch, merger|
|
|
110
|
+
process_result(branch, merger)
|
|
111
|
+
end
|
|
51
112
|
commit_branch_info
|
|
52
113
|
commit_rerere
|
|
53
114
|
end
|
|
54
115
|
|
|
55
|
-
@git.
|
|
116
|
+
@git.copy_temp_to_branch(@git.merge_branch, commit_message)
|
|
56
117
|
@git.delete_temp_merge_branch
|
|
57
|
-
@git.
|
|
118
|
+
@git.push(@git.merge_branch, true)
|
|
58
119
|
end
|
|
59
120
|
|
|
60
121
|
print_errors
|
|
@@ -67,6 +128,20 @@ module FlashFlow
|
|
|
67
128
|
end
|
|
68
129
|
end
|
|
69
130
|
|
|
131
|
+
def check_branches
|
|
132
|
+
requested_not_ready_branches = (@release_branches.map(&:ref) - shippable_branch_names)
|
|
133
|
+
raise RuntimeError.new("The following branches are not ready to ship:\n#{requested_not_ready_branches.join("\n")}") unless requested_not_ready_branches.empty?
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
def shippable_branch_names
|
|
137
|
+
@shippable_branch_names ||= begin
|
|
138
|
+
status = MergeMaster::Status.new(Config.configuration.issue_tracker, Config.configuration.branches, Config.configuration.branch_info_file, Config.configuration.git, logger: logger)
|
|
139
|
+
|
|
140
|
+
all_branches = status.branches
|
|
141
|
+
all_branches.values.select { |b| b[:shippable?] }.map { |b| b[:name] }
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
|
|
70
145
|
def check_repo
|
|
71
146
|
if @local_git.staged_and_working_dir_files.any?
|
|
72
147
|
raise RuntimeError.new('You have changes in your working directory. Please stash and try again')
|
|
@@ -104,8 +179,8 @@ module FlashFlow
|
|
|
104
179
|
60 * 60 * 24 * 14
|
|
105
180
|
end
|
|
106
181
|
|
|
107
|
-
def merge_branches
|
|
108
|
-
ordered_branches = MergeOrder.new(@git,
|
|
182
|
+
def merge_branches(branches)
|
|
183
|
+
ordered_branches = MergeOrder.new(@git, branches).get_order
|
|
109
184
|
ordered_branches.each_with_index do |branch, index|
|
|
110
185
|
branch.merge_order = index + 1
|
|
111
186
|
|
|
@@ -115,18 +190,26 @@ module FlashFlow
|
|
|
115
190
|
end
|
|
116
191
|
|
|
117
192
|
@git.fetch(branch.remote)
|
|
118
|
-
git_merge(branch
|
|
193
|
+
merger = git_merge(branch)
|
|
194
|
+
|
|
195
|
+
yield(branch, merger)
|
|
119
196
|
end
|
|
120
197
|
end
|
|
121
198
|
|
|
122
|
-
def git_merge(branch
|
|
199
|
+
def git_merge(branch)
|
|
123
200
|
merger = BranchMerger.new(@git, branch)
|
|
124
|
-
forget_rerere = is_working_branch && @rerere_forget
|
|
201
|
+
forget_rerere = is_working_branch(branch) && @rerere_forget
|
|
202
|
+
|
|
203
|
+
merger.do_merge(forget_rerere)
|
|
125
204
|
|
|
126
|
-
|
|
205
|
+
merger
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
def process_result(branch, merger)
|
|
209
|
+
case merger.result
|
|
127
210
|
when :deleted
|
|
128
211
|
@data.mark_deleted(branch)
|
|
129
|
-
@notifier.deleted_branch(branch) unless is_working_branch
|
|
212
|
+
@notifier.deleted_branch(branch) unless is_working_branch(branch)
|
|
130
213
|
|
|
131
214
|
when :success
|
|
132
215
|
branch.sha = merger.sha
|
|
@@ -134,7 +217,7 @@ module FlashFlow
|
|
|
134
217
|
@data.set_resolutions(branch, merger.resolutions)
|
|
135
218
|
|
|
136
219
|
when :conflict
|
|
137
|
-
if is_working_branch
|
|
220
|
+
if is_working_branch(branch)
|
|
138
221
|
@data.mark_failure(branch, merger.conflict_sha)
|
|
139
222
|
else
|
|
140
223
|
@data.mark_failure(branch, nil)
|
|
@@ -143,11 +226,15 @@ module FlashFlow
|
|
|
143
226
|
end
|
|
144
227
|
end
|
|
145
228
|
|
|
229
|
+
def is_working_branch(branch)
|
|
230
|
+
branch.ref == @git.working_branch
|
|
231
|
+
end
|
|
232
|
+
|
|
146
233
|
def open_pull_request
|
|
147
234
|
return false if [@local_git.master_branch, @local_git.merge_branch].include?(@local_git.working_branch)
|
|
148
235
|
|
|
149
236
|
# TODO - This should use the actual remote for the branch we're on
|
|
150
|
-
@local_git.push(@local_git.working_branch,
|
|
237
|
+
@local_git.push(@local_git.working_branch, @force)
|
|
151
238
|
raise OutOfSyncWithRemote.new("Your branch is out of sync with the remote. If you want to force push, run 'flash_flow -f'") unless @local_git.last_success?
|
|
152
239
|
|
|
153
240
|
# TODO - This should use the actual remote for the branch we're on
|
|
@@ -181,6 +268,14 @@ module FlashFlow
|
|
|
181
268
|
end
|
|
182
269
|
end
|
|
183
270
|
|
|
271
|
+
def release_commit_message
|
|
272
|
+
message =<<-EOS
|
|
273
|
+
Flash Flow merged these branches:
|
|
274
|
+
#{@release_branches.map(&:ref).join("\n")}
|
|
275
|
+
EOS
|
|
276
|
+
message.gsub(/'/, '')
|
|
277
|
+
end
|
|
278
|
+
|
|
184
279
|
def commit_message
|
|
185
280
|
message =<<-EOS
|
|
186
281
|
Flash Flow run from branch: #{@local_git.working_branch}
|
|
@@ -198,4 +293,4 @@ Removed branches:
|
|
|
198
293
|
end
|
|
199
294
|
|
|
200
295
|
end
|
|
201
|
-
end
|
|
296
|
+
end
|
data/lib/flash_flow/git.rb
CHANGED
|
@@ -3,7 +3,7 @@ require 'shellwords'
|
|
|
3
3
|
|
|
4
4
|
module FlashFlow
|
|
5
5
|
class Git
|
|
6
|
-
ATTRIBUTES = [:merge_remote, :merge_branch, :master_branch, :use_rerere]
|
|
6
|
+
ATTRIBUTES = [:merge_remote, :merge_branch, :master_branch, :release_branch, :use_rerere]
|
|
7
7
|
attr_reader *ATTRIBUTES
|
|
8
8
|
attr_reader :working_branch
|
|
9
9
|
|
|
@@ -11,7 +11,7 @@ module FlashFlow
|
|
|
11
11
|
|
|
12
12
|
def initialize(config, logger=nil)
|
|
13
13
|
@cmd_runner = CmdRunner.new(logger: logger)
|
|
14
|
-
|
|
14
|
+
config['release_branch'] ||= config['master_branch']
|
|
15
15
|
ATTRIBUTES.each do |attr|
|
|
16
16
|
unless config.has_key?(attr.to_s)
|
|
17
17
|
raise RuntimeError.new("git configuration missing. Required config parameters: #{ATTRIBUTES}")
|
|
@@ -51,10 +51,6 @@ module FlashFlow
|
|
|
51
51
|
run("commit -m '#{message}'")
|
|
52
52
|
end
|
|
53
53
|
|
|
54
|
-
def push(branch, options)
|
|
55
|
-
run("push #{'-f' if options[:force]} #{merge_remote} #{branch}")
|
|
56
|
-
end
|
|
57
|
-
|
|
58
54
|
def merge(branch)
|
|
59
55
|
run("merge #{branch}")
|
|
60
56
|
end
|
|
@@ -73,14 +69,7 @@ module FlashFlow
|
|
|
73
69
|
end
|
|
74
70
|
|
|
75
71
|
def in_original_merge_branch
|
|
76
|
-
|
|
77
|
-
starting_branch = current_branch
|
|
78
|
-
run("checkout #{merge_remote}/#{merge_branch}")
|
|
79
|
-
|
|
80
|
-
yield
|
|
81
|
-
ensure
|
|
82
|
-
run("checkout #{starting_branch}")
|
|
83
|
-
end
|
|
72
|
+
in_branch("#{merge_remote}/#{merge_branch}") { yield }
|
|
84
73
|
end
|
|
85
74
|
|
|
86
75
|
def read_file_from_merge_branch(filename)
|
|
@@ -204,21 +193,22 @@ module FlashFlow
|
|
|
204
193
|
end
|
|
205
194
|
end
|
|
206
195
|
|
|
207
|
-
def
|
|
208
|
-
run("push -f #{merge_remote} #{
|
|
196
|
+
def push(branch, force=false)
|
|
197
|
+
run("push #{'-f' if force} #{merge_remote} #{branch}")
|
|
209
198
|
end
|
|
210
199
|
|
|
211
|
-
def
|
|
200
|
+
def copy_temp_to_branch(branch, squash_message = nil)
|
|
212
201
|
run("checkout #{temp_merge_branch}")
|
|
213
|
-
run("merge --strategy=ours --no-edit #{
|
|
214
|
-
run("checkout #{
|
|
202
|
+
run("merge --strategy=ours --no-edit #{branch}")
|
|
203
|
+
run("checkout #{branch}")
|
|
215
204
|
run("merge #{temp_merge_branch}")
|
|
216
205
|
|
|
217
|
-
|
|
206
|
+
|
|
207
|
+
squash_commits(branch, squash_message) if squash_message
|
|
218
208
|
end
|
|
219
209
|
|
|
220
210
|
def delete_temp_merge_branch
|
|
221
|
-
|
|
211
|
+
in_branch(master_branch) do
|
|
222
212
|
run("branch -d #{temp_merge_branch}")
|
|
223
213
|
end
|
|
224
214
|
end
|
|
@@ -244,17 +234,26 @@ module FlashFlow
|
|
|
244
234
|
|
|
245
235
|
private
|
|
246
236
|
|
|
247
|
-
def squash_commits(commit_message)
|
|
237
|
+
def squash_commits(branch, commit_message)
|
|
238
|
+
unless branch_exists?("#{merge_remote}/#{branch}")
|
|
239
|
+
run("push #{merge_remote} #{master_branch}:#{branch}")
|
|
240
|
+
end
|
|
241
|
+
|
|
248
242
|
# Get all the files that differ between existing acceptance and new acceptance
|
|
249
|
-
run("diff --name-only #{merge_remote}/#{
|
|
243
|
+
run("diff --name-only #{merge_remote}/#{branch} #{branch}")
|
|
250
244
|
files = last_stdout.split("\n")
|
|
251
|
-
run("reset #{merge_remote}/#{
|
|
245
|
+
run("reset #{merge_remote}/#{branch}")
|
|
252
246
|
|
|
253
247
|
run("add -f #{files.map { |f| "\"#{Shellwords.escape(f)}\"" }.join(" ")}")
|
|
254
248
|
|
|
255
249
|
run("commit -m '#{commit_message}'")
|
|
256
250
|
end
|
|
257
251
|
|
|
252
|
+
def branch_exists?(branch)
|
|
253
|
+
run("rev-parse --verify #{branch}")
|
|
254
|
+
last_success?
|
|
255
|
+
end
|
|
256
|
+
|
|
258
257
|
def temp_merge_branch
|
|
259
258
|
"flash_flow/#{merge_branch}"
|
|
260
259
|
end
|
|
@@ -61,7 +61,7 @@ module FlashFlow
|
|
|
61
61
|
def story_deployable?(story_id)
|
|
62
62
|
story = get_story(story_id)
|
|
63
63
|
|
|
64
|
-
story.current_state == 'accepted'
|
|
64
|
+
story && story.current_state == 'accepted'
|
|
65
65
|
end
|
|
66
66
|
|
|
67
67
|
def story_link(story_id)
|
|
@@ -79,7 +79,7 @@ module FlashFlow
|
|
|
79
79
|
def release_keys(story_id)
|
|
80
80
|
story = get_story(story_id)
|
|
81
81
|
|
|
82
|
-
return []
|
|
82
|
+
return [] unless story && story.labels
|
|
83
83
|
|
|
84
84
|
story.labels.split(",").map(&:strip).select { |label| label =~ @release_label_prefix }.map(&:strip)
|
|
85
85
|
end
|
|
@@ -55,7 +55,7 @@
|
|
|
55
55
|
</h2>
|
|
56
56
|
|
|
57
57
|
<h2>
|
|
58
|
-
<span class="status <%= branch_hash[:
|
|
58
|
+
<span class="status <%= branch_hash[:code_reviewed?] ? 'shippable' : 'not-shippable' %>"><%= branch_hash[:code_reviewed?] ? 'Code ready' : 'Code not ready' %></span>
|
|
59
59
|
<a href='<%= branch_hash[:branch_url] %>'><%= branch_hash[:name] %></a>
|
|
60
60
|
</h2>
|
|
61
61
|
|
|
@@ -79,7 +79,7 @@
|
|
|
79
79
|
<% branch_hash[:my_stories].each do |story_id| %>
|
|
80
80
|
<% story = @stories[story_id] %>
|
|
81
81
|
<div class="story">
|
|
82
|
-
<span class="status <%= story[:
|
|
82
|
+
<span class="status <%= story[:accepted?] ? 'shippable' : 'not-shippable' %>"><%= story[:accepted?] ? 'Story ready' : 'Story not ready' %></span>
|
|
83
83
|
<a href='<%= story[:url] %>'><%= story[:title] %></a>
|
|
84
84
|
<span><%= story[:release_keys].empty? ? '' : "Related releases: #{story[:release_keys].join(", ")}" %></span>
|
|
85
85
|
</div>
|
|
@@ -92,7 +92,7 @@
|
|
|
92
92
|
<% (branch_hash[:stories] - branch_hash[:my_stories]).each do |story_id| %>
|
|
93
93
|
<% story = @stories[story_id] %>
|
|
94
94
|
<div class="story">
|
|
95
|
-
<span class="status <%= story[:
|
|
95
|
+
<span class="status <%= story[:accepted?] ? 'shippable' : 'not-shippable' %>"><%= story[:accepted?] ? 'Story ready' : 'Story not ready' %></span>
|
|
96
96
|
<a href='<%= story[:url] %>'><%= story[:title] %></a>
|
|
97
97
|
<span><%= story[:release_keys].empty? ? '' : "Related releases: #{story[:release_keys].join(", ")}" %></span>
|
|
98
98
|
</div>
|
|
@@ -116,7 +116,7 @@
|
|
|
116
116
|
<% @releases[release_key][:stories].each do |story_id| %>
|
|
117
117
|
<% story = @stories[story_id] %>
|
|
118
118
|
<div class="story">
|
|
119
|
-
<span class="status <%= story[:
|
|
119
|
+
<span class="status <%= story[:accepted?] ? 'shippable' : 'not-shippable' %>"><%= story[:accepted?] ? 'Story ready' : 'Story not ready' %></span>
|
|
120
120
|
<a href='<%= story[:url] %>'><%= story[:title] %></a>
|
|
121
121
|
<span><%= story[:release_keys].empty? ? '' : "Related releases: #{story[:release_keys].join(", ")}" %></span>
|
|
122
122
|
</div>
|
|
@@ -141,4 +141,4 @@
|
|
|
141
141
|
|
|
142
142
|
<% end %>
|
|
143
143
|
</body>
|
|
144
|
-
</html>
|
|
144
|
+
</html>
|
|
@@ -10,7 +10,27 @@ module FlashFlow
|
|
|
10
10
|
@collection = Data::Base.new(branches_config, branch_info_file, ShadowGit.new(git_config)).merged_branches
|
|
11
11
|
end
|
|
12
12
|
|
|
13
|
-
def status
|
|
13
|
+
def status
|
|
14
|
+
filename = File.dirname(__FILE__) + '/merge_status.csv'
|
|
15
|
+
checkmark = "\u2713".encode('utf-8')
|
|
16
|
+
|
|
17
|
+
CSV.open(filename, 'w') do |f|
|
|
18
|
+
f << ['Ready', 'Branch', 'Stories', 'Review', 'Can ship?']
|
|
19
|
+
branches.each do |_, branch_hash|
|
|
20
|
+
f << [
|
|
21
|
+
branch_hash[:shippable?] ? checkmark : 'x',
|
|
22
|
+
branch_hash[:name],
|
|
23
|
+
unshippable_stories(branch_hash[:stories]).empty? ? checkmark : 'x',
|
|
24
|
+
branch_hash[:code_reviewed?] ? checkmark : 'x',
|
|
25
|
+
branch_hash[:can_ship?] ? checkmark : 'x'
|
|
26
|
+
]
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
CSV.foreach(filename) { |row| puts '%-10s %-70s %-10s %-10s %-10s' % row }
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def status_html(filename=nil)
|
|
14
34
|
filename = File.dirname(__FILE__) + '/merge_status.html'
|
|
15
35
|
@branches = branches
|
|
16
36
|
|
|
@@ -39,7 +59,8 @@ module FlashFlow
|
|
|
39
59
|
Hash.new.tap do |hash|
|
|
40
60
|
hash[:name] = branch.ref
|
|
41
61
|
hash[:branch_url] = collection.branch_link(branch)
|
|
42
|
-
hash[:
|
|
62
|
+
hash[:code_reviewed?] = collection.code_reviewed?(branch)
|
|
63
|
+
hash[:can_ship?] = collection.can_ship?(branch)
|
|
43
64
|
hash[:connected_branches] = connected_branches
|
|
44
65
|
hash[:image] = graph_file
|
|
45
66
|
hash[:my_stories] = branch.stories.to_a
|
|
@@ -70,7 +91,7 @@ module FlashFlow
|
|
|
70
91
|
|
|
71
92
|
def mark_as_shippable(branches)
|
|
72
93
|
branches.each do |_, b|
|
|
73
|
-
b[:shippable?] = b[:
|
|
94
|
+
b[:shippable?] = b[:code_reviewed?] && b[:can_ship?] &&
|
|
74
95
|
unshippable_stories(b[:stories]).empty? &&
|
|
75
96
|
unshippable_releases(b[:releases]).empty?
|
|
76
97
|
end
|
|
@@ -86,11 +107,14 @@ module FlashFlow
|
|
|
86
107
|
arr.select do |release_key|
|
|
87
108
|
!unshippable_stories(@releases[release_key][:stories]).empty?
|
|
88
109
|
end
|
|
89
|
-
|
|
90
110
|
end
|
|
91
111
|
|
|
92
112
|
def unshippable_stories(arr)
|
|
93
|
-
arr.select { |story| !@stories[story][:
|
|
113
|
+
arr.select { |story| !@stories[story][:accepted?] }
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
def stories_accepted_branches
|
|
117
|
+
branches.select { |_, b| unshippable_stories(b[:stories]).empty? }
|
|
94
118
|
end
|
|
95
119
|
|
|
96
120
|
def story_info_hash(story_id)
|
|
@@ -98,7 +122,7 @@ module FlashFlow
|
|
|
98
122
|
id: story_id,
|
|
99
123
|
url: issue_tracker.story_link(story_id),
|
|
100
124
|
title: issue_tracker.story_title(story_id),
|
|
101
|
-
|
|
125
|
+
accepted?: issue_tracker.story_deployable?(story_id),
|
|
102
126
|
release_keys: issue_tracker.release_keys(story_id)
|
|
103
127
|
}
|
|
104
128
|
end
|
|
@@ -10,9 +10,15 @@ module FlashFlow
|
|
|
10
10
|
end
|
|
11
11
|
|
|
12
12
|
def merge_conflict(branch)
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
13
|
+
begin
|
|
14
|
+
user_name = branch.metadata['user_url'].split('/').last
|
|
15
|
+
user_url_link = %{<a href="#{branch.metadata['user_url']}">#{user_name}</a>}
|
|
16
|
+
ref_link = %{<a href="#{branch.metadata['repo_url']}/tree/#{branch.ref}">#{branch.ref}</a>}
|
|
17
|
+
rescue => e
|
|
18
|
+
puts "An error occurred in the hipchat notifier: #{e.message}."
|
|
19
|
+
user_url_link = 'Unknown'
|
|
20
|
+
ref_link = branch.ref
|
|
21
|
+
end
|
|
16
22
|
|
|
17
23
|
message = %{#{user_url_link}'s branch (#{ref_link}) did not merge successfully}
|
|
18
24
|
@client[@room].send("FlashFlow", message)
|
data/lib/flash_flow/options.rb
CHANGED
|
@@ -22,6 +22,8 @@ module FlashFlow
|
|
|
22
22
|
opts.on('--resolve', 'Launch a bash shell to save your conflict resolutions') { |v| options[:resolve] = true }
|
|
23
23
|
opts.on('--resolve-manual', 'Print instructions to use git to resolve conflicts') { |v| options[:resolve_manual] = true }
|
|
24
24
|
opts.on('--merge-status', 'Show status of all branches and their stories and exit') { |v| options[:merge_status] = true }
|
|
25
|
+
opts.on('--merge-status-html', 'Show status of all branches and their stories in html format and exit') { |v| options[:merge_status_html] = true }
|
|
26
|
+
opts.on('--merge-master branch1,branch2', 'Comma-delimited list of branches to merge to master. Master gets pushed to origin. Run "--merge-master ready" to merge all ready to ship branches') { |v| options[:release_branches] = v.split(',') }
|
|
25
27
|
|
|
26
28
|
opts.on_tail("-h", "--help", "Show this message") do
|
|
27
29
|
puts opts
|
data/lib/flash_flow/version.rb
CHANGED
|
@@ -214,14 +214,14 @@ module FlashFlow
|
|
|
214
214
|
@fake_branches.verify
|
|
215
215
|
end
|
|
216
216
|
|
|
217
|
-
def
|
|
217
|
+
def test_code_reviewd_returns_true
|
|
218
218
|
collection = Collection.new({})
|
|
219
|
-
assert(collection.
|
|
219
|
+
assert(collection.code_reviewed?(@branch))
|
|
220
220
|
end
|
|
221
221
|
|
|
222
|
-
def
|
|
223
|
-
@fake_branches.expect(:
|
|
224
|
-
@collection.
|
|
222
|
+
def test_code_reviewd_calls_branches_class
|
|
223
|
+
@fake_branches.expect(:code_reviewed?, true, [@branch])
|
|
224
|
+
@collection.code_reviewed?(@branch)
|
|
225
225
|
@fake_branches.verify
|
|
226
226
|
end
|
|
227
227
|
|
|
@@ -20,6 +20,7 @@ module FlashFlow
|
|
|
20
20
|
STORY_ID4 = '444'
|
|
21
21
|
STORY_ID5 = '555'
|
|
22
22
|
STORY_ID6 = '666'
|
|
23
|
+
STORY_ID7 = '777'
|
|
23
24
|
|
|
24
25
|
def story_deployable?(story_id)
|
|
25
26
|
{
|
|
@@ -29,6 +30,7 @@ module FlashFlow
|
|
|
29
30
|
STORY_ID4 => true,
|
|
30
31
|
STORY_ID5 => false,
|
|
31
32
|
STORY_ID6 => true,
|
|
33
|
+
STORY_ID7 => true
|
|
32
34
|
}[story_id]
|
|
33
35
|
end
|
|
34
36
|
|
|
@@ -40,6 +42,7 @@ module FlashFlow
|
|
|
40
42
|
STORY_ID4 => ['release3', 'release4'],
|
|
41
43
|
STORY_ID5 => [],
|
|
42
44
|
STORY_ID6 => ['release4'],
|
|
45
|
+
STORY_ID7 => [],
|
|
43
46
|
}[story_id]
|
|
44
47
|
end
|
|
45
48
|
|
|
@@ -64,21 +67,32 @@ module FlashFlow
|
|
|
64
67
|
BRANCH1 = Data::Branch.from_hash('ref' => 'branch1', 'stories' => ['111', '222'])
|
|
65
68
|
BRANCH2 = Data::Branch.from_hash('ref' => 'branch2', 'stories' => ['333'])
|
|
66
69
|
BRANCH3 = Data::Branch.from_hash('ref' => 'branch3', 'stories' => ['444', '555', '666'])
|
|
70
|
+
BRANCH4 = Data::Branch.from_hash('ref' => 'branch4', 'stories' => ['777'])
|
|
67
71
|
|
|
68
72
|
def branch_link(branch)
|
|
69
73
|
return "link-#{branch.ref}"
|
|
70
74
|
end
|
|
71
75
|
|
|
72
|
-
def
|
|
76
|
+
def code_reviewed?(branch)
|
|
73
77
|
{
|
|
74
78
|
BRANCH1 => true,
|
|
75
79
|
BRANCH2 => true,
|
|
76
80
|
BRANCH3 => false,
|
|
81
|
+
BRANCH4 => true,
|
|
82
|
+
}[branch]
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def can_ship?(branch)
|
|
86
|
+
{
|
|
87
|
+
BRANCH1 => true,
|
|
88
|
+
BRANCH2 => true,
|
|
89
|
+
BRANCH3 => true,
|
|
90
|
+
BRANCH4 => false,
|
|
77
91
|
}[branch]
|
|
78
92
|
end
|
|
79
93
|
|
|
80
94
|
def current_branches
|
|
81
|
-
[BRANCH1, BRANCH2, BRANCH3]
|
|
95
|
+
[BRANCH1, BRANCH2, BRANCH3, BRANCH4]
|
|
82
96
|
end
|
|
83
97
|
end
|
|
84
98
|
|
|
@@ -96,7 +110,7 @@ module FlashFlow
|
|
|
96
110
|
branch1 = branches[FakeCollection::BRANCH1]
|
|
97
111
|
branch2 = branches[FakeCollection::BRANCH2]
|
|
98
112
|
|
|
99
|
-
assert(branch1[:stories].all? { |s| @merge_master.stories[s][:
|
|
113
|
+
assert(branch1[:stories].all? { |s| @merge_master.stories[s][:accepted?] })
|
|
100
114
|
assert_equal(branch1[:stories], branch2[:stories])
|
|
101
115
|
assert(branch1[:shippable?])
|
|
102
116
|
end
|
|
@@ -104,11 +118,17 @@ module FlashFlow
|
|
|
104
118
|
def test_not_shippable_branch
|
|
105
119
|
branches = @merge_master.branches
|
|
106
120
|
branch3 = branches[FakeCollection::BRANCH3]
|
|
107
|
-
|
|
108
|
-
assert_equal(branch3[:stories].map { |s| @merge_master.stories[s][:can_ship?] }, [true, false, true])
|
|
121
|
+
assert_equal(branch3[:stories].map { |s| @merge_master.stories[s][:accepted?] }, [true, false, true])
|
|
109
122
|
refute(branch3[:shippable?])
|
|
110
123
|
end
|
|
111
124
|
|
|
125
|
+
def test_not_shippable_branch_without_shippable_label
|
|
126
|
+
branches = @merge_master.branches
|
|
127
|
+
branch4 = branches[FakeCollection::BRANCH4]
|
|
128
|
+
assert_equal(branch4[:stories].map { |s| @merge_master.stories[s][:accepted?] }, [true])
|
|
129
|
+
refute(branch4[:shippable?])
|
|
130
|
+
end
|
|
131
|
+
|
|
112
132
|
private
|
|
113
133
|
|
|
114
134
|
def find_branch(status_list, branch)
|
data/test/lib/test_deploy.rb
CHANGED
|
@@ -87,11 +87,9 @@ module FlashFlow
|
|
|
87
87
|
|
|
88
88
|
notifier.expect(:deleted_branch, true, [@branch])
|
|
89
89
|
|
|
90
|
-
merger.expect(:
|
|
90
|
+
merger.expect(:result, :deleted)
|
|
91
91
|
|
|
92
|
-
|
|
93
|
-
@deploy.git_merge(@branch, false)
|
|
94
|
-
end
|
|
92
|
+
@deploy.process_result(@branch, merger)
|
|
95
93
|
|
|
96
94
|
notifier.verify
|
|
97
95
|
data.verify
|
|
@@ -103,12 +101,9 @@ module FlashFlow
|
|
|
103
101
|
|
|
104
102
|
notifier.expect(:merge_conflict, true, [@branch])
|
|
105
103
|
|
|
106
|
-
merger
|
|
107
|
-
.expect(:do_merge, :conflict, [ false ])
|
|
104
|
+
merger.expect(:result, :conflict)
|
|
108
105
|
|
|
109
|
-
|
|
110
|
-
@deploy.git_merge(@branch, false)
|
|
111
|
-
end
|
|
106
|
+
@deploy.process_result(@branch, merger)
|
|
112
107
|
|
|
113
108
|
notifier.verify
|
|
114
109
|
data.verify
|
|
@@ -120,13 +115,11 @@ module FlashFlow
|
|
|
120
115
|
data.expect(:set_resolutions, true, [ @branch, { 'filename' => ["resolution_sha"] } ])
|
|
121
116
|
|
|
122
117
|
merger.
|
|
123
|
-
expect(:
|
|
118
|
+
expect(:result, :success).
|
|
124
119
|
expect(:sha, 'sha').
|
|
125
120
|
expect(:resolutions, { 'filename' => ["resolution_sha"] })
|
|
126
121
|
|
|
127
|
-
|
|
128
|
-
@deploy.git_merge(@branch, false)
|
|
129
|
-
end
|
|
122
|
+
@deploy.process_result(@branch, merger)
|
|
130
123
|
|
|
131
124
|
data.verify
|
|
132
125
|
merger.verify
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: flash_flow
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.3.
|
|
4
|
+
version: 1.3.2
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Flashfunders
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2016-
|
|
11
|
+
date: 2016-07-27 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: octokit
|
|
@@ -224,7 +224,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
224
224
|
version: '0'
|
|
225
225
|
requirements: []
|
|
226
226
|
rubyforge_project:
|
|
227
|
-
rubygems_version: 2.
|
|
227
|
+
rubygems_version: 2.5.1
|
|
228
228
|
signing_key:
|
|
229
229
|
specification_version: 4
|
|
230
230
|
summary: Implementation of the flashfunders workflow
|