diff --git a/.rubocop.yml b/.rubocop.yml new file mode 100644 index 0000000..c1b082a --- /dev/null +++ b/.rubocop.yml @@ -0,0 +1,49 @@ +# Rubocop thinks we're using Kernel#open, while we're using OpenURI#open's implementation +Security/Open: + Enabled: false + +Style/RegexpLiteral: + AllowInnerSlashes: true + +Style/StringLiterals: + Enabled: false + +Style/GlobalVars: + Enabled: false + +Documentation: + Enabled: false + +Metrics/AbcSize: + Max: 22 + +Metrics/ClassLength: + Enabled: false + +Metrics/CyclomaticComplexity: + Max: 9 + +Metrics/LineLength: + Max: 120 + +Metrics/ModuleLength: + Enabled: false + +Metrics/MethodLength: + Max: 20 + +Metrics/PerceivedComplexity: + Max: 10 + +Naming/FileName: + Enabled: false + +# The two following cops have to be disabled due to false positives +Performance/RedundantMatch: + Enabled: false + +Performance/RegexpMatch: + Enabled: false + +AllCops: + TargetRubyVersion: 2.4.3 diff --git a/.travis.yml b/.travis.yml index a8ba9cc..fc90dad 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,9 +1,5 @@ -script: bundle exec cucumber && bundle exec cucumber -p validate +script: bundle exec rubocop && bundle exec cucumber && bundle exec cucumber -p validate language: ruby rvm: - - 2.2.0 - - 2.2.1 - - 2.3.0 - - 2.3.1 - - 2.4.0 + - 2.4.3 - 2.5.0 diff --git a/Dockerfile b/Dockerfile index fa582c0..9b83920 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,13 +1,9 @@ # -*- conf -*- -FROM ubuntu:16.04 +FROM ruby:2.5 -COPY config/docker_apt_preferences /etc/apt/preferences.d/brightbox-ruby-ng RUN set -e -x ; \ - echo 'deb http://ppa.launchpad.net/brightbox/ruby-ng/ubuntu trusty main' > /etc/apt/sources.list.d/brightbox-ruby-ng.list ; \ - apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv C3173AA6 ; \ apt-get update ; \ - apt-get install --yes ruby2.2 ruby2.2-dev git build-essential libssl-dev libicu-dev cmake pkg-config python-virtualenv; \ - gem install bundler --no-rdoc --no-ri ; \ + apt-get install --yes cmake python-virtualenv ; \ rm -rf /var/lib/apt/lists/* /var/cache/apt/archives/* COPY . /opt/chef-browser diff --git a/Gemfile b/Gemfile index 404f99a..f754e37 100644 --- a/Gemfile +++ b/Gemfile @@ -1,30 +1,34 @@ +# frozen_string_literal: true + source 'https://rubygems.org' -gem "sinatra" +gem "coderay" +gem "deep_merge" gem "erubis", "~> 2.7.0" -gem "ridley", "~> 5.0.0" -gem "tinyconfig", "~> 0.1" -gem "oj", platforms: :ruby # to be used by multijson +gem "github-linguist", "~> 6.0.1" +gem "github-markup" gem "jrjackson", platforms: :jruby # to be used by multijson -gem "puma" -gem "rubysl", "~> 2.0", platforms: :rbx -gem "racc", platforms: :rbx -gem "deep_merge" gem "kramdown" -gem "github-markup" -gem "coderay" +gem "oj", platforms: :ruby # to be used by multijson +gem "puma" gem "pygments.rb" -gem "github-linguist", "~> 3.0" -gem 'rugged', '= 0.21.1b2' # github-linguist specifies (~> 0.21.1b2), but 0.21.4 breaks +gem "racc", platforms: :rbx +gem "ridley", "~> 5.1.1" +gem "rubysl", "~> 2.0", platforms: :rbx +gem 'rugged' +gem "sinatra" +gem "tinyconfig", "~> 0.1" group :development do gem "capybara" - gem "chef-zero" + gem "chef-zero", "~> 14.0.0" gem "cucumber" - gem "rack-test" - gem "wrong", "= 0.7.1" + gem "ffi" gem "pry" + gem "rack-test" + gem "rubocop", "~> 0.54.0" gem "rubysl-test-unit", "~> 2.0", platforms: :rbx + gem "wrong", "= 0.7.1" end group :test do diff --git a/Gemfile.lock b/Gemfile.lock index 682b92f..cf47c04 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,43 +1,45 @@ GEM remote: https://rubygems.org/ specs: - addressable (2.5.0) - public_suffix (~> 2.0, >= 2.0.2) + addressable (2.5.2) + public_suffix (>= 2.0.2, < 4.0) + ast (2.4.0) buff-config (2.0.0) buff-extensions (~> 2.0) varia_model (~> 0.6) buff-extensions (2.0.0) buff-ignore (1.2.0) - buff-ruby_engine (0.1.0) - buff-shell_out (0.2.0) - buff-ruby_engine (~> 0.1.0) - builder (3.2.2) - capybara (2.11.0) + buff-ruby_engine (1.0.0) + buff-shell_out (1.1.0) + buff-ruby_engine (~> 1.0) + builder (3.2.3) + capybara (3.0.2) addressable - mime-types (>= 1.16) - nokogiri (>= 1.3.3) - rack (>= 1.0.0) - rack-test (>= 0.5.4) - xpath (~> 2.0) + mini_mime (>= 0.1.3) + nokogiri (~> 1.8) + rack (>= 1.6.0) + rack-test (>= 0.6.3) + xpath (~> 3.0) celluloid (0.16.0) timers (~> 4.0.0) celluloid-io (0.16.2) celluloid (>= 0.16.0) nio4r (>= 1.1.0) - charlock_holmes (0.7.3) - chef-config (12.16.42) + charlock_holmes (0.7.6) + chef-config (14.0.190) addressable fuzzyurl mixlib-config (~> 2.0) mixlib-shellout (~> 2.0) - chef-zero (4.9.0) + tomlrb (~> 1.2) + chef-zero (14.0.1) ffi-yajl (~> 2.2) hashie (>= 2.0, < 4.0) - mixlib-log (~> 1.3) - rack (< 2) + mixlib-log (~> 2) + rack (~> 2.0) uuidtools (~> 2.1) - coderay (1.1.1) - cucumber (2.4.0) + coderay (1.1.2) + cucumber (2.99.0) builder (>= 2.1.2) cucumber-core (~> 1.5.0) cucumber-wire (~> 0.0.1) @@ -48,76 +50,82 @@ GEM cucumber-core (1.5.0) gherkin (~> 4.0) cucumber-wire (0.0.1) - deep_merge (1.1.1) + deep_merge (1.2.1) diff-lcs (1.2.5) erubis (2.7.0) - escape_utils (1.0.1) - faraday (0.9.2) + escape_utils (1.2.1) + faraday (0.14.0) multipart-post (>= 1.2, < 3) - ffi-yajl (2.3.0) + ffi (1.9.23) + ffi-yajl (2.3.1) libyajl2 (~> 1.2) ffi2-generators (0.1.1) fuzzyurl (0.9.0) - gherkin (4.0.0) - github-linguist (3.5.2) - charlock_holmes (~> 0.7.3) - escape_utils (~> 1.0.1) - mime-types (~> 1.19) - pygments.rb (~> 0.6.0) - rugged (~> 0.21.1b2) - github-markup (1.4.0) - hashie (3.4.6) - hitimes (1.2.4) + gherkin (4.1.3) + github-linguist (6.0.1) + charlock_holmes (~> 0.7.5) + escape_utils (~> 1.2.0) + mime-types (>= 1.19) + rugged (>= 0.25.1) + github-markup (2.0.0) + hashie (3.5.7) + hitimes (1.2.6) httpclient (2.8.3) - json (2.0.2) - kramdown (1.13.1) + json (2.1.0) + kramdown (1.16.2) libyajl2 (1.2.0) - method_source (0.8.2) - mime-types (1.25.1) - mini_portile2 (2.1.0) + method_source (0.9.0) + mime-types (3.1) + mime-types-data (~> 3.2015) + mime-types-data (3.2016.0521) + mini_mime (1.0.0) + mini_portile2 (2.3.0) minitest (4.7.5) - mixlib-authentication (1.4.1) - mixlib-log - mixlib-config (2.2.4) - mixlib-log (1.7.1) - mixlib-shellout (2.2.7) - multi_json (1.12.1) + mixlib-authentication (2.0.0) + mixlib-config (2.2.6) + tomlrb + mixlib-log (2.0.4) + mixlib-shellout (2.3.2) + multi_json (1.13.1) multi_test (0.1.2) multipart-post (2.0.0) - nio4r (1.2.1) - nokogiri (1.6.8.1) - mini_portile2 (~> 2.1.0) - oj (2.18.0) - posix-spawn (0.3.12) + mustermann (1.0.2) + nio4r (2.3.0) + nokogiri (1.8.2) + mini_portile2 (~> 2.3.0) + oj (3.5.1) + parallel (1.12.1) + parser (2.5.1.0) + ast (~> 2.4.0) + powerpack (0.1.1) predicated (0.2.6) - pry (0.10.4) + pry (0.11.3) coderay (~> 1.1.0) - method_source (~> 0.8.1) - slop (~> 3.4) - public_suffix (2.0.4) - puma (3.6.2) - pygments.rb (0.6.3) - posix-spawn (~> 0.3.6) - yajl-ruby (~> 1.2.0) + method_source (~> 0.9.0) + public_suffix (3.0.2) + puma (3.11.4) + pygments.rb (1.2.1) + multi_json (>= 1.0.0) racc (1.4.14) - rack (1.6.5) - rack-protection (1.5.3) + rack (2.0.4) + rack-protection (2.0.1) rack - rack-test (0.6.3) - rack (>= 1.0) - rake (12.0.0) + rack-test (1.0.0) + rack (>= 1.0, < 3) + rainbow (3.0.0) + rake (12.3.1) retryable (2.0.4) - ridley (5.0.0) + ridley (5.1.1) addressable buff-config (~> 2.0) buff-extensions (~> 2.0) buff-ignore (~> 1.2) - buff-shell_out (~> 0.1) + buff-shell_out (~> 1.0) celluloid (~> 0.16.0) celluloid-io (~> 0.16.1) chef-config (>= 12.5.0) erubis - faraday (~> 0.9.0) + faraday (~> 0.9) hashie (>= 2.0.2, < 4.0.0) httpclient (~> 2.7) json (>= 1.7.7) @@ -125,11 +133,19 @@ GEM retryable (~> 2.0) semverse (~> 2.0) varia_model (~> 0.6) - ruby2ruby (2.3.2) + rubocop (0.54.0) + parallel (~> 1.10) + parser (>= 2.5) + powerpack (~> 0.1) + rainbow (>= 2.2.2, < 4.0) + ruby-progressbar (~> 1.7) + unicode-display_width (~> 1.0, >= 1.0.1) + ruby-progressbar (1.9.0) + ruby2ruby (2.4.1) ruby_parser (~> 3.1) sexp_processor (~> 4.6) - ruby_parser (3.8.3) - sexp_processor (~> 4.1) + ruby_parser (3.11.0) + sexp_processor (~> 4.9) rubysl (2.2.0) rubysl-abbrev (~> 2.0) rubysl-base64 (~> 2.0) @@ -271,7 +287,7 @@ GEM rubysl-mkmf (2.1) rubysl-fileutils (~> 2.0) rubysl-shellwords (~> 2.0) - rubysl-monitor (2.0.0) + rubysl-monitor (2.1) rubysl-mutex_m (2.0.0) rubysl-net-ftp (2.0.1) rubysl-net-http (2.0.4) @@ -287,7 +303,7 @@ GEM rubysl-observer (2.0.0) rubysl-open-uri (2.0.0) rubysl-open3 (2.0.0) - rubysl-openssl (2.9) + rubysl-openssl (2.10) rubysl-optparse (2.0.1) rubysl-shellwords (~> 2.0) rubysl-ostruct (2.1.0) @@ -308,9 +324,9 @@ GEM rubysl-set (2.0.1) rubysl-shellwords (2.0.0) rubysl-singleton (2.0.0) - rubysl-socket (2.2) + rubysl-socket (2.2.1) rubysl-fcntl (~> 2.0) - rubysl-stringio (2.1.0) + rubysl-stringio (2.2) rubysl-strscan (2.0.0) rubysl-sync (2.0.0) rubysl-syslog (2.1.0) @@ -318,7 +334,7 @@ GEM rubysl-tempfile (2.0.1) rubysl-test-unit (2.0.3) minitest (~> 4.7) - rubysl-thread (2.0.3) + rubysl-thread (2.1) rubysl-thwait (2.0.0) rubysl-time (2.0.3) rubysl-timeout (2.0.0) @@ -334,18 +350,20 @@ GEM rubysl-xmlrpc (2.0.0) rubysl-yaml (2.1.0) rubysl-zlib (2.0.1) - rugged (0.21.1b2) + rugged (0.27.0) semverse (2.0.0) - sexp_processor (4.7.0) - sinatra (1.4.7) - rack (~> 1.5) - rack-protection (~> 1.4) - tilt (>= 1.3, < 3) - slop (3.6.0) - tilt (2.0.5) + sexp_processor (4.11.0) + sinatra (2.0.1) + mustermann (~> 1.0) + rack (~> 2.0) + rack-protection (= 2.0.1) + tilt (~> 2.0) + tilt (2.0.8) timers (4.0.4) hitimes tinyconfig (0.1.1) + tomlrb (1.2.6) + unicode-display_width (1.3.0) uuidtools (2.1.5) varia_model (0.6.0) buff-extensions (~> 2.0) @@ -356,21 +374,21 @@ GEM ruby2ruby (>= 2.0.1) ruby_parser (>= 3.0.1) sexp_processor (>= 4.0) - xpath (2.0.0) - nokogiri (~> 1.3) - yajl-ruby (1.2.1) + xpath (3.0.0) + nokogiri (~> 1.8) PLATFORMS ruby DEPENDENCIES capybara - chef-zero + chef-zero (~> 14.0.0) coderay cucumber deep_merge erubis (~> 2.7.0) - github-linguist (~> 3.0) + ffi + github-linguist (~> 6.0.1) github-markup jrjackson kramdown @@ -381,13 +399,14 @@ DEPENDENCIES racc rack-test rake - ridley (~> 5.0.0) + ridley (~> 5.1.1) + rubocop (~> 0.54.0) rubysl (~> 2.0) rubysl-test-unit (~> 2.0) - rugged (= 0.21.1b2) + rugged sinatra tinyconfig (~> 0.1) wrong (= 0.7.1) BUNDLED WITH - 1.13.6 + 1.11.2 diff --git a/README.md b/README.md index 611c952..b71fd95 100644 --- a/README.md +++ b/README.md @@ -73,7 +73,10 @@ When login is required, Chef-browser uses Rack sessions. By default, on each res ## Ruby versions -Chef-browser works with Ruby versions: 2.2.0, 2.2.1, 2.3.0 and 2.3.1 (JRuby and RBX should work, but haven't been tested properly yet). +Chef-browser works with following Ruby versions: + +- 2.4.3, +- 2.5.0. ## Contributing diff --git a/config.ru b/config.ru index 4f85a9d..4047ed9 100644 --- a/config.ru +++ b/config.ru @@ -1,8 +1,10 @@ +# frozen_string_literal: true + require 'bundler/setup' # Insert `lib/` subdirectory in front of require path -_lib = File.join(File.dirname(__FILE__), 'lib') -$:.unshift(_lib) unless $:.include?(_lib) +libfolder = File.join(File.dirname(__FILE__), 'lib') +$LOAD_PATH.unshift(libfolder) unless $LOAD_PATH.include?(libfolder) require 'chef-browser' diff --git a/config/docker_apt_preferences b/config/docker_apt_preferences deleted file mode 100644 index 9cb46b3..0000000 --- a/config/docker_apt_preferences +++ /dev/null @@ -1,7 +0,0 @@ -Package: ruby* libruby* ri* -Pin: release o=LP-PPA-brightbox-ruby-ng -Pin-Priority: 666 - -Package: * -Pin: release o=LP-PPA-brightbox-ruby-ng -Pin-Priority: -1 diff --git a/config/docker_settings.rb b/config/docker_settings.rb index 1472faa..bce6ce6 100644 --- a/config/docker_settings.rb +++ b/config/docker_settings.rb @@ -1,10 +1,12 @@ +# frozen_string_literal: true + server_url ::ENV['CHEF_SERVER_URL'] client_name ::ENV['CHEF_CLIENT_NAME'] client_key ::ENV['CHEF_CLIENT_KEY'] connection[:ssl] = { verify: false } if ::ENV['INSECURE_SSL'] title ::ENV['TITLE'] use_partial_search !::ENV['NO_PARTIAL_SEARCH'] -login !!::ENV['LOGIN'] +login ::ENV['LOGIN'] ::File.write('var/secret', ::SecureRandom.base64(64)) unless ::File.exist?('var/secret') cookie_secret ::File.read('var/secret') diff --git a/features/fixtures/settings.rb b/features/fixtures/settings.rb index 91d0b2b..a567923 100644 --- a/features/fixtures/settings.rb +++ b/features/fixtures/settings.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + server_url "http://127.0.0.1:#{::ENV['CHEF_ZERO_PORT']}" client_name "stub" client_key ::File.join(::File.dirname(__FILE__), 'stub.pem') diff --git a/features/step_definitions/chef-zero.rb b/features/step_definitions/chef-zero.rb index 676dcb8..42d4bac 100644 --- a/features/step_definitions/chef-zero.rb +++ b/features/step_definitions/chef-zero.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'json' Given(/^a Chef server populated with following data:$/) do |json_data| diff --git a/features/step_definitions/chef_browser.rb b/features/step_definitions/chef_browser.rb index d167a07..3f9b040 100644 --- a/features/step_definitions/chef_browser.rb +++ b/features/step_definitions/chef_browser.rb @@ -1,4 +1,6 @@ -Given /a settings.rb configuration:/ do |settings| +# frozen_string_literal: true + +Given(/a settings.rb configuration:/) do |settings| @chef_browser_settings = ChefBrowser::App.settings.rb new_settings = ChefBrowser::Settings.load diff --git a/features/step_definitions/hello_world_steps.rb b/features/step_definitions/hello_world_steps.rb index 521cfa9..d1d78db 100644 --- a/features/step_definitions/hello_world_steps.rb +++ b/features/step_definitions/hello_world_steps.rb @@ -1,6 +1,8 @@ +# frozen_string_literal: true + PAGES = { 'main page' => '/' -} +}.freeze When(/^I visit (?:the )?"(.*?)"$/) do |page_url| page_url = PAGES[page_url.downcase] unless page_url =~ /^\// @@ -11,14 +13,14 @@ Then(/^I can see "(.*?)"$/) do |text| # We normally expect the request to succeed, put the assertion here # to avoid too verbose feature files. - assert { (200..399).include?(page.status_code) } + assert { (200..399).cover?(page.status_code) } assert { page.has_content?(text) } end Then(/^I can't see "(.*?)"$/) do |text| # We normally expect the request to succeed, put the assertion here # to avoid too verbose feature files. - assert { (200..399).include?(page.status_code) } + assert { (200..399).cover?(page.status_code) } assert { page.has_content?(text) == false } end @@ -50,7 +52,7 @@ end When(/^I search for "(.*?)"$/) do |search_query| - page.fill_in 'q', with: "#{search_query}" + page.fill_in 'q', with: search_query.to_s page.find('button[id="search-submit"]').click end @@ -59,8 +61,8 @@ end When(/^I log in as "(.*?)" with password "(.*?)"$/) do |user, password| - page.fill_in 'username', with: "#{user}" - page.fill_in 'password', with: "#{password}" + page.fill_in 'username', with: user.to_s + page.fill_in 'password', with: password.to_s page.find('button[type="submit"]').click end diff --git a/features/support/chef-zero.rb b/features/support/chef-zero.rb index e7dfca7..ecfd578 100644 --- a/features/support/chef-zero.rb +++ b/features/support/chef-zero.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'chef_zero/server' $chef_zero = ChefZero::Server.new(port: ENV['CHEF_ZERO_PORT'].to_i) diff --git a/features/support/env.rb b/features/support/env.rb index 0bca82b..c3c9edb 100644 --- a/features/support/env.rb +++ b/features/support/env.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + lib = File.realpath(File.join(File.dirname(__FILE__), '../../lib')) $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) diff --git a/features/support/rack_app.rb b/features/support/rack_app.rb index 2892ee9..9f4583a 100644 --- a/features/support/rack_app.rb +++ b/features/support/rack_app.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + $rack_app = ChefBrowser::App if ENV['RACK_SCRIPT_PATH'] @@ -6,9 +8,9 @@ end if ENV['VALIDATE_HTML'] - _app_before_validate = $rack_app + app_before_validate = $rack_app $rack_app = lambda do |env| - resp = _app_before_validate.call(env) + resp = app_before_validate.call(env) resp[2] = [resp[2].join] validate_html(resp[2].first) if resp[0] == 200 && resp[1]['Content-Type'] =~ /^text\/html\s*(;.*)?$/ resp diff --git a/features/support/validate_html.rb b/features/support/validate_html.rb index fa91416..1f7c7b6 100644 --- a/features/support/validate_html.rb +++ b/features/support/validate_html.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'net/http' # Raise exception if `html_str` is not valid HTML according to @@ -6,13 +8,10 @@ def validate_html(html_str) resp = Net::HTTP.start('validator.w3.org') do |http| http.post '/nu/?out=text', html_str, 'Content-Type' => 'text/html; charset=utf-8' end - resp.value # raise error if not 2xx - unless resp.body.rstrip.end_with?("The document validates according to the specified schema(s).") - lines = [] - html_str.lines.each_with_index do |line, i| - lines << "#{i + 1}\t#{line}" - end - $stderr.puts "Invalid HTML:\n\n#{lines.join}\n\n#{resp.body.force_encoding('utf-8')}" - fail "Invalid HTML" - end + resp.value # raise error if not 2xx + return if resp.body.rstrip.end_with?("The document validates according to the specified schema(s).") + lines = [] + html_str.lines.each_with_index { |line, i| lines << "#{i + 1}\t#{line}" } + warn "Invalid HTML:\n\n#{lines.join}\n\n#{resp.body.force_encoding('utf-8')}" + raise "Invalid HTML" end diff --git a/lib/chef-browser.rb b/lib/chef-browser.rb index 3bd4113..d4fd9ef 100644 --- a/lib/chef-browser.rb +++ b/lib/chef-browser.rb @@ -1 +1,3 @@ +# frozen_string_literal: true + require 'chef-browser/app' diff --git a/lib/chef-browser/app.rb b/lib/chef-browser/app.rb index 59c17d5..440f672 100644 --- a/lib/chef-browser/app.rb +++ b/lib/chef-browser/app.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'erubis' require 'sinatra' require 'ridley' @@ -25,7 +27,7 @@ class App < Sinatra::Base ['Roles', '/roles', '/role'], ['Data Bags', '/data_bags', '/data_bag'], ['Cookbooks', '/cookbooks', '/cookbook'] - ] + ].freeze ## ## Settings @@ -54,7 +56,8 @@ class App < Sinatra::Base SECTIONS.each do |section, list_route, item_route| before "#{item_route}*" do - @search_url = list_route unless section == 'Data Bags' || section == 'Cookbooks' # Data bags and Cookbooks are special. + # Data bags and Cookbooks are special + @search_url = list_route unless ['Data Bags', 'Cookbooks'].include? section @search_for = section @title << section @section = section @@ -151,7 +154,8 @@ class App < Sinatra::Base end get '/data_bag/:data_bag_id/:data_bag_item_id/?' do - data_bag_item = chef_server.data_bag.find(params[:data_bag_id]).item.find(params[:data_bag_item_id]) + data_bag_item = chef_server.data_bag.find(params[:data_bag_id]) + .item.find(params[:data_bag_item_id]) pass unless data_bag_item @title << params[:data_bag_item_id] erb :data_bag_item, locals: { data_bag_item: data_bag_item } @@ -204,10 +208,14 @@ class App < Sinatra::Base } end - COOKBOOK_BASIC_METADATA = %w(maintainer maintainer_email license platforms dependencies recommendations providing suggestions conflicting replacing groupings long_description).map(&:freeze).freeze + COOKBOOK_BASIC_METADATA = %w[maintainer maintainer_email license platforms + dependencies recommendations providing suggestions + conflicting replacing groupings long_description + issues_url source_url] + .map(&:freeze).freeze # single cookbook get '/cookbook/:cookbook/?' do - template_name = if request.query_string =~ /^\w+$/ + template_name = if request.query_string.match(/^\w+$/) "cookbook_tab_#{request.query_string}".to_sym else :cookbook diff --git a/lib/chef-browser/file_content.rb b/lib/chef-browser/file_content.rb index dacbdc0..c594e23 100644 --- a/lib/chef-browser/file_content.rb +++ b/lib/chef-browser/file_content.rb @@ -1,13 +1,18 @@ +# frozen_string_literal: true + require 'chef-browser/app' module ChefBrowser + # We use different gems for highlighting different file types: Github's markup + # for Markdown files, and Linguist and Pygments for everything else. We don't render non-test + # files, but allow the user to download them. class FileContent include Linguist::BlobHelper attr_accessor :name, :path, :data - HIGHLIGHT_OPTIONS = { encoding: 'utf-8', formatter: 'html', linenos: 'inline' } - MARKUP_FILES = %w(license contributing testing readme) + @highlight_options = { encoding: 'utf-8', formatter: 'html', linenos: 'inline' } + @markup_files = %w[license changelog code_of_conduct contributing testing readme] def initialize(name, path, content) @name = name @@ -17,30 +22,30 @@ def initialize(name, path, content) class << self def show_file(file, uri_options = {}) + # Using Kernel#open (via OpenURI#open) can be a security risk but + # alternatives don't seem to work here: we're opening a URL, + # not a file on disk and File#open fails. content = FileContent.new(file.name, file.url, open(file.url, uri_options).read) extname = File.extname(file.name).downcase - if extname == '.md' || MARKUP_FILES.include?(file[:name].downcase) - GitHub::Markup.render('README.md', content.data) - else - case - when content.image? - # Unfortunately, this has to be handled by file.erb - 'image' - when content.text? - FileContent.highlight_file(content.name, extname, content.data) - end + if extname == '.md' || @markup_files.include?(file[:name].downcase) + GitHub::Markup.render_s(GitHub::Markups::MARKUP_MARKDOWN, content.data.force_encoding("utf-8")) + elsif content.image? + # Unfortunately, this part has to be handled by views/file.erb + 'image' + elsif content.text? + FileContent.highlight_file(content.name, extname, content.data) end end def highlight_file(filename, extname, content) lexer = (Linguist::Language[extname.gsub(/^\./, '')] || Linguist::Language.find_by_filename(filename).first || - Linguist::Language[Linguist.interpreter_from_shebang(content)] + Linguist::Language.find_by_extension(extname).first ) if lexer - lexer.colorize(content, options: HIGHLIGHT_OPTIONS) + Pygments.highlight(content, lexer: lexer.name, options: @highlight_options) else - Pygments.highlight(content, lexer: 'text', options: HIGHLIGHT_OPTIONS) + Pygments.highlight(content, lexer: 'text', options: @highlight_options) end end end diff --git a/lib/chef-browser/helpers.rb b/lib/chef-browser/helpers.rb index 5c62540..f45ec85 100644 --- a/lib/chef-browser/helpers.rb +++ b/lib/chef-browser/helpers.rb @@ -1,9 +1,11 @@ +# frozen_string_literal: true + require 'chef-browser/app' module ChefBrowser module Helpers - COOKBOOK_FILE_TYPES = %w(attributes templates files definitions resources providers libraries) - .map(&:freeze).freeze + COOKBOOK_FILE_TYPES = %w[attributes templates files definitions resources providers libraries] + .map(&:freeze).freeze def chef_server @chef_server ||= settings.rb.ridley @@ -76,28 +78,35 @@ def pretty_value(value) def search_query @search_query || params['q'] - @search_query ||= (params['q'] && params['q'].strip) + @search_query ||= (params['q']&.strip) + end + + def map_resource(results, resource, data_bag = nil) + case resource + when :node then results + when :role then results.map { |attrs| Ridley::RoleObject.new(nil, attrs["data"]) } + when :environment then results.map { |attrs| Ridley::EnvironmentObject.new(nil, attrs["data"]) } + else results.map { |attrs| Ridley::DataBagItemObject.new(nil, data_bag, attrs["data"]) } + end end def search(search_query, resource, data_bag = nil) - search_query = "tags:*#{search_query}* OR roles:*#{search_query}* OR fqdn:*#{search_query}* OR addresses:*#{search_query}*" unless search_query[':'] + unless search_query[':'] + search_query = "tags:*#{search_query}* OR roles:*#{search_query}* OR \ + fqdn:*#{search_query}* OR addresses:*#{search_query}*" + end if settings.rb.use_partial_search resource = data_bag.chef_id if data_bag - results = chef_server.partial_search(resource, search_query, %w(chef_type name id)) - case resource - when :node then results - when :role then results.map { |attrs| Ridley::RoleObject.new(nil, attrs["data"]) } - when :environment then results.map { |attrs| Ridley::EnvironmentObject.new(nil, attrs["data"]) } - else results.map { |attrs| Ridley::DataBagItemObject.new(nil, data_bag, attrs["data"]) } + results = chef_server.partial_search(resource, search_query, %w[chef_type name id]) + map_resource(results, resource, data_bag) + elsif data_bag + # For data bag search, Ridley returns untyped Hashie::Mash, + # we want to augment it with our methods. + chef_server.search(data_bag.chef_id, search_query).map do |attrs| + Ridley::DataBagItemObject.new(nil, data_bag, attrs[:raw_data]) end else - if data_bag - # For data bag search, Ridley returns untyped Hashie::Mash, - # we want to augment it with our methods. - chef_server.search(data_bag.chef_id, search_query).map { |attrs| Ridley::DataBagItemObject.new(nil, data_bag, attrs[:raw_data]) } - else - chef_server.search(resource, search_query) - end + chef_server.search(resource, search_query) end end @@ -115,12 +124,16 @@ def resource_list(resource, data_bag = nil) def pretty_metadata(key, value) case key - when 'long_description' then GitHub::Markup.render('README.md', value) + when 'long_description' then GitHub::Markup.render_s(GitHub::Markups::MARKUP_MARKDOWN, value) when 'attributes' then nil - when 'maintainer_email' then "
#{key.capitalize.gsub('_', ' ')}:
#{value}
" - when 'platforms', 'dependencies', 'suggestions', 'conflicting', 'replacing', 'providing', 'recipes', 'recommendations', 'groupings' + when 'maintainer_email' + "
#{key.capitalize.tr('_', ' ')}:
#{value}
" + when 'issues_url', 'source_url' + "
#{key.capitalize.tr('_', ' ')}:
#{value}
" + when 'platforms', 'dependencies', 'suggestions', 'conflicting', + 'replacing', 'providing', 'recipes', 'recommendations', 'groupings' unless value.empty? - list = "
#{key.capitalize}: