From 146c5c4583a80ecab41017749eb97aade600e365 Mon Sep 17 00:00:00 2001 From: gbarc-dt <133209247+gbarc-dt@users.noreply.github.com> Date: Tue, 27 Feb 2024 15:33:16 +0100 Subject: [PATCH] ruby upgrade (#21) * ruby up * rubocop * Update version.rb --------- Co-authored-by: Dominik --- .rubocop.yml | 61 ++++++++++++++ .ruby-version | 1 + .travis.yml | 8 +- Gemfile | 14 +++- Rakefile | 8 +- bin/console | 7 +- lib/travis/github_apps.rb | 47 +++++------ lib/travis/github_apps/version.rb | 4 +- spec/spec_helper.rb | 10 ++- spec/travis/github_apps_spec.rb | 127 +++++++++++++++--------------- travis-github_apps.gemspec | 50 ++++++------ 11 files changed, 212 insertions(+), 125 deletions(-) create mode 100644 .rubocop.yml create mode 100644 .ruby-version diff --git a/.rubocop.yml b/.rubocop.yml new file mode 100644 index 0000000..2eb2005 --- /dev/null +++ b/.rubocop.yml @@ -0,0 +1,61 @@ +require: rubocop-performance +require: rubocop-rspec + +Documentation: + Enabled: false +Metrics/ClassLength: + Enabled: false +Style/ClassAndModuleChildren: + Enabled: false +Metrics/LineLength: + Enabled: false +Metrics/MethodLength: + Max: 40 +Style/AsciiComments: + Enabled: false +Metrics/AbcSize: + Enabled: false +Style/GuardClause: + Enabled: false +Style/FormatStringToken: + Enabled: false +Lint/AssignmentInCondition: + Enabled: false +Style/IfUnlessModifier: + Enabled: false +Naming/MemoizedInstanceVariableName: + EnforcedStyleForLeadingUnderscores: required +Style/MultilineBlockChain: + Enabled: false +Lint/ConstantDefinitionInBlock: + Enabled: false +Naming/VariableNumber: + Enabled: false +Metrics/BlockLength: + Enabled: false +Lint/ImplicitStringConcatenation: + Enabled: false +Metrics/MethodLength: + Enabled: false +Style/StructInheritance: + Enabled: false +Lint/RescueException: + Enabled: false +Lint/SuppressedException: + Enabled: false +Naming/MemoizedInstanceVariableName: + Enabled: false +Style/SymbolProc: + Enabled: false +RSpec/MultipleMemoizedHelpers: + Enabled: false +RSpec/NamedSubject: + Enabled: false +RSpec/AnyInstance: + Enabled: false +RSpec/ContextWording: + Enabled: false +RSpec/ExampleLength: + Enabled: false +RSpec/MultipleExpectations: + Enabled: false diff --git a/.ruby-version b/.ruby-version new file mode 100644 index 0000000..be94e6f --- /dev/null +++ b/.ruby-version @@ -0,0 +1 @@ +3.2.2 diff --git a/.travis.yml b/.travis.yml index 9d3d8dd..ec9d634 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,11 +1,9 @@ language: ruby rvm: - - 2.5.5 + - 3.2.2 before_install: - - gem install bundler -v 1.16.1 + - gem install bundler -v 2.4.14 env: matrix: - - ACTIVESUPPORT_VERSION=3.2.22.5 - - ACTIVESUPPORT_VERSION=4.2.10 - - ACTIVESUPPORT_VERSION=5.2.0 + - ACTIVESUPPORT_VERSION=7.0.0 - ACTIVESUPPORT_VERSION=6.0.2.2 diff --git a/Gemfile b/Gemfile index 1907548..5d70124 100644 --- a/Gemfile +++ b/Gemfile @@ -1,7 +1,17 @@ -source "https://rubygems.org" +# frozen_string_literal: true -git_source(:github) {|repo_name| "https://github.com/#{repo_name}" } +source 'https://rubygems.org' + +git_source(:github) { |repo_name| "https://github.com/#{repo_name}" } # Specify your gem's dependencies in travis-github_apps.gemspec gemspec gem 'activesupport', ENV['ACTIVESUPPORT_VERSION'] if ENV['ACTIVESUPPORT_VERSION'] + +group :development, :test do + gem 'rubocop' + gem 'rubocop-performance' + gem 'rubocop-rspec' + gem 'simplecov' + gem 'simplecov-console' +end diff --git a/Rakefile b/Rakefile index b7e9ed5..82bb534 100644 --- a/Rakefile +++ b/Rakefile @@ -1,6 +1,8 @@ -require "bundler/gem_tasks" -require "rspec/core/rake_task" +# frozen_string_literal: true + +require 'bundler/gem_tasks' +require 'rspec/core/rake_task' RSpec::Core::RakeTask.new(:spec) -task :default => :spec +task default: :spec diff --git a/bin/console b/bin/console index fd9b6dc..50f203b 100755 --- a/bin/console +++ b/bin/console @@ -1,7 +1,8 @@ #!/usr/bin/env ruby +# frozen_string_literal: true -require "bundler/setup" -require "travis/github_apps" +require 'bundler/setup' +require 'travis/github_apps' # You can add fixtures and/or initialization code here to make experimenting # with your gem easier. You can also use a different console, if you like. @@ -10,5 +11,5 @@ require "travis/github_apps" # require "pry" # Pry.start -require "irb" +require 'irb' IRB.start(__FILE__) diff --git a/lib/travis/github_apps.rb b/lib/travis/github_apps.rb index f99b564..46b08aa 100644 --- a/lib/travis/github_apps.rb +++ b/lib/travis/github_apps.rb @@ -1,11 +1,13 @@ -require "travis/github_apps/version" +# frozen_string_literal: true + +require 'travis/github_apps/version' require 'active_support' require 'json' require 'jwt' require 'redis' require 'faraday' -require 'faraday_middleware' +require 'faraday/follow_redirects' module Travis # Object for working with GitHub Apps installations @@ -20,29 +22,29 @@ class GithubApps # GitHub allows JWT token to be valid up to 10 minutes. # We set this to 9 minutes, to be sure. # - JWT_TOKEN_TTL = 9*60 + JWT_TOKEN_TTL = 9 * 60 # GitHub Apps tokens have a maximum life of 60 minutes, but we'll use 40 to # allow a healthy buffer for timing issues, lag, etc. # - APP_TOKEN_TTL = 40*60 # 40 minutes in seconds + APP_TOKEN_TTL = 40 * 60 # 40 minutes in seconds attr_reader :accept_header, :cache, :installation_id, :debug, :repositories - def initialize(installation_id, config = {}, repositories = nil) + def initialize(installation_id, config = {}, repositories = nil) # rubocop:disable Metrics/CyclomaticComplexity # ID of the GitHub App. This value can be found on the "General Information" # page of the App. # # TODO: this value is set to those of the "travis-ci-staging" app for # development. # - @github_apps_id = ENV['GITHUB_APPS_ID'] || config[:apps_id] || 10131 + @github_apps_id = ENV['GITHUB_APPS_ID'] || config[:apps_id] || 10_131 - @github_api_endpoint = ENV['GITHUB_API_ENDPOINT'] || config[:api_endpoint] || "https://api.github.com" + @github_api_endpoint = ENV['GITHUB_API_ENDPOINT'] || config[:api_endpoint] || 'https://api.github.com' @github_private_pem = ENV['GITHUB_PRIVATE_PEM'] || config[:private_pem] || read_private_key_from_file @github_private_key = OpenSSL::PKey::RSA.new(@github_private_pem) - @accept_header = config.fetch(:accept_header, "application/vnd.github.machine-man-preview+json") + @accept_header = config.fetch(:accept_header, 'application/vnd.github.machine-man-preview+json') @installation_id = installation_id @repositories = repositories @cache = Redis.new(config[:redis]) if config[:redis] @@ -108,8 +110,8 @@ def fetch_new_access_token response = github_api_conn.post do |req| req.url "app/installations/#{installation_id}/access_tokens" req.headers['Authorization'] = "Bearer #{authorization_jwt}" - req.headers['Accept'] = "application/vnd.github.machine-man-preview+json" - req.body = JSON.dump({:repository_ids => repositories_list, :permissions => { :contents => "read" } }) if repositories + req.headers['Accept'] = 'application/vnd.github.machine-man-preview+json' + req.body = JSON.dump({ repository_ids: repositories_list, permissions: { contents: 'read' } }) if repositories end # We probably want to do something different than `raise` here but I don't @@ -121,8 +123,8 @@ def fetch_new_access_token # Parse the response for the token and expiration # - response_body = JSON.load(response.body) - github_access_token = response_body.fetch('token') + response_body = JSON.parse(response.body) + github_access_token = response_body.fetch('token') # Store the access_token in redis, with a computed expiration. We need to # recalculate this here instead of using APP_TOKEN_TTL because we don't @@ -139,7 +141,7 @@ def repositories_list end def authorization_jwt - JWT.encode(jwt_payload, @github_private_key, "RS256") + JWT.encode(jwt_payload, @github_private_key, 'RS256') end def jwt_payload(jwt_token_ttl: JWT_TOKEN_TTL) @@ -161,22 +163,23 @@ def jwt_payload(jwt_token_ttl: JWT_TOKEN_TTL) def github_api_conn @_github_api_conn ||= Faraday.new(url: @github_api_endpoint) do |f| f.response :logger if debug - f.use FaradayMiddleware::FollowRedirects, limit: 5 + f.response :follow_redirects, limit: 5 f.request :retry f.adapter Faraday.default_adapter - end + end end def read_cache - cache.get(cache_key) if cache + cache&.get(cache_key) end def write_cache(token) - cache.set(cache_key, token, ex: APP_TOKEN_TTL) if cache + cache&.set(cache_key, token, ex: APP_TOKEN_TTL) end def cache_key return "github_access_token_repo:#{installation_id}_#{repositories_list.join('-')}" if repositories + "github_access_token:#{installation_id}" end @@ -186,13 +189,13 @@ def cache_key # source control. If you need a copy of this for development, ask Kerri. # def read_private_key_from_file - pem_files_in_root = Dir["*.pem"] + pem_files_in_root = Dir['*.pem'] @_github_private_key ||= if pem_files_in_root.any? - File.read(pem_files_in_root.first) - else - raise "Sorry, can't find local pem file" - end + File.read(pem_files_in_root.first) + else + raise "Sorry, can't find local pem file" + end end end end diff --git a/lib/travis/github_apps/version.rb b/lib/travis/github_apps/version.rb index a4b8f18..1b5fee6 100644 --- a/lib/travis/github_apps/version.rb +++ b/lib/travis/github_apps/version.rb @@ -1,5 +1,7 @@ +# frozen_string_literal: true + module Travis class GithubApps - VERSION = "0.2.1" + VERSION = '0.3.0' end end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 017da0d..0662525 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,11 +1,15 @@ -require "bundler/setup" -require "travis/github_apps" +# frozen_string_literal: true + +require 'bundler/setup' +require 'travis/github_apps' +require 'simplecov' +SimpleCov.start RSpec.configure do |config| config.mock_with :mocha # Enable flags like --only-failures and --next-failure - config.example_status_persistence_file_path = ".rspec_status" + config.example_status_persistence_file_path = '.rspec_status' # Disable RSpec exposing methods globally on `Module` and `main` config.disable_monkey_patching! diff --git a/spec/travis/github_apps_spec.rb b/spec/travis/github_apps_spec.rb index f74786a..a76c128 100644 --- a/spec/travis/github_apps_spec.rb +++ b/spec/travis/github_apps_spec.rb @@ -1,66 +1,67 @@ +# frozen_string_literal: true + RSpec.describe Travis::GithubApps do - it "has a version number" do - expect(Travis::GithubApps::VERSION).not_to be nil - end + after { ENV.delete('GITHUB_PRIVATE_PEM') } + before { ENV['GITHUB_PRIVATE_PEM'] = File.read('spec/fixtures/github_pem.txt') } + let(:installation_id) { 12_345 } + let(:payload) do + { + # Note that Time.now is frozen in tests, so we can get away with multiple + # calls to it without drift. + # + iat: Time.now.to_i, + exp: Time.now.to_i + subject.class::JWT_TOKEN_TTL, + iss: subject.instance_variable_get(:@github_apps_id) + } + end + let(:subject) { described_class.new(installation_id, config) } let(:config) { { redis: { url: 'redis://localhost' } } } - let(:subject){ Travis::GithubApps.new(installation_id, config) } - let(:installation_id) { '12345' } - - let(:payload){ { - # Note that Time.now is frozen in tests, so we can get away with multiple - # calls to it without drift. - # - iat: Time.now.to_i, - exp: Time.now.to_i + subject.class::JWT_TOKEN_TTL, - iss: subject.instance_variable_get(:@github_apps_id) - }} - - let(:installation_id){ 12345 } - before { ENV['GITHUB_PRIVATE_PEM'] = File.read('spec/fixtures/github_pem.txt') } - after { ENV.delete('GITHUB_PRIVATE_PEM') } + it 'has a version number' do + expect(Travis::GithubApps::VERSION).not_to be nil + end - describe "#initialize" do - it "intializes and loads initial configuration values" do - expect(subject).to be + describe '#initialize' do + it 'intializes and loads initial configuration values' do + expect(subject).to be_a(described_class) # ivars # - expect(subject.instance_variable_get(:@github_apps_id)).to eq 10131 + expect(subject.instance_variable_get(:@github_apps_id)).to eq 10_131 expect(subject.instance_variable_get(:@github_private_pem)) - .to include("BEGIN RSA PRIVATE KEY") + .to include('BEGIN RSA PRIVATE KEY') expect(subject.instance_variable_get(:@github_private_key)) .to be_a OpenSSL::PKey::RSA end - context "when accept-header is specified" do - let(:subject) { Travis::GithubApps.new({accept_header: "application/vnd.github.antiope-preview+json"})} + context 'when accept-header is specified' do + let(:subject) { described_class.new({ accept_header: 'application/vnd.github.antiope-preview+json' }) } - it "intializes" do - expect(subject).to be + it 'intializes' do + expect(subject).to be_a(described_class) end end end - describe "#authorization_jwt" do - it "returns a JWT string" do + describe '#authorization_jwt' do + it 'returns a JWT string' do expect(subject.send(:authorization_jwt)).to be_a String end end - describe "#jwt_payload" do - it "formats a JWT payload correctly" do + describe '#jwt_payload' do + it 'formats a JWT payload correctly' do expect(subject.send(:jwt_payload)).to eq payload end end - describe "#access_token" do - context "when the access token exists in the cache" do - it "returns the value contained in the cache" do - random_string = ('a'..'z').to_a.shuffle[0,8].join + describe '#access_token' do + context 'when the access token exists in the cache' do + it 'returns the value contained in the cache' do + random_string = ('a'..'z').to_a.sample(8).join Redis.any_instance.stubs(:get).returns(random_string) @@ -68,11 +69,11 @@ end end - context "when the access token does not exist in the cache" do + context 'when the access token does not exist in the cache' do # This test is treading perilously close to testing that stubbing works # - it "returns the value from an API lookup" do - random_string = ('a'..'z').to_a.shuffle[0,8].join + it 'returns the value from an API lookup' do + random_string = ('a'..'z').to_a.sample(8).join Redis.any_instance.stubs(:get).returns(nil) @@ -83,64 +84,66 @@ end end - describe "#fetch_new_access_token" do - let(:conn) { + describe '#fetch_new_access_token' do + let(:conn) do Faraday.new do |builder| builder.adapter :test do |stub| - stub.post("/app/installations/12345/access_tokens") { |env| [201, {}, "{\"token\":\"github_apps_access_token\",\"expires_at\":\"2018-04-03T20:52:14Z\"}"] } - stub.post("/app/installations/23456/access_tokens") { |env| [404, {}, ""] } - stub.post("/app/installations/45678/access_tokens", JSON.dump({:repository_ids => ["123", "456"], :permissions => { :contents => "read" } })) { |env| [201, {}, "{\"token\":\"github_apps_access_token\",\"expires_at\":\"2018-04-03T20:52:14Z\",\"repositories\":\"[{id:123},{id:456}]\"}"] } - stub.post("/app/installations/56789/access_tokens", JSON.dump({:repository_ids => ["789"], :permissions => { :contents => "read" } })) { |env| [404, {}, ""] } + stub.post('/app/installations/12345/access_tokens') { |_env| [201, {}, '{"token":"github_apps_access_token","expires_at":"2018-04-03T20:52:14Z"}'] } + stub.post('/app/installations/23456/access_tokens') { |_env| [404, {}, ''] } + stub.post('/app/installations/45678/access_tokens', JSON.dump({ repository_ids: %w[123 456], permissions: { contents: 'read' } })) { |_env| [201, {}, '{"token":"github_apps_access_token","expires_at":"2018-04-03T20:52:14Z","repositories":"[{id:123},{id:456}]"}'] } + stub.post('/app/installations/56789/access_tokens', JSON.dump({ repository_ids: ['789'], permissions: { contents: 'read' } })) { |_env| [404, {}, ''] } end end - } + end - context "on a 201 response" do - it "sets the access token in the cache and returns it to the caller" do + context 'on a 201 response' do + it 'sets the access token in the cache and returns it to the caller' do subject.expects(:github_api_conn).returns(conn) Redis.any_instance.stubs(:set).returns(nil) - expect(subject.send(:fetch_new_access_token)).to eq "github_apps_access_token" + expect(subject.send(:fetch_new_access_token)).to eq 'github_apps_access_token' end end - context "with repository_ids on a 201 response" do + context 'with repository_ids on a 201 response' do let(:installation_id) { '45678' } let(:repositories) { [123, 456] } - let(:subject){ Travis::GithubApps.new(installation_id, config, repositories) } - it "sets the access token in the cache and returns it to the caller" do + let(:subject) { described_class.new(installation_id, config, repositories) } + + it 'sets the access token in the cache and returns it to the caller' do subject.expects(:github_api_conn).returns(conn) Redis.any_instance.stubs(:set).returns(nil) - expect(subject.send(:fetch_new_access_token)).to eq "github_apps_access_token" + expect(subject.send(:fetch_new_access_token)).to eq 'github_apps_access_token' end end - context "on a non-201 response" do + context 'on a non-201 response' do let(:installation_id) { '23456' } - it "raises an error" do + + it 'raises an error' do subject.expects(:github_api_conn).returns(conn) Redis.any_instance.stubs(:set).returns(nil) - expect { + expect do subject.send(:fetch_new_access_token) - }.to raise_error(RuntimeError) + end.to raise_error(RuntimeError) end end - context "with repository_ids on a non-201 response" do + context 'with repository_ids on a non-201 response' do let(:installation_id) { '56789' } let(:repositories) { [789] } - let(:subject){ Travis::GithubApps.new(installation_id, config, repositories) } - it "raises an error" do + let(:subject) { described_class.new(installation_id, config, repositories) } + + it 'raises an error' do subject.expects(:github_api_conn).returns(conn) Redis.any_instance.stubs(:set).returns(nil) - expect { + expect do subject.send(:fetch_new_access_token) - }.to raise_error(RuntimeError) + end.to raise_error(RuntimeError) end end - end end diff --git a/travis-github_apps.gemspec b/travis-github_apps.gemspec index 499e092..d6b1bf5 100644 --- a/travis-github_apps.gemspec +++ b/travis-github_apps.gemspec @@ -1,43 +1,45 @@ +# frozen_string_literal: true -lib = File.expand_path("../lib", __FILE__) +lib = File.expand_path('lib', __dir__) $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) -require "travis/github_apps/version" +require 'travis/github_apps/version' Gem::Specification.new do |spec| - spec.name = "travis-github_apps" + spec.name = 'travis-github_apps' spec.version = Travis::GithubApps::VERSION - spec.authors = ["Kerri Miller"] - spec.email = ["kerrizor@kerrizor.com"] + spec.authors = ['Kerri Miller'] + spec.email = ['kerrizor@kerrizor.com'] - spec.summary = %q{A small library for fetching, storing, and renewing GitHub Apps access tokens} - spec.description = %q{A small library for fetching, storing, and renewing GitHub Apps access tokens} - spec.homepage = "https://github.com/travis-ci/github_apps" - spec.license = "MIT" + spec.summary = 'A small library for fetching, storing, and renewing GitHub Apps access tokens' + spec.description = 'A small library for fetching, storing, and renewing GitHub Apps access tokens' + spec.homepage = 'https://github.com/travis-ci/github_apps' + spec.license = 'MIT' + spec.required_ruby_version = '~> 3.2' # Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host' # to allow pushing to a single host or delete this section to allow pushing to any host. if spec.respond_to?(:metadata) - spec.metadata["allowed_push_host"] = "TODO: Set to 'http://mygemserver.com'" + spec.metadata['allowed_push_host'] = "TODO: Set to 'http://mygemserver.com'" else - raise "RubyGems 2.0 or newer is required to protect against " \ - "public gem pushes." + raise 'RubyGems 2.0 or newer is required to protect against ' \ + 'public gem pushes.' end - spec.files = `git ls-files -z`.split("\x0").reject do |f| + spec.files = `git ls-files -z`.split("\x0").reject do |f| f.match(%r{^(test|spec|features)/}) end - spec.bindir = "exe" + spec.bindir = 'exe' spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) } - spec.require_paths = ["lib"] + spec.require_paths = ['lib'] - spec.add_development_dependency "bundler" - spec.add_development_dependency "mocha" - spec.add_development_dependency "rake", "~> 12.3" - spec.add_development_dependency "rspec", "~> 3.0" + spec.add_development_dependency 'bundler' + spec.add_development_dependency 'mocha' + spec.add_development_dependency 'rake', '~> 13' + spec.add_development_dependency 'rspec', '~> 3' - spec.add_dependency "activesupport", ">= 3.2" - spec.add_dependency "jwt" - spec.add_dependency "redis" - spec.add_dependency "faraday" - spec.add_dependency "faraday_middleware" + spec.add_dependency 'activesupport', '>= 3.2' + spec.add_dependency 'faraday', '~> 2' + spec.add_dependency 'faraday-follow_redirects' + spec.add_dependency 'jwt' + spec.add_dependency 'redis' end