cp8_cli 4.0.1
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 +7 -0
- data/.gitignore +10 -0
- data/.travis.yml +4 -0
- data/CODE_OF_CONDUCT.md +49 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +21 -0
- data/README.md +19 -0
- data/Rakefile +11 -0
- data/cp8_cli.gemspec +42 -0
- data/exe/cp8 +34 -0
- data/exe/git-cleanup +7 -0
- data/exe/git-finish +19 -0
- data/exe/git-open +7 -0
- data/exe/git-start +7 -0
- data/lib/cp8_cli.rb +9 -0
- data/lib/cp8_cli/branch.rb +61 -0
- data/lib/cp8_cli/branch_name.rb +30 -0
- data/lib/cp8_cli/cleanup.rb +36 -0
- data/lib/cp8_cli/command.rb +61 -0
- data/lib/cp8_cli/config_store.rb +32 -0
- data/lib/cp8_cli/current_user.rb +29 -0
- data/lib/cp8_cli/github/base.rb +13 -0
- data/lib/cp8_cli/github/issue.rb +92 -0
- data/lib/cp8_cli/github/parsed_short_link.rb +25 -0
- data/lib/cp8_cli/github/parsed_url.rb +25 -0
- data/lib/cp8_cli/global_config.rb +51 -0
- data/lib/cp8_cli/local_config.rb +29 -0
- data/lib/cp8_cli/main.rb +74 -0
- data/lib/cp8_cli/pull_request.rb +63 -0
- data/lib/cp8_cli/repo.rb +21 -0
- data/lib/cp8_cli/story_query.rb +23 -0
- data/lib/cp8_cli/table.rb +38 -0
- data/lib/cp8_cli/table/row.rb +45 -0
- data/lib/cp8_cli/trello/base.rb +36 -0
- data/lib/cp8_cli/trello/board.rb +13 -0
- data/lib/cp8_cli/trello/card.rb +71 -0
- data/lib/cp8_cli/trello/error.rb +5 -0
- data/lib/cp8_cli/trello/error_handler.rb +15 -0
- data/lib/cp8_cli/trello/json_parser.rb +13 -0
- data/lib/cp8_cli/trello/label.rb +15 -0
- data/lib/cp8_cli/trello/list.rb +32 -0
- data/lib/cp8_cli/trello/member.rb +11 -0
- data/lib/cp8_cli/version.rb +27 -0
- metadata +357 -0
@@ -0,0 +1,32 @@
|
|
1
|
+
module Cp8Cli
|
2
|
+
class ConfigStore
|
3
|
+
def initialize(path)
|
4
|
+
@path = path
|
5
|
+
end
|
6
|
+
|
7
|
+
def [](key)
|
8
|
+
data[key]
|
9
|
+
end
|
10
|
+
|
11
|
+
def save(key, value)
|
12
|
+
data[key] = value
|
13
|
+
File.new(path, "w") unless File.exists?(path)
|
14
|
+
File.open(path, "w") { |f| f.write(data.to_yaml) }
|
15
|
+
value
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
attr_reader :path
|
21
|
+
|
22
|
+
def data
|
23
|
+
@_data ||= load_data
|
24
|
+
end
|
25
|
+
|
26
|
+
def load_data
|
27
|
+
YAML.load File.read(path)
|
28
|
+
rescue
|
29
|
+
{}
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Cp8Cli
|
2
|
+
class CurrentUser
|
3
|
+
def initials
|
4
|
+
git_user_name.parameterize(separator: " ").split.map(&:first).join
|
5
|
+
end
|
6
|
+
|
7
|
+
def trello_id
|
8
|
+
trello_user.id
|
9
|
+
end
|
10
|
+
|
11
|
+
def github_login
|
12
|
+
github_user.login
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def git_user_name
|
18
|
+
@_git_user_name ||= Command.read("git config user.name")
|
19
|
+
end
|
20
|
+
|
21
|
+
def github_user
|
22
|
+
@_github_user ||= Github::Base.client.user
|
23
|
+
end
|
24
|
+
|
25
|
+
def trello_user
|
26
|
+
@_trello_user ||= Trello::Member.current
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,92 @@
|
|
1
|
+
require "cp8_cli/github/base"
|
2
|
+
require "cp8_cli/github/parsed_url"
|
3
|
+
require "cp8_cli/github/parsed_short_link"
|
4
|
+
|
5
|
+
module Cp8Cli
|
6
|
+
module Github
|
7
|
+
class Issue < Base
|
8
|
+
def initialize(number:, repo:, attributes:)
|
9
|
+
@number = number
|
10
|
+
@repo = repo
|
11
|
+
@attributes = attributes
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.fields
|
15
|
+
[:title]
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.find_by_url(url)
|
19
|
+
url = ParsedUrl.new(url)
|
20
|
+
issue = client.issue(url.repo, url.number)
|
21
|
+
new number: url.number, repo: url.repo, attributes: issue
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.find_by_short_link(short_link)
|
25
|
+
short_link = ParsedShortLink.new(short_link)
|
26
|
+
issue = client.issue(short_link.repo, short_link.number)
|
27
|
+
new number: short_link.number, repo: short_link.repo, attributes: issue
|
28
|
+
end
|
29
|
+
|
30
|
+
def title
|
31
|
+
attributes[:title]
|
32
|
+
end
|
33
|
+
|
34
|
+
def pr_title
|
35
|
+
title
|
36
|
+
end
|
37
|
+
|
38
|
+
def url
|
39
|
+
attributes[:html_url]
|
40
|
+
end
|
41
|
+
|
42
|
+
def summary
|
43
|
+
"Closes #{short_link}"
|
44
|
+
end
|
45
|
+
|
46
|
+
def start
|
47
|
+
# noop for now
|
48
|
+
end
|
49
|
+
|
50
|
+
def finish
|
51
|
+
# noop for now
|
52
|
+
end
|
53
|
+
|
54
|
+
def accept
|
55
|
+
# noop for now
|
56
|
+
end
|
57
|
+
|
58
|
+
def assign(user)
|
59
|
+
# add_assignes not released as gem yet https://github.com/octokit/octokit.rb/pull/894
|
60
|
+
client.post "#{Octokit::Repository.path repo}/issues/#{number}/assignees", assignees: [user.github_login]
|
61
|
+
end
|
62
|
+
|
63
|
+
def add_label(label)
|
64
|
+
self.class.request(:post, "cards/#{id}/idLabels", value: label.id)
|
65
|
+
end
|
66
|
+
|
67
|
+
def attach(url:)
|
68
|
+
self.class.request(:post, "cards/#{id}/attachments", url: url)
|
69
|
+
end
|
70
|
+
|
71
|
+
def short_link
|
72
|
+
"#{repo}##{number}"
|
73
|
+
end
|
74
|
+
|
75
|
+
def short_url
|
76
|
+
attributes[:shortUrl]
|
77
|
+
end
|
78
|
+
|
79
|
+
private
|
80
|
+
|
81
|
+
attr_reader :number, :repo, :attributes
|
82
|
+
|
83
|
+
def move_to(list)
|
84
|
+
self.class.with("cards/:id/idList").where(id: id, value: list.id).put
|
85
|
+
end
|
86
|
+
|
87
|
+
def member_ids
|
88
|
+
attributes["idMembers"] || []
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Cp8Cli
|
2
|
+
module Github
|
3
|
+
class ParsedShortLink
|
4
|
+
def initialize(short_link)
|
5
|
+
@short_link = short_link
|
6
|
+
end
|
7
|
+
|
8
|
+
def number
|
9
|
+
parts.last
|
10
|
+
end
|
11
|
+
|
12
|
+
def repo
|
13
|
+
parts.first
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
attr_accessor :short_link
|
19
|
+
|
20
|
+
def parts
|
21
|
+
short_link.split("#")
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Cp8Cli
|
2
|
+
module Github
|
3
|
+
class ParsedUrl
|
4
|
+
def initialize(url)
|
5
|
+
@url = url
|
6
|
+
end
|
7
|
+
|
8
|
+
def number
|
9
|
+
parts.last
|
10
|
+
end
|
11
|
+
|
12
|
+
def repo
|
13
|
+
parts[3, 2].join("/")
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
attr_accessor :url
|
19
|
+
|
20
|
+
def parts
|
21
|
+
url.split("/")
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require "cp8_cli/config_store"
|
2
|
+
require "cp8_cli/trello/base"
|
3
|
+
|
4
|
+
module Cp8Cli
|
5
|
+
class GlobalConfig
|
6
|
+
PATH = ENV["HOME"] + "/.trello_flow"
|
7
|
+
|
8
|
+
def initialize(store = nil)
|
9
|
+
@store = store || ConfigStore.new(PATH)
|
10
|
+
end
|
11
|
+
|
12
|
+
def trello_key
|
13
|
+
@_trello_key ||= store[:key] || configure_trello_key
|
14
|
+
end
|
15
|
+
|
16
|
+
def trello_token
|
17
|
+
@_trello_token ||= store[:token] || configure_trello_token
|
18
|
+
end
|
19
|
+
|
20
|
+
def github_token
|
21
|
+
@_github_token ||= store[:github_token] || env_github_token || configure_github_token
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
attr_reader :store
|
27
|
+
|
28
|
+
def configure_trello_key
|
29
|
+
Command.ask "Press enter to setup Trello for this project (will open public key url)"
|
30
|
+
Command.open_url "https://trello.com/app-key"
|
31
|
+
store.save :key, Command.ask("Input Developer API key")
|
32
|
+
end
|
33
|
+
|
34
|
+
def configure_trello_token
|
35
|
+
Command.open_url trello_authorize_url
|
36
|
+
store.save :token, Command.ask("Input member token")
|
37
|
+
end
|
38
|
+
|
39
|
+
def env_github_token
|
40
|
+
ENV["OCTOKIT_ACCESS_TOKEN"]
|
41
|
+
end
|
42
|
+
|
43
|
+
def configure_github_token
|
44
|
+
store.save :github_token, Command.ask("Input GitHub token")
|
45
|
+
end
|
46
|
+
|
47
|
+
def trello_authorize_url
|
48
|
+
"https://trello.com/1/authorize?key=#{trello_key}&name=Trello-Flow&scope=read,write,account&expiration=never&response_type=token"
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Cp8Cli
|
2
|
+
class LocalConfig
|
3
|
+
PATH = ".trello_flow"
|
4
|
+
|
5
|
+
def initialize(store = nil)
|
6
|
+
@store = store || ConfigStore.new(PATH)
|
7
|
+
end
|
8
|
+
|
9
|
+
def board
|
10
|
+
@_board ||= Trello::Board.find(board_id)
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
attr_reader :store
|
16
|
+
|
17
|
+
def board_id
|
18
|
+
@_board_id ||= store[:board_id] || configure_board_id
|
19
|
+
end
|
20
|
+
|
21
|
+
def configure_board_id
|
22
|
+
store.save :board_id, Table.pick(trello_user.boards.active).id
|
23
|
+
end
|
24
|
+
|
25
|
+
def trello_user
|
26
|
+
Trello::Member.current
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
data/lib/cp8_cli/main.rb
ADDED
@@ -0,0 +1,74 @@
|
|
1
|
+
require "cp8_cli/version"
|
2
|
+
require "cp8_cli/local_config"
|
3
|
+
require "cp8_cli/global_config"
|
4
|
+
require "cp8_cli/github/issue"
|
5
|
+
require "cp8_cli/current_user"
|
6
|
+
|
7
|
+
module Cp8Cli
|
8
|
+
class Main
|
9
|
+
def initialize(global_config = GlobalConfig.new, local_config = LocalConfig.new)
|
10
|
+
Trello::Base.configure(key: global_config.trello_key, token: global_config.trello_token)
|
11
|
+
Github::Base.configure(token: global_config.github_token)
|
12
|
+
@local_config = local_config
|
13
|
+
end
|
14
|
+
|
15
|
+
def start(name)
|
16
|
+
Command.error "Your `cp8_cli` version is out of date. Please run `gem update cp8_cli`." unless Version.latest?
|
17
|
+
story = create_or_pick_story(name)
|
18
|
+
story.assign(current_user)
|
19
|
+
story.start
|
20
|
+
Branch.from_story(user: current_user, story: story).checkout
|
21
|
+
rescue Trello::Error => error
|
22
|
+
Command.error(error.message)
|
23
|
+
end
|
24
|
+
|
25
|
+
def open
|
26
|
+
Branch.current.open_story_in_browser
|
27
|
+
end
|
28
|
+
|
29
|
+
def finish(options = {})
|
30
|
+
branch = Branch.current
|
31
|
+
branch.push
|
32
|
+
branch.open_pull_request(options)
|
33
|
+
end
|
34
|
+
|
35
|
+
def cleanup
|
36
|
+
Cleanup.new(Branch.current.target).run
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
attr_reader :local_config
|
42
|
+
|
43
|
+
def board
|
44
|
+
@_board ||= local_config.board
|
45
|
+
end
|
46
|
+
|
47
|
+
def create_or_pick_story(name)
|
48
|
+
if name.to_s.start_with?("https://github.com")
|
49
|
+
Github::Issue.find_by_url(name)
|
50
|
+
elsif name.to_s.start_with?("http")
|
51
|
+
Trello::Card.find_by_url(name)
|
52
|
+
elsif name.present?
|
53
|
+
create_new_card(name)
|
54
|
+
else
|
55
|
+
pick_existing_card
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def create_new_card(name)
|
60
|
+
label = Table.pick board.labels, caption: "Add label:"
|
61
|
+
card = board.lists.backlog.cards.create name: name
|
62
|
+
card.add_label(label) if label
|
63
|
+
card
|
64
|
+
end
|
65
|
+
|
66
|
+
def pick_existing_card
|
67
|
+
Table.pick board.lists.backlog.cards
|
68
|
+
end
|
69
|
+
|
70
|
+
def current_user
|
71
|
+
@_current_user ||= CurrentUser.new
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
require "cp8_cli/repo"
|
2
|
+
|
3
|
+
module Cp8Cli
|
4
|
+
class PullRequest
|
5
|
+
def initialize(from:, target:, story: nil, **options)
|
6
|
+
@story = story
|
7
|
+
@from = from
|
8
|
+
@target = target
|
9
|
+
@options = options
|
10
|
+
end
|
11
|
+
|
12
|
+
def open
|
13
|
+
Command.open_url url
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
attr_reader :story, :from, :target, :options
|
19
|
+
|
20
|
+
def url
|
21
|
+
repo.url + "/compare/#{target}...#{escape from}?expand=1&title=#{escape title_with_prefixes}&body=#{escape body}"
|
22
|
+
end
|
23
|
+
|
24
|
+
def title
|
25
|
+
return unless story
|
26
|
+
story.pr_title
|
27
|
+
end
|
28
|
+
|
29
|
+
def body
|
30
|
+
return unless story
|
31
|
+
body = story.summary
|
32
|
+
body << release_note unless release_branch?
|
33
|
+
body
|
34
|
+
end
|
35
|
+
|
36
|
+
def release_note
|
37
|
+
"\n\n_Release note: #{story.title}_"
|
38
|
+
end
|
39
|
+
|
40
|
+
def prefixes
|
41
|
+
prefixes = []
|
42
|
+
prefixes << "[WIP]" if options[:wip]
|
43
|
+
prefixes << "[#{target.titleize}]" if release_branch?
|
44
|
+
prefixes.join(" ")
|
45
|
+
end
|
46
|
+
|
47
|
+
def release_branch?
|
48
|
+
target != "master"
|
49
|
+
end
|
50
|
+
|
51
|
+
def title_with_prefixes
|
52
|
+
"#{prefixes} #{title}".strip
|
53
|
+
end
|
54
|
+
|
55
|
+
def escape(text)
|
56
|
+
URI.escape text.to_s.strip
|
57
|
+
end
|
58
|
+
|
59
|
+
def repo
|
60
|
+
@_repo ||= Repo.new
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
data/lib/cp8_cli/repo.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
module Cp8Cli
|
2
|
+
class Repo
|
3
|
+
def user
|
4
|
+
path.split('/').first
|
5
|
+
end
|
6
|
+
|
7
|
+
def name
|
8
|
+
path.split('/').last
|
9
|
+
end
|
10
|
+
|
11
|
+
def url
|
12
|
+
"https://github.com/#{user}/#{name}"
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def path
|
18
|
+
@_path ||= Command.read("git config --get remote.origin.url").match(/github.com[:\/](\S+\/\S+)\.git/)[1]
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|