Example using a Gem::Package
Builds a .gem file given a Gem::Specification
. A .gem file is a tarball which contains a data.tar.gz, metadata.gz, checksums.yaml.gz and possibly signatures.
require 'rubygems' require 'rubygems/package' spec = Gem::Specification.new do |s| s.summary = "Ruby based make-like utility." s.name = 'rake' s.version = PKG_VERSION s.requirements << 'none' s.files = PKG_FILES s.description = <<-EOF Rake is a Make-like program implemented in Ruby. Tasks and dependencies are specified in standard Ruby syntax. EOF end Gem::Package.build spec
Reads a .gem file.
require 'rubygems' require 'rubygems/package' the_gem = Gem::Package.new(path_to_dot_gem) the_gem.contents the_gem.extract_files destination_directory the_gem.spec the_gem.verify
files
are the files in the .gem tar file, not the Ruby files in the gem extract_files
and contents
automatically call verify
Checksums for the contents of the package
Permission for other files
Permission for directories
The files in this package. This is not the contents of the gem, just the files in the top-level container.
Reference to the gem being packaged.
Permission for program files
The security policy used for verifying the contents of this package.
Public Class Methods Sourcedef self.build(spec, skip_validation = false, strict_validation = false, file_name = nil) gem_file = file_name || spec.file_name package = new gem_file package.spec = spec package.build skip_validation, strict_validation gem_file endSource
def self.new(gem, security_policy = nil) gem = if gem.is_a?(Gem::Package::Source) gem elsif gem.respond_to? :read Gem::Package::IOSource.new gem else Gem::Package::FileSource.new gem end return super unless self == Gem::Package return super unless gem.present? return super unless gem.start return super unless gem.start.include? "MD5SUM =" Gem::Package::Old.new gem end
Creates a new Gem::Package
for the file at gem
. gem
can also be provided as an IO
object.
If gem
is an existing file in the old format a Gem::Package::Old
will be returned.
def self.raw_spec(path, security_policy = nil) format = new(path, security_policy) spec = format.spec metadata = nil File.open path, Gem.binary_mode do |io| tar = Gem::Package::TarReader.new io tar.each_entry do |entry| case entry.full_name when "metadata" then metadata = entry.read when "metadata.gz" then metadata = Gem::Util.gunzip entry.read end end end [spec, metadata] end
Extracts the Gem::Specification
and raw metadata from the .gem file at path
.
def add_checksums(tar) Gem.load_yaml checksums_by_algorithm = Hash.new {|h, algorithm| h[algorithm] = {} } @checksums.each do |name, digests| digests.each do |algorithm, digest| checksums_by_algorithm[algorithm][name] = digest.hexdigest end end tar.add_file_signed "checksums.yaml.gz", 0o444, @signer do |io| gzip_to io do |gz_io| Psych.dump checksums_by_algorithm, gz_io end end end
Adds a checksum for each entry in the gem to checksums.yaml.gz.
Sourcedef build(skip_validation = false, strict_validation = false) raise ArgumentError, "skip_validation = true and strict_validation = true are incompatible" if skip_validation && strict_validation Gem.load_yaml @spec.validate true, strict_validation unless skip_validation setup_signer( signer_options: { expiration_length_days: Gem.configuration.cert_expiration_length_days, } ) @gem.with_write_io do |gem_io| Gem::Package::TarWriter.new gem_io do |gem| add_metadata gem add_contents gem add_checksums gem end end say <<-EOM Successfully built RubyGem Name: #{@spec.name} Version: #{@spec.version} File: #{File.basename @gem.path} EOM ensure @signer = nil end
Builds this package based on the specification set by spec=
def contents return @contents if @contents verify unless @spec @contents = [] @gem.with_read_io do |io| gem_tar = Gem::Package::TarReader.new io gem_tar.each do |entry| next unless entry.full_name == "data.tar.gz" open_tar_gz entry do |pkg_tar| pkg_tar.each do |contents_entry| @contents << contents_entry.full_name end end return @contents end end rescue Zlib::GzipFile::Error, EOFError, Gem::Package::TarInvalidError => e raise Gem::Package::FormatError.new e.message, @gem end
A list of file names contained in this gem
Sourcedef copy_to(path) FileUtils.cp @gem.path, path unless File.exist? path end
Copies this package to path
(if possible)
def gzip_to(io) gz_io = Zlib::GzipWriter.new io, Zlib::BEST_COMPRESSION gz_io.mtime = @build_time yield gz_io ensure gz_io.close end
Gzips content written to gz_io
to io
.
def limit_read(io, name, limit) bytes = io.read(limit + 1) raise Gem::Package::FormatError, "#{name} is too big (over #{limit} bytes)" if bytes.size > limit bytes endSource
def normalize_path(pathname) if Gem.win_platform? pathname.downcase else pathname end endSource
def read_checksums(gem) Gem.load_yaml @checksums = gem.seek "checksums.yaml.gz" do |entry| Zlib::GzipReader.wrap entry do |gz_io| Gem::SafeYAML.safe_load limit_read(gz_io, "checksums.yaml.gz", 10 * 1024 * 1024) end end end
Reads and loads checksums.yaml.gz from the tar file gem
def setup_signer(signer_options: {}) passphrase = ENV["GEM_PRIVATE_KEY_PASSPHRASE"] if @spec.signing_key @signer = Gem::Security::Signer.new( @spec.signing_key, @spec.cert_chain, passphrase, signer_options ) @spec.signing_key = nil @spec.cert_chain = @signer.cert_chain.map(&:to_s) else @signer = Gem::Security::Signer.new nil, nil, passphrase @spec.cert_chain = @signer.cert_chain.map(&:to_pem) if @signer.cert_chain end end
Prepares the gem for signing and checksum generation. If a signing certificate and key are not present only checksum generation is set up.
Sourcedef spec verify unless @spec @spec end
The spec for this gem.
If this is a package for a built gem the spec is loaded from the gem and returned. If this is a package for a gem being built the provided spec is returned.
Sourcedef verify @files = [] @spec = nil @gem.with_read_io do |io| Gem::Package::TarReader.new io do |reader| read_checksums reader verify_files reader end end verify_checksums @digests, @checksums @security_policy&.verify_signatures @spec, @digests, @signatures true rescue Gem::Security::Exception @spec = nil @files = [] raise rescue Errno::ENOENT => e raise Gem::Package::FormatError.new e.message rescue Zlib::GzipFile::Error, EOFError, Gem::Package::TarInvalidError => e raise Gem::Package::FormatError.new e.message, @gem end
Verifies that this gem:
Contains a valid gem specification
Contains a contents archive
The contents archive is not corrupt
After verification the gem specification from the gem is available from spec
def verify_entry(entry) file_name = entry.full_name @files << file_name case file_name when /\.sig$/ then @signatures[$`] = limit_read(entry, file_name, 1024 * 1024) if @security_policy return else digest entry end case file_name when "metadata", "metadata.gz" then load_spec entry when "data.tar.gz" then verify_gz entry end rescue StandardError warn "Exception while verifying #{@gem.path}" raise end
Verifies entry
in a .gem file.
def verify_files(gem) gem.each do |entry| verify_entry entry end unless @spec raise Gem::Package::FormatError.new "package metadata is missing", @gem end unless @files.include? "data.tar.gz" raise Gem::Package::FormatError.new \ "package content (data.tar.gz) is missing", @gem end if (duplicates = @files.group_by {|f| f }.select {|_k,v| v.size > 1 }.map(&:first)) && duplicates.any? raise Gem::Security::Exception, "duplicate files in the package: (#{duplicates.map(&:inspect).join(", ")})" end end
Verifies the files of the gem
def initialize(gem, security_policy) require "zlib" @gem = gem @build_time = Gem.source_date_epoch @checksums = {} @contents = nil @digests = Hash.new {|h, algorithm| h[algorithm] = {} } @files = nil @security_policy = security_policy @signatures = {} @signer = nil @spec = nil end
Creates a new package that will read or write to the file gem
.
RetroSearch is an open source project built by @garambo | Open a GitHub Issue
Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo
HTML:
3.2
| Encoding:
UTF-8
| Version:
0.7.4