flux 0.0.3 → 0.0.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.
- data/.flux.local.rb.sample +3 -0
- data/.flux.rb.sample +4 -0
- data/Gemfile +2 -0
- data/README.md +52 -2
- data/VERSION +1 -1
- data/flux.gemspec +18 -11
- data/lib/flux.rb +27 -31
- data/lib/flux/cli.rb +3 -0
- data/lib/flux/cli/feature.rb +39 -0
- data/lib/flux/cli/pivotal_tracker.rb +72 -0
- data/lib/flux/cli/review.rb +190 -0
- data/lib/flux/git.rb +81 -0
- data/lib/flux/pivotal_tracker.rb +81 -0
- data/lib/flux/rcs.rb +1 -0
- data/lib/flux/rcs/git.rb +132 -15
- data/lib/flux/util.rb +0 -1
- data/lib/flux/workflows/mojotech.rb +23 -17
- data/spec/flux_spec.rb +17 -28
- metadata +51 -28
- data/.flux +0 -9
- data/.flux.local.sample +0 -3
- data/lib/flux/trackers/pivotal_tracker.rb +0 -146
- data/lib/flux/util/table.rb +0 -99
- data/spec/flux/rcs/git_spec.rb +0 -47
- data/spec/flux/trackers/pivotal_tracker_spec.rb +0 -164
- data/spec/flux/util/table_spec.rb +0 -47
- data/spec/support/matchers/print_table.rb +0 -46
data/.flux.rb.sample
ADDED
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -46,8 +46,8 @@ This project's `.flux` file is as follows:
|
|
46
46
|
The `.flux` file should be used for project-wide configuration and kept under
|
47
47
|
version control. Obviously you aren't going to store your account credentials
|
48
48
|
there, so you need a different place where you can store them. That place is
|
49
|
-
`.flux.local`. If you're using Git
|
50
|
-
`.gitignore`.
|
49
|
+
`.flux.local`. If you're using Git (and really, why wouldn't you?), this file
|
50
|
+
should be added to the project's `.gitignore`.
|
51
51
|
|
52
52
|
Here's an example of a `.flux.local` file.
|
53
53
|
|
@@ -56,3 +56,53 @@ Here's an example of a `.flux.local` file.
|
|
56
56
|
email: david@mojotech.com
|
57
57
|
|
58
58
|
Note for Pivotal Tracker: Your API token is at https://www.pivotaltracker.com/profile
|
59
|
+
|
60
|
+
|
61
|
+
Code Reviews Workflow
|
62
|
+
---------------------
|
63
|
+
|
64
|
+
# start your feature on a branch
|
65
|
+
$ git checkout feature-branch
|
66
|
+
|
67
|
+
# code your feature
|
68
|
+
$ git commit # rinse, repeat
|
69
|
+
|
70
|
+
# make sure your code to review is available
|
71
|
+
$ git push
|
72
|
+
|
73
|
+
# request a review
|
74
|
+
$ flux branches:review feature-branch [ --parent master ]
|
75
|
+
# Note: currently, you must explicitly list your feature branch name
|
76
|
+
# This feature branch name will be used to keep track of multiple rounds
|
77
|
+
# of the review.
|
78
|
+
|
79
|
+
# flux will generate a pull request and print its URL
|
80
|
+
|
81
|
+
# Github takes sends out notices.
|
82
|
+
# other devs review and comment
|
83
|
+
|
84
|
+
# You fix up your commits.
|
85
|
+
$ git rebase -i origin/master
|
86
|
+
# I'm in your branch... rewriting your history.
|
87
|
+
|
88
|
+
# Time to resubmit for review
|
89
|
+
$ git push
|
90
|
+
$ flux request-review feature-branch [ --parent master ] [ --close ]
|
91
|
+
|
92
|
+
# flux closes old review request for feature-branch (optionally)
|
93
|
+
# flux opens a new review request for feature-branch
|
94
|
+
# The process repeats.
|
95
|
+
|
96
|
+
# Now the reviewers want to see if you addressed their concerns.
|
97
|
+
# They run:
|
98
|
+
$ flux branches:all_reviews
|
99
|
+
# (TODO: should be able to limit this report to one feature branch)
|
100
|
+
|
101
|
+
# They see: the most recent review request for each branch, but also links
|
102
|
+
# to the diffs between each previous review request for that branch.
|
103
|
+
|
104
|
+
# So, instead of repeating the entire review, they can look at just the "inter-diff"
|
105
|
+
|
106
|
+
|
107
|
+
|
108
|
+
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.0.
|
1
|
+
0.0.5
|
data/flux.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = "flux"
|
8
|
-
s.version = "0.0.
|
8
|
+
s.version = "0.0.5"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["David Leal"]
|
12
|
-
s.date = "
|
12
|
+
s.date = "2012-02-17"
|
13
13
|
s.email = "david@mojotech.com"
|
14
14
|
s.executables = ["flux"]
|
15
15
|
s.extra_rdoc_files = [
|
@@ -18,8 +18,8 @@ Gem::Specification.new do |s|
|
|
18
18
|
]
|
19
19
|
s.files = [
|
20
20
|
".document",
|
21
|
-
".flux",
|
22
|
-
".flux.
|
21
|
+
".flux.local.rb.sample",
|
22
|
+
".flux.rb.sample",
|
23
23
|
".rspec",
|
24
24
|
"Gemfile",
|
25
25
|
"LICENSE.txt",
|
@@ -29,25 +29,26 @@ Gem::Specification.new do |s|
|
|
29
29
|
"bin/flux",
|
30
30
|
"flux.gemspec",
|
31
31
|
"lib/flux.rb",
|
32
|
+
"lib/flux/cli.rb",
|
33
|
+
"lib/flux/cli/feature.rb",
|
34
|
+
"lib/flux/cli/pivotal_tracker.rb",
|
35
|
+
"lib/flux/cli/review.rb",
|
32
36
|
"lib/flux/ext/pivotal-tracker.rb",
|
37
|
+
"lib/flux/git.rb",
|
38
|
+
"lib/flux/pivotal_tracker.rb",
|
39
|
+
"lib/flux/rcs.rb",
|
33
40
|
"lib/flux/rcs/git.rb",
|
34
|
-
"lib/flux/trackers/pivotal_tracker.rb",
|
35
41
|
"lib/flux/util.rb",
|
36
42
|
"lib/flux/util/output.rb",
|
37
|
-
"lib/flux/util/table.rb",
|
38
43
|
"lib/flux/workflows/mojotech.rb",
|
39
|
-
"spec/flux/rcs/git_spec.rb",
|
40
|
-
"spec/flux/trackers/pivotal_tracker_spec.rb",
|
41
|
-
"spec/flux/util/table_spec.rb",
|
42
44
|
"spec/flux_spec.rb",
|
43
45
|
"spec/spec_helper.rb",
|
44
|
-
"spec/support/matchers/print_table.rb",
|
45
46
|
"spec/support/rr.rb"
|
46
47
|
]
|
47
48
|
s.homepage = "http://github.com/mojotech/flux"
|
48
49
|
s.licenses = ["MIT"]
|
49
50
|
s.require_paths = ["lib"]
|
50
|
-
s.rubygems_version = "1.8.
|
51
|
+
s.rubygems_version = "1.8.11"
|
51
52
|
s.summary = "Command line workflow manager."
|
52
53
|
|
53
54
|
if s.respond_to? :specification_version then
|
@@ -57,6 +58,8 @@ Gem::Specification.new do |s|
|
|
57
58
|
s.add_runtime_dependency(%q<thor>, ["~> 0.14.0"])
|
58
59
|
s.add_runtime_dependency(%q<pivotal-tracker>, ["~> 0.4.0"])
|
59
60
|
s.add_runtime_dependency(%q<grit>, ["~> 2.4.0"])
|
61
|
+
s.add_runtime_dependency(%q<hirb>, [">= 0"])
|
62
|
+
s.add_runtime_dependency(%q<github_api>, [">= 0"])
|
60
63
|
s.add_development_dependency(%q<rspec>, ["~> 2.0"])
|
61
64
|
s.add_development_dependency(%q<bundler>, ["~> 1.0.0"])
|
62
65
|
s.add_development_dependency(%q<jeweler>, ["~> 1.6.2"])
|
@@ -66,6 +69,8 @@ Gem::Specification.new do |s|
|
|
66
69
|
s.add_dependency(%q<thor>, ["~> 0.14.0"])
|
67
70
|
s.add_dependency(%q<pivotal-tracker>, ["~> 0.4.0"])
|
68
71
|
s.add_dependency(%q<grit>, ["~> 2.4.0"])
|
72
|
+
s.add_dependency(%q<hirb>, [">= 0"])
|
73
|
+
s.add_dependency(%q<github_api>, [">= 0"])
|
69
74
|
s.add_dependency(%q<rspec>, ["~> 2.0"])
|
70
75
|
s.add_dependency(%q<bundler>, ["~> 1.0.0"])
|
71
76
|
s.add_dependency(%q<jeweler>, ["~> 1.6.2"])
|
@@ -76,6 +81,8 @@ Gem::Specification.new do |s|
|
|
76
81
|
s.add_dependency(%q<thor>, ["~> 0.14.0"])
|
77
82
|
s.add_dependency(%q<pivotal-tracker>, ["~> 0.4.0"])
|
78
83
|
s.add_dependency(%q<grit>, ["~> 2.4.0"])
|
84
|
+
s.add_dependency(%q<hirb>, [">= 0"])
|
85
|
+
s.add_dependency(%q<github_api>, [">= 0"])
|
79
86
|
s.add_dependency(%q<rspec>, ["~> 2.0"])
|
80
87
|
s.add_dependency(%q<bundler>, ["~> 1.0.0"])
|
81
88
|
s.add_dependency(%q<jeweler>, ["~> 1.6.2"])
|
data/lib/flux.rb
CHANGED
@@ -6,21 +6,34 @@ require 'yaml'
|
|
6
6
|
require 'flux/util'
|
7
7
|
|
8
8
|
module Flux
|
9
|
-
|
10
|
-
|
9
|
+
NAME = '.flux'
|
10
|
+
RC = "#{NAME}.rb"
|
11
|
+
RC_LOCAL = "#{NAME}.local.rb"
|
11
12
|
|
12
13
|
class FluxError < StandardError; end
|
13
14
|
class TrackerError < FluxError; end
|
14
15
|
|
15
16
|
class << self
|
16
|
-
|
17
|
+
def environment
|
18
|
+
@project ||= {}
|
19
|
+
end
|
17
20
|
|
18
21
|
def setup
|
19
|
-
|
22
|
+
load rc
|
23
|
+
load rc_local if File.exist?(rc_local)
|
24
|
+
|
25
|
+
require 'flux/rcs'
|
26
|
+
require 'flux/cli'
|
27
|
+
end
|
28
|
+
|
29
|
+
def rc
|
30
|
+
find_upwards(RC, Dir.pwd) or
|
31
|
+
raise FluxError, "Could not find a '#{RC}' " <<
|
32
|
+
"file in the current filesystem hierarchy."
|
33
|
+
end
|
20
34
|
|
21
|
-
|
22
|
-
|
23
|
-
}
|
35
|
+
def rc_local
|
36
|
+
find_upwards(RC_LOCAL, Dir.pwd)
|
24
37
|
end
|
25
38
|
|
26
39
|
def find_upwards(object, start_dir)
|
@@ -32,34 +45,17 @@ module Flux
|
|
32
45
|
elsif p == p.parent
|
33
46
|
nil
|
34
47
|
else
|
35
|
-
find_upwards(p.parent)
|
36
|
-
end
|
37
|
-
end
|
38
|
-
|
39
|
-
private
|
40
|
-
|
41
|
-
def load_adapter(kind, adapter)
|
42
|
-
adapter_path = "flux/#{kind}/#{adapter}"
|
43
|
-
|
44
|
-
begin
|
45
|
-
require adapter_path
|
46
|
-
rescue LoadError
|
47
|
-
raise FluxError, "Could not load `#{adapter_path}'."
|
48
|
+
find_upwards(object, p.parent)
|
48
49
|
end
|
49
50
|
end
|
51
|
+
end
|
52
|
+
end
|
50
53
|
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
rc_l = File.join(File.dirname(rc), RC_LOCAL)
|
56
|
-
|
57
|
-
env = YAML.load_file(rc)
|
58
|
-
env_l = File.exist?(rc_l) ? YAML.load_file(rc_l) : {}
|
54
|
+
module Kernel
|
55
|
+
def use(mod, options = {})
|
56
|
+
(Flux.environment[mod.to_s] ||= {}).
|
57
|
+
merge! Hash[*options.map { |k, v| [k.to_s, v] }.flatten]
|
59
58
|
|
60
|
-
env_l.each { |k, v| env[k].merge!(v) if env[k].respond_to?(:merge) }
|
61
59
|
|
62
|
-
env
|
63
|
-
end
|
64
60
|
end
|
65
61
|
end
|
data/lib/flux/cli.rb
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'flux/git'
|
2
|
+
|
3
|
+
module Flux
|
4
|
+
module CLI
|
5
|
+
class Feature < Thor
|
6
|
+
namespace :feature
|
7
|
+
|
8
|
+
desc "start STORY_ID BRANCH_ID", "start working on a story"
|
9
|
+
method_option :estimate, :type => :numeric
|
10
|
+
method_option :parent_branch, :type => :string,
|
11
|
+
:default => 'master',
|
12
|
+
:aliases => '-b'
|
13
|
+
def start(story_id, branch_id)
|
14
|
+
invoke 'pt:grab', [story_id], :estimate => options[:estimate]
|
15
|
+
invoke 'pt:start', [story_id]
|
16
|
+
|
17
|
+
create_branch branch_id, story_id, options
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def create_branch(branch_id, story_id, options = {})
|
23
|
+
up = Git::Branch.remote('origin', branch_id)
|
24
|
+
parent = Git::Branch.local(options[:parent_branch])
|
25
|
+
branch = Git::Branch.local(branch_id).
|
26
|
+
create(parent).
|
27
|
+
publish(up).
|
28
|
+
track(up).
|
29
|
+
checkout
|
30
|
+
|
31
|
+
link_branch_to_story branch_id, story_id
|
32
|
+
end
|
33
|
+
|
34
|
+
def link_branch_to_story(branch_id, story_id)
|
35
|
+
Git::Branch.local(branch_id).config('story_id', story_id)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
require 'pivotal-tracker'
|
2
|
+
require 'hirb'
|
3
|
+
require 'flux/pivotal_tracker'
|
4
|
+
require 'flux/ext/pivotal-tracker'
|
5
|
+
require 'forwardable'
|
6
|
+
|
7
|
+
module Flux
|
8
|
+
module CLI
|
9
|
+
class PT < Thor
|
10
|
+
extend Forwardable
|
11
|
+
include Flux::Util
|
12
|
+
|
13
|
+
namespace :pt
|
14
|
+
|
15
|
+
default_task :list
|
16
|
+
|
17
|
+
class << self
|
18
|
+
def config
|
19
|
+
Flux.environment['pivotal_tracker']
|
20
|
+
end
|
21
|
+
|
22
|
+
def login
|
23
|
+
::PivotalTracker::Client.token = config['token']
|
24
|
+
end
|
25
|
+
|
26
|
+
def story_update(name, attrs = nil, &get_attrs)
|
27
|
+
desc "#{name} STORY_ID", "#{name} a story"
|
28
|
+
method_option :estimate, :type => :numeric, :aliases => '-e'
|
29
|
+
define_method name do |story_id|
|
30
|
+
a = attrs || instance_eval(&get_attrs)
|
31
|
+
|
32
|
+
a[:estimate] = options[:estimate] if options[:estimate]
|
33
|
+
|
34
|
+
story(story_id).update a
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
no_tasks {
|
40
|
+
def_delegator self, :config
|
41
|
+
}
|
42
|
+
|
43
|
+
login if config
|
44
|
+
|
45
|
+
desc "list", "list stories, excluding icebox by default"
|
46
|
+
def list
|
47
|
+
puts Hirb::Helpers::ObjectTable.render(
|
48
|
+
current_project.stories.scheduled,
|
49
|
+
:fields => [:id, :state, :owned_by, :est, :name]
|
50
|
+
)
|
51
|
+
end
|
52
|
+
|
53
|
+
story_update :finish, :current_state => 'finished'
|
54
|
+
story_update(:grab) {{:owned_by => me.name}}
|
55
|
+
story_update :start, :current_state => 'started'
|
56
|
+
|
57
|
+
private
|
58
|
+
|
59
|
+
def current_project
|
60
|
+
Flux::PT::Project.new(config['project_id'])
|
61
|
+
end
|
62
|
+
|
63
|
+
def me
|
64
|
+
current_project.members.find { |m| m.email == config['email'] }
|
65
|
+
end
|
66
|
+
|
67
|
+
def story(id)
|
68
|
+
current_project.stories.find(id)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,190 @@
|
|
1
|
+
require 'open-uri'
|
2
|
+
require 'flux/git'
|
3
|
+
require 'github_api'
|
4
|
+
|
5
|
+
module Flux
|
6
|
+
module CLI
|
7
|
+
class Review < Thor
|
8
|
+
namespace 'review'
|
9
|
+
|
10
|
+
desc "diff", "show interdiff between last 2 reviews"
|
11
|
+
method_option :from, :type => :numeric, :aliases => '-f'
|
12
|
+
method_option :to, :type => :numeric, :aliases => '-t'
|
13
|
+
method_option :color, :type => :boolean, :aliases => '-c'
|
14
|
+
def diff(branch_name = Git::Branch.current.name)
|
15
|
+
reqs = pull_requests(:state => 'all', :branch_name => branch_name)
|
16
|
+
|
17
|
+
if options[:from] && options[:to]
|
18
|
+
to = reqs.find { |r| r[:number].to_i == options[:to].to_i }
|
19
|
+
from = reqs.find { |r| r[:number].to_i == options[:from].to_i }
|
20
|
+
else
|
21
|
+
to, from = reqs.first(2)
|
22
|
+
end
|
23
|
+
|
24
|
+
if from
|
25
|
+
auth = [gh_config['username'], gh_config['password']]
|
26
|
+
|
27
|
+
to_diff = Tempfile.new("to_diff")
|
28
|
+
to_diff.write(open(to[:diff_url],
|
29
|
+
:http_basic_authentication => auth).read)
|
30
|
+
|
31
|
+
from_diff = Tempfile.new("from_diff")
|
32
|
+
from_diff.write(open(from[:diff_url],
|
33
|
+
:http_basic_authentication => auth).read)
|
34
|
+
|
35
|
+
cmd = "interdiff #{from_diff.path} #{to_diff.path}"
|
36
|
+
cmd << "| colordiff"
|
37
|
+
|
38
|
+
system cmd
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
desc "request", "request a review for the current feature"
|
43
|
+
def request
|
44
|
+
branch = Git::Branch.current
|
45
|
+
story = current_project.stories.find(branch.config('story_id'))
|
46
|
+
body = {:branch_name => branch.name,
|
47
|
+
:story_url => story.url,
|
48
|
+
:iterations => iterations(branch.name)}
|
49
|
+
data = {:base => 'master',
|
50
|
+
:head => branch.commit.sha,
|
51
|
+
:title => story.name,
|
52
|
+
:body => PullRequestBody.to_markdown(body)}
|
53
|
+
|
54
|
+
gh.pull_requests.create_request gh_config['repo_user'],
|
55
|
+
gh_config['repo_name'],
|
56
|
+
data
|
57
|
+
end
|
58
|
+
|
59
|
+
desc "list", "list pull requests for the current project"
|
60
|
+
def list
|
61
|
+
list_pull_requests pull_requests
|
62
|
+
end
|
63
|
+
|
64
|
+
desc "history BRANCH_ID", "show review history for branch"
|
65
|
+
def history(branch_id = Git::Branch.current.name)
|
66
|
+
reqs = pull_requests(:branch_name => branch_id, :state => 'all')
|
67
|
+
|
68
|
+
list_pull_requests reqs
|
69
|
+
end
|
70
|
+
|
71
|
+
private
|
72
|
+
|
73
|
+
def config
|
74
|
+
Flux.environment
|
75
|
+
end
|
76
|
+
|
77
|
+
def current_project
|
78
|
+
Flux::PT::Project.new(pt_config['project_id'])
|
79
|
+
end
|
80
|
+
|
81
|
+
def gh
|
82
|
+
Github.new :login => gh_config['username'],
|
83
|
+
:password => gh_config['password']
|
84
|
+
end
|
85
|
+
|
86
|
+
def gh_config
|
87
|
+
config['github']
|
88
|
+
end
|
89
|
+
|
90
|
+
def pt_config
|
91
|
+
config['pivotal_tracker']
|
92
|
+
end
|
93
|
+
|
94
|
+
def iterations(branch_name)
|
95
|
+
last = pull_requests(:branch_name => branch_name).first
|
96
|
+
|
97
|
+
if last
|
98
|
+
[last[:url]].concat(last[:body][:iterations])
|
99
|
+
else
|
100
|
+
[]
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
def pull_requests(opts = {})
|
105
|
+
if opts[:state] == 'all'
|
106
|
+
return pull_requests(opts.merge(:state => 'open')) +
|
107
|
+
pull_requests(opts.merge(:state => 'closed'))
|
108
|
+
end
|
109
|
+
|
110
|
+
branch_name = opts.delete(:branch_name)
|
111
|
+
|
112
|
+
reqs = gh.pull_requests.pull_requests(gh_config['repo_user'],
|
113
|
+
gh_config['repo_name'],
|
114
|
+
opts).each { |r|
|
115
|
+
r[:author] = r[:user][:login]
|
116
|
+
r[:body] = PullRequestBody.from_markdown(r[:body])
|
117
|
+
r[:story_id] = Flux::PT::Story.id_from_url(r[:body][:story_url])
|
118
|
+
}
|
119
|
+
|
120
|
+
if branch_name
|
121
|
+
reqs.select { |r| r[:body][:branch_name] == branch_name }
|
122
|
+
else
|
123
|
+
reqs
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
def list_pull_requests(pull_requests)
|
128
|
+
puts Hirb::Helpers::Table.render(
|
129
|
+
pull_requests,
|
130
|
+
:fields => [:number, :title, :story_id, :author, :created_at]
|
131
|
+
)
|
132
|
+
end
|
133
|
+
|
134
|
+
module PullRequestBody
|
135
|
+
def self.from_markdown(md)
|
136
|
+
if (md || '').include?(signoff)
|
137
|
+
_, branch_name, story_url, iter = md.
|
138
|
+
gsub(signoff, '').
|
139
|
+
split(/### [^\n]+/).map(&:strip)
|
140
|
+
|
141
|
+
if iter
|
142
|
+
_, iterations = iter.split('*').map(&:strip)
|
143
|
+
|
144
|
+
iterations = Array(iterations)
|
145
|
+
else
|
146
|
+
iterations = []
|
147
|
+
end
|
148
|
+
else
|
149
|
+
branch_name = nil
|
150
|
+
story_url = "http://mojotech.com"
|
151
|
+
iterations = []
|
152
|
+
end
|
153
|
+
|
154
|
+
{:branch_name => branch_name,
|
155
|
+
:story_url => story_url,
|
156
|
+
:iterations => iterations}
|
157
|
+
end
|
158
|
+
|
159
|
+
def self.to_markdown(data)
|
160
|
+
tmpl = <<MARKDOWN
|
161
|
+
### Branch
|
162
|
+
|
163
|
+
#{data[:branch_name]}
|
164
|
+
|
165
|
+
### Story
|
166
|
+
|
167
|
+
#{data[:story_url]}
|
168
|
+
|
169
|
+
MARKDOWN
|
170
|
+
|
171
|
+
if data[:iterations].empty?
|
172
|
+
tmpl << signoff << "\n"
|
173
|
+
else
|
174
|
+
tmpl << <<ITERATIONS
|
175
|
+
### Prior Versions
|
176
|
+
|
177
|
+
#{"* " + data[:iterations].join("\n* ") + "\n"}
|
178
|
+
|
179
|
+
#{signoff}
|
180
|
+
ITERATIONS
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
def self.signoff
|
185
|
+
"*Your friendly neighborhood Flux*"
|
186
|
+
end
|
187
|
+
end
|
188
|
+
end
|
189
|
+
end
|
190
|
+
end
|