linear-cli 0.5.3 → 0.6.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +20 -1
- data/Readme.adoc +37 -2
- data/changelog/0.5.3/added_support_for_multiline_descriptions_without_failing.yml +4 -0
- data/changelog/0.5.4/tag.yml +1 -0
- data/changelog/0.5.5/added_lclose_alias_and_issue_update_subcommand.yml +4 -0
- data/changelog/0.5.5/tag.yml +1 -0
- data/changelog/0.6.0/tag.yml +1 -0
- data/exe/lclose +4 -0
- data/exe/lclose.sh +9 -0
- data/lib/linear/api.rb +8 -1
- data/lib/linear/cli/sub_commands.rb +15 -1
- data/lib/linear/cli/version.rb +1 -1
- data/lib/linear/commands/issue/update.rb +37 -0
- data/lib/linear/commands/issue.rb +33 -6
- data/lib/linear/models/base_model.rb +20 -0
- data/lib/linear/models/issue.rb +82 -21
- data/lib/linear/models/label.rb +2 -2
- data/lib/linear/models/team.rb +21 -2
- data/lib/linear/models/workflow_state.rb +33 -0
- metadata +13 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: bc273e12a3dd9e864894c2484bd08a203531da62873505b33d5706f13349e2c6
|
4
|
+
data.tar.gz: ffb38d1c66023c229bc98812fa55e4a58e29342adb46e96f63cf0054560a3964
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2323c788c7095f385320006a7be3b167b7dc9fc731c96a13ea5af48dcdec0dc013f8e6f55c71e9631022ae744a5faecbc34c8bda668c172cfb98d935c1af40a0
|
7
|
+
data.tar.gz: c8422224df18d500f92d1d8e0d90cd27f41cc8d0c148943b6f53f6e6269d67aae676f2fd006e9a4210199e5aae572e39e9b3bf46ddce2206f2880d88c3a78fac
|
data/CHANGELOG.md
CHANGED
@@ -2,8 +2,27 @@
|
|
2
2
|
|
3
3
|
## [Unreleased]
|
4
4
|
|
5
|
+
## [0.6.0] - 2024-02-04
|
6
|
+
|
7
|
+
## [0.5.5] - 2024-02-04
|
8
|
+
### Added
|
9
|
+
- Added lclose alias and 'issue update' subcommand (@bougyman)
|
10
|
+
|
11
|
+
## [0.5.4] - 2024-02-04
|
12
|
+
|
13
|
+
## [0.5.3] - 2024-02-03
|
14
|
+
### Added
|
15
|
+
- Added support for multiline descriptions without failing (@bougyman)
|
16
|
+
|
17
|
+
### Changed
|
18
|
+
- Changed default branch to use upstream default branch name (@bougyman)
|
19
|
+
|
5
20
|
## [0.5.2] - 2024-02-03
|
6
21
|
### Added
|
7
22
|
- Added new changelog management system (changelog-rb) (@bougyman)
|
8
23
|
|
9
|
-
[Unreleased]: https://github.com/rubyists/linear-cli/compare/
|
24
|
+
[Unreleased]: https://github.com/rubyists/linear-cli/compare/0.6.0...HEAD
|
25
|
+
[0.6.0]: https://github.com/rubyists/linear-cli/compare/v0.5.5...0.6.0
|
26
|
+
[0.5.5]: https://github.com/rubyists/linear-cli/compare/v0.5.4...v0.5.5
|
27
|
+
[0.5.4]: https://github.com/rubyists/linear-cli/compare/v0.5.3...v0.5.4
|
28
|
+
[0.5.3]: https://github.com/rubyists/linear-cli/compare/v0.5.2...v0.5.3
|
data/Readme.adoc
CHANGED
@@ -82,6 +82,7 @@ $ lc w --teams
|
|
82
82
|
----
|
83
83
|
$ lcls
|
84
84
|
$ lcls --full
|
85
|
+
$ lcls -f CRY-1
|
85
86
|
----
|
86
87
|
|
87
88
|
==== Assign one or more issues to yourself (take em!)
|
@@ -100,7 +101,41 @@ $ lc issue take CRY-456 CRY-789
|
|
100
101
|
|
101
102
|
[source,sh]
|
102
103
|
----
|
103
|
-
$ lc i c --title "My new issue" --description "This is a new issue"
|
104
|
+
$ lc i c --title "My new issue" --description "This is a new issue" --labels Bug,Feature --team CRY
|
105
|
+
$ lc i c -t "My new issue" -T CRY -l Improvment,Feature
|
106
|
+
----
|
107
|
+
|
108
|
+
NOTE: If you don't provide a title, team, labels or description, you will be prompted to enter them.
|
109
|
+
|
110
|
+
==== Develop an issue
|
111
|
+
|
112
|
+
This will switch to the branch for the issue, creating the branch if it doesn't exist.
|
113
|
+
|
114
|
+
'dev' is a shortcut for the 'develop' subcommand of the issue command
|
115
|
+
|
116
|
+
[source,sh]
|
117
|
+
----
|
118
|
+
$ lc i dev CRY-1234
|
119
|
+
----
|
120
|
+
|
121
|
+
TIP: You may pass the --dev option to the create subcommand to immediately develop the creted issue.
|
122
|
+
|
123
|
+
==== Update an issue
|
124
|
+
|
125
|
+
All of the update options can work on multiple issues, so long as it's not more than 50
|
126
|
+
at a time. You can also use the 'u' alias for 'update', and as always, the 'i' alias for 'issue'.
|
127
|
+
|
128
|
+
===== Add a comment to one or more issues
|
129
|
+
|
130
|
+
[source,sh]
|
131
|
+
----
|
132
|
+
$ lc issue update --comment "Here is a comment" CRY-1234
|
133
|
+
----
|
134
|
+
|
135
|
+
===== Close one or many issues
|
136
|
+
|
137
|
+
[source,sh]
|
138
|
+
----
|
139
|
+
$ lc i u --close --reason "These were closable" CRY-1234 CRY-2
|
104
140
|
----
|
105
141
|
|
106
|
-
NOTE: If you don't provide a title or description, you will be prompted to enter them.
|
@@ -0,0 +1 @@
|
|
1
|
+
date: 2024-02-04
|
@@ -0,0 +1 @@
|
|
1
|
+
date: 2024-02-04
|
@@ -0,0 +1 @@
|
|
1
|
+
date: 2024-02-04
|
data/exe/lclose
ADDED
data/exe/lclose.sh
ADDED
@@ -0,0 +1,9 @@
|
|
1
|
+
#!/usr/bin/env bash
|
2
|
+
if [[ "$*" =~ "--help" ]]
|
3
|
+
then
|
4
|
+
printf "This wrapper adds the --close option to the 'issue update' command.\n" >&2
|
5
|
+
printf "It is used to close one or many issues. The issues are specified by their ID/slugs.\n" >&2
|
6
|
+
printf "For closing multiple issues, you really want to pass --reason so you do not get prompted for each issue.\n\n" >&2
|
7
|
+
exec lc issue update --help
|
8
|
+
fi
|
9
|
+
exec lc issue update --close "$@"
|
data/lib/linear/api.rb
CHANGED
@@ -41,7 +41,13 @@ module Rubyists
|
|
41
41
|
end
|
42
42
|
|
43
43
|
def query(query)
|
44
|
-
call format('{ "query":
|
44
|
+
call format('{ "query": %s }', query.to_s.to_json)
|
45
|
+
rescue StandardError => e
|
46
|
+
logger.error('Error in query', query:, error: e)
|
47
|
+
raise e unless Rubyists::Linear.verbosity > 2
|
48
|
+
|
49
|
+
require 'pry'
|
50
|
+
binding.pry # rubocop:disable Lint/Debugger
|
45
51
|
end
|
46
52
|
|
47
53
|
def api_key
|
@@ -51,6 +57,7 @@ module Rubyists
|
|
51
57
|
@api_key = ENV.fetch('LINEAR_API_KEY')
|
52
58
|
end
|
53
59
|
end
|
60
|
+
# Acts as a singleton for a GraphApi instance
|
54
61
|
Api = Rubyists::Linear::GraphApi.new
|
55
62
|
end
|
56
63
|
end
|
@@ -47,10 +47,24 @@ module Rubyists
|
|
47
47
|
ask_for_team
|
48
48
|
end
|
49
49
|
|
50
|
+
def reason_for(reason = nil, four: nil)
|
51
|
+
return reason if reason
|
52
|
+
|
53
|
+
question = four ? "Reason for #{four}:" : 'Reason:'
|
54
|
+
prompt.ask(question)
|
55
|
+
end
|
56
|
+
|
57
|
+
def completed_state_for(thingy)
|
58
|
+
states = thingy.completed_states
|
59
|
+
return states.first if states.size == 1
|
60
|
+
|
61
|
+
prompt.select('Choose a completed state', states.to_h { |s| [s.name, s.id] })
|
62
|
+
end
|
63
|
+
|
50
64
|
def description_for(description = nil)
|
51
65
|
return description if description
|
52
66
|
|
53
|
-
prompt.multiline('Description:').join('
|
67
|
+
prompt.multiline('Description:').map(&:chomp).join('\\n')
|
54
68
|
end
|
55
69
|
|
56
70
|
def title_for(title = nil)
|
data/lib/linear/cli/version.rb
CHANGED
@@ -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
|
+
Update = Class.new Dry::CLI::Command
|
15
|
+
# The Update class is a Dry::CLI::Command to update an issue
|
16
|
+
class Update
|
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 'Update an issue'
|
21
|
+
argument :issue_ids, type: :array, required: true, desc: 'Issue IDs (i.e. ISS-1)'
|
22
|
+
option :comment, type: :string, aliases: ['--message'], desc: 'Comment to add to the issue'
|
23
|
+
option :pr, type: :boolean, aliases: ['--pull-request'], default: false, desc: 'Create a pull request'
|
24
|
+
option :close, type: :boolean, default: false, desc: 'Close the issue'
|
25
|
+
option :reason, type: :string, aliases: ['--close-reason'], desc: 'Reason for closing the issue'
|
26
|
+
|
27
|
+
def call(issue_ids:, **options)
|
28
|
+
logger.debug('Updating issues', issue_ids:, options:)
|
29
|
+
Rubyists::Linear::Issue.find_all(issue_ids).each do |issue|
|
30
|
+
update_issue(issue, **options)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -12,13 +12,40 @@ module Rubyists
|
|
12
12
|
include CLI::SubCommands
|
13
13
|
# Aliases for Issue commands
|
14
14
|
ALIASES = {
|
15
|
-
create: %w[c new add],
|
16
|
-
develop: %w[d dev],
|
17
|
-
list: %w[l ls],
|
18
|
-
|
19
|
-
issue: %w[i issues]
|
15
|
+
create: %w[c new add], # aliases for the create command
|
16
|
+
develop: %w[d dev], # aliases for the develop command
|
17
|
+
list: %w[l ls], # aliases for the list command
|
18
|
+
update: %w[u], # aliases for the close command
|
19
|
+
issue: %w[i issues] # aliases for the main issue command itself
|
20
20
|
}.freeze
|
21
21
|
|
22
|
+
def issue_comment(issue, comment)
|
23
|
+
issue.add_comment(comment)
|
24
|
+
prompt.ok("Comment added to #{issue.identifier}")
|
25
|
+
end
|
26
|
+
|
27
|
+
def close_issue(issue, **options)
|
28
|
+
reason = reason_for(options[:reason], four: "closing #{issue.identifier} - #{issue.title}")
|
29
|
+
issue_comment(issue, reason)
|
30
|
+
close_state = completed_state_for(issue)
|
31
|
+
issue.close!(state: close_state, trash: options[:trash])
|
32
|
+
prompt.ok("#{issue.identifier} was closed")
|
33
|
+
end
|
34
|
+
|
35
|
+
def issue_pr(issue)
|
36
|
+
issue.create_pr!
|
37
|
+
prompt.ok("Pull request created for #{issue.identifier}")
|
38
|
+
end
|
39
|
+
|
40
|
+
def update_issue(issue, **options)
|
41
|
+
issue_comment(issue, options[:comment]) if options[:comment]
|
42
|
+
return close_issue(issue, **options) if options[:close]
|
43
|
+
return issue_pr(issue) if options[:pr]
|
44
|
+
|
45
|
+
prompt.warn('No action taken, no options specified')
|
46
|
+
prompt.ok('Issue was not updated')
|
47
|
+
end
|
48
|
+
|
22
49
|
def make_da_issue!(**options)
|
23
50
|
# These *_for methods are defined in Rubyists::Linear::CLI::SubCommands
|
24
51
|
title = title_for options[:title]
|
@@ -28,7 +55,7 @@ module Rubyists
|
|
28
55
|
Rubyists::Linear::Issue.create(title:, description:, team:, labels:)
|
29
56
|
end
|
30
57
|
|
31
|
-
def gimme_da_issue!(issue_id, me) # rubocop:disable Naming/MethodParameterName
|
58
|
+
def gimme_da_issue!(issue_id, me: Rubyists::Linear::User.me) # rubocop:disable Naming/MethodParameterName
|
32
59
|
logger.trace('Looking up issue', issue_id:, me:)
|
33
60
|
issue = Rubyists::Linear::Issue.find(issue_id)
|
34
61
|
if issue.assignee && issue.assignee[:id] == me.id
|
@@ -5,7 +5,9 @@ require 'semantic_logger'
|
|
5
5
|
require 'sequel/extensions/inflector'
|
6
6
|
|
7
7
|
module Rubyists
|
8
|
+
# Namespace for Linear
|
8
9
|
module Linear
|
10
|
+
L :api, :fragments
|
9
11
|
# Module which provides a base model for Linear models.
|
10
12
|
class BaseModel
|
11
13
|
extend GQLi::DSL
|
@@ -32,6 +34,20 @@ module Rubyists
|
|
32
34
|
|
33
35
|
# Class methods for Linear models.
|
34
36
|
class << self
|
37
|
+
def has_one(relation, klass) # rubocop:disable Naming/PredicateName
|
38
|
+
define_method relation do
|
39
|
+
return instance_variable_get("@#{relation}") if instance_variable_defined?("@#{relation}")
|
40
|
+
|
41
|
+
instance_variable_set("@#{relation}", Rubyists::Linear.const_get(klass).new(data[relation]))
|
42
|
+
end
|
43
|
+
|
44
|
+
define_method "#{relation}=" do |val|
|
45
|
+
hash = val.is_a?(Hash) ? val : val.data
|
46
|
+
updated_data[relation] = hash
|
47
|
+
instance_variable_set("@#{relation}", Rubyists::Linear.const_get(klass).new(hash))
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
35
51
|
def const_added(const)
|
36
52
|
return unless const == :Base
|
37
53
|
|
@@ -108,6 +124,10 @@ module Rubyists
|
|
108
124
|
data != updated_data
|
109
125
|
end
|
110
126
|
|
127
|
+
def completed_states
|
128
|
+
workflow_states.select { |ws| ws.type == 'completed' }
|
129
|
+
end
|
130
|
+
|
111
131
|
def to_h
|
112
132
|
updated_data
|
113
133
|
end
|
data/lib/linear/models/issue.rb
CHANGED
@@ -5,14 +5,13 @@ require 'gqli'
|
|
5
5
|
module Rubyists
|
6
6
|
# Namespace for Linear
|
7
7
|
module Linear
|
8
|
-
|
9
|
-
L :fragments
|
10
|
-
M :base_model
|
11
|
-
M :user
|
8
|
+
M :base_model, :user
|
12
9
|
Issue = Class.new(BaseModel)
|
13
10
|
# The Issue class represents a Linear issue.
|
14
|
-
class Issue
|
11
|
+
class Issue # rubocop:disable Metrics/ClassLength
|
15
12
|
include SemanticLogger::Loggable
|
13
|
+
has_one :assignee, :User
|
14
|
+
has_one :team, :Team
|
16
15
|
|
17
16
|
BASIC_FILTER = { completedAt: { null: true } }.freeze
|
18
17
|
|
@@ -20,7 +19,6 @@ module Rubyists
|
|
20
19
|
id
|
21
20
|
identifier
|
22
21
|
title
|
23
|
-
assignee { ___ User::Base }
|
24
22
|
branchName
|
25
23
|
description
|
26
24
|
createdAt
|
@@ -28,33 +26,88 @@ module Rubyists
|
|
28
26
|
end
|
29
27
|
|
30
28
|
class << self
|
29
|
+
def base_fragment
|
30
|
+
@base_fragment ||= fragment('IssueWithTeams', 'Issue') do
|
31
|
+
___ Base
|
32
|
+
assignee { ___ User.base_fragment }
|
33
|
+
team { ___ Team.base_fragment }
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
31
37
|
def find(slug)
|
32
|
-
q = query { issue(id: slug) { ___
|
38
|
+
q = query { issue(id: slug) { ___ Issue.base_fragment } }
|
33
39
|
data = Api.query(q)
|
34
40
|
raise NotFoundError, "Issue not found: #{slug}" if data.nil?
|
35
41
|
|
36
42
|
new(data[:issue])
|
37
43
|
end
|
38
44
|
|
45
|
+
def find_all(*slugs)
|
46
|
+
slugs.flatten.map { |slug| find(slug) }
|
47
|
+
end
|
48
|
+
|
39
49
|
def create(title:, description:, team:, labels: [])
|
40
50
|
team_id = team.id
|
41
51
|
label_ids = labels.map(&:id)
|
42
52
|
input = { title:, description:, teamId: team_id }
|
43
|
-
input
|
44
|
-
m = mutation { issueCreate(input:) { issue { ___
|
45
|
-
|
46
|
-
new(
|
53
|
+
input[:labelIds] = label_ids unless label_ids.empty?
|
54
|
+
m = mutation { issueCreate(input:) { issue { ___ Issue.base_fragment } } }
|
55
|
+
query_data = Api.query(m)
|
56
|
+
new query_data.dig(:issueCreate, :issue)
|
47
57
|
end
|
48
58
|
end
|
49
59
|
|
50
|
-
def
|
60
|
+
def comment_fragment
|
61
|
+
@comment_fragment ||= fragment('Comment', 'Comment') do
|
62
|
+
id
|
63
|
+
body
|
64
|
+
url
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
# Reference for this mutation:
|
69
|
+
# https://studio.apollographql.com/public/Linear-API/variant/current/schema/reference/inputs/CommentCreateInput
|
70
|
+
def add_comment(comment)
|
71
|
+
id_for_this = identifier
|
72
|
+
comment_frag = comment_fragment
|
73
|
+
m = mutation { commentCreate(input: { issueId: id_for_this, body: comment }) { comment { ___ comment_frag } } }
|
74
|
+
|
75
|
+
query_data = Api.query(m)
|
76
|
+
query_data.dig(:commentCreate, :comment)
|
77
|
+
self
|
78
|
+
end
|
79
|
+
|
80
|
+
def close_mutation(close_state, trash: false)
|
51
81
|
id_for_this = identifier
|
52
|
-
|
53
|
-
|
54
|
-
|
82
|
+
input = { stateId: close_state.id }
|
83
|
+
input[:trash] = true if trash
|
84
|
+
mutation { issueUpdate(id: id_for_this, input:) { issue { ___ Issue.base_fragment } } }
|
85
|
+
end
|
86
|
+
|
87
|
+
def close!(state: nil, trash: false)
|
88
|
+
logger.warn "Using first completed state found: #{completed_states.first}" if state.nil?
|
89
|
+
state ||= completed_states.first
|
90
|
+
query_data = Api.query close_mutation(state, trash:)
|
91
|
+
updated = query_data.dig(:issueUpdate, :issue)
|
92
|
+
raise SmellsBad, "Unknown response for issue close: #{data} (should have :issueUpdate key)" if updated.nil?
|
93
|
+
|
94
|
+
@data = @updated_data = updated
|
95
|
+
self
|
96
|
+
end
|
97
|
+
|
98
|
+
def assign!(user)
|
99
|
+
this_id = identifier
|
100
|
+
m = mutation { issueUpdate(id: this_id, input: { assigneeId: user.id }) { issue { ___ Issue.base_fragment } } }
|
101
|
+
query_data = Api.query(m)
|
102
|
+
updated = query_data.dig(:issueUpdate, :issue)
|
55
103
|
raise SmellsBad, "Unknown response for issue update: #{data} (should have :issueUpdate key)" if updated.nil?
|
56
104
|
|
57
|
-
|
105
|
+
@data = @updated_data = updated
|
106
|
+
self
|
107
|
+
end
|
108
|
+
|
109
|
+
def workflow_states
|
110
|
+
@workflow_states ||= team.workflow_states
|
58
111
|
end
|
59
112
|
|
60
113
|
def inspection
|
@@ -62,18 +115,26 @@ module Rubyists
|
|
62
115
|
end
|
63
116
|
|
64
117
|
def to_s
|
65
|
-
basic = format('%<id>-12s %<title>s', id:
|
118
|
+
basic = format('%<id>-12s %<title>s', id: identifier, title:)
|
66
119
|
return basic unless (name = data.dig(:assignee, :name))
|
67
120
|
|
68
121
|
format('%<basic>s (%<name>s)', basic:, name:)
|
69
122
|
end
|
70
123
|
|
124
|
+
def parsed_description
|
125
|
+
return TTY::Markdown.parse(description) if description && !description.empty?
|
126
|
+
|
127
|
+
TTY::Markdown.parse(['# No Description For this issue??',
|
128
|
+
'Issues really need description',
|
129
|
+
"## What's up with that?"].join("\n"))
|
130
|
+
rescue StandardError => e
|
131
|
+
logger.error 'Error parsing description', e
|
132
|
+
"Description was unparsable: #{description}\n"
|
133
|
+
end
|
134
|
+
|
71
135
|
def full
|
72
136
|
sep = '-' * to_s.length
|
73
|
-
format("%<to_s>s\n%<sep>s\n%<description>s\n",
|
74
|
-
sep:,
|
75
|
-
to_s:,
|
76
|
-
description: (TTY::Markdown.parse(data[:description]) rescue 'No Description?')) # rubocop:disable Style/RescueModifier
|
137
|
+
format("%<to_s>s\n%<sep>s\n%<description>s\n", sep:, to_s:, description: parsed_description)
|
77
138
|
end
|
78
139
|
|
79
140
|
def display(options)
|
data/lib/linear/models/label.rb
CHANGED
@@ -31,14 +31,14 @@ module Rubyists
|
|
31
31
|
fragment('LabelWithTeams', 'IssueLabel') do
|
32
32
|
___ Base
|
33
33
|
parent { ___ Base }
|
34
|
-
team { ___ Team
|
34
|
+
team { ___ Team.base_fragment }
|
35
35
|
end
|
36
36
|
end
|
37
37
|
|
38
38
|
def self.find_all_by_name(names)
|
39
39
|
q = query do
|
40
40
|
issueLabels(filter: { name: { in: names } }) do
|
41
|
-
edges { node { ___
|
41
|
+
edges { node { ___ base_fragment } }
|
42
42
|
end
|
43
43
|
end
|
44
44
|
data = Api.query(q)
|
data/lib/linear/models/team.rb
CHANGED
@@ -5,8 +5,7 @@ require 'gqli'
|
|
5
5
|
module Rubyists
|
6
6
|
# Namespace for Linear
|
7
7
|
module Linear
|
8
|
-
|
9
|
-
M :base_model, :issue, :user
|
8
|
+
M :base_model, :issue, :user, :workflow_state
|
10
9
|
Team = Class.new(BaseModel)
|
11
10
|
# The Issue class represents a Linear issue.
|
12
11
|
class Team
|
@@ -96,6 +95,26 @@ module Rubyists
|
|
96
95
|
def display(_options)
|
97
96
|
printf "%s\n", full
|
98
97
|
end
|
98
|
+
|
99
|
+
def workflow_states_query
|
100
|
+
team_id = id
|
101
|
+
query do
|
102
|
+
team(id: team_id) do
|
103
|
+
states do
|
104
|
+
nodes { ___ WorkflowState.base_fragment }
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
def workflow_states
|
111
|
+
return @workflow_states if @workflow_states
|
112
|
+
|
113
|
+
data = Api.query(workflow_states_query)
|
114
|
+
@workflow_states = data.dig(:team, :states, :nodes)&.map do |state|
|
115
|
+
WorkflowState.new state
|
116
|
+
end
|
117
|
+
end
|
99
118
|
end
|
100
119
|
end
|
101
120
|
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'gqli'
|
4
|
+
|
5
|
+
module Rubyists
|
6
|
+
# Namespace for Linear
|
7
|
+
module Linear
|
8
|
+
M :base_model
|
9
|
+
WorkflowState = Class.new(BaseModel)
|
10
|
+
# The WorkflowState class represents a Linear workflow state.
|
11
|
+
class WorkflowState
|
12
|
+
include SemanticLogger::Loggable
|
13
|
+
|
14
|
+
Base = fragment('BaseWorkflowState', 'WorkflowState') do
|
15
|
+
id
|
16
|
+
name
|
17
|
+
position
|
18
|
+
type
|
19
|
+
description
|
20
|
+
createdAt
|
21
|
+
updatedAt
|
22
|
+
end
|
23
|
+
|
24
|
+
def to_s
|
25
|
+
format('%<name>-12s %<type>s', name:, type:)
|
26
|
+
end
|
27
|
+
|
28
|
+
def inspection
|
29
|
+
format('name: "%<name>s" type: "%<type>s"', name:, type:)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
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.6.0
|
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-05 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: base64
|
@@ -183,6 +183,8 @@ email:
|
|
183
183
|
- tj@rubyists.com
|
184
184
|
executables:
|
185
185
|
- lc
|
186
|
+
- lclose
|
187
|
+
- lclose.sh
|
186
188
|
- lcls
|
187
189
|
- lcls.sh
|
188
190
|
extensions: []
|
@@ -194,10 +196,17 @@ files:
|
|
194
196
|
- Readme.adoc
|
195
197
|
- changelog/0.5.2/added_new_changelog_management_system_changelog_rb.yml
|
196
198
|
- changelog/0.5.2/tag.yml
|
199
|
+
- changelog/0.5.3/added_support_for_multiline_descriptions_without_failing.yml
|
197
200
|
- changelog/0.5.3/changed_default_branch_to_use_upstream_default_branch_name.yml
|
198
201
|
- changelog/0.5.3/tag.yml
|
202
|
+
- changelog/0.5.4/tag.yml
|
203
|
+
- changelog/0.5.5/added_lclose_alias_and_issue_update_subcommand.yml
|
204
|
+
- changelog/0.5.5/tag.yml
|
205
|
+
- changelog/0.6.0/tag.yml
|
199
206
|
- changelog/unreleased/.gitkeep
|
200
207
|
- exe/lc
|
208
|
+
- exe/lclose
|
209
|
+
- exe/lclose.sh
|
201
210
|
- exe/lcls
|
202
211
|
- exe/lcls.sh
|
203
212
|
- lib/linear.rb
|
@@ -213,6 +222,7 @@ files:
|
|
213
222
|
- lib/linear/commands/issue/develop.rb
|
214
223
|
- lib/linear/commands/issue/list.rb
|
215
224
|
- lib/linear/commands/issue/take.rb
|
225
|
+
- lib/linear/commands/issue/update.rb
|
216
226
|
- lib/linear/commands/team.rb
|
217
227
|
- lib/linear/commands/team/list.rb
|
218
228
|
- lib/linear/commands/whoami.rb
|
@@ -223,6 +233,7 @@ files:
|
|
223
233
|
- lib/linear/models/label.rb
|
224
234
|
- lib/linear/models/team.rb
|
225
235
|
- lib/linear/models/user.rb
|
236
|
+
- lib/linear/models/workflow_state.rb
|
226
237
|
- lib/linear/version.rb
|
227
238
|
- linear-cli.gemspec
|
228
239
|
- sig/linear/cli.rbs
|