Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
Josef Stribny committed Apr 29, 2014
0 parents commit 1aaa295
Show file tree
Hide file tree
Showing 14 changed files with 946 additions and 0 deletions.
20 changes: 20 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
Copyright (c) Josef Strzibny.

Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
'Software'), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:

The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
17 changes: 17 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# WORK IN PROGRESS

# gem-compare

## Description

## Installation

## Usage

### Supported options

## Requirements

## Copyright

Released under the MIT license. Feel free to contribute!
39 changes: 39 additions & 0 deletions Rakefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
require 'rubygems/package_task'
require 'rake/testtask'
require 'rdoc/task'

gemspec = Gem::Specification.new do |s|
s.name = "gem-compare"
s.version = "0.0.1"
s.platform = Gem::Platform::RUBY
s.summary = "RubyGems plugin for comparing gem versions."
s.description = <<-EOF
`gem-compare` is a RubyGems plugin that helps to compare versions of the given gem.
It searches for differences in metadata as well as in files.
EOF
s.homepage = "http://github.com/strzibny/gem-compare"
s.licenses = ["MIT"]
s.author = "Josef Stribny"
s.email = "strzibny@strzibny.name"
s.required_ruby_version = ">= 1.9.3"
s.required_rubygems_version = ">= 1.8"
s.files = FileList["README.md", "LICENSE", "Rakefile",
"lib/**/*.rb", "lib/**/**/*.rb", "test/**/test*.rb"]
#TODO: add requires, diffy, rainbow
end

Gem::PackageTask.new gemspec do |pkg|
end

Rake::RDocTask.new do |rd|
rd.main = "README.md"
rd.rdoc_files.include("README.md", "lib/**/*.rb")
end

Rake::TestTask.new('test') do |t|
t.libs << 'test'
t.pattern = 'test/**/test*.rb'
t.verbose = true
end

task :default => [:test]
72 changes: 72 additions & 0 deletions lib/rubygems/commands/compare_command.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
require 'rubygems/command'
require 'rubygems/comparator'

class Gem::Commands::CompareCommand < Gem::Command
def initialize
super 'compare', 'Compare gem\'s versions and generate a report of changes.',
:output => Dir.pwd

add_option('-a', '--all', 'Show every comparison.') do
options[:log_all] = true
end

add_option('-k', '--keep-all', 'Keep downloaded and extracted gem files.') do
options[:keep_all] = true
end

add_option('-n', '--no-color', 'Do not colorize output.') do
options[:no_color] = true
end

add_option('-pPARAM', '--param=PARAM', 'Compare only a given paramater.') do |param, options|
options[:param] = param
end

add_option('-b', '--brief', 'Include only important changes in the report.') do
options[:log_all] = false
options[:brief] = true
end

end

def arguments # :nodoc:
args = <<-EOF
GEMNAME gem name
VERSION [VERSION ...] list of versions to compare
EOF
return args.gsub(/^\s+/, '')
end

def description # :nodoc:
desc = <<-EOF
gem-compare is a RubyGems plugin that compares versions of the given gem.
It searches for differences in metadata as well as in files.
EOF
return desc.gsub(/^\s+/, '')
end

def usage # :nodoc:
"#{program_name} GEMNAME VERSION [VERSION ...]"
end

def execute
gem_name = options[:args].shift
versions = options[:args]

# No gem specified
unless gem_name
raise Gem::CommandLineError,
"Please specify a gem (e.g. gem compare foo VERSION [VERSION ...])"
end

# No versions specified
if versions.empty?
raise Gem::CommandLineError,
"Please specify versions you want to compare (e.g. gem compare foo 0.1.0 0.2.0)"
end

comparator = Gem::Comparator.new(options)
comparator.compare_versions(gem_name, versions)
comparator.print_results
end
end
150 changes: 150 additions & 0 deletions lib/rubygems/comparator.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
#####
# TODO:
# - compare Gemfiles in detail
# - specs from rubygems json api
# https://api.rubygems.org/quick/Marshal.4.8/json-1.5.5-java.gemspec.rz
# - packagers brief mode
#####

require 'tmpdir'
require 'rbconfig'
require 'rainbow'
require 'rubygems/package'
require 'rubygems/dependency'
require 'rubygems/spec_fetcher'
require 'rubygems/comparator/base'
require 'rubygems/comparator/report'
require 'rubygems/comparator/spec_comparator'
require 'rubygems/comparator/file_list_comparator'
require 'rubygems/comparator/dependency_comparator'

class Gem::Comparator
include Gem::Comparator::Base
attr_accessor :options, :report

