forked from sig_core/toolkit
Compare commits
22 Commits
fresh-empa
...
devel
Author | SHA1 | Date | |
---|---|---|---|
e061332e2b | |||
ab57120508 | |||
edd17ab0d8 | |||
f268502918 | |||
a4d6899601 | |||
df4bb47691 | |||
d53e0d939d | |||
0bb3867c6a | |||
78301b7906 | |||
2cb5ae42b9 | |||
96f8877d1b | |||
1470e590d3 | |||
546f8b4687 | |||
7f3a4b4761 | |||
4906749ed0 | |||
1a45143b00 | |||
fc0b738c75 | |||
689e7aa793 | |||
9c1b828ab7 | |||
448b8c035b | |||
a6f4632d66 | |||
08d8995344 |
@ -1,4 +1,4 @@
|
||||
FROM quay.io/rockylinux/rockylinux:9
|
||||
FROM quay.io/centos/centos:stream9
|
||||
|
||||
ADD images/get_arch /get_arch
|
||||
|
||||
|
@ -38,8 +38,6 @@ RUN dnf install -y \
|
||||
sudo \
|
||||
mock \
|
||||
python-pip \
|
||||
mock \
|
||||
fuse-overlayfs \
|
||||
imagefactory \
|
||||
imagefactory-plugins*
|
||||
|
||||
|
@ -1,5 +0,0 @@
|
||||
"""Empanadas Backends (fillings)"""
|
||||
|
||||
from .imagefactory import ImageFactoryBackend
|
||||
from .kiwi import KiwiBackend
|
||||
from .interface import BackendInterface
|
@ -1,318 +0,0 @@
|
||||
"""Backend for ImageFactory"""
|
||||
|
||||
import json
|
||||
import os
|
||||
import pathlib
|
||||
import tempfile
|
||||
|
||||
from .interface import BackendInterface
|
||||
from empanadas.builders import utils
|
||||
|
||||
from attrs import define, field
|
||||
|
||||
from typing import List, Optional, Callable, Union
|
||||
|
||||
KICKSTART_PATH = pathlib.Path(os.environ.get("KICKSTART_PATH", "/kickstarts"))
|
||||
STORAGE_DIR = pathlib.Path("/var/lib/imagefactory/storage")
|
||||
|
||||
|
||||
@define(kw_only=True)
|
||||
class ImageFactoryBackend(BackendInterface):
|
||||
"""Build an image using ImageFactory"""
|
||||
kickstart_arg: List[str] = field(factory=list)
|
||||
kickstart_path: pathlib.Path = field(init=False)
|
||||
base_uuid: Optional[str] = field(default="")
|
||||
target_uuid: Optional[str] = field(default="")
|
||||
tdl_path: pathlib.Path = field(init=False)
|
||||
out_type: str = field(init=False)
|
||||
command_args: List[str] = field(factory=list)
|
||||
common_args: List[str] = field(factory=list)
|
||||
package_args: List[str] = field(factory=list)
|
||||
metadata: pathlib.Path = field(init=False)
|
||||
stage_commands: Optional[List[List[Union[str, Callable]]]] = field(init=False)
|
||||
|
||||
# The url to use in the path when fetching artifacts for the build
|
||||
kickstart_dir: str = field() # 'os' or 'kickstart'
|
||||
|
||||
# The git repository to fetch kickstarts from
|
||||
kickstart_repo: str = field()
|
||||
|
||||
def prepare(self):
|
||||
self.out_type = self.image_format()
|
||||
|
||||
tdl_template = self.ctx.tmplenv.get_template('icicle/tdl.xml.tmpl')
|
||||
|
||||
self.tdl_path = self.render_icicle_template(tdl_template)
|
||||
if not self.tdl_path:
|
||||
exit(2)
|
||||
|
||||
self.metadata = pathlib.Path(self.ctx.outdir, ".imagefactory-metadata.json")
|
||||
|
||||
self.kickstart_path = pathlib.Path(f"{KICKSTART_PATH}/Rocky-{self.ctx.architecture.major}-{self.ctx.type_variant}.ks")
|
||||
|
||||
self.checkout_kickstarts()
|
||||
self.kickstart_arg = self.kickstart_imagefactory_args()
|
||||
|
||||
try:
|
||||
os.mkdir(self.ctx.outdir)
|
||||
except FileExistsError:
|
||||
self.log.info("Directory already exists for this release. If possible, previously executed steps may be skipped")
|
||||
except Exception as e:
|
||||
self.log.exception("Some other exception occured while creating the output directory", e)
|
||||
return 0
|
||||
|
||||
if os.path.exists(self.metadata):
|
||||
self.ctx.log.info(f"Found metadata at {self.metadata}")
|
||||
with open(self.metadata, "r") as f:
|
||||
try:
|
||||
o = json.load(f)
|
||||
self.base_uuid = o['base_uuid']
|
||||
self.target_uuid = o['target_uuid']
|
||||
except json.decoder.JSONDecodeError as e:
|
||||
self.ctx.log.exception("Couldn't decode metadata file", e)
|
||||
finally:
|
||||
f.flush()
|
||||
|
||||
self.command_args = self._command_args()
|
||||
self.package_args = self._package_args()
|
||||
self.common_args = self._common_args()
|
||||
|
||||
self.setup_staging()
|
||||
|
||||
def build(self) -> int:
|
||||
if self.base_uuid:
|
||||
return 0
|
||||
|
||||
self.fix_ks()
|
||||
|
||||
# TODO(neil): this should be a lambda which is called from the function
|
||||
ret, out, err, uuid = self.ctx.prepare_and_run(self.build_command(), search=True)
|
||||
if uuid:
|
||||
self.base_uuid = uuid.rstrip()
|
||||
self.save()
|
||||
|
||||
if ret > 0:
|
||||
return ret
|
||||
|
||||
ret = self.package()
|
||||
|
||||
if ret > 0:
|
||||
return ret
|
||||
|
||||
|
||||
def clean(self):
|
||||
pass
|
||||
|
||||
def save(self):
|
||||
with open(self.metadata, "w") as f:
|
||||
try:
|
||||
o = {
|
||||
name: getattr(self, name) for name in [
|
||||
"base_uuid", "target_uuid"
|
||||
]
|
||||
}
|
||||
self.ctx.log.debug(o)
|
||||
json.dump(o, f)
|
||||
except AttributeError as e:
|
||||
self.ctx.log.error("Couldn't find attribute in object. Something is probably wrong", e)
|
||||
except Exception as e:
|
||||
self.ctx.log.exception(e)
|
||||
finally:
|
||||
f.flush()
|
||||
|
||||
def package(self) -> int:
|
||||
# Some build types don't need to be packaged by imagefactory
|
||||
# @TODO remove business logic if possible
|
||||
if self.ctx.image_type in ["GenericCloud", "EC2", "Azure", "Vagrant", "OCP", "RPI", "GenericArm"]:
|
||||
self.target_uuid = self.base_uuid if hasattr(self, 'base_uuid') else ""
|
||||
|
||||
if self.target_uuid:
|
||||
return 0
|
||||
|
||||
ret, out, err, uuid = self.ctx.prepare_and_run(self.package_command(), search=True)
|
||||
if uuid:
|
||||
self.target_uuid = uuid.rstrip()
|
||||
self.save()
|
||||
return ret
|
||||
|
||||
def stage(self) -> int:
|
||||
""" Stage the artifacst from wherever they are (unpacking and converting if needed)"""
|
||||
self.ctx.log.info("Executing staging commands")
|
||||
if not hasattr(self, 'stage_commands'):
|
||||
return 0
|
||||
|
||||
returns = []
|
||||
for command in self.stage_commands: # type: ignore
|
||||
ret, out, err, _ = self.ctx.prepare_and_run(command, search=False)
|
||||
returns.append(ret)
|
||||
|
||||
if (res := all(ret > 0 for ret in returns) > 0):
|
||||
raise Exception(res)
|
||||
|
||||
return 0
|
||||
|
||||
def checkout_kickstarts(self) -> int:
|
||||
cmd = ["git", "clone", "--branch", f"r{self.ctx.architecture.major}",
|
||||
self.kickstart_repo, f"{KICKSTART_PATH}"]
|
||||
ret, out, err, _ = self.ctx.prepare_and_run(cmd, search=False)
|
||||
self.ctx.log.debug(out)
|
||||
self.ctx.log.debug(err)
|
||||
if ret > 0:
|
||||
ret = self.pull_kickstarts()
|
||||
return ret
|
||||
|
||||
def pull_kickstarts(self) -> int:
|
||||
cmd: utils.CMD_PARAM_T = ["git", "-C", f"{KICKSTART_PATH}", "reset", "--hard", "HEAD"]
|
||||
ret, out, err, _ = self.ctx.prepare_and_run(cmd, search=False)
|
||||
self.ctx.log.debug(out)
|
||||
self.ctx.log.debug(err)
|
||||
if ret == 0:
|
||||
cmd = ["git", "-C", f"{KICKSTART_PATH}", "pull"]
|
||||
ret, out, err, _ = self.ctx.prepare_and_run(cmd, search=False)
|
||||
self.ctx.log.debug(out)
|
||||
self.ctx.log.debug(err)
|
||||
return ret
|
||||
|
||||
def _command_args(self):
|
||||
args_mapping = {
|
||||
"debug": "--debug",
|
||||
}
|
||||
# NOTE(neil): i'm intentionally leaving this as is; deprecated
|
||||
return [param for name, param in args_mapping.items() if self.ctx.debug]
|
||||
|
||||
def _package_args(self) -> List[str]:
|
||||
if self.ctx.image_type in ["Container"]:
|
||||
return ["--parameter", "compress", "xz"]
|
||||
return [""]
|
||||
|
||||
def _common_args(self) -> List[str]:
|
||||
args = []
|
||||
if self.ctx.image_type in ["Container"]:
|
||||
args = ["--parameter", "offline_icicle", "true"]
|
||||
if self.ctx.image_type in ["GenericCloud", "EC2", "Vagrant", "Azure", "OCP", "RPI", "GenericArm"]:
|
||||
args = ["--parameter", "generate_icicle", "false"]
|
||||
return args
|
||||
|
||||
def image_format(self) -> str:
|
||||
mapping = {
|
||||
"Container": "docker"
|
||||
}
|
||||
return mapping[self.ctx.image_type] if self.ctx.image_type in mapping.keys() else ''
|
||||
|
||||
def kickstart_imagefactory_args(self) -> List[str]:
|
||||
|
||||
if not self.kickstart_path.is_file():
|
||||
self.ctx.log.warning(f"Kickstart file is not available: {self.kickstart_path}")
|
||||
if not self.ctx.debug:
|
||||
self.ctx.log.warning("Exiting because debug mode is not enabled.")
|
||||
exit(2)
|
||||
|
||||
return ["--file-parameter", "install_script", str(self.kickstart_path)]
|
||||
|
||||
def render_icicle_template(self, tdl_template) -> pathlib.Path:
|
||||
output = tempfile.NamedTemporaryFile(delete=False).name
|
||||
return utils.render_template(output, tdl_template,
|
||||
architecture=self.ctx.architecture.name,
|
||||
iso8601date=self.ctx.build_time.strftime("%Y%m%d"),
|
||||
installdir=self.kickstart_dir,
|
||||
major=self.ctx.architecture.major,
|
||||
minor=self.ctx.architecture.minor,
|
||||
release=self.ctx.release,
|
||||
size="10G",
|
||||
type=self.ctx.image_type,
|
||||
utcnow=self.ctx.build_time,
|
||||
version_variant=self.ctx.architecture.version if not self.ctx.variant else f"{self.ctx.architecture.version}-{self.ctx.variant}",
|
||||
)
|
||||
|
||||
def build_command(self) -> List[str]:
|
||||
build_command = ["imagefactory", "--timeout", self.ctx.timeout,
|
||||
*self.command_args, "base_image", *self.common_args,
|
||||
*self.kickstart_arg, self.tdl_path]
|
||||
return build_command
|
||||
|
||||
def package_command(self) -> List[str]:
|
||||
package_command = ["imagefactory", *self.command_args, "target_image",
|
||||
self.out_type, *self.common_args,
|
||||
"--id", f"{self.base_uuid}",
|
||||
*self.package_args,
|
||||
"--parameter", "repository", self.ctx.outname]
|
||||
return package_command
|
||||
|
||||
def fix_ks(self):
|
||||
cmd: utils.CMD_PARAM_T = ["sed", "-i", f"s,$basearch,{self.ctx.architecture.name},", str(self.kickstart_path)]
|
||||
self.ctx.prepare_and_run(cmd, search=False)
|
||||
|
||||
def setup_staging(self):
|
||||
# Yes, this is gross. I'll fix it later.
|
||||
if self.ctx.image_type in ["Container"]:
|
||||
self.stage_commands = [
|
||||
["tar", "-C", f"{self.ctx.outdir}", "--strip-components=1", "-x", "-f", lambda: f"{STORAGE_DIR}/{self.target_uuid}.body", "*/layer.tar"],
|
||||
["xz", f"{self.ctx.outdir}/layer.tar"]
|
||||
]
|
||||
if self.ctx.image_type in ["RPI"]:
|
||||
self.stage_commands = [
|
||||
["cp", lambda: f"{STORAGE_DIR}/{self.target_uuid}.body", f"{self.ctx.outdir}/{self.ctx.outname}.raw"],
|
||||
["xz", f"{self.ctx.outdir}/{self.ctx.outname}.raw"]
|
||||
]
|
||||
if self.ctx.image_type in ["GenericCloud", "OCP", "GenericArm"]:
|
||||
self.stage_commands = [
|
||||
["qemu-img", "convert", "-c", "-f", "raw", "-O", "qcow2",
|
||||
lambda: f"{STORAGE_DIR}/{self.target_uuid}.body", f"{self.ctx.outdir}/{self.ctx.outname}.qcow2"]
|
||||
]
|
||||
if self.ctx.image_type in ["EC2"]:
|
||||
self.stage_commands = [
|
||||
["qemu-img", "convert", "-f", "raw", "-O", "qcow2", lambda: f"{STORAGE_DIR}/{self.target_uuid}.body", f"{self.ctx.outdir}/{self.ctx.outname}.qcow2"]
|
||||
]
|
||||
if self.ctx.image_type in ["Azure"]:
|
||||
self.stage_commands = [
|
||||
["/prep-azure.sh", lambda: f"{STORAGE_DIR}/{self.target_uuid}.body", f"{STORAGE_DIR}"],
|
||||
["cp", lambda: f"{STORAGE_DIR}/{self.target_uuid}.vhd", f"{self.ctx.outdir}/{self.ctx.outname}.vhd"]
|
||||
]
|
||||
if self.ctx.image_type in ["Vagrant"]:
|
||||
_map = {
|
||||
"Vbox": {"format": "vmdk", "provider": "virtualbox"},
|
||||
"Libvirt": {"format": "qcow2", "provider": "libvirt", "virtual_size": 10},
|
||||
"VMware": {"format": "vmdk", "provider": "vmware_desktop"}
|
||||
}
|
||||
output = f"{_map[self.ctx.variant]['format']}" # type: ignore
|
||||
provider = f"{_map[self.ctx.variant]['provider']}" # type: ignore
|
||||
|
||||
# pop from the options map that will be passed to the vagrant metadata.json
|
||||
convert_options = _map[self.ctx.variant].pop('convertOptions') if 'convertOptions' in _map[self.ctx.variant].keys() else '' # type: ignore
|
||||
|
||||
self.stage_commands = [
|
||||
["qemu-img", "convert", "-c", "-f", "raw", "-O", output, *convert_options,
|
||||
lambda: f"{STORAGE_DIR}/{self.target_uuid}.body", f"{self.ctx.outdir}/{self.ctx.outname}.{output}"],
|
||||
["tar", "-C", self.ctx.outdir, "-czf", f"/tmp/{self.ctx.outname}.box", '.'],
|
||||
["mv", f"/tmp/{self.ctx.outname}.box", self.ctx.outdir]
|
||||
]
|
||||
self.prepare_vagrant(_map[self.ctx.variant])
|
||||
|
||||
if self.stage_commands:
|
||||
self.stage_commands.append(["cp", "-v", lambda: f"{STORAGE_DIR}/{self.target_uuid}.meta", f"{self.ctx.outdir}/build.meta"])
|
||||
|
||||
def prepare_vagrant(self, options):
|
||||
"""Setup the output directory for the Vagrant type variant, dropping templates as required"""
|
||||
|
||||
templates = {}
|
||||
templates['Vagrantfile'] = self.ctx.tmplenv.get_template(f"vagrant/Vagrantfile.{self.ctx.variant}")
|
||||
templates['metadata.json'] = self.ctx.tmplenv.get_template('vagrant/metadata.tmpl.json')
|
||||
templates['info.json'] = self.ctx.tmplenv.get_template('vagrant/info.tmpl.json')
|
||||
|
||||
if self.ctx.variant == "VMware":
|
||||
templates[f"{self.ctx.outname}.vmx"] = self.ctx.tmplenv.get_template('vagrant/vmx.tmpl')
|
||||
|
||||
if self.ctx.variant == "Vbox":
|
||||
templates['box.ovf'] = self.ctx.tmplenv.get_template('vagrant/box.tmpl.ovf')
|
||||
|
||||
if self.ctx.variant == "Libvirt":
|
||||
# Libvirt vagrant driver expects the qcow2 file to be called box.img.
|
||||
qemu_command_index = [i for i, d in enumerate(self.stage_commands) if d[0] == "qemu-img"][0]
|
||||
self.stage_commands.insert(qemu_command_index+1, ["mv", f"{self.ctx.outdir}/{self.ctx.outname}.qcow2", f"{self.ctx.outdir}/box.img"])
|
||||
|
||||
for name, template in templates.items():
|
||||
utils.render_template(f"{self.ctx.outdir}/{name}", template,
|
||||
name=self.ctx.outname,
|
||||
arch=self.ctx.architecture.name,
|
||||
options=options
|
||||
)
|
@ -1,40 +0,0 @@
|
||||
"""
|
||||
empanadas backend interface
|
||||
"""
|
||||
from abc import ABC, abstractmethod
|
||||
from attrs import define, field
|
||||
|
||||
|
||||
@define
|
||||
class BackendInterface(ABC):
|
||||
ctx = field(init=False)
|
||||
"""
|
||||
Interface to build images (or whatever)
|
||||
"""
|
||||
@abstractmethod
|
||||
def prepare(self):
|
||||
"""
|
||||
Prepares the environment necessary for building the image.
|
||||
This might include setting up directories, checking prerequisites, etc.
|
||||
"""
|
||||
|
||||
@abstractmethod
|
||||
def build(self):
|
||||
"""
|
||||
Performs the image build operation. This is the core method
|
||||
where the actual image building logic is implemented.
|
||||
"""
|
||||
|
||||
@abstractmethod
|
||||
def stage(self):
|
||||
"""
|
||||
Transforms and copies artifacts from build directory to the
|
||||
location expected by the builder (usually in /tmp/)
|
||||
"""
|
||||
|
||||
@abstractmethod
|
||||
def clean(self):
|
||||
"""
|
||||
Cleans up any resources or temporary files created during
|
||||
the image building process.
|
||||
"""
|
@ -1,241 +0,0 @@
|
||||
"""Backend for Kiwi"""
|
||||
|
||||
from .interface import BackendInterface
|
||||
from .kiwi_imagedata import ImagesData
|
||||
|
||||
from empanadas.builders import utils
|
||||
from empanadas.common import AttributeDict
|
||||
|
||||
from attrs import define, field
|
||||
from functools import wraps
|
||||
from typing import List
|
||||
|
||||
import git
|
||||
import os
|
||||
import pathlib
|
||||
import tempfile
|
||||
import shutil
|
||||
import sys
|
||||
|
||||
# TODO(neil): this should be part of the config, somewhere
|
||||
temp = AttributeDict(
|
||||
{
|
||||
"Azure": {
|
||||
"kiwiType": "oem",
|
||||
"kiwiProfile": "Cloud-Azure",
|
||||
"fileType": "raw", # post-converted into vhd on MB boundary
|
||||
"outputKey": "disk_format_image",
|
||||
},
|
||||
"OCP": {
|
||||
"kiwiType": "oem",
|
||||
"kiwiProfile": "Cloud-OCP",
|
||||
"fileType": "qcow2",
|
||||
"outputKey": "disk_format_image",
|
||||
},
|
||||
"GenericCloud": {
|
||||
"kiwiType": "oem",
|
||||
"kiwiProfile": "Cloud-GenericCloud",
|
||||
"fileType": "qcow2",
|
||||
"outputKey": "disk_format_image",
|
||||
},
|
||||
"EC2": {
|
||||
"kiwiType": "oem",
|
||||
"kiwiProfile": "Cloud-EC2",
|
||||
"fileType": "qcow2",
|
||||
"outputKey": "disk_format_image",
|
||||
},
|
||||
"Vagrant": {
|
||||
"kiwiType": "oem",
|
||||
"kiwiProfile": "Vagrant",
|
||||
"fileType": "box",
|
||||
"outputKey": "disk_format_image",
|
||||
},
|
||||
"Container": {
|
||||
"kiwiType": "oci",
|
||||
"kiwiProfile": "Container",
|
||||
"fileType": "tar.xz",
|
||||
"outputKey": "container"
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
def ensure_kiwi_conf(func):
|
||||
@wraps(func)
|
||||
def wrapper(self, *args, **kwargs):
|
||||
if not hasattr(self, 'kiwi_conf') or self.kiwi_conf is None:
|
||||
self.kiwi_conf = temp[self.ctx.image_type]
|
||||
return func(self, *args, **kwargs)
|
||||
return wrapper
|
||||
|
||||
|
||||
@define
|
||||
class KiwiBackend(BackendInterface):
|
||||
"""Build an image using Kiwi"""
|
||||
|
||||
build_args: List[str] = field(factory=list)
|
||||
image_result: ImagesData = field(init=False)
|
||||
kiwi_conf: AttributeDict = field(init=False)
|
||||
|
||||
def prepare(self):
|
||||
"""
|
||||
Checkout mock-rocky-configs and rocky-kiwi-descriptions,
|
||||
init the mock env, and setup to run kiwi
|
||||
"""
|
||||
self.checkout_repos()
|
||||
self.setup_mock()
|
||||
self.setup_kiwi()
|
||||
|
||||
@ensure_kiwi_conf
|
||||
def build(self):
|
||||
self.build_args += [f"--type={self.kiwi_conf.kiwiType}", f"--profile={self.kiwi_conf.kiwiProfile}-{self.ctx.variant}"]
|
||||
|
||||
kiwi_command = [
|
||||
"kiwi-ng", "--color-output",
|
||||
*self.build_args,
|
||||
]
|
||||
if self.ctx.debug:
|
||||
kiwi_command.append("--debug")
|
||||
|
||||
kiwi_system_command = [
|
||||
"system", "build",
|
||||
"--description='/builddir/rocky-kiwi-descriptions'",
|
||||
"--target-dir", f"/builddir/{self.ctx.outdir}"
|
||||
]
|
||||
|
||||
build_command = [
|
||||
"--shell", "--enable-network", "--", *kiwi_command, *kiwi_system_command
|
||||
]
|
||||
ret, out, err = self.run_mock_command(build_command)
|
||||
if ret > 0:
|
||||
raise Exception(f"Kiwi build failed: code {ret}")
|
||||
sys.exit(ret)
|
||||
|
||||
@ensure_kiwi_conf
|
||||
def stage(self):
|
||||
ret, out, err = self.run_mock_command(["--copyout", f"/builddir/{self.ctx.outdir}", self.ctx.outdir])
|
||||
if ret > 0:
|
||||
raise Exception("failed to copy build result out")
|
||||
|
||||
kiwi_result_path = pathlib.Path(f"{self.ctx.outdir}/kiwi.result.json")
|
||||
if not os.path.exists(kiwi_result_path):
|
||||
raise Exception("Missing kiwi.result.json. Aborting")
|
||||
|
||||
with open(kiwi_result_path, "r") as kiwi_result:
|
||||
self.image_result = ImagesData.from_json(kiwi_result.read()).images
|
||||
|
||||
source = self.image_result[self.kiwi_conf.outputKey].filename
|
||||
filetype = self.kiwi_conf.fileType
|
||||
|
||||
source = utils.remove_first_directory(source)
|
||||
dest = f"{self.ctx.outdir}/{self.ctx.outname}.{filetype}"
|
||||
|
||||
# NOTE(neil): only because we are preparing the 'final' image in clean step...
|
||||
if self.ctx.image_type == 'Container':
|
||||
dest = f"{self.ctx.outdir}/{self.ctx.outname}.oci"
|
||||
|
||||
try:
|
||||
shutil.move(source, dest)
|
||||
except Exception as e:
|
||||
raise e
|
||||
|
||||
# TODO(neil): refactor
|
||||
if self.ctx.image_type == 'Azure':
|
||||
try:
|
||||
utils.resize_and_convert_raw_image_to_vhd(dest, self.ctx.outdir)
|
||||
# Remove old raw image
|
||||
pathlib.Path(f"{self.ctx.outdir}/{self.ctx.outname}.raw").unlink()
|
||||
except Exception as e:
|
||||
raise e
|
||||
|
||||
def clean(self):
|
||||
# TODO(neil): refactor
|
||||
if self.ctx.image_type == 'Container':
|
||||
# need to do this before we remove it, otherwise we have to extract from the OCI tarball
|
||||
root = f"/builddir{self.ctx.outdir}"
|
||||
builddir = f"{root}/build/image-root"
|
||||
ret, out, err = self.run_mock_command(["--shell", "--", "tar", "-C", builddir, "-cJf", f"{root}/{self.ctx.outname}.tar.xz", "."])
|
||||
if ret > 0:
|
||||
raise Exception(err)
|
||||
|
||||
ret, out, err = self.run_mock_command(["--shell", "rm", "-fr", f"/builddir/{self.ctx.outdir}/build/"])
|
||||
return ret
|
||||
|
||||
def run_mock_command(self, mock_command: List[str]):
|
||||
mock_args = ["--configdir", "/tmp/mock-rocky-configs/etc/mock", "-r", f"rl-9-{self.ctx.architecture.name}-core-infra"]
|
||||
if self.ctx.image_type != 'Container':
|
||||
mock_args.append("--isolation=simple")
|
||||
command = [
|
||||
"mock",
|
||||
*mock_args,
|
||||
*mock_command,
|
||||
]
|
||||
ret, out, err, _ = self.ctx.prepare_and_run(command)
|
||||
return ret, out, err
|
||||
|
||||
def setup_mock(self):
|
||||
# TODO(neil): add error checking
|
||||
ret, out, err = self.run_mock_command(["--init"])
|
||||
|
||||
packages = [
|
||||
"kiwi-boxed-plugin",
|
||||
"kiwi-cli",
|
||||
"git",
|
||||
"dracut-kiwi-live",
|
||||
"fuse-overlayfs",
|
||||
"kiwi-systemdeps-bootloaders",
|
||||
"kiwi-systemdeps-containers",
|
||||
"kiwi-systemdeps-core",
|
||||
"kiwi-systemdeps-disk-images",
|
||||
"kiwi-systemdeps-filesystems",
|
||||
"kiwi-systemdeps-image-validation",
|
||||
"kiwi-systemdeps-iso-media",
|
||||
"epel-release",
|
||||
"rocky-release-core"
|
||||
]
|
||||
ret, out, err = self.run_mock_command(["--install", *packages])
|
||||
|
||||
ret, out, err = self.run_mock_command(["--copyin", "/tmp/rocky-kiwi-descriptions", "/builddir/"])
|
||||
return ret
|
||||
|
||||
def checkout_repos(self):
|
||||
"""
|
||||
Checkout sig_core/mock-rocky-configs and sig_core/rocky-kiwi-descriptions to /tmp
|
||||
"""
|
||||
repos = {
|
||||
"mock-rocky-configs": "main",
|
||||
"rocky-kiwi-descriptions": "r9"
|
||||
}
|
||||
|
||||
for repo, branch in repos.items():
|
||||
repo_url = f"https://git.resf.org/sig_core/{repo}"
|
||||
clone_dir = f"/tmp/{repo}"
|
||||
|
||||
if os.path.isdir(os.path.join(clone_dir, ".git")):
|
||||
try:
|
||||
# The directory exists and is a git repository, so attempt to pull the latest changes
|
||||
git.Repo(clone_dir).remotes.origin.pull(branch)
|
||||
self.ctx.log.info(f"pulled the latest changes for {branch} branch in {clone_dir}")
|
||||
except Exception as e:
|
||||
raise Exception(f"Failed to pull the repository: {str(e)}")
|
||||
finally:
|
||||
continue
|
||||
|
||||
try:
|
||||
git.Repo.clone_from(repo_url, clone_dir, branch=branch)
|
||||
print(f"Repository cloned into {clone_dir}")
|
||||
except Exception as e:
|
||||
print(f"Failed to clone repository: {str(e)}")
|
||||
|
||||
def setup_kiwi(self):
|
||||
self.ctx.log.info("Generating kiwi.yml from template")
|
||||
template = self.ctx.tmplenv.get_template('kiwi/kiwi.yml.j2')
|
||||
output = tempfile.NamedTemporaryFile(delete=False).name
|
||||
res = utils.render_template(output, template)
|
||||
|
||||
self.ctx.log.info("Copying generated kiwi.yml into build root")
|
||||
ret, out, err = self.run_mock_command(["--copyin", res, "/etc/kiwi.yml"])
|
||||
if ret > 0:
|
||||
raise Exception("Failed to configure kiwi")
|
||||
|
||||
self.ctx.log.info("Finished setting up kiwi")
|
@ -1,24 +0,0 @@
|
||||
from attrs import define, field
|
||||
from typing import Dict
|
||||
|
||||
import json
|
||||
|
||||
|
||||
@define(auto_attribs=True, kw_only=True)
|
||||
class ImageInfo:
|
||||
compress: bool
|
||||
filename: str
|
||||
shasum: bool
|
||||
use_for_bundle: bool
|
||||
|
||||
|
||||
@define(auto_attribs=True, kw_only=True)
|
||||
class ImagesData:
|
||||
images: Dict[str, ImageInfo] = field(factory=dict)
|
||||
|
||||
@staticmethod
|
||||
def from_json(data: str) -> 'ImagesData':
|
||||
json_data = json.loads(data)
|
||||
images = {key: ImageInfo(**value) for key, value in json_data.items()}
|
||||
|
||||
return ImagesData(images=images)
|
@ -1 +0,0 @@
|
||||
from .imagebuild import ImageBuild
|
@ -1,118 +0,0 @@
|
||||
"""Build an image with a given backend"""
|
||||
|
||||
import datetime
|
||||
import logging
|
||||
import os
|
||||
import pathlib
|
||||
|
||||
from attrs import define, field
|
||||
|
||||
from empanadas.backends import BackendInterface, KiwiBackend
|
||||
from empanadas.common import Architecture
|
||||
from empanadas.common import _rootdir
|
||||
from . import utils
|
||||
|
||||
from jinja2 import Environment, FileSystemLoader, Template
|
||||
from typing import List, Optional, Tuple, Callable
|
||||
|
||||
|
||||
@define(kw_only=True)
|
||||
class ImageBuild: # pylint: disable=too-few-public-methods
|
||||
"""Image builder using a given backend"""
|
||||
tmplenv: Environment = field(init=False)
|
||||
|
||||
# Only things we know we're keeping in this class here
|
||||
architecture: Architecture = field()
|
||||
backend: BackendInterface = field()
|
||||
build_time: datetime.datetime = field()
|
||||
debug: bool = field(default=False)
|
||||
log: logging.Logger = field()
|
||||
release: int = field(default=0)
|
||||
timeout: str = field(default='3600')
|
||||
|
||||
image_type: str = field() # the type of the image
|
||||
type_variant: str = field(init=False)
|
||||
variant: Optional[str] = field()
|
||||
|
||||
# Kubernetes job template
|
||||
job_template: Optional[Template] = field(init=False) # the kube Job tpl
|
||||
|
||||
# Commands to stage artifacts
|
||||
|
||||
# Where the artifacts should go to
|
||||
outdir: pathlib.Path = field(init=False)
|
||||
outname: str = field(init=False)
|
||||
|
||||
def __attrs_post_init__(self):
|
||||
self.backend.ctx = self
|
||||
|
||||
file_loader = FileSystemLoader(f"{_rootdir}/templates")
|
||||
self.tmplenv = Environment(loader=file_loader)
|
||||
|
||||
self.job_template = self.tmplenv.get_template('kube/Job.tmpl')
|
||||
|
||||
self.type_variant = self.type_variant_name()
|
||||
self.outdir, self.outname = self.output_name()
|
||||
|
||||
def output_name(self) -> Tuple[pathlib.Path, str]:
|
||||
directory = f"Rocky-{self.architecture.major}-{self.type_variant}-{self.architecture.version}-{self.build_time.strftime('%Y%m%d')}.{self.release}"
|
||||
name = f"{directory}.{self.architecture.name}"
|
||||
outdir = pathlib.Path("/tmp/", directory)
|
||||
return outdir, name
|
||||
|
||||
def type_variant_name(self):
|
||||
return self.image_type if not self.variant else f"{self.image_type}-{self.variant}"
|
||||
|
||||
def prepare_and_run(self, command: utils.CMD_PARAM_T, search: Callable = None) -> utils.CMD_RESULT_T:
|
||||
return utils.runCmd(self, self.prepare_command(command), search)
|
||||
|
||||
def prepare_command(self, command_list: utils.CMD_PARAM_T) -> List[str]:
|
||||
"""
|
||||
Commands may be a callable, which should be a lambda to be evaluated at
|
||||
preparation time with available locals. This can be used to, among
|
||||
other things, perform lazy evaluations of f-strings which have values
|
||||
not available at assignment time. e.g., filling in a second command
|
||||
with a value extracted from the previous step or command.
|
||||
"""
|
||||
|
||||
r = []
|
||||
for c in command_list:
|
||||
if callable(c) and c.__name__ == '<lambda>':
|
||||
r.append(c())
|
||||
else:
|
||||
r.append(str(c))
|
||||
return r
|
||||
|
||||
def render_kubernetes_job(self):
|
||||
# TODO(neil): should this be put in the builder class itself to return the right thing for us?
|
||||
if self.backend == KiwiBackend:
|
||||
self.log.error("Kube not implemented for Kiwi")
|
||||
|
||||
commands = [self.backend.build_command(), self.backend.package_command(), self.backend.copy_command()]
|
||||
if not self.job_template:
|
||||
return None
|
||||
template = self.job_template.render(
|
||||
architecture=self.architecture.name,
|
||||
backoffLimit=4,
|
||||
buildTime=self.build_time.strftime("%s"),
|
||||
command=commands,
|
||||
imageName="ghcr.io/rockylinux/sig-core-toolkit:latest",
|
||||
jobname="buildimage",
|
||||
namespace="empanadas",
|
||||
major=self.architecture.major,
|
||||
minor=self.architecture.minor,
|
||||
restartPolicy="Never",
|
||||
)
|
||||
return template
|
||||
|
||||
def upload(self, skip=False) -> int:
|
||||
if not skip:
|
||||
self.log.info("Copying files to output directory")
|
||||
copy_command = ["aws", "s3", "cp", "--recursive", f"{self.outdir}/",
|
||||
f"s3://resf-empanadas/buildimage-{self.architecture.version}-{self.architecture.name}/{self.outname}/{self.build_time.strftime('%s')}/"
|
||||
]
|
||||
ret, out, err, _ = self.prepare_and_run(copy_command, search=False)
|
||||
return ret
|
||||
|
||||
self.ctx.log.info(f"Build complete! Output available in {self.ctx.outdir}/")
|
||||
return 0
|
@ -1,139 +0,0 @@
|
||||
import json
|
||||
import os
|
||||
import logging
|
||||
import pathlib
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
from typing import Callable, List, Tuple, Union
|
||||
|
||||
CMD_PARAM_T = List[Union[str, Callable[..., str]]]
|
||||
|
||||
STR_NONE_T = Union[bytes, None]
|
||||
BYTES_NONE_T = Union[bytes, None]
|
||||
# Tuple of int, stdout, stderr, uuid
|
||||
CMD_RESULT_T = Tuple[int, BYTES_NONE_T, BYTES_NONE_T, STR_NONE_T]
|
||||
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
log.setLevel(logging.INFO)
|
||||
handler = logging.StreamHandler(sys.stdout)
|
||||
handler.setLevel(logging.INFO)
|
||||
formatter = logging.Formatter(
|
||||
'%(asctime)s :: %(name)s :: %(message)s',
|
||||
'%Y-%m-%d %H:%M:%S'
|
||||
)
|
||||
handler.setFormatter(formatter)
|
||||
log.addHandler(handler)
|
||||
|
||||
|
||||
def render_template(path, template, **kwargs) -> pathlib.Path:
|
||||
with open(path, "wb") as f:
|
||||
_template = template.render(**kwargs)
|
||||
f.write(_template.encode())
|
||||
f.flush()
|
||||
output = pathlib.Path(path)
|
||||
if not output.exists():
|
||||
raise Exception("Failed to template")
|
||||
return output
|
||||
|
||||
|
||||
def runCmd(ctx, prepared_command: List[str], search: Callable = None) -> CMD_RESULT_T:
|
||||
ctx.log.info(f"Running command: {' '.join(prepared_command)}")
|
||||
|
||||
kwargs = {
|
||||
"stderr": subprocess.PIPE,
|
||||
"stdout": subprocess.PIPE
|
||||
}
|
||||
|
||||
if ctx.debug:
|
||||
del kwargs["stderr"]
|
||||
|
||||
with subprocess.Popen(prepared_command, **kwargs) as p:
|
||||
uuid = None
|
||||
# @TODO implement this as a callback?
|
||||
if search:
|
||||
for _, line in enumerate(p.stdout): # type: ignore
|
||||
ln = line.decode()
|
||||
if ln.startswith("UUID: "):
|
||||
uuid = ln.split(" ")[-1]
|
||||
ctx.log.debug(f"found uuid: {uuid}")
|
||||
|
||||
out, err = p.communicate()
|
||||
res = p.wait(), out, err, uuid
|
||||
|
||||
if res[0] > 0:
|
||||
ctx.log.error(f"Problem while executing command: '{prepared_command}'")
|
||||
if search and not res[3]:
|
||||
ctx.log.error("UUID not found in stdout. Dumping stdout and stderr")
|
||||
log_subprocess(ctx, res)
|
||||
|
||||
return res
|
||||
|
||||
|
||||
def log_subprocess(ctx, result: CMD_RESULT_T):
|
||||
def log_lines(title, lines):
|
||||
ctx.log.info(f"====={title}=====")
|
||||
ctx.log.info(lines.decode())
|
||||
ctx.log.info(f"Command return code: {result[0]}")
|
||||
stdout = result[1]
|
||||
stderr = result[2]
|
||||
if stdout:
|
||||
log_lines("Command STDOUT", stdout)
|
||||
if stderr:
|
||||
log_lines("Command STDERR", stderr)
|
||||
|
||||
|
||||
def remove_first_directory(path):
|
||||
p = pathlib.Path(path)
|
||||
# Check if the path is absolute
|
||||
if p.is_absolute():
|
||||
# For an absolute path, start the new path with the root
|
||||
new_path = pathlib.Path(p.root, *p.parts[2:])
|
||||
else:
|
||||
# For a relative path, simply skip the first part
|
||||
new_path = pathlib.Path(*p.parts[1:])
|
||||
return new_path
|
||||
|
||||
|
||||
def resize_and_convert_raw_image_to_vhd(raw_image_path, outdir=None):
|
||||
log.info(f"Will resize and convert {raw_image_path}")
|
||||
MB = 1024 * 1024 # For calculations - 1048576 bytes
|
||||
|
||||
if outdir is None:
|
||||
outdir = os.getcwd()
|
||||
|
||||
# Ensure the output directory exists
|
||||
pathlib.Path(outdir).mkdir(parents=True, exist_ok=True)
|
||||
|
||||
# Getting the size of the raw image
|
||||
result = subprocess.run(['qemu-img', 'info', '-f', 'raw', '--output', 'json', raw_image_path], capture_output=True, text=True)
|
||||
if result.returncode != 0:
|
||||
log.error("Error getting image info")
|
||||
raise Exception(result)
|
||||
|
||||
image_info = json.loads(result.stdout)
|
||||
size = int(image_info['virtual-size'])
|
||||
|
||||
# Calculate the new size rounded to the nearest MB
|
||||
rounded_size = ((size + MB - 1) // MB) * MB
|
||||
|
||||
# Prepare output filename (.raw replaced by .vhd)
|
||||
outfilename = pathlib.Path(raw_image_path).name.replace("raw", "vhd")
|
||||
outfile = os.path.join(outdir, outfilename)
|
||||
|
||||
# Resize the image
|
||||
log.info(f"Resizing {raw_image_path} to nearest MB boundary")
|
||||
result = subprocess.run(['qemu-img', 'resize', '-f', 'raw', raw_image_path, str(rounded_size)])
|
||||
if result.returncode != 0:
|
||||
log.error("Error resizing image")
|
||||
raise Exception(result)
|
||||
|
||||
# Convert the image
|
||||
log.info(f"Converting {raw_image_path} to vhd")
|
||||
result = subprocess.run(['qemu-img', 'convert', '-f', 'raw', '-o', 'subformat=fixed,force_size', '-O', 'vpc', raw_image_path, outfile])
|
||||
if result.returncode != 0:
|
||||
log.error("Error converting image to VHD format")
|
||||
raise Exception(result)
|
||||
|
||||
log.info(f"Image converted and saved to {outfile}")
|
@ -1,22 +1,21 @@
|
||||
# All imports are here
|
||||
import glob
|
||||
import hashlib
|
||||
import logging
|
||||
import os
|
||||
import platform
|
||||
import time
|
||||
from collections import defaultdict
|
||||
from attrs import define, field
|
||||
|
||||
from typing import Tuple
|
||||
|
||||
import rpm
|
||||
import yaml
|
||||
|
||||
|
||||
# An implementation from the Fabric python library
|
||||
class AttributeDict(dict):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(AttributeDict, self).__init__(*args, **kwargs)
|
||||
for key, value in self.items():
|
||||
if isinstance(value, dict):
|
||||
self[key] = AttributeDict(value)
|
||||
class AttributeDict(defaultdict):
|
||||
def __init__(self):
|
||||
super(AttributeDict, self).__init__(AttributeDict)
|
||||
|
||||
def __getattr__(self, key):
|
||||
try:
|
||||
@ -27,11 +26,6 @@ class AttributeDict(dict):
|
||||
def __setattr__(self, key, value):
|
||||
self[key] = value
|
||||
|
||||
def __setitem__(self, key, value):
|
||||
if isinstance(value, dict):
|
||||
value = AttributeDict(value)
|
||||
super(AttributeDict, self).__setitem__(key, value)
|
||||
|
||||
|
||||
# These are a bunch of colors we may use in terminal output
|
||||
class Color:
|
||||
@ -65,7 +59,6 @@ config = {
|
||||
"category_stub": "mirror/pub/rocky",
|
||||
"sig_category_stub": "mirror/pub/sig",
|
||||
"repo_base_url": "https://yumrepofs.build.resf.org/v1/projects",
|
||||
"staging_base_url": "https://dl.rockylinux.org/stg/rocky",
|
||||
"mock_work_root": "/builddir",
|
||||
"container": "centos:stream9",
|
||||
"distname": "Rocky Linux",
|
||||
@ -114,7 +107,7 @@ for conf in glob.iglob(f"{_rootdir}/sig/*.yaml"):
|
||||
|
||||
ALLOWED_TYPE_VARIANTS = {
|
||||
"Azure": ["Base", "LVM"],
|
||||
"Container": ["Base", "Minimal", "UBI", "WSL", "Toolbox"],
|
||||
"Container": ["Base", "Minimal", "UBI", "WSL"],
|
||||
"EC2": ["Base", "LVM"],
|
||||
"GenericCloud": ["Base", "LVM"],
|
||||
"Vagrant": ["Libvirt", "Vbox", "VMware"],
|
||||
@ -127,7 +120,7 @@ ALLOWED_TYPE_VARIANTS = {
|
||||
def valid_type_variant(_type: str, variant: str = "") -> bool:
|
||||
if _type not in ALLOWED_TYPE_VARIANTS:
|
||||
raise Exception(f"Type is invalid: ({_type}, {variant})")
|
||||
if ALLOWED_TYPE_VARIANTS[_type] is None:
|
||||
if ALLOWED_TYPE_VARIANTS[_type] == None:
|
||||
if variant is not None:
|
||||
raise Exception(f"{_type} Type expects no variant type.")
|
||||
return True
|
||||
@ -142,6 +135,9 @@ def valid_type_variant(_type: str, variant: str = "") -> bool:
|
||||
return True
|
||||
|
||||
|
||||
from attrs import define, field
|
||||
|
||||
|
||||
@define(kw_only=True)
|
||||
class Architecture:
|
||||
name: str = field()
|
||||
|
@ -31,7 +31,6 @@
|
||||
- 'AppStream'
|
||||
- 'CRB'
|
||||
- 'HighAvailability'
|
||||
- 'ResilientStorage'
|
||||
- 'RT'
|
||||
- 'NFV'
|
||||
- 'SAP'
|
||||
@ -190,9 +189,6 @@
|
||||
HighAvailability:
|
||||
- BaseOS
|
||||
- AppStream
|
||||
ResilientStorage:
|
||||
- BaseOS
|
||||
- AppStream
|
||||
RT:
|
||||
- BaseOS
|
||||
- AppStream
|
||||
|
@ -31,7 +31,6 @@
|
||||
- 'AppStream'
|
||||
- 'CRB'
|
||||
- 'HighAvailability'
|
||||
- 'ResilientStorage'
|
||||
- 'RT'
|
||||
- 'NFV'
|
||||
- 'SAP'
|
||||
@ -190,9 +189,6 @@
|
||||
HighAvailability:
|
||||
- BaseOS
|
||||
- AppStream
|
||||
ResilientStorage:
|
||||
- BaseOS
|
||||
- AppStream
|
||||
RT:
|
||||
- BaseOS
|
||||
- AppStream
|
||||
|
@ -18,7 +18,7 @@
|
||||
- x86_64
|
||||
- aarch64
|
||||
provide_multilib: False
|
||||
project_id: 'df5bcbfc-ba83-4da8-84d6-ae0168921b4d'
|
||||
project_id: 'e9cfc87c-d2d2-42d5-a121-852101f1a966'
|
||||
repo_symlinks:
|
||||
devel: 'Devel'
|
||||
NFV: 'nfv'
|
||||
|
@ -1,10 +1,10 @@
|
||||
---
|
||||
'9-beta':
|
||||
fullname: 'Rocky Linux 9.4'
|
||||
revision: '9.4'
|
||||
fullname: 'Rocky Linux 9.6'
|
||||
revision: '9.6'
|
||||
rclvl: 'BETA1'
|
||||
major: '9'
|
||||
minor: '4'
|
||||
minor: '6'
|
||||
profile: '9-beta'
|
||||
disttag: 'el9'
|
||||
code: "Blue Onyx"
|
||||
@ -20,7 +20,7 @@
|
||||
- ppc64le
|
||||
- s390x
|
||||
provide_multilib: True
|
||||
project_id: 'df5bcbfc-ba83-4da8-84d6-ae0168921b4d'
|
||||
project_id: 'ae163d6a-f050-484f-bbaa-100ca673f146'
|
||||
repo_symlinks:
|
||||
NFV: 'nfv'
|
||||
renames:
|
||||
|
@ -1,10 +1,10 @@
|
||||
---
|
||||
'9':
|
||||
fullname: 'Rocky Linux 9.4'
|
||||
revision: '9.4'
|
||||
fullname: 'Rocky Linux 9.5'
|
||||
revision: '9.5'
|
||||
rclvl: 'RC1'
|
||||
major: '9'
|
||||
minor: '4'
|
||||
minor: '5'
|
||||
profile: '9'
|
||||
disttag: 'el9'
|
||||
code: "Blue Onyx"
|
||||
@ -20,7 +20,7 @@
|
||||
- ppc64le
|
||||
- s390x
|
||||
provide_multilib: True
|
||||
project_id: 'df5bcbfc-ba83-4da8-84d6-ae0168921b4d'
|
||||
project_id: 'ae163d6a-f050-484f-bbaa-100ca673f146'
|
||||
repo_symlinks:
|
||||
NFV: 'nfv'
|
||||
renames:
|
||||
|
@ -1,10 +1,10 @@
|
||||
---
|
||||
'9-lookahead':
|
||||
fullname: 'Rocky Linux 9.5'
|
||||
revision: '9.5'
|
||||
fullname: 'Rocky Linux 9.6'
|
||||
revision: '9.6'
|
||||
rclvl: 'LH1'
|
||||
major: '9'
|
||||
minor: '5'
|
||||
minor: '6'
|
||||
profile: '9-lookahead'
|
||||
disttag: 'el9'
|
||||
code: "Blue Onyx"
|
||||
@ -20,7 +20,7 @@
|
||||
- ppc64le
|
||||
- s390x
|
||||
provide_multilib: True
|
||||
project_id: '6794b5a8-290b-4d0d-ad5a-47164329cbb0'
|
||||
project_id: 'ae163d6a-f050-484f-bbaa-100ca673f146'
|
||||
repo_symlinks:
|
||||
NFV: 'nfv'
|
||||
renames:
|
||||
|
@ -1,49 +1,44 @@
|
||||
# Builds an image given a version, type, variant, anctx.d architecture
|
||||
# Builds an image given a version, type, variant, and architecture
|
||||
# Defaults to the running host's architecture
|
||||
|
||||
import argparse
|
||||
import datetime
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
import pathlib
|
||||
import platform
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
import time
|
||||
|
||||
from attrs import define, Factory, field, asdict
|
||||
from botocore import args
|
||||
from jinja2 import Environment, FileSystemLoader, Template
|
||||
from typing import Callable, List, NoReturn, Optional, Tuple, IO, Union
|
||||
|
||||
from empanadas.common import Architecture, rldict, valid_type_variant
|
||||
from empanadas.builders import ImageBuild
|
||||
from empanadas.backends import ImageFactoryBackend, KiwiBackend
|
||||
from empanadas.common import _rootdir
|
||||
|
||||
parser = argparse.ArgumentParser(description="ISO Compose")
|
||||
|
||||
parser.add_argument('--version',
|
||||
type=str, help="Release Version (8.6, 9.1)", required=True)
|
||||
parser.add_argument('--version', type=str, help="Release Version (8.6, 9.1)", required=True)
|
||||
parser.add_argument('--rc', action='store_true', help="Release Candidate")
|
||||
parser.add_argument('--kickstartdir', action='store_true',
|
||||
help="Use the kickstart dir instead of the os dir")
|
||||
parser.add_argument('--kickstartdir', action='store_true', help="Use the kickstart dir instead of the os dir for repositories")
|
||||
parser.add_argument('--debug', action='store_true', help="debug?")
|
||||
parser.add_argument('--skip', type=str,
|
||||
help="what stage(s) to skip",
|
||||
required=False)
|
||||
parser.add_argument('--type', type=str,
|
||||
help="Image type (container, genclo, azure, aws, vagrant)",
|
||||
required=True)
|
||||
parser.add_argument('--type', type=str, help="Image type (container, genclo, azure, aws, vagrant)", required=True)
|
||||
parser.add_argument('--variant', type=str, help="", required=False)
|
||||
parser.add_argument('--release', type=str,
|
||||
help="Image release for builds with the same date stamp",
|
||||
required=False)
|
||||
parser.add_argument('--kube', action='store_true',
|
||||
help="output as a K8s job(s)",
|
||||
required=False)
|
||||
parser.add_argument('--timeout', type=str,
|
||||
help="change timeout for imagefactory build process",
|
||||
required=False, default='3600')
|
||||
parser.add_argument('--backend', type=str,
|
||||
help="which backend to use (kiwi|imagefactory)",
|
||||
required=False, default='kiwi')
|
||||
parser.add_argument('--release', type=str, help="Image release for subsequent builds with the same date stamp (rarely needed)", required=False)
|
||||
parser.add_argument('--kube', action='store_true', help="output as a K8s job(s)", required=False)
|
||||
parser.add_argument('--timeout', type=str, help="change timeout for imagefactory build process (default 3600)", required=False, default='3600')
|
||||
|
||||
|
||||
results = parser.parse_args()
|
||||
rlvars = rldict[results.version]
|
||||
major = rlvars["major"]
|
||||
|
||||
|
||||
debug = results.debug
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
@ -57,6 +52,405 @@ formatter = logging.Formatter(
|
||||
handler.setFormatter(formatter)
|
||||
log.addHandler(handler)
|
||||
|
||||
STORAGE_DIR = pathlib.Path("/var/lib/imagefactory/storage")
|
||||
KICKSTART_PATH = pathlib.Path(os.environ.get("KICKSTART_PATH", "/kickstarts"))
|
||||
BUILDTIME = datetime.datetime.utcnow()
|
||||
|
||||
|
||||
CMD_PARAM_T = List[Union[str, Callable[..., str]]]
|
||||
|
||||
@define(kw_only=True)
|
||||
class ImageBuild:
|
||||
architecture: Architecture = field()
|
||||
base_uuid: Optional[str] = field(default="")
|
||||
cli_args: argparse.Namespace = field()
|
||||
command_args: List[str] = field(factory=list)
|
||||
common_args: List[str] = field(factory=list)
|
||||
debug: bool = field(default=False)
|
||||
image_type: str = field()
|
||||
job_template: Optional[Template] = field(init=False)
|
||||
kickstart_arg: List[str] = field(factory=list)
|
||||
kickstart_path: pathlib.Path = field(init=False)
|
||||
metadata: pathlib.Path = field(init=False)
|
||||
out_type: str = field(init=False)
|
||||
outdir: pathlib.Path = field(init=False)
|
||||
outname: str = field(init=False)
|
||||
package_args: List[str] = field(factory=list)
|
||||
release: int = field(default=0)
|
||||
stage_commands: Optional[List[List[Union[str,Callable]]]] = field(init=False)
|
||||
target_uuid: Optional[str] = field(default="")
|
||||
tdl_path: pathlib.Path = field(init=False)
|
||||
template: Template = field()
|
||||
timeout: str = field(default='3600')
|
||||
type_variant: str = field(init=False)
|
||||
variant: Optional[str] = field()
|
||||
|
||||
def __attrs_post_init__(self):
|
||||
self.tdl_path = self.render_icicle_template()
|
||||
if not self.tdl_path:
|
||||
exit(2)
|
||||
self.type_variant = self.type_variant_name()
|
||||
self.outdir, self.outname = self.output_name()
|
||||
self.out_type = self.image_format()
|
||||
self.command_args = self._command_args()
|
||||
self.package_args = self._package_args()
|
||||
self.common_args = self._common_args()
|
||||
|
||||
self.metadata = pathlib.Path(self.outdir, ".imagefactory-metadata.json")
|
||||
|
||||
self.kickstart_path = pathlib.Path(f"{KICKSTART_PATH}/Rocky-{self.architecture.major}-{self.type_variant}.ks")
|
||||
|
||||
self.checkout_kickstarts()
|
||||
self.kickstart_arg = self.kickstart_imagefactory_args()
|
||||
|
||||
try:
|
||||
os.mkdir(self.outdir)
|
||||
except FileExistsError as e:
|
||||
log.info("Directory already exists for this release. If possible, previously executed steps may be skipped")
|
||||
except Exception as e:
|
||||
log.exception("Some other exception occured while creating the output directory", e)
|
||||
return 0
|
||||
|
||||
if os.path.exists(self.metadata):
|
||||
with open(self.metadata, "r") as f:
|
||||
try:
|
||||
o = json.load(f)
|
||||
self.base_uuid = o['base_uuid']
|
||||
self.target_uuid = o['target_uuid']
|
||||
except json.decoder.JSONDecodeError as e:
|
||||
log.exception("Couldn't decode metadata file", e)
|
||||
finally:
|
||||
f.flush()
|
||||
|
||||
# Yes, this is gross. I'll fix it later.
|
||||
if self.image_type in ["Container"]:
|
||||
self.stage_commands = [
|
||||
["tar", "-C", f"{self.outdir}", "--strip-components=1", "-x", "-f", lambda: f"{STORAGE_DIR}/{self.target_uuid}.body", "*/layer.tar"],
|
||||
["xz", f"{self.outdir}/layer.tar"]
|
||||
]
|
||||
if self.image_type in ["RPI"]:
|
||||
self.stage_commands = [
|
||||
["cp", lambda: f"{STORAGE_DIR}/{self.target_uuid}.body", f"{self.outdir}/{self.outname}.raw"],
|
||||
["xz", f"{self.outdir}/{self.outname}.raw"]
|
||||
]
|
||||
if self.image_type in ["GenericCloud", "OCP", "GenericArm"]:
|
||||
self.stage_commands = [
|
||||
["qemu-img", "convert", "-c", "-f", "raw", "-O", "qcow2", lambda: f"{STORAGE_DIR}/{self.target_uuid}.body", f"{self.outdir}/{self.outname}.qcow2"]
|
||||
]
|
||||
if self.image_type in ["EC2"]:
|
||||
self.stage_commands = [
|
||||
["qemu-img", "convert", "-f", "raw", "-O", "qcow2", lambda: f"{STORAGE_DIR}/{self.target_uuid}.body", f"{self.outdir}/{self.outname}.qcow2"]
|
||||
]
|
||||
if self.image_type in ["Azure"]:
|
||||
self.stage_commands = [
|
||||
["/prep-azure.sh", lambda: f"{STORAGE_DIR}/{self.target_uuid}.body", f"{STORAGE_DIR}"],
|
||||
["cp", lambda: f"{STORAGE_DIR}/{self.target_uuid}.vhd", f"{self.outdir}/{self.outname}.vhd"]
|
||||
]
|
||||
if self.image_type in ["Vagrant"]:
|
||||
_map = {
|
||||
"Vbox": {"format": "vmdk", "provider": "virtualbox"},
|
||||
"Libvirt": {"format": "qcow2", "provider": "libvirt", "virtual_size": 10},
|
||||
"VMware": {"format": "vmdk", "provider": "vmware_desktop"}
|
||||
}
|
||||
output = f"{_map[self.variant]['format']}" #type: ignore
|
||||
provider = f"{_map[self.variant]['provider']}" # type: ignore
|
||||
|
||||
# pop from the options map that will be passed to the vagrant metadata.json
|
||||
convert_options = _map[self.variant].pop('convertOptions') if 'convertOptions' in _map[self.variant].keys() else '' #type: ignore
|
||||
|
||||
|
||||
self.stage_commands = [
|
||||
["qemu-img", "convert", "-c", "-f", "raw", "-O", output, *convert_options, lambda: f"{STORAGE_DIR}/{self.target_uuid}.body", f"{self.outdir}/{self.outname}.{output}"],
|
||||
["tar", "-C", self.outdir, "-czf", f"/tmp/{self.outname}.box", '.'],
|
||||
["mv", f"/tmp/{self.outname}.box", self.outdir]
|
||||
]
|
||||
self.prepare_vagrant(_map[self.variant])
|
||||
|
||||
if self.stage_commands:
|
||||
self.stage_commands.append(["cp", "-v", lambda: f"{STORAGE_DIR}/{self.target_uuid}.meta", f"{self.outdir}/build.meta"])
|
||||
|
||||
|
||||
def prepare_vagrant(self, options):
|
||||
"""Setup the output directory for the Vagrant type variant, dropping templates as required"""
|
||||
file_loader = FileSystemLoader(f"{_rootdir}/templates")
|
||||
tmplenv = Environment(loader=file_loader)
|
||||
|
||||
templates = {}
|
||||
templates['Vagrantfile'] = tmplenv.get_template(f"vagrant/Vagrantfile.{self.variant}")
|
||||
templates['metadata.json'] = tmplenv.get_template('vagrant/metadata.tmpl.json')
|
||||
templates['info.json'] = tmplenv.get_template('vagrant/info.tmpl.json')
|
||||
|
||||
if self.variant == "VMware":
|
||||
templates[f"{self.outname}.vmx"] = tmplenv.get_template('vagrant/vmx.tmpl')
|
||||
|
||||
if self.variant == "Vbox":
|
||||
templates['box.ovf'] = tmplenv.get_template('vagrant/box.tmpl.ovf')
|
||||
|
||||
if self.variant == "Libvirt":
|
||||
# Libvirt vagrant driver expects the qcow2 file to be called box.img.
|
||||
qemu_command_index = [i for i, d in enumerate(self.stage_commands) if d[0] == "qemu-img"][0]
|
||||
self.stage_commands.insert(qemu_command_index+1, ["mv", f"{self.outdir}/{self.outname}.qcow2", f"{self.outdir}/box.img"])
|
||||
|
||||
for name, template in templates.items():
|
||||
self.render_template(f"{self.outdir}/{name}", template,
|
||||
name=self.outname,
|
||||
arch=self.architecture.name,
|
||||
options=options
|
||||
)
|
||||
|
||||
def checkout_kickstarts(self) -> int:
|
||||
cmd = ["git", "clone", "--branch", f"r{self.architecture.major}", rlvars['livemap']['git_repo'], f"{KICKSTART_PATH}"]
|
||||
ret, out, err, _ = self.runCmd(cmd, search=False)
|
||||
log.debug(out)
|
||||
log.debug(err)
|
||||
if ret > 0:
|
||||
ret = self.pull_kickstarts()
|
||||
return ret
|
||||
|
||||
def pull_kickstarts(self) -> int:
|
||||
cmd: CMD_PARAM_T = ["git", "-C", f"{KICKSTART_PATH}", "reset", "--hard", "HEAD"]
|
||||
ret, out, err, _ = self.runCmd(cmd, search=False)
|
||||
log.debug(out)
|
||||
log.debug(err)
|
||||
if ret == 0:
|
||||
cmd = ["git", "-C", f"{KICKSTART_PATH}", "pull"]
|
||||
ret, out, err, _ = self.runCmd(cmd, search=False)
|
||||
log.debug(out)
|
||||
log.debug(err)
|
||||
return ret
|
||||
|
||||
|
||||
def output_name(self) -> Tuple[pathlib.Path, str]:
|
||||
directory = f"Rocky-{self.architecture.major}-{self.type_variant}-{self.architecture.version}-{BUILDTIME.strftime('%Y%m%d')}.{self.release}"
|
||||
name = f"{directory}.{self.architecture.name}"
|
||||
outdir = pathlib.Path(f"/tmp/", directory)
|
||||
return outdir, name
|
||||
|
||||
def type_variant_name(self):
|
||||
return self.image_type if not self.variant else f"{self.image_type}-{self.variant}"
|
||||
|
||||
def _command_args(self):
|
||||
args_mapping = {
|
||||
"debug": "--debug",
|
||||
}
|
||||
return [param for name, param in args_mapping.items() if getattr(self.cli_args, name)]
|
||||
|
||||
def _package_args(self) -> List[str]:
|
||||
if self.image_type in ["Container"]:
|
||||
return ["--parameter", "compress", "xz"]
|
||||
return [""]
|
||||
|
||||
def _common_args(self) -> List[str]:
|
||||
args = []
|
||||
if self.image_type in ["Container"]:
|
||||
args = ["--parameter", "offline_icicle", "true"]
|
||||
if self.image_type in ["GenericCloud", "EC2", "Vagrant", "Azure", "OCP", "RPI", "GenericArm"]:
|
||||
args = ["--parameter", "generate_icicle", "false"]
|
||||
return args
|
||||
|
||||
def image_format(self) -> str:
|
||||
mapping = {
|
||||
"Container": "docker"
|
||||
}
|
||||
return mapping[self.image_type] if self.image_type in mapping.keys() else ''
|
||||
|
||||
def kickstart_imagefactory_args(self) -> List[str]:
|
||||
|
||||
if not self.kickstart_path.is_file():
|
||||
log.warning(f"Kickstart file is not available: {self.kickstart_path}")
|
||||
if not debug:
|
||||
log.warning("Exiting because debug mode is not enabled.")
|
||||
exit(2)
|
||||
|
||||
return ["--file-parameter", "install_script", str(self.kickstart_path)]
|
||||
|
||||
def render_template(self, path, template, **kwargs) -> pathlib.Path:
|
||||
with open(path, "wb") as f:
|
||||
_template = template.render(**kwargs)
|
||||
f.write(_template.encode())
|
||||
f.flush()
|
||||
output = pathlib.Path(path)
|
||||
if not output.exists():
|
||||
log.error("Failed to write template")
|
||||
raise Exception("Failed to template")
|
||||
return output
|
||||
|
||||
def render_icicle_template(self) -> pathlib.Path:
|
||||
output = tempfile.NamedTemporaryFile(delete=False).name
|
||||
return self.render_template(output, self.template,
|
||||
architecture=self.architecture.name,
|
||||
iso8601date=BUILDTIME.strftime("%Y%m%d"),
|
||||
installdir="kickstart" if self.cli_args.kickstartdir else "os",
|
||||
major=self.architecture.major,
|
||||
minor=self.architecture.minor,
|
||||
release=self.release,
|
||||
size="10G",
|
||||
type=self.image_type,
|
||||
utcnow=BUILDTIME,
|
||||
version_variant=self.architecture.version if not self.variant else f"{self.architecture.version}-{self.variant}",
|
||||
)
|
||||
|
||||
def build_command(self) -> List[str]:
|
||||
build_command = ["imagefactory", "--timeout", self.timeout, *self.command_args, "base_image", *self.common_args, *self.kickstart_arg, self.tdl_path]
|
||||
return build_command
|
||||
def package_command(self) -> List[str]:
|
||||
package_command = ["imagefactory", *self.command_args, "target_image", self.out_type, *self.common_args,
|
||||
"--id", f"{self.base_uuid}",
|
||||
*self.package_args,
|
||||
"--parameter", "repository", self.outname,
|
||||
]
|
||||
return package_command
|
||||
|
||||
def copy_command(self) -> List[str]:
|
||||
|
||||
copy_command = ["aws", "s3", "cp", "--recursive", f"{self.outdir}/",
|
||||
f"s3://resf-empanadas/buildimage-{self.architecture.version}-{self.architecture.name}/{ self.outname }/{ BUILDTIME.strftime('%s') }/"
|
||||
]
|
||||
|
||||
return copy_command
|
||||
|
||||
def build(self) -> int:
|
||||
if self.base_uuid:
|
||||
return 0
|
||||
|
||||
self.fix_ks()
|
||||
|
||||
ret, out, err, uuid = self.runCmd(self.build_command())
|
||||
if uuid:
|
||||
self.base_uuid = uuid.rstrip()
|
||||
self.save()
|
||||
return ret
|
||||
|
||||
def package(self) -> int:
|
||||
# Some build types don't need to be packaged by imagefactory
|
||||
# @TODO remove business logic if possible
|
||||
if self.image_type in ["GenericCloud", "EC2", "Azure", "Vagrant", "OCP", "RPI", "GenericArm"]:
|
||||
self.target_uuid = self.base_uuid if hasattr(self, 'base_uuid') else ""
|
||||
|
||||
if self.target_uuid:
|
||||
return 0
|
||||
|
||||
ret, out, err, uuid = self.runCmd(self.package_command())
|
||||
if uuid:
|
||||
self.target_uuid = uuid.rstrip()
|
||||
self.save()
|
||||
return ret
|
||||
|
||||
def stage(self) -> int:
|
||||
""" Stage the artifacst from wherever they are (unpacking and converting if needed)"""
|
||||
if not hasattr(self,'stage_commands'):
|
||||
return 0
|
||||
|
||||
returns = []
|
||||
for command in self.stage_commands: #type: ignore
|
||||
ret, out, err, _ = self.runCmd(command, search=False)
|
||||
returns.append(ret)
|
||||
|
||||
return all(ret > 0 for ret in returns)
|
||||
|
||||
def copy(self, skip=False) -> int:
|
||||
# move or unpack if necessary
|
||||
log.info("Executing staging commands")
|
||||
if (stage := self.stage() > 0):
|
||||
raise Exception(stage)
|
||||
|
||||
if not skip:
|
||||
log.info("Copying files to output directory")
|
||||
ret, out, err, _ = self.runCmd(self.copy_command(), search=False)
|
||||
return ret
|
||||
|
||||
log.info(f"Build complete! Output available in {self.outdir}/")
|
||||
return 0
|
||||
|
||||
def runCmd(self, command: CMD_PARAM_T, search: bool = True) -> Tuple[int, Union[bytes,None], Union[bytes,None], Union[str,None]]:
|
||||
prepared, _ = self.prepare_command(command)
|
||||
log.info(f"Running command: {' '.join(prepared)}")
|
||||
|
||||
kwargs = {
|
||||
"stderr": subprocess.PIPE,
|
||||
"stdout": subprocess.PIPE
|
||||
}
|
||||
if debug: del kwargs["stderr"]
|
||||
|
||||
with subprocess.Popen(prepared, **kwargs) as p:
|
||||
uuid = None
|
||||
# @TODO implement this as a callback?
|
||||
if search:
|
||||
for _, line in enumerate(p.stdout): # type: ignore
|
||||
ln = line.decode()
|
||||
if ln.startswith("UUID: "):
|
||||
uuid = ln.split(" ")[-1]
|
||||
log.debug(f"found uuid: {uuid}")
|
||||
|
||||
out, err = p.communicate()
|
||||
res = p.wait(), out, err, uuid
|
||||
|
||||
if res[0] > 0:
|
||||
log.error(f"Problem while executing command: '{prepared}'")
|
||||
if search and not res[3]:
|
||||
log.error("UUID not found in stdout. Dumping stdout and stderr")
|
||||
self.log_subprocess(res)
|
||||
|
||||
return res
|
||||
|
||||
def prepare_command(self, command_list: CMD_PARAM_T) -> Tuple[List[str],List[None]]:
|
||||
"""
|
||||
Commands may be a callable, which should be a lambda to be evaluated at
|
||||
preparation time with available locals. This can be used to, among
|
||||
other things, perform lazy evaluations of f-strings which have values
|
||||
not available at assignment time. e.g., filling in a second command
|
||||
with a value extracted from the previous step or command.
|
||||
|
||||
"""
|
||||
|
||||
r = []
|
||||
return r, [r.append(c()) if (callable(c) and c.__name__ == '<lambda>') else r.append(str(c)) for c in command_list]
|
||||
|
||||
def log_subprocess(self, result: Tuple[int, Union[bytes, None], Union[bytes, None], Union[str, None]]):
|
||||
def log_lines(title, lines):
|
||||
log.info(f"====={title}=====")
|
||||
log.info(lines.decode())
|
||||
log.info(f"Command return code: {result[0]}")
|
||||
stdout = result[1]
|
||||
stderr = result[2]
|
||||
if stdout:
|
||||
log_lines("Command STDOUT", stdout)
|
||||
if stderr:
|
||||
log_lines("Command STDERR", stderr)
|
||||
|
||||
def fix_ks(self):
|
||||
cmd: CMD_PARAM_T = ["sed", "-i", f"s,$basearch,{self.architecture.name},", str(self.kickstart_path)]
|
||||
self.runCmd(cmd, search=False)
|
||||
|
||||
def render_kubernetes_job(self):
|
||||
commands = [self.build_command(), self.package_command(), self.copy_command()]
|
||||
if not self.job_template:
|
||||
return None
|
||||
template = self.job_template.render(
|
||||
architecture=self.architecture.name,
|
||||
backoffLimit=4,
|
||||
buildTime=BUILDTIME.strftime("%s"),
|
||||
command=commands,
|
||||
imageName="ghcr.io/rockylinux/sig-core-toolkit:latest",
|
||||
jobname="buildimage",
|
||||
namespace="empanadas",
|
||||
major=major,
|
||||
restartPolicy="Never",
|
||||
)
|
||||
return template
|
||||
|
||||
def save(self):
|
||||
with open(self.metadata, "w") as f:
|
||||
try:
|
||||
o = { name: getattr(self, name) for name in ["base_uuid", "target_uuid"] }
|
||||
log.debug(o)
|
||||
json.dump(o, f)
|
||||
except AttributeError as e:
|
||||
log.error("Couldn't find attribute in object. Something is probably wrong", e)
|
||||
except Exception as e:
|
||||
log.exception(e)
|
||||
finally:
|
||||
f.flush()
|
||||
|
||||
def run():
|
||||
try:
|
||||
@ -65,51 +459,28 @@ def run():
|
||||
log.exception(e)
|
||||
exit(2)
|
||||
|
||||
file_loader = FileSystemLoader(f"{_rootdir}/templates")
|
||||
tmplenv = Environment(loader=file_loader)
|
||||
tdl_template = tmplenv.get_template('icicle/tdl.xml.tmpl')
|
||||
|
||||
arches = rlvars['allowed_arches'] if results.kube else [platform.uname().machine]
|
||||
|
||||
for architecture in arches:
|
||||
if results.backend == "kiwi":
|
||||
backend = KiwiBackend()
|
||||
else:
|
||||
backend = ImageFactoryBackend(
|
||||
kickstart_dir="kickstart" if results.kickstartdir else "os",
|
||||
kickstart_repo=rlvars['livemap']['git_repo']
|
||||
)
|
||||
IB = ImageBuild(
|
||||
architecture=Architecture.from_version(architecture, rlvars['revision']),
|
||||
cli_args=results,
|
||||
debug=results.debug,
|
||||
image_type=results.type,
|
||||
release=results.release if results.release else 0,
|
||||
template=tdl_template,
|
||||
variant=results.variant,
|
||||
build_time=datetime.datetime.utcnow(),
|
||||
backend=backend,
|
||||
log=log,
|
||||
)
|
||||
|
||||
if results.kube:
|
||||
# commands = IB.kube_commands()
|
||||
IB.job_template = tmplenv.get_template('kube/Job.tmpl')
|
||||
#commands = IB.kube_commands()
|
||||
print(IB.render_kubernetes_job())
|
||||
sys.exit(0)
|
||||
|
||||
skip_stages = results.skip.split(',') if results.skip else []
|
||||
stages = ["prepare", "build", "clean", "stage"]
|
||||
for i, stage in enumerate(stages):
|
||||
skip_stage = stage in skip_stages
|
||||
|
||||
log.info(f"Stage {i} - {stage}{' SKIP' if skip_stage else ''}")
|
||||
|
||||
if skip_stage:
|
||||
continue
|
||||
|
||||
method = getattr(IB.backend, stage)
|
||||
if callable(method):
|
||||
method()
|
||||
else:
|
||||
log.fatal(f"Unable to execute {stage}")
|
||||
ret = IB.build()
|
||||
ret = IB.package()
|
||||
ret = IB.copy()
|
||||
|
||||
if 'upload' in skip_stages:
|
||||
return
|
||||
|
||||
log.info("Final stage - Upload")
|
||||
|
||||
IB.upload(skip='upload' in skip_stages)
|
||||
|
@ -2,7 +2,8 @@
|
||||
|
||||
import argparse
|
||||
|
||||
from empanadas.common import config, rldict
|
||||
from empanadas.common import *
|
||||
from empanadas.util import Checks
|
||||
from empanadas.util import IsoBuild
|
||||
|
||||
parser = argparse.ArgumentParser(description="ISO Compose")
|
||||
@ -28,6 +29,5 @@ a = IsoBuild(
|
||||
logger=results.logger,
|
||||
)
|
||||
|
||||
|
||||
def run():
|
||||
a.run()
|
||||
|
@ -9,7 +9,6 @@ from empanadas.util import IsoBuild
|
||||
parser = argparse.ArgumentParser(description="ISO Compose")
|
||||
|
||||
parser.add_argument('--release', type=str, help="Major Release Version or major-type (eg 9-beta)", required=True)
|
||||
parser.add_argument('--rc', action='store_true', help="Release Candidate, Beta, RLN")
|
||||
parser.add_argument('--arch', type=str, help="Architecture")
|
||||
parser.add_argument('--isolation', type=str, help="Mock Isolation")
|
||||
parser.add_argument('--local-compose', action='store_true', help="Compose Directory is Here")
|
||||
@ -27,7 +26,6 @@ a = IsoBuild(
|
||||
rlvars,
|
||||
config,
|
||||
major=major,
|
||||
rc=results.rc,
|
||||
arch=results.arch,
|
||||
isolation=results.isolation,
|
||||
extra_iso=results.extra_iso,
|
||||
|
@ -11,6 +11,7 @@ parser = argparse.ArgumentParser(description="Peridot Sync and Compose")
|
||||
|
||||
# All of our options
|
||||
parser.add_argument('--release', type=str, help="Major Release Version or major-type (eg 9-beta)", required=True)
|
||||
parser.add_argument('--overwrite', action='store_true', help="Overwrites current treeinfo and discinfo files")
|
||||
parser.add_argument('--logger', type=str)
|
||||
|
||||
# Parse them
|
||||
@ -26,6 +27,7 @@ a = RepoSync(
|
||||
rlvars,
|
||||
config,
|
||||
major=major,
|
||||
refresh_treeinfo=results.overwrite,
|
||||
logger=results.logger,
|
||||
)
|
||||
|
||||
|
@ -33,7 +33,6 @@ parser.add_argument('--logger', type=str)
|
||||
parser.add_argument('--disable-gpg-check', action='store_false')
|
||||
parser.add_argument('--disable-repo-gpg-check', action='store_false')
|
||||
parser.add_argument('--clean-old-packages', action='store_true')
|
||||
parser.add_argument('--use-staging', action='store_true')
|
||||
|
||||
# Parse them
|
||||
results = parser.parse_args()
|
||||
@ -65,7 +64,6 @@ a = RepoSync(
|
||||
gpg_check=results.disable_gpg_check,
|
||||
repo_gpg_check=results.disable_repo_gpg_check,
|
||||
reposync_clean_old=results.clean_old_packages,
|
||||
use_staging=results.use_staging,
|
||||
)
|
||||
|
||||
def run():
|
||||
|
@ -46,4 +46,29 @@ core:
|
||||
branch: 'r9'
|
||||
list:
|
||||
- 'SOURCES/RPM-GPG-KEY-Rocky-SIG-Core'
|
||||
'10':
|
||||
profile: 'core'
|
||||
project_id: '391b67bf-423d-4192-9a31-749a126ebfca'
|
||||
addtional_dirs: []
|
||||
gpg_key:
|
||||
- 'https://git.rockylinux.org/staging/src/rocky-release-core/-/raw/r10/SOURCES/RPM-GPG-KEY-Rocky-SIG-Core'
|
||||
repo:
|
||||
core-common:
|
||||
allowed_arches:
|
||||
- x86_64
|
||||
- aarch64
|
||||
- ppc64le
|
||||
- s390x
|
||||
core-infra:
|
||||
allowed_arches:
|
||||
- x86_64
|
||||
- aarch64
|
||||
- ppc64le
|
||||
- s390x
|
||||
extra_files:
|
||||
git_repo: 'https://git.rockylinux.org/staging/src/rocky-release-core.git'
|
||||
git_raw_path: 'https://git.rockylinux.org/staging/src/rocky-release-core/-/raw/r10/'
|
||||
branch: 'r10'
|
||||
list:
|
||||
- 'SOURCES/RPM-GPG-KEY-Rocky-SIG-Core'
|
||||
...
|
||||
|
@ -22,7 +22,7 @@ lorax --product="${PRODUCT}" \
|
||||
--isfinal \
|
||||
{%- endif %}
|
||||
{%- for repo in repos %}
|
||||
--source='{{ repo.url }}' \
|
||||
--source={{ repo.url }} \
|
||||
{%- endfor %}
|
||||
{%- if squashfs_only %}
|
||||
--squashfs-only \
|
||||
|
@ -1,162 +0,0 @@
|
||||
# KIWI - Build configuration file
|
||||
#
|
||||
# Below all configuration parameters available to control
|
||||
# KIWI's build process are listed as comments. The values
|
||||
# used here provides the default values applied by KIWI if
|
||||
# no other information is specified.
|
||||
#
|
||||
# To make any of the below effective, please uncomment the
|
||||
# respective section(s) and adapt the parameters according
|
||||
# to your needs
|
||||
#
|
||||
|
||||
# Setup access to security keys
|
||||
#credentials:
|
||||
# # Specify private key(s) used for signing operations
|
||||
# - verification_metadata_signing_key_file: /path/to/private.pem
|
||||
|
||||
# Setup access options for the Open BuildService
|
||||
#obs:
|
||||
# # Specify the URL of the Open BuildService download server
|
||||
# - download_url: http://download.opensuse.org/repositories
|
||||
# # Specify if the BuildService download server is public or private.
|
||||
# # This information is used to verify if the request to populate
|
||||
# # the repositories via the imageinclude attribute is possible
|
||||
# - public: true
|
||||
|
||||
|
||||
# Setup behaviour of the kiwi result bundle command
|
||||
#bundle:
|
||||
# # Specify if the bundle tarball should contain compressed results.
|
||||
# # Note: Already compressed result information will not be touched.
|
||||
# # Build results that generate an encrypted filesystem, i.e.
|
||||
# # luks setup, will not be compressed. The intention for result compression
|
||||
# # is to produce a smaller representation of the original. Encrypted data
|
||||
# # generally grows when an attempt is made to compress the data. This is
|
||||
# # due to the nature of compression algorithms. Therefore this setting is
|
||||
# # ignored when encryption is enabled.
|
||||
# - compress: false
|
||||
# # Specify if the image build result and bundle should contain
|
||||
# # a .changes file. The .changes file contains the package changelog
|
||||
# # information from all packages installed into the image.
|
||||
# - has_package_changes: false
|
||||
|
||||
|
||||
# Setup behaviour of XZ compressor
|
||||
#xz:
|
||||
# # Specify options used in any xz compression call
|
||||
# - options: '--threads=0'
|
||||
|
||||
|
||||
# Setup process parameters for container image creation
|
||||
#container:
|
||||
# # Specify compression for container images
|
||||
# # Possible values are true, false, xz or none.
|
||||
# - compress: true
|
||||
|
||||
|
||||
# Setup process parameters for ISO image creation
|
||||
#iso:
|
||||
# # Specify tool category which should be used to build iso images
|
||||
# # Possible values are: xorriso
|
||||
# - tool_category: xorriso
|
||||
|
||||
|
||||
# Setup process parameters for OCI toolchain
|
||||
#oci:
|
||||
# # Specify OCI archive tool which should be used on creation of
|
||||
# # container archives for OCI compliant images, e.g docker
|
||||
# # Possible values are umoci and buildah
|
||||
# - archive_tool: buildah
|
||||
|
||||
|
||||
# Specify build constraints that applies during the image build
|
||||
# process. If one or more constraints are violated the build exits
|
||||
# with an appropriate error message.
|
||||
#build_constraints:
|
||||
# # Maximum result image size. The value can be specified in
|
||||
# # bytes or it can be specified with m=MB or g=GB. The constraint
|
||||
# # is checked prior to the result bundle creation
|
||||
# - max_size: 700m
|
||||
|
||||
# Setup process parameters for partition mapping
|
||||
mapper:
|
||||
# # Specify tool to use for creating partition maps
|
||||
# # Possible values are: kpartx and partx
|
||||
- part_mapper: {{ "partx" if architecture in ["s390x"] else "kpartx" }}
|
||||
|
||||
# Setup process parameters to handle runtime checks
|
||||
#runtime_checks:
|
||||
# # Specify list of runtime checks to disable
|
||||
# - disable:
|
||||
# # verify that the host has the required container tools installed
|
||||
# - check_container_tool_chain_installed
|
||||
|
||||
# # verify that there are repositories configured
|
||||
# - check_repositories_configured
|
||||
|
||||
# # verify that the URL for imageinclude repos is accessable
|
||||
# - check_image_include_repos_publicly_resolvable
|
||||
|
||||
# # verify secure boot setup disabled for overlay configured disk images
|
||||
# - check_efi_mode_for_disk_overlay_correctly_setup
|
||||
|
||||
# # verify for legacy kiwi boot images that they exist on the host
|
||||
# - check_boot_description_exists
|
||||
|
||||
# # verify if kiwi initrd_system was set if a boot attribute exists
|
||||
# - check_initrd_selection_required
|
||||
|
||||
# # verify for legacy kiwi boot images that the same kernel is used
|
||||
# - check_consistent_kernel_in_boot_and_system_image
|
||||
|
||||
# # check for reserved label names used in LVM setup
|
||||
# - check_volume_setup_defines_reserved_labels
|
||||
|
||||
# # verify only one full size volume is specified for LVM images
|
||||
# - check_volume_setup_defines_multiple_fullsize_volumes
|
||||
|
||||
# # verify no / volume setup is setup but the @root volume is used
|
||||
# - check_volume_setup_has_no_root_definition
|
||||
|
||||
# # verify if volume label is really used with a volume setup
|
||||
# - check_volume_label_used_with_lvm
|
||||
|
||||
# # verify that there is a xen domain setup for xen images
|
||||
# - check_xen_uniquely_setup_as_server_or_guest
|
||||
|
||||
# # verify mediacheck is installed for ISO images that requests it
|
||||
# - check_mediacheck_installed
|
||||
|
||||
# # verify dracut-kiwi-live is installed for ISO images
|
||||
# - check_dracut_module_for_live_iso_in_package_list
|
||||
|
||||
# # verify dracut-kiwi-overlay is installed for overlay disk images
|
||||
# - check_dracut_module_for_disk_overlay_in_package_list
|
||||
|
||||
# # verify dracut-kiwi-repart is installed for OEM disk images
|
||||
# - check_dracut_module_for_disk_oem_in_package_list
|
||||
|
||||
# # verify dracut-kiwi-oem-dump is installed for OEM install images
|
||||
# - check_dracut_module_for_oem_install_in_package_list
|
||||
|
||||
# # verify configured firmware is compatible with host architecture
|
||||
# - check_architecture_supports_iso_firmware_setup
|
||||
|
||||
# # verify WSL naming conventions
|
||||
# - check_appx_naming_conventions_valid
|
||||
|
||||
# # check kiwi dracut modules compatible with kiwi builder
|
||||
# - check_dracut_module_versions_compatible_to_kiwi
|
||||
|
||||
# # check for unresolved include statements in the XML description
|
||||
# - check_include_references_unresolvable
|
||||
|
||||
# # validate options passed to cryptsetup via luksformat element
|
||||
# - check_luksformat_options_valid
|
||||
|
||||
# # check devicepersistency compatible with partition table type
|
||||
# - check_partuuid_persistency_type_used_with_mbr
|
||||
|
||||
# # check efifatimagesize does not exceed the max El Torito load size
|
||||
# - check_efi_fat_image_has_correct_size
|
3432
iso/empanadas/empanadas/templates/minimal/9/aarch64.xml
Normal file
3432
iso/empanadas/empanadas/templates/minimal/9/aarch64.xml
Normal file
File diff suppressed because it is too large
Load Diff
3443
iso/empanadas/empanadas/templates/minimal/9/ppc64le.xml
Normal file
3443
iso/empanadas/empanadas/templates/minimal/9/ppc64le.xml
Normal file
File diff suppressed because it is too large
Load Diff
3388
iso/empanadas/empanadas/templates/minimal/9/s390x.xml
Normal file
3388
iso/empanadas/empanadas/templates/minimal/9/s390x.xml
Normal file
File diff suppressed because it is too large
Load Diff
3465
iso/empanadas/empanadas/templates/minimal/9/x86_64.xml
Normal file
3465
iso/empanadas/empanadas/templates/minimal/9/x86_64.xml
Normal file
File diff suppressed because it is too large
Load Diff
@ -62,9 +62,7 @@ class RepoSync:
|
||||
fpsync: bool = False,
|
||||
logger=None,
|
||||
log_level='INFO',
|
||||
use_staging: bool = False,
|
||||
):
|
||||
|
||||
self.nofail = nofail
|
||||
self.dryrun = dryrun
|
||||
self.fullrun = fullrun
|
||||
@ -82,14 +80,11 @@ class RepoSync:
|
||||
# This makes it so every repo is synced at the same time.
|
||||
# This is EXTREMELY dangerous.
|
||||
self.just_pull_everything = just_pull_everything
|
||||
# Use staging url instead of pulling from peridot (or, for EL8)
|
||||
self.use_staging = use_staging
|
||||
# Relevant config items
|
||||
self.major_version = major
|
||||
self.date_stamp = config['date_stamp']
|
||||
self.timestamp = time.time()
|
||||
self.repo_base_url = config['repo_base_url']
|
||||
self.staging_base_url = config['staging_base_url']
|
||||
self.compose_root = config['compose_root']
|
||||
self.compose_base = config['compose_root'] + "/" + major
|
||||
self.profile = rlvars['profile']
|
||||
@ -107,7 +102,6 @@ class RepoSync:
|
||||
self.project_id = rlvars['project_id']
|
||||
self.repo_renames = rlvars['renames']
|
||||
self.repos = rlvars['all_repos']
|
||||
self.extra_repos = rlvars['extra_repos']
|
||||
self.multilib = rlvars['provide_multilib']
|
||||
self.repo = repo
|
||||
self.extra_files = rlvars['extra_files']
|
||||
@ -276,9 +270,7 @@ class RepoSync:
|
||||
self.gpg_check,
|
||||
self.repo_gpg_check,
|
||||
self.tmplenv,
|
||||
self.log,
|
||||
staging_base_url=self.staging_base_url,
|
||||
use_staging=self.use_staging,
|
||||
self.log
|
||||
)
|
||||
|
||||
if self.dryrun:
|
||||
@ -669,10 +661,7 @@ class RepoSync:
|
||||
repoclosure_entry_name_list = []
|
||||
self.log.info('Setting up repoclosure for {}'.format(repo))
|
||||
|
||||
arches_for_repoclosure = self.arches
|
||||
if self.arch:
|
||||
arches_for_repoclosure = self.arch.split(',')
|
||||
for arch in arches_for_repoclosure:
|
||||
for arch in self.repoclosure_map['arches']:
|
||||
repo_combination = []
|
||||
repoclosure_entry_name = f'repoclosure-{repo}-{arch}'
|
||||
repoclosure_entry_name_list.append(repoclosure_entry_name)
|
||||
@ -1254,7 +1243,7 @@ class RepoSync:
|
||||
It is rare that this should be called.
|
||||
"""
|
||||
sync_root = self.compose_latest_sync
|
||||
self.deploy_treeinfo(self.repo, sync_root, self.arch, refresh=True)
|
||||
self.deploy_treeinfo(self.repo, sync_root, self.arch, refresh=self.refresh_treeinfo)
|
||||
self.tweak_treeinfo(self.repo, sync_root, self.arch)
|
||||
|
||||
def run_compose_closeout(self):
|
||||
@ -1457,8 +1446,7 @@ class RepoSync:
|
||||
self.gpg_check,
|
||||
self.repo_gpg_check,
|
||||
self.tmplenv,
|
||||
self.log,
|
||||
staging_base_url=self.staging_base_url,
|
||||
self.log
|
||||
)
|
||||
|
||||
|
||||
|
@ -110,7 +110,6 @@ class IsoBuild:
|
||||
self.revision = rlvars['revision']
|
||||
self.rclvl = rlvars['rclvl']
|
||||
self.repos = rlvars['iso_map']['lorax']['repos']
|
||||
self.extra_repos = rlvars['extra_repos']
|
||||
self.repo_base_url = config['repo_base_url']
|
||||
self.project_id = rlvars['project_id']
|
||||
self.structure = rlvars['structure']
|
||||
@ -203,8 +202,7 @@ class IsoBuild:
|
||||
self.current_arch,
|
||||
self.compose_latest_sync,
|
||||
self.compose_dir_is_here,
|
||||
self.hashed,
|
||||
self.extra_repos
|
||||
self.hashed
|
||||
)
|
||||
self.log.info(self.revision_level)
|
||||
|
||||
@ -270,8 +268,6 @@ class IsoBuild:
|
||||
dist=self.disttag,
|
||||
repos=self.repolist,
|
||||
user_agent='{{ user_agent }}',
|
||||
compose_dir_is_here=self.compose_dir_is_here,
|
||||
compose_dir=self.compose_root,
|
||||
)
|
||||
|
||||
mock_sh_template_output = mock_sh_template.render(
|
||||
@ -775,6 +771,7 @@ class IsoBuild:
|
||||
if not os.path.exists(lorax_path):
|
||||
self.log.error(Color.FAIL + 'Lorax not found at all. This is considered fatal.')
|
||||
|
||||
# do we need to do a hard exit here?
|
||||
raise SystemExit()
|
||||
|
||||
grafts = self._generate_graft_points(
|
||||
@ -783,7 +780,12 @@ class IsoBuild:
|
||||
self.iso_map['images'][y]['repos'],
|
||||
reposcan=reposcan
|
||||
)
|
||||
self._extra_iso_local_config(a, y, grafts, work_root, volname)
|
||||
try:
|
||||
self._extra_iso_local_config(a, y, grafts, work_root)
|
||||
except ValueError as exc:
|
||||
self.log.error(Color.FAIL + f'An error occured while configuring extra ISO build {exc}')
|
||||
self.log.error(Color.FAIL + f'Error: {exc}')
|
||||
continue
|
||||
|
||||
if self.extra_iso_mode == 'local':
|
||||
self._extra_iso_local_run(a, y, work_root)
|
||||
@ -803,7 +805,7 @@ class IsoBuild:
|
||||
|
||||
self._extra_iso_podman_run(arches_to_build, images_to_build_podman, work_root)
|
||||
|
||||
def _extra_iso_local_config(self, arch, image, grafts, work_root, volname):
|
||||
def _extra_iso_local_config(self, arch, image, grafts, work_root):
|
||||
"""
|
||||
Local ISO build configuration - This generates the configuration for
|
||||
both mock and podman entries
|
||||
@ -823,7 +825,6 @@ class IsoBuild:
|
||||
iso_template_path = f'{entries_dir}/buildExtraImage-{arch}-{image}.sh'
|
||||
xorriso_template_path = f'{entries_dir}/xorriso-{arch}-{image}.txt'
|
||||
iso_readme_path = f'{self.iso_work_dir}/{arch}/README'
|
||||
#print(iso_readme_path)
|
||||
|
||||
log_root = os.path.join(
|
||||
work_root,
|
||||
@ -844,12 +845,15 @@ class IsoBuild:
|
||||
if self.updated_image:
|
||||
datestamp = '-' + self.updated_image_date
|
||||
|
||||
volid = f'{self.shortname}-{self.major_version}-{self.minor_version}{rclevel}-{arch}-{volname}'
|
||||
volid = Idents.get_vol_id(boot_iso)
|
||||
isoname = f'{self.shortname}-{self.release}{rclevel}{datestamp}-{arch}-{image}.iso'
|
||||
generic_isoname = f'{self.shortname}-{arch}-{image}.iso'
|
||||
latest_isoname = f'{self.shortname}-{self.major_version}-latest-{arch}-{image}.iso'
|
||||
required_pkgs = self.iso_map['lorax']['required_pkgs']
|
||||
|
||||
if not volid:
|
||||
raise ValueError('Volume ID could not be determined')
|
||||
|
||||
lorax_pkg_cmd = '/usr/bin/dnf install {} -y {}'.format(
|
||||
' '.join(required_pkgs),
|
||||
log_path_command
|
||||
@ -858,7 +862,6 @@ class IsoBuild:
|
||||
mock_iso_template_output = mock_iso_template.render(
|
||||
arch=self.current_arch,
|
||||
major=self.major_version,
|
||||
releasever=self.release,
|
||||
fullname=self.fullname,
|
||||
shortname=self.shortname,
|
||||
required_pkgs=required_pkgs,
|
||||
@ -872,7 +875,6 @@ class IsoBuild:
|
||||
mock_sh_template_output = mock_sh_template.render(
|
||||
arch=self.current_arch,
|
||||
major=self.major_version,
|
||||
releasever=self.release,
|
||||
isolation=self.mock_isolation,
|
||||
builddir=self.mock_work_root,
|
||||
shortname=self.shortname,
|
||||
@ -889,6 +891,8 @@ class IsoBuild:
|
||||
'iso_level': self.iso_map['iso_level'],
|
||||
}
|
||||
|
||||
self.log.info(Color.INFO + f'boot.iso volume name: {volid}')
|
||||
|
||||
# Generate a xorriso compatible dialog
|
||||
with open(grafts) as xp:
|
||||
xorpoint = xp.read()
|
||||
|
@ -13,6 +13,8 @@ import boto3
|
||||
import xmltodict
|
||||
import productmd.treeinfo
|
||||
import productmd.composeinfo
|
||||
import pycdlib
|
||||
import magic
|
||||
import empanadas
|
||||
import kobo.shortcuts
|
||||
from empanadas.common import Color
|
||||
@ -24,8 +26,13 @@ class ArchCheck:
|
||||
archfile = {
|
||||
'x86_64': [
|
||||
'isolinux/vmlinuz',
|
||||
'images/efiboot.img',
|
||||
'images/eltorito.img',
|
||||
'images/grub.conf',
|
||||
'EFI/BOOT/BOOTX64.EFI'
|
||||
'images/install.img',
|
||||
'boot/grub2/grub.cfg',
|
||||
'EFI/BOOT/BOOTX64.EFI',
|
||||
'EFI/BOOT/grub.cfg'
|
||||
],
|
||||
'aarch64': [
|
||||
'EFI/BOOT/BOOTAA64.EFI'
|
||||
@ -446,9 +453,7 @@ class Shared:
|
||||
repo_gpg_check,
|
||||
templates,
|
||||
logger,
|
||||
dest_path='/var/tmp',
|
||||
staging_base_url='https://dl.rockylinux.org/stg',
|
||||
use_staging=False,
|
||||
dest_path='/var/tmp'
|
||||
) -> str:
|
||||
"""
|
||||
Generates the necessary repo conf file for the operation. This repo
|
||||
@ -477,23 +482,9 @@ class Shared:
|
||||
if not os.path.exists(dest_path):
|
||||
os.makedirs(dest_path, exist_ok=True)
|
||||
config_file = open(fname, "w+")
|
||||
|
||||
repolist = []
|
||||
for repo in repos:
|
||||
|
||||
if use_staging:
|
||||
constructed_url = '{}/{}/{}/$basearch/os'.format(
|
||||
staging_base_url,
|
||||
major_version,
|
||||
repo,
|
||||
)
|
||||
|
||||
constructed_url_src = '{}/{}/{}/source/tree'.format(
|
||||
staging_base_url,
|
||||
major_version,
|
||||
repo,
|
||||
)
|
||||
else:
|
||||
constructed_url = '{}/{}/repo/{}{}/$basearch'.format(
|
||||
repo_base_url,
|
||||
project_id,
|
||||
@ -919,9 +910,7 @@ class Shared:
|
||||
compose_latest_sync,
|
||||
compose_dir_is_here: bool = False,
|
||||
hashed: bool = False,
|
||||
extra_repos: list = None,
|
||||
staging_base_url: str = 'https://dl.rockylinux.org/stg',
|
||||
use_staging: bool = False,
|
||||
extra_repos: list = None
|
||||
):
|
||||
"""
|
||||
Builds the repo dictionary
|
||||
@ -956,9 +945,7 @@ class Shared:
|
||||
repolist.append(repodata)
|
||||
|
||||
if extra_repos:
|
||||
extras = Shared.parse_extra_repos(extra_repos)
|
||||
for repo in extras:
|
||||
repolist.append(repo)
|
||||
repolist.append(repo for repo in Shared.parse_extra_repos(extra_repos))
|
||||
|
||||
return repolist
|
||||
|
||||
@ -967,9 +954,6 @@ class Shared:
|
||||
# must be in format URL[,PRIORITY]
|
||||
result = []
|
||||
for idx, candidate in enumerate(extra_repos):
|
||||
if isinstance(candidate, dict):
|
||||
url, priority = candidate['url'], candidate.get('priority', None)
|
||||
else:
|
||||
url, priority = candidate.split(',')
|
||||
if not priority:
|
||||
priority = 100
|
||||
@ -1360,10 +1344,42 @@ class Idents:
|
||||
return False
|
||||
|
||||
@staticmethod
|
||||
def get_vol_id(opts):
|
||||
def get_magic(m):
|
||||
"""
|
||||
Gets a volume ID
|
||||
Gets magic data of a given file
|
||||
"""
|
||||
try:
|
||||
meta = magic.detect_from_filename(m)
|
||||
except ValueError as exc:
|
||||
print(exc)
|
||||
return False
|
||||
|
||||
return meta
|
||||
|
||||
@staticmethod
|
||||
def get_vol_id(i):
|
||||
"""
|
||||
Gets a volume ID of a given ISO
|
||||
"""
|
||||
iso = pycdlib.PyCdlib()
|
||||
try:
|
||||
iso.open(i)
|
||||
except pycdlib.pycdlibexception.PyCdlibInvalidISO as exc:
|
||||
print(exc)
|
||||
return False
|
||||
# This is for s390x. a temporary hack
|
||||
except pycdlib.pycdlibexception.PyCdlibInvalidInput as exc:
|
||||
print(exc)
|
||||
print('Trying magic instead')
|
||||
magic_data = Idents.get_magic(i)
|
||||
if magic_data.mime_type == 'application/x-iso9660-image':
|
||||
volume_id = magic_data.name.split("'")[1]
|
||||
return volume_id
|
||||
|
||||
pvd = iso.pvd
|
||||
volume_id = pvd.volume_identifier.decode('UTF-8').strip()
|
||||
iso.close()
|
||||
return volume_id
|
||||
|
||||
class Syncs:
|
||||
"""
|
||||
|
463
iso/empanadas/poetry.lock
generated
463
iso/empanadas/poetry.lock
generated
@ -1,4 +1,4 @@
|
||||
# This file is automatically @generated by Poetry 1.7.1 and should not be changed by hand.
|
||||
# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand.
|
||||
|
||||
[[package]]
|
||||
name = "atomicwrites"
|
||||
@ -12,36 +12,36 @@ files = [
|
||||
|
||||
[[package]]
|
||||
name = "attrs"
|
||||
version = "23.2.0"
|
||||
version = "24.2.0"
|
||||
description = "Classes Without Boilerplate"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "attrs-23.2.0-py3-none-any.whl", hash = "sha256:99b87a485a5820b23b879f04c2305b44b951b502fd64be915879d77a7e8fc6f1"},
|
||||
{file = "attrs-23.2.0.tar.gz", hash = "sha256:935dc3b529c262f6cf76e50877d35a4bd3c1de194fd41f47a2b7ae8f19971f30"},
|
||||
{file = "attrs-24.2.0-py3-none-any.whl", hash = "sha256:81921eb96de3191c8258c199618104dd27ac608d9366f5e35d011eae1867ede2"},
|
||||
{file = "attrs-24.2.0.tar.gz", hash = "sha256:5cfb1b9148b5b086569baec03f20d7b6bf3bcacc9a42bebf87ffaaca362f6346"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
cov = ["attrs[tests]", "coverage[toml] (>=5.3)"]
|
||||
dev = ["attrs[tests]", "pre-commit"]
|
||||
docs = ["furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier", "zope-interface"]
|
||||
tests = ["attrs[tests-no-zope]", "zope-interface"]
|
||||
tests-mypy = ["mypy (>=1.6)", "pytest-mypy-plugins"]
|
||||
tests-no-zope = ["attrs[tests-mypy]", "cloudpickle", "hypothesis", "pympler", "pytest (>=4.3.0)", "pytest-xdist[psutil]"]
|
||||
benchmark = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-codspeed", "pytest-mypy-plugins", "pytest-xdist[psutil]"]
|
||||
cov = ["cloudpickle", "coverage[toml] (>=5.3)", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"]
|
||||
dev = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pre-commit", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"]
|
||||
docs = ["cogapp", "furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier (<24.7)"]
|
||||
tests = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"]
|
||||
tests-mypy = ["mypy (>=1.11.1)", "pytest-mypy-plugins"]
|
||||
|
||||
[[package]]
|
||||
name = "boto3"
|
||||
version = "1.34.159"
|
||||
version = "1.35.62"
|
||||
description = "The AWS SDK for Python"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "boto3-1.34.159-py3-none-any.whl", hash = "sha256:21120d23cc37c0e80dc4f64434bc5664d2a5645dcd9bf8a8fa97ed5c82164ca0"},
|
||||
{file = "boto3-1.34.159.tar.gz", hash = "sha256:ffe7bbb88ba81b5d54bc8fa0cfb2f3b7fe63a6cffa0f9207df2ef5c22a1c0587"},
|
||||
{file = "boto3-1.35.62-py3-none-any.whl", hash = "sha256:e6574047701ab009c2b2bb17b530a3a2fb34de8698b77f8bbb34dd0c9286c117"},
|
||||
{file = "boto3-1.35.62.tar.gz", hash = "sha256:f80eefe7506aa01799b1027d03eddfd3c4a60548d6db5c32f139e1dec9f3f4f5"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
botocore = ">=1.34.159,<1.35.0"
|
||||
botocore = ">=1.35.62,<1.36.0"
|
||||
jmespath = ">=0.7.1,<2.0.0"
|
||||
s3transfer = ">=0.10.0,<0.11.0"
|
||||
|
||||
@ -50,13 +50,13 @@ crt = ["botocore[crt] (>=1.21.0,<2.0a0)"]
|
||||
|
||||
[[package]]
|
||||
name = "botocore"
|
||||
version = "1.34.159"
|
||||
version = "1.35.62"
|
||||
description = "Low-level, data-driven core of boto 3."
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "botocore-1.34.159-py3-none-any.whl", hash = "sha256:7633062491457419a49f5860c014251ae85689f78266a3ce020c2c8688a76b97"},
|
||||
{file = "botocore-1.34.159.tar.gz", hash = "sha256:dc28806eb21e3c8d690c422530dff8b4b242ac033cbe98f160a9d37796c09cb1"},
|
||||
{file = "botocore-1.35.62-py3-none-any.whl", hash = "sha256:4c3960a33289371d96eba5116364c41e6b848b5afbed3a43f5d8c7ba36f55e1d"},
|
||||
{file = "botocore-1.35.62.tar.gz", hash = "sha256:9df762294d5c727d9ea1c48b98579729a0ba40fd317c3262a6b8d8e12fb67489"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
@ -68,116 +68,131 @@ urllib3 = [
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
crt = ["awscrt (==0.21.2)"]
|
||||
crt = ["awscrt (==0.22.0)"]
|
||||
|
||||
[[package]]
|
||||
name = "certifi"
|
||||
version = "2024.7.4"
|
||||
version = "2024.8.30"
|
||||
description = "Python package for providing Mozilla's CA Bundle."
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
files = [
|
||||
{file = "certifi-2024.7.4-py3-none-any.whl", hash = "sha256:c198e21b1289c2ab85ee4e67bb4b4ef3ead0892059901a8d5b622f24a1101e90"},
|
||||
{file = "certifi-2024.7.4.tar.gz", hash = "sha256:5a1e7645bc0ec61a09e26c36f6106dd4cf40c6db3a1fb6352b0244e7fb057c7b"},
|
||||
{file = "certifi-2024.8.30-py3-none-any.whl", hash = "sha256:922820b53db7a7257ffbda3f597266d435245903d80737e34f8a45ff3e3230d8"},
|
||||
{file = "certifi-2024.8.30.tar.gz", hash = "sha256:bec941d2aa8195e248a60b31ff9f0558284cf01a52591ceda73ea9afffd69fd9"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "charset-normalizer"
|
||||
version = "3.3.2"
|
||||
version = "3.4.0"
|
||||
description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet."
|
||||
optional = false
|
||||
python-versions = ">=3.7.0"
|
||||
files = [
|
||||
{file = "charset-normalizer-3.3.2.tar.gz", hash = "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5"},
|
||||
{file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3"},
|
||||
{file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:06435b539f889b1f6f4ac1758871aae42dc3a8c0e24ac9e60c2384973ad73027"},
|
||||
{file = "charset_normalizer-3.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9063e24fdb1e498ab71cb7419e24622516c4a04476b17a2dab57e8baa30d6e03"},
|
||||
{file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6897af51655e3691ff853668779c7bad41579facacf5fd7253b0133308cf000d"},
|
||||
{file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1d3193f4a680c64b4b6a9115943538edb896edc190f0b222e73761716519268e"},
|
||||
{file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd70574b12bb8a4d2aaa0094515df2463cb429d8536cfb6c7ce983246983e5a6"},
|
||||
{file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8465322196c8b4d7ab6d1e049e4c5cb460d0394da4a27d23cc242fbf0034b6b5"},
|
||||
{file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a9a8e9031d613fd2009c182b69c7b2c1ef8239a0efb1df3f7c8da66d5dd3d537"},
|
||||
{file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:beb58fe5cdb101e3a055192ac291b7a21e3b7ef4f67fa1d74e331a7f2124341c"},
|
||||
{file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e06ed3eb3218bc64786f7db41917d4e686cc4856944f53d5bdf83a6884432e12"},
|
||||
{file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:2e81c7b9c8979ce92ed306c249d46894776a909505d8f5a4ba55b14206e3222f"},
|
||||
{file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:572c3763a264ba47b3cf708a44ce965d98555f618ca42c926a9c1616d8f34269"},
|
||||
{file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fd1abc0d89e30cc4e02e4064dc67fcc51bd941eb395c502aac3ec19fab46b519"},
|
||||
{file = "charset_normalizer-3.3.2-cp310-cp310-win32.whl", hash = "sha256:3d47fa203a7bd9c5b6cee4736ee84ca03b8ef23193c0d1ca99b5089f72645c73"},
|
||||
{file = "charset_normalizer-3.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:10955842570876604d404661fbccbc9c7e684caf432c09c715ec38fbae45ae09"},
|
||||
{file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db"},
|
||||
{file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96"},
|
||||
{file = "charset_normalizer-3.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e"},
|
||||
{file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f"},
|
||||
{file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574"},
|
||||
{file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4"},
|
||||
{file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8"},
|
||||
{file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc"},
|
||||
{file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae"},
|
||||
{file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887"},
|
||||
{file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae"},
|
||||
{file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce"},
|
||||
{file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f"},
|
||||
{file = "charset_normalizer-3.3.2-cp311-cp311-win32.whl", hash = "sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab"},
|
||||
{file = "charset_normalizer-3.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77"},
|
||||
{file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8"},
|
||||
{file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b"},
|
||||
{file = "charset_normalizer-3.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6"},
|
||||
{file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a"},
|
||||
{file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389"},
|
||||
{file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa"},
|
||||
{file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b"},
|
||||
{file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed"},
|
||||
{file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26"},
|
||||
{file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d"},
|
||||
{file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068"},
|
||||
{file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143"},
|
||||
{file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4"},
|
||||
{file = "charset_normalizer-3.3.2-cp312-cp312-win32.whl", hash = "sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7"},
|
||||
{file = "charset_normalizer-3.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001"},
|
||||
{file = "charset_normalizer-3.3.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:95f2a5796329323b8f0512e09dbb7a1860c46a39da62ecb2324f116fa8fdc85c"},
|
||||
{file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c002b4ffc0be611f0d9da932eb0f704fe2602a9a949d1f738e4c34c75b0863d5"},
|
||||
{file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a981a536974bbc7a512cf44ed14938cf01030a99e9b3a06dd59578882f06f985"},
|
||||
{file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3287761bc4ee9e33561a7e058c72ac0938c4f57fe49a09eae428fd88aafe7bb6"},
|
||||
{file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:42cb296636fcc8b0644486d15c12376cb9fa75443e00fb25de0b8602e64c1714"},
|
||||
{file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0a55554a2fa0d408816b3b5cedf0045f4b8e1a6065aec45849de2d6f3f8e9786"},
|
||||
{file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:c083af607d2515612056a31f0a8d9e0fcb5876b7bfc0abad3ecd275bc4ebc2d5"},
|
||||
{file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:87d1351268731db79e0f8e745d92493ee2841c974128ef629dc518b937d9194c"},
|
||||
{file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:bd8f7df7d12c2db9fab40bdd87a7c09b1530128315d047a086fa3ae3435cb3a8"},
|
||||
{file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:c180f51afb394e165eafe4ac2936a14bee3eb10debc9d9e4db8958fe36afe711"},
|
||||
{file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:8c622a5fe39a48f78944a87d4fb8a53ee07344641b0562c540d840748571b811"},
|
||||
{file = "charset_normalizer-3.3.2-cp37-cp37m-win32.whl", hash = "sha256:db364eca23f876da6f9e16c9da0df51aa4f104a972735574842618b8c6d999d4"},
|
||||
{file = "charset_normalizer-3.3.2-cp37-cp37m-win_amd64.whl", hash = "sha256:86216b5cee4b06df986d214f664305142d9c76df9b6512be2738aa72a2048f99"},
|
||||
{file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:6463effa3186ea09411d50efc7d85360b38d5f09b870c48e4600f63af490e56a"},
|
||||
{file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6c4caeef8fa63d06bd437cd4bdcf3ffefe6738fb1b25951440d80dc7df8c03ac"},
|
||||
{file = "charset_normalizer-3.3.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:37e55c8e51c236f95b033f6fb391d7d7970ba5fe7ff453dad675e88cf303377a"},
|
||||
{file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb69256e180cb6c8a894fee62b3afebae785babc1ee98b81cdf68bbca1987f33"},
|
||||
{file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ae5f4161f18c61806f411a13b0310bea87f987c7d2ecdbdaad0e94eb2e404238"},
|
||||
{file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b2b0a0c0517616b6869869f8c581d4eb2dd83a4d79e0ebcb7d373ef9956aeb0a"},
|
||||
{file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:45485e01ff4d3630ec0d9617310448a8702f70e9c01906b0d0118bdf9d124cf2"},
|
||||
{file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eb00ed941194665c332bf8e078baf037d6c35d7c4f3102ea2d4f16ca94a26dc8"},
|
||||
{file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:2127566c664442652f024c837091890cb1942c30937add288223dc895793f898"},
|
||||
{file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a50aebfa173e157099939b17f18600f72f84eed3049e743b68ad15bd69b6bf99"},
|
||||
{file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:4d0d1650369165a14e14e1e47b372cfcb31d6ab44e6e33cb2d4e57265290044d"},
|
||||
{file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:923c0c831b7cfcb071580d3f46c4baf50f174be571576556269530f4bbd79d04"},
|
||||
{file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:06a81e93cd441c56a9b65d8e1d043daeb97a3d0856d177d5c90ba85acb3db087"},
|
||||
{file = "charset_normalizer-3.3.2-cp38-cp38-win32.whl", hash = "sha256:6ef1d82a3af9d3eecdba2321dc1b3c238245d890843e040e41e470ffa64c3e25"},
|
||||
{file = "charset_normalizer-3.3.2-cp38-cp38-win_amd64.whl", hash = "sha256:eb8821e09e916165e160797a6c17edda0679379a4be5c716c260e836e122f54b"},
|
||||
{file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c235ebd9baae02f1b77bcea61bce332cb4331dc3617d254df3323aa01ab47bd4"},
|
||||
{file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5b4c145409bef602a690e7cfad0a15a55c13320ff7a3ad7ca59c13bb8ba4d45d"},
|
||||
{file = "charset_normalizer-3.3.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:68d1f8a9e9e37c1223b656399be5d6b448dea850bed7d0f87a8311f1ff3dabb0"},
|
||||
{file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22afcb9f253dac0696b5a4be4a1c0f8762f8239e21b99680099abd9b2b1b2269"},
|
||||
{file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e27ad930a842b4c5eb8ac0016b0a54f5aebbe679340c26101df33424142c143c"},
|
||||
{file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1f79682fbe303db92bc2b1136016a38a42e835d932bab5b3b1bfcfbf0640e519"},
|
||||
{file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b261ccdec7821281dade748d088bb6e9b69e6d15b30652b74cbbac25e280b796"},
|
||||
{file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:122c7fa62b130ed55f8f285bfd56d5f4b4a5b503609d181f9ad85e55c89f4185"},
|
||||
{file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d0eccceffcb53201b5bfebb52600a5fb483a20b61da9dbc885f8b103cbe7598c"},
|
||||
{file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9f96df6923e21816da7e0ad3fd47dd8f94b2a5ce594e00677c0013018b813458"},
|
||||
{file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:7f04c839ed0b6b98b1a7501a002144b76c18fb1c1850c8b98d458ac269e26ed2"},
|
||||
{file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:34d1c8da1e78d2e001f363791c98a272bb734000fcef47a491c1e3b0505657a8"},
|
||||
{file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ff8fa367d09b717b2a17a052544193ad76cd49979c805768879cb63d9ca50561"},
|
||||
{file = "charset_normalizer-3.3.2-cp39-cp39-win32.whl", hash = "sha256:aed38f6e4fb3f5d6bf81bfa990a07806be9d83cf7bacef998ab1a9bd660a581f"},
|
||||
{file = "charset_normalizer-3.3.2-cp39-cp39-win_amd64.whl", hash = "sha256:b01b88d45a6fcb69667cd6d2f7a9aeb4bf53760d7fc536bf679ec94fe9f3ff3d"},
|
||||
{file = "charset_normalizer-3.3.2-py3-none-any.whl", hash = "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc"},
|
||||
{file = "charset_normalizer-3.4.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:4f9fc98dad6c2eaa32fc3af1417d95b5e3d08aff968df0cd320066def971f9a6"},
|
||||
{file = "charset_normalizer-3.4.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0de7b687289d3c1b3e8660d0741874abe7888100efe14bd0f9fd7141bcbda92b"},
|
||||
{file = "charset_normalizer-3.4.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5ed2e36c3e9b4f21dd9422f6893dec0abf2cca553af509b10cd630f878d3eb99"},
|
||||
{file = "charset_normalizer-3.4.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:40d3ff7fc90b98c637bda91c89d51264a3dcf210cade3a2c6f838c7268d7a4ca"},
|
||||
{file = "charset_normalizer-3.4.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1110e22af8ca26b90bd6364fe4c763329b0ebf1ee213ba32b68c73de5752323d"},
|
||||
{file = "charset_normalizer-3.4.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:86f4e8cca779080f66ff4f191a685ced73d2f72d50216f7112185dc02b90b9b7"},
|
||||
{file = "charset_normalizer-3.4.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7f683ddc7eedd742e2889d2bfb96d69573fde1d92fcb811979cdb7165bb9c7d3"},
|
||||
{file = "charset_normalizer-3.4.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:27623ba66c183eca01bf9ff833875b459cad267aeeb044477fedac35e19ba907"},
|
||||
{file = "charset_normalizer-3.4.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:f606a1881d2663630ea5b8ce2efe2111740df4b687bd78b34a8131baa007f79b"},
|
||||
{file = "charset_normalizer-3.4.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:0b309d1747110feb25d7ed6b01afdec269c647d382c857ef4663bbe6ad95a912"},
|
||||
{file = "charset_normalizer-3.4.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:136815f06a3ae311fae551c3df1f998a1ebd01ddd424aa5603a4336997629e95"},
|
||||
{file = "charset_normalizer-3.4.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:14215b71a762336254351b00ec720a8e85cada43b987da5a042e4ce3e82bd68e"},
|
||||
{file = "charset_normalizer-3.4.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:79983512b108e4a164b9c8d34de3992f76d48cadc9554c9e60b43f308988aabe"},
|
||||
{file = "charset_normalizer-3.4.0-cp310-cp310-win32.whl", hash = "sha256:c94057af19bc953643a33581844649a7fdab902624d2eb739738a30e2b3e60fc"},
|
||||
{file = "charset_normalizer-3.4.0-cp310-cp310-win_amd64.whl", hash = "sha256:55f56e2ebd4e3bc50442fbc0888c9d8c94e4e06a933804e2af3e89e2f9c1c749"},
|
||||
{file = "charset_normalizer-3.4.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:0d99dd8ff461990f12d6e42c7347fd9ab2532fb70e9621ba520f9e8637161d7c"},
|
||||
{file = "charset_normalizer-3.4.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c57516e58fd17d03ebe67e181a4e4e2ccab1168f8c2976c6a334d4f819fe5944"},
|
||||
{file = "charset_normalizer-3.4.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6dba5d19c4dfab08e58d5b36304b3f92f3bd5d42c1a3fa37b5ba5cdf6dfcbcee"},
|
||||
{file = "charset_normalizer-3.4.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bf4475b82be41b07cc5e5ff94810e6a01f276e37c2d55571e3fe175e467a1a1c"},
|
||||
{file = "charset_normalizer-3.4.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ce031db0408e487fd2775d745ce30a7cd2923667cf3b69d48d219f1d8f5ddeb6"},
|
||||
{file = "charset_normalizer-3.4.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8ff4e7cdfdb1ab5698e675ca622e72d58a6fa2a8aa58195de0c0061288e6e3ea"},
|
||||
{file = "charset_normalizer-3.4.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3710a9751938947e6327ea9f3ea6332a09bf0ba0c09cae9cb1f250bd1f1549bc"},
|
||||
{file = "charset_normalizer-3.4.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:82357d85de703176b5587dbe6ade8ff67f9f69a41c0733cf2425378b49954de5"},
|
||||
{file = "charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:47334db71978b23ebcf3c0f9f5ee98b8d65992b65c9c4f2d34c2eaf5bcaf0594"},
|
||||
{file = "charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:8ce7fd6767a1cc5a92a639b391891bf1c268b03ec7e021c7d6d902285259685c"},
|
||||
{file = "charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:f1a2f519ae173b5b6a2c9d5fa3116ce16e48b3462c8b96dfdded11055e3d6365"},
|
||||
{file = "charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:63bc5c4ae26e4bc6be6469943b8253c0fd4e4186c43ad46e713ea61a0ba49129"},
|
||||
{file = "charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:bcb4f8ea87d03bc51ad04add8ceaf9b0f085ac045ab4d74e73bbc2dc033f0236"},
|
||||
{file = "charset_normalizer-3.4.0-cp311-cp311-win32.whl", hash = "sha256:9ae4ef0b3f6b41bad6366fb0ea4fc1d7ed051528e113a60fa2a65a9abb5b1d99"},
|
||||
{file = "charset_normalizer-3.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:cee4373f4d3ad28f1ab6290684d8e2ebdb9e7a1b74fdc39e4c211995f77bec27"},
|
||||
{file = "charset_normalizer-3.4.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0713f3adb9d03d49d365b70b84775d0a0d18e4ab08d12bc46baa6132ba78aaf6"},
|
||||
{file = "charset_normalizer-3.4.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:de7376c29d95d6719048c194a9cf1a1b0393fbe8488a22008610b0361d834ecf"},
|
||||
{file = "charset_normalizer-3.4.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4a51b48f42d9358460b78725283f04bddaf44a9358197b889657deba38f329db"},
|
||||
{file = "charset_normalizer-3.4.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b295729485b06c1a0683af02a9e42d2caa9db04a373dc38a6a58cdd1e8abddf1"},
|
||||
{file = "charset_normalizer-3.4.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ee803480535c44e7f5ad00788526da7d85525cfefaf8acf8ab9a310000be4b03"},
|
||||
{file = "charset_normalizer-3.4.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3d59d125ffbd6d552765510e3f31ed75ebac2c7470c7274195b9161a32350284"},
|
||||
{file = "charset_normalizer-3.4.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8cda06946eac330cbe6598f77bb54e690b4ca93f593dee1568ad22b04f347c15"},
|
||||
{file = "charset_normalizer-3.4.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:07afec21bbbbf8a5cc3651aa96b980afe2526e7f048fdfb7f1014d84acc8b6d8"},
|
||||
{file = "charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6b40e8d38afe634559e398cc32b1472f376a4099c75fe6299ae607e404c033b2"},
|
||||
{file = "charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:b8dcd239c743aa2f9c22ce674a145e0a25cb1566c495928440a181ca1ccf6719"},
|
||||
{file = "charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:84450ba661fb96e9fd67629b93d2941c871ca86fc38d835d19d4225ff946a631"},
|
||||
{file = "charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:44aeb140295a2f0659e113b31cfe92c9061622cadbc9e2a2f7b8ef6b1e29ef4b"},
|
||||
{file = "charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:1db4e7fefefd0f548d73e2e2e041f9df5c59e178b4c72fbac4cc6f535cfb1565"},
|
||||
{file = "charset_normalizer-3.4.0-cp312-cp312-win32.whl", hash = "sha256:5726cf76c982532c1863fb64d8c6dd0e4c90b6ece9feb06c9f202417a31f7dd7"},
|
||||
{file = "charset_normalizer-3.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:b197e7094f232959f8f20541ead1d9862ac5ebea1d58e9849c1bf979255dfac9"},
|
||||
{file = "charset_normalizer-3.4.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:dd4eda173a9fcccb5f2e2bd2a9f423d180194b1bf17cf59e3269899235b2a114"},
|
||||
{file = "charset_normalizer-3.4.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e9e3c4c9e1ed40ea53acf11e2a386383c3304212c965773704e4603d589343ed"},
|
||||
{file = "charset_normalizer-3.4.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:92a7e36b000bf022ef3dbb9c46bfe2d52c047d5e3f3343f43204263c5addc250"},
|
||||
{file = "charset_normalizer-3.4.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:54b6a92d009cbe2fb11054ba694bc9e284dad30a26757b1e372a1fdddaf21920"},
|
||||
{file = "charset_normalizer-3.4.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ffd9493de4c922f2a38c2bf62b831dcec90ac673ed1ca182fe11b4d8e9f2a64"},
|
||||
{file = "charset_normalizer-3.4.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:35c404d74c2926d0287fbd63ed5d27eb911eb9e4a3bb2c6d294f3cfd4a9e0c23"},
|
||||
{file = "charset_normalizer-3.4.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4796efc4faf6b53a18e3d46343535caed491776a22af773f366534056c4e1fbc"},
|
||||
{file = "charset_normalizer-3.4.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e7fdd52961feb4c96507aa649550ec2a0d527c086d284749b2f582f2d40a2e0d"},
|
||||
{file = "charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:92db3c28b5b2a273346bebb24857fda45601aef6ae1c011c0a997106581e8a88"},
|
||||
{file = "charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ab973df98fc99ab39080bfb0eb3a925181454d7c3ac8a1e695fddfae696d9e90"},
|
||||
{file = "charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:4b67fdab07fdd3c10bb21edab3cbfe8cf5696f453afce75d815d9d7223fbe88b"},
|
||||
{file = "charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:aa41e526a5d4a9dfcfbab0716c7e8a1b215abd3f3df5a45cf18a12721d31cb5d"},
|
||||
{file = "charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ffc519621dce0c767e96b9c53f09c5d215578e10b02c285809f76509a3931482"},
|
||||
{file = "charset_normalizer-3.4.0-cp313-cp313-win32.whl", hash = "sha256:f19c1585933c82098c2a520f8ec1227f20e339e33aca8fa6f956f6691b784e67"},
|
||||
{file = "charset_normalizer-3.4.0-cp313-cp313-win_amd64.whl", hash = "sha256:707b82d19e65c9bd28b81dde95249b07bf9f5b90ebe1ef17d9b57473f8a64b7b"},
|
||||
{file = "charset_normalizer-3.4.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:dbe03226baf438ac4fda9e2d0715022fd579cb641c4cf639fa40d53b2fe6f3e2"},
|
||||
{file = "charset_normalizer-3.4.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dd9a8bd8900e65504a305bf8ae6fa9fbc66de94178c420791d0293702fce2df7"},
|
||||
{file = "charset_normalizer-3.4.0-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b8831399554b92b72af5932cdbbd4ddc55c55f631bb13ff8fe4e6536a06c5c51"},
|
||||
{file = "charset_normalizer-3.4.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a14969b8691f7998e74663b77b4c36c0337cb1df552da83d5c9004a93afdb574"},
|
||||
{file = "charset_normalizer-3.4.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dcaf7c1524c0542ee2fc82cc8ec337f7a9f7edee2532421ab200d2b920fc97cf"},
|
||||
{file = "charset_normalizer-3.4.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:425c5f215d0eecee9a56cdb703203dda90423247421bf0d67125add85d0c4455"},
|
||||
{file = "charset_normalizer-3.4.0-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:d5b054862739d276e09928de37c79ddeec42a6e1bfc55863be96a36ba22926f6"},
|
||||
{file = "charset_normalizer-3.4.0-cp37-cp37m-musllinux_1_2_i686.whl", hash = "sha256:f3e73a4255342d4eb26ef6df01e3962e73aa29baa3124a8e824c5d3364a65748"},
|
||||
{file = "charset_normalizer-3.4.0-cp37-cp37m-musllinux_1_2_ppc64le.whl", hash = "sha256:2f6c34da58ea9c1a9515621f4d9ac379871a8f21168ba1b5e09d74250de5ad62"},
|
||||
{file = "charset_normalizer-3.4.0-cp37-cp37m-musllinux_1_2_s390x.whl", hash = "sha256:f09cb5a7bbe1ecae6e87901a2eb23e0256bb524a79ccc53eb0b7629fbe7677c4"},
|
||||
{file = "charset_normalizer-3.4.0-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:0099d79bdfcf5c1f0c2c72f91516702ebf8b0b8ddd8905f97a8aecf49712c621"},
|
||||
{file = "charset_normalizer-3.4.0-cp37-cp37m-win32.whl", hash = "sha256:9c98230f5042f4945f957d006edccc2af1e03ed5e37ce7c373f00a5a4daa6149"},
|
||||
{file = "charset_normalizer-3.4.0-cp37-cp37m-win_amd64.whl", hash = "sha256:62f60aebecfc7f4b82e3f639a7d1433a20ec32824db2199a11ad4f5e146ef5ee"},
|
||||
{file = "charset_normalizer-3.4.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:af73657b7a68211996527dbfeffbb0864e043d270580c5aef06dc4b659a4b578"},
|
||||
{file = "charset_normalizer-3.4.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:cab5d0b79d987c67f3b9e9c53f54a61360422a5a0bc075f43cab5621d530c3b6"},
|
||||
{file = "charset_normalizer-3.4.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:9289fd5dddcf57bab41d044f1756550f9e7cf0c8e373b8cdf0ce8773dc4bd417"},
|
||||
{file = "charset_normalizer-3.4.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6b493a043635eb376e50eedf7818f2f322eabbaa974e948bd8bdd29eb7ef2a51"},
|
||||
{file = "charset_normalizer-3.4.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9fa2566ca27d67c86569e8c85297aaf413ffab85a8960500f12ea34ff98e4c41"},
|
||||
{file = "charset_normalizer-3.4.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a8e538f46104c815be19c975572d74afb53f29650ea2025bbfaef359d2de2f7f"},
|
||||
{file = "charset_normalizer-3.4.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6fd30dc99682dc2c603c2b315bded2799019cea829f8bf57dc6b61efde6611c8"},
|
||||
{file = "charset_normalizer-3.4.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2006769bd1640bdf4d5641c69a3d63b71b81445473cac5ded39740a226fa88ab"},
|
||||
{file = "charset_normalizer-3.4.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:dc15e99b2d8a656f8e666854404f1ba54765871104e50c8e9813af8a7db07f12"},
|
||||
{file = "charset_normalizer-3.4.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:ab2e5bef076f5a235c3774b4f4028a680432cded7cad37bba0fd90d64b187d19"},
|
||||
{file = "charset_normalizer-3.4.0-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:4ec9dd88a5b71abfc74e9df5ebe7921c35cbb3b641181a531ca65cdb5e8e4dea"},
|
||||
{file = "charset_normalizer-3.4.0-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:43193c5cda5d612f247172016c4bb71251c784d7a4d9314677186a838ad34858"},
|
||||
{file = "charset_normalizer-3.4.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:aa693779a8b50cd97570e5a0f343538a8dbd3e496fa5dcb87e29406ad0299654"},
|
||||
{file = "charset_normalizer-3.4.0-cp38-cp38-win32.whl", hash = "sha256:7706f5850360ac01d80c89bcef1640683cc12ed87f42579dab6c5d3ed6888613"},
|
||||
{file = "charset_normalizer-3.4.0-cp38-cp38-win_amd64.whl", hash = "sha256:c3e446d253bd88f6377260d07c895816ebf33ffffd56c1c792b13bff9c3e1ade"},
|
||||
{file = "charset_normalizer-3.4.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:980b4f289d1d90ca5efcf07958d3eb38ed9c0b7676bf2831a54d4f66f9c27dfa"},
|
||||
{file = "charset_normalizer-3.4.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f28f891ccd15c514a0981f3b9db9aa23d62fe1a99997512b0491d2ed323d229a"},
|
||||
{file = "charset_normalizer-3.4.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a8aacce6e2e1edcb6ac625fb0f8c3a9570ccc7bfba1f63419b3769ccf6a00ed0"},
|
||||
{file = "charset_normalizer-3.4.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bd7af3717683bea4c87acd8c0d3d5b44d56120b26fd3f8a692bdd2d5260c620a"},
|
||||
{file = "charset_normalizer-3.4.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5ff2ed8194587faf56555927b3aa10e6fb69d931e33953943bc4f837dfee2242"},
|
||||
{file = "charset_normalizer-3.4.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e91f541a85298cf35433bf66f3fab2a4a2cff05c127eeca4af174f6d497f0d4b"},
|
||||
{file = "charset_normalizer-3.4.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:309a7de0a0ff3040acaebb35ec45d18db4b28232f21998851cfa709eeff49d62"},
|
||||
{file = "charset_normalizer-3.4.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:285e96d9d53422efc0d7a17c60e59f37fbf3dfa942073f666db4ac71e8d726d0"},
|
||||
{file = "charset_normalizer-3.4.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:5d447056e2ca60382d460a604b6302d8db69476fd2015c81e7c35417cfabe4cd"},
|
||||
{file = "charset_normalizer-3.4.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:20587d20f557fe189b7947d8e7ec5afa110ccf72a3128d61a2a387c3313f46be"},
|
||||
{file = "charset_normalizer-3.4.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:130272c698667a982a5d0e626851ceff662565379baf0ff2cc58067b81d4f11d"},
|
||||
{file = "charset_normalizer-3.4.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:ab22fbd9765e6954bc0bcff24c25ff71dcbfdb185fcdaca49e81bac68fe724d3"},
|
||||
{file = "charset_normalizer-3.4.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:7782afc9b6b42200f7362858f9e73b1f8316afb276d316336c0ec3bd73312742"},
|
||||
{file = "charset_normalizer-3.4.0-cp39-cp39-win32.whl", hash = "sha256:2de62e8801ddfff069cd5c504ce3bc9672b23266597d4e4f50eda28846c322f2"},
|
||||
{file = "charset_normalizer-3.4.0-cp39-cp39-win_amd64.whl", hash = "sha256:95c3c157765b031331dd4db3c775e58deaee050a3042fcad72cbc4189d7c8dca"},
|
||||
{file = "charset_normalizer-3.4.0-py3-none-any.whl", hash = "sha256:fe9f97feb71aa9896b81973a7bbada8c49501dc73e58a10fcef6663af95e5079"},
|
||||
{file = "charset_normalizer-3.4.0.tar.gz", hash = "sha256:223217c3d4f82c3ac5e29032b3f1c2eb0fb591b72161f86d93f5719079dae93e"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -191,6 +206,17 @@ files = [
|
||||
{file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "file-magic"
|
||||
version = "0.4.1"
|
||||
description = "Python front end for libmagic(3)"
|
||||
optional = false
|
||||
python-versions = ">=2.7"
|
||||
files = [
|
||||
{file = "file-magic-0.4.1.tar.gz", hash = "sha256:a91d1483117f7ed48cd0238ad9be36b04824d57e9c38ea7523113989e81b9c53"},
|
||||
{file = "file_magic-0.4.1-py3-none-any.whl", hash = "sha256:cb9496a1656baf75cadd771479f63b53081095e968d0be72b9b7a7ed538e4fb8"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gitdb"
|
||||
version = "4.0.11"
|
||||
@ -225,15 +251,18 @@ test = ["coverage[toml]", "ddt (>=1.1.1,!=1.4.3)", "mock", "mypy", "pre-commit",
|
||||
|
||||
[[package]]
|
||||
name = "idna"
|
||||
version = "3.7"
|
||||
version = "3.10"
|
||||
description = "Internationalized Domain Names in Applications (IDNA)"
|
||||
optional = false
|
||||
python-versions = ">=3.5"
|
||||
python-versions = ">=3.6"
|
||||
files = [
|
||||
{file = "idna-3.7-py3-none-any.whl", hash = "sha256:82fee1fc78add43492d3a1898bfa6d8a904cc97d8427f683ed8e798d07761aa0"},
|
||||
{file = "idna-3.7.tar.gz", hash = "sha256:028ff3aadf0609c1fd278d8ea3089299412a7a8b9bd005dd08b9f8285bcb5cfc"},
|
||||
{file = "idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3"},
|
||||
{file = "idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
all = ["flake8 (>=7.1.1)", "mypy (>=1.11.2)", "pytest (>=8.3.2)", "ruff (>=0.6.2)"]
|
||||
|
||||
[[package]]
|
||||
name = "importlib-resources"
|
||||
version = "5.13.0"
|
||||
@ -282,13 +311,13 @@ files = [
|
||||
|
||||
[[package]]
|
||||
name = "kobo"
|
||||
version = "0.36.2"
|
||||
version = "0.37.0"
|
||||
description = "A pile of python modules used by Red Hat release engineering to build their tools"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
files = [
|
||||
{file = "kobo-0.36.2-py3-none-any.whl", hash = "sha256:e41aeb5739d2b3578fb411978ee66f5d84a8d47a0f23fe076f905eb94806ec32"},
|
||||
{file = "kobo-0.36.2.tar.gz", hash = "sha256:e451421280a08df9d794582af9693f03fe2290db5041a08fdbee02a587a51bbf"},
|
||||
{file = "kobo-0.37.0-py3-none-any.whl", hash = "sha256:3a76e9748a1d218aa91e7ae6ffaed43dcd25984cb09a9c1db19a1f7648665141"},
|
||||
{file = "kobo-0.37.0.tar.gz", hash = "sha256:05ee956fb40aff3eaa2abf2cdec931245f5d6fb6ba8ea6c19f6e26c7ea602fb9"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
@ -296,93 +325,94 @@ six = "*"
|
||||
|
||||
[[package]]
|
||||
name = "markupsafe"
|
||||
version = "2.1.5"
|
||||
version = "3.0.2"
|
||||
description = "Safely add untrusted strings to HTML/XML markup."
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
python-versions = ">=3.9"
|
||||
files = [
|
||||
{file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a17a92de5231666cfbe003f0e4b9b3a7ae3afb1ec2845aadc2bacc93ff85febc"},
|
||||
{file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:72b6be590cc35924b02c78ef34b467da4ba07e4e0f0454a2c5907f473fc50ce5"},
|
||||
{file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e61659ba32cf2cf1481e575d0462554625196a1f2fc06a1c777d3f48e8865d46"},
|
||||
{file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2174c595a0d73a3080ca3257b40096db99799265e1c27cc5a610743acd86d62f"},
|
||||
{file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae2ad8ae6ebee9d2d94b17fb62763125f3f374c25618198f40cbb8b525411900"},
|
||||
{file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:075202fa5b72c86ad32dc7d0b56024ebdbcf2048c0ba09f1cde31bfdd57bcfff"},
|
||||
{file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:598e3276b64aff0e7b3451b72e94fa3c238d452e7ddcd893c3ab324717456bad"},
|
||||
{file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fce659a462a1be54d2ffcacea5e3ba2d74daa74f30f5f143fe0c58636e355fdd"},
|
||||
{file = "MarkupSafe-2.1.5-cp310-cp310-win32.whl", hash = "sha256:d9fad5155d72433c921b782e58892377c44bd6252b5af2f67f16b194987338a4"},
|
||||
{file = "MarkupSafe-2.1.5-cp310-cp310-win_amd64.whl", hash = "sha256:bf50cd79a75d181c9181df03572cdce0fbb75cc353bc350712073108cba98de5"},
|
||||
{file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:629ddd2ca402ae6dbedfceeba9c46d5f7b2a61d9749597d4307f943ef198fc1f"},
|
||||
{file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5b7b716f97b52c5a14bffdf688f971b2d5ef4029127f1ad7a513973cfd818df2"},
|
||||
{file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6ec585f69cec0aa07d945b20805be741395e28ac1627333b1c5b0105962ffced"},
|
||||
{file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b91c037585eba9095565a3556f611e3cbfaa42ca1e865f7b8015fe5c7336d5a5"},
|
||||
{file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7502934a33b54030eaf1194c21c692a534196063db72176b0c4028e140f8f32c"},
|
||||
{file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0e397ac966fdf721b2c528cf028494e86172b4feba51d65f81ffd65c63798f3f"},
|
||||
{file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c061bb86a71b42465156a3ee7bd58c8c2ceacdbeb95d05a99893e08b8467359a"},
|
||||
{file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3a57fdd7ce31c7ff06cdfbf31dafa96cc533c21e443d57f5b1ecc6cdc668ec7f"},
|
||||
{file = "MarkupSafe-2.1.5-cp311-cp311-win32.whl", hash = "sha256:397081c1a0bfb5124355710fe79478cdbeb39626492b15d399526ae53422b906"},
|
||||
{file = "MarkupSafe-2.1.5-cp311-cp311-win_amd64.whl", hash = "sha256:2b7c57a4dfc4f16f7142221afe5ba4e093e09e728ca65c51f5620c9aaeb9a617"},
|
||||
{file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:8dec4936e9c3100156f8a2dc89c4b88d5c435175ff03413b443469c7c8c5f4d1"},
|
||||
{file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:3c6b973f22eb18a789b1460b4b91bf04ae3f0c4234a0a6aa6b0a92f6f7b951d4"},
|
||||
{file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ac07bad82163452a6884fe8fa0963fb98c2346ba78d779ec06bd7a6262132aee"},
|
||||
{file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f5dfb42c4604dddc8e4305050aa6deb084540643ed5804d7455b5df8fe16f5e5"},
|
||||
{file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ea3d8a3d18833cf4304cd2fc9cbb1efe188ca9b5efef2bdac7adc20594a0e46b"},
|
||||
{file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d050b3361367a06d752db6ead6e7edeb0009be66bc3bae0ee9d97fb326badc2a"},
|
||||
{file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:bec0a414d016ac1a18862a519e54b2fd0fc8bbfd6890376898a6c0891dd82e9f"},
|
||||
{file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:58c98fee265677f63a4385256a6d7683ab1832f3ddd1e66fe948d5880c21a169"},
|
||||
{file = "MarkupSafe-2.1.5-cp312-cp312-win32.whl", hash = "sha256:8590b4ae07a35970728874632fed7bd57b26b0102df2d2b233b6d9d82f6c62ad"},
|
||||
{file = "MarkupSafe-2.1.5-cp312-cp312-win_amd64.whl", hash = "sha256:823b65d8706e32ad2df51ed89496147a42a2a6e01c13cfb6ffb8b1e92bc910bb"},
|
||||
{file = "MarkupSafe-2.1.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c8b29db45f8fe46ad280a7294f5c3ec36dbac9491f2d1c17345be8e69cc5928f"},
|
||||
{file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ec6a563cff360b50eed26f13adc43e61bc0c04d94b8be985e6fb24b81f6dcfdf"},
|
||||
{file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a549b9c31bec33820e885335b451286e2969a2d9e24879f83fe904a5ce59d70a"},
|
||||
{file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4f11aa001c540f62c6166c7726f71f7573b52c68c31f014c25cc7901deea0b52"},
|
||||
{file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:7b2e5a267c855eea6b4283940daa6e88a285f5f2a67f2220203786dfa59b37e9"},
|
||||
{file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:2d2d793e36e230fd32babe143b04cec8a8b3eb8a3122d2aceb4a371e6b09b8df"},
|
||||
{file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:ce409136744f6521e39fd8e2a24c53fa18ad67aa5bc7c2cf83645cce5b5c4e50"},
|
||||
{file = "MarkupSafe-2.1.5-cp37-cp37m-win32.whl", hash = "sha256:4096e9de5c6fdf43fb4f04c26fb114f61ef0bf2e5604b6ee3019d51b69e8c371"},
|
||||
{file = "MarkupSafe-2.1.5-cp37-cp37m-win_amd64.whl", hash = "sha256:4275d846e41ecefa46e2015117a9f491e57a71ddd59bbead77e904dc02b1bed2"},
|
||||
{file = "MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:656f7526c69fac7f600bd1f400991cc282b417d17539a1b228617081106feb4a"},
|
||||
{file = "MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:97cafb1f3cbcd3fd2b6fbfb99ae11cdb14deea0736fc2b0952ee177f2b813a46"},
|
||||
{file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f3fbcb7ef1f16e48246f704ab79d79da8a46891e2da03f8783a5b6fa41a9532"},
|
||||
{file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fa9db3f79de01457b03d4f01b34cf91bc0048eb2c3846ff26f66687c2f6d16ab"},
|
||||
{file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffee1f21e5ef0d712f9033568f8344d5da8cc2869dbd08d87c84656e6a2d2f68"},
|
||||
{file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:5dedb4db619ba5a2787a94d877bc8ffc0566f92a01c0ef214865e54ecc9ee5e0"},
|
||||
{file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:30b600cf0a7ac9234b2638fbc0fb6158ba5bdcdf46aeb631ead21248b9affbc4"},
|
||||
{file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8dd717634f5a044f860435c1d8c16a270ddf0ef8588d4887037c5028b859b0c3"},
|
||||
{file = "MarkupSafe-2.1.5-cp38-cp38-win32.whl", hash = "sha256:daa4ee5a243f0f20d528d939d06670a298dd39b1ad5f8a72a4275124a7819eff"},
|
||||
{file = "MarkupSafe-2.1.5-cp38-cp38-win_amd64.whl", hash = "sha256:619bc166c4f2de5caa5a633b8b7326fbe98e0ccbfacabd87268a2b15ff73a029"},
|
||||
{file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:7a68b554d356a91cce1236aa7682dc01df0edba8d043fd1ce607c49dd3c1edcf"},
|
||||
{file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:db0b55e0f3cc0be60c1f19efdde9a637c32740486004f20d1cff53c3c0ece4d2"},
|
||||
{file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3e53af139f8579a6d5f7b76549125f0d94d7e630761a2111bc431fd820e163b8"},
|
||||
{file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:17b950fccb810b3293638215058e432159d2b71005c74371d784862b7e4683f3"},
|
||||
{file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4c31f53cdae6ecfa91a77820e8b151dba54ab528ba65dfd235c80b086d68a465"},
|
||||
{file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:bff1b4290a66b490a2f4719358c0cdcd9bafb6b8f061e45c7a2460866bf50c2e"},
|
||||
{file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:bc1667f8b83f48511b94671e0e441401371dfd0f0a795c7daa4a3cd1dde55bea"},
|
||||
{file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5049256f536511ee3f7e1b3f87d1d1209d327e818e6ae1365e8653d7e3abb6a6"},
|
||||
{file = "MarkupSafe-2.1.5-cp39-cp39-win32.whl", hash = "sha256:00e046b6dd71aa03a41079792f8473dc494d564611a8f89bbbd7cb93295ebdcf"},
|
||||
{file = "MarkupSafe-2.1.5-cp39-cp39-win_amd64.whl", hash = "sha256:fa173ec60341d6bb97a89f5ea19c85c5643c1e7dedebc22f5181eb73573142c5"},
|
||||
{file = "MarkupSafe-2.1.5.tar.gz", hash = "sha256:d283d37a890ba4c1ae73ffadf8046435c76e7bc2247bbb63c00bd1a709c6544b"},
|
||||
{file = "MarkupSafe-3.0.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7e94c425039cde14257288fd61dcfb01963e658efbc0ff54f5306b06054700f8"},
|
||||
{file = "MarkupSafe-3.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9e2d922824181480953426608b81967de705c3cef4d1af983af849d7bd619158"},
|
||||
{file = "MarkupSafe-3.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:38a9ef736c01fccdd6600705b09dc574584b89bea478200c5fbf112a6b0d5579"},
|
||||
{file = "MarkupSafe-3.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bbcb445fa71794da8f178f0f6d66789a28d7319071af7a496d4d507ed566270d"},
|
||||
{file = "MarkupSafe-3.0.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:57cb5a3cf367aeb1d316576250f65edec5bb3be939e9247ae594b4bcbc317dfb"},
|
||||
{file = "MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:3809ede931876f5b2ec92eef964286840ed3540dadf803dd570c3b7e13141a3b"},
|
||||
{file = "MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e07c3764494e3776c602c1e78e298937c3315ccc9043ead7e685b7f2b8d47b3c"},
|
||||
{file = "MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b424c77b206d63d500bcb69fa55ed8d0e6a3774056bdc4839fc9298a7edca171"},
|
||||
{file = "MarkupSafe-3.0.2-cp310-cp310-win32.whl", hash = "sha256:fcabf5ff6eea076f859677f5f0b6b5c1a51e70a376b0579e0eadef8db48c6b50"},
|
||||
{file = "MarkupSafe-3.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:6af100e168aa82a50e186c82875a5893c5597a0c1ccdb0d8b40240b1f28b969a"},
|
||||
{file = "MarkupSafe-3.0.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9025b4018f3a1314059769c7bf15441064b2207cb3f065e6ea1e7359cb46db9d"},
|
||||
{file = "MarkupSafe-3.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:93335ca3812df2f366e80509ae119189886b0f3c2b81325d39efdb84a1e2ae93"},
|
||||
{file = "MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2cb8438c3cbb25e220c2ab33bb226559e7afb3baec11c4f218ffa7308603c832"},
|
||||
{file = "MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a123e330ef0853c6e822384873bef7507557d8e4a082961e1defa947aa59ba84"},
|
||||
{file = "MarkupSafe-3.0.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e084f686b92e5b83186b07e8a17fc09e38fff551f3602b249881fec658d3eca"},
|
||||
{file = "MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d8213e09c917a951de9d09ecee036d5c7d36cb6cb7dbaece4c71a60d79fb9798"},
|
||||
{file = "MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:5b02fb34468b6aaa40dfc198d813a641e3a63b98c2b05a16b9f80b7ec314185e"},
|
||||
{file = "MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:0bff5e0ae4ef2e1ae4fdf2dfd5b76c75e5c2fa4132d05fc1b0dabcd20c7e28c4"},
|
||||
{file = "MarkupSafe-3.0.2-cp311-cp311-win32.whl", hash = "sha256:6c89876f41da747c8d3677a2b540fb32ef5715f97b66eeb0c6b66f5e3ef6f59d"},
|
||||
{file = "MarkupSafe-3.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:70a87b411535ccad5ef2f1df5136506a10775d267e197e4cf531ced10537bd6b"},
|
||||
{file = "MarkupSafe-3.0.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:9778bd8ab0a994ebf6f84c2b949e65736d5575320a17ae8984a77fab08db94cf"},
|
||||
{file = "MarkupSafe-3.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:846ade7b71e3536c4e56b386c2a47adf5741d2d8b94ec9dc3e92e5e1ee1e2225"},
|
||||
{file = "MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1c99d261bd2d5f6b59325c92c73df481e05e57f19837bdca8413b9eac4bd8028"},
|
||||
{file = "MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e17c96c14e19278594aa4841ec148115f9c7615a47382ecb6b82bd8fea3ab0c8"},
|
||||
{file = "MarkupSafe-3.0.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:88416bd1e65dcea10bc7569faacb2c20ce071dd1f87539ca2ab364bf6231393c"},
|
||||
{file = "MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2181e67807fc2fa785d0592dc2d6206c019b9502410671cc905d132a92866557"},
|
||||
{file = "MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:52305740fe773d09cffb16f8ed0427942901f00adedac82ec8b67752f58a1b22"},
|
||||
{file = "MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ad10d3ded218f1039f11a75f8091880239651b52e9bb592ca27de44eed242a48"},
|
||||
{file = "MarkupSafe-3.0.2-cp312-cp312-win32.whl", hash = "sha256:0f4ca02bea9a23221c0182836703cbf8930c5e9454bacce27e767509fa286a30"},
|
||||
{file = "MarkupSafe-3.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:8e06879fc22a25ca47312fbe7c8264eb0b662f6db27cb2d3bbbc74b1df4b9b87"},
|
||||
{file = "MarkupSafe-3.0.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ba9527cdd4c926ed0760bc301f6728ef34d841f405abf9d4f959c478421e4efd"},
|
||||
{file = "MarkupSafe-3.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f8b3d067f2e40fe93e1ccdd6b2e1d16c43140e76f02fb1319a05cf2b79d99430"},
|
||||
{file = "MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:569511d3b58c8791ab4c2e1285575265991e6d8f8700c7be0e88f86cb0672094"},
|
||||
{file = "MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15ab75ef81add55874e7ab7055e9c397312385bd9ced94920f2802310c930396"},
|
||||
{file = "MarkupSafe-3.0.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f3818cb119498c0678015754eba762e0d61e5b52d34c8b13d770f0719f7b1d79"},
|
||||
{file = "MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:cdb82a876c47801bb54a690c5ae105a46b392ac6099881cdfb9f6e95e4014c6a"},
|
||||
{file = "MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:cabc348d87e913db6ab4aa100f01b08f481097838bdddf7c7a84b7575b7309ca"},
|
||||
{file = "MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:444dcda765c8a838eaae23112db52f1efaf750daddb2d9ca300bcae1039adc5c"},
|
||||
{file = "MarkupSafe-3.0.2-cp313-cp313-win32.whl", hash = "sha256:bcf3e58998965654fdaff38e58584d8937aa3096ab5354d493c77d1fdd66d7a1"},
|
||||
{file = "MarkupSafe-3.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:e6a2a455bd412959b57a172ce6328d2dd1f01cb2135efda2e4576e8a23fa3b0f"},
|
||||
{file = "MarkupSafe-3.0.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:b5a6b3ada725cea8a5e634536b1b01c30bcdcd7f9c6fff4151548d5bf6b3a36c"},
|
||||
{file = "MarkupSafe-3.0.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:a904af0a6162c73e3edcb969eeeb53a63ceeb5d8cf642fade7d39e7963a22ddb"},
|
||||
{file = "MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4aa4e5faecf353ed117801a068ebab7b7e09ffb6e1d5e412dc852e0da018126c"},
|
||||
{file = "MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0ef13eaeee5b615fb07c9a7dadb38eac06a0608b41570d8ade51c56539e509d"},
|
||||
{file = "MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d16a81a06776313e817c951135cf7340a3e91e8c1ff2fac444cfd75fffa04afe"},
|
||||
{file = "MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:6381026f158fdb7c72a168278597a5e3a5222e83ea18f543112b2662a9b699c5"},
|
||||
{file = "MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:3d79d162e7be8f996986c064d1c7c817f6df3a77fe3d6859f6f9e7be4b8c213a"},
|
||||
{file = "MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:131a3c7689c85f5ad20f9f6fb1b866f402c445b220c19fe4308c0b147ccd2ad9"},
|
||||
{file = "MarkupSafe-3.0.2-cp313-cp313t-win32.whl", hash = "sha256:ba8062ed2cf21c07a9e295d5b8a2a5ce678b913b45fdf68c32d95d6c1291e0b6"},
|
||||
{file = "MarkupSafe-3.0.2-cp313-cp313t-win_amd64.whl", hash = "sha256:e444a31f8db13eb18ada366ab3cf45fd4b31e4db1236a4448f68778c1d1a5a2f"},
|
||||
{file = "MarkupSafe-3.0.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:eaa0a10b7f72326f1372a713e73c3f739b524b3af41feb43e4921cb529f5929a"},
|
||||
{file = "MarkupSafe-3.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:48032821bbdf20f5799ff537c7ac3d1fba0ba032cfc06194faffa8cda8b560ff"},
|
||||
{file = "MarkupSafe-3.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1a9d3f5f0901fdec14d8d2f66ef7d035f2157240a433441719ac9a3fba440b13"},
|
||||
{file = "MarkupSafe-3.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:88b49a3b9ff31e19998750c38e030fc7bb937398b1f78cfa599aaef92d693144"},
|
||||
{file = "MarkupSafe-3.0.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cfad01eed2c2e0c01fd0ecd2ef42c492f7f93902e39a42fc9ee1692961443a29"},
|
||||
{file = "MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:1225beacc926f536dc82e45f8a4d68502949dc67eea90eab715dea3a21c1b5f0"},
|
||||
{file = "MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:3169b1eefae027567d1ce6ee7cae382c57fe26e82775f460f0b2778beaad66c0"},
|
||||
{file = "MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:eb7972a85c54febfb25b5c4b4f3af4dcc731994c7da0d8a0b4a6eb0640e1d178"},
|
||||
{file = "MarkupSafe-3.0.2-cp39-cp39-win32.whl", hash = "sha256:8c4e8c3ce11e1f92f6536ff07154f9d49677ebaaafc32db9db4620bc11ed480f"},
|
||||
{file = "MarkupSafe-3.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:6e296a513ca3d94054c2c881cc913116e90fd030ad1c656b3869762b754f5f8a"},
|
||||
{file = "markupsafe-3.0.2.tar.gz", hash = "sha256:ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "more-itertools"
|
||||
version = "10.4.0"
|
||||
version = "10.5.0"
|
||||
description = "More routines for operating on iterables, beyond itertools"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "more-itertools-10.4.0.tar.gz", hash = "sha256:fe0e63c4ab068eac62410ab05cccca2dc71ec44ba8ef29916a0090df061cf923"},
|
||||
{file = "more_itertools-10.4.0-py3-none-any.whl", hash = "sha256:0f7d9f83a0a8dcfa8a2694a770590d98a67ea943e3d9f5298309a484758c4e27"},
|
||||
{file = "more-itertools-10.5.0.tar.gz", hash = "sha256:5482bfef7849c25dc3c6dd53a6173ae4795da2a41a80faea6700d9f5846c5da6"},
|
||||
{file = "more_itertools-10.5.0-py3-none-any.whl", hash = "sha256:037b0d3203ce90cca8ab1defbbdac29d5f993fc20131f3664dc8d6acfa872aef"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "packaging"
|
||||
version = "24.1"
|
||||
version = "24.2"
|
||||
description = "Core utilities for Python packages"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "packaging-24.1-py3-none-any.whl", hash = "sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124"},
|
||||
{file = "packaging-24.1.tar.gz", hash = "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002"},
|
||||
{file = "packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759"},
|
||||
{file = "packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -424,6 +454,17 @@ files = [
|
||||
{file = "py-1.11.0.tar.gz", hash = "sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pycdlib"
|
||||
version = "1.14.0"
|
||||
description = "Pure python ISO manipulation library"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
files = [
|
||||
{file = "pycdlib-1.14.0-py2.py3-none-any.whl", hash = "sha256:a905827335f0066af3fd416c5cf9b1f29dffaf4d0914b714555213d1809f38d4"},
|
||||
{file = "pycdlib-1.14.0.tar.gz", hash = "sha256:8ec306b31d9c850f28c5fda52438d904edd1e8fcf862c5ffd756272efac9f422"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pytest"
|
||||
version = "5.4.3"
|
||||
@ -548,13 +589,13 @@ use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"]
|
||||
|
||||
[[package]]
|
||||
name = "rpm"
|
||||
version = "0.2.0"
|
||||
version = "0.3.1"
|
||||
description = "Shim RPM module for use in virtualenvs."
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
files = [
|
||||
{file = "rpm-0.2.0-py3-none-any.whl", hash = "sha256:4050b6033f7403be0a34f42a742c49ba74f2b0c6129f0247115b6078b24ddd71"},
|
||||
{file = "rpm-0.2.0.tar.gz", hash = "sha256:b92285f65c9ddf77678cb3e51aa67827426408fac34cdd8d537d8c14e3eaffbf"},
|
||||
{file = "rpm-0.3.1-py3-none-any.whl", hash = "sha256:6a130be8b953781bdbe652927dd9c50d2160f26344ec6e3c0defe823aeda1789"},
|
||||
{file = "rpm-0.3.1.tar.gz", hash = "sha256:d75c5dcb581f1e9c4f89cb6667e938e944c6e7c17dd96829e1553c39f3a4c961"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
@ -562,13 +603,13 @@ testing = ["tox"]
|
||||
|
||||
[[package]]
|
||||
name = "s3transfer"
|
||||
version = "0.10.2"
|
||||
version = "0.10.3"
|
||||
description = "An Amazon S3 Transfer Manager"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "s3transfer-0.10.2-py3-none-any.whl", hash = "sha256:eca1c20de70a39daee580aef4986996620f365c4e0fda6a86100231d62f1bf69"},
|
||||
{file = "s3transfer-0.10.2.tar.gz", hash = "sha256:0711534e9356d3cc692fdde846b4a1e4b0cb6519971860796e6bc4c7aea00ef6"},
|
||||
{file = "s3transfer-0.10.3-py3-none-any.whl", hash = "sha256:263ed587a5803c6c708d3ce44dc4dfedaab4c1a32e8329bab818933d79ddcf5d"},
|
||||
{file = "s3transfer-0.10.3.tar.gz", hash = "sha256:4f50ed74ab84d474ce614475e0b8d5047ff080810aac5d01ea25231cfc944b0c"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
@ -601,13 +642,13 @@ files = [
|
||||
|
||||
[[package]]
|
||||
name = "urllib3"
|
||||
version = "1.26.19"
|
||||
version = "1.26.20"
|
||||
description = "HTTP library with thread-safe connection pooling, file post, and more."
|
||||
optional = false
|
||||
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7"
|
||||
files = [
|
||||
{file = "urllib3-1.26.19-py2.py3-none-any.whl", hash = "sha256:37a0344459b199fce0e80b0d3569837ec6b6937435c5244e7fd73fa6006830f3"},
|
||||
{file = "urllib3-1.26.19.tar.gz", hash = "sha256:3e3d753a8618b86d7de333b4223005f68720bcd6a7d2bcb9fbd2229ec7c1e429"},
|
||||
{file = "urllib3-1.26.20-py2.py3-none-any.whl", hash = "sha256:0ed14ccfbf1c30a9072c7ca157e4319b70d65f623e91e7b32fadb2853431016e"},
|
||||
{file = "urllib3-1.26.20.tar.gz", hash = "sha256:40c2dc0c681e47eb8f90e7e27bf6ff7df2e677421fd46756da1161c39ca70d32"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
@ -617,13 +658,13 @@ socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"]
|
||||
|
||||
[[package]]
|
||||
name = "urllib3"
|
||||
version = "2.2.2"
|
||||
version = "2.2.3"
|
||||
description = "HTTP library with thread-safe connection pooling, file post, and more."
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "urllib3-2.2.2-py3-none-any.whl", hash = "sha256:a448b2f64d686155468037e1ace9f2d2199776e17f0a46610480d311f73e3472"},
|
||||
{file = "urllib3-2.2.2.tar.gz", hash = "sha256:dd505485549a7a552833da5e6063639d0d177c04f23bc3864e41e5dc5f612168"},
|
||||
{file = "urllib3-2.2.3-py3-none-any.whl", hash = "sha256:ca899ca043dcb1bafa3e262d73aa25c465bfb49e0bd9dd5d59f1d0acba2f8fac"},
|
||||
{file = "urllib3-2.2.3.tar.gz", hash = "sha256:e7d814a81dad81e6caf2ec9fdedb284ecc9c73076b62654547cc64ccdcae26e9"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
@ -645,31 +686,35 @@ files = [
|
||||
|
||||
[[package]]
|
||||
name = "xmltodict"
|
||||
version = "0.13.0"
|
||||
version = "0.14.2"
|
||||
description = "Makes working with XML feel like you are working with JSON"
|
||||
optional = false
|
||||
python-versions = ">=3.4"
|
||||
python-versions = ">=3.6"
|
||||
files = [
|
||||
{file = "xmltodict-0.13.0-py2.py3-none-any.whl", hash = "sha256:aa89e8fd76320154a40d19a0df04a4695fb9dc5ba977cbb68ab3e4eb225e7852"},
|
||||
{file = "xmltodict-0.13.0.tar.gz", hash = "sha256:341595a488e3e01a85a9d8911d8912fd922ede5fecc4dce437eb4b6c8d037e56"},
|
||||
{file = "xmltodict-0.14.2-py2.py3-none-any.whl", hash = "sha256:20cc7d723ed729276e808f26fb6b3599f786cbc37e06c65e192ba77c40f20aac"},
|
||||
{file = "xmltodict-0.14.2.tar.gz", hash = "sha256:201e7c28bb210e374999d1dde6382923ab0ed1a8a5faeece48ab525b7810a553"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zipp"
|
||||
version = "3.20.0"
|
||||
version = "3.21.0"
|
||||
description = "Backport of pathlib-compatible object wrapper for zip files"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
python-versions = ">=3.9"
|
||||
files = [
|
||||
{file = "zipp-3.20.0-py3-none-any.whl", hash = "sha256:58da6168be89f0be59beb194da1250516fdaa062ccebd30127ac65d30045e10d"},
|
||||
{file = "zipp-3.20.0.tar.gz", hash = "sha256:0145e43d89664cfe1a2e533adc75adafed82fe2da404b4bbb6b026c0157bdb31"},
|
||||
{file = "zipp-3.21.0-py3-none-any.whl", hash = "sha256:ac1bbe05fd2991f160ebce24ffbac5f6d11d83dc90891255885223d42b3cd931"},
|
||||
{file = "zipp-3.21.0.tar.gz", hash = "sha256:2c9958f6430a2040341a52eb608ed6dd93ef4392e02ffe219417c1b28b5dd1f4"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)"]
|
||||
cover = ["pytest-cov"]
|
||||
doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"]
|
||||
test = ["big-O", "importlib-resources", "jaraco.functools", "jaraco.itertools", "jaraco.test", "more-itertools", "pytest (>=6,!=8.1.*)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-ignore-flaky", "pytest-mypy", "pytest-ruff (>=0.2.1)"]
|
||||
enabler = ["pytest-enabler (>=2.2)"]
|
||||
test = ["big-O", "importlib-resources", "jaraco.functools", "jaraco.itertools", "jaraco.test", "more-itertools", "pytest (>=6,!=8.1.*)", "pytest-ignore-flaky"]
|
||||
type = ["pytest-mypy"]
|
||||
|
||||
[metadata]
|
||||
lock-version = "2.0"
|
||||
python-versions = ">=3.9,<4"
|
||||
content-hash = "4ef7a9006f0f5485e840d95417574a6dc2f25981fceeaf938690eccd9ce0c73e"
|
||||
content-hash = "60742cc3e07d8fe7b8bbe48db7be51cb2b5e09c194eedb0c4aef064f2d2c5eba"
|
||||
|
@ -1,23 +1,25 @@
|
||||
[tool.poetry]
|
||||
name = "empanadas"
|
||||
version = "0.7.0"
|
||||
version = "0.7.1"
|
||||
description = "hand crafted ISOs with love and spice"
|
||||
authors = ["Louis Abel <label@rockylinux.org>", "Neil Hanlon <neil@rockylinux.org>"]
|
||||
|
||||
[tool.poetry.dependencies]
|
||||
python = ">=3.9,<4"
|
||||
rpm = ">=0.1.0"
|
||||
MarkupSafe = "==2.1.5"
|
||||
PyYAML = "~6.0.1"
|
||||
MarkupSafe = "==3.0.2"
|
||||
PyYAML = "~6.0.2"
|
||||
Jinja2 = "~3"
|
||||
productmd = "~1.38"
|
||||
importlib-resources = "^5.8.0"
|
||||
boto3 = "^1.34.110"
|
||||
xmltodict = "^0.13.0"
|
||||
boto3 = "^1.35.55"
|
||||
xmltodict = "^0.14.2"
|
||||
requests = "^2.32.2"
|
||||
kobo = "^0.36.0"
|
||||
attrs = "^23.2.0"
|
||||
kobo = "^0.37.0"
|
||||
attrs = "^24.2.0"
|
||||
GitPython = ">=3.1.30"
|
||||
file-magic = ">=0.4.0"
|
||||
pycdlib = ">=1.14.0"
|
||||
|
||||
[tool.poetry.dev-dependencies]
|
||||
pytest = "~5"
|
||||
@ -39,16 +41,6 @@ generate-compose = "empanadas.scripts.generate_compose:run"
|
||||
peridot-repoclosure = "empanadas.scripts.peridot_repoclosure:run"
|
||||
refresh-all-treeinfo = "empanadas.scripts.refresh_all_treeinfo:run"
|
||||
|
||||
[tool.pylint.main]
|
||||
init-hook ="""
|
||||
try:
|
||||
import pylint_venv
|
||||
except ImportError:
|
||||
pass
|
||||
else:
|
||||
pylint_venv.inithook()
|
||||
"""
|
||||
|
||||
[build-system]
|
||||
requires = ["poetry-core>=1.0.0"]
|
||||
build-backend = "poetry.core.masonry.api"
|
||||
|
@ -1,2 +0,0 @@
|
||||
[pycodestyle]
|
||||
max-line-length = 160
|
@ -47,7 +47,6 @@ class common:
|
||||
'CRB': ['aarch64', 'ppc64le', 's390x', 'x86_64'],
|
||||
'HighAvailability': ['aarch64', 'ppc64le', 's390x', 'x86_64'],
|
||||
'NFV': ['x86_64'],
|
||||
'ResilientStorage': ['ppc64le', 's390x', 'x86_64'],
|
||||
'RT': ['x86_64'],
|
||||
'SAP': ['ppc64le', 's390x', 'x86_64'],
|
||||
'SAPHANA': ['ppc64le', 'x86_64']
|
||||
|
@ -1,6 +1,6 @@
|
||||
# To be sourced by scripts to use
|
||||
|
||||
REPO=("BaseOS" "AppStream" "CRB" "HighAvailability" "ResilientStorage" "NFV" "RT" "SAP" "SAPHANA")
|
||||
REPO=("BaseOS" "AppStream" "CRB" "HighAvailability" "NFV" "RT" "SAP" "SAPHANA")
|
||||
ARCH=("aarch64" "ppc64le" "s390x" "x86_64")
|
||||
|
||||
MAJOR="10"
|
||||
|
@ -9,6 +9,12 @@ else
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -f /usr/bin/dnf4 ]; then
|
||||
SAFEDNF=/usr/bin/dnf4
|
||||
else
|
||||
SAFEDNF=/usr/bin/dnf
|
||||
fi
|
||||
|
||||
export RLVER=$MAJOR
|
||||
source common
|
||||
|
||||
@ -20,7 +26,7 @@ eln_repo_url="${ELN_KOJI_REPO}/${tag_template}/latest"
|
||||
|
||||
pushd "${tmpdir}" || { echo "Could not change directory"; exit 1; }
|
||||
for y in "${ARCH[@]}"; do
|
||||
repodatas=( $(dnf reposync --repofrompath ${tag_template},${eln_repo_url}/${y} --download-metadata --repoid=${tag_template} -p ${tag_template}/${y} --forcearch ${y} --norepopath --remote-time --assumeyes -u | grep repodata) )
|
||||
repodatas=( $($SAFEDNF reposync --repofrompath ${tag_template},${eln_repo_url}/${y} --download-metadata --repoid=${tag_template} -p ${tag_template}/${y} --forcearch ${y} --norepopath --remote-time --assumeyes -u | grep repodata) )
|
||||
mkdir -p "${tag_template}/${y}/repodata"
|
||||
pushd "${tag_template}/${y}/repodata" || { echo "Could not change directory"; exit 1; }
|
||||
for z in "${repodatas[@]}"; do
|
||||
|
@ -9,6 +9,12 @@ else
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -f /usr/bin/dnf4 ]; then
|
||||
SAFEDNF=/usr/bin/dnf4
|
||||
else
|
||||
SAFEDNF=/usr/bin/dnf
|
||||
fi
|
||||
|
||||
export RLVER=$MAJOR
|
||||
source common
|
||||
|
||||
@ -20,7 +26,7 @@ stream_repo_url="${STREAM_KOJI_REPO}/${tag_template}/latest"
|
||||
|
||||
pushd "${tmpdir}" || { echo "Could not change directory"; exit 1; }
|
||||
for y in "${ARCH[@]}"; do
|
||||
repodatas=( $(dnf reposync --repofrompath ${tag_template},${stream_repo_url}/${y} --download-metadata --repoid=${tag_template} -p ${tag_template}/${y} --forcearch ${y} --norepopath --remote-time --assumeyes -u | grep repodata) )
|
||||
repodatas=( $($SAFEDNF reposync --repofrompath ${tag_template},${stream_repo_url}/${y} --download-metadata --repoid=${tag_template} -p ${tag_template}/${y} --forcearch ${y} --norepopath --remote-time --assumeyes -u | grep repodata) )
|
||||
mkdir -p "${tag_template}/${y}/repodata"
|
||||
pushd "${tag_template}/${y}/repodata" || { echo "Could not change directory"; exit 1; }
|
||||
for z in "${repodatas[@]}"; do
|
||||
|
@ -10,6 +10,12 @@ else
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -f /usr/bin/dnf4 ]; then
|
||||
SAFEDNF=/usr/bin/dnf4
|
||||
else
|
||||
SAFEDNF=/usr/bin/dnf
|
||||
fi
|
||||
|
||||
# Verify the date format
|
||||
echo "${DATE}" | grep -Eq '[0-9]+\.[0-9]'
|
||||
grep_val=$?
|
||||
|
@ -9,6 +9,12 @@ else
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -f /usr/bin/dnf4 ]; then
|
||||
SAFEDNF=/usr/bin/dnf4
|
||||
else
|
||||
SAFEDNF=/usr/bin/dnf
|
||||
fi
|
||||
|
||||
export RLVER=$MAJOR
|
||||
source common
|
||||
|
||||
|
@ -9,6 +9,12 @@ else
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -f /usr/bin/dnf4 ]; then
|
||||
SAFEDNF=/usr/bin/dnf4
|
||||
else
|
||||
SAFEDNF=/usr/bin/dnf
|
||||
fi
|
||||
|
||||
export RLVER=$MAJOR
|
||||
source common
|
||||
|
||||
@ -21,7 +27,7 @@ stream_repo_url="https://kojidev.rockylinux.org/kojifiles/repos/${tag_template}/
|
||||
|
||||
pushd "${tmpdir}" || { echo "Could not change directory"; exit 1; }
|
||||
for y in x86_64 aarch64 i386; do
|
||||
repodatas=( $(dnf reposync --repofrompath ${str_template},${stream_repo_url}/${y} --download-metadata --repoid=${str_template} -p ${str_template}/${y} --forcearch ${y} --norepopath --remote-time --assumeyes -u | grep repodata) )
|
||||
repodatas=( $($SAFEDNF reposync --repofrompath ${str_template},${stream_repo_url}/${y} --download-metadata --repoid=${str_template} -p ${str_template}/${y} --forcearch ${y} --norepopath --remote-time --assumeyes -u | grep repodata) )
|
||||
mkdir -p "${str_template}/${y}/repodata"
|
||||
pushd "${str_template}/${y}/repodata" || { echo "Could not change directory"; exit 1; }
|
||||
for z in "${repodatas[@]}"; do
|
||||
|
@ -10,6 +10,12 @@ else
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -f /usr/bin/dnf4 ]; then
|
||||
SAFEDNF=/usr/bin/dnf4
|
||||
else
|
||||
SAFEDNF=/usr/bin/dnf
|
||||
fi
|
||||
|
||||
# Verify the date format
|
||||
echo "${DATE}" | grep -Eq '[0-9]+\.[0-9]'
|
||||
grep_val=$?
|
||||
@ -31,7 +37,7 @@ pushd "${tmpdir}" || { echo "Could not change directory"; exit 1; }
|
||||
for x in "${REPO[@]}"; do
|
||||
echo "Working on ${x}"
|
||||
for y in "${ARCH[@]}"; do
|
||||
repodatas=( $(dnf reposync --repofrompath ${x},${stream_compose_url}/${x}/${y}/os --download-metadata --repoid=${x} -p ${x}/${y} --forcearch ${y} --norepopath --remote-time --assumeyes -u | grep repodata) )
|
||||
repodatas=( $($SAFEDNF reposync --repofrompath ${x},${stream_compose_url}/${x}/${y}/os --download-metadata --repoid=${x} -p ${x}/${y} --forcearch ${y} --norepopath --remote-time --assumeyes -u | grep repodata) )
|
||||
mkdir -p "${x}/${y}/repodata"
|
||||
pushd "${x}/${y}/repodata" || { echo "Could not change directory"; exit 1; }
|
||||
for z in "${repodatas[@]}"; do
|
||||
|
@ -10,6 +10,12 @@ else
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -f /usr/bin/dnf4 ]; then
|
||||
SAFEDNF=/usr/bin/dnf4
|
||||
else
|
||||
SAFEDNF=/usr/bin/dnf
|
||||
fi
|
||||
|
||||
# Verify the date format
|
||||
echo "${DATE}" | grep -Eq '[0-9]+\.[0-9]'
|
||||
grep_val=$?
|
||||
@ -27,11 +33,17 @@ current=$(pwd)
|
||||
tmpdir=$(mktemp -d)
|
||||
stream_compose_url="https://composes.stream.centos.org/stream-${MAJOR}/production/CentOS-Stream-${MAJOR}-${DATE}/compose"
|
||||
|
||||
if [ -f /usr/bin/dnf4 ]; then
|
||||
SAFEDNF=/usr/bin/dnf4
|
||||
else
|
||||
SAFEDNF=/usr/bin/dnf
|
||||
fi
|
||||
|
||||
pushd "${tmpdir}" || { echo "Could not change directory"; exit 1; }
|
||||
for x in "${REPO[@]}"; do
|
||||
echo "Working on ${x}"
|
||||
for y in "${ARCH[@]}"; do
|
||||
repodatas=( $(dnf reposync --repofrompath ${x},${stream_compose_url}/${x}/${y}/os --download-metadata --repoid=${x} -p ${x}/${y} --forcearch ${y} --norepopath --remote-time --assumeyes -u | grep repodata) )
|
||||
repodatas=( $($SAFEDNF reposync --repofrompath ${x},${stream_compose_url}/${x}/${y}/os --download-metadata --repoid=${x} -p ${x}/${y} --forcearch ${y} --norepopath --remote-time --assumeyes -u | grep repodata) )
|
||||
mkdir -p "${x}/${y}/repodata"
|
||||
pushd "${x}/${y}/repodata" || { echo "Could not change directory"; exit 1; }
|
||||
for z in "${repodatas[@]}"; do
|
||||
|
@ -9,6 +9,12 @@ else
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -f /usr/bin/dnf4 ]; then
|
||||
SAFEDNF=/usr/bin/dnf4
|
||||
else
|
||||
SAFEDNF=/usr/bin/dnf
|
||||
fi
|
||||
|
||||
export RLVER="${MAJOR}"
|
||||
source common
|
||||
|
||||
@ -20,7 +26,7 @@ stream_repo_url="${STREAM_KOJI_REPO}/${tag_template}/latest"
|
||||
|
||||
pushd "${tmpdir}" || { echo "Could not change directory"; exit 1; }
|
||||
for y in "${ARCH[@]}"; do
|
||||
repodatas=( $(dnf reposync --repofrompath ${tag_template},${stream_repo_url}/${y} --download-metadata --repoid=${tag_template} -p ${tag_template}/${y} --forcearch ${y} --norepopath --remote-time --assumeyes -u | grep repodata) )
|
||||
repodatas=( $($SAFEDNF reposync --repofrompath ${tag_template},${stream_repo_url}/${y} --download-metadata --repoid=${tag_template} -p ${tag_template}/${y} --forcearch ${y} --norepopath --remote-time --assumeyes -u | grep repodata) )
|
||||
mkdir -p "${tag_template}/${y}/repodata"
|
||||
pushd "${tag_template}/${y}/repodata" || { echo "Could not change directory"; exit 1; }
|
||||
for z in "${repodatas[@]}"; do
|
||||
|
@ -13,6 +13,12 @@ if [ -n "$2" ] && [[ "$2" == "lh" ]]; then
|
||||
export LH="lh"
|
||||
fi
|
||||
|
||||
if [ -f /usr/bin/dnf4 ]; then
|
||||
SAFEDNF=/usr/bin/dnf4
|
||||
else
|
||||
SAFEDNF=/usr/bin/dnf
|
||||
fi
|
||||
|
||||
export RLVER="${MAJOR}"
|
||||
source common
|
||||
|
||||
@ -24,7 +30,7 @@ peridot_repo_url="${PERIDOT_REPO}/${PERIDOT_PROJECT_ID}/repo/${tag_template}"
|
||||
|
||||
pushd "${tmpdir}" || { echo "Could not change directory"; exit 1; }
|
||||
for y in "${ARCH[@]}"; do
|
||||
repodatas=( $(dnf reposync --repofrompath ${tag_template},${peridot_repo_url}/${y} --download-metadata --repoid=${tag_template} -p ${tag_template}/${y} --forcearch ${y} --norepopath --remote-time --assumeyes -u | grep repodata) )
|
||||
repodatas=( $($SAFEDNF reposync --repofrompath ${tag_template},${peridot_repo_url}/${y} --download-metadata --repoid=${tag_template} -p ${tag_template}/${y} --forcearch ${y} --norepopath --remote-time --assumeyes -u | grep repodata) )
|
||||
mkdir -p "${tag_template}/${y}/repodata"
|
||||
pushd "${tag_template}/${y}/repodata" || { echo "Could not change directory"; exit 1; }
|
||||
for z in "${repodatas[@]}"; do
|
||||
|
@ -18,6 +18,12 @@ if [ "$grep_val" -ne 0 ]; then
|
||||
echo "Date format incorrect. You must use: YYYYMMDD.X"
|
||||
fi
|
||||
|
||||
if [ -f /usr/bin/dnf4 ]; then
|
||||
SAFEDNF=/usr/bin/dnf4
|
||||
else
|
||||
SAFEDNF=/usr/bin/dnf
|
||||
fi
|
||||
|
||||
export RLVER="${MAJOR}"
|
||||
source common
|
||||
|
||||
@ -30,7 +36,7 @@ pushd "${tmpdir}" || { echo "Could not change directory"; exit 1; }
|
||||
for x in "${REPO[@]}"; do
|
||||
echo "Working on ${x}"
|
||||
for y in "${ARCH[@]}"; do
|
||||
repodatas=( $(dnf reposync --repofrompath ${x},${stream_compose_url}/${x}/${y}/os --download-metadata --repoid=${x} -p ${x}/${y} --forcearch ${y} --norepopath --remote-time --assumeyes -u | grep repodata) )
|
||||
repodatas=( $($SAFEDNF reposync --repofrompath ${x},${stream_compose_url}/${x}/${y}/os --download-metadata --repoid=${x} -p ${x}/${y} --forcearch ${y} --norepopath --remote-time --assumeyes -u | grep repodata) )
|
||||
mkdir -p "${x}/${y}/repodata"
|
||||
pushd "${x}/${y}/repodata" || { echo "Could not change directory"; exit 1; }
|
||||
for z in "${repodatas[@]}"; do
|
||||
|
@ -18,6 +18,12 @@ if [ "$grep_val" -ne 0 ]; then
|
||||
echo "Date format incorrect. You must use: YYYYMMDD.X"
|
||||
fi
|
||||
|
||||
if [ -f /usr/bin/dnf4 ]; then
|
||||
SAFEDNF=/usr/bin/dnf4
|
||||
else
|
||||
SAFEDNF=/usr/bin/dnf
|
||||
fi
|
||||
|
||||
export RLVER="${MAJOR}"
|
||||
source common
|
||||
|
||||
@ -31,7 +37,7 @@ pushd "${tmpdir}" || { echo "Could not change directory"; exit 1; }
|
||||
for x in "${REPO[@]}"; do
|
||||
echo "Working on ${x}"
|
||||
for y in "${ARCH[@]}"; do
|
||||
repodatas=( $(dnf reposync --repofrompath ${x},${stream_compose_url}/${x}/${y}/os --download-metadata --repoid=${x} -p ${x}/${y} --forcearch ${y} --norepopath --remote-time --assumeyes -u | grep repodata) )
|
||||
repodatas=( $($SAFEDNF reposync --repofrompath ${x},${stream_compose_url}/${x}/${y}/os --download-metadata --repoid=${x} -p ${x}/${y} --forcearch ${y} --norepopath --remote-time --assumeyes -u | grep repodata) )
|
||||
mkdir -p "${x}/${y}/repodata"
|
||||
pushd "${x}/${y}/repodata" || { echo "Could not change directory"; exit 1; }
|
||||
for z in "${repodatas[@]}"; do
|
||||
|
@ -12,6 +12,8 @@ IGNORES = [
|
||||
'insights-client',
|
||||
'lorax-templates-rhel',
|
||||
'shim',
|
||||
'shim-unsigned-x64',
|
||||
'shim-unsigned-aarch64',
|
||||
'redhat-cloud-client-configuration',
|
||||
'rhc',
|
||||
'rhc-worker-playbook',
|
||||
|
@ -20,6 +20,9 @@ REPOS = switcher.rlver(results.version,
|
||||
# Source packages we do not ship or are rocky branded
|
||||
IGNORES = [
|
||||
'insights-client',
|
||||
'shim',
|
||||
'shim-unsigned-x64',
|
||||
'shim-unsigned-aarch64',
|
||||
'redhat-cloud-client-configuration',
|
||||
'rhc',
|
||||
'rhc-worker-playbook',
|
||||
|
@ -304,7 +304,7 @@ class IPAAudit:
|
||||
}
|
||||
|
||||
print('User Information')
|
||||
print('----------------------------------------')
|
||||
print('------------------------------------------')
|
||||
for key, value in starter_user.items():
|
||||
if len(value) > 0:
|
||||
print(f'{key: <16}{value}')
|
||||
@ -312,14 +312,54 @@ class IPAAudit:
|
||||
|
||||
if deep:
|
||||
group_list = [] if not user_results.get('memberof_group', None) else user_results['memberof_group']
|
||||
IPAAudit.user_deep_list(api, name, group_list)
|
||||
hbac_list = [] if not user_results.get('memberof_hbacrule', None) else user_results['memberof_hbacrule']
|
||||
IPAAudit.user_deep_list(api, name, group_list, hbac_list)
|
||||
|
||||
@staticmethod
|
||||
def group_pull(api, name, deep):
|
||||
"""
|
||||
Gets requested rbac info
|
||||
"""
|
||||
print()
|
||||
try:
|
||||
group_results = IPAQuery.group_data(api, name)
|
||||
except:
|
||||
print(f'Could not find {name}', sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
group_name = '' if not group_results.get('cn', None) else group_results['cn'][0]
|
||||
group_gidnum = '' if not group_results.get('gidnumber', None) else group_results['gidnumber'][0]
|
||||
group_members_direct = [] if not group_results.get('member_user', None) else group_results['member_user']
|
||||
group_members_indirect = [] if not group_results.get('memberindirect_user', None) else group_results['memberindirect_user']
|
||||
group_members = list(group_members_direct) + list(group_members_indirect)
|
||||
num_of_group_members = str(len(group_members))
|
||||
|
||||
group_hbacs_direct = [] if not group_results.get('memberof_hbacrule', None) else group_results['memberof_hbacrule']
|
||||
group_hbacs_indirect = [] if not group_results.get('memberofindirect_hbacrule', None) else group_results['memberofindirect_hbacrule']
|
||||
group_hbacs = list(group_hbacs_direct) + list(group_hbacs_indirect)
|
||||
num_of_hbacs = str(len(group_hbacs))
|
||||
|
||||
group_sudo_direct = [] if not group_results.get('memberof_sudorule', None) else group_results['memberof_sudorule']
|
||||
group_sudo_indirect = [] if not group_results.get('memberofindirect_sudorule', None) else group_results['memberofindirect_sudorule']
|
||||
group_sudos = list(group_sudo_direct) + list(group_sudo_indirect)
|
||||
num_of_sudos = str(len(group_sudos))
|
||||
|
||||
starter_group = {
|
||||
'Group name': group_name,
|
||||
'GID': group_gidnum,
|
||||
'Number of Users': num_of_group_members,
|
||||
'Number of HBAC Rules': num_of_hbacs,
|
||||
'Number of SUDO Rules': num_of_sudos,
|
||||
}
|
||||
|
||||
print('Group Information')
|
||||
print('------------------------------------------')
|
||||
for key, value in starter_group.items():
|
||||
if len(value) > 0:
|
||||
print(f'{key: <24}{value}')
|
||||
print('')
|
||||
|
||||
if deep:
|
||||
IPAAudit.group_deep_list(api, name, group_members, group_hbacs, group_sudos)
|
||||
|
||||
@staticmethod
|
||||
def hbac_pull(api, name, deep):
|
||||
@ -463,14 +503,13 @@ class IPAAudit:
|
||||
print(f'{key: <24}{value}')
|
||||
|
||||
@staticmethod
|
||||
def user_deep_list(api, user, groups):
|
||||
def user_deep_list(api, user, groups, hbacs):
|
||||
"""
|
||||
Does a recursive dig on a user
|
||||
"""
|
||||
hbac_rule_list = []
|
||||
hbac_rule_list = list(hbacs)
|
||||
hbac_rule_all_hosts = []
|
||||
host_list = []
|
||||
hostgroup_list = []
|
||||
for group in groups:
|
||||
group_results = IPAQuery.group_data(api, group)
|
||||
hbac_list = [] if not group_results.get('memberof_hbacrule', None) else group_results['memberof_hbacrule']
|
||||
@ -481,12 +520,13 @@ class IPAAudit:
|
||||
# TODO: Add HBAC list (including services)
|
||||
# TODO: Add RBAC list
|
||||
|
||||
hbac_hosts = []
|
||||
hbac_host_dict = {}
|
||||
for hbac in hbac_rule_list:
|
||||
hbac_hosts = []
|
||||
hbac_results = IPAQuery.hbac_data(api, hbac)
|
||||
hbac_host_list = [] if not hbac_results.get('memberhost_host', None) else hbac_results['memberhost_host']
|
||||
hbac_hostgroup_list = [] if not hbac_results.get('memberhost_hostgroup', None) else hbac_results['memberhost_hostgroup']
|
||||
if hbac_results.get('servicecategory'):
|
||||
if hbac_results.get('hostcategory'):
|
||||
hbac_rule_all_hosts.append(hbac)
|
||||
|
||||
for host in hbac_host_list:
|
||||
@ -497,19 +537,29 @@ class IPAAudit:
|
||||
host_list = [] if not hostgroup_data.get('member_host', None) else hostgroup_data['member_host']
|
||||
hbac_hosts.extend(host_list)
|
||||
|
||||
new_hbac_hosts = sorted(set(hbac_hosts))
|
||||
hbac_host_dict[hbac] = hbac_hosts
|
||||
|
||||
#new_hbac_hosts = sorted(set(hbac_hosts))
|
||||
print('User Has Access To These Hosts')
|
||||
print('------------------------------------------')
|
||||
for hhost in new_hbac_hosts:
|
||||
print(hhost)
|
||||
if len(hbac_rule_all_hosts) > 0:
|
||||
print('!! Notice: User has access to ALL hosts from the following rules:')
|
||||
hbac_rule_all_hosts = sorted(set(hbac_rule_all_hosts))
|
||||
for allrule in hbac_rule_all_hosts:
|
||||
print(allrule)
|
||||
else:
|
||||
for hrule in hbac_host_dict:
|
||||
print()
|
||||
print(f'HBAC Rule: {hrule}')
|
||||
print('==========================================')
|
||||
for h in hbac_host_dict[hrule]:
|
||||
print(h)
|
||||
|
||||
if len(hbac_host_dict[hrule]) == 0:
|
||||
print('(No hosts set for this rule)')
|
||||
|
||||
@staticmethod
|
||||
def group_deep_list(api, group):
|
||||
def group_deep_list(api, group, members, hbacs, sudos):
|
||||
"""
|
||||
Does a recursive dig on a group
|
||||
"""
|
||||
|
@ -3,19 +3,19 @@
|
||||
# Revision must always start with a major number
|
||||
case "${RLREL}" in
|
||||
stable)
|
||||
REVISION=9.4
|
||||
PREREV=9.3
|
||||
REVISION=9.5
|
||||
PREREV=9.4
|
||||
APPEND_TO_DIR="-RC1"
|
||||
;;
|
||||
beta)
|
||||
REVISION=9.5
|
||||
PREREV=9.4
|
||||
REVISION=9.6
|
||||
PREREV=9.5
|
||||
APPEND_TO_DIR="-beta"
|
||||
COMPOSE_APPEND="-beta"
|
||||
;;
|
||||
lh)
|
||||
REVISION=9.5
|
||||
PREREV=9.4
|
||||
REVISION=9.6
|
||||
PREREV=9.5
|
||||
APPEND_TO_DIR="-lookahead"
|
||||
COMPOSE_APPEND="-lookahead"
|
||||
;;
|
||||
|
Loading…
Reference in New Issue
Block a user