SOURCE_DATE_EPOCH from app's git otherwise fdroiddata metadata file
https://reproducible-builds.org/docs/source-date-epoch
This commit is contained in:
parent
0b6e304922
commit
20b36f1970
@ -479,7 +479,7 @@ def build_local(app, build, vcs, build_dir, output_dir, log_dir, srclib_dir, ext
|
|||||||
logging.critical("Android NDK '%s' is not a directory!" % ndk_path)
|
logging.critical("Android NDK '%s' is not a directory!" % ndk_path)
|
||||||
raise FDroidException()
|
raise FDroidException()
|
||||||
|
|
||||||
common.set_FDroidPopen_env(build)
|
common.set_FDroidPopen_env(app, build)
|
||||||
|
|
||||||
# create ..._toolsversion.log when running in builder vm
|
# create ..._toolsversion.log when running in builder vm
|
||||||
if onserver:
|
if onserver:
|
||||||
|
@ -1201,6 +1201,25 @@ def get_src_tarball_name(appid, versionCode):
|
|||||||
return f"{appid}_{versionCode}_src.tar.gz"
|
return f"{appid}_{versionCode}_src.tar.gz"
|
||||||
|
|
||||||
|
|
||||||
|
def get_source_date_epoch(build_dir):
|
||||||
|
"""Return timestamp suitable for the SOURCE_DATE_EPOCH variable.
|
||||||
|
|
||||||
|
https://reproducible-builds.org/docs/source-date-epoch/
|
||||||
|
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
return git.repo.Repo(build_dir).git.log(n=1, pretty='%ct')
|
||||||
|
except Exception as e:
|
||||||
|
logging.warning('%s: %s', e.__class__.__name__, build_dir)
|
||||||
|
build_dir = Path(build_dir)
|
||||||
|
appid = build_dir.name
|
||||||
|
data_dir = build_dir.parent.parent
|
||||||
|
metadata_file = f'metadata/{appid}.yml'
|
||||||
|
if (data_dir / '.git').exists() and (data_dir / metadata_file).exists():
|
||||||
|
repo = git.repo.Repo(data_dir)
|
||||||
|
return repo.git.log('-n1', '--pretty=%ct', '--', metadata_file)
|
||||||
|
|
||||||
|
|
||||||
def get_build_dir(app):
|
def get_build_dir(app):
|
||||||
"""Get the dir that this app will be built in."""
|
"""Get the dir that this app will be built in."""
|
||||||
if app.RepoType == 'srclib':
|
if app.RepoType == 'srclib':
|
||||||
@ -3202,12 +3221,16 @@ def remove_signing_keys(build_dir):
|
|||||||
logging.info("Cleaned %s of keysigning configs at %s" % (propfile, path))
|
logging.info("Cleaned %s of keysigning configs at %s" % (propfile, path))
|
||||||
|
|
||||||
|
|
||||||
def set_FDroidPopen_env(build=None):
|
def set_FDroidPopen_env(app=None, build=None):
|
||||||
"""Set up the environment variables for the build environment.
|
"""Set up the environment variables for the build environment.
|
||||||
|
|
||||||
There is only a weak standard, the variables used by gradle, so also set
|
There is only a weak standard, the variables used by gradle, so also set
|
||||||
up the most commonly used environment variables for SDK and NDK. Also, if
|
up the most commonly used environment variables for SDK and NDK. Also, if
|
||||||
there is no locale set, this will set the locale (e.g. LANG) to en_US.UTF-8.
|
there is no locale set, this will set the locale (e.g. LANG) to en_US.UTF-8.
|
||||||
|
|
||||||
|
If an App instance is provided, then the SOURCE_DATE_EPOCH
|
||||||
|
environment variable will be set based on that app's source repo.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
global env, orig_path
|
global env, orig_path
|
||||||
|
|
||||||
@ -3230,6 +3253,8 @@ def set_FDroidPopen_env(build=None):
|
|||||||
if missinglocale:
|
if missinglocale:
|
||||||
env['LANG'] = 'en_US.UTF-8'
|
env['LANG'] = 'en_US.UTF-8'
|
||||||
|
|
||||||
|
if app:
|
||||||
|
env['SOURCE_DATE_EPOCH'] = get_source_date_epoch(get_build_dir(app))
|
||||||
if build is not None:
|
if build is not None:
|
||||||
path = build.ndk_path()
|
path = build.ndk_path()
|
||||||
paths = orig_path.split(os.pathsep)
|
paths = orig_path.split(os.pathsep)
|
||||||
|
@ -205,6 +205,7 @@ class BuildTest(unittest.TestCase):
|
|||||||
@mock.patch('fdroidserver.build.FDroidPopen')
|
@mock.patch('fdroidserver.build.FDroidPopen')
|
||||||
@mock.patch('fdroidserver.common.is_debuggable_or_testOnly', lambda f: False)
|
@mock.patch('fdroidserver.common.is_debuggable_or_testOnly', lambda f: False)
|
||||||
@mock.patch('fdroidserver.common.get_native_code', lambda f: 'x86')
|
@mock.patch('fdroidserver.common.get_native_code', lambda f: 'x86')
|
||||||
|
@mock.patch('fdroidserver.common.get_source_date_epoch', lambda f: '1234567890')
|
||||||
def test_build_local_maven(self, fake_FDroidPopen, fake_get_apk_id):
|
def test_build_local_maven(self, fake_FDroidPopen, fake_get_apk_id):
|
||||||
"""Test build_local() with a maven project"""
|
"""Test build_local() with a maven project"""
|
||||||
|
|
||||||
@ -330,6 +331,8 @@ class BuildTest(unittest.TestCase):
|
|||||||
'fdroidserver.build.FDroidPopen', FakeProcess
|
'fdroidserver.build.FDroidPopen', FakeProcess
|
||||||
) as _ignored, mock.patch(
|
) as _ignored, mock.patch(
|
||||||
'sdkmanager.install', wraps=fake_sdkmanager_install
|
'sdkmanager.install', wraps=fake_sdkmanager_install
|
||||||
|
) as _ignored, mock.patch(
|
||||||
|
'fdroidserver.common.get_source_date_epoch', lambda f: '1234567890'
|
||||||
) as _ignored:
|
) as _ignored:
|
||||||
_ignored # silence the linters
|
_ignored # silence the linters
|
||||||
with self.assertRaises(
|
with self.assertRaises(
|
||||||
@ -378,6 +381,7 @@ class BuildTest(unittest.TestCase):
|
|||||||
@mock.patch('fdroidserver.build.FDroidPopen', FakeProcess)
|
@mock.patch('fdroidserver.build.FDroidPopen', FakeProcess)
|
||||||
@mock.patch('fdroidserver.common.get_native_code', lambda _ignored: 'x86')
|
@mock.patch('fdroidserver.common.get_native_code', lambda _ignored: 'x86')
|
||||||
@mock.patch('fdroidserver.common.is_debuggable_or_testOnly', lambda _ignored: False)
|
@mock.patch('fdroidserver.common.is_debuggable_or_testOnly', lambda _ignored: False)
|
||||||
|
@mock.patch('fdroidserver.common.get_source_date_epoch', lambda f: '1234567890')
|
||||||
@mock.patch(
|
@mock.patch(
|
||||||
'fdroidserver.common.sha256sum',
|
'fdroidserver.common.sha256sum',
|
||||||
lambda f: 'ad7ce5467e18d40050dc51b8e7affc3e635c85bd8c59be62de32352328ed467e',
|
lambda f: 'ad7ce5467e18d40050dc51b8e7affc3e635c85bd8c59be62de32352328ed467e',
|
||||||
@ -453,6 +457,7 @@ class BuildTest(unittest.TestCase):
|
|||||||
self.assertTrue(ndk_dir.exists())
|
self.assertTrue(ndk_dir.exists())
|
||||||
self.assertTrue(os.path.exists(config['ndk_paths'][ndk_version]))
|
self.assertTrue(os.path.exists(config['ndk_paths'][ndk_version]))
|
||||||
|
|
||||||
|
@mock.patch('fdroidserver.common.get_source_date_epoch', lambda f: '1234567890')
|
||||||
def test_build_local_clean(self):
|
def test_build_local_clean(self):
|
||||||
"""Test if `fdroid build` cleans ant and gradle build products"""
|
"""Test if `fdroid build` cleans ant and gradle build products"""
|
||||||
os.chdir(self.testdir)
|
os.chdir(self.testdir)
|
||||||
|
@ -2468,7 +2468,7 @@ class CommonTest(SetUpTearDownMixin, unittest.TestCase):
|
|||||||
fdroidserver.common.fill_config_defaults(config)
|
fdroidserver.common.fill_config_defaults(config)
|
||||||
build = fdroidserver.metadata.Build()
|
build = fdroidserver.metadata.Build()
|
||||||
with self.assertRaises(TypeError):
|
with self.assertRaises(TypeError):
|
||||||
fdroidserver.common.set_FDroidPopen_env(build)
|
fdroidserver.common.set_FDroidPopen_env(build=build)
|
||||||
|
|
||||||
@mock.patch.dict(os.environ, clear=True)
|
@mock.patch.dict(os.environ, clear=True)
|
||||||
def test_ndk_paths_in_config_must_be_strings(self):
|
def test_ndk_paths_in_config_must_be_strings(self):
|
||||||
@ -2480,7 +2480,7 @@ class CommonTest(SetUpTearDownMixin, unittest.TestCase):
|
|||||||
build.ndk = 'r21d'
|
build.ndk = 'r21d'
|
||||||
os.environ['PATH'] = '/usr/bin:/usr/sbin'
|
os.environ['PATH'] = '/usr/bin:/usr/sbin'
|
||||||
with self.assertRaises(TypeError):
|
with self.assertRaises(TypeError):
|
||||||
fdroidserver.common.set_FDroidPopen_env(build)
|
fdroidserver.common.set_FDroidPopen_env(build=build)
|
||||||
|
|
||||||
@mock.patch.dict(os.environ, clear=True)
|
@mock.patch.dict(os.environ, clear=True)
|
||||||
def test_FDroidPopen_envs_paths_can_be_pathlib(self):
|
def test_FDroidPopen_envs_paths_can_be_pathlib(self):
|
||||||
@ -2567,7 +2567,7 @@ class CommonTest(SetUpTearDownMixin, unittest.TestCase):
|
|||||||
|
|
||||||
with mock.patch.dict(os.environ, clear=True):
|
with mock.patch.dict(os.environ, clear=True):
|
||||||
os.environ['PATH'] = '/usr/bin:/usr/sbin'
|
os.environ['PATH'] = '/usr/bin:/usr/sbin'
|
||||||
fdroidserver.common.set_FDroidPopen_env(build)
|
fdroidserver.common.set_FDroidPopen_env(build=build)
|
||||||
self.assertNotIn('', os.getenv('PATH').split(os.pathsep))
|
self.assertNotIn('', os.getenv('PATH').split(os.pathsep))
|
||||||
|
|
||||||
def test_is_repo_file(self):
|
def test_is_repo_file(self):
|
||||||
@ -2993,6 +2993,53 @@ class CommonTest(SetUpTearDownMixin, unittest.TestCase):
|
|||||||
for mirror in fdroidserver.common.append_filename_to_mirrors(filename, mirrors):
|
for mirror in fdroidserver.common.append_filename_to_mirrors(filename, mirrors):
|
||||||
self.assertTrue(mirror['url'].endswith('/' + filename))
|
self.assertTrue(mirror['url'].endswith('/' + filename))
|
||||||
|
|
||||||
|
def test_get_source_date_epoch(self):
|
||||||
|
git_repo = git.Repo.init(self.testdir)
|
||||||
|
Path('README').write_text('file to commit')
|
||||||
|
git_repo.git.add(all=True)
|
||||||
|
git_repo.index.commit("README")
|
||||||
|
self.assertEqual(
|
||||||
|
git_repo.git.log(n=1, pretty='%ct'),
|
||||||
|
fdroidserver.common.get_source_date_epoch(self.testdir),
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_get_source_date_epoch_no_scm(self):
|
||||||
|
self.assertIsNone(fdroidserver.common.get_source_date_epoch(self.testdir))
|
||||||
|
|
||||||
|
def test_get_source_date_epoch_not_git(self):
|
||||||
|
"""Test when build_dir is not a git repo, e.g. hg, svn, etc."""
|
||||||
|
appid = 'com.example'
|
||||||
|
build_dir = Path(self.testdir) / 'build' / appid
|
||||||
|
fdroiddata = build_dir.parent.parent
|
||||||
|
(fdroiddata / 'metadata').mkdir()
|
||||||
|
build_dir.mkdir(parents=True)
|
||||||
|
os.chdir(build_dir)
|
||||||
|
git_repo = git.Repo.init(fdroiddata) # fdroiddata is always a git repo
|
||||||
|
with (fdroiddata / f'metadata/{appid}.yml').open('w') as fp:
|
||||||
|
fp.write('AutoName: Example App\n')
|
||||||
|
git_repo.git.add(all=True)
|
||||||
|
git_repo.index.commit("update README")
|
||||||
|
self.assertEqual(
|
||||||
|
git.repo.Repo(fdroiddata).git.log(n=1, pretty='%ct'),
|
||||||
|
fdroidserver.common.get_source_date_epoch(build_dir),
|
||||||
|
)
|
||||||
|
|
||||||
|
@mock.patch.dict(os.environ, {'PATH': os.getenv('PATH')}, clear=True)
|
||||||
|
def test_set_FDroidPopen_env_with_app(self):
|
||||||
|
"""Test SOURCE_DATE_EPOCH in FDroidPopen when build_dir is a git repo."""
|
||||||
|
os.chdir(self.testdir)
|
||||||
|
app = fdroidserver.metadata.App()
|
||||||
|
app.id = 'com.example'
|
||||||
|
build_dir = Path(self.testdir) / 'build' / app.id
|
||||||
|
git_repo = git.Repo.init(build_dir)
|
||||||
|
Path('README').write_text('file to commit')
|
||||||
|
git_repo.git.add(all=True)
|
||||||
|
now = datetime.now(timezone.utc)
|
||||||
|
git_repo.index.commit("README", commit_date=now)
|
||||||
|
fdroidserver.common.set_FDroidPopen_env(app)
|
||||||
|
p = fdroidserver.common.FDroidPopen(['printenv', 'SOURCE_DATE_EPOCH'])
|
||||||
|
self.assertEqual(int(p.output), int(now.timestamp()))
|
||||||
|
|
||||||
|
|
||||||
APKS_WITH_JAR_SIGNATURES = (
|
APKS_WITH_JAR_SIGNATURES = (
|
||||||
(
|
(
|
||||||
|
@ -350,6 +350,8 @@ class ScannerTest(unittest.TestCase):
|
|||||||
with mock.patch(
|
with mock.patch(
|
||||||
'fdroidserver.common.get_apk_id',
|
'fdroidserver.common.get_apk_id',
|
||||||
return_value=(app.id, build.versionCode, build.versionName),
|
return_value=(app.id, build.versionCode, build.versionName),
|
||||||
|
), mock.patch(
|
||||||
|
'fdroidserver.common.get_source_date_epoch', lambda f: '1234567890'
|
||||||
):
|
):
|
||||||
with mock.patch(
|
with mock.patch(
|
||||||
'fdroidserver.common.is_debuggable_or_testOnly',
|
'fdroidserver.common.is_debuggable_or_testOnly',
|
||||||
|
Loading…
x
Reference in New Issue
Block a user