From 981075f829d1aca2334e114dd5cccacc7e78f27e Mon Sep 17 00:00:00 2001 From: PeterHattyar Date: Thu, 14 Dec 2023 09:39:33 +0000 Subject: [PATCH 1/2] Added fetching methods to github, addressing issues due to discrepancies, and related tests --- lib/version_checker.rb | 41 +++++++++++++++++++++++++++++++----- spec/example.gemspec | 7 ++++++ spec/version_checker_spec.rb | 28 ++++++++++++------------ 3 files changed, 58 insertions(+), 18 deletions(-) create mode 100644 spec/example.gemspec diff --git a/lib/version_checker.rb b/lib/version_checker.rb index 5a58b01..aeeb8c6 100644 --- a/lib/version_checker.rb +++ b/lib/version_checker.rb @@ -8,20 +8,51 @@ class HttpError < RuntimeError end +class GemspecError < RuntimeError +end + class VersionChecker - def fetch_rubygems_version(gemname) - uri = URI("https://rubygems.org/api/v1/gems/#{gemname}.json") + def fetch_all_govuk_gem_versions + uri = URI("https://docs.publishing.service.gov.uk/gems.json") res = Net::HTTP.get_response(uri) raise HttpError unless res.is_a?(Net::HTTPSuccess) parsed_json = JSON.parse(res.body) - parsed_json['version'] + govuk_gem_names = parsed_json.map { |gem| gem["app_name"] } + + govuk_versions = govuk_gem_names.map { |name| fetch_github_version(name) }.compact + rubygems_versions = govuk_gem_names.map { |name| fetch_rubygems_version(name) }.compact + puts govuk_versions + puts rubygems_versions + end + + def fetch_rubygems_version(gemname) + uri = URI("https://rubygems.org/api/v1/gems/#{gemname}.json") + res = Net::HTTP.get_response(uri) + + if res.is_a?(Net::HTTPSuccess) + parsed_json = JSON.parse(res.body) + parsed_json['version'] + end end def fetch_github_version(gemname) - payload = Octokit.contents("alphagov/#{gemname}", path: "lib/#{gemname}/version.rb") - Base64.decode64(payload[:content]).split('"')[1] + Dir.mktmpdir do |path| + Dir.chdir(path) do + clone_github_repo(gemname) + gemspecs = Dir.glob("*.gemspec", base: gemname) + if gemspecs.count == 1 + Gem::Specification::load("#{gemname}/#{gemspecs.first}").version.to_s + else + nil + end + end + end + end + + def clone_github_repo(name) + `git clone --recursive --depth 1 --shallow-submodules git@github.com:alphagov/#{name}.git > /dev/null 2>&1` end def rubygems_and_github_match?(gemname) diff --git a/spec/example.gemspec b/spec/example.gemspec new file mode 100644 index 0000000..201d6a6 --- /dev/null +++ b/spec/example.gemspec @@ -0,0 +1,7 @@ +Gem::Specification.new do |s| + s.name = 'example' + s.version = "1.2.3" + s.authors = ["Ruby Coder"] + s.files = ["lib/example.rb"] + s.summary = "This is an example!" +end \ No newline at end of file diff --git a/spec/version_checker_spec.rb b/spec/version_checker_spec.rb index 3098510..b8ac83a 100644 --- a/spec/version_checker_spec.rb +++ b/spec/version_checker_spec.rb @@ -7,38 +7,40 @@ it 'fetches version number of a gem from rubygems' do stub_rubygems_call('6.0.1') - expect(subject.fetch_rubygems_version('govuk_app_config')).to eq('6.0.1') + expect(subject.fetch_rubygems_version('example')).to eq('6.0.1') end it 'fetches version number of a gem from GitHub' do - stub_github_call('9.7.0') + stub_github_call - expect(subject.fetch_github_version('govuk_app_config')).to eq('9.7.0') + expect(subject.fetch_github_version('example')).to eq('1.2.3') end it "compares versions of gems on GitHub and rubygems and it's a match" do - stub_github_call('9.7.0') - stub_rubygems_call('9.7.0') + stub_github_call + stub_rubygems_call('1.2.3') - expect(subject.rubygems_and_github_match?('govuk_app_config')).to eq(true) + expect(subject.rubygems_and_github_match?('example')).to eq(true) end it "compares versions of gems on GitHub and rubygems and it's not a match" do - stub_github_call('9.7.0') + stub_github_call stub_rubygems_call('9.7.1') - expect(subject.rubygems_and_github_match?('govuk_app_config')).to eq(false) + expect(subject.rubygems_and_github_match?('example')).to eq(false) end - def stub_github_call(version) - repo = { content: Base64.encode64(%(module GovukAppConfig\n VERSION = "#{version}".freeze\nend\n)) } - stub_request(:get, 'https://api.github.com/repos/alphagov/govuk_app_config/contents/lib/govuk_app_config/version.rb') - .to_return(status: 200, body: repo.to_json, headers: { 'Content-Type' => 'application/json' }) + def stub_github_call + allow(subject).to receive(:clone_github_repo) do + FileUtils.mkdir "example" + FileUtils.cp File.join(__dir__, "example.gemspec"), File.join(Dir.pwd, "example") + # Copy a dummy gemspec into the current working directory. + end end def stub_rubygems_call(version) repo = { "version": version } - stub_request(:get, 'https://rubygems.org/api/v1/gems/govuk_app_config.json') + stub_request(:get, 'https://rubygems.org/api/v1/gems/example.json') .to_return(status: 200, body: repo.to_json, headers: {}) end end From 3cffa491d6096925cc4e6685904bc3d63f4d5081 Mon Sep 17 00:00:00 2001 From: PeterHattyar Date: Fri, 15 Dec 2023 11:38:18 +0000 Subject: [PATCH 2/2] Added comparison method and appropriate responses, created rake task and relevant tests. --- Gemfile | 1 + Gemfile.lock | 2 ++ Rakefile | 7 +++++ lib/version_checker.rb | 56 ++++++++++++++++++++---------------- spec/version_checker_spec.rb | 34 ++++++++++++++++------ 5 files changed, 67 insertions(+), 33 deletions(-) create mode 100644 Rakefile diff --git a/Gemfile b/Gemfile index 2126866..fb4e025 100644 --- a/Gemfile +++ b/Gemfile @@ -4,6 +4,7 @@ source 'https://rubygems.org' gem 'octokit' gem 'webmock' +gem 'rake' group :development, :test do gem 'rspec' diff --git a/Gemfile.lock b/Gemfile.lock index 511b4e3..bfad1f0 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -17,6 +17,7 @@ GEM faraday (>= 1, < 3) sawyer (~> 0.9) public_suffix (5.0.3) + rake (13.1.0) rexml (3.2.6) rspec (3.12.0) rspec-core (~> 3.12.0) @@ -45,6 +46,7 @@ PLATFORMS DEPENDENCIES octokit + rake rspec webmock diff --git a/Rakefile b/Rakefile new file mode 100644 index 0000000..248c2d5 --- /dev/null +++ b/Rakefile @@ -0,0 +1,7 @@ +require './lib/version_checker' + +task default: %w[gem_release_alert] + +task :gem_release_alert do + VersionChecker.new.print_version_discrepancies +end diff --git a/lib/version_checker.rb b/lib/version_checker.rb index aeeb8c6..f16f001 100644 --- a/lib/version_checker.rb +++ b/lib/version_checker.rb @@ -8,42 +8,52 @@ class HttpError < RuntimeError end -class GemspecError < RuntimeError -end - class VersionChecker - def fetch_all_govuk_gem_versions + def print_version_discrepancies + discrepancies_found = false + + fetch_all_govuk_gemspecs.each do |gemspec| + rubygems_version = fetch_rubygems_version(gemspec.name) + unless gemspec.version == rubygems_version + discrepancies_found = true + puts "Version mismatch for '#{gemspec.name}':" + puts " Version on RubyGems: #{rubygems_version}" + puts " Version on GitHub: #{gemspec.version}" + end + end + + unless discrepancies_found + puts "No discrepancies found!" + end + end + + def fetch_all_govuk_gemspecs uri = URI("https://docs.publishing.service.gov.uk/gems.json") res = Net::HTTP.get_response(uri) raise HttpError unless res.is_a?(Net::HTTPSuccess) - parsed_json = JSON.parse(res.body) - govuk_gem_names = parsed_json.map { |gem| gem["app_name"] } - - govuk_versions = govuk_gem_names.map { |name| fetch_github_version(name) }.compact - rubygems_versions = govuk_gem_names.map { |name| fetch_rubygems_version(name) }.compact - puts govuk_versions - puts rubygems_versions + JSON.parse(res.body) + .map { |gem| fetch_gemspec(gem["app_name"]) } + .compact end - def fetch_rubygems_version(gemname) - uri = URI("https://rubygems.org/api/v1/gems/#{gemname}.json") + def fetch_rubygems_version(gem_name) + uri = URI("https://rubygems.org/api/v1/gems/#{gem_name}.json") res = Net::HTTP.get_response(uri) - if res.is_a?(Net::HTTPSuccess) - parsed_json = JSON.parse(res.body) - parsed_json['version'] - end + raise HttpError unless res.is_a?(Net::HTTPSuccess) + + JSON.parse(res.body)['version'] end - def fetch_github_version(gemname) + def fetch_gemspec(repo_name) Dir.mktmpdir do |path| Dir.chdir(path) do - clone_github_repo(gemname) - gemspecs = Dir.glob("*.gemspec", base: gemname) + clone_github_repo(repo_name) + gemspecs = Dir.glob("*.gemspec", base: repo_name) if gemspecs.count == 1 - Gem::Specification::load("#{gemname}/#{gemspecs.first}").version.to_s + Gem::Specification::load("#{repo_name}/#{gemspecs.first}") else nil end @@ -54,8 +64,4 @@ def fetch_github_version(gemname) def clone_github_repo(name) `git clone --recursive --depth 1 --shallow-submodules git@github.com:alphagov/#{name}.git > /dev/null 2>&1` end - - def rubygems_and_github_match?(gemname) - fetch_rubygems_version(gemname) == fetch_github_version(gemname) - end end diff --git a/spec/version_checker_spec.rb b/spec/version_checker_spec.rb index b8ac83a..2a1c591 100644 --- a/spec/version_checker_spec.rb +++ b/spec/version_checker_spec.rb @@ -4,6 +4,8 @@ require 'base64' RSpec.describe VersionChecker do + let(:example_gemspec_path) { File.join(__dir__, "example.gemspec") } + it 'fetches version number of a gem from rubygems' do stub_rubygems_call('6.0.1') @@ -13,28 +15,38 @@ it 'fetches version number of a gem from GitHub' do stub_github_call - expect(subject.fetch_github_version('example')).to eq('1.2.3') + expect(subject.fetch_gemspec('example').version).to eq('1.2.3') + end + + it "fetches gemspecs for all govuk repos" do + stub_devdocs_call + stub_github_call + + expect(subject.fetch_all_govuk_gemspecs).to eq([Gem::Specification.load(example_gemspec_path)]) end - it "compares versions of gems on GitHub and rubygems and it's a match" do + it "detects when there are no version discrepancies" do + stub_devdocs_call stub_github_call stub_rubygems_call('1.2.3') - expect(subject.rubygems_and_github_match?('example')).to eq(true) + expect { subject.print_version_discrepancies }.to output("No discrepancies found!\n").to_stdout end - it "compares versions of gems on GitHub and rubygems and it's not a match" do + it "detects when there are are version discrepancies" do + stub_devdocs_call stub_github_call - stub_rubygems_call('9.7.1') + stub_rubygems_call('1.2.2') - expect(subject.rubygems_and_github_match?('example')).to eq(false) + expect { subject.print_version_discrepancies }.to output( + "Version mismatch for 'example':\n Version on RubyGems: 1.2.2\n Version on GitHub: 1.2.3\n" + ).to_stdout end def stub_github_call allow(subject).to receive(:clone_github_repo) do FileUtils.mkdir "example" - FileUtils.cp File.join(__dir__, "example.gemspec"), File.join(Dir.pwd, "example") - # Copy a dummy gemspec into the current working directory. + FileUtils.cp example_gemspec_path, File.join(Dir.pwd, "example") end end @@ -43,4 +55,10 @@ def stub_rubygems_call(version) stub_request(:get, 'https://rubygems.org/api/v1/gems/example.json') .to_return(status: 200, body: repo.to_json, headers: {}) end + + def stub_devdocs_call + repo = [{ "app_name": "example" }] + stub_request(:get, 'https://docs.publishing.service.gov.uk/gems.json') + .to_return(status: 200, body: repo.to_json, headers: {}) + end end