From 44499d90faa4ae24fb933b419132f9d84bd856f8 Mon Sep 17 00:00:00 2001 From: linsui <2873532-linsui@users.noreply.gitlab.com> Date: Wed, 10 Dec 2025 14:12:40 +0100 Subject: [PATCH 1/2] Don't require versionName --- fdroidserver/build.py | 57 +++++++++++++++---------------- fdroidserver/checkupdates.py | 24 ++++++------- fdroidserver/common.py | 22 +++++++++--- fdroidserver/import_subcommand.py | 6 +--- fdroidserver/index.py | 30 +++++++++------- fdroidserver/lint.py | 14 ++++---- fdroidserver/scanner.py | 6 ++-- 7 files changed, 84 insertions(+), 75 deletions(-) diff --git a/fdroidserver/build.py b/fdroidserver/build.py index 1266e928..2b119cc0 100644 --- a/fdroidserver/build.py +++ b/fdroidserver/build.py @@ -277,7 +277,7 @@ def build_server(app, build, vcs, build_dir, output_dir, log_dir, force): message = "Timeout exceeded! Build VM force-stopped for {0}:{1}" else: message = "Build.py failed on server for {0}:{1}" - raise BuildException(message.format(app.id, build.versionName), + raise BuildException(message.format(app.id, build.versionCode), str(output, 'utf-8', 'replace')) # Retreive logs... @@ -304,7 +304,7 @@ def build_server(app, build, vcs, build_dir, output_dir, log_dir, force): except Exception as exc: raise BuildException( "Build failed for {0}:{1} - missing output files".format( - app.id, build.versionName), str(output, 'utf-8', 'replace')) from exc + app.id, build.versionCode), str(output, 'utf-8', 'replace')) from exc ftp.close() finally: @@ -480,18 +480,18 @@ def build_local(app, build, vcs, build_dir, output_dir, log_dir, srclib_dir, ext p = FDroidPopen(['sudo', 'DEBIAN_FRONTEND=noninteractive', 'bash', '-e', '-u', '-o', 'pipefail', '-x', '-c', '; '.join(build.sudo)]) if p.returncode != 0: - raise BuildException("Error running sudo command for %s:%s" % - (app.id, build.versionName), p.output) + raise BuildException("Error running sudo command for %s:%d" % + (app.id, build.versionCode), p.output) p = FDroidPopen(['sudo', 'passwd', '--lock', 'root']) if p.returncode != 0: - raise BuildException("Error locking root account for %s:%s" % - (app.id, build.versionName), p.output) + raise BuildException("Error locking root account for %s:%d" % + (app.id, build.versionCode), p.output) p = FDroidPopen(['sudo', 'SUDO_FORCE_REMOVE=yes', 'dpkg', '--purge', 'sudo']) if p.returncode != 0: - raise BuildException("Error removing sudo for %s:%s" % - (app.id, build.versionName), p.output) + raise BuildException("Error removing sudo for %s:%d" % + (app.id, build.versionCode), p.output) log_path = os.path.join(log_dir, common.get_toolsversion_logname(app, build)) @@ -499,8 +499,8 @@ def build_local(app, build, vcs, build_dir, output_dir, log_dir, srclib_dir, ext f.write(common.get_android_tools_version_log()) else: if build.sudo: - logging.warning('%s:%s runs this on the buildserver with sudo:\n\t%s\nThese commands were skipped because fdroid build is not running on a dedicated build server.' - % (app.id, build.versionName, build.sudo)) + logging.warning('%s:%d runs this on the buildserver with sudo:\n\t%s\nThese commands were skipped because fdroid build is not running on a dedicated build server.' + % (app.id, build.versionCode, build.sudo)) # Prepare the source code... root_dir, srclibpaths = common.prepare_source(vcs, app, build, @@ -551,8 +551,8 @@ def build_local(app, build, vcs, build_dir, output_dir, log_dir, srclib_dir, ext p = FDroidPopen(['ant', 'clean'], cwd=root_dir) if p is not None and p.returncode != 0: - raise BuildException("Error cleaning %s:%s" % - (app.id, build.versionName), p.output) + raise BuildException("Error cleaning %s:%d" % + (app.id, build.versionCode), p.output) for root, dirs, files in os.walk(build_dir): @@ -629,8 +629,8 @@ def build_local(app, build, vcs, build_dir, output_dir, log_dir, srclib_dir, ext p = FDroidPopen(['bash', '-e', '-u', '-o', 'pipefail', '-x', '-c', cmd], cwd=root_dir) if p.returncode != 0: - raise BuildException("Error running build command for %s:%s" % - (app.id, build.versionName), p.output) + raise BuildException("Error running build command for %s:%d" % + (app.id, build.versionCode), p.output) # Build native stuff if required... if build.buildjni and build.buildjni != ['no']: @@ -658,7 +658,7 @@ def build_local(app, build, vcs, build_dir, output_dir, log_dir, srclib_dir, ext del manifest_text p = FDroidPopen(cmd, cwd=os.path.join(root_dir, d)) if p.returncode != 0: - raise BuildException("NDK build failed for %s:%s" % (app.id, build.versionName), p.output) + raise BuildException("NDK build failed for %s:%d" % (app.id, build.versionCode), p.output) p = None # Build the release... @@ -716,10 +716,8 @@ def build_local(app, build, vcs, build_dir, output_dir, log_dir, srclib_dir, ext commit_id = build.commit if p is not None and p.returncode != 0: - raise BuildException("Build failed for %s:%s@%s" % (app.id, build.versionName, commit_id), + raise BuildException("Build failed for %s:%d@%s" % (app.id, build.versionCode, commit_id), p.output) - logging.info("Successfully built version {versionName} of {appid} from {commit_id}" - .format(versionName=build.versionName, appid=app.id, commit_id=commit_id)) omethod = build.output_method() if omethod == 'maven': @@ -820,13 +818,15 @@ def build_local(app, build, vcs, build_dir, output_dir, log_dir, srclib_dir, ext raise BuildException("Unsigned APK is not at expected location of " + src) if common.get_file_extension(src) == 'apk': - vercode, version = get_metadata_from_apk(app, build, src) - if version != build.versionName or vercode != build.versionCode: - raise BuildException(("Unexpected version/version code in output;" - " APK: '%s' / '%d', " - " Expected: '%s' / '%d'") - % (version, vercode, build.versionName, - build.versionCode)) + versionCode, _ignored = get_metadata_from_apk(app, build, src) + if versionCode != build.versionCode: + raise BuildException(("Unexpected versionCode in output;" + " APK: '%d', " + " Expected: '%d'") + % (versionCode, build.versionCode)) + + logging.info(f"Successfully built {app.id}:{versionCode} from {commit_id}") + if (options.scan_binary or config.get('scan_binary')) and not options.skipscan: if scanner.scan_binary(src): raise BuildException("Found blocklisted packages in final apk!") @@ -914,8 +914,7 @@ def trybuild(app, build, build_dir, output_dir, log_dir, also_check_dir, if build.disable and not options.force: return False - logging.info("Building version %s (%s) of %s" % ( - build.versionName, build.versionCode, app.id)) + logging.info(f"Building {app.id}:{build.versionCode}") if server: # When using server mode, still keep a local cache of the repo, by @@ -1309,8 +1308,8 @@ def main(): tstamp = time.strftime("%Y-%m-%d %H:%M:%SZ", time.gmtime()) with open(os.path.join(log_dir, appid + '.log'), 'a+') as f: f.write('\n\n============================================================\n') - f.write('versionCode: %s\nversionName: %s\ncommit: %s\n' % - (build.versionCode, build.versionName, build.commit)) + f.write('versionCode: %s\ncommit: %s\n' % + (build.versionCode, build.commit)) f.write('Build completed at ' + tstamp + '\n') f.write('\n' + tools_version_log + '\n') diff --git a/fdroidserver/checkupdates.py b/fdroidserver/checkupdates.py index 8463e746..c5d28589 100644 --- a/fdroidserver/checkupdates.py +++ b/fdroidserver/checkupdates.py @@ -425,10 +425,6 @@ def _getappname(app: metadata.App) -> str: return common.get_app_display_name(app) -def _getcvname(app: metadata.App) -> str: - return '%s (%s)' % (app.CurrentVersion, app.CurrentVersionCode) - - def fetch_autoname(app: metadata.App, tag: str) -> Optional[str]: """Fetch AutoName. @@ -587,9 +583,8 @@ def checkupdates_app(app: metadata.App, auto: bool, commit: bool = False) -> Non if updating: name = _getappname(app) - ver = _getcvname(app) - logging.info('...updating to version %s' % ver) - commitmsg = 'Update CurrentVersion of %s to %s' % (name, ver) + logging.info('...updating to version %d' % app.CurrentVersionCode) + commitmsg = 'Update CurrentVersionCode of %s to %d' % (name, app.CurrentVersionCode) if auto: mode = app.AutoUpdateMode @@ -651,22 +646,23 @@ def checkupdates_app(app: metadata.App, auto: bool, commit: bool = False) -> Non for b, v in zip(newbuilds, vercodes): b.disable = False b.versionCode = v - b.versionName = app.CurrentVersion + suffix.replace( - '%c', str(v) - ) - logging.info("...auto-generating build for " + b.versionName) + if b.versionName and app.CurrentVersion: + b.versionName = app.CurrentVersion + suffix.replace( + '%c', str(v) + ) + logging.info("...auto-generating build for " + str(b.versionCode)) if tag: b.commit = tag else: - commit = pattern.replace('%v', app.CurrentVersion) + if app.CurrentVersion: + commit = pattern.replace('%v', app.CurrentVersion) commit = commit.replace('%c', str(v)) b.commit = commit app['Builds'].extend(newbuilds) name = _getappname(app) - ver = _getcvname(app) - commitmsg = "Update %s to %s" % (name, ver) + commitmsg = "Update %s to %s" % (name, app.CurrentVersionCode) else: raise MetaDataException( _('Invalid AutoUpdateMode: {mode}').format(mode=mode) diff --git a/fdroidserver/common.py b/fdroidserver/common.py index 9c1bf0e9..8069b88d 100644 --- a/fdroidserver/common.py +++ b/fdroidserver/common.py @@ -2714,8 +2714,7 @@ def prepare_source( ) if p.returncode != 0: raise BuildException( - "Error running init command for %s:%s" % (app.id, build.versionName), - p.output, + f"Error running init command for {app.id}:{build.versionCode}", p.output ) # Apply patches if any @@ -2811,6 +2810,9 @@ def prepare_source( # Insert versionCode and number into the manifest if necessary if build.forceversion: + if not build.versionName: + raise MetaDataException(_("forceversion requires versionName to be set")) + logging.info("Changing the versionName") for path in manifest_paths(root_dir, flavors): if not os.path.isfile(path): @@ -2895,8 +2897,10 @@ def prepare_source( ['bash', '-e', '-u', '-o', 'pipefail', '-x', '-c', '--', cmd], cwd=root_dir ) if p.returncode != 0: - raise BuildException("Error running prebuild command for %s:%s" % - (app.id, build.versionName), p.output) + raise BuildException( + f"Error running prebuild command for {app.id}:{build.versionCode}", + p.output, + ) # Generate (or update) the ant build file, build.xml... if build.build_method() == 'ant' and build.androidupdate != ['no']: @@ -3532,8 +3536,16 @@ def set_FDroidPopen_env(app=None, build=None): def replace_build_vars(cmd, build): cmd = cmd.replace('$$COMMIT$$', build.commit) - cmd = cmd.replace('$$VERSION$$', build.versionName) cmd = cmd.replace('$$VERCODE$$', str(build.versionCode)) + + version_tag = '$$VERSION$$' + if build.get('versionName'): + cmd = cmd.replace(version_tag, build.versionName) + elif version_tag in cmd: + raise MetaDataException( + _("{tag} requires versionName to be set").format(tag=version_tag) + ) + return cmd diff --git a/fdroidserver/import_subcommand.py b/fdroidserver/import_subcommand.py index 017ebe54..99811b4e 100644 --- a/fdroidserver/import_subcommand.py +++ b/fdroidserver/import_subcommand.py @@ -348,11 +348,9 @@ def main(): paths = get_all_gradle_and_manifests(tmp_importer_dir) gradle_subdir = get_gradle_subdir(tmp_importer_dir, paths) if paths: - versionName, versionCode, appid = common.parse_androidmanifests(paths, app) + _ignored, versionCode, appid = common.parse_androidmanifests(paths, app) if not appid: raise FDroidException(_("Couldn't find Application ID")) - if not versionName: - logging.warning(_('Could not find latest versionName')) if not versionCode: logging.warning(_('Could not find latest versionCode')) else: @@ -363,8 +361,6 @@ def main(): raise FDroidException(_('Package "{appid}" already exists').format(appid=appid)) # Create a build line... - build.versionName = versionName or 'Unknown' - app.CurrentVersion = build.versionName build.versionCode = versionCode or 0 app.CurrentVersionCode = build.versionCode if options.subdir: diff --git a/fdroidserver/index.py b/fdroidserver/index.py index 2c1acba7..f4377fc7 100644 --- a/fdroidserver/index.py +++ b/fdroidserver/index.py @@ -796,12 +796,15 @@ def make_v2( for build in app.get('Builds', []): if build['versionCode'] == package['versionCode']: versionName = build.get('versionName') - logging.info( - _( - 'Overriding blank versionName in {apkfilename} from metadata: {version}' - ).format(apkfilename=package['apkName'], version=versionName) - ) - package['versionName'] = versionName + if versionName: + logging.info( + _( + 'Overriding blank versionName in {apkfilename} from metadata: {version}' + ).format( + apkfilename=package['apkName'], version=versionName + ) + ) + package['versionName'] = versionName break if packageName in output_packages: packagelist = output_packages[packageName] @@ -1003,12 +1006,15 @@ def make_v1(apps, packages, repodir, repodict, requestsdict, signer_fingerprints for build in app.get('Builds', []): if build['versionCode'] == package['versionCode']: versionName = build.get('versionName') - logging.info( - _( - 'Overriding blank versionName in {apkfilename} from metadata: {version}' - ).format(apkfilename=package['apkName'], version=versionName) - ) - package['versionName'] = versionName + if versionName: + logging.info( + _( + 'Overriding blank versionName in {apkfilename} from metadata: {version}' + ).format( + apkfilename=package['apkName'], version=versionName + ) + ) + package['versionName'] = versionName break if packageName in output_packages: packagelist = output_packages[packageName] diff --git a/fdroidserver/lint.py b/fdroidserver/lint.py index 78d98b5f..65326e48 100644 --- a/fdroidserver/lint.py +++ b/fdroidserver/lint.py @@ -1898,7 +1898,7 @@ FILLING_UCMS = re.compile(r'^(Tags.*|RepoManifest.*)') def check_checkupdates_ran(app): if FILLING_UCMS.match(app.UpdateCheckMode): - if not app.AutoName and not app.CurrentVersion and app.CurrentVersionCode == 0: + if not app.AutoName and app.CurrentVersionCode == 0: yield _( "UpdateCheckMode is set but it looks like checkupdates hasn't been run yet." ) @@ -1960,8 +1960,8 @@ def check_builds(app): for s in ['master', 'main', 'origin', 'HEAD', 'default', 'develop', 'trunk']: if build.commit and build.commit.startswith(s): yield _( - "Branch '{branch}' used as commit in build '{versionName}'" - ).format(branch=s, versionName=build.versionName) + "Branch '{branch}' used as commit in build '{versionCode}'" + ).format(branch=s, versionCode=build.versionCode) for srclib in build.srclibs: if '@' in srclib: ref = srclib.split('@')[1].split('/')[0] @@ -2006,8 +2006,8 @@ def check_files_dir(app): for build in app.get('Builds', []): for fname in build.patch: if fname not in files: - yield _("Unknown file '{filename}' in build '{versionName}'").format( - filename=fname, versionName=build.versionName + yield _("Unknown file '{filename}' in build '{versionCode}'").format( + filename=fname, versionCode=build.versionCode ) else: used.add(fname) @@ -2068,8 +2068,8 @@ def check_extlib_dir(apps): # Don't show error on archived versions if i >= len(builds) - archive_policy: yield _( - "{appid}: Unknown extlib {path} in build '{versionName}'" - ).format(appid=app.id, path=path, versionName=build.versionName) + "{appid}: Unknown extlib {path} in build '{versionCode}'" + ).format(appid=app.id, path=path, versionCode=build.versionCode) else: used.add(path) diff --git a/fdroidserver/scanner.py b/fdroidserver/scanner.py index 469706c3..96b6a1e2 100644 --- a/fdroidserver/scanner.py +++ b/fdroidserver/scanner.py @@ -1285,12 +1285,12 @@ def main(): if build.disable and not options.force: logging.info( - "...skipping version %s - %s" - % (build.versionName, build.get('disable', build.commit[1:])) + "...skipping versionCode %s - %s" + % (build.versionCode, build.get('disable', build.commit[1:])) ) continue - logging.info("...scanning version " + build.versionName) + logging.info("...scanning versionCode " + build.versionCode) # Prepare the source code... common.prepare_source( vcs, app, build, build_dir, srclib_dir, extlib_dir, False From 34447616b6dcb497601d9ca1ee56adb7af9f5a9a Mon Sep 17 00:00:00 2001 From: Hans-Christoph Steiner Date: Wed, 10 Dec 2025 14:33:30 +0100 Subject: [PATCH 2/2] add tests for common.replace_build_vars() --- tests/test_common.py | 64 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/tests/test_common.py b/tests/test_common.py index 4e743f33..e4027fa4 100755 --- a/tests/test_common.py +++ b/tests/test_common.py @@ -1,5 +1,6 @@ #!/usr/bin/env python3 +import base64 import difflib import glob import gzip @@ -3780,3 +3781,66 @@ class CalculateMathStringTest(unittest.TestCase): def test_calculate_math_string_error_comment(self): with self.assertRaises(SyntaxError): fdroidserver.common.calculate_math_string('1-1 # no comment') + + +class BuildVarsTest(unittest.TestCase): + def setUp(self): + self.build = fdroidserver.metadata.Build() + self.build.commit = 'deadcafebeef' + self.build.versionCode = 1 + + def test_replace_build_vars_empty(self): + testvalue = '' + self.assertEqual( + testvalue, + fdroidserver.common.replace_build_vars(testvalue, self.build), + ) + + def test_replace_build_vars_random(self): + length = 1024 + testvalue = base64.b64encode(os.urandom(length)).decode('utf-8')[:length] + self.assertEqual( + testvalue, + fdroidserver.common.replace_build_vars(testvalue, self.build), + ) + + def test_replace_build_vars_non_var(self): + testvalue = '$$NOTANFDROIDVAR$$' + self.assertEqual( + testvalue, + fdroidserver.common.replace_build_vars(testvalue, self.build), + ) + + def test_replace_build_vars_commit(self): + testvalue = '$$COMMIT$$' + self.assertEqual( + self.build.commit, + fdroidserver.common.replace_build_vars(testvalue, self.build), + ) + + def test_replace_build_vars_versionCode(self): + testvalue = '$$VERCODE$$' + self.assertEqual( + str(self.build.versionCode), + fdroidserver.common.replace_build_vars(testvalue, self.build), + ) + + def test_replace_build_vars_versionName(self): + self.build.versionName = 'v1.0' + testvalue = '$$VERSION$$' + self.assertEqual( + self.build.versionName, + fdroidserver.common.replace_build_vars(testvalue, self.build), + ) + + def test_replace_build_vars_all(self): + self.build.versionName = 'v1.0' + testvalue = '$$COMMIT$$-$$VERCODE$$-$$VERSION$$' + self.assertEqual( + f'{self.build.commit}-{self.build.versionCode}-{self.build.versionName}', + fdroidserver.common.replace_build_vars(testvalue, self.build), + ) + + def test_replace_build_vars_no_versionName(self): + with self.assertRaises(fdroidserver.exception.MetaDataException): + fdroidserver.common.replace_build_vars('$$VERSION$$', self.build),