linear-cli 0.4.5 → 0.5.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Readme.adoc +1 -1
- data/lib/linear/cli/sub_commands.rb +58 -9
- data/lib/linear/cli/version.rb +1 -1
- data/lib/linear/commands/issue/create.rb +6 -3
- data/lib/linear/commands/issue/develop.rb +37 -0
- data/lib/linear/commands/issue.rb +10 -3
- data/lib/linear/models/issue.rb +1 -0
- data/lib/linear/models/label.rb +16 -1
- data/lib/linear/models/team.rb +18 -12
- data/linear-cli.gemspec +2 -0
- metadata +31 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 206356cf2490034419acd5c602e22c0a1f73057072301e956ef63b5657a520f6
|
4
|
+
data.tar.gz: 48ca5f95f3cab78035d6d5c9669fb2ab5cc166300713cc49937d21a19ba0dc45
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5d681c1875a5c09ab169573bd4e464030613e25209bf58bc72126ac7d58b52c596c784414cc81922c963098bd451b8e92ba563019b441d84d327abac02afd3ba
|
7
|
+
data.tar.gz: a4f884b9149d1361618dc8df774424a6e5e6d797ff0a6e3dfdc126005287ee13c989195548198570dd2c5c59f44805f03b753092bd668462f68a2baffbb580be
|
data/Readme.adoc
CHANGED
@@ -15,21 +15,25 @@ module Rubyists
|
|
15
15
|
end
|
16
16
|
end
|
17
17
|
|
18
|
-
def
|
18
|
+
def choose_a_team!(teams)
|
19
|
+
prompt.on(:keypress) do |event|
|
20
|
+
prompt.trigger(:keydown) if event.value == 'j'
|
21
|
+
prompt.trigger(:keyup) if event.value == 'k'
|
22
|
+
end
|
23
|
+
key = prompt.select('Choose a team', teams.to_h { |t| [t.name, t.key] })
|
24
|
+
Rubyists::Linear::Team.find key
|
25
|
+
end
|
26
|
+
|
27
|
+
def ask_for_team
|
19
28
|
teams = Rubyists::Linear::Team.mine
|
20
29
|
if teams.size == 1
|
21
30
|
logger.info('Only one team found, using it', team: teams.first.name)
|
22
31
|
teams.first
|
23
32
|
elsif teams.empty?
|
24
|
-
logger.error('No teams found for you. Please join a team or pass an existing team
|
25
|
-
raise SmellsBad, 'No team given and none found for you'
|
33
|
+
logger.error('No teams found for you. Please join a team or pass an existing team ID.')
|
34
|
+
raise SmellsBad, 'No team given and none found for you (try joining a team or use a team id from `lc teams --no-mine`)' # rubocop:disable Layout/LineLength
|
26
35
|
else
|
27
|
-
|
28
|
-
prompt.trigger(:keydown) if event.value == 'j'
|
29
|
-
prompt.trigger(:keyup) if event.value == 'k'
|
30
|
-
end
|
31
|
-
key = prompt.select('Choose a team', teams.to_h { |t| [t.name, t.key] })
|
32
|
-
Rubyists::Linear::Team.find key
|
36
|
+
choose_a_team! teams
|
33
37
|
end
|
34
38
|
end
|
35
39
|
|
@@ -58,8 +62,53 @@ module Rubyists
|
|
58
62
|
def labels_for(team, labels = nil)
|
59
63
|
return Rubyists::Linear::Label.find_all_by_name(labels.map(&:strip)) if labels
|
60
64
|
|
65
|
+
prompt.on(:keypress) do |event|
|
66
|
+
prompt.trigger(:keydown) if event.value == 'j'
|
67
|
+
prompt.trigger(:keyup) if event.value == 'k'
|
68
|
+
end
|
61
69
|
prompt.multi_select('Labels:', team.labels.to_h { |t| [t.name, t] })
|
62
70
|
end
|
71
|
+
|
72
|
+
def cut_branch!(branch_name)
|
73
|
+
if current_branch != default_branch
|
74
|
+
prompt.yes?("You are not on the default branch (#{default_branch}). Do you want to checkout #{default_branch} and create a new branch?") && git.checkout(default_branch) # rubocop:disable Layout/LineLength
|
75
|
+
end
|
76
|
+
git.branch(branch_name)
|
77
|
+
end
|
78
|
+
|
79
|
+
def branch_for(branch_name)
|
80
|
+
logger.trace('Looking for branch', branch_name:)
|
81
|
+
existing = git.branches[branch_name]
|
82
|
+
return cut_branch!(branch_name) unless existing
|
83
|
+
|
84
|
+
logger.trace('Branch found', branch: existing&.name)
|
85
|
+
existing
|
86
|
+
end
|
87
|
+
|
88
|
+
def current_branch
|
89
|
+
git.current_branch
|
90
|
+
end
|
91
|
+
|
92
|
+
# Horrible way to do this, but it is working for now
|
93
|
+
def pull_or_push_new_branch!(branch_name)
|
94
|
+
git.pull
|
95
|
+
rescue Git::FailedError
|
96
|
+
prompt.warn("Upstream branch not found, pushing local #{branch_name} to origin")
|
97
|
+
git.push('origin', branch_name)
|
98
|
+
`git branch --set-upstream-to=origin/#{branch_name} #{branch_name}`
|
99
|
+
prompt.ok("Set upstream to origin/#{branch_name}")
|
100
|
+
end
|
101
|
+
|
102
|
+
def git
|
103
|
+
@git ||= Git.open('.')
|
104
|
+
rescue Git::Repository::NoRepositoryError => e
|
105
|
+
logger.error('Your current directory is not a git repository!', error: e)
|
106
|
+
exit 121
|
107
|
+
end
|
108
|
+
|
109
|
+
def default_branch
|
110
|
+
@default_branch ||= Git.default_branch git.repo.path
|
111
|
+
end
|
63
112
|
end
|
64
113
|
end
|
65
114
|
end
|
data/lib/linear/cli/version.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'semantic_logger'
|
4
|
+
require_relative '../issue'
|
4
5
|
|
5
6
|
module Rubyists
|
6
7
|
# Namespace for Linear
|
@@ -14,12 +15,13 @@ module Rubyists
|
|
14
15
|
class Create
|
15
16
|
include SemanticLogger::Loggable
|
16
17
|
include Rubyists::Linear::CLI::CommonOptions
|
17
|
-
include Rubyists::Linear::CLI::Issue # for #gimme_da_issue and other methods
|
18
|
-
desc 'Create a
|
18
|
+
include Rubyists::Linear::CLI::Issue # for #gimme_da_issue! and other Issue methods
|
19
|
+
desc 'Create a new issue'
|
19
20
|
option :title, type: :string, aliases: ['-t'], desc: 'Issue Title'
|
20
|
-
option :team, type: :string, aliases: ['-T'], desc: 'Team Identifier'
|
21
21
|
option :description, type: :string, aliases: ['-d'], desc: 'Issue Description'
|
22
|
+
option :team, type: :string, aliases: ['-T'], desc: 'Team Identifier'
|
22
23
|
option :labels, type: :array, aliases: ['-l'], desc: 'Labels for the issue (Comma separated list)'
|
24
|
+
option :develop, type: :boolean, aliases: ['-D', '--dev'], desc: 'Start development after creating the issue'
|
23
25
|
|
24
26
|
def call(**options)
|
25
27
|
logger.debug('Creating issue', options:)
|
@@ -27,6 +29,7 @@ module Rubyists
|
|
27
29
|
logger.debug('Issue created', issue:)
|
28
30
|
prompt.yes?('Do you want to take this issue?') && gimme_da_issue!(issue.id, User.me)
|
29
31
|
display issue, options
|
32
|
+
Rubyists::Linear::CLI::Issue::Develop.new.call(issue_id: issue.id, **options) if options[:develop]
|
30
33
|
end
|
31
34
|
end
|
32
35
|
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'semantic_logger'
|
4
|
+
require 'git'
|
5
|
+
require_relative '../issue'
|
6
|
+
|
7
|
+
module Rubyists
|
8
|
+
# Namespace for Linear
|
9
|
+
module Linear
|
10
|
+
M :issue, :user, :label
|
11
|
+
# Namespace for CLI
|
12
|
+
module CLI
|
13
|
+
module Issue
|
14
|
+
Develop = Class.new Dry::CLI::Command
|
15
|
+
# The Develop class is a Dry::CLI::Command to start/update development status of an issue
|
16
|
+
class Develop
|
17
|
+
include SemanticLogger::Loggable
|
18
|
+
include Rubyists::Linear::CLI::CommonOptions
|
19
|
+
include Rubyists::Linear::CLI::Issue # for #gimme_da_issue! and other Issue methods
|
20
|
+
desc 'Start or update development status of an issue'
|
21
|
+
argument :issue_id, required: true, desc: 'The Issue (i.e. ISS-1)'
|
22
|
+
|
23
|
+
def call(issue_id:, **options)
|
24
|
+
logger.debug('Developing issue', options:)
|
25
|
+
issue = gimme_da_issue!(issue_id, Rubyists::Linear::User.me)
|
26
|
+
branch_name = issue.branchName
|
27
|
+
branch = branch_for(branch_name)
|
28
|
+
branch.checkout
|
29
|
+
prompt.ok "Checked out branch #{branch_name}"
|
30
|
+
pull_or_push_new_branch!(branch_name)
|
31
|
+
prompt.ok 'Ready to develop!'
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -13,8 +13,9 @@ module Rubyists
|
|
13
13
|
# Aliases for Issue commands
|
14
14
|
ALIASES = {
|
15
15
|
create: %w[c new add], # aliases for the create command
|
16
|
+
develop: %w[d dev], # aliases for the create command
|
16
17
|
list: %w[l ls], # aliases for the list command
|
17
|
-
show: %w[s view v
|
18
|
+
show: %w[s view v], # aliases for the show command
|
18
19
|
issue: %w[i issues] # aliases for the main issue command itself
|
19
20
|
}.freeze
|
20
21
|
|
@@ -28,10 +29,16 @@ module Rubyists
|
|
28
29
|
end
|
29
30
|
|
30
31
|
def gimme_da_issue!(issue_id, me) # rubocop:disable Naming/MethodParameterName
|
32
|
+
logger.trace('Looking up issue', issue_id:, me:)
|
31
33
|
issue = Rubyists::Linear::Issue.find(issue_id)
|
32
|
-
|
34
|
+
if issue.assignee && issue.assignee[:id] == me.id
|
35
|
+
prompt.say("You are already assigned #{issue_id}")
|
36
|
+
return issue
|
37
|
+
end
|
38
|
+
|
39
|
+
prompt.say("Assigning issue #{issue_id} to ya")
|
33
40
|
updated = issue.assign! me
|
34
|
-
logger.
|
41
|
+
logger.trace 'Issue taken', issue: updated
|
35
42
|
updated
|
36
43
|
end
|
37
44
|
end
|
data/lib/linear/models/issue.rb
CHANGED
data/lib/linear/models/label.rb
CHANGED
@@ -12,14 +12,29 @@ module Rubyists
|
|
12
12
|
class Label
|
13
13
|
include SemanticLogger::Loggable
|
14
14
|
|
15
|
+
PLURAL = :issueLabels
|
15
16
|
Base = fragment('BaseLabel', 'IssueLabel') do
|
16
17
|
id
|
17
18
|
description
|
18
19
|
name
|
20
|
+
isGroup
|
19
21
|
createdAt
|
20
22
|
updatedAt
|
21
23
|
end
|
22
24
|
|
25
|
+
def self.base_fragment # rubocop:disable Metrics/AbcSize
|
26
|
+
define_method(:team) { updated_data[:team] }
|
27
|
+
define_method(:team=) { |val| updated_data[:team] = val }
|
28
|
+
define_method(:parent) { updated_data[:parent] }
|
29
|
+
define_method(:parent=) { |val| updated_data[:parent] = val }
|
30
|
+
|
31
|
+
fragment('LabelWithTeams', 'IssueLabel') do
|
32
|
+
___ Base
|
33
|
+
parent { ___ Base }
|
34
|
+
team { ___ Team::Base }
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
23
38
|
def self.find_all_by_name(names)
|
24
39
|
q = query do
|
25
40
|
issueLabels(filter: { name: { in: names } }) do
|
@@ -46,7 +61,7 @@ module Rubyists
|
|
46
61
|
end
|
47
62
|
|
48
63
|
def full
|
49
|
-
format('%<to_s>-10s %<description>s', description
|
64
|
+
format('%<to_s>-10s %<description>s', description:, to_s:)
|
50
65
|
end
|
51
66
|
|
52
67
|
def display(_options)
|
data/lib/linear/models/team.rb
CHANGED
@@ -13,7 +13,13 @@ module Rubyists
|
|
13
13
|
include SemanticLogger::Loggable
|
14
14
|
|
15
15
|
# TODO: Make this configurable
|
16
|
-
|
16
|
+
BaseFilter = { # rubocop:disable Naming/ConstantName
|
17
|
+
and: [
|
18
|
+
{ name: { notEndsWith: ' Releases' } },
|
19
|
+
{ name: { notEndsWith: '-ios' } },
|
20
|
+
{ name: { notEndsWith: '-android' } }
|
21
|
+
]
|
22
|
+
}.freeze
|
17
23
|
|
18
24
|
Base = fragment('BaseTeam', 'Team') do
|
19
25
|
description
|
@@ -51,26 +57,26 @@ module Rubyists
|
|
51
57
|
team_id = id
|
52
58
|
query do
|
53
59
|
team(id: team_id) do
|
54
|
-
labels do
|
55
|
-
nodes { ___ Label
|
60
|
+
labels(first: 100, filter: BaseFilter) do
|
61
|
+
nodes { ___ Label.base_fragment }
|
56
62
|
end
|
57
63
|
end
|
58
64
|
end
|
59
65
|
end
|
60
66
|
|
61
|
-
def
|
62
|
-
|
63
|
-
|
64
|
-
labels.reject { |label| IgnoredLabels.detect { |i| label.name.match? i } }
|
67
|
+
def label_groups
|
68
|
+
@label_groups ||= []
|
65
69
|
end
|
66
70
|
|
67
|
-
def labels
|
68
|
-
return @labels if @labels
|
71
|
+
def labels # rubocop:disable Metrics/CyclomaticComplexity
|
72
|
+
return @labels if @labels
|
73
|
+
|
74
|
+
@labels = Api.query(label_query).dig(:team, :labels, :nodes)&.map do |label|
|
75
|
+
label_groups << Label.new(label) if label[:isGroup]
|
76
|
+
next if label[:isGroup] || label[:parent]
|
69
77
|
|
70
|
-
all = Api.query(label_query).dig(:team, :labels, :nodes)&.map do |label|
|
71
78
|
Label.new label
|
72
|
-
end
|
73
|
-
@labels = label_filter(all)
|
79
|
+
end&.compact
|
74
80
|
end
|
75
81
|
|
76
82
|
def members
|
data/linear-cli.gemspec
CHANGED
@@ -36,8 +36,10 @@ Gem::Specification.new do |spec|
|
|
36
36
|
spec.add_dependency 'base64', '~> 0.2'
|
37
37
|
spec.add_dependency 'dry-cli', '~> 1.0'
|
38
38
|
spec.add_dependency 'dry-cli-completion', '~> 1.0'
|
39
|
+
spec.add_dependency 'git', '~> 1.5'
|
39
40
|
spec.add_dependency 'gqli', '~> 1.2'
|
40
41
|
spec.add_dependency 'httpx', '~> 1.2'
|
42
|
+
spec.add_dependency 'octokit', '~> 5.0'
|
41
43
|
spec.add_dependency 'semantic_logger', '~> 4.0'
|
42
44
|
spec.add_dependency 'sequel', '~> 5.0'
|
43
45
|
spec.add_dependency 'sqlite3', '~> 1.7'
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: linear-cli
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.5.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Tj (bougyman) Vanderpoel
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-02-
|
11
|
+
date: 2024-02-03 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: base64
|
@@ -52,6 +52,20 @@ dependencies:
|
|
52
52
|
- - "~>"
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: '1.0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: git
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '1.5'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '1.5'
|
55
69
|
- !ruby/object:Gem::Dependency
|
56
70
|
name: gqli
|
57
71
|
requirement: !ruby/object:Gem::Requirement
|
@@ -80,6 +94,20 @@ dependencies:
|
|
80
94
|
- - "~>"
|
81
95
|
- !ruby/object:Gem::Version
|
82
96
|
version: '1.2'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: octokit
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - "~>"
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '5.0'
|
104
|
+
type: :runtime
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - "~>"
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '5.0'
|
83
111
|
- !ruby/object:Gem::Dependency
|
84
112
|
name: semantic_logger
|
85
113
|
requirement: !ruby/object:Gem::Requirement
|
@@ -177,6 +205,7 @@ files:
|
|
177
205
|
- lib/linear/cli/watcher.rb
|
178
206
|
- lib/linear/commands/issue.rb
|
179
207
|
- lib/linear/commands/issue/create.rb
|
208
|
+
- lib/linear/commands/issue/develop.rb
|
180
209
|
- lib/linear/commands/issue/list.rb
|
181
210
|
- lib/linear/commands/issue/take.rb
|
182
211
|
- lib/linear/commands/team.rb
|