inimit 0.0.1 → 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (6) hide show
  1. data/Gemfile +1 -0
  2. data/VERSION +1 -1
  3. data/bin/inimit +1 -1
  4. data/inimit.gemspec +79 -0
  5. data/lib/inimit.rb +128 -40
  6. metadata +3 -2
data/Gemfile CHANGED
@@ -4,6 +4,7 @@ source "http://rubygems.org"
4
4
  # gem "activesupport", ">= 2.3.5"
5
5
 
6
6
  gem 'paint', '~> 0.8.5'
7
+ #gem 'iopts', '~> 0.1.0'
7
8
 
8
9
  # Add dependencies to develop your gem here.
9
10
  # Include everything needed to run rake, tests, features, etc.
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.0.1
1
+ 0.0.2
data/bin/inimit CHANGED
@@ -1,4 +1,4 @@
1
1
  #!/usr/bin/env ruby
2
2
  require File.join(File.dirname(__FILE__), '..', 'lib', 'inimit')
3
3
 
4
- Inimit::compare *ARGV
4
+ Inimit::run *ARGV
@@ -0,0 +1,79 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = "inimit"
8
+ s.version = "0.0.2"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["AlphaHydrae"]
12
+ s.date = "2012-09-12"
13
+ s.description = "Compares files in two directories using SHA1."
14
+ s.email = "hydrae.alpha@gmail.com"
15
+ s.executables = ["inimit"]
16
+ s.extra_rdoc_files = [
17
+ "LICENSE.txt",
18
+ "README.md"
19
+ ]
20
+ s.files = [
21
+ ".rspec",
22
+ ".rvmrc",
23
+ ".screenrc",
24
+ ".travis.yml",
25
+ "Gemfile",
26
+ "Gemfile.lock",
27
+ "LICENSE.txt",
28
+ "README.md",
29
+ "Rakefile",
30
+ "VERSION",
31
+ "bin/inimit",
32
+ "inimit.gemspec",
33
+ "lib/inimit.rb",
34
+ "spec/helper.rb",
35
+ "spec/version_spec.rb"
36
+ ]
37
+ s.homepage = "http://github.com/AlphaHydrae/inimit"
38
+ s.licenses = ["MIT"]
39
+ s.require_paths = ["lib"]
40
+ s.rubygems_version = "1.8.24"
41
+ s.summary = "Recursive file comparison command."
42
+
43
+ if s.respond_to? :specification_version then
44
+ s.specification_version = 3
45
+
46
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
47
+ s.add_runtime_dependency(%q<paint>, ["~> 0.8.5"])
48
+ s.add_development_dependency(%q<bundler>, [">= 0"])
49
+ s.add_development_dependency(%q<rake>, [">= 0"])
50
+ s.add_development_dependency(%q<rspec>, [">= 0"])
51
+ s.add_development_dependency(%q<jeweler>, [">= 0"])
52
+ s.add_development_dependency(%q<gemcutter>, [">= 0"])
53
+ s.add_development_dependency(%q<gem-release>, [">= 0"])
54
+ s.add_development_dependency(%q<rake-version>, [">= 0"])
55
+ s.add_development_dependency(%q<simplecov>, [">= 0"])
56
+ else
57
+ s.add_dependency(%q<paint>, ["~> 0.8.5"])
58
+ s.add_dependency(%q<bundler>, [">= 0"])
59
+ s.add_dependency(%q<rake>, [">= 0"])
60
+ s.add_dependency(%q<rspec>, [">= 0"])
61
+ s.add_dependency(%q<jeweler>, [">= 0"])
62
+ s.add_dependency(%q<gemcutter>, [">= 0"])
63
+ s.add_dependency(%q<gem-release>, [">= 0"])
64
+ s.add_dependency(%q<rake-version>, [">= 0"])
65
+ s.add_dependency(%q<simplecov>, [">= 0"])
66
+ end
67
+ else
68
+ s.add_dependency(%q<paint>, ["~> 0.8.5"])
69
+ s.add_dependency(%q<bundler>, [">= 0"])
70
+ s.add_dependency(%q<rake>, [">= 0"])
71
+ s.add_dependency(%q<rspec>, [">= 0"])
72
+ s.add_dependency(%q<jeweler>, [">= 0"])
73
+ s.add_dependency(%q<gemcutter>, [">= 0"])
74
+ s.add_dependency(%q<gem-release>, [">= 0"])
75
+ s.add_dependency(%q<rake-version>, [">= 0"])
76
+ s.add_dependency(%q<simplecov>, [">= 0"])
77
+ end
78
+ end
79
+
@@ -1,83 +1,171 @@
1
1
  require 'find'
