Skip to content

Commit 20719f0

Browse files
committed
Merge pull request #2503 from JojoBoulix/stable
Extend SOURCE_DATE_EPOCH support
2 parents bf273a1 + 0f33e6c commit 20719f0

File tree

6 files changed

+76
-4
lines changed

6 files changed

+76
-4
lines changed

sphinx/builders/gettext.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111

1212
from __future__ import unicode_literals
1313

14-
from os import path, walk
14+
from os import path, walk, getenv
1515
from codecs import open
1616
from time import time
1717
from datetime import datetime, tzinfo, timedelta
@@ -130,6 +130,12 @@ def write_doc(self, docname, doctree):
130130
timestamp = time()
131131
tzdelta = datetime.fromtimestamp(timestamp) - \
132132
datetime.utcfromtimestamp(timestamp)
133+
# set timestamp from SOURCE_DATE_EPOCH if set
134+
# see https://reproducible-builds.org/specs/source-date-epoch/
135+
source_date_epoch = getenv('SOURCE_DATE_EPOCH')
136+
if source_date_epoch is not None:
137+
timestamp = float(source_date_epoch)
138+
tzdelta = 0
133139

134140

135141
class LocalTimeZone(tzinfo):

sphinx/config.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
"""
1111

1212
import re
13-
from os import path, environ
13+
from os import path, environ, getenv
1414
import shlex
1515

1616
from six import PY2, PY3, iteritems, string_types, binary_type, text_type, integer_types
@@ -19,8 +19,10 @@
1919
from sphinx.locale import l_
2020
from sphinx.util.osutil import make_filename, cd
2121
from sphinx.util.pycompat import execfile_, NoneType
22+
from sphinx.util.i18n import format_date
2223

2324
nonascii_re = re.compile(br'[\x80-\xff]')
25+
copyright_year_re = re.compile(r'^((\d{4}-)?)(\d{4})(?=[ ,])')
2426

2527
CONFIG_SYNTAX_ERROR = "There is a syntax error in your configuration file: %s"
2628
if PY3:
@@ -298,6 +300,15 @@ def __init__(self, dirname, filename, overrides, tags):
298300
self.setup = config.get('setup', None)
299301
self.extensions = config.get('extensions', [])
300302

303+
# correct values of copyright year that are not coherent with
304+
# the SOURCE_DATE_EPOCH environment variable (if set)
305+
# See https://reproducible-builds.org/specs/source-date-epoch/
306+
if getenv('SOURCE_DATE_EPOCH') is not None:
307+
for k in ('copyright', 'epub_copyright'):
308+
if k in config:
309+
config[k] = copyright_year_re.sub('\g<1>%s' % format_date('%Y'),
310+
config[k])
311+
301312
def check_types(self, warn):
302313
# check all values for deviation from the default value's type, since
303314
# that can result in TypeErrors all over the place

sphinx/util/i18n.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414
import re
1515
import warnings
1616
from os import path
17-
from time import gmtime
1817
from datetime import datetime
1918
from collections import namedtuple
2019

@@ -188,7 +187,7 @@ def format_date(format, date=None, language=None, warn=None):
188187
# See https://wiki.debian.org/ReproducibleBuilds/TimestampsProposal
189188
source_date_epoch = os.getenv('SOURCE_DATE_EPOCH')
190189
if source_date_epoch is not None:
191-
date = gmtime(float(source_date_epoch))
190+
date = datetime.utcfromtimestamp(float(source_date_epoch))
192191
else:
193192
date = datetime.now()
194193

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
2+
copyright = u'2006-2009, Author'
3+
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
=================
2+
test-correct-year
3+
=================
4+

tests/test_correct_year.py

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
# -*- coding: utf-8 -*-
2+
"""
3+
test_correct_year
4+
~~~~~~~~~~~~~~~~~
5+
6+
Test copyright year adjustment
7+
8+
:copyright: Copyright 2007-2016 by the Sphinx team, see AUTHORS.
9+
:license: BSD, see LICENSE for details.
10+
"""
11+
import os
12+
13+
from util import TestApp
14+
15+
16+
def test_correct_year():
17+
try:
18+
# save current value of SOURCE_DATE_EPOCH
19+
sde = os.environ.pop('SOURCE_DATE_EPOCH',None)
20+
21+
# test with SOURCE_DATE_EPOCH unset: no modification
22+
app = TestApp(buildername='html',testroot='correct-year')
23+
app.builder.build_all()
24+
content = (app.outdir / 'contents.html').text()
25+
app.cleanup()
26+
assert '2006-2009' in content
27+
28+
# test with SOURCE_DATE_EPOCH set: copyright year should be
29+
# updated
30+
os.environ['SOURCE_DATE_EPOCH'] = "1293840000"
31+
app = TestApp(buildername='html',testroot='correct-year')
32+
app.builder.build_all()
33+
content = (app.outdir / 'contents.html').text()
34+
app.cleanup()
35+
assert '2006-2011' in content
36+
37+
os.environ['SOURCE_DATE_EPOCH'] = "1293839999"
38+
app = TestApp(buildername='html',testroot='correct-year')
39+
app.builder.build_all()
40+
content = (app.outdir / 'contents.html').text()
41+
app.cleanup()
42+
assert '2006-2010' in content
43+
44+
finally:
45+
# Restores SOURCE_DATE_EPOCH
46+
if sde == None:
47+
os.environ.pop('SOURCE_DATE_EPOCH',None)
48+
else:
49+
os.environ['SOURCE_DATE_EPOCH'] = sde

0 commit comments

Comments
 (0)