Skip to content

Instantly share code, notes, and snippets.

@LadyNamedLaura
Created November 21, 2018 14:10
Show Gist options
  • Save LadyNamedLaura/cab8cd9aeb58849164799df1c1a9afe7 to your computer and use it in GitHub Desktop.
Save LadyNamedLaura/cab8cd9aeb58849164799df1c1a9afe7 to your computer and use it in GitHub Desktop.
update_fixtures.rb
require 'yaml'
require 'json'
require 'pathname'
require 'digest/sha1'
require 'optparse'
require 'ostruct'
options = OpenStruct.new
options.from_metadata = false
options.paths = []
options.puppetfile = nil
parser = OptionParser.new do |opts|
opts.accept(Pathname) do |path|
(Pathname.new path).cleanpath()
end
opts.banner = "Usage: update_fixtures.rb [options] [module]"
opts.on("-m", "--modulepath=PATH", Pathname,
'local module paths to load') do |path|
options.paths.push path
end
opts.on("-p", "--puppetfile=FILE", Pathname,
"path to the puppetfile") do | path|
p "puppetfie: #{path}"
end
opts.on("-r", "--recursive", "Walk the given directories in search of modules to update") do |r|
options.recursive = r
end
opts.on('--from-metadata',
'Get dependency data from metadata.json') do |m|
options.from_metadata = m
end
opts.on_tail("-h", "--help", "Show this message") do
puts opts
exit
end
end
parser.parse!
if ARGV.empty?
puts parser
exit
end
if options.puppetfile.nil?
options.treeroot = Pathname.pwd
else
options.treeroot = options.puppetfile.dirname
end
while options.puppetfile.nil?
if (options.treeroot+'Puppetfile').file?
options.puppetfile = (options.treeroot+'Puppetfile')
else
options.treeroot = options.treeroot.dirname
end
if options.treeroot.to_s == '/'
raise 'no Puppetfile found'
end
end
CACHEDIR=options.treeroot+'.modulecache'
CACHEDIR.mkpath
class PuppetModule
def initialize(fname)
@author, @name = fname.split(/[\/-]/)
end
def author
@author
end
def name
@name
end
def fullname
"#{@author}-#{@name}"
end
def install
end
def metadata
return @metadata unless @metadata.nil?
install
@metadata = JSON.parse((path+'metadata.json').read)
end
def path
CACHEDIR+fullname+version+name
end
def installed?
(path + 'metadata.json').file?
end
end
class ForgeModule < PuppetModule
def initialize(fname,version)
super(fname)
@version = version
end
def version
@version
end
def install
return if installed?
system("puppet module install -f -i #{path.dirname} -v #{version} #{fullname}")
end
def as_local
LocalModule.new ()
end
def fixturetype
"forge_modules"
end
def fixture(options)
{
name => {
'repo' => fullname,
'ref' => version,
}
}
end
end
class GitModule < PuppetModule
def initialize(fname, repourl, ref)
super(fname)
@url = repourl
@ref = ref
end
def version
@ref
end
def install
return if installed?
clonepath = CACHEDIR+'.gitrepos'+Digest::SHA1.hexdigest(@url)
unless clonepath.directory?
system("git clone --bare #{@url} #{clonepath}")
end
path.mkpath()
gitenv={
'GIT_DIR' => clonepath.to_s,
'GIT_WORK_TREE' => path.to_s,
}
system(gitenv,"git checkout -qf #{version}")
end
def fixturetype
"repositories"
end
def fixture(options)
{
name => {
'repo' => @url,
'ref' => @ref,
}
}
end
end
class LocalModule < PuppetModule
def initialize(name, path)
super("local/#{name}")
@path = path
end
def fixturetype
"symlinks"
end
def fixture(options)
if options.has_key? :relpath
relpath = path.relative_path_from(options[:relpath])
if relpath.to_s == '.'
p = '#{source_dir}'
else
p = '#{source_dir}/'+relpath.to_s
end
else
p = path.to_s
end
{
@name => p
}
end
def path
@path
end
end
class ModuleLib
def initialize(hash={})
@lib = hash
end
def add(mod)
@lib[mod.name] = mod
end
def addall(otherlib)
@lib.update otherlib.modules
end
def has?(name)
name = name.split(/[\/-]/).last
@lib.has_key?(name)
end
def get(name)
name = name.split(/[\/-]/).last
@lib[name]
end
def get_with_dependencies(name)
mods = ModuleLib.new
mods.add(get(name))
newmods = [get(name)]
while newmods.any? do
nextmods = []
newmods.each do |newmod|
newmod.metadata['dependencies'].each do |dep|
next if mods.has? dep['name']
next unless has? dep['name']
depmod = get dep['name']
nextmods.push depmod
mods.add depmod
end
end
newmods = nextmods
end
mods
end
def modules
@lib
end
def by_fixturetype(type)
modules.select {|k,v| v.fixturetype == type}
end
def to_fixtures(options)
fixtures = {}
['repositories','forge_modules','symlinks'].each do |type|
temp = {}
by_fixturetype(type).each do |name, mod|
temp.update mod.fixture(options)
end
fixtures[type] = Hash[ temp.sort_by { |key, val| key } ]
end
{
'fixtures' => fixtures
}
end
def import_from_puppetfile(filename)
PuppetFileBinding.new(self).get.eval(File.read(filename), filename.to_s)
end
def import_from_dir(dir)
dir = Pathname.new dir
dir = dir.realpath()
dir.each_child do |child|
add LocalModule.new child.basename, child
end
end
class PuppetFileBinding
def initialize(library)
@library = library
end
def mod(name, version = nil, git: nil, ref: nil, tag: nil)
if git.nil?
@library.add ForgeModule.new(name, version)
elsif tag.nil?
@library.add GitModule.new(name, git, ref)
else
@library.add GitModule.new(name, git, tag)
end
end
def get
binding
end
end
end
def find_modules(path)
return [] unless path.directory?
return [path.realpath] if (path + 'metadata.json').file?
modules = []
path.each_child do |child|
modules += find_modules child
end
return modules
end
# main
all = ModuleLib.new
all.import_from_puppetfile(options.puppetfile)
options.paths.each {|path| all.import_from_dir(path)}
modules = []
ARGV.each do |argument|
if options.recursive
modules = find_modules(Pathname.new(argument))
else
modules = [Pathname.new(argument).realpath]
end
end
modules.each do |moddir|
fixturesfile = moddir+'.fixtures.yml'
lib = ModuleLib.new(all.modules)
mod = LocalModule.new(moddir.basename, moddir)
lib.add(mod)
if options.from_metadata
outlib = lib.get_with_dependencies(mod.name)
else
outlib = ModuleLib.new
# work from existing .fixturs.yml
data = YAML.load(fixturesfile.read)
data['fixtures'].each do |type, fixtures|
fixtures.each_key do |name|
outlib.add(lib.get(name))
end
end
end
fixturesfile.open("w") do |file|
file.write(outlib.to_fixtures(:relpath => moddir).to_yaml)
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment