linear-cli 0.3.10 → 0.4.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 +4 -4
- data/lib/linear/cli/caller.rb +37 -0
- data/lib/linear/cli/common_options.rb +26 -0
- data/lib/linear/cli/sub_commands.rb +66 -0
- data/lib/linear/cli/version.rb +1 -1
- data/lib/linear/cli/watcher.rb +18 -0
- data/lib/linear/cli.rb +28 -45
- data/lib/linear/commands/issue/create.rb +35 -0
- data/lib/linear/commands/issue/list.rb +1 -1
- data/lib/linear/commands/issue/take.rb +1 -9
- data/lib/linear/commands/issue.rb +28 -11
- data/lib/linear/commands/team/list.rb +0 -4
- data/lib/linear/commands/team.rb +15 -2
- data/lib/linear/commands/whoami.rb +4 -2
- data/lib/linear/models/issue.rb +10 -0
- data/lib/linear/models/label.rb +57 -0
- data/lib/linear/models/team.rb +41 -1
- data/lib/linear/models/user.rb +7 -5
- data/linear-cli.gemspec +1 -0
- metadata +22 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 78d3b6f808a137827fc21be0dd57daab6ef08fe4b54d6c2b428dec93e5801baa
|
4
|
+
data.tar.gz: 96e390ec3116a9cfd0ec844351ec0b9c86148f808ef9471a9bce37e95674c2b6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f9cfd202b19123a1c32b81995335f6947e40e204e50b588e9a059d508c495756de852f4cc3293f93590f14f1c9ad2a6c4978169d4a6e5a9dbd82c3d24f5e3478
|
7
|
+
data.tar.gz: b690c54cd0657929a45fea1bd68fe2621ea8afe4eefcf1af86f09575b04e8c864e9bc2a0a98523820406e6c6319b567aea98bfeba459a397a9de29efee6065ab
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Rubyists
|
4
|
+
module Linear
|
5
|
+
module CLI
|
6
|
+
# This module is prepended to all commands to log their calls
|
7
|
+
module Caller
|
8
|
+
def self.prepended(mod) # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
|
9
|
+
# Global options for all commands
|
10
|
+
mod.instance_eval do
|
11
|
+
option :output, type: :string, default: 'text', values: %w[text json], desc: 'Output format'
|
12
|
+
option :debug, type: :integer, default: 0, desc: 'Debug level'
|
13
|
+
end
|
14
|
+
Caller.class_eval do
|
15
|
+
# Wraps the :call method so the debug option is honored, and we can trace the call
|
16
|
+
# as well as handle any exceptions that are raised
|
17
|
+
define_method :call do |**method_args| # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
|
18
|
+
debug = method_args[:debug].to_i
|
19
|
+
Rubyists::Linear.verbosity = debug
|
20
|
+
logger.trace "Calling #{self.class} with #{method_args}"
|
21
|
+
super(**method_args)
|
22
|
+
rescue SmellsBad => e
|
23
|
+
logger.error e.message
|
24
|
+
exit 1
|
25
|
+
rescue NotFoundError => e
|
26
|
+
logger.error e.message
|
27
|
+
rescue StandardError => e
|
28
|
+
logger.error e.message
|
29
|
+
logger.error e.backtrace.join("\n") if Rubyists::Linear.verbosity.positive?
|
30
|
+
exit 5
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Rubyists
|
4
|
+
module Linear
|
5
|
+
module CLI
|
6
|
+
# The CommonOptions module contains common options for all commands
|
7
|
+
module CommonOptions
|
8
|
+
def self.included(mod)
|
9
|
+
mod.instance_eval do
|
10
|
+
extend Rubyists::Linear::CLI::Watcher
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def display(subject, options)
|
15
|
+
return puts(JSON.pretty_generate(subject)) if options[:output] == 'json'
|
16
|
+
return subject.each { |s| s.display(options) } if subject.respond_to?(:each)
|
17
|
+
unless subject.respond_to?(:display)
|
18
|
+
raise SmellsBad, "Cannot display #{subject}, there is no #display method and it is not a collection"
|
19
|
+
end
|
20
|
+
|
21
|
+
subject.display(options)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Rubyists
|
4
|
+
module Linear
|
5
|
+
module CLI
|
6
|
+
# The SubCommands module should be included in all commands with subcommands
|
7
|
+
module SubCommands
|
8
|
+
def self.included(mod)
|
9
|
+
mod.instance_eval do
|
10
|
+
def const_added(const)
|
11
|
+
return unless const == :ALIASES
|
12
|
+
|
13
|
+
Rubyists::Linear::CLI.load_and_register! self
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def ask_for_team # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
|
19
|
+
teams = Rubyists::Linear::Team.mine
|
20
|
+
if teams.size == 1
|
21
|
+
logger.info('Only one team found, using it', team: teams.first.name)
|
22
|
+
teams.first
|
23
|
+
elsif teams.empty?
|
24
|
+
logger.error('No teams found for you. Please join a team or pass an existing team name.')
|
25
|
+
raise SmellsBad, 'No team given and none found for you'
|
26
|
+
else
|
27
|
+
prompt.on(:keypress) do |event|
|
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
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def prompt
|
37
|
+
@prompt ||= CLI.prompt
|
38
|
+
end
|
39
|
+
|
40
|
+
def team_for(key = nil)
|
41
|
+
return Rubyists::Linear::Team.find(key) if key
|
42
|
+
|
43
|
+
ask_for_team
|
44
|
+
end
|
45
|
+
|
46
|
+
def description_for(description = nil)
|
47
|
+
return description if description
|
48
|
+
|
49
|
+
prompt.multiline('Description:').join(' ')
|
50
|
+
end
|
51
|
+
|
52
|
+
def title_for(title = nil)
|
53
|
+
return title if title
|
54
|
+
|
55
|
+
prompt.ask('Title:')
|
56
|
+
end
|
57
|
+
|
58
|
+
def labels_for(team, labels = nil)
|
59
|
+
return Rubyists::Linear::Label.find_all_by_name(labels.map(&:strip)) if labels
|
60
|
+
|
61
|
+
prompt.multi_select('Labels:', team.labels.to_h { |t| [t.name, t] })
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
data/lib/linear/cli/version.rb
CHANGED
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Rubyists
|
4
|
+
module Linear
|
5
|
+
module CLI
|
6
|
+
# Watch for the call method to be added to a command
|
7
|
+
module Watcher
|
8
|
+
def self.extended(_mod)
|
9
|
+
define_method :method_added do |method_name|
|
10
|
+
return unless method_name == :call
|
11
|
+
|
12
|
+
prepend Rubyists::Linear::CLI::Caller
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
data/lib/linear/cli.rb
CHANGED
@@ -3,72 +3,55 @@
|
|
3
3
|
require 'dry/cli'
|
4
4
|
require 'dry/cli/completion/command'
|
5
5
|
require_relative '../linear'
|
6
|
+
require 'semantic_logger'
|
6
7
|
require 'tty-markdown'
|
8
|
+
require 'tty-prompt'
|
7
9
|
|
8
10
|
# The Rubyists module is the top-level namespace for all Rubyists projects
|
9
11
|
module Rubyists
|
10
12
|
module Linear
|
11
13
|
# The CLI module is a Dry::CLI::Registry that contains all the commands
|
12
14
|
module CLI
|
15
|
+
include SemanticLogger::Loggable
|
13
16
|
extend Dry::CLI::Registry
|
14
17
|
|
15
|
-
|
16
|
-
|
17
|
-
def self.extended(_mod)
|
18
|
-
define_method :method_added do |method_name|
|
19
|
-
return unless method_name == :call
|
20
|
-
|
21
|
-
prepend Rubyists::Linear::CLI::Caller
|
22
|
-
end
|
23
|
-
end
|
18
|
+
def self.prompt
|
19
|
+
@prompt ||= TTY::Prompt.new
|
24
20
|
end
|
25
21
|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
22
|
+
def self.register_sub!(command, sub_file, klass)
|
23
|
+
# The filename is expected to define a class of the same name, but capitalized
|
24
|
+
name = sub_file.basename('.rb').to_s
|
25
|
+
subklass = klass.const_get(name.capitalize)
|
26
|
+
if (aliases = klass::ALIASES[name.to_sym])
|
27
|
+
command.register name, subklass, aliases: Array(aliases)
|
28
|
+
else
|
29
|
+
command.register name, subklass
|
34
30
|
end
|
31
|
+
end
|
35
32
|
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
raise SmellsBad, "Cannot display #{subject}, there is no #display method and it is not a collection"
|
41
|
-
end
|
42
|
-
|
43
|
-
subject.display(options)
|
33
|
+
def self.register_subcommands!(command, name, klass)
|
34
|
+
Pathname.new(__FILE__).dirname.join("commands/#{name}").glob('*.rb').each do |file|
|
35
|
+
require file.expand_path
|
36
|
+
register_sub! command, file, klass
|
44
37
|
end
|
45
38
|
end
|
46
39
|
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
Rubyists::Linear.verbosity = debug
|
54
|
-
logger.trace "Calling #{self.class} with #{method_args}"
|
55
|
-
super(**method_args)
|
56
|
-
rescue SmellsBad => e
|
57
|
-
logger.error e.message
|
58
|
-
exit 1
|
59
|
-
rescue NotFoundError => e
|
60
|
-
logger.error e.message
|
61
|
-
rescue StandardError => e
|
62
|
-
logger.error e.message
|
63
|
-
logger.error e.backtrace.join("\n") if Rubyists::Linear.verbosity.positive?
|
64
|
-
exit 5
|
65
|
-
end
|
66
|
-
end
|
40
|
+
def self.load_and_register!(command)
|
41
|
+
logger.debug "Registering #{command}"
|
42
|
+
name = command.name.split('::').last.downcase
|
43
|
+
command_aliases = command::ALIASES[name.to_sym] || []
|
44
|
+
register name, aliases: Array(command_aliases) do |cmd|
|
45
|
+
register_subcommands! cmd, name, command
|
67
46
|
end
|
68
47
|
end
|
69
48
|
end
|
70
49
|
end
|
71
50
|
|
51
|
+
Pathname.new(__FILE__).dirname.join('cli').glob('*.rb').each do |file|
|
52
|
+
require file.expand_path
|
53
|
+
end
|
54
|
+
|
72
55
|
# Load all our commands
|
73
56
|
Pathname.new(__FILE__).dirname.join('commands').glob('*.rb').each do |file|
|
74
57
|
require file.expand_path
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'semantic_logger'
|
4
|
+
|
5
|
+
module Rubyists
|
6
|
+
# Namespace for Linear
|
7
|
+
module Linear
|
8
|
+
M :issue, :user, :label
|
9
|
+
# Namespace for CLI
|
10
|
+
module CLI
|
11
|
+
module Issue
|
12
|
+
Create = Class.new Dry::CLI::Command
|
13
|
+
# The Create class is a Dry::CLI::Command to create a new issue
|
14
|
+
class Create
|
15
|
+
include SemanticLogger::Loggable
|
16
|
+
include Rubyists::Linear::CLI::CommonOptions
|
17
|
+
include Rubyists::Linear::CLI::Issue # for #gimme_da_issue and other methods
|
18
|
+
desc 'Create a new issue'
|
19
|
+
option :title, type: :string, aliases: ['-t'], desc: 'Issue Title'
|
20
|
+
option :team, type: :string, aliases: ['-T'], desc: 'Team Identifier'
|
21
|
+
option :description, type: :string, aliases: ['-d'], desc: 'Issue Description'
|
22
|
+
option :labels, type: :array, aliases: ['-l'], desc: 'Labels for the issue (Comma separated list)'
|
23
|
+
|
24
|
+
def call(**options)
|
25
|
+
logger.debug('Creating issue', options:)
|
26
|
+
issue = make_da_issue!(**options)
|
27
|
+
logger.debug('Issue created', issue:)
|
28
|
+
prompt.yes?('Do you want to take this issue?') && gimme_da_issue!(issue.id, User.me)
|
29
|
+
display issue, options
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -38,7 +38,7 @@ module Rubyists
|
|
38
38
|
|
39
39
|
def issues_for(options)
|
40
40
|
logger.debug('Fetching issues', options:)
|
41
|
-
return options[:ids].map { |id| Rubyists::Linear::Issue.find(id) } if options[:ids]
|
41
|
+
return options[:ids].map { |id| Rubyists::Linear::Issue.find(id.upcase) } if options[:ids]
|
42
42
|
return Rubyists::Linear::Issue.all(filter: { assignee: { null: true } }) if options[:unassigned]
|
43
43
|
return Rubyists::Linear::User.me.issues if options[:mine]
|
44
44
|
|
@@ -17,18 +17,10 @@ module Rubyists
|
|
17
17
|
desc 'Assign one or more issues to yourself'
|
18
18
|
argument :issue_ids, type: :array, required: true, desc: 'Issue Identifiers'
|
19
19
|
|
20
|
-
def gimme_da_issue(issue_id, me) # rubocop:disable Naming/MethodParameterName
|
21
|
-
issue = Rubyists::Linear::Issue.find(issue_id)
|
22
|
-
logger.debug 'Taking issue', issue:, assignee: me
|
23
|
-
updated = issue.assign! me
|
24
|
-
logger.debug 'Issue taken', issue: updated
|
25
|
-
updated
|
26
|
-
end
|
27
|
-
|
28
20
|
def call(issue_ids:, **options)
|
29
21
|
me = Rubyists::Linear::User.me
|
30
22
|
updates = issue_ids.map do |issue_id|
|
31
|
-
gimme_da_issue issue_id, me
|
23
|
+
gimme_da_issue! issue_id, me # gimme_da_issue! is defined in Rubyists::Linear::CLI::Issue
|
32
24
|
rescue NotFoundError => e
|
33
25
|
logger.warn e.message
|
34
26
|
next
|
@@ -1,21 +1,38 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require_relative '../cli/sub_commands'
|
4
|
+
|
3
5
|
module Rubyists
|
4
6
|
module Linear
|
5
7
|
# The Cli module is defined in cli.rb and is the top-level namespace for all CLI commands
|
6
8
|
module CLI
|
7
|
-
#
|
8
|
-
#
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
# The Issue module is the namespace for all issue-related commands, and
|
10
|
+
# should be included in any command that deals with issues
|
11
|
+
module Issue
|
12
|
+
include CLI::SubCommands
|
13
|
+
# Aliases for Issue commands
|
14
|
+
ALIASES = {
|
15
|
+
create: %w[c new add], # aliases for the create command
|
16
|
+
list: %w[l ls], # aliases for the list command
|
17
|
+
show: %w[s view v display d], # aliases for the show command
|
18
|
+
issue: %w[i issues] # aliases for the main issue command itself
|
19
|
+
}.freeze
|
20
|
+
|
21
|
+
def make_da_issue!(**options)
|
22
|
+
# These *_for methods are defined in Rubyists::Linear::CLI::SubCommands
|
23
|
+
title = title_for options[:title]
|
24
|
+
description = description_for options[:description]
|
25
|
+
team = team_for options[:team]
|
26
|
+
labels = labels_for team, options[:labels]
|
27
|
+
Rubyists::Linear::Issue.create(title:, description:, team:, labels:)
|
28
|
+
end
|
12
29
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
30
|
+
def gimme_da_issue!(issue_id, me) # rubocop:disable Naming/MethodParameterName
|
31
|
+
issue = Rubyists::Linear::Issue.find(issue_id)
|
32
|
+
logger.debug 'Taking issue', issue:, assignee: me
|
33
|
+
updated = issue.assign! me
|
34
|
+
logger.debug 'Issue taken', issue: updated
|
35
|
+
updated
|
19
36
|
end
|
20
37
|
end
|
21
38
|
end
|
data/lib/linear/commands/team.rb
CHANGED
@@ -1,5 +1,18 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
|
3
|
+
module Rubyists
|
4
|
+
module Linear
|
5
|
+
# The Cli module is defined in cli.rb and is the top-level namespace for all CLI commands
|
6
|
+
module CLI
|
7
|
+
# The Team module is the namespace for all team-related commands
|
8
|
+
module Team
|
9
|
+
include CLI::SubCommands
|
10
|
+
# Aliases for Team commands.
|
11
|
+
ALIASES = {
|
12
|
+
list: %w[ls l], # aliases for the list command
|
13
|
+
team: %w[t teams] # aliases for the main team command itself
|
14
|
+
}.freeze
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
5
18
|
end
|
@@ -14,7 +14,9 @@ module Rubyists
|
|
14
14
|
include SemanticLogger::Loggable
|
15
15
|
include Rubyists::Linear::CLI::CommonOptions
|
16
16
|
|
17
|
-
|
17
|
+
desc 'Get your own user info'
|
18
|
+
|
19
|
+
option :teams, aliases: ['-t'], type: :boolean, default: false, desc: 'Show teams'
|
18
20
|
|
19
21
|
def call(**options)
|
20
22
|
logger.debug 'Getting user info'
|
@@ -23,7 +25,7 @@ module Rubyists
|
|
23
25
|
|
24
26
|
prepend Rubyists::Linear::CLI::Caller
|
25
27
|
end
|
26
|
-
register 'whoami', WhoAmI
|
28
|
+
register 'whoami', WhoAmI, aliases: %w[me w who whodat]
|
27
29
|
end
|
28
30
|
end
|
29
31
|
end
|
data/lib/linear/models/issue.rb
CHANGED
@@ -34,6 +34,16 @@ module Rubyists
|
|
34
34
|
|
35
35
|
new(data[:issue])
|
36
36
|
end
|
37
|
+
|
38
|
+
def create(title:, description:, team:, labels: [])
|
39
|
+
team_id = team.id
|
40
|
+
label_ids = labels.map(&:id)
|
41
|
+
input = { title:, description:, teamId: team_id }
|
42
|
+
input.merge!(labelIds: label_ids) unless label_ids.empty?
|
43
|
+
m = mutation { issueCreate(input:) { issue { ___ Base } } }
|
44
|
+
data = Api.query(m)
|
45
|
+
new(data[:issueCreate][:issue])
|
46
|
+
end
|
37
47
|
end
|
38
48
|
|
39
49
|
def assign!(user)
|
@@ -0,0 +1,57 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'gqli'
|
4
|
+
|
5
|
+
module Rubyists
|
6
|
+
# Namespace for Linear
|
7
|
+
module Linear
|
8
|
+
L :api, :fragments
|
9
|
+
M :base_model, :issue, :user, :team
|
10
|
+
Label = Class.new(BaseModel)
|
11
|
+
# The Label class represents a Linear issue label.
|
12
|
+
class Label
|
13
|
+
include SemanticLogger::Loggable
|
14
|
+
|
15
|
+
Base = fragment('BaseLabel', 'IssueLabel') do
|
16
|
+
id
|
17
|
+
description
|
18
|
+
name
|
19
|
+
createdAt
|
20
|
+
updatedAt
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.find_all_by_name(names)
|
24
|
+
q = query do
|
25
|
+
issueLabels(filter: { name: { in: names } }) do
|
26
|
+
edges { node { ___ Base } }
|
27
|
+
end
|
28
|
+
end
|
29
|
+
data = Api.query(q)
|
30
|
+
edges = data.dig(:issueLabels, :edges)
|
31
|
+
raise NotFoundError, "No labels found: #{names}" unless edges
|
32
|
+
|
33
|
+
edges.map { |edge| new edge[:node] }
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.find_by_name(name)
|
37
|
+
them = find_all_by_name([name])
|
38
|
+
if them.size > 1
|
39
|
+
logger.warn('Found multiple matches for label name, using the first one returned', labels: them)
|
40
|
+
end
|
41
|
+
them.first
|
42
|
+
end
|
43
|
+
|
44
|
+
def to_s
|
45
|
+
format('%<name>s', name:)
|
46
|
+
end
|
47
|
+
|
48
|
+
def full
|
49
|
+
format('%<to_s>-10s %<description>s', description: , to_s:)
|
50
|
+
end
|
51
|
+
|
52
|
+
def display(_options)
|
53
|
+
printf "%s\n", full
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
data/lib/linear/models/team.rb
CHANGED
@@ -12,6 +12,9 @@ module Rubyists
|
|
12
12
|
class Team
|
13
13
|
include SemanticLogger::Loggable
|
14
14
|
|
15
|
+
# TODO: Make this configurable
|
16
|
+
IgnoredLabels = [/-ios$/, /-android$/].freeze # rubocop:disable Naming/ConstantName
|
17
|
+
|
15
18
|
Base = fragment('BaseTeam', 'Team') do
|
16
19
|
description
|
17
20
|
id
|
@@ -21,6 +24,17 @@ module Rubyists
|
|
21
24
|
updatedAt
|
22
25
|
end
|
23
26
|
|
27
|
+
def self.find(key)
|
28
|
+
q = query do
|
29
|
+
team(id: key) { ___ Base }
|
30
|
+
end
|
31
|
+
data = Api.query(q)
|
32
|
+
hash = data[:team]
|
33
|
+
raise NotFoundError, "Team not found: #{key}" unless hash
|
34
|
+
|
35
|
+
new hash
|
36
|
+
end
|
37
|
+
|
24
38
|
def self.mine
|
25
39
|
User.me.teams
|
26
40
|
end
|
@@ -33,8 +47,34 @@ module Rubyists
|
|
33
47
|
format('%<key>-6s %<to_s>s', key:, to_s:)
|
34
48
|
end
|
35
49
|
|
50
|
+
def label_query
|
51
|
+
team_id = id
|
52
|
+
query do
|
53
|
+
team(id: team_id) do
|
54
|
+
labels do
|
55
|
+
nodes { ___ Label::Base }
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def label_filter(labels = nil)
|
62
|
+
return [] unless labels
|
63
|
+
|
64
|
+
labels.reject { |label| IgnoredLabels.detect { |i| label.name.match? i } }
|
65
|
+
end
|
66
|
+
|
67
|
+
def labels
|
68
|
+
return @labels if @labels && !@labels.empty?
|
69
|
+
|
70
|
+
all = Api.query(label_query).dig(:team, :labels, :nodes)&.map do |label|
|
71
|
+
Label.new label
|
72
|
+
end
|
73
|
+
@labels = label_filter(all)
|
74
|
+
end
|
75
|
+
|
36
76
|
def members
|
37
|
-
return @members
|
77
|
+
return @members if @members && !@members.empty?
|
38
78
|
|
39
79
|
q = query do
|
40
80
|
team(id:) do
|
data/lib/linear/models/user.rb
CHANGED
@@ -18,15 +18,17 @@ module Rubyists
|
|
18
18
|
email
|
19
19
|
end
|
20
20
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
21
|
+
def self.with_teams
|
22
|
+
@with_teams = fragment('UserWithTeams', 'User') do
|
23
|
+
___ Base
|
24
|
+
teams do
|
25
|
+
nodes { ___ Team::Base }
|
26
|
+
end
|
25
27
|
end
|
26
28
|
end
|
27
29
|
|
28
30
|
def self.me(teams: false)
|
29
|
-
fragment = teams ?
|
31
|
+
fragment = teams ? with_teams : Base
|
30
32
|
q = query do
|
31
33
|
viewer do
|
32
34
|
___ fragment
|
data/linear-cli.gemspec
CHANGED
@@ -42,6 +42,7 @@ Gem::Specification.new do |spec|
|
|
42
42
|
spec.add_dependency 'sequel', '~> 5.0'
|
43
43
|
spec.add_dependency 'sqlite3', '~> 1.7'
|
44
44
|
spec.add_dependency 'tty-markdown', '~> 0.7'
|
45
|
+
spec.add_dependency 'tty-prompt', '~> 0.23'
|
45
46
|
|
46
47
|
# For more information and examples about making a new gem, check out our
|
47
48
|
# guide at: https://bundler.io/guides/creating_gem.html
|
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.4.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-02 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: base64
|
@@ -136,6 +136,20 @@ dependencies:
|
|
136
136
|
- - "~>"
|
137
137
|
- !ruby/object:Gem::Version
|
138
138
|
version: '0.7'
|
139
|
+
- !ruby/object:Gem::Dependency
|
140
|
+
name: tty-prompt
|
141
|
+
requirement: !ruby/object:Gem::Requirement
|
142
|
+
requirements:
|
143
|
+
- - "~>"
|
144
|
+
- !ruby/object:Gem::Version
|
145
|
+
version: '0.23'
|
146
|
+
type: :runtime
|
147
|
+
prerelease: false
|
148
|
+
version_requirements: !ruby/object:Gem::Requirement
|
149
|
+
requirements:
|
150
|
+
- - "~>"
|
151
|
+
- !ruby/object:Gem::Version
|
152
|
+
version: '0.23'
|
139
153
|
description: A CLI for interacting with Linear.app. Loosely based on the GitHub CLI
|
140
154
|
email:
|
141
155
|
- tj@rubyists.com
|
@@ -156,8 +170,13 @@ files:
|
|
156
170
|
- lib/linear.rb
|
157
171
|
- lib/linear/api.rb
|
158
172
|
- lib/linear/cli.rb
|
173
|
+
- lib/linear/cli/caller.rb
|
174
|
+
- lib/linear/cli/common_options.rb
|
175
|
+
- lib/linear/cli/sub_commands.rb
|
159
176
|
- lib/linear/cli/version.rb
|
177
|
+
- lib/linear/cli/watcher.rb
|
160
178
|
- lib/linear/commands/issue.rb
|
179
|
+
- lib/linear/commands/issue/create.rb
|
161
180
|
- lib/linear/commands/issue/list.rb
|
162
181
|
- lib/linear/commands/issue/take.rb
|
163
182
|
- lib/linear/commands/team.rb
|
@@ -167,6 +186,7 @@ files:
|
|
167
186
|
- lib/linear/fragments.rb
|
168
187
|
- lib/linear/models/base_model.rb
|
169
188
|
- lib/linear/models/issue.rb
|
189
|
+
- lib/linear/models/label.rb
|
170
190
|
- lib/linear/models/team.rb
|
171
191
|
- lib/linear/models/user.rb
|
172
192
|
- lib/linear/version.rb
|