chef-steel 0.0.1

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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 0347e49b84d3e3d0e0075ce03507cbff50721da7
4
+ data.tar.gz: 289cfe57544a0f38d6ea7135f04a651a9055d39d
5
+ SHA512:
6
+ metadata.gz: 27be27899ebd28569bd4fe2527066d7e09eae74e48c4cf65e215415ccadeb53f735e1b1cea681236a7a9b53363f00c37983b1cced34362547c558e008556755c
7
+ data.tar.gz: 9806a3bcc0a3a19b157179892792171df82f8f49ba81da29fbc0aa469831da06a2e94892aa9ae5a7ae444134bd5d7ac5a87a551324cc8ea8371bc31854c372a2
@@ -0,0 +1 @@
1
+ *.gem
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2017 Ryan Frantz
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
@@ -0,0 +1,6 @@
1
+ # chef-steel
2
+ A tool to keep testing-related configurations up-to-date
3
+
4
+ ## See `chef-steel` in Action!
5
+
6
+ [![asciicast](https://asciinema.org/a/140663.png)](https://asciinema.org/a/140663)
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ chef_steel_lib = File.expand_path(File.join(File.dirname(__FILE__), "..", "lib"))
4
+ $:.unshift(chef_steel_lib)
5
+ require "chef/steel/runner"
6
+
7
+ Chef::Steel::Runner.new.run
@@ -0,0 +1,39 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path("../lib", __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require "chef/steel/version"
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "chef-steel"
8
+ spec.version = Chef::Steel::VERSION
9
+ spec.authors = ["Ryan Frantz"]
10
+ spec.email = ["ryanleefrantz@gmail.com"]
11
+
12
+ spec.summary = %q{A tool to keep testing-related configurations up-to-date}
13
+ spec.description = <<-EOD
14
+ Hone your tools with chef-steel!
15
+
16
+ chef-steel is a tool that can keep testing-related configurations up-to-date
17
+ within one or more repos.
18
+
19
+ If you work within many different repos that share a common set of testing tools
20
+ (i.e. Rubocop, Foodcritic, Travis, Jenkins, etc.) it can be easy for their
21
+ configuration to drift. With chef-steel you can update one or more of these
22
+ configuration files from a central repository, as needed.
23
+ EOD
24
+ spec.homepage = 'https://github.com/RyanFrantz/chef-steel'
25
+ spec.license = 'MIT'
26
+
27
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
28
+ f.match(%r{^(test|spec|features)/})
29
+ end
30
+ spec.bindir = "bin"
31
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
32
+ spec.require_paths = ["lib"]
33
+
34
+ spec.add_runtime_dependency 'choice', '~> 0.2', '>= 0.2.0'
35
+ spec.add_runtime_dependency 'colorize', '~> 0.8', '>= 0.8.1'
36
+
37
+ spec.add_development_dependency "rake", "~> 10.0"
38
+ spec.add_development_dependency "rspec", "~> 3.0"
39
+ end
@@ -0,0 +1,6 @@
1
+ require "chef/steel/version"
2
+
3
+ module Chef
4
+ module Steel
5
+ end
6
+ end
@@ -0,0 +1,11 @@
1
+ module Chef
2
+ module Steel
3
+
4
+ class MissingConfigFile < StandardError
5
+ end
6
+
7
+ class NotInAGitRepository < StandardError
8
+ end
9
+
10
+ end
11
+ end
@@ -0,0 +1,33 @@
1
+ require 'chef/steel/logger'
2
+ require 'digest'
3
+
4
+ module Chef
5
+ module Steel
6
+ module FileTools
7
+
8
+ include Chef::Steel::Logger
9
+
10
+ # Compare two files' digests to determine if they're the same file.
11
+ # Returns true/false based on the comparison of digest strings.
12
+ def same_file?(local_file, remote_file)
13
+ local_digest = Digest::SHA256.file(local_file).hexdigest
14
+ remote_digest = Digest::SHA256.file(remote_file).hexdigest
15
+ local_digest == remote_digest
16
+ end
17
+
18
+ # Copy a file from its source to its destination.
19
+ def copy_file(src, dst)
20
+ FileUtils.cp(src, dst)
21
+ end
22
+
23
+ # Remove the temporary directory using a naive guard to ensure we're
24
+ # deleting what we expect.
25
+ def clean_up(tmp_dir)
26
+ info "\nCleaning up temporary directory '#{tmp_dir}"
27
+ re_tmp_dir = Regexp.new('chef-steel')
28
+ FileUtils.rm_rf(tmp_dir) if tmp_dir.match(re_tmp_dir)
29
+ end
30
+
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,30 @@
1
+ require 'chef/steel/logger'
2
+
3
+ module Chef
4
+ module Steel
5
+ module Git
6
+
7
+ include Chef::Steel::Logger
8
+
9
+ # Clone a repo into a given destination. We assume the destination is
10
+ # a temporary directory.
11
+ def git_clone(repo, destination)
12
+ result = `git clone -q #{repo} #{destination}`
13
+ if $?.exitstatus != 0
14
+ error "Failed to clone #{repo} into #{destination} (Exit status: #{$?.exitstatus})!"
15
+ error "Result: #{result}" unless result.empty?
16
+ exit $?.exitstatus
17
+ end
18
+ end
19
+
20
+ def git_diff(local_file, remote_file)
21
+ result = `git diff --color=always #{local_file} #{remote_file}`
22
+ log ""
23
+ result.split("\n").each do |line|
24
+ raw_log "\t" + line
25
+ end
26
+ end
27
+
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,38 @@
1
+ module Chef
2
+ module Steel
3
+ module Logger
4
+
5
+ require 'colorize'
6
+
7
+ # Print the raw string (including escape sequences, if any).
8
+ def raw_log(msg = '')
9
+ puts msg
10
+ end
11
+
12
+ def prompt(msg = '')
13
+ print msg
14
+ end
15
+
16
+ def info(msg = '')
17
+ log(msg, :green)
18
+ end
19
+
20
+ def notice(msg = '')
21
+ log(msg, :light_magenta)
22
+ end
23
+
24
+ def warn(msg = '')
25
+ log(msg, :yellow)
26
+ end
27
+
28
+ def error(msg = '')
29
+ log(msg, :red)
30
+ end
31
+
32
+ def log(msg = '', color = nil)
33
+ puts msg.colorize(color)
34
+ end
35
+
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,216 @@
1
+ require 'chef/steel/git'
2
+ require 'chef/steel/exceptions'
3
+ require 'chef/steel/filetools'
4
+ require 'chef/steel/logger'
5
+ require 'chef/steel/version'
6
+ require 'choice'
7
+ require 'tmpdir' # Extends Dir
8
+ require 'yaml'
9
+
10
+ module Chef
11
+ module Steel
12
+ class Runner
13
+
14
+ include Chef::Steel::FileTools
15
+ include Chef::Steel::Git
16
+ include Chef::Steel::Logger
17
+
18
+ def initialize
19
+ @config = config
20
+ @clone_destination = nil # Wait until we need it...
21
+ @file_candidates = nil # Wait until we need it...
22
+ end
23
+
24
+ # Validate we're in a git repo. If not, we likely don't want to do anything.
25
+ def validate_in_repo
26
+ unless File.exist?('.git')
27
+ raise NotInAGitRepository, "We don't appear to be inside a Git repository!"
28
+ end
29
+ end
30
+
31
+ # A hash of values describing our config.
32
+ def config
33
+ @config ||= {}
34
+ end
35
+
36
+ # An array of path names for files that are candidates for being copied.
37
+ def file_candidates
38
+ @file_candidates ||= []
39
+ end
40
+
41
+ # Create a temporary directory to clone the repo into.
42
+ def clone_destination
43
+ @clone_destination ||= Dir.mktmpdir('chef-steel-')
44
+ end
45
+
46
+ # Look for and parse steel.yml.
47
+ # Supports a global and local config (in the repo).
48
+ # Order matters with latter configs' definitions overriding
49
+ # previous configs' values.
50
+ def parse_config
51
+ %w(/etc/steel/steel/yml steel.yml).each do |cfg|
52
+ if File.exist?(cfg)
53
+ begin
54
+ y = YAML.load_file(cfg)
55
+ rescue Psych::SyntaxError => e
56
+ error "[#{e.class}] Failed to parse '#{cfg}'!!"
57
+ error e.message
58
+ exit 1
59
+ end
60
+ # Merge the contents of the config into @config.
61
+ config.merge!(y)
62
+ end
63
+ end
64
+ end
65
+
66
+ # Command line options.
67
+ def parse_options
68
+ Choice.options do
69
+ header ''
70
+ header 'Hone your tools!'
71
+ header ''
72
+ header 'Options:'
73
+
74
+ default_xfiles = %w(
75
+ .gitignore
76
+ Policyfile.rb
77
+ README.md
78
+ metadata.rb
79
+ )
80
+ option :exclude_files do
81
+ short '-e'
82
+ long '--exclude-files *XFILES' # Yeah, XFILES!
83
+ desc 'One or more explicit file names (space-separated) to *exclude* from being copied from the repo'
84
+ desc 'Ex. -e README.md'
85
+ desc 'Ex. --exclude-files README.md metadata.rb'
86
+ desc "Default: #{default_xfiles.to_s}"
87
+ default default_xfiles
88
+ end
89
+
90
+ option :files do
91
+ short '-f'
92
+ long '--files *FILES'
93
+ desc 'One or more explicit file names (space-separated) to copy from the repo'
94
+ desc 'Ex. -f .rubocop.yml'
95
+ desc 'Ex. --files .rubocop.yml .rspec'
96
+ desc 'Default: All top-level files in the repo (excluding the value if --exclude-files)'
97
+ end
98
+
99
+ option :repo do
100
+ short '-r'
101
+ long '--repo'
102
+ desc 'The full URL of a repo containing config files to clone'
103
+ end
104
+
105
+ option :version do
106
+ short '-v'
107
+ long '--version'
108
+ desc 'Show version and exit'
109
+ action do
110
+ puts "chef-steel v#{Chef::Steel::VERSION}"
111
+ exit
112
+ end
113
+ end
114
+
115
+ option :answer_yes do
116
+ short '-y'
117
+ long '--yes'
118
+ desc 'Answer "yes" to all prompts'
119
+ desc 'Default: false'
120
+ default false
121
+ end
122
+
123
+ footer ''
124
+ end
125
+ # Merge command line options on top of the config values.
126
+ # Command line option win. Every. Time.
127
+ config.merge!(Choice.choices)
128
+ end
129
+
130
+ # After we've parsed the config and command line options, let's
131
+ # ensure the final config includes a minimal set of options.
132
+ def validate_config
133
+ if config['repo'].nil? || !config['repo'].is_a?(String) || config['repo'].empty?
134
+ error "No 'repo' value found in 'steel.yml' or on the command line!"
135
+ error "Which repo would you like to pull files from?"
136
+ Choice.help
137
+ exit 2
138
+ end
139
+ end
140
+
141
+ # Locate potential files at the top of the directory.
142
+ # We won't traverse into any subdirectories.
143
+ # use Dir.glob rather than Find.find as the latter doesn't
144
+ # provide a native way to minimize depth (like the `find` command).
145
+ # File::FNM_DOTMATCH is handy flag that surfaces dotfiles.
146
+ def find_top_files(clone_destination)
147
+ globule = File.join(clone_destination, '*') # Define a variable here to the next line is legible.
148
+ Dir.glob(globule, File::FNM_DOTMATCH).each do |path|
149
+ next if File.directory?(path) # Should omit '.' and '..' as well.
150
+ next if config['exclude_files'].include? File.basename(path)
151
+ unless config['files'].nil? || config['files'].empty?
152
+ next unless config['files'].include? File.basename(path)
153
+ end
154
+ file_candidates << path
155
+ end
156
+ end
157
+
158
+ # Copy files from the cloned repo into this local repo.
159
+ def copy_files
160
+ file_candidates.each do |remote_file|
161
+ local_file = File.basename(remote_file)
162
+ if File.exist?(local_file)
163
+ if same_file?(local_file, remote_file)
164
+ info "\n>> '#{local_file}' has the same contents here as in the repo. Leaving it alone."
165
+ else
166
+ if config['answer_yes']
167
+ warn "\n>> '#{local_file}' is different than its counterpart in the repo."
168
+ info "Copying #{remote_file} to #{local_file}... (answer_yes is true)"
169
+ copy_file(remote_file, local_file)
170
+ else
171
+ warn "\n>> '#{local_file}' is different than its counterpart in the repo (see below)"
172
+ git_diff(local_file, remote_file)
173
+ prompt "\nDo you want to overwrite #{local_file} with the version from the repo? [y/N]: "
174
+
175
+ answer = $stdin.gets.chomp
176
+ case answer
177
+ when ''
178
+ error 'Moving on.' # Default behavior.
179
+ when /y/i
180
+ info "Copying #{remote_file} to #{local_file}..."
181
+ copy_file(remote_file, local_file)
182
+ when /n/i
183
+ error 'Moving on.'
184
+ else
185
+ error 'Unknown selection. Moving on.'
186
+ end
187
+ end
188
+
189
+ end
190
+ else
191
+ info "\n>> '#{local_file}' does not exist locally."
192
+ info "Copying #{remote_file} to #{local_file}..."
193
+ copy_file(remote_file, local_file)
194
+ end
195
+ end
196
+ end
197
+
198
+ def run
199
+ validate_in_repo
200
+ parse_config
201
+ parse_options
202
+ validate_config
203
+
204
+ # Do work, son.
205
+ repo = config['repo']
206
+ log "\nCloning #{repo} into #{clone_destination}..."
207
+ git_clone(repo, clone_destination)
208
+ find_top_files(clone_destination)
209
+ copy_files
210
+ clean_up(clone_destination)
211
+ info "\nAll done!"
212
+ end
213
+
214
+ end
215
+ end
216
+ end
@@ -0,0 +1,5 @@
1
+ module Chef
2
+ module Steel
3
+ VERSION = "0.0.1"
4
+ end
5
+ end
@@ -0,0 +1,21 @@
1
+ # chef-steel
2
+ # Define values that instruct steel what to do. These values can be overridden
3
+ # on the command line.
4
+
5
+ # Set 'repo' to the URL of a repository from which steel will clone and copy
6
+ # top-level files.
7
+ repo: git@github.com:RyanFrantz/chef-testing-configs.git
8
+ #repo: https://github.com/RyanFrantz/chef-testing-configs.git
9
+
10
+ # Set 'answer_yes' to true to perform any operation that steel would normally prompt for.
11
+ #answer_yes: true
12
+
13
+ # 'files' is a list of files that should explicitly be copied.
14
+ #files:
15
+ # - .rubocop.yml
16
+ # - Jenkinsfile
17
+
18
+ # 'exclude_files' is a list of files that should not be copied.
19
+ #exclude_files:
20
+ # - .gitignore
21
+ # - .rspec
metadata ADDED
@@ -0,0 +1,137 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: chef-steel
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Ryan Frantz
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2017-10-03 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: choice
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '0.2'
20
+ - - ">="
21
+ - !ruby/object:Gem::Version
22
+ version: 0.2.0
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - "~>"
28
+ - !ruby/object:Gem::Version
29
+ version: '0.2'
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: 0.2.0
33
+ - !ruby/object:Gem::Dependency
34
+ name: colorize
35
+ requirement: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: '0.8'
40
+ - - ">="
41
+ - !ruby/object:Gem::Version
42
+ version: 0.8.1
43
+ type: :runtime
44
+ prerelease: false
45
+ version_requirements: !ruby/object:Gem::Requirement
46
+ requirements:
47
+ - - "~>"
48
+ - !ruby/object:Gem::Version
49
+ version: '0.8'
50
+ - - ">="
51
+ - !ruby/object:Gem::Version
52
+ version: 0.8.1
53
+ - !ruby/object:Gem::Dependency
54
+ name: rake
55
+ requirement: !ruby/object:Gem::Requirement
56
+ requirements:
57
+ - - "~>"
58
+ - !ruby/object:Gem::Version
59
+ version: '10.0'
60
+ type: :development
61
+ prerelease: false
62
+ version_requirements: !ruby/object:Gem::Requirement
63
+ requirements:
64
+ - - "~>"
65
+ - !ruby/object:Gem::Version
66
+ version: '10.0'
67
+ - !ruby/object:Gem::Dependency
68
+ name: rspec
69
+ requirement: !ruby/object:Gem::Requirement
70
+ requirements:
71
+ - - "~>"
72
+ - !ruby/object:Gem::Version
73
+ version: '3.0'
74
+ type: :development
75
+ prerelease: false
76
+ version_requirements: !ruby/object:Gem::Requirement
77
+ requirements:
78
+ - - "~>"
79
+ - !ruby/object:Gem::Version
80
+ version: '3.0'
81
+ description: |2
82
+ Hone your tools with chef-steel!
83
+
84
+ chef-steel is a tool that can keep testing-related configurations up-to-date
85
+ within one or more repos.
86
+
87
+ If you work within many different repos that share a common set of testing tools
88
+ (i.e. Rubocop, Foodcritic, Travis, Jenkins, etc.) it can be easy for their
89
+ configuration to drift. With chef-steel you can update one or more of these
90
+ configuration files from a central repository, as needed.
91
+ email:
92
+ - ryanleefrantz@gmail.com
93
+ executables:
94
+ - steel
95
+ extensions: []
96
+ extra_rdoc_files: []
97
+ files:
98
+ - ".gitignore"
99
+ - ".rspec"
100
+ - LICENSE.txt
101
+ - README.md
102
+ - Rakefile
103
+ - bin/steel
104
+ - chef-steel.gemspec
105
+ - lib/chef/steel.rb
106
+ - lib/chef/steel/exceptions.rb
107
+ - lib/chef/steel/filetools.rb
108
+ - lib/chef/steel/git.rb
109
+ - lib/chef/steel/logger.rb
110
+ - lib/chef/steel/runner.rb
111
+ - lib/chef/steel/version.rb
112
+ - steel.yml.example
113
+ homepage: https://github.com/RyanFrantz/chef-steel
114
+ licenses:
115
+ - MIT
116
+ metadata: {}
117
+ post_install_message:
118
+ rdoc_options: []
119
+ require_paths:
120
+ - lib
121
+ required_ruby_version: !ruby/object:Gem::Requirement
122
+ requirements:
123
+ - - ">="
124
+ - !ruby/object:Gem::Version
125
+ version: '0'
126
+ required_rubygems_version: !ruby/object:Gem::Requirement
127
+ requirements:
128
+ - - ">="
129
+ - !ruby/object:Gem::Version
130
+ version: '0'
131
+ requirements: []
132
+ rubyforge_project:
133
+ rubygems_version: 2.6.11
134
+ signing_key:
135
+ specification_version: 4
136
+ summary: A tool to keep testing-related configurations up-to-date
137
+ test_files: []