r10k 1.5.1 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +15 -0
- data/.travis.yml +0 -1
- data/CHANGELOG.mkd +63 -0
- data/Gemfile +2 -2
- data/README.mkd +1 -10
- data/doc/dynamic-environments/configuration.mkd +24 -6
- data/doc/dynamic-environments/quickstart.mkd +1 -1
- data/integration/Gemfile +1 -1
- data/integration/configs/pe/centos-5-64mda +25 -0
- data/integration/configs/pe/centos-6-64mda +1 -1
- data/integration/configs/pe/centos-7-64mda +1 -1
- data/integration/configs/pe/debian-6-64mda +1 -1
- data/integration/configs/pe/debian-7-64mda +1 -1
- data/integration/configs/pe/redhat-6-64mda +1 -1
- data/integration/configs/pe/redhat-7-64mda +1 -1
- data/integration/configs/pe/sles-11-64mda +1 -1
- data/integration/configs/pe/sles-12-64mda +25 -0
- data/integration/configs/pe/ubuntu-1004-64mda +1 -1
- data/integration/configs/pe/ubuntu-1204-64mda +1 -1
- data/integration/configs/pe/ubuntu-1404-64mda +1 -1
- data/integration/files/pre-suite/git_config.pp.erb +19 -0
- data/integration/pre-suite/01_git_config.rb +5 -17
- data/integration/pre-suite/02_pe_r10k.rb +0 -2
- data/integration/scripts/README.mkd +86 -0
- data/integration/scripts/setup_r10k_env_centos5.sh +23 -0
- data/integration/scripts/setup_r10k_env_centos6.sh +23 -0
- data/integration/scripts/setup_r10k_env_rhel7.sh +23 -0
- data/integration/scripts/setup_r10k_env_sles11.sh +23 -0
- data/integration/scripts/setup_r10k_env_sles12.sh +23 -0
- data/integration/scripts/setup_r10k_env_ubuntu1004.sh +23 -0
- data/integration/scripts/setup_r10k_env_ubuntu1204.sh +23 -0
- data/integration/scripts/setup_r10k_env_ubuntu1404.sh +23 -0
- data/integration/tests/basic_functionality/negative/neg_invalid_git_provider.rb +44 -0
- data/integration/tests/basic_functionality/rugged_git_provider_with_ssh.rb +106 -0
- data/integration/tests/basic_functionality/rugged_git_provider_without_ssh.rb +107 -0
- data/integration/tests/git_source/git_source_git.rb +1 -1
- data/integration/tests/git_source/git_source_ssh.rb +1 -1
- data/integration/tests/git_source/negative/neg_git_unauthorized_https.rb +1 -1
- data/integration/tests/git_source/negative/neg_git_unauthorized_ssh.rb +1 -1
- data/integration/tests/user_scenario/basic_workflow/negative/neg_bad_basedir.rb +1 -1
- data/integration/tests/user_scenario/basic_workflow/negative/neg_bad_git_module.rb +8 -2
- data/integration/tests/user_scenario/basic_workflow/negative/neg_bad_git_module_ref.rb +1 -1
- data/integration/tests/user_scenario/basic_workflow/negative/neg_bad_git_remote.rb +1 -1
- data/integration/tests/user_scenario/basic_workflow/negative/neg_branch_name_collision.rb +1 -1
- data/integration/tests/user_scenario/basic_workflow/negative/neg_disk_full.rb +5 -1
- data/integration/tests/user_scenario/basic_workflow/negative/neg_duplicate_module_names.rb +3 -3
- data/integration/tests/user_scenario/basic_workflow/negative/neg_invalid_puppet_file.rb +1 -1
- data/integration/tests/user_scenario/basic_workflow/negative/neg_read_only.rb +1 -1
- data/integration/tests/user_scenario/basic_workflow/single_env_10000_files.rb +4 -0
- data/integration/tests/user_scenario/basic_workflow/single_env_large_files.rb +4 -0
- data/integration/tests/user_scenario/complex_workflow/multi_env_unamanaged.rb +77 -0
- data/integration/tests/user_scenario/complex_workflow/single_env_git_module_update.rb +99 -0
- data/lib/r10k/action/deploy/display.rb +0 -1
- data/lib/r10k/action/runner.rb +20 -0
- data/lib/r10k/cli.rb +0 -3
- data/lib/r10k/cli/deploy.rb +0 -3
- data/lib/r10k/deployment.rb +1 -7
- data/lib/r10k/deployment/config.rb +15 -1
- data/lib/r10k/deployment/config/loader.rb +0 -11
- data/lib/r10k/environment/git.rb +0 -9
- data/lib/r10k/environment/svn.rb +0 -9
- data/lib/r10k/errors.rb +0 -3
- data/lib/r10k/feature.rb +24 -3
- data/lib/r10k/forge/module_release.rb +142 -0
- data/lib/r10k/git.rb +32 -11
- data/lib/r10k/module/forge.rb +25 -54
- data/lib/r10k/module_repository/forge.rb +2 -5
- data/lib/r10k/puppetfile.rb +1 -1
- data/lib/r10k/util/license.rb +20 -0
- data/lib/r10k/version.rb +1 -1
- data/lib/shared/puppet_forge/connection.rb +62 -0
- data/lib/shared/puppet_forge/error.rb +28 -0
- data/lib/shared/puppet_forge/tar.rb +10 -0
- data/lib/shared/puppet_forge/tar/mini.rb +81 -0
- data/lib/shared/puppet_forge/unpacker.rb +68 -0
- data/lib/shared/puppet_forge/v3.rb +13 -0
- data/lib/shared/puppet_forge/v3/module.rb +66 -0
- data/lib/shared/puppet_forge/v3/module_release.rb +73 -0
- data/lib/shared/puppet_forge/version.rb +3 -0
- data/r10k.gemspec +2 -2
- data/r10k.yaml.example +80 -5
- data/spec/spec_helper.rb +0 -12
- data/spec/unit/action/runner_spec.rb +53 -0
- data/spec/unit/deployment/config/loader_spec.rb +5 -19
- data/spec/unit/deployment/config_spec.rb +8 -0
- data/spec/unit/deployment_spec.rb +1 -1
- data/spec/unit/forge/module_release_spec.rb +130 -0
- data/spec/unit/git_spec.rb +5 -5
- data/spec/unit/module/forge_spec.rb +66 -116
- data/spec/unit/module_repository/forge_spec.rb +35 -5
- data/spec/unit/module_spec.rb +10 -10
- data/spec/unit/puppet_forge/connection_spec.rb +41 -0
- data/spec/unit/puppet_forge/tar/mini_spec.rb +87 -0
- data/spec/unit/puppet_forge/tar_spec.rb +9 -0
- data/spec/unit/puppet_forge/unpacker_spec.rb +59 -0
- data/spec/unit/puppet_forge/v3/module_release_spec.rb +68 -0
- data/spec/unit/puppet_forge/v3/module_spec.rb +67 -0
- metadata +48 -112
- data/lib/r10k/cli/environment.rb +0 -28
- data/lib/r10k/cli/environment/deploy.rb +0 -26
- data/lib/r10k/cli/environment/list.rb +0 -23
- data/lib/r10k/cli/environment/stale.rb +0 -24
- data/lib/r10k/cli/module.rb +0 -29
- data/lib/r10k/cli/module/deploy.rb +0 -27
- data/lib/r10k/cli/module/list.rb +0 -24
- data/lib/r10k/cli/synchronize.rb +0 -27
- data/lib/r10k/deployment/basedir.rb +0 -4
- data/lib/r10k/deployment/environment.rb +0 -20
- data/lib/r10k/deployment/source.rb +0 -37
- data/lib/r10k/git/commit.rb +0 -22
- data/lib/r10k/git/head.rb +0 -36
- data/lib/r10k/git/ref.rb +0 -66
- data/lib/r10k/git/remote_head.rb +0 -19
- data/lib/r10k/git/repository.rb +0 -158
- data/lib/r10k/git/tag.rb +0 -29
- data/lib/r10k/git/working_dir.rb +0 -186
- data/lib/r10k/registry.rb +0 -4
- data/lib/r10k/semver.rb +0 -128
- data/lib/r10k/task.rb +0 -12
- data/lib/r10k/task/deployment.rb +0 -164
- data/lib/r10k/task/environment.rb +0 -31
- data/lib/r10k/task/module.rb +0 -19
- data/lib/r10k/task/puppetfile.rb +0 -102
- data/lib/r10k/task_runner.rb +0 -72
- data/lib/r10k/util/monkey_patches.rb +0 -11
- data/spec/fixtures/vcr/cassettes/R10K_ModuleRepository_Forge/and_the_expected_version_is_latest/can_fetch_all_versions_of_a_given_module.yml +0 -187
- data/spec/fixtures/vcr/cassettes/R10K_ModuleRepository_Forge/and_the_expected_version_is_latest/can_fetch_the_latest_version_of_a_given_module.yml +0 -187
- data/spec/fixtures/vcr/cassettes/R10K_ModuleRepository_Forge/and_the_expected_version_is_latest/ignores_deleted_releases.yml +0 -190
- data/spec/fixtures/vcr/cassettes/R10K_ModuleRepository_Forge/it_handles_errors_from_forgeapi_puppetlabs_com/raises_an_error_for_a_non-existant_module.yml +0 -34
- data/spec/fixtures/vcr/cassettes/R10K_Module_Forge/and_the_expected_version_is_latest/sets_the_expected_version_based_on_the_latest_forge_version.yml +0 -103
- data/spec/shared-examples/git-ref.rb +0 -49
- data/spec/unit/deployment/environment_spec.rb +0 -25
- data/spec/unit/git/commit_spec.rb +0 -34
- data/spec/unit/git/head_spec.rb +0 -22
- data/spec/unit/git/ref_spec.rb +0 -45
- data/spec/unit/git/repository_spec.rb +0 -34
- data/spec/unit/git/tag_spec.rb +0 -32
- data/spec/unit/git/working_dir_spec.rb +0 -122
- data/spec/unit/util/monkey_patches_spec.rb +0 -20
@@ -69,12 +69,9 @@ class R10K::ModuleRepository::Forge
|
|
69
69
|
private
|
70
70
|
|
71
71
|
def make_conn
|
72
|
-
# Force use of json_pure with multi_json on Ruby 1.8.7
|
73
|
-
multi_json_opts = (RUBY_VERSION == "1.8.7" ? {:adapter => :json_pure} : {})
|
74
|
-
|
75
72
|
Faraday.new(:url => "https://#{@forge}") do |builder|
|
76
|
-
builder.request(:multi_json
|
77
|
-
builder.response(:multi_json
|
73
|
+
builder.request(:multi_json)
|
74
|
+
builder.response(:multi_json)
|
78
75
|
|
79
76
|
# This needs to be _after_ request/response configuration for testing
|
80
77
|
# purposes. Without this ordering the tests get badly mangled.
|
data/lib/r10k/puppetfile.rb
CHANGED
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'r10k/errors'
|
2
|
+
require 'rubygems'
|
3
|
+
|
4
|
+
module R10K
|
5
|
+
module Util
|
6
|
+
module License
|
7
|
+
def self.load
|
8
|
+
if Gem::Specification::find_all_by_name('pe-license').any?
|
9
|
+
begin
|
10
|
+
return PELicense.load_license_key
|
11
|
+
rescue PELicense::InvalidLicenseError => e
|
12
|
+
raise R10K::Error.wrap(e, "Invalid PE license detected: #{e.message}")
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
nil
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
data/lib/r10k/version.rb
CHANGED
@@ -0,0 +1,62 @@
|
|
1
|
+
require 'shared/puppet_forge/version'
|
2
|
+
|
3
|
+
require 'faraday'
|
4
|
+
require 'faraday_middleware'
|
5
|
+
|
6
|
+
module PuppetForge
|
7
|
+
# Provide a common mixin for adding a HTTP connection to classes.
|
8
|
+
#
|
9
|
+
# This module provides a common method for creating HTTP connections as well
|
10
|
+
# as reusing a single connection object between multiple classes. Including
|
11
|
+
# classes can invoke #conn to get a reasonably configured HTTP connection.
|
12
|
+
# Connection objects can be passed with the #conn= method.
|
13
|
+
#
|
14
|
+
# @example
|
15
|
+
# class HTTPThing
|
16
|
+
# include PuppetForge::Connection
|
17
|
+
# end
|
18
|
+
# thing = HTTPThing.new
|
19
|
+
# thing.conn = thing.make_connection('https://non-standard-forge.site')
|
20
|
+
#
|
21
|
+
# @api private
|
22
|
+
module Connection
|
23
|
+
|
24
|
+
attr_writer :conn
|
25
|
+
|
26
|
+
USER_AGENT = "PuppetForge/#{PuppetForge::VERSION} Faraday/#{Faraday::VERSION} Ruby/#{RUBY_VERSION}-p#{RUBY_PATCHLEVEL} (#{RUBY_PLATFORM})"
|
27
|
+
|
28
|
+
def self.authorization=(token)
|
29
|
+
@authorization = token
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.authorization
|
33
|
+
@authorization
|
34
|
+
end
|
35
|
+
|
36
|
+
# @return [Faraday::Connection] An existing Faraday connection if one was
|
37
|
+
# already set, otherwise a new Faraday connection.
|
38
|
+
def conn
|
39
|
+
@conn ||= make_connection('https://forgeapi.puppetlabs.com')
|
40
|
+
end
|
41
|
+
|
42
|
+
# Generate a new Faraday connection for the given URL.
|
43
|
+
#
|
44
|
+
# @param url [String] the base URL for this connection
|
45
|
+
# @return [Faraday::Connection]
|
46
|
+
def make_connection(url, adapter_args = nil)
|
47
|
+
adapter_args ||= [Faraday.default_adapter]
|
48
|
+
options = { :headers => { :user_agent => USER_AGENT }}
|
49
|
+
|
50
|
+
if token = PuppetForge::Connection.authorization
|
51
|
+
options[:headers][:authorization] = token
|
52
|
+
end
|
53
|
+
|
54
|
+
Faraday.new(url, options) do |builder|
|
55
|
+
builder.response(:json, :content_type => /\bjson$/)
|
56
|
+
builder.response(:raise_error)
|
57
|
+
|
58
|
+
builder.adapter(*adapter_args)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module PuppetForge
|
2
|
+
class Error < RuntimeError
|
3
|
+
attr_accessor :original
|
4
|
+
def initialize(message, original=nil)
|
5
|
+
super(message)
|
6
|
+
@original = original
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
class ExecutionFailure < PuppetForge::Error
|
11
|
+
end
|
12
|
+
|
13
|
+
class InvalidPathInPackageError < PuppetForge::Error
|
14
|
+
def initialize(options)
|
15
|
+
@entry_path = options[:entry_path]
|
16
|
+
@directory = options[:directory]
|
17
|
+
super "Attempt to install file into #{@entry_path.inspect} under #{@directory.inspect}"
|
18
|
+
end
|
19
|
+
|
20
|
+
def multiline
|
21
|
+
<<-MSG.strip
|
22
|
+
Could not install package
|
23
|
+
Package attempted to install file into
|
24
|
+
#{@entry_path.inspect} under #{@directory.inspect}.
|
25
|
+
MSG
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
require 'zlib'
|
2
|
+
require 'archive/tar/minitar'
|
3
|
+
|
4
|
+
module PuppetForge
|
5
|
+
class Tar
|
6
|
+
class Mini
|
7
|
+
|
8
|
+
SYMLINK_FLAGS = [2]
|
9
|
+
VALID_TAR_FLAGS = (0..7)
|
10
|
+
|
11
|
+
# @return [Hash{:symbol => Array<String>}] a hash with file-category keys pointing to lists of filenames.
|
12
|
+
def unpack(sourcefile, destdir)
|
13
|
+
# directories need to be changed outside of the Minitar::unpack because directories don't have a :file_done action
|
14
|
+
dirlist = []
|
15
|
+
file_lists = {}
|
16
|
+
Zlib::GzipReader.open(sourcefile) do |reader|
|
17
|
+
file_lists = validate_files(reader)
|
18
|
+
Archive::Tar::Minitar.unpack(reader, destdir, file_lists[:valid]) do |action, name, stats|
|
19
|
+
case action
|
20
|
+
when :file_done
|
21
|
+
FileUtils.chmod('u+rw,g+r,a-st', "#{destdir}/#{name}")
|
22
|
+
when :file_start
|
23
|
+
validate_entry(destdir, name)
|
24
|
+
when :dir
|
25
|
+
validate_entry(destdir, name)
|
26
|
+
dirlist << "#{destdir}/#{name}"
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
dirlist.each {|d| File.chmod(0755, d)}
|
31
|
+
file_lists
|
32
|
+
end
|
33
|
+
|
34
|
+
def pack(sourcedir, destfile)
|
35
|
+
Zlib::GzipWriter.open(destfile) do |writer|
|
36
|
+
Archive::Tar::Minitar.pack(sourcedir, writer)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
# Categorize all the files in tarfile as :valid, :invalid, or :symlink.
|
43
|
+
#
|
44
|
+
# :invalid files include 'x' and 'g' flags from the PAX standard but and any other non-standard tar flags.
|
45
|
+
# tar format info: http://pic.dhe.ibm.com/infocenter/zos/v1r13/index.jsp?topic=%2Fcom.ibm.zos.r13.bpxa500%2Ftaf.htm
|
46
|
+
# pax format info: http://pic.dhe.ibm.com/infocenter/zos/v1r13/index.jsp?topic=%2Fcom.ibm.zos.r13.bpxa500%2Fpxarchfm.htm
|
47
|
+
# :symlinks are not supported in Puppet modules
|
48
|
+
# :valid files are any of those that can be used in modules
|
49
|
+
# @param tarfile name of the tarfile
|
50
|
+
# @return [Hash{:symbol => Array<String>}] a hash with file-category keys pointing to lists of filenames.
|
51
|
+
def validate_files(tarfile)
|
52
|
+
file_lists = {:valid => [], :invalid => [], :symlinks => []}
|
53
|
+
Archive::Tar::Minitar.open(tarfile).each do |entry|
|
54
|
+
flag = entry.typeflag
|
55
|
+
if flag.nil? || flag =~ /[[:digit:]]/ && SYMLINK_FLAGS.include?(flag.to_i)
|
56
|
+
file_lists[:symlinks] << entry.name
|
57
|
+
elsif flag.nil? || flag =~ /[[:digit:]]/ && VALID_TAR_FLAGS.include?(flag.to_i)
|
58
|
+
file_lists[:valid] << entry.name
|
59
|
+
else
|
60
|
+
file_lists[:invalid] << entry.name
|
61
|
+
end
|
62
|
+
end
|
63
|
+
file_lists
|
64
|
+
end
|
65
|
+
|
66
|
+
def validate_entry(destdir, path)
|
67
|
+
if Pathname.new(path).absolute?
|
68
|
+
raise PuppetForge::InvalidPathInPackageError, :entry_path => path, :directory => destdir
|
69
|
+
end
|
70
|
+
|
71
|
+
path = File.expand_path File.join(destdir, path)
|
72
|
+
|
73
|
+
if path !~ /\A#{Regexp.escape destdir}/
|
74
|
+
raise PuppetForge::InvalidPathInPackageError, :entry_path => path, :directory => destdir
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
end
|
81
|
+
|
@@ -0,0 +1,68 @@
|
|
1
|
+
require 'pathname'
|
2
|
+
require 'shared/puppet_forge/error'
|
3
|
+
require 'shared/puppet_forge/tar'
|
4
|
+
|
5
|
+
module PuppetForge
|
6
|
+
class Unpacker
|
7
|
+
# Unpack a tar file into a specified directory
|
8
|
+
#
|
9
|
+
# @param filename [String] the file to unpack
|
10
|
+
# @param target [String] the target directory to unpack into
|
11
|
+
# @return [Hash{:symbol => Array<String>}] a hash with file-category keys pointing to lists of filenames.
|
12
|
+
# The categories are :valid, :invalid and :symlink
|
13
|
+
def self.unpack(filename, target, tmpdir)
|
14
|
+
inst = self.new(filename, target, tmpdir)
|
15
|
+
file_lists = inst.unpack
|
16
|
+
inst.move_into(Pathname.new(target))
|
17
|
+
file_lists
|
18
|
+
end
|
19
|
+
|
20
|
+
# Set the owner/group of the target directory to those of the source
|
21
|
+
# Note: don't call this function on Microsoft Windows
|
22
|
+
#
|
23
|
+
# @param source [Pathname] source of the permissions
|
24
|
+
# @param target [Pathname] target of the permissions change
|
25
|
+
def self.harmonize_ownership(source, target)
|
26
|
+
FileUtils.chown_R(source.stat.uid, source.stat.gid, target)
|
27
|
+
end
|
28
|
+
|
29
|
+
# @param filename [String] the file to unpack
|
30
|
+
# @param target [String] the target directory to unpack into
|
31
|
+
def initialize(filename, target, tmpdir)
|
32
|
+
@filename = filename
|
33
|
+
@target = target
|
34
|
+
@tmpdir = tmpdir
|
35
|
+
end
|
36
|
+
|
37
|
+
# @api private
|
38
|
+
def unpack
|
39
|
+
begin
|
40
|
+
PuppetForge::Tar.instance.unpack(@filename, @tmpdir)
|
41
|
+
rescue PuppetForge::ExecutionFailure => e
|
42
|
+
raise RuntimeError, "Could not extract contents of module archive: #{e.message}"
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
# @api private
|
47
|
+
def move_into(dir)
|
48
|
+
dir.rmtree if dir.exist?
|
49
|
+
FileUtils.mv(root_dir, dir)
|
50
|
+
ensure
|
51
|
+
FileUtils.rmtree(@tmpdir)
|
52
|
+
end
|
53
|
+
|
54
|
+
# @api private
|
55
|
+
def root_dir
|
56
|
+
return @root_dir if @root_dir
|
57
|
+
|
58
|
+
# Grab the first directory containing a metadata.json file
|
59
|
+
metadata_file = Dir["#{@tmpdir}/**/metadata.json"].sort_by(&:length)[0]
|
60
|
+
|
61
|
+
if metadata_file
|
62
|
+
@root_dir = Pathname.new(metadata_file).dirname
|
63
|
+
else
|
64
|
+
raise "No valid metadata.json found!"
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module PuppetForge
|
2
|
+
module V3
|
3
|
+
# Normalize a module name to use a hyphen as the separator between the
|
4
|
+
# author and module.
|
5
|
+
#
|
6
|
+
# @example
|
7
|
+
# PuppetForge::V3.normalize_name('my/module') #=> 'my-module'
|
8
|
+
# PuppetForge::V3.normalize_name('my-module') #=> 'my-module'
|
9
|
+
def self.normalize_name(name)
|
10
|
+
name.tr('/', '-')
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
require 'shared/puppet_forge/v3'
|
2
|
+
require 'shared/puppet_forge/v3/module_release'
|
3
|
+
require 'shared/puppet_forge/connection'
|
4
|
+
|
5
|
+
module PuppetForge
|
6
|
+
module V3
|
7
|
+
# Represents metadata for a single Forge module and provides access to
|
8
|
+
# the releases of a module.
|
9
|
+
class Module
|
10
|
+
|
11
|
+
include PuppetForge::Connection
|
12
|
+
|
13
|
+
# @!attribute [r] full_name
|
14
|
+
# @return [String] The hyphen separated full name of this module
|
15
|
+
attr_reader :full_name
|
16
|
+
|
17
|
+
# @param full_name [String] The name of this module, will be normalized to
|
18
|
+
# a hyphen separated name.
|
19
|
+
def initialize(full_name)
|
20
|
+
@full_name = PuppetForge::V3.normalize_name(full_name)
|
21
|
+
end
|
22
|
+
|
23
|
+
# Get all released versions of this module
|
24
|
+
#
|
25
|
+
# @example
|
26
|
+
# mod = PuppetForge::V3::Module.new('timmy-boolean')
|
27
|
+
# mod.versions
|
28
|
+
# #=> ["0.9.0-rc1", "0.9.0", "1.0.0", "1.0.1"]
|
29
|
+
#
|
30
|
+
# @return [Array<String>] All published versions of the given module
|
31
|
+
def versions
|
32
|
+
path = "/v3/modules/#{@full_name}"
|
33
|
+
response = conn.get(path)
|
34
|
+
|
35
|
+
releases = []
|
36
|
+
|
37
|
+
response.body['releases'].each do |release|
|
38
|
+
if !release['deleted_at']
|
39
|
+
releases << release['version']
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
releases.reverse
|
44
|
+
end
|
45
|
+
|
46
|
+
# Get all released versions of this module
|
47
|
+
#
|
48
|
+
# @example
|
49
|
+
# mod = PuppetForge::V3::Module.new('timmy-boolean')
|
50
|
+
# mod.latest_version
|
51
|
+
# #=> "1.0.1"
|
52
|
+
#
|
53
|
+
# @return [String] The latest published version of the given module
|
54
|
+
def latest_version
|
55
|
+
versions.last
|
56
|
+
end
|
57
|
+
|
58
|
+
# Get a specific release of this module off of the forge.
|
59
|
+
#
|
60
|
+
# @return [ModuleRelease] a release object of the given version for this module.
|
61
|
+
def release(version)
|
62
|
+
PuppetForge::V3::ModuleRelease.new(@full_name, version).tap { |mr| mr.conn = conn }
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
require 'shared/puppet_forge/v3'
|
2
|
+
require 'shared/puppet_forge/connection'
|
3
|
+
|
4
|
+
module PuppetForge
|
5
|
+
module V3
|
6
|
+
# Access metadata and downloads for a specific module release.
|
7
|
+
class ModuleRelease
|
8
|
+
|
9
|
+
include PuppetForge::Connection
|
10
|
+
|
11
|
+
# @!attribute [r] full_name
|
12
|
+
# @return [String] The hyphen delimited name of this module
|
13
|
+
attr_reader :full_name
|
14
|
+
|
15
|
+
# @!attribute [r] version
|
16
|
+
# @return [String] The version of this module
|
17
|
+
attr_reader :version
|
18
|
+
|
19
|
+
# @param full_name [String] The name of the module, will be normalized
|
20
|
+
# to a hyphen delimited name.
|
21
|
+
# @param version [String]
|
22
|
+
def initialize(full_name, version)
|
23
|
+
@full_name = PuppetForge::V3.normalize_name(full_name)
|
24
|
+
@version = version
|
25
|
+
end
|
26
|
+
|
27
|
+
# @return [Hash] The complete Forge resposne for this release.
|
28
|
+
def data
|
29
|
+
@data ||= conn.get(resource_url).body
|
30
|
+
end
|
31
|
+
|
32
|
+
# @return [String] The unique identifier for this module release.
|
33
|
+
def slug
|
34
|
+
"#{full_name}-#{version}"
|
35
|
+
end
|
36
|
+
|
37
|
+
# Download this module release to the specified path.
|
38
|
+
#
|
39
|
+
# @param path [Pathname]
|
40
|
+
# @return [void]
|
41
|
+
def download(path)
|
42
|
+
resp = conn.get(file_url)
|
43
|
+
path.open('wb') { |fh| fh.write(resp.body) }
|
44
|
+
end
|
45
|
+
|
46
|
+
# Verify that a downloaded module matches the checksum in the metadata for this release.
|
47
|
+
#
|
48
|
+
# @param path [Pathname]
|
49
|
+
# @return [void]
|
50
|
+
def verify(path)
|
51
|
+
expected_md5 = data['file_md5']
|
52
|
+
file_md5 = Digest::MD5.file(path).hexdigest
|
53
|
+
if expected_md5 != file_md5
|
54
|
+
raise ChecksumMismatch.new("Expected #{path} checksum to be #{expected_md5}, got #{file_md5}")
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
private
|
59
|
+
|
60
|
+
def file_url
|
61
|
+
"/v3/files/#{slug}.tar.gz"
|
62
|
+
end
|
63
|
+
|
64
|
+
def resource_url
|
65
|
+
"/v3/releases/#{slug}"
|
66
|
+
end
|
67
|
+
|
68
|
+
class ChecksumMismatch < StandardError
|
69
|
+
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|