2023-03-24 21:29:43 +00:00
|
|
|
#!/usr/bin/python3
|
|
|
|
|
|
|
|
import time
|
|
|
|
import os
|
|
|
|
import subprocess
|
|
|
|
import boto3
|
2023-07-19 23:16:04 +00:00
|
|
|
import io
|
2023-03-24 21:29:43 +00:00
|
|
|
import json
|
|
|
|
import click
|
|
|
|
import sys
|
|
|
|
import requests
|
|
|
|
from datetime import date
|
|
|
|
import shutil
|
2023-07-14 19:55:24 +00:00
|
|
|
import xml.etree.ElementTree as ET
|
2023-03-24 21:29:43 +00:00
|
|
|
|
|
|
|
|
|
|
|
def fail(message):
|
|
|
|
click.echo(message, err=True)
|
|
|
|
sys.exit(1)
|
|
|
|
|
|
|
|
|
2023-07-14 19:55:24 +00:00
|
|
|
tree = ET.parse("config.xml")
|
|
|
|
for e in tree.getroot().iter("release-version"):
|
|
|
|
RELEASE = e.text
|
|
|
|
break
|
|
|
|
|
2023-03-24 21:29:43 +00:00
|
|
|
|
|
|
|
if not RELEASE:
|
2023-07-14 19:55:24 +00:00
|
|
|
fail("Could not parse release from config.xml")
|
2023-03-24 21:29:43 +00:00
|
|
|
|
2023-07-20 18:42:05 +00:00
|
|
|
if os.path.exists("buildver"):
|
|
|
|
with open("buildver", "r") as f:
|
|
|
|
BUILDVER = f.read().strip()
|
|
|
|
else:
|
|
|
|
BUILDVER = date.today().strftime("%Y%m%d%H%m")
|
|
|
|
with open("buildver", "w") as f:
|
|
|
|
f.write(BUILDVER)
|
2023-05-09 16:58:00 +00:00
|
|
|
|
2023-03-24 21:29:43 +00:00
|
|
|
# TODO: should be a class using abc
|
|
|
|
TARGETS = {
|
|
|
|
"kde": {
|
|
|
|
"profile": "Workstation-KDE",
|
2023-07-20 18:42:05 +00:00
|
|
|
"name": f"Fedora Linux {RELEASE.capitalize()} with KDE Plasma ({BUILDVER})",
|
2023-05-09 16:58:00 +00:00
|
|
|
"os_name": "Fedora Linux with KDE Plasma",
|
2023-03-24 21:29:43 +00:00
|
|
|
"id": "kde",
|
|
|
|
},
|
2023-05-09 11:52:05 +00:00
|
|
|
"gnome": {
|
|
|
|
"profile": "Workstation-GNOME",
|
2023-07-20 18:42:05 +00:00
|
|
|
"name": f"Fedora Linux {RELEASE.capitalize()} with GNOME ({BUILDVER})",
|
2023-05-09 16:58:00 +00:00
|
|
|
"os_name": "Fedora Linux with GNOME",
|
2023-05-09 11:52:05 +00:00
|
|
|
"id": "gnome",
|
|
|
|
},
|
2023-03-24 21:29:43 +00:00
|
|
|
"server": {
|
|
|
|
"profile": "Server",
|
2023-07-20 18:42:05 +00:00
|
|
|
"name": f"Fedora Linux {RELEASE.capitalize()} Server ({BUILDVER})",
|
2023-05-09 16:58:00 +00:00
|
|
|
"os_name": "Fedora Linux Server",
|
2023-03-24 21:29:43 +00:00
|
|
|
"id": "server",
|
|
|
|
},
|
|
|
|
"minimal": {
|
|
|
|
"profile": "Minimal",
|
2023-07-20 18:42:05 +00:00
|
|
|
"name": f"Fedora Linux {RELEASE.capitalize()} Minimal ({BUILDVER})",
|
2023-05-09 16:58:00 +00:00
|
|
|
"os_name": "Fedora Linux Minimal",
|
2023-03-24 21:29:43 +00:00
|
|
|
"id": "minimal",
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
2023-07-19 23:16:04 +00:00
|
|
|
MANIFEST = os.getenv("MANIFEST", "installer_data.json")
|
2023-03-24 21:29:43 +00:00
|
|
|
S3_BUCKET = os.getenv("S3_BUCKET")
|
|
|
|
|
|
|
|
|
|
|
|
def requireCommands(commands):
|
|
|
|
missing = []
|
|
|
|
for cmd in commands:
|
|
|
|
if shutil.which(cmd) is None:
|
|
|
|
missing.append(cmd)
|
|
|
|
|
|
|
|
if len(missing) > 0:
|
|
|
|
fail("Required commands not found: {}".format(" ".join(missing)))
|
|
|
|
|
|
|
|
|
|
|
|
def runCommand(cmd, shell=False):
|
|
|
|
p = subprocess.run(
|
|
|
|
cmd,
|
|
|
|
stdout=subprocess.PIPE,
|
|
|
|
stderr=subprocess.STDOUT,
|
|
|
|
universal_newlines=True,
|
|
|
|
shell=shell,
|
|
|
|
)
|
|
|
|
if p.returncode != 0:
|
|
|
|
fail(p.stdout)
|
|
|
|
|
|
|
|
return p
|
|
|
|
|
|
|
|
|
|
|
|
def kiwiBuild(profile):
|
|
|
|
requireCommands(["kiwi-ng"])
|
|
|
|
|
|
|
|
# TODO: check current status first
|
|
|
|
# runCommand(
|
|
|
|
# [
|
|
|
|
# "sudo",
|
|
|
|
# "setenforce",
|
|
|
|
# "0",
|
|
|
|
# ]
|
|
|
|
# )
|
|
|
|
# TODO: use the API instead of shelling out
|
|
|
|
runCommand(
|
|
|
|
[
|
|
|
|
"sudo",
|
|
|
|
"kiwi-ng",
|
|
|
|
"--debug",
|
|
|
|
"--type=oem",
|
|
|
|
f"--profile={profile}",
|
|
|
|
"system",
|
|
|
|
"build",
|
|
|
|
"--description",
|
|
|
|
"./",
|
|
|
|
"--target-dir",
|
|
|
|
"./outdir",
|
|
|
|
]
|
|
|
|
)
|
|
|
|
# runCommand(
|
|
|
|
# [
|
|
|
|
# "sudo",
|
|
|
|
# "setenforce",
|
|
|
|
# "1",
|
|
|
|
# ]
|
|
|
|
# )
|
|
|
|
|
|
|
|
|
|
|
|
def packageBuild(target):
|
|
|
|
# TODO: rewrite in python instead of shelling out
|
|
|
|
runCommand(["./make-asahi-installer-package.sh"])
|
|
|
|
|
2023-07-20 18:42:05 +00:00
|
|
|
base = f"fedora-{RELEASE}-{target['id']}-{BUILDVER}"
|
|
|
|
os.rename(f"fedora-{RELEASE}-{BUILDVER}.zip", f"{base}.zip")
|
|
|
|
os.rename(f"fedora-{RELEASE}-{BUILDVER}.logs.zip", f"{base}.logs.zip")
|
|
|
|
os.rename(f"fedora-{RELEASE}-{BUILDVER}.raw.zst", f"{base}.raw.zst")
|
|
|
|
os.rename(f"fedora-{RELEASE}-{BUILDVER}.json", f"{base}.json")
|
2023-07-20 17:00:28 +00:00
|
|
|
with open(f"{base}.json", "r") as f:
|
2023-03-24 21:29:43 +00:00
|
|
|
data = json.load(f)
|
|
|
|
|
2023-07-20 17:00:28 +00:00
|
|
|
data["name"] = target["name"]
|
|
|
|
data["default_os_name"] = target["os_name"]
|
|
|
|
data["package"] = f"{base}.zip"
|
2023-08-28 16:53:11 +00:00
|
|
|
# Clobber extras as we don't want them on headless builds and
|
|
|
|
# make-asahi-installer-package.sh doesn't have awareness of the target
|
|
|
|
if target["id"] == "server" or target["id"] == "minimal":
|
|
|
|
data["extras"] = {}
|
2023-03-24 21:29:43 +00:00
|
|
|
|
2023-07-20 17:00:28 +00:00
|
|
|
with open(f"{base}.json", "w") as f:
|
2023-03-24 21:29:43 +00:00
|
|
|
json.dump(data, f)
|
|
|
|
|
2023-07-20 17:00:28 +00:00
|
|
|
if os.path.exists("installer_data.json"):
|
|
|
|
with open("installer_data.json", "r") as f:
|
|
|
|
data = json.load(f)
|
|
|
|
|
|
|
|
data["os_list"][0]["name"] = target["name"]
|
|
|
|
data["os_list"][0]["default_os_name"] = target["os_name"]
|
|
|
|
data["os_list"][0]["package"] = f"{base}.zip"
|
|
|
|
|
|
|
|
with open("installer_data.json", "w") as f:
|
|
|
|
json.dump(data, f)
|
|
|
|
|
2023-03-24 21:29:43 +00:00
|
|
|
|
|
|
|
def uploadToS3(source, destination):
|
2023-07-20 18:42:05 +00:00
|
|
|
if S3_BUCKET is None:
|
|
|
|
fail("S3_BUCKET is not set")
|
|
|
|
|
2023-03-24 21:29:43 +00:00
|
|
|
s3 = boto3.client("s3")
|
|
|
|
s3.upload_file(source, S3_BUCKET, destination)
|
|
|
|
|
|
|
|
|
|
|
|
def packageUpload(target):
|
2023-07-20 18:42:05 +00:00
|
|
|
base = f"fedora-{RELEASE}-{target['id']}-{BUILDVER}"
|
2023-07-20 00:39:31 +00:00
|
|
|
package = f"{base}.zip"
|
2023-07-20 17:00:28 +00:00
|
|
|
logs_package = f"{base}.logs.zip"
|
|
|
|
image = f"{base}.raw.zst"
|
2023-07-20 15:31:18 +00:00
|
|
|
manifest = f"{base}.json"
|
2023-03-24 21:29:43 +00:00
|
|
|
|
|
|
|
uploadToS3(package, f"os/{package}")
|
2023-07-20 00:39:31 +00:00
|
|
|
uploadToS3(logs_package, f"os/{logs_package}")
|
|
|
|
uploadToS3(image, f"os/{image}")
|
2023-07-20 15:31:18 +00:00
|
|
|
uploadToS3(manifest, f"os/{manifest}")
|
2023-03-24 21:29:43 +00:00
|
|
|
|
|
|
|
|
|
|
|
def getManifest():
|
2023-07-20 18:42:05 +00:00
|
|
|
if S3_BUCKET is None:
|
|
|
|
fail("S3_BUCKET is not set")
|
|
|
|
|
2023-07-19 23:16:04 +00:00
|
|
|
s3 = boto3.resource("s3")
|
|
|
|
obj = s3.Object(S3_BUCKET, MANIFEST)
|
|
|
|
data = io.BytesIO()
|
|
|
|
obj.download_fileobj(data)
|
|
|
|
|
|
|
|
return json.loads(data.getvalue().decode("utf-8"))
|
2023-03-24 21:29:43 +00:00
|
|
|
|
|
|
|
|
|
|
|
def manifestUpdate():
|
|
|
|
old = getManifest()
|
|
|
|
with open("installer_data.json", "r") as f:
|
|
|
|
new = json.load(f)
|
|
|
|
|
|
|
|
old["os_list"].insert(0, new["os_list"][0])
|
|
|
|
with open("merged_installer_data.json", "w") as f:
|
|
|
|
json.dump(old, f)
|
|
|
|
|
|
|
|
|
|
|
|
@click.group()
|
|
|
|
def cli():
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
@cli.command()
|
|
|
|
@click.argument("target")
|
|
|
|
def build(target):
|
|
|
|
if target not in TARGETS.keys():
|
|
|
|
fail(f"Unknown target: {target}")
|
|
|
|
|
|
|
|
target = TARGETS[target]
|
|
|
|
kiwiBuild(target["profile"])
|
|
|
|
|
|
|
|
|
|
|
|
@cli.command()
|
|
|
|
@click.argument("target")
|
|
|
|
def package(target):
|
|
|
|
if target not in TARGETS.keys():
|
|
|
|
fail(f"Unknown target: {target}")
|
|
|
|
|
|
|
|
target = TARGETS[target]
|
|
|
|
packageBuild(target)
|
|
|
|
|
|
|
|
|
|
|
|
@cli.command()
|
2023-07-20 15:34:26 +00:00
|
|
|
@click.option("--manifest/--no-manifest", default=True)
|
2023-07-20 17:00:28 +00:00
|
|
|
@click.argument("target")
|
|
|
|
def upload(manifest, target):
|
2023-03-24 21:29:43 +00:00
|
|
|
if target not in TARGETS.keys():
|
|
|
|
fail(f"Unknown target: {target}")
|
|
|
|
|
|
|
|
target = TARGETS[target]
|
|
|
|
|
|
|
|
packageUpload(target)
|
2023-07-20 15:34:26 +00:00
|
|
|
if manifest:
|
|
|
|
manifestUpdate()
|
|
|
|
uploadToS3("merged_installer_data.json", MANIFEST)
|
2023-03-24 21:29:43 +00:00
|
|
|
|
|
|
|
|
|
|
|
@cli.command()
|
|
|
|
@click.option("--update/--no-update", default=False)
|
|
|
|
@click.option("--upload/--no-upload", default=False)
|
2023-07-19 21:08:43 +00:00
|
|
|
def manifest(update, upload):
|
2023-03-24 21:29:43 +00:00
|
|
|
if update:
|
|
|
|
manifestUpdate()
|
|
|
|
manifest = "merged_installer_data.json"
|
|
|
|
if upload:
|
2023-07-19 23:54:18 +00:00
|
|
|
uploadToS3(manifest, MANIFEST)
|
2023-03-24 21:29:43 +00:00
|
|
|
else:
|
|
|
|
manifest = getManifest()
|
|
|
|
|
|
|
|
click.echo(json.dumps(manifest, indent=2))
|
|
|
|
|
|
|
|
|
|
|
|
@cli.command()
|
|
|
|
def clean():
|
|
|
|
runCommand(["sudo", "rm", "-rf", "outdir"])
|
|
|
|
runCommand(["rm", "-f", "installer_data.json", "merged_installer_data.json"])
|
|
|
|
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
cli()
|