2024-10-12 08:22:14 +00:00
|
|
|
#!/usr/bin/env python3
|
|
|
|
import os
|
|
|
|
import yaml
|
2024-11-03 20:28:20 +00:00
|
|
|
import subprocess
|
2024-10-12 08:22:14 +00:00
|
|
|
import logging
|
|
|
|
|
|
|
|
from jinja2 import Environment, PackageLoader, select_autoescape
|
|
|
|
|
|
|
|
logging.basicConfig(level=logging.INFO)
|
|
|
|
|
|
|
|
repo_owner = os.getenv("REPO_OWNER") or os.getenv(
|
|
|
|
"GITHUB_REPOSITORY_OWNER", "default_owner"
|
|
|
|
)
|
|
|
|
repo_name = os.getenv("REPO_NAME") or os.getenv("GITHUB_REPOSITORY", "default_repo")
|
|
|
|
|
|
|
|
env = Environment(loader=PackageLoader("render-readme"), autoescape=select_autoescape())
|
|
|
|
|
|
|
|
def load_metadata(file_path):
|
|
|
|
try:
|
|
|
|
with open(file_path, "r") as f:
|
|
|
|
return yaml.safe_load(f)
|
|
|
|
except yaml.YAMLError as e:
|
|
|
|
logging.error(f"Error loading YAML file {file_path}: {e}")
|
|
|
|
except FileNotFoundError:
|
|
|
|
logging.error(f"File {file_path} not found.")
|
|
|
|
return None
|
|
|
|
|
|
|
|
def process_metadata(apps_dir):
|
|
|
|
app_images = []
|
|
|
|
for subdir, _, files in os.walk(apps_dir):
|
|
|
|
if "metadata.yaml" not in files:
|
|
|
|
continue # Skip if metadata file not found
|
|
|
|
|
|
|
|
meta = load_metadata(os.path.join(subdir, "metadata.yaml"))
|
|
|
|
if not meta:
|
|
|
|
continue # Skip if metadata couldn't be loaded
|
|
|
|
|
2024-11-03 20:28:20 +00:00
|
|
|
dockerfile_path = os.path.join(subdir, "Dockerfile")
|
|
|
|
docker_labels = load_docker_labels(dockerfile_path)
|
|
|
|
|
|
|
|
# Compliance check and badge setting
|
|
|
|
goss_file = os.path.join(subdir, "ci", "goss.yaml")
|
|
|
|
print(goss_file)
|
|
|
|
badge = run_compliance_check(goss_file)
|
|
|
|
|
2024-10-12 08:22:14 +00:00
|
|
|
for channel in meta.get("channels", []):
|
|
|
|
name = (
|
|
|
|
meta["app"]
|
|
|
|
if channel.get("stable", False)
|
|
|
|
else f"{meta['app']}-{channel['name']}"
|
|
|
|
)
|
|
|
|
image = {
|
|
|
|
"name": name,
|
|
|
|
"channel": channel["name"],
|
2024-11-03 20:28:20 +00:00
|
|
|
"version": meta["version"],
|
|
|
|
"platforms": channel["platforms"],
|
|
|
|
"tests_enabled": channel["tests"]["enabled"],
|
|
|
|
"tests_type": channel["tests"]["type"],
|
|
|
|
"html_url": f"https://code.252.no/{repo_owner}/containers/{name}",
|
2024-10-12 08:22:14 +00:00
|
|
|
"owner": repo_owner,
|
2024-11-03 20:28:20 +00:00
|
|
|
"maintainer": docker_labels.get("maintainer"),
|
|
|
|
"description": docker_labels.get("org.opencontainers.image.description"),
|
|
|
|
"source": docker_labels.get("org.opencontainers.image.source"),
|
|
|
|
"vendor": docker_labels.get("org.opencontainers.image.vendor"),
|
|
|
|
"authors": docker_labels.get("org.opencontainers.image.authors"),
|
|
|
|
"badge": badge,
|
2024-10-12 08:22:14 +00:00
|
|
|
}
|
|
|
|
app_images.append(image)
|
2024-11-03 20:28:20 +00:00
|
|
|
logging.info(f"Added image {name} from channel {channel['name']} with badge {badge}")
|
2024-10-12 08:22:14 +00:00
|
|
|
return app_images
|
|
|
|
|
2024-11-03 20:28:20 +00:00
|
|
|
def load_docker_labels(dockerfile_path):
|
|
|
|
labels = {}
|
|
|
|
try:
|
|
|
|
with open(dockerfile_path, "r") as f:
|
|
|
|
for line in f:
|
|
|
|
if line.startswith("LABEL"):
|
|
|
|
label_parts = line.split("=", 1)
|
|
|
|
if len(label_parts) == 2:
|
|
|
|
key, value = label_parts
|
|
|
|
key = key.replace("LABEL ", "").strip().replace("\"", "")
|
|
|
|
value = value.strip().replace("\"", "")
|
|
|
|
labels[key] = value
|
|
|
|
except FileNotFoundError:
|
|
|
|
logging.warning(f"Dockerfile {dockerfile_path} not found.")
|
|
|
|
return labels
|
|
|
|
|
|
|
|
def run_compliance_check(goss_file, image_name):
|
|
|
|
"""Run compliance test using dgoss and return appropriate badge path."""
|
|
|
|
if not os.path.exists(goss_file):
|
|
|
|
logging.warning(f"Compliance file {goss_file} not found.")
|
|
|
|
return "assets/build-failing-red.svg" # Default to failing badge if no compliance file
|
|
|
|
|
|
|
|
# Set up the environment variables needed for dgoss
|
|
|
|
env = os.environ.copy()
|
|
|
|
env["CONTAINER_RUNTIME"] = "docker"
|
|
|
|
env["GOSS_FILE"] = goss_file
|
|
|
|
env["GOSS_OPTS"] = "--retry-timeout 60s --sleep 2s --color --format documentation"
|
|
|
|
env["GOSS_SLEEP"] = "2"
|
|
|
|
env["GOSS_FILES_STRATEGY"] = "cp"
|
|
|
|
|
|
|
|
print("Running dgoss with file:", goss_file, "on image:", image_name)
|
|
|
|
|
|
|
|
# Run dgoss against the container image
|
|
|
|
result = subprocess.run(
|
|
|
|
["dgoss", "run", image_name],
|
|
|
|
capture_output=True,
|
|
|
|
env=env,
|
|
|
|
shell=True # Necessary to handle dgoss's internal shell scripts
|
|
|
|
)
|
|
|
|
output = result.stdout.decode()
|
|
|
|
print(output) # Decode and print output for logging
|
|
|
|
|
|
|
|
if result.returncode == 0:
|
|
|
|
logging.info(f"Compliance check passed for {goss_file}")
|
|
|
|
return "assets/badges/build-passing-brightgreen.svg"
|
|
|
|
else:
|
|
|
|
logging.error(f"Compliance check failed for {goss_file}")
|
|
|
|
return "assets/build-failing-red.svg"
|
2024-10-12 08:22:14 +00:00
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
apps_dir = "./apps"
|
|
|
|
app_images = process_metadata(apps_dir)
|
|
|
|
|
|
|
|
try:
|
|
|
|
template = env.get_template("README.org.j2")
|
|
|
|
with open("./README.org", "w") as f:
|
|
|
|
f.write(template.render(app_images=app_images))
|
|
|
|
logging.info("README.org successfully generated.")
|
|
|
|
except Exception as e:
|
2024-11-03 20:28:20 +00:00
|
|
|
logging.error(f"Error rendering template: {e}")
|