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

129 lines
4.4 KiB
Python

#!/usr/bin/env python3
# SPDX-License-Identifier: BSD-2-Clause
#
# SPDX-FileCopyrightText: 2023 KDE e.V.
# SPDX-FileCopyrightText: 2023 Ben Cooksley <bcooksley@kde.org>
# SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de>
import logging
import os
import sys
import tarfile
from pathlib import Path
from tempfile import NamedTemporaryFile
from sftpnotary import config, projects, util
from sftpnotary.buildpublisher import PublishBuildJob
from sftpnotary.exceptions import Error
log = logging.getLogger("publishbuild")
def parseCommandLine():
import argparse
parser = argparse.ArgumentParser(description="Publish build artifacts to a remote server (usually a web server)")
# 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("--platform", required=True, help="the platform the artifacts correspond to")
parser.add_argument("folderToPublish", help="the folder containing build artifacts to be published; required")
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 publishBuild(buildArchiveFilePath: Path, projectPath: str, branch: str, platform: str) -> bool:
with util.connectToSftpServer() as sftp:
job = PublishBuildJob(
sftp,
buildArchiveFilePath=buildArchiveFilePath,
token=os.environ.get("CI_JOB_TOKEN", ""),
projectPath=projectPath,
branch=branch,
platform=platform,
)
try:
job.start()
job.waitForCompletion()
except Exception as e: # or more specific exception
log.error("Error: PublishBuildJob 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 build artifacts failed")
return job.success
def main() -> int:
options = parseCommandLine()
config.loadConfig(options.config)
util.setUpClientLogging(log, options.verbose)
projectSettingsLocation = config.settings.get("General", "ProjectSettings", "buildpublisher-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 build
projectPath = os.environ["CI_PROJECT_PATH"]
branch = os.environ["CI_COMMIT_REF_NAME"]
platform = options.platform
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
# Build a tarball of the build files and trigger the publishing process
with NamedTemporaryFile(delete=False, mode="wb") as artifactArchive:
# Open our temporary file as a tarball
archive = tarfile.open(fileobj=artifactArchive, mode="w")
# Determine the path to be archived and the files to be included
pathToArchive = Path(options.folderToPublish)
filesToInclude = os.listdir(pathToArchive)
# Start populating it with the build artifact files
for filename in filesToInclude:
fullPath = pathToArchive / filename
archive.add(fullPath, arcname=filename, recursive=True)
# Close it out to ensure everything is flushed to disk
# We have to use close() to ensure the file handle is fully closed otherwise 'sftp' on Windows will
# not be allowed to open the file
archive.close()
artifactArchive.close()
success = publishBuild(Path(artifactArchive.name), projectPath, branch, platform)
# Cleanup
os.remove(artifactArchive.name)
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)