linear-rb 0.1.0 → 0.2.0
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/bin/linear +50 -21
- data/lib/linear/commands.rb +43 -5
- data/lib/linear/queries.rb +22 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 1da26699f5a96c66c0ab9eaff87b6d6b0af051810e289ba0fd7474d5874c2e8d
|
|
4
|
+
data.tar.gz: 61c18f121fc85cd46821d4291f49021cd2d2e9334e0b75ec8d54fffb4b16f840
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: ff8b25a4e714cc5dc5b5db510912f4eb1a157ee9f1e7289166de519797fd9a4eb1008bf4d44dc38ad16456a6a43116ade1b2b9f8893f810e12f58c358dde5efd
|
|
7
|
+
data.tar.gz: 2a1fa04804fa329fc0cb8d1e73c926cc08e7b290d6eadc88dff4baae898f0007f861f30fc2a0c321bb39f0de505ff95ca5f6b59f0e7fca9a6380f01cb0d08ea0
|
data/bin/linear
CHANGED
|
@@ -8,21 +8,47 @@ def show_usage
|
|
|
8
8
|
Linear CLI - Ruby wrapper for Linear GraphQL API
|
|
9
9
|
|
|
10
10
|
Usage:
|
|
11
|
-
linear issue ISSUE_ID Fetch issue
|
|
12
|
-
linear
|
|
11
|
+
linear issue ISSUE_ID Fetch a specific issue by ID
|
|
12
|
+
linear issues [OPTIONS] List and filter issues (all filters are optional)
|
|
13
13
|
linear mine Show issues assigned to you
|
|
14
14
|
linear teams List all teams
|
|
15
|
+
linear projects List all projects
|
|
15
16
|
linear comment ISSUE_ID COMMENT Add a comment to an issue
|
|
16
17
|
linear update ISSUE_ID STATE Update issue state
|
|
17
18
|
|
|
18
|
-
|
|
19
|
+
Issues Filters (all optional, can be combined):
|
|
20
|
+
--query TEXT Search by title text
|
|
21
|
+
--project PROJECT_ID Filter by project ID
|
|
22
|
+
--state STATE Filter by state name (case-insensitive)
|
|
19
23
|
--team TEAM_KEY Filter by team key
|
|
20
|
-
--state STATE Filter by state name
|
|
21
24
|
|
|
22
25
|
Examples:
|
|
26
|
+
# View a specific issue
|
|
23
27
|
linear issue ENG-123
|
|
24
|
-
|
|
28
|
+
|
|
29
|
+
# List all issues
|
|
30
|
+
linear issues
|
|
31
|
+
|
|
32
|
+
# Search issues by title
|
|
33
|
+
linear issues --query "authentication"
|
|
34
|
+
|
|
35
|
+
# Filter by state (case-insensitive)
|
|
36
|
+
linear issues --state backlog
|
|
37
|
+
linear issues --state "In Progress"
|
|
38
|
+
|
|
39
|
+
# Filter by team
|
|
40
|
+
linear issues --team ENG
|
|
41
|
+
|
|
42
|
+
# Combine multiple filters
|
|
43
|
+
linear issues --query "bug" --state Backlog --team ENG
|
|
44
|
+
linear issues --project abc123 --state Done
|
|
45
|
+
|
|
46
|
+
# Your assigned issues
|
|
25
47
|
linear mine
|
|
48
|
+
|
|
49
|
+
# Other commands
|
|
50
|
+
linear teams
|
|
51
|
+
linear projects
|
|
26
52
|
linear comment FAT-85 "This is done"
|
|
27
53
|
linear update FAT-85 "Done"
|
|
28
54
|
|
|
@@ -50,38 +76,41 @@ when 'issue'
|
|
|
50
76
|
exit 1
|
|
51
77
|
end
|
|
52
78
|
|
|
53
|
-
when '
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
puts "
|
|
79
|
+
when 'mine'
|
|
80
|
+
begin
|
|
81
|
+
Linear::Commands.my_issues
|
|
82
|
+
rescue => e
|
|
83
|
+
puts "Error: #{e.message}"
|
|
58
84
|
exit 1
|
|
59
85
|
end
|
|
60
86
|
|
|
61
|
-
|
|
62
|
-
OptionParser.new do |opts|
|
|
63
|
-
opts.on("--team TEAM", "Filter by team key") { |v| options[:team] = v }
|
|
64
|
-
opts.on("--state STATE", "Filter by state name") { |v| options[:state] = v }
|
|
65
|
-
end.parse!
|
|
66
|
-
|
|
87
|
+
when 'teams'
|
|
67
88
|
begin
|
|
68
|
-
Linear::Commands.
|
|
89
|
+
Linear::Commands.list_teams
|
|
69
90
|
rescue => e
|
|
70
91
|
puts "Error: #{e.message}"
|
|
71
92
|
exit 1
|
|
72
93
|
end
|
|
73
94
|
|
|
74
|
-
when '
|
|
95
|
+
when 'projects'
|
|
75
96
|
begin
|
|
76
|
-
Linear::Commands.
|
|
97
|
+
Linear::Commands.list_projects
|
|
77
98
|
rescue => e
|
|
78
99
|
puts "Error: #{e.message}"
|
|
79
100
|
exit 1
|
|
80
101
|
end
|
|
81
102
|
|
|
82
|
-
when '
|
|
103
|
+
when 'issues'
|
|
104
|
+
options = {}
|
|
105
|
+
OptionParser.new do |opts|
|
|
106
|
+
opts.on("--query QUERY", "Filter by title text") { |v| options[:query] = v }
|
|
107
|
+
opts.on("--project PROJECT", "Filter by project ID") { |v| options[:project] = v }
|
|
108
|
+
opts.on("--state STATE", "Filter by state name") { |v| options[:state] = v }
|
|
109
|
+
opts.on("--team TEAM", "Filter by team key") { |v| options[:team] = v }
|
|
110
|
+
end.parse!
|
|
111
|
+
|
|
83
112
|
begin
|
|
84
|
-
Linear::Commands.
|
|
113
|
+
Linear::Commands.list_issues(options)
|
|
85
114
|
rescue => e
|
|
86
115
|
puts "Error: #{e.message}"
|
|
87
116
|
exit 1
|
data/lib/linear/commands.rb
CHANGED
|
@@ -13,16 +13,18 @@ module Linear
|
|
|
13
13
|
end
|
|
14
14
|
end
|
|
15
15
|
|
|
16
|
-
def
|
|
17
|
-
filter = {
|
|
16
|
+
def list_issues(options = {}, client: Client.new)
|
|
17
|
+
filter = {}
|
|
18
|
+
filter[:title] = { contains: options[:query] } if options[:query]
|
|
19
|
+
filter[:project] = { id: { eq: options[:project] } } if options[:project]
|
|
20
|
+
filter[:state] = { name: { eqIgnoreCase: options[:state] } } if options[:state]
|
|
18
21
|
filter[:team] = { key: { eq: options[:team] } } if options[:team]
|
|
19
|
-
filter[:state] = { name: { eq: options[:state] } } if options[:state]
|
|
20
22
|
|
|
21
|
-
result = client.query(Queries::
|
|
23
|
+
result = client.query(Queries::LIST_ISSUES, { filter: filter })
|
|
22
24
|
|
|
23
25
|
issues = result.dig("data", "issues", "nodes") || []
|
|
24
26
|
if issues.empty?
|
|
25
|
-
puts "No issues found
|
|
27
|
+
puts "No issues found"
|
|
26
28
|
else
|
|
27
29
|
display_issue_list(issues)
|
|
28
30
|
end
|
|
@@ -48,6 +50,17 @@ module Linear
|
|
|
48
50
|
end
|
|
49
51
|
end
|
|
50
52
|
|
|
53
|
+
def list_projects(client: Client.new)
|
|
54
|
+
result = client.query(Queries::PROJECTS)
|
|
55
|
+
|
|
56
|
+
projects = result.dig("data", "projects", "nodes") || []
|
|
57
|
+
if projects.empty?
|
|
58
|
+
puts "No projects found"
|
|
59
|
+
else
|
|
60
|
+
display_project_list(projects)
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
51
64
|
def add_comment(issue_id, body, client: Client.new)
|
|
52
65
|
# First get the issue to get its internal ID
|
|
53
66
|
issue_result = client.query(Queries::ISSUE, { id: issue_id })
|
|
@@ -178,5 +191,30 @@ module Linear
|
|
|
178
191
|
else "Unknown"
|
|
179
192
|
end
|
|
180
193
|
end
|
|
194
|
+
|
|
195
|
+
def display_project_list(projects)
|
|
196
|
+
puts "\nFound #{projects.length} project(s):\n\n"
|
|
197
|
+
projects.each do |project|
|
|
198
|
+
state_badge = "[#{project['state']}]".ljust(15)
|
|
199
|
+
progress = project['progress'] ? "#{(project['progress'] * 100).round}%" : "0%"
|
|
200
|
+
progress_badge = progress.ljust(6)
|
|
201
|
+
lead = (project.dig('lead', 'name') || 'No lead').ljust(20)
|
|
202
|
+
|
|
203
|
+
puts "#{project['name'].ljust(30)} #{state_badge} #{progress_badge} #{lead}"
|
|
204
|
+
|
|
205
|
+
if project['description'] && !project['description'].empty?
|
|
206
|
+
# Show first line of description
|
|
207
|
+
first_line = project['description'].lines.first&.strip
|
|
208
|
+
puts " #{first_line[0..80]}#{'...' if first_line && first_line.length > 80}" if first_line
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
if project['targetDate']
|
|
212
|
+
puts " Target: #{project['targetDate']}"
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
puts " URL: #{project['url']}" if project['url']
|
|
216
|
+
puts ""
|
|
217
|
+
end
|
|
218
|
+
end
|
|
181
219
|
end
|
|
182
220
|
end
|
data/lib/linear/queries.rb
CHANGED
|
@@ -23,7 +23,7 @@ module Linear
|
|
|
23
23
|
}
|
|
24
24
|
GQL
|
|
25
25
|
|
|
26
|
-
|
|
26
|
+
LIST_ISSUES = <<~GQL
|
|
27
27
|
query($filter: IssueFilter!) {
|
|
28
28
|
issues(filter: $filter) {
|
|
29
29
|
nodes {
|
|
@@ -76,6 +76,27 @@ module Linear
|
|
|
76
76
|
}
|
|
77
77
|
GQL
|
|
78
78
|
|
|
79
|
+
PROJECTS = <<~GQL
|
|
80
|
+
query {
|
|
81
|
+
projects {
|
|
82
|
+
nodes {
|
|
83
|
+
id
|
|
84
|
+
name
|
|
85
|
+
description
|
|
86
|
+
state
|
|
87
|
+
progress
|
|
88
|
+
startDate
|
|
89
|
+
targetDate
|
|
90
|
+
url
|
|
91
|
+
lead {
|
|
92
|
+
name
|
|
93
|
+
email
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
GQL
|
|
99
|
+
|
|
79
100
|
WORKFLOW_STATES = <<~GQL
|
|
80
101
|
query($teamId: String!) {
|
|
81
102
|
team(id: $teamId) {
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: linear-rb
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Dave Kinkead
|
|
@@ -57,7 +57,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
57
57
|
- !ruby/object:Gem::Version
|
|
58
58
|
version: '0'
|
|
59
59
|
requirements: []
|
|
60
|
-
rubygems_version:
|
|
60
|
+
rubygems_version: 4.0.1
|
|
61
61
|
specification_version: 4
|
|
62
62
|
summary: Ruby CLI wrapper for Linear GraphQL API
|
|
63
63
|
test_files: []
|