[前][次][番号順一覧][スレッド一覧]

ruby-changes:57913

From: Ellen <ko1@a...>
Date: Thu, 26 Sep 2019 18:11:23 +0900 (JST)
Subject: [ruby-changes:57913] 508afe2c26 (master): [rubygems/rubygems] Set SOURCE_DATE_EPOCH env var if not provided.

https://git.ruby-lang.org/ruby.git/commit/?id=508afe2c26

From 508afe2c26737e0be60a72faa9d6740a06b0914c Mon Sep 17 00:00:00 2001
From: Ellen Marie Dash <the@s...>
Date: Sat, 17 Aug 2019 04:45:09 +0000
Subject: [rubygems/rubygems] Set SOURCE_DATE_EPOCH env var if not provided.

Fixes #2290.

1. `Gem::Specification.date` returns SOURCE_DATE_EPOCH when defined,
2. this commit makes RubyGems set it _persistently_ when not provided.

This combination means that you can build a gem, check the build time,
and use that value to generate a new build -- and then verify they're
the same.

https://github.com/rubygems/rubygems/commit/d830d53f59

diff --git a/lib/rubygems.rb b/lib/rubygems.rb
index 4d06d98..2676fbd 100644
--- a/lib/rubygems.rb
+++ b/lib/rubygems.rb
@@ -1242,6 +1242,23 @@ An Array (#{env.inspect}) was passed in from #{caller[3]} https://github.com/ruby/ruby/blob/trunk/lib/rubygems.rb#L1242
 
   end
 
+  ##
+  # The SOURCE_DATE_EPOCH environment variable (or, if that's not set, the current time), converted to Time object.
+  # This is used throughout RubyGems for enabling reproducible builds.
+  #
+  # If it is not set as an environment variable already, this also sets it.
+  #
+  # Details on SOURCE_DATE_EPOCH:
+  # https://reproducible-builds.org/specs/source-date-epoch/
+
+  def self.source_date_epoch
+    if ENV["SOURCE_DATE_EPOCH"].nil? || ENV["SOURCE_DATE_EPOCH"].empty?
+      ENV["SOURCE_DATE_EPOCH"] = Time.now.to_i.to_s
+    end
+
+    Time.at(ENV["SOURCE_DATE_EPOCH"].to_i).utc.freeze
+  end
+
   # FIX: Almost everywhere else we use the `def self.` way of defining class
   # methods, and then we switch over to `class << self` here. Pick one or the
   # other.
diff --git a/lib/rubygems/package.rb b/lib/rubygems/package.rb
index 16cda5a..bef37ae 100644
--- a/lib/rubygems/package.rb
+++ b/lib/rubygems/package.rb
@@ -193,7 +193,7 @@ class Gem::Package https://github.com/ruby/ruby/blob/trunk/lib/rubygems/package.rb#L193
   def initialize(gem, security_policy) # :notnew:
     @gem = gem
 
-    @build_time      = ENV["SOURCE_DATE_EPOCH"] ? Time.at(ENV["SOURCE_DATE_EPOCH"].to_i).utc : Time.now
+    @build_time      = Gem.source_date_epoch
     @checksums       = {}
     @contents        = nil
     @digests         = Hash.new { |h, algorithm| h[algorithm] = {} }
diff --git a/lib/rubygems/package/tar_writer.rb b/lib/rubygems/package/tar_writer.rb
index 87ee39a..96d8184 100644
--- a/lib/rubygems/package/tar_writer.rb
+++ b/lib/rubygems/package/tar_writer.rb
@@ -123,7 +123,7 @@ class Gem::Package::TarWriter https://github.com/ruby/ruby/blob/trunk/lib/rubygems/package/tar_writer.rb#L123
 
     header = Gem::Package::TarHeader.new :name => name, :mode => mode,
                                          :size => size, :prefix => prefix,
-                                         :mtime => ENV["SOURCE_DATE_EPOCH"] ? Time.at(ENV["SOURCE_DATE_EPOCH"].to_i).utc : Time.now
+                                         :mtime => Gem.source_date_epoch
 
     @io.write header
     @io.pos = final_pos
@@ -217,7 +217,7 @@ class Gem::Package::TarWriter https://github.com/ruby/ruby/blob/trunk/lib/rubygems/package/tar_writer.rb#L217
 
     header = Gem::Package::TarHeader.new(:name => name, :mode => mode,
                                          :size => size, :prefix => prefix,
-                                         :mtime => ENV["SOURCE_DATE_EPOCH"] ? Time.at(ENV["SOURCE_DATE_EPOCH"].to_i).utc : Time.now).to_s
+                                         :mtime => Gem.source_date_epoch).to_s
 
     @io.write header
     os = BoundedStream.new @io, size
@@ -245,7 +245,7 @@ class Gem::Package::TarWriter https://github.com/ruby/ruby/blob/trunk/lib/rubygems/package/tar_writer.rb#L245
                                          :size => 0, :typeflag => "2",
                                          :linkname => target,
                                          :prefix => prefix,
-                                         :mtime => ENV["SOURCE_DATE_EPOCH"] ? Time.at(ENV["SOURCE_DATE_EPOCH"].to_i).utc : Time.now).to_s
+                                         :mtime => Gem.source_date_epoch).to_s
 
     @io.write header
 
