linear_client 0.1.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 87b5a9b41c84b4044ca5462cd2570eb31d8c6aa35aaaa85dfac2f19be4e8da41
4
+ data.tar.gz: 9ebe3adfea20b1eef397fd295a9cefb1914a92b4e1d8806e617aa72c17cf6be2
5
+ SHA512:
6
+ metadata.gz: a0ae7497191bbd81967090e3854ace3069851e606059e0027e4e7957dc3a5a1eadf50b90d6be13152116954d86964f1a9a5f00a7e35866ac5f3df35af10bd268
7
+ data.tar.gz: 42b7cc949edaba299a73f4d7207c76ab4b29bde6f031cf4a447610be95c3e2e14faf378248823e9d6a2466f226642be7823539c7f0edd73a1bf7c6c59b442780
data/.gitignore ADDED
@@ -0,0 +1,5 @@
1
+ /.env
2
+ *.gem
3
+ /pkg/
4
+ /.bundle/
5
+ Gemfile.lock
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --require spec_helper
2
+ --format documentation
3
+ --color
data/CHANGELOG.md ADDED
@@ -0,0 +1,5 @@
1
+ # Changelog
2
+
3
+ ## 0.1.0
4
+
5
+ - Lançamento inicial: `LinearClient::Client` com `teams`, `viewer`, `my_issues`, `create_issue` e `create_comment`.
data/Gemfile ADDED
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Lenio Ferretto
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,110 @@
1
+ # linear_client
2
+
3
+ Cliente Ruby puro para a [API GraphQL do Linear](https://developers.linear.app/docs/graphql/working-with-the-graphql-api) — sem gems externas, apenas a stdlib (`net/http`, `uri`, `json`).
4
+
5
+ Não há SDK oficial para Ruby; este projeto preenche essa lacuna com uma interface simples para criar issues, comentar e consultar dados no Linear.
6
+
7
+ ## Instalação
8
+
9
+ ```bash
10
+ gem install linear_client
11
+ ```
12
+
13
+ ou no Gemfile:
14
+
15
+ ```ruby
16
+ gem "linear_client"
17
+ ```
18
+
19
+ ## Configuração
20
+
21
+ 1. Gere uma API key pessoal no Linear em `Settings > Account > Security > API keys`.
22
+ 2. Exporte a chave como variável de ambiente (nunca hardcode no código):
23
+
24
+ ```bash
25
+ export LINEAR_API_KEY="lin_api_xxxxxxxx"
26
+ ```
27
+
28
+ > Trate a API key como uma senha: não a compartilhe, não a commit em arquivos versionados, e revogue/gere outra se ela vazar.
29
+
30
+ ## Uso
31
+
32
+ ```ruby
33
+ require "linear_client"
34
+
35
+ client = LinearClient::Client.new
36
+ # ou, explicitamente:
37
+ client = LinearClient::Client.new(api_key: "lin_api_xxxxxxxx")
38
+ ```
39
+
40
+ ### Descobrir times
41
+
42
+ ```ruby
43
+ client.teams
44
+ # => [{"id"=>"...", "name"=>"Engineering", "key"=>"ENG"}, ...]
45
+ ```
46
+
47
+ ### Criar um card e comentar
48
+
49
+ ```ruby
50
+ result = client.create_issue(team_id: "TEAM_ID", title: "Bug no login", description: "Detalhes...")
51
+ issue_id = result.dig("issue", "id")
52
+
53
+ client.create_comment(issue_id: issue_id, body: "Já reproduzi, investigando.")
54
+ ```
55
+
56
+ ### Listar issues atribuídas a você
57
+
58
+ ```ruby
59
+ client.my_issues.each { |i| puts "#{i['identifier']} [#{i['state']['name']}] #{i['title']}" }
60
+ ```
61
+
62
+ ### Dados do usuário autenticado
63
+
64
+ ```ruby
65
+ client.viewer
66
+ # => {"name"=>"...", "email"=>"..."}
67
+ ```
68
+
69
+ ## Referência da API
70
+
71
+ | Método | Descrição | Retorno |
72
+ |---|---|---|
73
+ | `teams` | Lista os times do workspace (`id`, `name`, `key`) | `Array<Hash>` |
74
+ | `viewer` | Nome e e-mail do dono da API key | `Hash` |
75
+ | `my_issues` | Issues atribuídas ao dono da API key, com prioridade, datas, estado e time | `Array<Hash>` |
76
+ | `create_issue(team_id:, title:, description: nil)` | Cria um card num time | `Hash` com `"success"` e `"issue"` (`id`, `identifier`, `title`, `url`) |
77
+ | `create_comment(issue_id:, body:)` | Comenta num card existente | `Hash` com `"success"` e `"comment"` (`id`, `url`) |
78
+
79
+ Todos os métodos lançam `LinearClient::Client::Error` se a API retornar erros GraphQL.
80
+
81
+ ## Testes
82
+
83
+ Suíte em RSpec, com [WebMock](https://github.com/bblimke/webmock) simulando as respostas da API (nenhum teste bate na rede real):
84
+
85
+ ```bash
86
+ bundle install
87
+ bundle exec rspec
88
+ ```
89
+
90
+ ## Scripts de exemplo
91
+
92
+ ```bash
93
+ # Lista os times e cria um card de teste no time indicado pelo key (ex: ENG)
94
+ ruby examples/create_issue.rb ENG
95
+
96
+ # Lista as issues atribuídas a você
97
+ ruby examples/my_issues.rb
98
+
99
+ # Exporta viewer + issues atribuídas para JSON
100
+ ruby examples/report.rb caminho/para/saida.json
101
+ ```
102
+
103
+ ## Notas de segurança
104
+
105
+ - A API key vai no header `Authorization` (sem prefixo `Bearer`), conforme exigido pela API do Linear.
106
+ - Nunca dê commit em arquivos `.env` ou qualquer arquivo com a chave em texto puro — este repositório já ignora `.env` via `.gitignore`.
107
+
108
+ ## Licença
109
+
110
+ [MIT](LICENSE)
data/Rakefile ADDED
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rspec/core/rake_task"
4
+
5
+ RSpec::Core::RakeTask.new(:spec)
6
+
7
+ task default: :spec
@@ -0,0 +1,25 @@
1
+ #!/usr/bin/env ruby
2
+ require_relative "../lib/linear_client"
3
+
4
+ client = LinearClient::Client.new
5
+
6
+ teams = client.teams
7
+ puts "Times disponiveis:"
8
+ teams.each { |t| puts " #{t['key']} (#{t['name']}) -> id: #{t['id']}" }
9
+
10
+ team_key = ARGV.first || teams.first.fetch("key")
11
+ team = teams.find { |t| t.fetch("key") == team_key }
12
+ raise "Time '#{team_key}' nao encontrado" unless team
13
+
14
+ team_id = team.fetch("id")
15
+
16
+ result = client.create_issue(
17
+ team_id: team_id,
18
+ title: "Card de teste criado via Ruby",
19
+ description: "Criado pelo script examples/create_issue.rb"
20
+ )
21
+ issue = result.fetch("issue")
22
+ puts "Issue criada: #{issue['identifier']} - #{issue['url']}"
23
+
24
+ client.create_comment(issue_id: issue.fetch("id"), body: "Primeiro comentario via API.")
25
+ puts "Comentario adicionado."
@@ -0,0 +1,13 @@
1
+ #!/usr/bin/env ruby
2
+ require_relative "../lib/linear_client"
3
+
4
+ client = LinearClient::Client.new
5
+ issues = client.my_issues
6
+
7
+ if issues.empty?
8
+ puts "Nenhuma issue atribuida a voce."
9
+ else
10
+ issues.each do |issue|
11
+ puts "#{issue['identifier']} [#{issue['team']['key']} / #{issue['state']['name']}] #{issue['title']} -> #{issue['url']}"
12
+ end
13
+ end
@@ -0,0 +1,12 @@
1
+ #!/usr/bin/env ruby
2
+ require_relative "../lib/linear_client"
3
+ require "json"
4
+
5
+ client = LinearClient::Client.new
6
+ viewer = client.viewer
7
+ issues = client.my_issues
8
+
9
+ output = { viewer: viewer, issues: issues }
10
+ path = ARGV.first || "/tmp/linear_report_data.json"
11
+ File.write(path, JSON.pretty_generate(output))
12
+ puts "Dados salvos em #{path} (#{issues.size} issues)"
@@ -0,0 +1,114 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "net/http"
4
+ require "uri"
5
+ require "json"
6
+
7
+ module LinearClient
8
+ # Cliente minimo para a API GraphQL do Linear (https://api.linear.app/graphql).
9
+ # Nao ha SDK oficial para Ruby, entao falamos GraphQL diretamente via Net::HTTP.
10
+ class Client
11
+ ENDPOINT = "https://api.linear.app/graphql"
12
+
13
+ class Error < StandardError; end
14
+
15
+ def initialize(api_key: ENV["LINEAR_API_KEY"])
16
+ raise ArgumentError, "LINEAR_API_KEY nao configurada" if api_key.nil? || api_key.empty?
17
+
18
+ @api_key = api_key
19
+ end
20
+
21
+ # team_id: id do time no Linear (veja #teams para descobrir)
22
+ def create_issue(team_id:, title:, description: nil)
23
+ query = <<~GRAPHQL
24
+ mutation IssueCreate($input: IssueCreateInput!) {
25
+ issueCreate(input: $input) {
26
+ success
27
+ issue { id identifier title url }
28
+ }
29
+ }
30
+ GRAPHQL
31
+
32
+ data = execute(query, input: { teamId: team_id, title: title, description: description })
33
+ data.fetch("issueCreate")
34
+ end
35
+
36
+ def create_comment(issue_id:, body:)
37
+ query = <<~GRAPHQL
38
+ mutation CommentCreate($input: CommentCreateInput!) {
39
+ commentCreate(input: $input) {
40
+ success
41
+ comment { id url }
42
+ }
43
+ }
44
+ GRAPHQL
45
+
46
+ data = execute(query, input: { issueId: issue_id, body: body })
47
+ data.fetch("commentCreate")
48
+ end
49
+
50
+ # Issues atribuidas ao dono da API key
51
+ def my_issues
52
+ query = <<~GRAPHQL
53
+ query MyIssues {
54
+ viewer {
55
+ assignedIssues {
56
+ nodes {
57
+ id
58
+ identifier
59
+ title
60
+ url
61
+ priority
62
+ createdAt
63
+ updatedAt
64
+ completedAt
65
+ state { name type }
66
+ team { key }
67
+ }
68
+ }
69
+ }
70
+ }
71
+ GRAPHQL
72
+
73
+ execute(query).dig("viewer", "assignedIssues", "nodes")
74
+ end
75
+
76
+ def viewer
77
+ query = <<~GRAPHQL
78
+ query Viewer {
79
+ viewer { name email }
80
+ }
81
+ GRAPHQL
82
+
83
+ execute(query).fetch("viewer")
84
+ end
85
+
86
+ # Util para descobrir os teamIds disponiveis no workspace
87
+ def teams
88
+ query = <<~GRAPHQL
89
+ query Teams {
90
+ teams { nodes { id name key } }
91
+ }
92
+ GRAPHQL
93
+
94
+ execute(query).dig("teams", "nodes")
95
+ end
96
+
97
+ private
98
+
99
+ def execute(query, **variables)
100
+ uri = URI.parse(ENDPOINT)
101
+ request = Net::HTTP::Post.new(uri)
102
+ request["Content-Type"] = "application/json"
103
+ request["Authorization"] = @api_key
104
+ request.body = { query: query, variables: variables }.to_json
105
+
106
+ response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |http| http.request(request) }
107
+ payload = JSON.parse(response.body)
108
+
109
+ raise Error, payload["errors"].to_s if payload["errors"] && !payload["errors"].empty?
110
+
111
+ payload.fetch("data")
112
+ end
113
+ end
114
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module LinearClient
4
+ VERSION = "0.1.0"
5
+ end
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "linear_client/version"
4
+ require_relative "linear_client/client"
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "lib/linear_client/version"
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "linear_client"
7
+ spec.version = LinearClient::VERSION
8
+ spec.authors = ["Lenio Ferretto"]
9
+ spec.email = ["ferretto@users.noreply.github.com"]
10
+
11
+ spec.summary = "Cliente Ruby puro para a API GraphQL do Linear"
12
+ spec.description = "Cliente minimalista, sem dependencias externas, para criar issues, " \
13
+ "comentar e consultar dados no Linear (linear.app) via sua API GraphQL."
14
+ spec.homepage = "https://github.com/ferretto/linear_client"
15
+ spec.license = "MIT"
16
+ spec.required_ruby_version = ">= 3.0"
17
+
18
+ spec.metadata["source_code_uri"] = spec.homepage
19
+ spec.metadata["changelog_uri"] = "#{spec.homepage}/blob/main/CHANGELOG.md"
20
+
21
+ spec.files = Dir.chdir(__dir__) do
22
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{\A(?:test|spec|\.github)/}) }
23
+ end
24
+ spec.require_paths = ["lib"]
25
+
26
+ spec.add_development_dependency "rake", "~> 13.0"
27
+ spec.add_development_dependency "rspec", "~> 3.13"
28
+ spec.add_development_dependency "webmock", "~> 3.26"
29
+ end
metadata ADDED
@@ -0,0 +1,99 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: linear_client
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Lenio Ferretto
8
+ bindir: bin
9
+ cert_chain: []
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: rake
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - "~>"
17
+ - !ruby/object:Gem::Version
18
+ version: '13.0'
19
+ type: :development
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - "~>"
24
+ - !ruby/object:Gem::Version
25
+ version: '13.0'
26
+ - !ruby/object:Gem::Dependency
27
+ name: rspec
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - "~>"
31
+ - !ruby/object:Gem::Version
32
+ version: '3.13'
33
+ type: :development
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: '3.13'
40
+ - !ruby/object:Gem::Dependency
41
+ name: webmock
42
+ requirement: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - "~>"
45
+ - !ruby/object:Gem::Version
46
+ version: '3.26'
47
+ type: :development
48
+ prerelease: false
49
+ version_requirements: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - "~>"
52
+ - !ruby/object:Gem::Version
53
+ version: '3.26'
54
+ description: Cliente minimalista, sem dependencias externas, para criar issues, comentar
55
+ e consultar dados no Linear (linear.app) via sua API GraphQL.
56
+ email:
57
+ - ferretto@users.noreply.github.com
58
+ executables: []
59
+ extensions: []
60
+ extra_rdoc_files: []
61
+ files:
62
+ - ".gitignore"
63
+ - ".rspec"
64
+ - CHANGELOG.md
65
+ - Gemfile
66
+ - LICENSE
67
+ - README.md
68
+ - Rakefile
69
+ - examples/create_issue.rb
70
+ - examples/my_issues.rb
71
+ - examples/report.rb
72
+ - lib/linear_client.rb
73
+ - lib/linear_client/client.rb
74
+ - lib/linear_client/version.rb
75
+ - linear_client.gemspec
76
+ homepage: https://github.com/ferretto/linear_client
77
+ licenses:
78
+ - MIT
79
+ metadata:
80
+ source_code_uri: https://github.com/ferretto/linear_client
81
+ changelog_uri: https://github.com/ferretto/linear_client/blob/main/CHANGELOG.md
82
+ rdoc_options: []
83
+ require_paths:
84
+ - lib
85
+ required_ruby_version: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '3.0'
90
+ required_rubygems_version: !ruby/object:Gem::Requirement
91
+ requirements:
92
+ - - ">="
93
+ - !ruby/object:Gem::Version
94
+ version: '0'
95
+ requirements: []
96
+ rubygems_version: 3.6.7
97
+ specification_version: 4
98
+ summary: Cliente Ruby puro para a API GraphQL do Linear
99
+ test_files: []