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

133 lines
4.7 KiB
Python

# 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 dataclasses
import logging
import os
import tarfile
from pathlib import Path
from tempfile import TemporaryDirectory
from typing import Optional
from sftpnotary import projects, util
from sftpnotary.core import Job, Request, Response, TaskProcessor, Worker
from sftpnotary.exceptions import Error
from sftpnotary.sftp import SFTPClient, SFTPError
log = logging.getLogger(__name__)
@dataclasses.dataclass
class PublishWebsiteRequest(Request):
websiteArchiveFileName: Optional[str] = None
def validate(self):
util.validateFileName(self.websiteArchiveFileName, strict=True)
PublishWebsiteResponse = Response
class PublishWebsiteJob(Job):
def __init__(
self,
sftp: SFTPClient,
websiteArchiveFilePath: Path,
token: str,
projectPath: str,
branch: str,
):
super().__init__(sftp)
self.websiteArchiveFilePath = websiteArchiveFilePath
self.request = PublishWebsiteRequest(
websiteArchiveFileName=None,
token=token,
projectPath=projectPath,
branch=branch,
)
self.resultPath = self.websiteArchiveFilePath.parent
def start(self):
websiteArchiveFileName = self.websiteArchiveFilePath.name
self.sftp.upload(self.websiteArchiveFilePath, self.task.path() / websiteArchiveFileName)
self.request.websiteArchiveFileName = websiteArchiveFileName
self.task.putRequest(self.request)
self.taskQueue.commitTask(self.task)
def waitForCompletion(self):
self.taskQueue.waitForCompletion(self.task)
log.debug(f"Fetching response of task '{self.task.id}' ...")
response = None
try:
response = self.task.fetchResponse(PublishWebsiteResponse)
except SFTPError as e:
log.error("Error: %s", e)
if response is None:
log.error("Error: Got no response. Task failed.")
else:
self.success = True
self.downloadLogFiles(self.resultPath)
self.taskQueue.removeTask(self.task)
class PublishWebsiteProcessor(TaskProcessor):
requestClass = PublishWebsiteRequest
def getProjectSetting(self, name, required=True):
value = projects.settings.get(self.request.projectPath, self.request.branch, name)
if required and not value:
raise Error(
f"Project setting '{name}' not set for branch {self.request.branch} "
f"of project {self.request.projectPath}"
)
return value
def doProcess(self):
# Get the details for where we will publish the website to
rsyncDestination = self.getProjectSetting("rsyncDestination")
websiteDomain = self.getProjectSetting("domain")
# Make sure the website domain we have been given is valid...
if not websiteDomain:
raise Error(f"Website domain has not been set for branch {self.request.branch} and is mandatory")
with TemporaryDirectory() as workPathName:
workPath = Path(workPathName)
localWebsiteArchiveFilePath = workPath / self.request.websiteArchiveFileName
self.sftp.download(self.task.path() / self.request.websiteArchiveFileName, localWebsiteArchiveFilePath)
# Setup a path for us to unpack the website archive into...
unpackedWebsitePath = workPath / "unpacked-site"
os.makedirs(unpackedWebsitePath)
# Unpack the website archive....
archive = tarfile.open(name=localWebsiteArchiveFilePath, mode="r")
# Set "nosec B202" because of https://github.com/PyCQA/bandit/issues/1038#issuecomment-1635863856
archive.extractall(filter="tar", path=unpackedWebsitePath) # nosec B202
archive.close()
# Determine the final rsync destination
# We allow for the website domain to be optionally added into this via use of %s
# This avoids having to repeat ourselves in the configuration file
finalRsyncDestination = rsyncDestination % websiteDomain
# Build up the rsync command to run then run it
# The entire contents of
rsyncCommand = [
"rsync",
"--archive",
"--checksum",
"--delete",
"--verbose",
str(unpackedWebsitePath) + "/",
finalRsyncDestination.rstrip("/") + "/",
]
util.runCommand(rsyncCommand, cwd=workPathName)
class PublishWebsiteWorker(Worker):
processorClass = PublishWebsiteProcessor