Commit 4c984bc7 authored by Nico Weber's avatar Nico Weber Committed by Commit Bot

Make BUILD_DATE based off the last commit time instead of the current time.

BUILD_DATE used to take the current time and then set BUILD_DATE to
the first Sunday of the month (unofficial builds) or to that date at 5am
(official builds), so that the date stays constant for a while, to
keep binary hashes changing less frequently. (BUILD_DATE makes it into
base/, so most binaries contain it.)

This behavior is kept, but it's now based off the timestamp of the most
recent commit.

This has the advantage of being deterministic, so for example if some
Android bot builds the same revision twice, it's now guaranteed that
both builds will end up with the same timestamp. The motivation is that
I want to reland https://chromium-review.googlesource.com/1161104 which
broke Android bots which assume fully deterministic BUILD_DATEs and
currently only work by accident due to write_build_date_header.py not
rerunning in incremental builds.

Since querying git is slow and the timestamp will be queried at
gn gen time in a follow-up change, let lastchange.py write the last
commit's timestamp in a file next to build/util/LASTCHANGE and then read
that in write_build_date_header.py.

For official chrome builds we ship, this shouldn't make much of a difference,
since there's always recent commits when we ship those (if only to
update chrome/VERSION).

When syncing to an old revision (past the revision this change lands in, of
course -- say this lands and then 2 years pass, and then you sync back
1.5 years), that will now have an old date while it had a current date
before. This could happen while bisecting. You'd get more security
interstitials while bisecting.

Embedders might ship old Chromiums without having a recent commit
in src. These would have BUILD_DATEs in the past.  I announced this change on
https://groups.google.com/a/chromium.org/forum/#!topic/embedder-dev/Ymk3bHPQ45M
We can add an explicit opt-out if requested.

Build date currently affects:
- HSTS lists (considered valid for 70 days past build date)
-- but only enabled for official chrome builds (GOOGLE_CHROME_BUILD
   check in TransportSecurityState())
- certificate transparency (considered valid for 70 days past build date)
- invalid cert date interstitial (warns if system clock is
  more than 2 days behind build date, or more than 365 days
  in the future)
- browser upgrade detector indicator UI (stops checking after
  build is 12 weeks old)
- field trials (kNoExpirationYear is 2 years past build date)

//base:build_date already depends on lastchange_file and only reruns
when that file is touched (and on full builds).

Lots of prior discussion in https://codereview.chromium.org/1641413002/
(this approach but without widening was suggested in comment 26,
comment 46 mentions security concerns from agl, comment 67 onwards are about
security too. Eventually http://crbug.com/584880 got filed with a plan similar
to this CL here, but there was no time to evaluate the proposal at the time
due to internship schedule reasons. I talked to security -- namely,
felt agl ncarter carlosil -- and everyone thinks this has no obvious issues.)

Obsoletes https://chromium-review.googlesource.com/c/564598/, a prior attempt
at getting deterministic builds (opt-in, by setting an env var with the desired
date).

Bug: 314403,584880,871173
Change-Id: I6ccd78bff005572a9fa391e9820f4f5f83583c50
Reviewed-on: https://chromium-review.googlesource.com/1167913Reviewed-by: default avatarAlex Mineer <amineer@chromium.org>
Reviewed-by: default avatarDirk Pranke <dpranke@chromium.org>
Commit-Queue: Nico Weber <thakis@chromium.org>
Cr-Commit-Position: refs/heads/master@{#583420}
parent e0903d18
......@@ -15,9 +15,10 @@ import subprocess
import sys
class VersionInfo(object):
def __init__(self, revision_id, full_revision_string):
def __init__(self, revision_id, full_revision_string, timestamp):
self.revision_id = revision_id
self.revision = full_revision_string
self.timestamp = timestamp
def RunGitCommand(directory, command):
......@@ -58,14 +59,14 @@ def FetchGitRevision(directory, filter):
A VersionInfo object or None on error.
"""
hsh = ''
git_args = ['log', '-1', '--format=%H']
git_args = ['log', '-1', '--format=%H %ct']
if filter is not None:
git_args.append('--grep=' + filter)
proc = RunGitCommand(directory, git_args)
if proc:
output = proc.communicate()[0].strip()
if proc.returncode == 0 and output:
hsh = output
hsh, ct = output.split()
else:
logging.error('Git error: rc=%d, output=%r' %
(proc.returncode, output))
......@@ -80,7 +81,7 @@ def FetchGitRevision(directory, filter):
if line.startswith('Cr-Commit-Position:'):
pos = line.rsplit()[-1].strip()
break
return VersionInfo(hsh, '%s-%s' % (hsh, pos))
return VersionInfo(hsh, '%s-%s' % (hsh, pos), int(ct))
def FetchVersionInfo(directory=None, filter=None):
......@@ -90,7 +91,7 @@ def FetchVersionInfo(directory=None, filter=None):
"""
version_info = FetchGitRevision(directory, filter)
if not version_info:
version_info = VersionInfo('0', '0')
version_info = VersionInfo('0', '0', 0)
return version_info
......@@ -136,6 +137,7 @@ def WriteIfChanged(file_name, contents):
"""
Writes the specified contents to the specified file_name
iff the contents are different than the current contents.
Returns if new data was written.
"""
try:
old_contents = open(file_name, 'r').read()
......@@ -143,9 +145,10 @@ def WriteIfChanged(file_name, contents):
pass
else:
if contents == old_contents:
return
return False
os.unlink(file_name)
open(file_name, 'w').write(contents)
return True
def main(argv=None):
......@@ -211,7 +214,9 @@ def main(argv=None):
sys.stdout.write(contents)
else:
if out_file:
WriteIfChanged(out_file, contents)
if WriteIfChanged(out_file, contents):
with open(out_file + '.committime', 'w') as timefile:
timefile.write(str(version_info.timestamp))
if header:
WriteIfChanged(header,
GetHeaderContents(header, opts.version_macro,
......
......@@ -7,7 +7,7 @@
build_type impacts the timestamp generated:
- default: the build date is set to the most recent first Sunday of a month at
5:00am. The reason is that it is a time where invalidating the build cache
shouldn't have major reprecussions (due to lower load).
shouldn't have major repercussions (due to lower load).
- official: the build date is set to the current date at 5:00am, or the day
before if the current time is before 5:00am.
Either way, it is guaranteed to be in the past and always in UTC.
......@@ -23,6 +23,9 @@ import os
import sys
THIS_DIR = os.path.abspath(os.path.dirname(__file__))
def GetFirstSundayOfMonth(year, month):
"""Returns the first sunday of the given month of the given year.
......@@ -80,7 +83,16 @@ def main():
'build_type', help='The type of build', choices=('official', 'default'))
args = argument_parser.parse_args()
now = datetime.datetime.utcnow()
# The mtime of the revision in build/util/LASTCHANGE is stored in a file
# next to it. Read it, to get a deterministic time close to "now".
# That date is then modified as described at the top of the file so that
# it changes less frequently than with every commit.
# This intentionally always uses build/util/LASTCHANGE's commit time even if
# use_dummy_lastchange is set.
lastchange_file = os.path.join(THIS_DIR, 'util', 'LASTCHANGE.committime')
last_commit_timestamp = int(open(lastchange_file).read())
now = datetime.datetime.utcfromtimestamp(last_commit_timestamp)
if now.hour < 5:
# The time is locked at 5:00 am in UTC to cause the build cache
# invalidation to not happen exactly at midnight. Use the same calculation
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment