diff --git a/.rubocop.yml b/.rubocop.yml new file mode 100644 index 0000000000..379a703682 --- /dev/null +++ b/.rubocop.yml @@ -0,0 +1,32 @@ +require: rubocop-performance + +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 diff --git a/.ruby-version b/.ruby-version index 8e8299dcc0..be94e6f53d 100644 --- a/.ruby-version +++ b/.ruby-version @@ -1 +1 @@ -2.4.2 +3.2.2 diff --git a/Dockerfile b/Dockerfile index 47853d60e2..62ca5e5bff 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM ruby:2.4 +FROM ruby:3.2.2 LABEL maintainer Travis CI GmbH diff --git a/Gemfile b/Gemfile index 310f476c44..721522ec7e 100644 --- a/Gemfile +++ b/Gemfile @@ -1,28 +1,35 @@ -ruby "~> 2.4.2" +# frozen_string_literal: true + +ruby '~> 3.2.2' source 'https://rubygems.org' -gem 'travis-web', path: 'waiter' -gem 'puma', '~> 3.12.4' -gem 'rack-ssl', '~> 1.4' -gem 'rack-protection', '~> 1.4' -gem 'rack-mobile-detect' -gem 'sinatra' gem 'hashr' +gem 'puma', '~> 6' +gem 'rack-mobile-detect' +gem 'rack-protection', '~> 3.0' +gem 'rack-ssl', '~> 1.4' gem 'sanitize' +gem 'sinatra' +gem 'travis-web', path: 'waiter' group :development, :test do gem 'rake' end - group :development do # gem 'debugger' gem 'foreman' + gem 'rubocop' + gem 'rubocop-performance' + gem 'rubocop-rspec' + gem 'simplecov' + gem 'simplecov-console' end group :test do - gem 'rspec', '~> 2.11' - gem 'test-unit' + gem 'rspec', '~> 3.12' + gem 'rack-test' gem 'sinatra-contrib' + gem 'test-unit' end diff --git a/Gemfile.lock b/Gemfile.lock index ca0403923c..f6ef837989 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -6,78 +6,138 @@ PATH GEM remote: https://rubygems.org/ specs: - backports (3.6.8) + ansi (1.5.0) + ast (2.4.2) crass (1.0.6) - diff-lcs (1.2.5) - foreman (0.82.0) - thor (~> 0.19.1) - hashr (2.0.0) - mini_portile2 (2.4.0) - multi_json (1.12.1) - nokogiri (1.10.9) - mini_portile2 (~> 2.4.0) - nokogumbo (2.0.2) - nokogiri (~> 1.8, >= 1.8.4) - power_assert (0.4.1) - puma (3.12.6) - rack (1.6.12) + diff-lcs (1.5.0) + docile (1.4.0) + foreman (0.87.2) + hashr (2.0.1) + json (2.6.3) + language_server-protocol (3.17.0.3) + multi_json (1.15.0) + mustermann (3.0.0) + ruby2_keywords (~> 0.0.1) + nio4r (2.5.9) + nokogiri (1.15.2-x86_64-linux) + racc (~> 1.4) + parallel (1.23.0) + parser (3.2.2.3) + ast (~> 2.4.1) + racc + power_assert (2.0.3) + puma (6.3.0) + nio4r (~> 2.0) + racc (1.7.1) + rack (2.2.7) rack-mobile-detect (0.4.0) rack - rack-protection (1.5.5) + rack-protection (3.0.6) rack rack-ssl (1.4.1) rack - rack-test (0.6.3) - rack (>= 1.0) - rake (12.3.3) - rspec (2.99.0) - rspec-core (~> 2.99.0) - rspec-expectations (~> 2.99.0) - rspec-mocks (~> 2.99.0) - rspec-core (2.99.2) - rspec-expectations (2.99.2) - diff-lcs (>= 1.1.3, < 2.0) - rspec-mocks (2.99.4) - sanitize (5.2.1) + rack-test (2.1.0) + rack (>= 1.3) + rainbow (3.1.1) + rake (13.0.6) + regexp_parser (2.8.1) + rexml (3.2.5) + rspec (3.12.0) + rspec-core (~> 3.12.0) + rspec-expectations (~> 3.12.0) + rspec-mocks (~> 3.12.0) + rspec-core (3.12.2) + rspec-support (~> 3.12.0) + rspec-expectations (3.12.3) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.12.0) + rspec-mocks (3.12.5) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.12.0) + rspec-support (3.12.1) + rubocop (1.54.0) + json (~> 2.3) + language_server-protocol (>= 3.17.0) + parallel (~> 1.10) + parser (>= 3.2.2.3) + rainbow (>= 2.2.2, < 4.0) + regexp_parser (>= 1.8, < 3.0) + rexml (>= 3.2.5, < 4.0) + rubocop-ast (>= 1.28.0, < 2.0) + ruby-progressbar (~> 1.7) + unicode-display_width (>= 2.4.0, < 3.0) + rubocop-ast (1.29.0) + parser (>= 3.2.1.0) + rubocop-capybara (2.18.0) + rubocop (~> 1.41) + rubocop-factory_bot (2.23.1) + rubocop (~> 1.33) + rubocop-performance (1.18.0) + rubocop (>= 1.7.0, < 2.0) + rubocop-ast (>= 0.4.0) + rubocop-rspec (2.22.0) + rubocop (~> 1.33) + rubocop-capybara (~> 2.17) + rubocop-factory_bot (~> 2.22) + ruby-progressbar (1.13.0) + ruby2_keywords (0.0.5) + sanitize (6.0.1) crass (~> 1.0.2) - nokogiri (>= 1.8.0) - nokogumbo (~> 2.0) - sinatra (1.4.8) - rack (~> 1.5) - rack-protection (~> 1.4) - tilt (>= 1.3, < 3) - sinatra-contrib (1.4.7) - backports (>= 2.0) + nokogiri (>= 1.12.0) + simplecov (0.22.0) + docile (~> 1.1) + simplecov-html (~> 0.11) + simplecov_json_formatter (~> 0.1) + simplecov-console (0.9.1) + ansi + simplecov + terminal-table + simplecov-html (0.12.3) + simplecov_json_formatter (0.1.4) + sinatra (3.0.6) + mustermann (~> 3.0) + rack (~> 2.2, >= 2.2.4) + rack-protection (= 3.0.6) + tilt (~> 2.0) + sinatra-contrib (3.0.6) multi_json - rack-protection - rack-test - sinatra (~> 1.4.0) - tilt (>= 1.3, < 3) - test-unit (3.2.3) + mustermann (~> 3.0) + rack-protection (= 3.0.6) + sinatra (= 3.0.6) + tilt (~> 2.0) + terminal-table (3.0.2) + unicode-display_width (>= 1.1.1, < 3) + test-unit (3.6.1) power_assert - thor (0.19.1) - tilt (2.0.8) + tilt (2.2.0) + unicode-display_width (2.4.2) PLATFORMS - ruby + x86_64-linux DEPENDENCIES foreman hashr - puma (~> 3.12.4) + puma (~> 6) rack-mobile-detect - rack-protection (~> 1.4) + rack-protection (~> 3.0) rack-ssl (~> 1.4) + rack-test rake - rspec (~> 2.11) + rspec (~> 3.12) + rubocop + rubocop-performance + rubocop-rspec sanitize + simplecov + simplecov-console sinatra sinatra-contrib test-unit travis-web! RUBY VERSION - ruby 2.4.2p198 + ruby 3.2.2p53 BUNDLED WITH - 1.17.3 + 2.4.14 diff --git a/app/components/profile-nav.js b/app/components/profile-nav.js index 3dcd807d58..4548f3ad24 100644 --- a/app/components/profile-nav.js +++ b/app/components/profile-nav.js @@ -73,7 +73,7 @@ export default Component.extend({ isOrganizationAdmin: and('isOrganization', 'hasAdminPermissions'), showOrganizationSettings: computed('isOrganizationAdmin', 'isProVersion', 'hasSettingsReadPermissions', function () { const forOrganization = !this.isOrganization || this.hasSettingsReadPermissions; - return this.isOrganizationAdmin && this.isProVersion && forOrganization; + return (this.isOrganizationAdmin || forOrganization) && this.isProVersion; }), showSubscriptionTab: computed('features.enterpriseVersion', 'hasPlanViewPermissions', diff --git a/app/templates/components/multi-signin-button.hbs b/app/templates/components/multi-signin-button.hbs index 0a556e55a4..fe868d54a0 100644 --- a/app/templates/components/multi-signin-button.hbs +++ b/app/templates/components/multi-signin-button.hbs @@ -72,4 +72,4 @@ -{{/if}} \ No newline at end of file +{{/if}} diff --git a/app/templates/components/profile-nav.hbs b/app/templates/components/profile-nav.hbs index eafbdcbb48..654bb441dc 100644 --- a/app/templates/components/profile-nav.hbs +++ b/app/templates/components/profile-nav.hbs @@ -167,7 +167,7 @@ {{/if}} - {{#if (and this.showSubscriptionTab this.isOrganizationAdmin)}} + {{#if this.showSubscriptionTab}}
  • Plan diff --git a/config/environment.js b/config/environment.js index a6174fdcdb..e6807c5de3 100644 --- a/config/environment.js +++ b/config/environment.js @@ -154,7 +154,7 @@ module.exports = function (environment) { hidePostalCode: true, style: { base: { - fontStyle: 'Source Sans Pro', + fontStyle: 'sans-serif', fontSize: '15px', color: '#666', '::placeholder': { diff --git a/waiter/Rakefile b/waiter/Rakefile index bcfc8d23b5..d01b610fd2 100644 --- a/waiter/Rakefile +++ b/waiter/Rakefile @@ -1,6 +1,10 @@ -$: << 'lib' +# frozen_string_literal: true + +$LOAD_PATH << 'lib' task :update_emojis do - s = Dir.glob('assets/images/emoji/*.png').map {|png| png.split('/', 4)[3].gsub('.png', '')}.map{|png| "'#{png}'"}.join(", ") + s = Dir.glob('assets/images/emoji/*.png').map do |png| + png.split('/', 4)[3].gsub('.png', '') + end.map { |png| "'#{png}'" }.join(', ') e = "@EmojiDictionary = [#{s}]" - File.open("assets/scripts/config/emoij.coffee", "w") {|f| f.write(e) } + File.open('assets/scripts/config/emoij.coffee', 'w') { |f| f.write(e) } end diff --git a/waiter/config.ru b/waiter/config.ru index 1c89b3ae66..78c8371a21 100644 --- a/waiter/config.ru +++ b/waiter/config.ru @@ -1,11 +1,13 @@ +# frozen_string_literal: true + # Make sure we set that before everything ENV['RACK_ENV'] ||= ENV['RAILS_ENV'] || ENV['ENV'] ENV['RAILS_ENV'] = ENV['RACK_ENV'] -$: << 'lib' +$LOAD_PATH << 'lib' require 'travis/web' -class RedirectSubdomain < Struct.new(:app, :from) +RedirectSubdomain = Struct.new(:app, :from) do def call(env) request = Rack::Request.new(env) if request.host == from @@ -16,10 +18,10 @@ class RedirectSubdomain < Struct.new(:app, :from) end end -class RedirectPages < Struct.new(:app, :from, :to, :page) +RedirectPages = Struct.new(:app, :from, :to, :page) do def call(env) request = Rack::Request.new(env) - if request.host == from && request.fullpath == page + if request.host == from && request.fullpath == page [301, { 'Location' => "https://#{to}#{request.fullpath}", 'Content-Type' => 'text/html' }, []] else app.call(env) @@ -28,34 +30,32 @@ class RedirectPages < Struct.new(:app, :from, :to, :page) end if ENV['TRAVIS_PRO'] - ENV['API_ENDPOINT'] ||= "https://api.travis-ci.com" - ENV['PAGES_ENDPOINT'] ||= "https://travis-ci.com/account/plan" - ENV['BILLING_ENDPOINT'] ||= "https://travis-ci.com/account/plan" + ENV['API_ENDPOINT'] ||= 'https://api.travis-ci.com' + ENV['PAGES_ENDPOINT'] ||= 'https://travis-ci.com/account/plan' + ENV['BILLING_ENDPOINT'] ||= 'https://travis-ci.com/account/plan' - ENV['SSH_KEY_ENABLED'] = 'true' unless ENV.has_key?('SSH_KEY_ENABLED') - ENV['CACHES_ENABLED'] = 'true' unless ENV.has_key?('CACHES_ENABLED') + ENV['SSH_KEY_ENABLED'] = 'true' unless ENV.key?('SSH_KEY_ENABLED') + ENV['CACHES_ENABLED'] = 'true' unless ENV.key?('CACHES_ENABLED') - ENV['PUSHER_KEY'] ||= "59236bc0716a551eab40" - ENV['GA_CODE'] ||= "UA-24868285-5" + ENV['PUSHER_KEY'] ||= '59236bc0716a551eab40' + ENV['GA_CODE'] ||= 'UA-24868285-5' - ENV['REDIRECT_FROM'] ||= "travis-ci.org" - ENV['REDIRECT_TO'] ||= "app.travis-ci.com" - ENV['TRAVIS_WP_SITE'] ||= "www.travis-ci.com" + ENV['REDIRECT_FROM'] ||= 'travis-ci.org' + ENV['REDIRECT_TO'] ||= 'app.travis-ci.com' + ENV['TRAVIS_WP_SITE'] ||= 'www.travis-ci.com' end -unless ENV['TRAVIS_PRO'] - if ENV['REDIRECT'] - use RedirectSubdomain, 'secure.travis-ci.org' - use RedirectPages, ENV['REDIRECT_FROM'], ENV['REDIRECT_TO'], '/signin' - use RedirectPages, ENV['REDIRECT_FROM'], ENV['REDIRECT_TO'], '/signup' - use RedirectPages, ENV['REDIRECT_FROM'], ENV['TRAVIS_WP_SITE'], '/help' - use RedirectPages, ENV['REDIRECT_FROM'], ENV['TRAVIS_WP_SITE'], '/' - end +if ENV['REDIRECT'] && !ENV['TRAVIS_PRO'] + use RedirectSubdomain, 'secure.travis-ci.org' + use RedirectPages, ENV['REDIRECT_FROM'], ENV['REDIRECT_TO'], '/signin' + use RedirectPages, ENV['REDIRECT_FROM'], ENV['REDIRECT_TO'], '/signup' + use RedirectPages, ENV['REDIRECT_FROM'], ENV['TRAVIS_WP_SITE'], '/help' + use RedirectPages, ENV['REDIRECT_FROM'], ENV['TRAVIS_WP_SITE'], '/' end use RedirectPages, ENV['REDIRECT_TO'], ENV['TRAVIS_WP_SITE'], '/help' if ENV['TRAVIS_PRO'] && ENV['REDIRECT'] -use Rack::MobileDetect, :redirect_to => ENV['MOBILE_ENDPOINT'] if ENV['MOBILE_ENDPOINT'] +use Rack::MobileDetect, redirect_to: ENV['MOBILE_ENDPOINT'] if ENV['MOBILE_ENDPOINT'] use Travis::Web::SentryDeployHook @@ -67,28 +67,28 @@ use Travis::Web::ApiRedirect do |app| end if ENV['TRAVIS_ENTERPRISE'] - ENV['SSH_KEY_ENABLED'] = 'true' unless ENV.has_key?('SSH_KEY_ENABLED') - ENV['CACHES_ENABLED'] = 'true' unless ENV.has_key?('CACHES_ENABLED') + ENV['SSH_KEY_ENABLED'] = 'true' unless ENV.key?('SSH_KEY_ENABLED') + ENV['CACHES_ENABLED'] = 'true' unless ENV.key?('CACHES_ENABLED') end run Travis::Web::App.build( - userlike: ENV['USERLIKE'], - environment: ENV['RACK_ENV'] || 'development', - api_endpoint: ENV['API_ENDPOINT'], + userlike: ENV['USERLIKE'], + environment: ENV['RACK_ENV'] || 'development', + api_endpoint: ENV['API_ENDPOINT'], github_apps_endpoint: 'https://github.com/apps', - pages_endpoint: ENV['PAGES_ENDPOINT'], + pages_endpoint: ENV['PAGES_ENDPOINT'], billing_endpoint: ENV['BILLING_ENDPOINT'], source_endpoint: ENV['SOURCE_ENDPOINT'] || 'https://github.com', - pusher_key: ENV['PUSHER_KEY'], - pusher_host: ENV['PUSHER_HOST'] || 'ws.pusherapp.com', - pusher_path: ENV['PUSHER_PATH'], + pusher_key: ENV['PUSHER_KEY'], + pusher_host: ENV['PUSHER_HOST'] || 'ws.pusherapp.com', + pusher_path: ENV['PUSHER_PATH'], pusher_channel_prefix: ENV['PUSHER_CHANNEL_PREFIX'], - ga_code: ENV['GA_CODE'], - root: File.expand_path('../../dist', __FILE__), - server_start: Time.now, - caches_enabled: ENV['CACHES_ENABLED'], + ga_code: ENV['GA_CODE'], + root: File.expand_path('../../dist', __FILE__), + server_start: Time.now, + caches_enabled: ENV['CACHES_ENABLED'], ssh_key_enabled: ENV['SSH_KEY_ENABLED'], - pusher_log_fallback: ENV['PUSHER_LOG_FALLBACK'], + pusher_log_fallback: ENV['PUSHER_LOG_FALLBACK'], customer_io_site_id: ENV['CUSTOMER_IO_SITE_ID'], pro: ENV['TRAVIS_PRO'], enterprise: ENV['TRAVIS_ENTERPRISE'], @@ -99,5 +99,6 @@ run Travis::Web::App.build( github_apps_app_name: ENV['GITHUB_APPS_APP_NAME'], enable_feature_flags: ENV['ENABLE_FEATURE_FLAGS'], stripe_publishable_key: ENV['STRIPE_PUBLISHABLE_KEY'], - default_provider: ENV['DEFAULT_PROVIDER'] + default_provider: ENV['DEFAULT_PROVIDER'], + log_limit: ENV['LOG_LIMIT'] ) diff --git a/waiter/lib/travis/utils/deep_merge.rb b/waiter/lib/travis/utils/deep_merge.rb index ac0a5f1a69..d43ddf92e1 100644 --- a/waiter/lib/travis/utils/deep_merge.rb +++ b/waiter/lib/travis/utils/deep_merge.rb @@ -1,7 +1,9 @@ +# frozen_string_literal: true + module Travis module DeepMerge def deep_merge(hash, other_hash) - hash.merge(other_hash) do |key, oldval, newval| + hash.merge(other_hash) do |_key, oldval, newval| oldval = oldval.to_hash if oldval.respond_to?(:to_hash) newval = newval.to_hash if newval.respond_to?(:to_hash) oldval.class.to_s == 'Hash' && newval.class.to_s == 'Hash' ? deep_merge(oldval, newval) : newval diff --git a/waiter/lib/travis/web.rb b/waiter/lib/travis/web.rb index 84449ebd36..4d3265d2aa 100644 --- a/waiter/lib/travis/web.rb +++ b/waiter/lib/travis/web.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Travis module Web autoload :Allow, 'travis/web/allow' @@ -9,6 +11,6 @@ module Web end def self.config - @config ||= Travis::Web::Config.new + @_config ||= Travis::Web::Config.new end end diff --git a/waiter/lib/travis/web/allow.rb b/waiter/lib/travis/web/allow.rb index 222fff96d4..9e8b886184 100644 --- a/waiter/lib/travis/web/allow.rb +++ b/waiter/lib/travis/web/allow.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Travis module Web class Allow @@ -5,12 +7,12 @@ class Allow def initialize(app, options = {}) @app = app - @allow = options[:allow] || ['GET', 'HEAD'] + @allow = options[:allow] || %w[GET HEAD] @response = options.fetch(:response) do body = 'request method not allowed' headers = { - 'Content-Type' => 'text/plain', - 'Allow' => allow.join(', '), + 'Content-Type' => 'text/plain', + 'Allow' => allow.join(', '), 'Content-Length' => body.bytesize.to_s } [405, headers, [body]] diff --git a/waiter/lib/travis/web/api_redirect.rb b/waiter/lib/travis/web/api_redirect.rb index 90fbd159ac..bc3a052a4f 100644 --- a/waiter/lib/travis/web/api_redirect.rb +++ b/waiter/lib/travis/web/api_redirect.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'sinatra' class Travis::Web::ApiRedirect < Sinatra::Base @@ -5,21 +7,9 @@ class Travis::Web::ApiRedirect < Sinatra::Base set api_endpoint: 'https://api.travis-ci.org' set redirect_png: ENV['REDIRECT_PNG'] - class NotPublicImages - Match = Struct.new(:captures) - - def initialize(pattern, except) - @except = except - @pattern = pattern - @captures = Match.new([]) - end - - def match(str) - @captures if str =~ @pattern && str !~ @except - end - end + get %r{/([^/]+)/([^/]+)\.(png|svg)} do + pass if %r{/images/}.match?(request.path_info) - get NotPublicImages.new(%r{^/([^/]+)/([^/]+)\.(png|svg)$}, %r{^/images/}) do if settings.redirect_png redirect!(request.fullpath.gsub(/\.png$/, '.svg')) else @@ -33,12 +23,12 @@ def match(str) private - def public_image? - params[:owner_name] == 'images' - end + def public_image? + params[:owner_name] == 'images' + end - def redirect!(path = nil) - path = File.join(settings.api_endpoint, path || request.fullpath) - redirect(path, 301) - end + def redirect!(path = nil) + path = File.join(settings.api_endpoint, path || request.fullpath) + redirect(path, 301) + end end diff --git a/waiter/lib/travis/web/app.rb b/waiter/lib/travis/web/app.rb index 1cf9181b3c..b2ec7be427 100644 --- a/waiter/lib/travis/web/app.rb +++ b/waiter/lib/travis/web/app.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'rack' require 'rack/ssl' require 'rack/protection' @@ -5,6 +7,7 @@ require 'time' require 'json' require 'travis/utils/deep_merge' +require 'digest/md5' class Travis::Web::App autoload :AltVersions, 'travis/web/app/alt_versions' @@ -17,6 +20,7 @@ class Travis::Web::App # Key is the path, value the response. class Router < DelegateClass(Hash) attr_reader :main_app + def initialize(main_app) @main_app = main_app super({}) @@ -30,13 +34,14 @@ def call(env) class << self def new(options = {}) return super unless options[:environment] == 'development' + proc { |e| super.call(e) } # poor man's reloader end def build(options = {}) builder = Rack::Builder.new if options[:environment] == 'production' || - options[:environment] == 'staging' + options[:environment] == 'staging' builder.use Rack::SSL, hsts: Travis.config.ssl.hsts end builder.use Rack::Deflater @@ -64,210 +69,205 @@ def call(env) name = env['travis.alt'] || :default routers[name] ||= create_router(alt: name) route = routers[name].call(env) - route[1]["Date"] = Time.now.httpdate + route[1]['Date'] = Time.now.httpdate route end private - def create_router(options = {}) - router = Router.new(self) - load_routes(router, options) - router - end - - def load_routes(router, options = {}) - each_file { |file| router[path_for(file)] = response_for(file, options) } - router.default = router['/'] - end + def create_router(options = {}) + router = Router.new(self) + load_routes(router, options) + router + end - def response_for(file, options = {}) - content = File.read(file) - if fingerprinted?(file) - headers = { - 'Content-Length' => content.bytesize.to_s, - 'Cache-Control' => cache_control(file), - 'Content-Location' => path_for(file), - 'Content-Type' => mime_type(file), - 'Expires' => expires(file), - 'ETag' => fingerprint(file) - } - else - set_config(content, options) if config_needed?(file) - set_title(content) if index?(file) - - headers = { - 'Content-Length' => content.bytesize.to_s, - 'Cache-Control' => cache_control(file), - 'Content-Location' => path_for(file), - 'Content-Type' => mime_type(file), - 'Last-Modified' => server_start.httpdate, - 'Expires' => expires(file), - 'Vary' => vary_for(file), - 'ETag' => Digest::MD5.hexdigest(content) - } - end + def load_routes(router, options = {}) + each_file { |file| router[path_for(file)] = response_for(file, options) } + router.default = router['/'] + end - [ 200, headers, [content] ] + def response_for(file, options = {}) + content = File.read(file) + if fingerprinted?(file) + headers = { + 'Content-Length' => content.bytesize.to_s, + 'Cache-Control' => cache_control(file), + 'Content-Location' => path_for(file), + 'Content-Type' => mime_type(file), + 'Expires' => expires(file), + 'ETag' => fingerprint(file) + } + else + set_config(content, options) if config_needed?(file) + set_title(content) if index?(file) + + headers = { + 'Content-Length' => content.bytesize.to_s, + 'Cache-Control' => cache_control(file), + 'Content-Location' => path_for(file), + 'Content-Type' => mime_type(file), + 'Last-Modified' => server_start.httpdate, + 'Expires' => expires(file), + 'Vary' => vary_for(file), + 'ETag' => Digest::MD5.hexdigest(content) + } end - def each_file - Dir.glob(File.join(root, '**/*')) { |file| yield file if File.file?(file) } - end + [200, headers, [content]] + end - def config_needed?(file) - index?(file) || file.end_with?('spec.html') - end - alias csp_needed? config_needed? + def each_file + Dir.glob(File.join(root, '**/*')) { |file| yield file if File.file?(file) } + end - def index?(file) - file == File.join(root, 'index.html') || file == 'index.html' - end + def config_needed?(file) + index?(file) || file.end_with?('spec.html') + end + alias csp_needed? config_needed? - def fingerprint(file) - basename = File.basename(file) - extname = File.extname(file) - if result = basename.scan(/.+-([a-f0-9]{32})#{extname}$/) - result.flatten[0] - end - end - alias fingerprinted? fingerprint + def index?(file) + file == File.join(root, 'index.html') || file == 'index.html' + end - def cache_control(file) - case path_for(file) - when '/' then "public, must-revalidate, max-age=0" - else "public, max-age=#{age}" - end + def fingerprint(file) + basename = File.basename(file) + extname = File.extname(file) + if result = basename.scan(/.+-([a-f0-9]{32})#{extname}$/) + result.flatten[0] end + end + alias fingerprinted? fingerprint - def expires(file) - if fingerprinted?(file) - (server_start + age).httpdate - else - '0' - end + def cache_control(file) + case path_for(file) + when '/' then 'public, must-revalidate, max-age=0' + else "public, max-age=#{age}" end + end - def vary_for(file) - case path_for(file) - when '/' then 'Accept' - else '' - end + def expires(file) + if fingerprinted?(file) + (server_start + age).httpdate + else + '0' end + end - def path_for(file) - file = file.sub("#{root}/", '') - file = "" if index?(file) - "/#{file}" + def vary_for(file) + case path_for(file) + when '/' then 'Accept' + else '' end + end - def mime_type(file) - Rack::Mime.mime_type File.extname(file) - end + def path_for(file) + file = file.sub("#{root}/", '') + file = '' if index?(file) + "/#{file}" + end - def set_title(content) - content.gsub!(/().*(<\/title>)/, "\\1#{title}\\2") - end + def mime_type(file) + Rack::Mime.mime_type File.extname(file) + end - def title - default_title = "Travis CI - Test and Deploy Your Code with Confidence" - ENV['SITE_TITLE'] || default_title - end + def set_title(content) # rubocop:disable Naming/AccessorMethodName + content.gsub! %r{/(<title>).*()/, "\\1#{title}\\2"} + end - def set_assets_host(content) - content.gsub!(/\{\{assets_host\}\}/, ENV['ASSETS_HOST'] || '') - end + def title + default_title = 'Travis CI - Test and Deploy Your Code with Confidence' + ENV['SITE_TITLE'] || default_title + end - def set_config(string, opts = {}) - # TODO: clean up - config = {} + def set_assets_host(content) # rubocop:disable Naming/AccessorMethodName + content.gsub!(/\{\{assets_host\}\}/, ENV['ASSETS_HOST'] || '') + end - config['featureFlags'] ||= {} + def set_config(string, _opts = {}) # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity, Metrics/MethodLength + # TODO: clean up + config = {} - if options[:enable_feature_flags] - options[:enable_feature_flags].split(',').each do |flag| - config['featureFlags'][flag] = true - end - end + config['featureFlags'] ||= {} - if options[:pro] - config['pro'] = true - config['featureFlags']['pro-version'] = true - config['featureFlags']['github-apps'] = true - end - if options[:enterprise] - config['enterprise'] = true - config['featureFlags']['enterprise-version'] = true - end + options[:enable_feature_flags]&.split(',')&.each do |flag| + config['featureFlags'][flag] = true + end - if options[:github_apps_app_name] - config['githubApps'] ||= {} - config['githubApps']['appName'] = options[:github_apps_app_name] - end + if options[:pro] + config['pro'] = true + config['featureFlags']['pro-version'] = true + config['featureFlags']['github-apps'] = true + end + if options[:enterprise] + config['enterprise'] = true + config['featureFlags']['enterprise-version'] = true + end - if !options[:public_mode].nil? && (options[:public_mode] == 'false' || options[:public_mode] == false) - config['publicMode'] = false - else - config['publicMode'] = true - end + if options[:github_apps_app_name] + config['githubApps'] ||= {} + config['githubApps']['appName'] = options[:github_apps_app_name] + end - if config['enterprise'] - config['pagesEndpoint'] = false - config['billingEndpoint'] = false - else - config['pagesEndpoint'] = options[:pages_endpoint] if options[:pages_endpoint] - config['billingEndpoint'] = options[:billing_endpoint] if options[:billing_endpoint] - end + config['publicMode'] = !options[:public_mode].nil? && (options[:public_mode] == 'false' || options[:public_mode] == false) - config['defaultTitle'] = title - config['apiEndpoint'] = options[:api_endpoint] if options[:api_endpoint] - config['githubAppsEndpoint'] = options[:github_apps_endpoint] - source_endpoint = options[:source_endpoint] - if source_endpoint - config['sourceEndpoint'] = source_endpoint - config['githubAppsEndpoint'] = source_endpoint + '/github-apps' unless source_endpoint.include? 'github.com' - end - pusher = {} - pusher['key'] = options[:pusher_key] if options[:pusher_key] - pusher['host'] = options[:pusher_host] if options[:pusher_host] - pusher['path'] = options[:pusher_path] if options[:pusher_path] - pusher['channelPrefix'] = options[:pusher_channel_prefix] if options[:pusher_channel_prefix] - pusher['encrypted'] = true - config['pusher'] = pusher - - if options[:stripe_publishable_key] - stripe = {} - stripe['publishableKey'] = options[:stripe_publishable_key] - stripe['lazyLoad'] = true - config['stripe'] = stripe - end + if config['enterprise'] + config['pagesEndpoint'] = false + config['billingEndpoint'] = false + else + config['pagesEndpoint'] = options[:pages_endpoint] if options[:pages_endpoint] + config['billingEndpoint'] = options[:billing_endpoint] if options[:billing_endpoint] + end + + config['defaultTitle'] = title + config['apiEndpoint'] = options[:api_endpoint] if options[:api_endpoint] + config['githubAppsEndpoint'] = options[:github_apps_endpoint] + source_endpoint = options[:source_endpoint] + if source_endpoint + config['sourceEndpoint'] = source_endpoint + config['githubAppsEndpoint'] = "#{source_endpoint}/github-apps" unless source_endpoint.include? 'github.com' + end + pusher = {} + pusher['key'] = options[:pusher_key] if options[:pusher_key] + pusher['host'] = options[:pusher_host] if options[:pusher_host] + pusher['path'] = options[:pusher_path] if options[:pusher_path] + pusher['channelPrefix'] = options[:pusher_channel_prefix] if options[:pusher_channel_prefix] + pusher['encrypted'] = true + config['pusher'] = pusher + + if options[:stripe_publishable_key] + stripe = {} + stripe['publishableKey'] = options[:stripe_publishable_key] + stripe['lazyLoad'] = true + config['stripe'] = stripe + end - config['gaCode'] = options[:ga_code] if options[:ga_code] + config['gaCode'] = options[:ga_code] if options[:ga_code] - config['githubOrgsOauthAccessSettingsUrl'] = options[:github_orgs_oauth_access_settings_url] - config['ajaxPolling'] = true if options[:ajax_polling] - config['userlike'] = true if options[:userlike] + config['githubOrgsOauthAccessSettingsUrl'] = options[:github_orgs_oauth_access_settings_url] + config['ajaxPolling'] = true if options[:ajax_polling] + config['userlike'] = true if options[:userlike] + config['logLimit'] = options[:log_limit] if options[:log_limit] - config['endpoints'] = { - 'sshKey' => options[:ssh_key_enabled], - 'caches' => options[:caches_enabled] - } + config['endpoints'] = { + 'sshKey' => options[:ssh_key_enabled], + 'caches' => options[:caches_enabled] + } - if options[:default_provider] - provider = options[:default_provider] - config['providers'] ||= {} - config['providers'][provider] ||= {} - config['providers'][provider]['isDefault'] = true - end + if options[:default_provider] + provider = options[:default_provider] + config['providers'] ||= {} + config['providers'][provider] ||= {} + config['providers'][provider]['isDefault'] = true + end - regexp = %r( 'text/plain', 'Location' => location }, []] else app.call env diff --git a/waiter/lib/travis/web/config.rb b/waiter/lib/travis/web/config.rb index 902ef9f45d..3096d80b01 100644 --- a/waiter/lib/travis/web/config.rb +++ b/waiter/lib/travis/web/config.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'hashr' require 'yaml' @@ -17,7 +19,7 @@ module Web class Config < Hashr class << self def env - ENV['ENV'] || ENV['RAILS_ENV'] || ENV['RACK_ENV'] || 'development' + ENV['ENV'] || ENV['RAILS_ENV'] || ENV['RACK_ENV'] || 'development' end def load_env @@ -25,11 +27,13 @@ def load_env end def load_file - @load_file ||= YAML.load_file(filename)[env] if File.exists?(filename) rescue {} + @load_file ||= YAML.load_file(filename)[env] if File.exist?(filename) + rescue StandardError + {} end def filename - @filename ||= File.expand_path('config/travis.yml') + @_filename ||= File.expand_path('config/travis.yml') end end diff --git a/waiter/lib/travis/web/sentry_deploy_hook.rb b/waiter/lib/travis/web/sentry_deploy_hook.rb index 7d52c9892b..3decf2eeb9 100644 --- a/waiter/lib/travis/web/sentry_deploy_hook.rb +++ b/waiter/lib/travis/web/sentry_deploy_hook.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'sinatra' require 'uri' require 'net/http' @@ -8,21 +10,20 @@ class Travis::Web::SentryDeployHook < Sinatra::Base set sentry_org: 'travis-ci' set sentry_project: 'travis-web-h4' set sentry_releases_endpoint: "https://app.getsentry.com/api/0/projects/#{settings.sentry_org}/#{settings.sentry_project}/releases/" - set github_commit_url: "https://github.com/travis-web/travis-ci/commit" - + set github_commit_url: 'https://github.com/travis-web/travis-ci/commit' post '/deploy/hooks/sentry' do - version = determine_version(params["url"], params["head"]) + version = determine_version(params['url'], params['head']) request_body = { - version: version, - ref: params["head_long"], - url: "#{settings.github_commit_url}/#{params["head_long"]}" + version:, + ref: params['head_long'], + url: "#{settings.github_commit_url}/#{params['head_long']}" }.to_json url = URI(settings.sentry_releases_endpoint) - request = Net::HTTP::Post.new(url.request_uri, initheader = {'Content-Type' => 'application/json'}) + request = Net::HTTP::Post.new(url.request_uri, { 'Content-Type' => 'application/json' }) request.basic_auth settings.sentry_api_key, '' request.body = request_body @@ -33,7 +34,8 @@ class Travis::Web::SentryDeployHook < Sinatra::Base def determine_version(url, sha) return sha unless url - domain = url.include?("travis-web-production") ? "org" : "com" + + domain = url.include?('travis-web-production') ? 'org' : 'com' "#{domain}-#{sha}" end end diff --git a/waiter/lib/travis/web/set_token.rb b/waiter/lib/travis/web/set_token.rb index bbb34bd4d9..ec40489a78 100644 --- a/waiter/lib/travis/web/set_token.rb +++ b/waiter/lib/travis/web/set_token.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'rack/request' require 'rack/response' require 'sanitize' @@ -8,18 +10,21 @@ class SetToken attr_accessor :app, :template def initialize(app) - @app, @template = app, File.read(__FILE__).split('__END__').last + @app = app + @template = File.read(__FILE__).split('__END__').last end def call(env) set_info(env) || app.call(env) end - def set_info(env) + def set_info(env) # rubocop:disable Naming/AccessorMethodName return unless env['REQUEST_METHOD'] == 'POST' + request = Rack::Request.new(env) - token, rss_token, user, storage, become = request.params.values_at('token', 'rssToken', 'user', 'storage', 'become') - if token =~ /\A[a-zA-Z\-_\d]+\Z/ + token, rss_token, user, storage, become = request.params.values_at('token', 'rssToken', 'user', 'storage', + 'become') + if /\A[a-zA-Z\-_\d]+\Z/.match?(token) storage = 'sessionStorage' if storage != 'localStorage' become = become ? true : false info = [ diff --git a/waiter/script/prepare_deploy b/waiter/script/prepare_deploy index d8931438af..19fe65666c 100755 --- a/waiter/script/prepare_deploy +++ b/waiter/script/prepare_deploy @@ -1,4 +1,5 @@ #!/usr/bin/env ruby +# frozen_string_literal: true require 'digest' require 'fileutils' @@ -6,7 +7,8 @@ require 'fileutils' def fingerprint(file_path) # file_path is relative to public public_file_path = "public/#{file_path}" - return unless File.exists?(public_file_path) + return unless File.exist?(public_file_path) + digest = Digest::MD5.file public_file_path extension = File.extname(file_path) basename = File.basename(file_path, extension) @@ -15,11 +17,11 @@ def fingerprint(file_path) new_public_file_path = "public/#{new_file_path}" FileUtils.mv(public_file_path, new_public_file_path) - index_content = File.read("public/index.html") + index_content = File.read('public/index.html') index_content.gsub!(file_path, new_file_path) - File.open("public/index.html", "w") { |f| + File.open('public/index.html', 'w') do |f| f.write index_content - } + end end fingerprint('scripts/app.js') diff --git a/waiter/spec/allow_spec.rb b/waiter/spec/allow_spec.rb index b8e6698b7c..9b79398c06 100644 --- a/waiter/spec/allow_spec.rb +++ b/waiter/spec/allow_spec.rb @@ -1,11 +1,13 @@ +# frozen_string_literal: true + require 'spec_helper' describe Travis::Web::Allow do - example { post('/') .should_not be_ok } - example { delete('/') .should_not be_ok } - example { put('/') .should_not be_ok } - example { patch('/') .should_not be_ok } - example { options('/') .should_not be_ok } - example { head('/') .should be_ok } - example { get('/') .should be_ok } + example { post('/').should_not be_ok } + example { delete('/').should_not be_ok } + example { put('/').should_not be_ok } + example { patch('/').should_not be_ok } + example { options('/').should_not be_ok } + example { head('/').should be_ok } + example { get('/').should be_ok } end diff --git a/waiter/spec/api_redirect_spec.rb b/waiter/spec/api_redirect_spec.rb index 9993d0863b..a9cecac1b4 100644 --- a/waiter/spec/api_redirect_spec.rb +++ b/waiter/spec/api_redirect_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Travis::Web::ApiRedirect do diff --git a/waiter/spec/app_spec.rb b/waiter/spec/app_spec.rb index fbfac86512..e578521ab1 100644 --- a/waiter/spec/app_spec.rb +++ b/waiter/spec/app_spec.rb @@ -1,8 +1,10 @@ +# frozen_string_literal: true + require 'spec_helper' describe Travis::Web::App do before do - current_session.global_env['HTTP_ACCEPT'] = 'text/html,*/*' +# current_session.global_env['HTTP_ACCEPT'] = 'text/html,*/*' end describe 'catch all' do diff --git a/waiter/spec/mobile_redirect_spec.rb b/waiter/spec/mobile_redirect_spec.rb index 6ac7914778..c995cf9b91 100644 --- a/waiter/spec/mobile_redirect_spec.rb +++ b/waiter/spec/mobile_redirect_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' # disabled, we now use Rack::MobileDetect diff --git a/waiter/spec/spec_helper.rb b/waiter/spec/spec_helper.rb index e55dfb9983..e8972c8867 100644 --- a/waiter/spec/spec_helper.rb +++ b/waiter/spec/spec_helper.rb @@ -1,13 +1,15 @@ +# frozen_string_literal: true + ENV['RACK_ENV'] = 'test' require 'sinatra/contrib' require 'travis/web' -ru_file = File.expand_path('../../config.ru', __FILE__) +ru_file = File.expand_path('../config.ru', __dir__) web_app = Rack::Builder.parse_file(ru_file).first RSpec.configure do |config| - config.expect_with :rspec, :stdlib + config.expect_with :rspec config.include Sinatra::TestHelpers config.before(:each) { set_app(web_app) } end diff --git a/waiter/travis-web.gemspec b/waiter/travis-web.gemspec index 9bbc1bc6cc..ebe6b4c3dd 100644 --- a/waiter/travis-web.gemspec +++ b/waiter/travis-web.gemspec @@ -1,6 +1,9 @@ +# frozen_string_literal: true + Gem::Specification.new do |s| s.name = 'travis-web' s.version = '0.0.1' s.summary = '' s.authors = ['admin@travis-ci.org'] + s.required_ruby_version = '>= 3.2.2' end