#!/usr/bin/env python3 # SPDX-License-Identifier: BSD-2-Clause # # SPDX-FileCopyrightText: 2023 KDE e.V. # SPDX-FileContributor: Ingo Klöcker import logging import os import sys from pathlib import Path from typing import List, Optional from sftpnotary import config, projects, util from sftpnotary.exceptions import Error from sftpnotary.fdroidpublisher import PublishOnFDroidJob log = logging.getLogger("publishonfdroid") def parseCommandLine(): import argparse parser = argparse.ArgumentParser(description="Publish one or more APKs of a single app in an F-Droid repository") # debug options parser.add_argument("-v", "--verbose", action="count", default=0, help="increase the verbosity") parser.add_argument("--config", required=True, help="the configuration to use; required") parser.add_argument( "--fastlane", required=True, help="the Fastlane file containing the meta data for the APKs; required" ) parser.add_argument("apks", nargs="+", metavar="APK", help="the APKs to publish") options = parser.parse_args() return options def logTaskLog(logFile: Path): log.info("Logs of remote signing:") with open(logFile, "r", encoding="utf-8") as f: for line in f: log.info(line.rstrip()) def publishApks(apks: List[Path], fastlane: Path, projectPath: Optional[str], branch: Optional[str]) -> bool: with util.connectToSftpServer() as sftp: job = PublishOnFDroidJob( sftp, apkFilePaths=apks, fastlaneFilePath=fastlane, token=os.environ.get("CI_JOB_TOKEN"), projectPath=projectPath, branch=branch, ) try: job.start() job.waitForCompletion() except Exception as e: # or more specific exception log.error("Error: PublishOnFDroidJob failed", exc_info=e) for logFile in job.logFiles: if logFile.name == "task.log": logTaskLog(logFile) if not job.success: log.error("Error: Publishing the APKs failed") return job.success def main() -> int: options = parseCommandLine() config.loadConfig(options.config) util.setUpClientLogging(log, options.verbose) projectSettingsLocation = config.settings.get("General", "ProjectSettings", "fdroidpublisher-projects.yaml") util.loadProjectSettings( projectSettingsLocation, relativeTo=Path(options.config).absolute().parent, ) # Ignore merge request pipelines if os.environ.get("CI_PIPELINE_SOURCE") == "merge_request_event": log.info("Merge request pipelines are not cleared for publishing. Skipping.") return 0 # Check if it makes sense to attempt publishing the APK projectPath = os.environ["CI_PROJECT_PATH"] branch = os.environ["CI_COMMIT_REF_NAME"] if not projects.settings.exists(projectPath, branch): log.info( f"""Branch '{branch}' of project '{projectPath}' is not cleared for publishing. Skipping.\n You can clear it by adding the branch to {projectSettingsLocation}""" ) return 0 success = publishApks([Path(apk) for apk in options.apks], Path(options.fastlane), projectPath, branch) return 0 if success else 1 if __name__ == "__main__": try: sys.exit(main()) except Error as e: print("Error: %s" % e, file=sys.stderr) sys.exit(1)