@@ -298,7 +298,7 @@ class Gem::Package::TarWriter https://github.com/ruby/ruby/blob/trunk/lib/rubygems/package/tar_writer.rb#L298
     header = Gem::Package::TarHeader.new :name => name, :mode => mode,
                                          :typeflag => "5", :size => 0,
                                          :prefix => prefix,
-                                         :mtime => ENV["SOURCE_DATE_EPOCH"] ? Time.at(ENV["SOURCE_DATE_EPOCH"].to_i).utc : Time.now
+                                         :mtime => Gem.source_date_epoch
 
     @io.write header
 
diff --git a/lib/rubygems/specification.rb b/lib/rubygems/specification.rb
index c023e4f..b3db311 100644
--- a/lib/rubygems/specification.rb
+++ b/lib/rubygems/specification.rb
@@ -1667,7 +1667,7 @@ class Gem::Specification < Gem::BasicSpecification https://github.com/ruby/ruby/blob/trunk/lib/rubygems/specification.rb#L1667
   # https://reproducible-builds.org/specs/source-date-epoch/
 
   def date
-    @date ||= ENV["SOURCE_DATE_EPOCH"] ? Time.utc(*Time.at(ENV["SOURCE_DATE_EPOCH"].to_i).utc.to_a[3..5].reverse) : TODAY
+    @date ||= Time.utc(*Gem.source_date_epoch.utc.to_a[3..5].reverse)
   end
 
   DateLike = Object.new # :nodoc:
diff --git a/test/rubygems/test_gem_commands_build_command.rb b/test/rubygems/test_gem_commands_build_command.rb
index 0c511b2..50c447e 100644
--- a/test/rubygems/test_gem_commands_build_command.rb
+++ b/test/rubygems/test_gem_commands_build_command.rb
@@ -457,4 +457,36 @@ class TestGemCommandsBuildCommand < Gem::TestCase https://github.com/ruby/ruby/blob/trunk/test/rubygems/test_gem_commands_build_command.rb#L457
     assert_match(/INFO:  Your expired cert will be located at: .+\Wgem-public_cert\.pem\.expired\.[0-9]+/, output.shift)
   end
 
+  def test_build_is_reproducible
+    epoch = ENV["SOURCE_DATE_EPOCH"]
+    new_epoch = Time.now.to_i.to_s
+    ENV["SOURCE_DATE_EPOCH"] = new_epoch
+
+    gem_file = File.basename(@gem.cache_file)
+
+    gemspec_file = File.join(@tempdir, @gem.spec_name)
+    File.write(gemspec_file, @gem.to_ruby)
+    @cmd.options[:args] = [gemspec_file]
+
+    util_test_build_gem @gem
+
+    build1_contents = File.read(gem_file)
+
+    # Guarantee the time has changed.
+    sleep 1 if Time.now.to_i == new_epoch
+
+    ENV["SOURCE_DATE_EPOCH"] = new_epoch
+
+    @ui = Gem::MockGemUi.new
+    @cmd.options[:args] = [gemspec_file]
+
+    util_test_build_gem @gem
+
+    build2_contents = File.read(gem_file)
+
+    assert_equal build1_contents, build2_contents
+  ensure
+    ENV["SOURCE_DATE_EPOCH"] = epoch
+  end
+
 end
diff --git a/test/rubygems/test_gem_package.rb b/test/rubygems/test_gem_package.rb
index f2f712e..a282e0c 100644
--- a/test/rubygems/test_gem_package.rb
+++ b/test/rubygems/test_gem_package.rb
@@ -2,6 +2,7 @@ https://github.com/ruby/ruby/blob/trunk/test/rubygems/test_gem_package.rb#L2
 # frozen_string_literal: true
 
 require 'rubygems/package/tar_test_case'
+require 'digest'
 
 class TestGemPackage < Gem::Package::TarTestCase
 
@@ -123,6 +124,32 @@ class TestGemPackage < Gem::Package::TarTestCase https://github.com/ruby/ruby/blob/trunk/test/rubygems/test_gem_package.rb#L124
     ENV["SOURCE_DATE_EPOCH"] = epoch
   end
 
+  def test_build_time_source_date_epoch_automatically_set
+    epoch = ENV["SOURCE_DATE_EPOCH"]
+    ENV["SOURCE_DATE_EPOCH"] = nil
+
+    start_time = Time.now.utc.to_i
+
+    spec = Gem::Specification.new 'build', '1'
+    spec.summary = 'build'
+    spec.authors = 'build'
+    spec.files = ['lib/code.rb']
+    spec.rubygems_version = Gem::Version.new '0'
+
+    package = Gem::Package.new spec.file_name
+
+    end_time = Time.now.utc.to_i
+
+    assert package.build_time.is_a?(Time)
+
+    build_time = package.build_time.to_i
+
+    assert(start_time <= build_time)
+    assert(build_time <= end_time)
+  ensure
+    ENV["SOURCE_DATE_EPOCH"] = epoch
+  end
+
   def test_add_files
     spec = Gem::Specification.new
     spec.files = %w[lib/code.rb lib/empty]
-- 
cgit v0.10.2


--
ML: ruby-changes@q...
Info: http://www.atdot.net/~ko1/quickml/

[前][次][番号順一覧][スレッド一覧]