##
# Set the working dir and process options
#
# Creates temporal directory if the gem files shouldn't be kept

def initialize(options)
unless options[:keep_all]
options[:output] = Dir.mktmpdir
end

if options[:param]
unless ((SPEC_PARAMS.include? options[:param]) ||
(SPEC_FILES_PARAMS.include? options[:param]) ||
(DEPENDENCY_PARAMS.include? "#{options[:param]}".to_sym))
warn('Invalid parameter.')
exit 1
end
end

if options[:no_color]
Rainbow.enabled = false
end

@options = options
@output = {}

# Results from the comparison
@report = Gem::Comparator::Report.new
end

##
# Compare versions
#
# Compares file lists, requirements, other meta data

def compare_versions(gem_name, versions)
packages = []

# Expand versions (<=, >=, ~>) and sort them
versions = expand_versions(gem_name, versions)
if versions.size == 1
warn 'Only one version specified, no version to compare to. Specify at lease two versions.'
exit 1
end

versions.each do |version|
pkg = download_gem(gem_name, version)
packages << pkg
end

[SpecComparator, FileListComparator, DependencyComparator].each do |c|
comparator = c.new
@report = comparator.compare(packages, @report, @options)
end

# Clean up
FileUtils.rm_rf options[:output] unless options[:keep_all]
end

def print_results
info 'Printing results...'
@report.print
end

private

def expand_versions(gem_name, versions)
info 'Expanding versions...'
expanded = []
versions.each do |version|
if version =~ VERSION_REGEX
expanded << version
next
end
op, v = (version.scan /(>=|<=|~>)(.*)/).flatten

# Supported operator and version?
if OPERATORS.include?(op) && v =~ VERSION_REGEX
dep = Gem::Dependency.new gem_name, version
specs_and_sources, errors = Gem::SpecFetcher.fetcher.spec_for_dependency dep
specs_and_sources.each do |s|
expanded << s[0].version
end
else
warn "Unsupported version specification: #{version}, skipping."
end
end
expanded.uniq.map{ |v| Gem::Version.new v }.sort.map(&:to_s)
end

def gem_file_name(gem_name, version)
"#{gem_name}-#{version}.gem"
end

def download_gem(gem_name, version)
gem_file = gem_file_name(gem_name, version)

# Cache
if File.exists? File.join(@options[:output], gem_file)
info "#{gem_file} exists, using already downloaded file."

package = Gem::Package.new File.join(@options[:output], gem_file)
return package
end

dep = Gem::Dependency.new gem_name, version
specs_and_sources, errors = Gem::SpecFetcher.fetcher.spec_for_dependency dep
spec, source = specs_and_sources.max_by { |s,| s.version }

raise "Gem #{gem_name} in #{version} doesn't exist." if spec.nil?

Dir.chdir @options[:output] do
source.download spec
end

package = Gem::Package.new File.join(@options[:output], gem_file)
puts "#{gem_file} downloaded."

package
end

def download_gems?
options[:param] && SPEC_FILES_PARAMS.include?(options[:param])
end

end
42 changes: 42 additions & 0 deletions lib/rubygems/comparator/base.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
require 'rainbow'

class Gem::Comparator
module Base
include Gem::UserInteraction

class DiffCommandMissing < StandardError; end

SPACE = ' '
DEFAULT_INDENT = SPACE*7
OPERATORS = ['=', '!=', '>', '<', '>=', '<=', '~>']
VERSION_REGEX = /\A(\d+\.){0,}\d+(\.[a-zA-Z]+\d{0,1}){0,1}\z/
SHEBANG_REGEX = /\A#!.*/
FAIL = Rainbow('!!').red.bright
SUCCESS = Rainbow('OK').green.bright
SPEC_PARAMS = %w[ author authors name platform require_paths rubygems_version summary
license licenses bindir cert_chain description email executables
extensions homepage metadata post_install_message rdoc_options
required_ruby_version required_rubygems_version requirements
signing_key has_rdoc date version ].sort
SPEC_FILES_PARAMS = %w[ files test_files extra_rdoc_files ]
DEPENDENCY_PARAMS = [ :runtime, :development ]

private

def warn(msg)
say Rainbow("WARNING: #{msg}").red
end

def info(msg)
say msg if Gem.configuration.really_verbose
end

def check_diff_command_is_installed
begin
IO.popen('diff --version')
rescue Exception
raise DiffCommandMissing, 'Calling `diff` command failed. Do you have it installed?'
end
end
end
end
Loading

0 comments on commit 1aaa295

Please sign in to comment.