222 lines
No EOL
7.6 KiB
Python
222 lines
No EOL
7.6 KiB
Python
#!/usr/bin/env python3
|
|
import importlib.util
|
|
import sys
|
|
import os
|
|
|
|
import json
|
|
import yaml
|
|
import requests
|
|
|
|
from os.path import isfile
|
|
from subprocess import check_output
|
|
from packaging.version import Version
|
|
|
|
repo_owner = os.environ.get("REPO_OWNER", os.environ.get("GITHUB_REPOSITORY_OWNER"))
|
|
|
|
TESTABLE_PLATFORMS = ["linux/amd64"]
|
|
|
|
|
|
def load_metadata_file_yaml(file_path):
|
|
with open(file_path, "r") as f:
|
|
return yaml.safe_load(f)
|
|
|
|
|
|
def load_metadata_file_json(file_path):
|
|
with open(file_path, "r") as f:
|
|
return json.load(f)
|
|
|
|
|
|
def get_latest_version_py(latest_py_path, channel_name):
|
|
spec = importlib.util.spec_from_file_location("latest", latest_py_path)
|
|
latest = importlib.util.module_from_spec(spec)
|
|
sys.modules["latest"] = latest
|
|
spec.loader.exec_module(latest)
|
|
return latest.get_latest(channel_name)
|
|
|
|
|
|
def get_latest_version_sh(latest_sh_path, channel_name):
|
|
out = check_output([latest_sh_path, channel_name])
|
|
return out.decode("utf-8").strip()
|
|
|
|
|
|
def get_latest_version(subdir, channel_name):
|
|
ci_dir = os.path.join(subdir, "ci")
|
|
if os.path.isfile(os.path.join(ci_dir, "latest.py")):
|
|
return get_latest_version_py(os.path.join(ci_dir, "latest.py"), channel_name)
|
|
elif os.path.isfile(os.path.join(ci_dir, "latest.sh")):
|
|
return get_latest_version_sh(os.path.join(ci_dir, "latest.sh"), channel_name)
|
|
elif os.path.isfile(os.path.join(subdir, channel_name, "latest.py")):
|
|
return get_latest_version_py(
|
|
os.path.join(subdir, channel_name, "latest.py"), channel_name
|
|
)
|
|
elif os.path.isfile(os.path.join(subdir, channel_name, "latest.sh")):
|
|
return get_latest_version_sh(
|
|
os.path.join(subdir, channel_name, "latest.sh"), channel_name
|
|
)
|
|
return None
|
|
|
|
|
|
def get_published_version(image_name):
|
|
r = requests.get(
|
|
f"https://api.github.com/users/{repo_owner}/packages/container/{image_name}/versions",
|
|
headers={
|
|
"Accept": "application/vnd.github.v3+json",
|
|
"Authorization": "token " + os.environ["TOKEN"],
|
|
},
|
|
)
|
|
|
|
if r.status_code != 200:
|
|
return None
|
|
|
|
data = json.loads(r.text)
|
|
for image in data:
|
|
tags = image["metadata"]["container"]["tags"]
|
|
if "rolling" in tags:
|
|
tags.remove("rolling")
|
|
return max(tags, key=len)
|
|
|
|
|
|
def get_image_metadata(subdir, meta, forRelease=False, force=False, channels=None):
|
|
imagesToBuild = {"images": [], "imagePlatforms": []}
|
|
|
|
if channels is None:
|
|
channels = meta["channels"]
|
|
else:
|
|
channels = [
|
|
channel for channel in meta["channels"] if channel["name"] in channels
|
|
]
|
|
|
|
for channel in channels:
|
|
version = get_latest_version(subdir, channel["name"])
|
|
if not version:
|
|
continue
|
|
|
|
# Image Name
|
|
toBuild = {}
|
|
if channel.get("stable", False):
|
|
toBuild["name"] = meta["app"]
|
|
else:
|
|
toBuild["name"] = "-".join([meta["app"], channel["name"]])
|
|
|
|
# Skip if latest version already published
|
|
if not force:
|
|
published = get_published_version(toBuild["name"])
|
|
if published is not None and published == version:
|
|
continue
|
|
toBuild["published_version"] = published
|
|
|
|
toBuild["version"] = version
|
|
|
|
# Image Tags
|
|
toBuild["tags"] = ["rolling", version]
|
|
if meta.get("semver", False):
|
|
parts = version.split(".")[:-1]
|
|
while len(parts) > 0:
|
|
toBuild["tags"].append(".".join(parts))
|
|
parts = parts[:-1]
|
|
|
|
# Platform Metadata
|
|
for platform in channel["platforms"]:
|
|
|
|
if platform not in TESTABLE_PLATFORMS and not forRelease:
|
|
continue
|
|
|
|
toBuild.setdefault("platforms", []).append(platform)
|
|
|
|
target_os = platform.split("/")[0]
|
|
target_arch = platform.split("/")[1]
|
|
|
|
platformToBuild = {}
|
|
platformToBuild["name"] = toBuild["name"]
|
|
platformToBuild["platform"] = platform
|
|
platformToBuild["target_os"] = target_os
|
|
platformToBuild["target_arch"] = target_arch
|
|
platformToBuild["version"] = version
|
|
platformToBuild["channel"] = channel["name"]
|
|
platformToBuild["label_type"] = "org.opencontainers.image"
|
|
|
|
if isfile(os.path.join(subdir, channel["name"], "Dockerfile")):
|
|
platformToBuild["dockerfile"] = os.path.join(
|
|
subdir, channel["name"], "Dockerfile"
|
|
)
|
|
platformToBuild["context"] = os.path.join(subdir, channel["name"])
|
|
platformToBuild["goss_config"] = os.path.join(
|
|
subdir, channel["name"], "goss.yaml"
|
|
)
|
|
else:
|
|
platformToBuild["dockerfile"] = os.path.join(subdir, "Dockerfile")
|
|
platformToBuild["context"] = subdir
|
|
platformToBuild["goss_config"] = os.path.join(subdir, "ci", "goss.yaml")
|
|
|
|
platformToBuild["goss_args"] = (
|
|
"tail -f /dev/null"
|
|
if channel["tests"].get("type", "web") == "cli"
|
|
else ""
|
|
)
|
|
|
|
platformToBuild["tests_enabled"] = (
|
|
channel["tests"]["enabled"] and platform in TESTABLE_PLATFORMS
|
|
)
|
|
|
|
imagesToBuild["imagePlatforms"].append(platformToBuild)
|
|
imagesToBuild["images"].append(toBuild)
|
|
return imagesToBuild
|
|
|
|
|
|
if __name__ == "__main__":
|
|
apps = sys.argv[1]
|
|
forRelease = sys.argv[2] == "true"
|
|
force = sys.argv[3] == "true"
|
|
imagesToBuild = {"images": [], "imagePlatforms": []}
|
|
|
|
if apps != "all":
|
|
channels = None
|
|
apps = apps.split(",")
|
|
if len(sys.argv) == 5:
|
|
channels = sys.argv[4].split(",")
|
|
|
|
for app in apps:
|
|
if not os.path.exists(os.path.join("./apps", app)):
|
|
print(f'App "{app}" not found')
|
|
exit(1)
|
|
|
|
meta = None
|
|
if os.path.isfile(os.path.join("./apps", app, "metadata.yaml")):
|
|
meta = load_metadata_file_yaml(
|
|
os.path.join("./apps", app, "metadata.yaml")
|
|
)
|
|
elif os.path.isfile(os.path.join("./apps", app, "metadata.json")):
|
|
meta = load_metadata_file_json(
|
|
os.path.join("./apps", app, "metadata.json")
|
|
)
|
|
|
|
imageToBuild = get_image_metadata(
|
|
os.path.join("./apps", app),
|
|
meta,
|
|
forRelease,
|
|
force=force,
|
|
channels=channels,
|
|
)
|
|
if imageToBuild is not None:
|
|
imagesToBuild["images"].extend(imageToBuild["images"])
|
|
imagesToBuild["imagePlatforms"].extend(imageToBuild["imagePlatforms"])
|
|
else:
|
|
for subdir, dirs, files in os.walk("./apps"):
|
|
for file in files:
|
|
meta = None
|
|
if file == "metadata.yaml":
|
|
meta = load_metadata_file_yaml(os.path.join(subdir, file))
|
|
elif file == "metadata.json":
|
|
meta = load_metadata_file_json(os.path.join(subdir, file))
|
|
else:
|
|
continue
|
|
if meta is not None:
|
|
imageToBuild = get_image_metadata(
|
|
subdir, meta, forRelease, force=force
|
|
)
|
|
if imageToBuild is not None:
|
|
imagesToBuild["images"].extend(imageToBuild["images"])
|
|
imagesToBuild["imagePlatforms"].extend(
|
|
imageToBuild["imagePlatforms"]
|
|
)
|
|
print(json.dumps(imagesToBuild)) |