lex-github 0.3.2 → 0.3.3
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/CHANGELOG.md +6 -0
- data/lib/legion/extensions/github/cli/auth.rb +2 -2
- data/lib/legion/extensions/github/cli/runner.rb +70 -59
- data/lib/legion/extensions/github/helpers/browser_auth.rb +1 -1
- data/lib/legion/extensions/github/oauth/runners/auth.rb +10 -12
- data/lib/legion/extensions/github/runners/auth.rb +126 -0
- data/lib/legion/extensions/github/version.rb +1 -1
- data/lib/legion/extensions/github.rb +1 -0
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 375eae72829e4b18ecd5162a92eecb0a93f22552e51bdedee79866d46d720fdd
|
|
4
|
+
data.tar.gz: 543b65111d082eec0abfa3a2e1c9d44e7e248d5ded04a6664f26f1f5d1025b00
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 7d3f74a3a0bd859ca3c8a35afca74a6c45afb7d057f2aff06fa2f6842b77a94ca5afaa840c08d323a1172f1385d3db6f9f34fb5b886a6deae5cfe98f5b45f139
|
|
7
|
+
data.tar.gz: 4c19e0bbae310e0987bbda49565a525c5cb6bf90abb4a91c81134834d4a53f671160e4767b6ec20862a68f8d680ddf4083be635e079752d7e217a5f5b353db34
|
data/CHANGELOG.md
CHANGED
|
@@ -15,9 +15,9 @@ module Legion
|
|
|
15
15
|
csec = client_secret || settings_client_secret
|
|
16
16
|
sc = scopes || settings_scopes
|
|
17
17
|
|
|
18
|
-
unless cid
|
|
18
|
+
unless cid
|
|
19
19
|
return { error: 'missing_config',
|
|
20
|
-
description: 'Set github.oauth.client_id or github.app.client_id
|
|
20
|
+
description: 'Set github.oauth.client_id or github.app.client_id in settings' }
|
|
21
21
|
end
|
|
22
22
|
|
|
23
23
|
browser = Helpers::BrowserAuth.new(client_id: cid, client_secret: csec, scopes: sc)
|
|
@@ -1,90 +1,101 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require '
|
|
4
|
-
require '
|
|
5
|
-
require '
|
|
3
|
+
require 'json'
|
|
4
|
+
require 'net/http'
|
|
5
|
+
require 'uri'
|
|
6
|
+
require 'rbconfig'
|
|
6
7
|
|
|
7
8
|
module Legion
|
|
8
9
|
module Extensions
|
|
9
10
|
module Github
|
|
10
11
|
module CLI
|
|
11
|
-
|
|
12
|
-
include Legion::Logging::Helper if defined?(Legion::Logging::Helper)
|
|
13
|
-
include Github::CLI::Auth
|
|
14
|
-
include Github::App::Runners::CredentialStore
|
|
15
|
-
|
|
16
|
-
def credential_fingerprint(auth_type:, identifier:)
|
|
17
|
-
"#{auth_type}:#{identifier}"
|
|
18
|
-
end
|
|
12
|
+
DAEMON_URL = ENV.fetch('LEGION_API_URL', 'http://127.0.0.1:4567')
|
|
19
13
|
|
|
20
|
-
|
|
21
|
-
|
|
14
|
+
module DaemonApi
|
|
15
|
+
private
|
|
22
16
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
17
|
+
def api_post(path, body = {})
|
|
18
|
+
uri = URI("#{DAEMON_URL}#{path}")
|
|
19
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
|
20
|
+
http.open_timeout = 5
|
|
21
|
+
http.read_timeout = 30
|
|
22
|
+
request = Net::HTTP::Post.new(uri.path, 'Content-Type' => 'application/json')
|
|
23
|
+
request.body = ::JSON.generate(body)
|
|
24
|
+
parse_response(http.request(request))
|
|
25
|
+
rescue Errno::ECONNREFUSED, Errno::ECONNRESET => _e
|
|
26
|
+
{ error: 'daemon_unavailable', description: "Legion daemon not running at #{DAEMON_URL}. Start it with: legionio start" }
|
|
27
27
|
end
|
|
28
28
|
|
|
29
|
-
def
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
29
|
+
def api_get(path)
|
|
30
|
+
uri = URI("#{DAEMON_URL}#{path}")
|
|
31
|
+
parse_response(Net::HTTP.get_response(uri))
|
|
32
|
+
rescue Errno::ECONNREFUSED, Errno::ECONNRESET => _e
|
|
33
|
+
{ error: 'daemon_unavailable', description: "Legion daemon not running at #{DAEMON_URL}. Start it with: legionio start" }
|
|
34
34
|
end
|
|
35
35
|
|
|
36
|
-
def
|
|
37
|
-
|
|
38
|
-
rescue
|
|
39
|
-
|
|
40
|
-
false
|
|
36
|
+
def parse_response(response)
|
|
37
|
+
::JSON.parse(response.body, symbolize_names: true)
|
|
38
|
+
rescue ::JSON::ParserError => _e
|
|
39
|
+
{ error: "http_#{response.code}", description: response.body&.strip }
|
|
41
40
|
end
|
|
42
41
|
|
|
43
|
-
def
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
42
|
+
def print_json(result)
|
|
43
|
+
if result.is_a?(Hash) && result[:error]
|
|
44
|
+
warn "Error: #{result[:error]}"
|
|
45
|
+
warn " #{result[:description]}" if result[:description]
|
|
46
|
+
else
|
|
47
|
+
puts ::JSON.pretty_generate(result)
|
|
48
|
+
end
|
|
48
49
|
end
|
|
49
50
|
|
|
50
|
-
def
|
|
51
|
-
::
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
51
|
+
def open_browser(url)
|
|
52
|
+
cmd = case RbConfig::CONFIG['host_os']
|
|
53
|
+
when /darwin/ then 'open'
|
|
54
|
+
when /linux/ then 'xdg-open'
|
|
55
|
+
when /mswin|mingw/ then 'start'
|
|
56
|
+
end
|
|
57
|
+
system(cmd, url) if cmd
|
|
55
58
|
end
|
|
59
|
+
end
|
|
56
60
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
61
|
+
class AuthRunner
|
|
62
|
+
include DaemonApi
|
|
63
|
+
|
|
64
|
+
def status
|
|
65
|
+
print_json(api_post('/api/extensions/github/runners/auth/status'))
|
|
62
66
|
end
|
|
63
67
|
|
|
64
|
-
def
|
|
65
|
-
|
|
66
|
-
rescue StandardError => e
|
|
67
|
-
log.debug("[lex-github] local_cache_set failed: #{e.message}")
|
|
68
|
-
nil
|
|
68
|
+
def login
|
|
69
|
+
print_json(api_post('/api/extensions/github/runners/auth/login'))
|
|
69
70
|
end
|
|
70
71
|
end
|
|
71
72
|
|
|
72
73
|
class AppRunner
|
|
73
|
-
include
|
|
74
|
-
include Github::CLI::App
|
|
75
|
-
include Github::App::Runners::CredentialStore
|
|
74
|
+
include DaemonApi
|
|
76
75
|
|
|
77
|
-
def
|
|
78
|
-
|
|
79
|
-
end
|
|
76
|
+
def setup
|
|
77
|
+
result = api_post('/api/extensions/github/cli/app/setup')
|
|
80
78
|
|
|
81
|
-
|
|
82
|
-
|
|
79
|
+
if result[:error]
|
|
80
|
+
print_json(result)
|
|
81
|
+
return
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
url = result.dig(:data, :manifest_url)
|
|
85
|
+
if url
|
|
86
|
+
warn 'Opening browser to create GitHub App...'
|
|
87
|
+
open_browser(url)
|
|
88
|
+
warn 'Waiting for callback...'
|
|
89
|
+
poll = api_post('/api/extensions/github/cli/app/await_callback',
|
|
90
|
+
{ timeout: 300 })
|
|
91
|
+
print_json(poll)
|
|
92
|
+
else
|
|
93
|
+
print_json(result)
|
|
94
|
+
end
|
|
95
|
+
end
|
|
83
96
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
log.warn("[lex-github] vault_get failed: #{e.message}")
|
|
87
|
-
nil
|
|
97
|
+
def complete_setup
|
|
98
|
+
print_json(api_post('/api/extensions/github/cli/app/complete_setup'))
|
|
88
99
|
end
|
|
89
100
|
end
|
|
90
101
|
end
|
|
@@ -14,7 +14,7 @@ module Legion
|
|
|
14
14
|
|
|
15
15
|
attr_reader :client_id, :client_secret, :scopes
|
|
16
16
|
|
|
17
|
-
def initialize(client_id:, client_secret
|
|
17
|
+
def initialize(client_id:, client_secret: nil, scopes: DEFAULT_SCOPES, auth: nil, **)
|
|
18
18
|
@client_id = client_id
|
|
19
19
|
@client_secret = client_secret
|
|
20
20
|
@scopes = scopes
|
|
@@ -33,21 +33,19 @@ module Legion
|
|
|
33
33
|
{ result: "https://github.com/login/oauth/authorize?#{params}" }
|
|
34
34
|
end
|
|
35
35
|
|
|
36
|
-
def exchange_code(client_id:,
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
})
|
|
36
|
+
def exchange_code(client_id:, code:, redirect_uri:, code_verifier:, client_secret: nil, **)
|
|
37
|
+
body = { client_id: client_id, code: code,
|
|
38
|
+
redirect_uri: redirect_uri, code_verifier: code_verifier }
|
|
39
|
+
body[:client_secret] = client_secret if client_secret
|
|
40
|
+
response = oauth_connection.post('/login/oauth/access_token', body)
|
|
42
41
|
{ result: response.body }
|
|
43
42
|
end
|
|
44
43
|
|
|
45
|
-
def refresh_token(client_id:,
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
})
|
|
44
|
+
def refresh_token(client_id:, refresh_token:, client_secret: nil, **)
|
|
45
|
+
body = { client_id: client_id, refresh_token: refresh_token,
|
|
46
|
+
grant_type: 'refresh_token' }
|
|
47
|
+
body[:client_secret] = client_secret if client_secret
|
|
48
|
+
response = oauth_connection.post('/login/oauth/access_token', body)
|
|
51
49
|
{ result: response.body }
|
|
52
50
|
end
|
|
53
51
|
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'legion/extensions/github/helpers/client'
|
|
4
|
+
require 'legion/extensions/github/helpers/browser_auth'
|
|
5
|
+
require 'legion/extensions/github/app/runners/auth'
|
|
6
|
+
require 'legion/extensions/github/app/runners/credential_store'
|
|
7
|
+
require 'legion/extensions/github/oauth/runners/auth'
|
|
8
|
+
|
|
9
|
+
module Legion
|
|
10
|
+
module Extensions
|
|
11
|
+
module Github
|
|
12
|
+
module Runners
|
|
13
|
+
module Auth
|
|
14
|
+
include Legion::Extensions::Github::Helpers::Client
|
|
15
|
+
include Legion::Extensions::Github::App::Runners::Auth
|
|
16
|
+
include Legion::Extensions::Github::App::Runners::CredentialStore
|
|
17
|
+
include Legion::Extensions::Github::OAuth::Runners::Auth
|
|
18
|
+
|
|
19
|
+
def self.remote_invocable?
|
|
20
|
+
false
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def status(**)
|
|
24
|
+
cred = resolve_credential
|
|
25
|
+
unless cred
|
|
26
|
+
log.warn('[lex-github] auth status: no credential found across all sources')
|
|
27
|
+
return { result: { authenticated: false } }
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
log.info("[lex-github] auth status: credential found via #{cred[:auth_type]}")
|
|
31
|
+
|
|
32
|
+
user_info = {}
|
|
33
|
+
scopes = nil
|
|
34
|
+
begin
|
|
35
|
+
response = connection(token: cred[:token]).get('/user')
|
|
36
|
+
user_info = response.body || {}
|
|
37
|
+
headers = response.respond_to?(:headers) ? response.headers : {}
|
|
38
|
+
scopes_header = headers['X-OAuth-Scopes'] || headers['x-oauth-scopes']
|
|
39
|
+
scopes = scopes_header&.split(',')&.map(&:strip)
|
|
40
|
+
log.info("[lex-github] auth status: authenticated as #{user_info['login']} (#{cred[:auth_type]})")
|
|
41
|
+
rescue StandardError => e
|
|
42
|
+
log.warn("[lex-github] auth status: credential found but /user request failed: #{e.message}")
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
{ result: { authenticated: true, auth_type: cred[:auth_type],
|
|
46
|
+
user: user_info['login'], scopes: scopes } }
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def login(client_id: nil, scopes: nil, **)
|
|
50
|
+
cid = client_id || settings_client_id
|
|
51
|
+
unless cid
|
|
52
|
+
log.error('[lex-github] auth login: no client_id configured — set github.app.client_id in settings')
|
|
53
|
+
return { error: 'missing_config', description: 'Set github.app.client_id in settings' }
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
log.info("[lex-github] auth login: starting OAuth flow with client_id=#{cid[0..7]}...")
|
|
57
|
+
|
|
58
|
+
sc = scopes || settings_scopes
|
|
59
|
+
browser = Helpers::BrowserAuth.new(client_id: cid, scopes: sc)
|
|
60
|
+
result = browser.authenticate
|
|
61
|
+
|
|
62
|
+
if result[:error]
|
|
63
|
+
log.error("[lex-github] auth login failed: #{result[:error]} — #{result[:description]}")
|
|
64
|
+
return { result: nil, error: result[:error], description: result[:description] }
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
if result[:result]&.dig('access_token')
|
|
68
|
+
user = begin
|
|
69
|
+
current_user(token: result[:result]['access_token'])
|
|
70
|
+
rescue StandardError => e
|
|
71
|
+
log.warn("[lex-github] auth login: token obtained but /user lookup failed: #{e.message}")
|
|
72
|
+
'default'
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
log.info("[lex-github] auth login: authenticated as #{user}")
|
|
76
|
+
|
|
77
|
+
if respond_to?(:store_oauth_token, true)
|
|
78
|
+
store_oauth_token(
|
|
79
|
+
user: user,
|
|
80
|
+
access_token: result[:result]['access_token'],
|
|
81
|
+
refresh_token: result[:result]['refresh_token'],
|
|
82
|
+
expires_in: result[:result]['expires_in']
|
|
83
|
+
)
|
|
84
|
+
log.info("[lex-github] auth login: token stored for user=#{user}")
|
|
85
|
+
else
|
|
86
|
+
log.warn('[lex-github] auth login: store_oauth_token not available — token not persisted')
|
|
87
|
+
end
|
|
88
|
+
else
|
|
89
|
+
log.warn('[lex-github] auth login: OAuth completed but no access_token in response')
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
result
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def installations(**)
|
|
96
|
+
log.info('[lex-github] listing app installations')
|
|
97
|
+
list_installations(**)
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
private
|
|
101
|
+
|
|
102
|
+
def current_user(token:)
|
|
103
|
+
connection(token: token).get('/user').body['login']
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def settings_client_id
|
|
107
|
+
defined?(Legion::Settings) &&
|
|
108
|
+
(Legion::Settings.dig(:github, :oauth, :client_id) ||
|
|
109
|
+
Legion::Settings.dig(:github, :app, :client_id))
|
|
110
|
+
rescue StandardError => _e
|
|
111
|
+
nil
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def settings_scopes
|
|
115
|
+
defined?(Legion::Settings) && Legion::Settings.dig(:github, :oauth, :scopes)
|
|
116
|
+
rescue StandardError => _e
|
|
117
|
+
nil
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
include Legion::Extensions::Helpers::Lex if Legion::Extensions.const_defined?(:Helpers, false) &&
|
|
121
|
+
Legion::Extensions::Helpers.const_defined?(:Lex, false)
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
end
|
|
@@ -35,6 +35,7 @@ require 'legion/extensions/github/runners/actions'
|
|
|
35
35
|
require 'legion/extensions/github/runners/checks'
|
|
36
36
|
require 'legion/extensions/github/runners/releases'
|
|
37
37
|
require 'legion/extensions/github/runners/deployments'
|
|
38
|
+
require 'legion/extensions/github/runners/auth'
|
|
38
39
|
require 'legion/extensions/github/runners/repository_webhooks'
|
|
39
40
|
require 'legion/extensions/github/client'
|
|
40
41
|
require 'legion/extensions/github/cli/runner'
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: lex-github
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.3.
|
|
4
|
+
version: 0.3.3
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Esity
|
|
@@ -203,6 +203,7 @@ files:
|
|
|
203
203
|
- lib/legion/extensions/github/oauth/transport/exchanges/oauth.rb
|
|
204
204
|
- lib/legion/extensions/github/oauth/transport/queues/auth.rb
|
|
205
205
|
- lib/legion/extensions/github/runners/actions.rb
|
|
206
|
+
- lib/legion/extensions/github/runners/auth.rb
|
|
206
207
|
- lib/legion/extensions/github/runners/branches.rb
|
|
207
208
|
- lib/legion/extensions/github/runners/checks.rb
|
|
208
209
|
- lib/legion/extensions/github/runners/comments.rb
|