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.
Files changed (44) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +10 -0
  3. data/.travis.yml +4 -0
  4. data/CODE_OF_CONDUCT.md +49 -0
  5. data/Gemfile +4 -0
  6. data/LICENSE.txt +21 -0
  7. data/README.md +19 -0
  8. data/Rakefile +11 -0
  9. data/cp8_cli.gemspec +42 -0
  10. data/exe/cp8 +34 -0
  11. data/exe/git-cleanup +7 -0
  12. data/exe/git-finish +19 -0
  13. data/exe/git-open +7 -0
  14. data/exe/git-start +7 -0
  15. data/lib/cp8_cli.rb +9 -0
  16. data/lib/cp8_cli/branch.rb +61 -0
  17. data/lib/cp8_cli/branch_name.rb +30 -0
  18. data/lib/cp8_cli/cleanup.rb +36 -0
  19. data/lib/cp8_cli/command.rb +61 -0
  20. data/lib/cp8_cli/config_store.rb +32 -0
  21. data/lib/cp8_cli/current_user.rb +29 -0
  22. data/lib/cp8_cli/github/base.rb +13 -0
  23. data/lib/cp8_cli/github/issue.rb +92 -0
  24. data/lib/cp8_cli/github/parsed_short_link.rb +25 -0
  25. data/lib/cp8_cli/github/parsed_url.rb +25 -0
  26. data/lib/cp8_cli/global_config.rb +51 -0
  27. data/lib/cp8_cli/local_config.rb +29 -0
  28. data/lib/cp8_cli/main.rb +74 -0
  29. data/lib/cp8_cli/pull_request.rb +63 -0
  30. data/lib/cp8_cli/repo.rb +21 -0
  31. data/lib/cp8_cli/story_query.rb +23 -0
  32. data/lib/cp8_cli/table.rb +38 -0
  33. data/lib/cp8_cli/table/row.rb +45 -0
  34. data/lib/cp8_cli/trello/base.rb +36 -0
  35. data/lib/cp8_cli/trello/board.rb +13 -0
  36. data/lib/cp8_cli/trello/card.rb +71 -0
  37. data/lib/cp8_cli/trello/error.rb +5 -0
  38. data/lib/cp8_cli/trello/error_handler.rb +15 -0
  39. data/lib/cp8_cli/trello/json_parser.rb +13 -0
  40. data/lib/cp8_cli/trello/label.rb +15 -0
  41. data/lib/cp8_cli/trello/list.rb +32 -0
  42. data/lib/cp8_cli/trello/member.rb +11 -0
  43. data/lib/cp8_cli/version.rb +27 -0
  44. 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,13 @@
1
+ require "octokit"
2
+
3
+ module Cp8Cli
4
+ module Github
5
+ class Base
6
+ cattr_accessor :client
7
+
8
+ def self.configure(token:)
9
+ self.client = Octokit::Client.new(access_token: token)
10
+ end
11
+ end
12
+ end
13
+ 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
@@ -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
@@ -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