OpenMiInk/third_party/ci-notary-service/notarizemacapp.py
Hellings b8c8ff98e2 Hi
2026-10-16 09:05:56 -04:00

121 lines
4 KiB
Python

#!/usr/bin/env python3
# SPDX-License-Identifier: BSD-2-Clause
#
# SPDX-FileCopyrightText: 2023 KDE e.V.
# SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de>
# SPDX-FileCopyrightText: 2023 Julius Künzel <jk.kdedev@smartlab.uber.space>
import logging
import os
import sys
import tarfile
from pathlib import Path
from tempfile import TemporaryDirectory
from typing import Optional
from sftpnotary import config, projects, util
from sftpnotary.exceptions import Error
from sftpnotary.macappnotarizer import NotarizeMacAppJob
log = logging.getLogger("notarizemacapp")
def parseCommandLine():
import argparse
parser = argparse.ArgumentParser(description="Notarize macOS apps with signing service")
# debug options
parser.add_argument("-v", "--verbose", action="count", default=0, help="increase the verbosity")
parser.add_argument("--config")
parser.add_argument("appfile", help="the .dmg file or .app (inside tar archive) to notarize")
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 notarizeMacApp(app: Path, logFolder: Path, projectPath: Optional[str], branch: Optional[str]) -> bool:
with util.connectToSftpServer() as sftp:
job = NotarizeMacAppJob(
sftp,
macAppFilePath=app,
logFolder=logFolder,
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: NotarizeMacAppJob failed", exc_info=e)
for logFile in job.logFiles:
if logFile.name == "task.log":
logTaskLog(logFile)
if not job.success:
log.error("Error: Notarizing macOS app failed")
return job.success
def main() -> int:
options = parseCommandLine()
config.loadConfig(options.config)
util.setUpClientLogging(log, options.verbose)
projectSettingsLocation = config.settings.get("General", "ProjectSettings", "macappnotarizer-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 notarization. Skipping.")
return 0
# Check if it makes sense to attempt notarizing the macOS file
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 notarization. Skipping.\n
You can clear it by adding the branch to {projectSettingsLocation}"""
)
return 0
appFilePath = Path(options.appfile)
if appFilePath.suffix == ".app":
# Build a tarball since .app files are basically special folders
with TemporaryDirectory() as tempArchiveDir:
appArchivePath = Path(tempArchiveDir) / "shuttleArchive"
with tarfile.open(name=appArchivePath, mode="w") as archive:
archive.add(appFilePath, arcname=appFilePath.name, recursive=True)
success = notarizeMacApp(appArchivePath, appFilePath.parent, projectPath, branch)
if success:
# notarizeMacApp overwrites the archive with the unsigned content
# so we can just extract the archive at the same location again
util.extractTarArchive(appArchivePath, appFilePath.parent)
else:
success = notarizeMacApp(appFilePath, appFilePath.parent, 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)