2
- require 'digest/sha1'
2
+ require 'digest'
3
3
  require 'paint'
4
+ require 'optparse'
5
+
6
+ # TODO: lenient option to only fail on mismatch
4
7
 
5
8
  module Inimit
6
- VERSION = '0.0.1'
9
+ VERSION = '0.0.2'
7
10
 
8
- def self.compare *args
11
+ class Opts < OptionParser
12
+ def to_s
13
+ "#{banner}\nOptions:\n#{summarize ''}"
14
+ end
15
+ end
9
16
 
10
- self.abort "A source and target directory are required (got #{args.length} arguments)" unless args.length == 2
17
+ def self.run *args
18
+
19
+ options = {
20
+ :verbose => 0,
21
+ :mismatch_only => false
22
+ }
23
+ Opts.new do |opts|
24
+ opts.banner = "#{Paint[opts.program_name, :bold]} recursively compares files.\n\nUsage:\n inimit [OPTION]... [SOURCE] TARGET\n"
25
+ opts.version = VERSION
26
+ opts.on('-d', '--digest ALGORITHM', 'Use ALGORITHM to hash file contents (available: md5, sha1, sha256, sha384, sha512)') do |digest|
27
+ options[:digest] = digest
28
+ end
29
+ opts.on('-m', '--mismatch', 'Only show files when missing or the content does not match') do
30
+ options[:mismatch_only] = true
31
+ end
32
+ opts.on('-v', '--verbose', 'Increase verbosity (once: show hash prefixes, twice: show full hashes)') do
33
+ options[:verbose] += 1
34
+ end
35
+ opts.on('-h', '--help', 'Show this help and exit'){ puts opts; exit 0 }
36
+ opts.on('-u', '--usage', 'Show this help and exit'){ puts opts; exit 0 }
37
+ end.parse! args
38
+
39
+ # check number of args
40
+ self.abort "A target directory must be given" if args.length < 1
41
+ self.abort "Only one source and target must be given (got #{args.length} arguments)" if args.length > 2
42
+
43
+ source, target = case args.length
44
+ when 1
45
+ [ '.', args.shift ]
46
+ when 2
47
+ [ args.shift, args.shift ]
48
+ end
11
49
 
12
- source, target = args.shift, args.shift
13
- self.abort "Source #{source} is not a directory" unless File.directory? source
14
- self.abort "Target #{target} is not a directory" unless File.directory? target
50
+ if !File.exists?(source)
51
+ self.abort "Unknown file or directory #{source}"
52
+ elsif !File.file?(source) and !File.directory?(source)
53
+ self.abort "Source #{source} is neither a file nor a directory"
54
+ elsif File.file?(source) and !File.file?(target)
55
+ self.abort "Source #{source} is a file, target #{target} is not"
56
+ elsif File.directory?(source) and !File.directory?(target)
57
+ self.abort "Source #{source} is a directory, target #{target} is not"
58
+ end
59
+
60
+ if File.directory? source
61
+ self.compare source, target, options
62
+ else
63
+ self.compare_files source, target, options
64
+ end
65
+ end
66
+
67
+ def self.compare source, target, options = {}
15
68
 
16
69
  source, target = File.expand_path(source), File.expand_path(target)
17
70
 
18
71
  puts
19
- puts "Comparing files in #{source} to #{target}:"
20
- puts
72
+ puts "Comparing files in #{source} to #{target}."
73
+ puts unless options[:mismatch_only]
21
74
 
22
75
  Dir.chdir source
23
- unknown = []
24
- different = []
76
+ total, match, unknown, mismatch = 0, 0, 0, 0
25
77
 
26
78
  Find.find '.' do |file|
27
79
 
28
80
  # skip directories
29
81
  next unless File.file? file
30
-
31
- print "- #{file.sub /^\.\//, ''}..."
82
+ total += 1
32
83
 
33
84
  source_file = File.join source, file
34
85
  target_file = File.join target, file
35
86
 
36
87
  if !File.file? target_file
37
- unknown << file
88
+ unknown += 1
38
89
  puts Paint[" not found", :yellow]
39
90
  next
40
91
  end
41
92
 
42
- source_sha1 = self.hash source_file
43
- target_sha1 = self.hash target_file
93
+ source_sha1 = self.hash source_file, options
94
+ target_sha1 = self.hash target_file, options
44
95
 
