story_branch 0.2.11 → 0.3.0
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/.circleci/config.yml +37 -0
- data/.github/ISSUE_TEMPLATE.md +12 -0
- data/.gitignore +3 -0
- data/.rspec +2 -0
- data/Gemfile +6 -0
- data/Gemfile.lock +94 -0
- data/{LICENCE → LICENSE.txt} +1 -1
- data/README.md +79 -60
- data/Rakefile +6 -0
- data/exe/git-finish +3 -0
- data/exe/git-start +3 -0
- data/exe/git-story +3 -0
- data/exe/git-unstart +3 -0
- data/exe/story_branch +18 -0
- data/lib/story_branch.rb +2 -477
- data/lib/story_branch/cli.rb +93 -0
- data/lib/story_branch/command.rb +121 -0
- data/lib/story_branch/commands/.gitkeep +1 -0
- data/lib/story_branch/commands/add.rb +48 -0
- data/lib/story_branch/commands/create.rb +21 -0
- data/lib/story_branch/commands/finish.rb +20 -0
- data/lib/story_branch/commands/migrate.rb +100 -0
- data/lib/story_branch/commands/start.rb +20 -0
- data/lib/story_branch/commands/unstart.rb +20 -0
- data/lib/story_branch/config_manager.rb +18 -0
- data/lib/story_branch/git_utils.rb +85 -0
- data/lib/story_branch/main.rb +124 -0
- data/lib/story_branch/pivotal_utils.rb +146 -0
- data/lib/story_branch/string_utils.rb +23 -0
- data/lib/story_branch/templates/.gitkeep +1 -0
- data/lib/story_branch/templates/add/.gitkeep +1 -0
- data/lib/story_branch/templates/config/.gitkeep +1 -0
- data/lib/story_branch/templates/create/.gitkeep +1 -0
- data/lib/story_branch/templates/finish/.gitkeep +1 -0
- data/lib/story_branch/templates/migrate/.gitkeep +1 -0
- data/lib/story_branch/templates/start/.gitkeep +1 -0
- data/lib/story_branch/templates/unstart/.gitkeep +1 -0
- data/lib/story_branch/version.rb +3 -0
- data/story_branch.gemspec +54 -0
- metadata +168 -22
- data/bin/git-finish +0 -4
- data/bin/git-start +0 -4
- data/bin/git-story +0 -4
- data/bin/git-unstart +0 -4
@@ -0,0 +1,93 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'thor'
|
4
|
+
|
5
|
+
module StoryBranch
|
6
|
+
# Handle the application command line parsing
|
7
|
+
# and the dispatch to various command objects
|
8
|
+
#
|
9
|
+
# @api public
|
10
|
+
class CLI < Thor
|
11
|
+
# Error raised by this runner
|
12
|
+
Error = Class.new(StandardError)
|
13
|
+
|
14
|
+
desc 'version', 'story_branch gem version'
|
15
|
+
def version
|
16
|
+
require_relative 'version'
|
17
|
+
puts "v#{StoryBranch::VERSION}"
|
18
|
+
end
|
19
|
+
map %w[--version -v] => :version
|
20
|
+
|
21
|
+
desc 'unstart', 'Mark a started story as un-started in Pivotal Tracker'
|
22
|
+
method_option :help, aliases: '-h', type: :boolean,
|
23
|
+
desc: 'Display usage information'
|
24
|
+
def unstart(*)
|
25
|
+
if options[:help]
|
26
|
+
invoke :help, ['unstart']
|
27
|
+
else
|
28
|
+
require_relative 'commands/unstart'
|
29
|
+
StoryBranch::Commands::Unstart.new(options).execute
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
desc 'start', 'Mark an estimated story as started in Pivotal Tracker'
|
34
|
+
method_option :help, aliases: '-h', type: :boolean,
|
35
|
+
desc: 'Display usage information'
|
36
|
+
def start(*)
|
37
|
+
if options[:help]
|
38
|
+
invoke :help, ['start']
|
39
|
+
else
|
40
|
+
require_relative 'commands/start'
|
41
|
+
StoryBranch::Commands::Start.new(options).execute
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
desc 'finish', 'Creates a git commit message for the staged changes with a [Finishes] tag'
|
46
|
+
method_option :help, aliases: '-h', type: :boolean,
|
47
|
+
desc: 'Display usage information'
|
48
|
+
def finish(*)
|
49
|
+
if options[:help]
|
50
|
+
invoke :help, ['finish']
|
51
|
+
else
|
52
|
+
require_relative 'commands/finish'
|
53
|
+
StoryBranch::Commands::Finish.new(options).execute
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
desc 'create', 'Create branch from estimated stories in pivotal tracker'
|
58
|
+
method_option :help, aliases: '-h', type: :boolean,
|
59
|
+
desc: 'Display usage information'
|
60
|
+
def create(*)
|
61
|
+
if options[:help]
|
62
|
+
invoke :help, ['create']
|
63
|
+
else
|
64
|
+
require_relative 'commands/create'
|
65
|
+
StoryBranch::Commands::Create.new(options).execute
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
desc 'add', 'Add a new story branch configuration'
|
70
|
+
method_option :help, aliases: '-h', type: :boolean,
|
71
|
+
desc: 'Display usage information'
|
72
|
+
def add(*)
|
73
|
+
if options[:help]
|
74
|
+
invoke :help, ['add']
|
75
|
+
else
|
76
|
+
require_relative 'commands/add'
|
77
|
+
StoryBranch::Commands::Add.new(options).execute
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
desc 'migrate', 'Migrate old story branch configuration to the new format'
|
82
|
+
method_option :help, aliases: '-h', type: :boolean,
|
83
|
+
desc: 'Display usage information'
|
84
|
+
def migrate(*)
|
85
|
+
if options[:help]
|
86
|
+
invoke :help, ['migrate']
|
87
|
+
else
|
88
|
+
require_relative 'commands/migrate'
|
89
|
+
StoryBranch::Commands::Migrate.new(options).execute
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
@@ -0,0 +1,121 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'forwardable'
|
4
|
+
|
5
|
+
module StoryBranch
|
6
|
+
class Command
|
7
|
+
extend Forwardable
|
8
|
+
|
9
|
+
def_delegators :command, :run
|
10
|
+
|
11
|
+
# Execute this command
|
12
|
+
#
|
13
|
+
# @api public
|
14
|
+
def execute(*)
|
15
|
+
raise(
|
16
|
+
NotImplementedError,
|
17
|
+
"#{self.class}##{__method__} must be implemented"
|
18
|
+
)
|
19
|
+
end
|
20
|
+
|
21
|
+
# The external commands runner
|
22
|
+
#
|
23
|
+
# @see http://www.rubydoc.info/gems/tty-command
|
24
|
+
#
|
25
|
+
# @api public
|
26
|
+
def command(**options)
|
27
|
+
require 'tty-command'
|
28
|
+
TTY::Command.new(options)
|
29
|
+
end
|
30
|
+
|
31
|
+
# The cursor movement
|
32
|
+
#
|
33
|
+
# @see http://www.rubydoc.info/gems/tty-cursor
|
34
|
+
#
|
35
|
+
# @api public
|
36
|
+
def cursor
|
37
|
+
require 'tty-cursor'
|
38
|
+
TTY::Cursor
|
39
|
+
end
|
40
|
+
|
41
|
+
# Open a file or text in the user's preferred editor
|
42
|
+
#
|
43
|
+
# @see http://www.rubydoc.info/gems/tty-editor
|
44
|
+
#
|
45
|
+
# @api public
|
46
|
+
def editor
|
47
|
+
require 'tty-editor'
|
48
|
+
TTY::Editor
|
49
|
+
end
|
50
|
+
|
51
|
+
# File manipulation utility methods
|
52
|
+
#
|
53
|
+
# @see http://www.rubydoc.info/gems/tty-file
|
54
|
+
#
|
55
|
+
# @api public
|
56
|
+
def generator
|
57
|
+
require 'tty-file'
|
58
|
+
TTY::File
|
59
|
+
end
|
60
|
+
|
61
|
+
# Terminal output paging
|
62
|
+
#
|
63
|
+
# @see http://www.rubydoc.info/gems/tty-pager
|
64
|
+
#
|
65
|
+
# @api public
|
66
|
+
def pager(**options)
|
67
|
+
require 'tty-pager'
|
68
|
+
TTY::Pager.new(options)
|
69
|
+
end
|
70
|
+
|
71
|
+
# Terminal platform and OS properties
|
72
|
+
#
|
73
|
+
# @see http://www.rubydoc.info/gems/tty-pager
|
74
|
+
#
|
75
|
+
# @api public
|
76
|
+
def platform
|
77
|
+
require 'tty-platform'
|
78
|
+
TTY::Platform.new
|
79
|
+
end
|
80
|
+
|
81
|
+
# The interactive prompt
|
82
|
+
#
|
83
|
+
# @see http://www.rubydoc.info/gems/tty-prompt
|
84
|
+
#
|
85
|
+
# @api public
|
86
|
+
def prompt(**options)
|
87
|
+
require 'tty-prompt'
|
88
|
+
TTY::Prompt.new(options)
|
89
|
+
end
|
90
|
+
|
91
|
+
# Get terminal screen properties
|
92
|
+
#
|
93
|
+
# @see http://www.rubydoc.info/gems/tty-screen
|
94
|
+
#
|
95
|
+
# @api public
|
96
|
+
def screen
|
97
|
+
require 'tty-screen'
|
98
|
+
TTY::Screen
|
99
|
+
end
|
100
|
+
|
101
|
+
# The unix which utility
|
102
|
+
#
|
103
|
+
# @see http://www.rubydoc.info/gems/tty-which
|
104
|
+
#
|
105
|
+
# @api public
|
106
|
+
def which(*args)
|
107
|
+
require 'tty-which'
|
108
|
+
TTY::Which.which(*args)
|
109
|
+
end
|
110
|
+
|
111
|
+
# Check if executable exists
|
112
|
+
#
|
113
|
+
# @see http://www.rubydoc.info/gems/tty-which
|
114
|
+
#
|
115
|
+
# @api public
|
116
|
+
def exec_exist?(*args)
|
117
|
+
require 'tty-which'
|
118
|
+
TTY::Which.exist?(*args)
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
#
|
@@ -0,0 +1,48 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../config_manager'
|
4
|
+
require_relative '../command'
|
5
|
+
require 'tty-config'
|
6
|
+
require 'tty-prompt'
|
7
|
+
|
8
|
+
module StoryBranch
|
9
|
+
module Commands
|
10
|
+
# Command responsible for adding a new configuration to
|
11
|
+
# the available configurations
|
12
|
+
#
|
13
|
+
# It will try to load the existing global story branch config
|
14
|
+
# and then add the project id specified by the user.
|
15
|
+
class Add < StoryBranch::Command
|
16
|
+
def initialize(options)
|
17
|
+
@options = options
|
18
|
+
@config = ConfigManager.init_config(ENV['HOME'])
|
19
|
+
@local_config = ConfigManager.init_config('.')
|
20
|
+
end
|
21
|
+
|
22
|
+
def execute(_input: $stdin, output: $stdout)
|
23
|
+
create_global_config
|
24
|
+
create_local_config
|
25
|
+
output.puts 'Configuration added successfully'
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def create_local_config
|
31
|
+
@local_config.set(:project_id, value: project_id)
|
32
|
+
@local_config.write(force: true)
|
33
|
+
end
|
34
|
+
|
35
|
+
def create_global_config
|
36
|
+
api_key = prompt.ask 'Please provide the api key:'
|
37
|
+
@config.set(project_id, :api_key, value: api_key)
|
38
|
+
@config.write(force: true)
|
39
|
+
end
|
40
|
+
|
41
|
+
def project_id
|
42
|
+
return @project_id if @project_id
|
43
|
+
@project_id = prompt.ask "Please provide this project's id:"
|
44
|
+
@project_id
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../command'
|
4
|
+
|
5
|
+
module StoryBranch
|
6
|
+
module Commands
|
7
|
+
# Create command is used to create a branch from
|
8
|
+
# started stories in the tracker
|
9
|
+
class Create < StoryBranch::Command
|
10
|
+
def initialize(options)
|
11
|
+
@options = options
|
12
|
+
end
|
13
|
+
|
14
|
+
def execute(_input: $stdin, _output: $stdout)
|
15
|
+
require_relative '../main'
|
16
|
+
sb = StoryBranch::Main.new
|
17
|
+
sb.create_story_branch
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../command'
|
4
|
+
|
5
|
+
module StoryBranch
|
6
|
+
module Commands
|
7
|
+
# Command to finish a story
|
8
|
+
class Finish < StoryBranch::Command
|
9
|
+
def initialize(options)
|
10
|
+
@options = options
|
11
|
+
end
|
12
|
+
|
13
|
+
def execute(_input: $stdin, _output: $stdout)
|
14
|
+
require_relative '../main'
|
15
|
+
sb = StoryBranch::Main.new
|
16
|
+
sb.story_finish
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,100 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../config_manager'
|
4
|
+
require_relative '../command'
|
5
|
+
require 'yaml'
|
6
|
+
require 'fileutils'
|
7
|
+
|
8
|
+
module StoryBranch
|
9
|
+
module Commands
|
10
|
+
# Migrate command is intended to make the migration from old version
|
11
|
+
# of story branch to the latest one easier.
|
12
|
+
class Migrate < StoryBranch::Command
|
13
|
+
GLOBAL_CONFIG_FILE = "#{Dir.home}/.story_branch".freeze
|
14
|
+
LOCAL_CONFIG_FILE = '.story_branch'.freeze
|
15
|
+
OLD_CONFIG_FILES = [LOCAL_CONFIG_FILE, GLOBAL_CONFIG_FILE].freeze
|
16
|
+
|
17
|
+
def initialize(options)
|
18
|
+
@options = options
|
19
|
+
@config = ConfigManager.init_config(Dir.home)
|
20
|
+
end
|
21
|
+
|
22
|
+
def execute(_input: $stdin, output: $stdout)
|
23
|
+
if missing_old_config?
|
24
|
+
error_migrating(output, old_config_file_not_found)
|
25
|
+
return
|
26
|
+
end
|
27
|
+
@config.set(project_id, :api_key, value: api_key)
|
28
|
+
@config.write(force: true)
|
29
|
+
create_local_config
|
30
|
+
clean_old_config_files
|
31
|
+
output.puts 'Migration complete'
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def project_id
|
37
|
+
return @project_id if @project_id
|
38
|
+
@project_id = old_config_value('project', 'PIVOTAL_PROJECT_ID')
|
39
|
+
@project_id
|
40
|
+
end
|
41
|
+
|
42
|
+
def api_key
|
43
|
+
return @api_key if @api_key
|
44
|
+
@api_key = old_config_value('api', 'PIVOTAL_API_KEY')
|
45
|
+
@api_key
|
46
|
+
end
|
47
|
+
|
48
|
+
def error_migrating(output, error_message)
|
49
|
+
output.puts error_message
|
50
|
+
end
|
51
|
+
|
52
|
+
def missing_old_config?
|
53
|
+
OLD_CONFIG_FILES.each { |file| return false if File.exist?(file) }
|
54
|
+
return false if env_set?
|
55
|
+
true
|
56
|
+
end
|
57
|
+
|
58
|
+
def env_set?
|
59
|
+
ENV['PIVOTAL_API_KEY'].length.positive? ||
|
60
|
+
ENV['PIVOTAL_PROJECT_ID'].length.positive?
|
61
|
+
end
|
62
|
+
|
63
|
+
def old_config_value(key, env)
|
64
|
+
OLD_CONFIG_FILES.each do |config_file|
|
65
|
+
if File.exist? config_file
|
66
|
+
old_config = YAML.load_file config_file
|
67
|
+
return old_config[key].to_s if old_config && old_config[key]
|
68
|
+
end
|
69
|
+
end
|
70
|
+
ENV[env]
|
71
|
+
end
|
72
|
+
|
73
|
+
def create_local_config
|
74
|
+
local_config = ConfigManager.init_config('.')
|
75
|
+
local_config.set(:project_id, value: project_id)
|
76
|
+
local_config.write
|
77
|
+
end
|
78
|
+
|
79
|
+
def clean_old_config_files
|
80
|
+
[GLOBAL_CONFIG_FILE, LOCAL_CONFIG_FILE].each do |file|
|
81
|
+
FileUtils.rm file if File.exist? file
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def old_config_file_not_found
|
86
|
+
<<-MESSAGE
|
87
|
+
Old configuration not found.
|
88
|
+
Trying to start from scratch? Use story_branch add
|
89
|
+
MESSAGE
|
90
|
+
end
|
91
|
+
|
92
|
+
def cant_migrate_missing_value
|
93
|
+
<<-MESSAGE
|
94
|
+
Old configuration not found. Nothing has been migrated
|
95
|
+
Trying to start from scratch? Use story_branch add
|
96
|
+
MESSAGE
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../command'
|
4
|
+
|
5
|
+
module StoryBranch
|
6
|
+
module Commands
|
7
|
+
# Command to start an estimated story
|
8
|
+
class Start < StoryBranch::Command
|
9
|
+
def initialize(options)
|
10
|
+
@options = options
|
11
|
+
end
|
12
|
+
|
13
|
+
def execute(_input: $stdin, _output: $stdout)
|
14
|
+
require_relative '../main'
|
15
|
+
sb = StoryBranch::Main.new
|
16
|
+
sb.story_start
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../command'
|
4
|
+
|
5
|
+
module StoryBranch
|
6
|
+
module Commands
|
7
|
+
# Command to unstart a previously started story
|
8
|
+
class Unstart < StoryBranch::Command
|
9
|
+
def initialize(options)
|
10
|
+
@options = options
|
11
|
+
end
|
12
|
+
|
13
|
+
def execute(_input: $stdin, _output: $stdout)
|
14
|
+
require_relative '../main'
|
15
|
+
sb = StoryBranch::Main.new
|
16
|
+
sb.story_unstart
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|