github-authentication 1.1.0 → 1.3.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 232b7d332cfdf5a3b6ec3e6854b13203dcc204c4f242d730ea2a8510bb25164a
4
- data.tar.gz: 8715cff617884f40643eb2569704bca0ad3ce306277d0298806e550889d63b3f
3
+ metadata.gz: 516354d28c957aa1fe159319fe2c6ae1db1dbc50e3493a14b364f6c08526fc5a
4
+ data.tar.gz: 77810176f1496cef5171bbc279e0545d3a8624b268ef07f27a0ab3468a833d8f
5
5
  SHA512:
6
- metadata.gz: ab1d75961e37a620932f1cefe273c00bd95e96332368a13268b868114649d1d8b96c4133770adb898c4a2c1a3502d7c16fab3e840671cc7f90064d89ce6835c1
7
- data.tar.gz: 8ab8c0af8067b378f0a981173ff3e5ea53728c65cf6264a8ebeecd6fe8c30d75b739d89ec9aed893c292950bdc414ac2be46fe065eac48c722dbbbce98329f36
6
+ metadata.gz: 4bfaf85cdcb8a3c4c98369cea4ddb1bbea55a9ce1b4099529dbc898b2da80d158d8b82fc456088ad49575b4fe7c5183c3969fb5fbc40ad734e01a23af9f0ea6e
7
+ data.tar.gz: 87be1a5c672e5a02f55bb2665464e9df08efe1d1c79af56d2f0ef3d230229552af4770f0576a3ff9f3732a16de0e84be4b74b7c048212a72df66c00872f3a2ee
@@ -14,7 +14,6 @@ jobs:
14
14
  - name: Set up Ruby
15
15
  uses: ruby/setup-ruby@v1
16
16
  with:
17
- ruby-version: '2.7'
18
17
  bundler-cache: true
19
18
 
20
19
  - name: Lint Ruby
@@ -27,7 +26,7 @@ jobs:
27
26
  strategy:
28
27
  fail-fast: false
29
28
  matrix:
30
- ruby: [ '2.7', '3.0', '3.1', '3.2' ]
29
+ ruby: [ '3.2', '3.3', '3.4', '3.5', '4.0' ]
31
30
  steps:
32
31
  - uses: actions/checkout@v4
33
32
 
@@ -41,6 +40,7 @@ jobs:
41
40
  with:
42
41
  ruby-version: ${{ matrix.ruby }}
43
42
  bundler-cache: true
43
+ bundler: latest
44
44
 
45
45
  - name: Run Ruby tests
46
46
  run: |
data/.rubocop.yml CHANGED
@@ -2,7 +2,6 @@ inherit_gem:
2
2
  rubocop-shopify: rubocop.yml
3
3
 
4
4
  AllCops:
5
- TargetRubyVersion: 2.7
6
5
  CacheRootDirectory: tmp/rubocop-cache
7
6
  UseCache: true