45
96
  if source_sha1 == target_sha1
46
- puts Paint[" #{source_sha1[0,7]} == #{target_sha1[0,7]}", :green]
97
+ match += 1
98
+ unless options[:mismatch_only]
99
+ print Paint[self.hash_notice(source_sha1, target_sha1, options), :green]
100
+ puts " #{file.sub /^\.\//, ''}..."
101
+ end
47
102
  else
48
- puts Paint[" #{source_sha1[0,7]} != #{target_sha1[0,7]}", :red]
49
- different << { :file => file, :source => source_sha1, :target => target_sha1 }
103
+ mismatch += 1
104
+ print Paint[self.hash_notice(source_sha1, target_sha1, options), :red]
105
+ puts " #{file.sub /^\.\//, ''}..."
50
106
  end
51
107
  end
52
108
 
53
- if different.empty? and unknown.empty?
109
+ puts
110
+
111
+ notice = "Checked #{total} files: "
112
+ notice << Paint["#{match} identical", :green]
113
+ notice << ", "
114
+ notice << Paint["#{unknown} not found", :yellow]
115
+ notice << ", "
116
+ notice << Paint["#{mismatch} different", :red]
117
+
118
+ puts notice
119
+ puts
120
+
121
+ exit 1 if unknown >= 1 or mismatch >= 1
122
+ end
123
+
124
+ def self.compare_files source, target, options = {}
125
+
126
+ source_sha1, target_sha1 = self.hash(source, options), self.hash(target, options)
127
+
128
+ puts
129
+
130
+ if source_sha1 == target_sha1
131
+ msg = "Files are identical"
132
+ msg << " (#{self.hash_notice source_sha1, target_sha1, options})" if options[:verbose] >= 1
133
+ msg << "."
134
+ puts Paint[msg, :green]
54
135
  puts
55
- puts Paint["All files in #{source} are the same in #{target}.", :green, :bold]
136
+ else
137
+ msg = "The contents of the files do not match"
138
+ msg << " (#{self.hash_notice source_sha1, target_sha1, options})" if options[:verbose] >= 1
139
+ msg << "."
140
+ puts Paint[msg, :red]
56
141
  puts
57
- return
142
+ exit 1
58
143
  end
144
+ end
59
145
 
60
- if unknown.any?
61
- puts
62
- puts Paint["The following files in #{source} were not found in #{target}:", :yellow, :bold]
63
- unknown.each do |file|
64
- puts "- #{file}"
146
+ def self.hash_notice h1, h2, options = {}
147
+ if h1 == h2
148
+ case options[:verbose]
149
+ when 1; "#{h1[0,7]} == #{h2[0,7]}"
150
+ when 2; "#{h1} == #{h2}"
151
+ else; "identical"
65
152
  end
66
- end
67
-
68
- if different.any?
69
- puts
70
- puts Paint["The following files in #{source} do not have the same content in #{target}:", :red, :bold]
71
- different.each do |h|
72
- puts "- #{h[:file]}"
153
+ else
154
+ case options[:verbose]
155
+ when 1..2; "#{h1} != #{h2}"
156
+ else; "different"
73
157
  end
74
158
  end
75
-
76
- puts
77
159
  end
78
160
 
79
- def self.hash file
80
- dig = Digest::SHA1.new
161
+ def self.hash file, options = {}
162
+ dig = case options[:digest]
163
+ when 'md5'; Digest::MD5.new
164
+ when 'sha256'; Digest::SHA256.new
165
+ when 'sha384'; Digest::SHA384.new
166
+ when 'sha512'; Digest::SHA512.new
167
+ else; Digest::SHA1.new
168
+ end
81
169
  File.open file, 'rb' do |io|
82
170
  dig.update io.readpartial(4096) while !io.eof
83
171
  end
@@ -85,7 +173,7 @@ module Inimit
85
173
  end
86
174
 
87
175
  def self.abort msg
88
- warn msg
89
- exit 1
176
+ warn Paint[msg, :yellow]
177
+ exit 2
90
178
  end
91
179
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: inimit
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-09-11 00:00:00.000000000 Z
12
+ date: 2012-09-12 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: paint
@@ -175,6 +175,7 @@ files:
175
175
  - Rakefile
176
176
  - VERSION
177
177
  - bin/inimit
178
+ - inimit.gemspec
178
179
  - lib/inimit.rb
179
180
  - spec/helper.rb
180
181
  - spec/version_spec.rb