8
7
 
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 4.0.1
data/CHANGELOG.md ADDED
@@ -0,0 +1,11 @@
1
+ ### Next
2
+
3
+ ...
4
+
5
+ ### 1.3.0
6
+ - Add `GithubAuthentication.provider(org:, env:)` as a high-level entrypoint for Ruby code that needs GitHub App tokens without manually wiring up Environment, Generator, Cache, and Provider
7
+ - Simplify `GitCredentialHelper` to accept a `provider` directly
8
+ - Update test runners to test against more recent Ruby versions
9
+
10
+ ### 1.2.0
11
+ - Support multi-org credentials (https://github.com/Shopify/github-authentication/pull/34)
data/Gemfile.lock CHANGED
@@ -1,82 +1,96 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- github-authentication (1.1.0)
4
+ github-authentication (1.3.0)
5
5
  jwt (~> 2.2)
6
6
 
7
7
  GEM
8
8
  remote: https://rubygems.org/
9
9
  specs:
10
- activesupport (7.1.2)
10
+ activesupport (8.1.2)
11
11
  base64
12
12
  bigdecimal
13
- concurrent-ruby (~> 1.0, >= 1.0.2)
13
+ concurrent-ruby (~> 1.0, >= 1.3.1)
14
14
  connection_pool (>= 2.2.5)
15
15
  drb
16
16
  i18n (>= 1.6, < 2)
17
+ json
18
+ logger (>= 1.4.2)
17
19
  minitest (>= 5.1)
18
- mutex_m
19
- tzinfo (~> 2.0)
20
- addressable (2.8.5)
21
- public_suffix (>= 2.0.2, < 6.0)
22
- ast (2.4.2)
23
- base64 (0.2.0)
24
- bigdecimal (3.1.4)
25
- concurrent-ruby (1.2.2)
26
- connection_pool (2.4.1)
27
- crack (0.4.5)
20
+ securerandom (>= 0.3)
21
+ tzinfo (~> 2.0, >= 2.0.5)
22
+ uri (>= 0.13.1)
23
+ addressable (2.8.8)
24
+ public_suffix (>= 2.0.2, < 8.0)
25
+ ast (2.4.3)
26
+ base64 (0.3.0)
27
+ bigdecimal (4.0.1)
28
+ concurrent-ruby (1.3.6)
29
+ connection_pool (3.0.2)
30
+ crack (1.0.1)
31
+ bigdecimal
28
32
  rexml
29
- drb (2.2.0)
30
- ruby2_keywords
31
- hashdiff (1.0.1)
32
- i18n (1.14.1)
33
+ drb (2.2.3)
34
+ hashdiff (1.2.1)
35
+ i18n (1.14.8)
33
36
  concurrent-ruby (~> 1.0)
34
- json (2.6.3)
35
- jwt (2.7.1)
36
- language_server-protocol (3.17.0.3)
37
- minitest (5.20.0)
38
- mocha (2.1.0)
37
+ json (2.18.1)
38
+ jwt (2.10.2)
39
+ base64
40
+ language_server-protocol (3.17.0.5)
41
+ lint_roller (1.1.0)
42
+ logger (1.7.0)
43
+ minitest (5.27.0)
44
+ mocha (2.8.2)
39
45
  ruby2_keywords (>= 0.0.5)
40
- mutex_m (0.2.0)
41
- parallel (1.23.0)
42
- parser (3.2.2.4)
46
+ parallel (1.27.0)
47
+ parser (3.3.10.1)
43
48
  ast (~> 2.4.1)
44
49
  racc
45
- public_suffix (5.0.4)
46
- racc (1.7.3)
50
+ prism (1.9.0)
51
+ public_suffix (7.0.2)
52
+ racc (1.8.1)
47
53
  rainbow (3.1.1)
48
- rake (13.1.0)
49
- regexp_parser (2.8.2)
50
- rexml (3.2.6)
51
- rubocop (1.57.2)
54
+ rake (13.3.1)
55
+ regexp_parser (2.11.3)
56
+ rexml (3.4.4)
57
+ rubocop (1.84.2)
52
58
  json (~> 2.3)
53
- language_server-protocol (>= 3.17.0)
59
+ language_server-protocol (~> 3.17.0.2)
60
+ lint_roller (~> 1.1.0)
54
61
  parallel (~> 1.10)
55
- parser (>= 3.2.2.4)
62
+ parser (>= 3.3.0.2)
56
63
  rainbow (>= 2.2.2, < 4.0)
57
- regexp_parser (>= 1.8, < 3.0)
58
- rexml (>= 3.2.5, < 4.0)
59
- rubocop-ast (>= 1.28.1, < 2.0)
64
+ regexp_parser (>= 2.9.3, < 3.0)
65
+ rubocop-ast (>= 1.49.0, < 2.0)
60
66
  ruby-progressbar (~> 1.7)
61
- unicode-display_width (>= 2.4.0, < 3.0)
62
- rubocop-ast (1.30.0)
63
- parser (>= 3.2.1.0)
64
- rubocop-shopify (2.14.0)
65
- rubocop (~> 1.51)
67
+ unicode-display_width (>= 2.4.0, < 4.0)
68
+ rubocop-ast (1.49.0)
69
+ parser (>= 3.3.7.2)
70
+ prism (~> 1.7)
71
+ rubocop-shopify (2.18.0)
72
+ rubocop (~> 1.62)
66
73
  ruby-progressbar (1.13.0)
67
74
  ruby2_keywords (0.0.5)
68
- timecop (0.9.8)
75
+ securerandom (0.4.1)
76
+ timecop (0.9.10)
69
77
  tzinfo (2.0.6)
70
78
  concurrent-ruby (~> 1.0)
71
- unicode-display_width (2.5.0)
72
- vcr (6.2.0)
73
- webmock (3.19.1)
79
+ unicode-display_width (3.2.0)
80
+ unicode-emoji (~> 4.1)
81
+ unicode-emoji (4.2.0)
82
+ uri (1.1.1)
83
+ vcr (6.4.0)
84
+ webmock (3.26.1)
74
85
  addressable (>= 2.8.0)
75
86
  crack (>= 0.3.2)
76
87
  hashdiff (>= 0.4.0, < 2.0.0)
77
88
 
78
89
  PLATFORMS
90
+ arm64-darwin-21
79
91
  arm64-darwin-22
92
+ arm64-darwin-23
93
+ arm64-darwin-24
80
94
  x86_64-linux
81
95
 
82
96
  DEPENDENCIES
@@ -91,4 +105,4 @@ DEPENDENCIES
91
105
  webmock (~> 3.8)
92
106
 
93
107
  BUNDLED WITH
94
- 2.4.21
108
+ 2.3.8
data/README.md CHANGED
@@ -22,32 +22,43 @@ Or install it yourself as:
22
22
 
23
23
  ## Usage
24
24
 
25
+ The simplest way to get a GitHub App token is via `GithubAuthentication.provider`, which reads credentials from environment variables, handles JWT generation, token exchange, and caching:
26
+
25
27
  ```ruby
26
28
  require 'github-authentication'
27
29
 
28
- cache = GithubAuthentication::Cache.new(storage: GithubAuthentication::ObjectCache.new)
29
- generator = GithubAuthentication::Generator::App.new(pem: ENV['GITHUB_PEM'],
30
- installation_id: ENV['GITHUB_INSTALLATION_ID'],
31
- app_id: ENV['GITHUB_APP_ID'])
32
- provider = GithubAuthentication::Provider.new(generator: generator, cache: cache)
33
-
34
- provider.token
35
- provider.reset_token
30
+ provider = GithubAuthentication.provider(org: "myorg")
31
+ provider.token # => returns a cached or freshly generated token
36
32
  ```
37
33
 
38
- ### Cache
34
+ This expects the following environment variables to be set (optionally prefixed with the org name):
39
35
 
40
- The cache takes a storage argument. You can pass an instance of an `ActiveSupport::Cache` implementation or use the provided
41
- `GithubAuthentication::ObjectCache` if you are using it in a script.
36
+ - `GITHUB_APP_ID` (or `MYORG_GITHUB_APP_ID`)
37
+ - `GITHUB_APP_INSTALLATION_ID` (or `MYORG_GITHUB_APP_INSTALLATION_ID`)
38
+ - `GITHUB_APP_KEYFILE` (or `MYORG_GITHUB_APP_KEYFILE`)
42
39
 
43
- ### Generator::App
40
+ If `GITHUB_APP_CREDENTIAL_STORAGE_PATH` is set, tokens are cached to disk via `ActiveSupport::Cache::FileStore`. Otherwise an in-memory cache is used.
44
41
 
45
- Generates a token for a GitHub app.
42
+ ### Using with Octokit
46
43
 
47
44
  ```ruby
48
- GithubAuthentication::Generator::App.new(pem: ENV['GITHUB_PEM'],
45
+ provider = GithubAuthentication.provider(org: "myorg")
46
+ client = Octokit::Client.new(access_token: provider.token.to_s)
47
+ ```
48
+
49
+ ### Building a provider manually
50
+
51
+ If you need more control over the cache or generator, you can wire up the components yourself:
52
+
53
+ ```ruby
54
+ cache = GithubAuthentication::Cache.new(storage: GithubAuthentication::ObjectCache.new)
55
+ generator = GithubAuthentication::Generator::App.new(pem: ENV['GITHUB_PEM'],
49
56
  installation_id: ENV['GITHUB_INSTALLATION_ID'],
50
57
  app_id: ENV['GITHUB_APP_ID'])
58
+ provider = GithubAuthentication::Provider.new(generator: generator, cache: cache)
59
+
60
+ provider.token
61
+ provider.reset_token
51
62
  ```
52
63
 
53
64
  ### Generator::Personal
@@ -57,45 +68,6 @@ Mostly for testing purposes you can provide a github token that gets retrieved.
57
68
  GithubAuthentication::Generator::Personal.new(github_token: ENV['GITHUB_TOKEN'])
58
69
  ```
59
70
 
60
- ## Example
61
-
62
- ```ruby
63
-
64
- require "base64"
65
-
66
- module GitHub
67
- APP_ID = "<APP_ID>"
68
- INSTALLATION_ID = "<INSTALLATION_ID>"
69
-
70
- class << self
71
- def token
72
- @token_provider ||= begin
73
- if ENV['GITHUB_TOKEN']
74
- storage = GithubAuthentication::ObjectCache.new
75
- generator = GithubAuthentication::Generator::Personal.new(github_token: ENV['GITHUB_TOKEN'])
76
- else
77
- storage = ActiveSupport::Cache::RedisCacheStore.new
78
- pem = Base64.decode64(ENV['GITHUB_PEM'])
79
- generator = GithubAuthentication::Generator::App.new(pem: pem, installation_id: INSTALLATION_ID,
80
- app_id: APP_ID)
81
- end
82
- cache = GithubAuthentication::Cache.new(storage: storage)
83
- GithubAuthentication::Provider.new(generator: generator, cache: cache)
84
- end
85
- @token_provider.token
86
- end
87
-
88
- def client
89
- if ENV['GITHUB_TOKEN']
90
- Octokit::Client.new(access_token: token.to_s)
91
- else
92
- Octokit::Client.new(bearer_token: token.to_s)
93
- end
94
- end
95
- end
96
- end
97
- ```
98
-
99
71
  ## Git credential helper
100
72
 
101
73
  This gem also ships with a [git credential helper][0] to authenticate git
@@ -14,11 +14,13 @@ end
14
14
 
15
15
  case ARGV[0]
16
16
  when "get"
17
+ description = $stdin.each_line.map { |line| line.split("=", 2).map(&:strip) }.to_h
18
+ org = description.fetch("path", "").split("/").first
19
+ provider = GithubAuthentication.provider(org: org)
20
+
17
21
  exit_status = GithubAuthentication::GitCredentialHelper.new(
18
- pem: File.read(ENV.fetch("GITHUB_APP_KEYFILE")),
19
- app_id: ENV.fetch("GITHUB_APP_ID"),
20
- installation_id: ENV.fetch("GITHUB_APP_INSTALLATION_ID"),
21
- storage: ActiveSupport::Cache::FileStore.new(ENV.fetch("GITHUB_APP_CREDENTIAL_STORAGE_PATH")),
22
+ provider: provider,
23
+ description: description,
22
24
  ).handle_get
23
25
  exit(exit_status)
24
26
  when "store"
@@ -37,6 +37,8 @@ Gem::Specification.new do |spec|
37
37
 
38
38
  spec.add_dependency("jwt", "~> 2.2")
39
39
 
40
+ spec.required_ruby_version = ">= 2.7.0"
41
+
40
42
  spec.add_development_dependency("activesupport")
41
43
  spec.add_development_dependency("minitest", "~> 5.0")
42
44
  spec.add_development_dependency("mocha", "~> 2")
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/core_ext/object/blank.rb"
4
+
5
+ module GithubAuthentication
6
+ class Environment
7
+ def initialize(org:, env: ENV)
8
+ @org = org.presence
9
+ @env = env
10
+ end
11
+
12
+ def pem
13
+ File.read(resolve("GITHUB_APP_KEYFILE"))
14
+ end
15
+
16
+ def app_id
17
+ resolve("GITHUB_APP_ID")
18
+ end
19
+
20
+ def installation_id
21
+ resolve("GITHUB_APP_INSTALLATION_ID")
22
+ end
23
+
24
+ def storage
25
+ ActiveSupport::Cache::FileStore.new(resolve("GITHUB_APP_CREDENTIAL_STORAGE_PATH"))
26
+ end
27
+
28
+ private
29
+
30
+ def resolve(suffix)
31
+ if @org
32
+ @env["#{@org.upcase}_#{suffix}"] || @env.fetch(suffix)
33
+ else
34
+ @env.fetch(suffix)
35
+ end
36
+ end
37
+ end
38
+ end
@@ -2,23 +2,18 @@
2
2
 
3
3
  module GithubAuthentication
4
4
  class GitCredentialHelper
5
- def initialize(pem:, installation_id:, app_id:, storage: nil, stdin: $stdin)
6
- @pem = pem
7
- @installation_id = installation_id
8
- @app_id = app_id
9
- @storage = storage
10
- @stdin = stdin
5
+ def initialize(provider:, description:)
6
+ @provider = provider
7
+ @description = description
11
8
  end
12
9
 
13
10
  def handle_get
14
- description = parse_stdin
15
-
16
- unless description["protocol"] == "https" && description["host"] == "github.com"
17
- warn("Unsupported description: #{description}")
11
+ unless @description["protocol"] == "https" && @description["host"] == "github.com"
12
+ warn("Unsupported description: #{@description}")
18
13
  return 2
19
14
  end
20
15
 
21
- token = provider.token(seconds_ttl: min_cache_ttl)
16
+ token = @provider.token(seconds_ttl: min_cache_ttl)
22
17
  puts("password=#{token}")
23
18
  puts("username=api")
24
19
 
@@ -31,26 +26,5 @@ module GithubAuthentication
31
26
  # Tokens are valid for 60 minutes, allow a 10 minute buffer
32
27
  10 * 60
33
28
  end
34
-
35
- def parse_stdin
36
- # Credential description is written to STDIN in line delimited key=value form,
37
- # see https://git-scm.com/docs/git-credential#IOFMT
38
- @stdin.each_line.map { |line| line.split("=", 2).map(&:strip) }.to_h
39
- end
40
-
41
- def provider
42
- @provider ||= Provider.new(
43
- generator: generator,
44
- cache: Cache.new(storage: @storage || ObjectCache.new),
45
- )
46
- end
47
-
48
- def generator
49
- @generator ||= Generator::App.new(
50
- pem: @pem,
51
- app_id: @app_id,
52
- installation_id: @installation_id,
53
- )
54
- end
55
29
  end
56
30
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module GithubAuthentication
4
- VERSION = "1.1.0"
4
+ VERSION = "1.3.0"
5
5
  end
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "github_authentication/version"
4
+ require "github_authentication/environment"
4
5
  require "github_authentication/generator"
5
6
  require "github_authentication/provider"
6
7
  require "github_authentication/cache"
@@ -8,4 +9,21 @@ require "github_authentication/object_cache"
8
9
  require "github_authentication/git_credential_helper"
9
10
 
10
11
  module GithubAuthentication
12
+ class << self
13
+ def provider(org:, env: ENV)
14
+ ga_env = Environment.new(org: org, env: env)
15
+ generator = Generator::App.new(
16
+ pem: ga_env.pem,
17
+ installation_id: ga_env.installation_id,
18
+ app_id: ga_env.app_id,
19
+ )
20
+ storage = begin
21
+ ga_env.storage
22
+ rescue KeyError
23
+ ObjectCache.new
24
+ end
25
+ cache = Cache.new(storage: storage, key: org)
26
+ Provider.new(generator: generator, cache: cache)
27
+ end
28
+ end
11
29
  end
metadata CHANGED
@@ -1,14 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: github-authentication
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.0
4
+ version: 1.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Frederik Dudzik
8
- autorequire:
9
8
  bindir: exe
10
9
  cert_chain: []
11
- date: 2023-11-20 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
13
  name: jwt
@@ -147,6 +146,8 @@ files:
147
146
  - ".github/workflows/tests.yml"
148
147
  - ".gitignore"
149
148
  - ".rubocop.yml"
149
+ - ".ruby-version"
150
+ - CHANGELOG.md
150
151
  - Gemfile
151
152
  - Gemfile.lock
152
153
  - LICENSE.txt
@@ -162,6 +163,7 @@ files:
162
163
  - lib/github-authentication.rb
163
164
  - lib/github_authentication.rb
164
165
  - lib/github_authentication/cache.rb
166
+ - lib/github_authentication/environment.rb
165
167
  - lib/github_authentication/generator.rb
166
168
  - lib/github_authentication/generator/app.rb
167
169
  - lib/github_authentication/generator/personal.rb
@@ -179,7 +181,6 @@ metadata:
179
181
  homepage_uri: https://github.com/Shopify/github-authentication
180
182
  source_code_uri: https://github.com/Shopify/github-authentication
181
183
  allowed_push_host: https://rubygems.org
182
- post_install_message:
183
184
  rdoc_options: []
184
185
  require_paths:
185
186
  - lib
@@ -187,15 +188,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
187
188
  requirements:
188
189
  - - ">="
189
190
  - !ruby/object:Gem::Version
190
- version: '0'
191
+ version: 2.7.0
191
192
  required_rubygems_version: !ruby/object:Gem::Requirement
192
193
  requirements:
193
194
  - - ">="
194
195
  - !ruby/object:Gem::Version
195
196
  version: '0'
196
197
  requirements: []
197
- rubygems_version: 3.4.21
198
- signing_key:
198
+ rubygems_version: 4.0.6
199
199
  specification_version: 4
200
200
  summary: GitHub Authetication
201
201
  test_files: []