Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Thunderbird/scripts/changelog/   (Thunderbird Email Client Version 5.6©)  Datei vom 22.9.2026 mit Größe 8 kB image not shown  

Quelle  generate_changelog_json.py   Sprache: Python

 

#!/usr/bin/env python3
# Converts Thunderbird/K-9 Mail release notes into JSON changelog resources.
#
# The script:
# - Loads release notes from https://github.com/thunderbird/thunderbird-notes.
# - Extracts changelog data for a specific release version.
# - Generates version-specific JSON changelog files.
# - Updates the changelog index.
# - Validates all generated files against the project's JSON schemas.
#
# Supported application IDs:
#   - com.fsck.k9
#   - net.thunderbird.android
#   - net.thunderbird.android.beta
#
# Usage:
#   python scripts/changelog/generate_changelog_json.py <applicationid> <version> <versioncode>
#
# Example:
#   python scripts/changelog/generate_changelog_json.py net.thunderbird.android 11.0 3320

import argparse
import json
import re
from pathlib import Path

import requests
import yaml
from jsonschema import Draft202012Validator

APP_CONFIG = {
    "com.fsck.k9": {"app_dir": "app-k9mail", "build_type": "release"},
    "net.thunderbird.android": {"app_dir": "app-thunderbird", "build_type": "release"},
    "net.thunderbird.android.beta": {"app_dir": "app-thunderbird", "build_type": "beta"},
}

def to_resource_name(version: str) -> str:
    normalized = re.sub(r"[^a-z0-9]+", "_", version.lower()).strip("_")
    return f"changelog_release_{normalized}"

def load_schema(schema_path: Path) -> dict:
    with schema_path.open("r", encoding="utf-8") as file:
        return json.load(file)

def validate_json(
    data: dict,
    schema: dict,
    description: str,
) -> None:
    validator = Draft202012Validator(
        schema
    )

    errors = sorted(
        validator.iter_errors(data),
        key=lambda error: list(
            error.absolute_path
        ),
    )

    if not errors:
        return

    messages = []

    for error in errors:
        location = (
            "/".join(
                str(part)
                for part in error.absolute_path
            )
            or "<root>"
        )

        messages.append(
            f"{location}: "
            f"{error.message}"
        )

    raise ValueError(
        f"{description} failed schema "
        f"validation:\n"
        + "\n".join(messages)
    )

def load_release_notes(version: str, notesrepo: str, notesbranch: str) -> dict:
    filename = f"{version}.yml"
    directory = "android_release"
    if re.search(r"0b\d+$", version):
        filename = f"{re.sub(r'b\d+$', 'beta', version)}.yml"
        directory = "android_beta"

    repo_path = Path(notesrepo).expanduser()
    if repo_path.is_dir():
        yaml_file = repo_path / directory / filename
        with yaml_file.open("r", encoding="utf-8") as f:
            return yaml.safe_load(f)

    url = (
        f"https://api.github.com/repos/{notesrepo}/contents/"
        f"{directory}/{filename}?ref={notesbranch}"
    )
    response = requests.get(
        url,
        headers={"Accept": "application/vnd.github.v3.raw"},
        timeout=30,
    )
    response.raise_for_status()
    return yaml.safe_load(response.text)

def extract_release(version: str, versioncode: int, application: str, yaml_content: dict) -> dict:
    release_info = next(
        (r for r in yaml_content["release"]["releases"] if r["version"] == version),
        None,
    )
    if release_info is None:
        raise ValueError(f"Version '{version}' not found")

    beta_group = None
    match = re.search(r"0b(\d+)$", version)
    if match:
        beta_group = int(match.group(1))

    notes = []
    for note in yaml_content["notes"]:
        if beta_group is not None and note.get("group") != beta_group:
            continue
        if note.get("thunderbird_only", False) and application == "k9mail":
            continue
        if note.get("k9mail_only", False) and application == "thunderbird":
            continue

        text = note.get("note", "").strip()
        if not text:
            continue

        change_type = determine_change_type(note.get("tag", "changed").lower())
        entry = {
            "type": change_type,
            "text": text,
            "source": "thunderbird-notes",
        }
        if "issues" in note:
            entry["issues"] = note["issues"]
        notes.append(entry)

    return {
        "schemaVersion": 1,
        "version": version,
        "versioncode": versioncode,
        "date": release_info["release_date"],
        "notes": notes,
    }

def determine_change_type(
    text: str,
) -> str:
    normalized = text.lower()

    if normalized.startswith(
        "fixed"
    ):
        return "fixed"

    if (
        normalized.startswith(
            "added"
        )
        or normalized.startswith(
        "new"
    )
        or normalized.startswith(
        "support"
    )
    ):
        return "new"

    return "changed"

def write_release_file(release_data: dict, output_dir: Path) -> str:
    resource_name = to_resource_name(release_data["version"])
    output_file = output_dir / f"{resource_name}.json"
    with output_file.open("w", encoding="utf-8") as f:
        json.dump(release_data, f, indent=2, ensure_ascii=False)
        f.write("\n")
    return resource_name

def create_index_entry(release_data: dict, resource_name: str) -> dict:
    return {
        "version": release_data["version"],
        "versioncode": release_data["versioncode"],
        "date": release_data["date"],
        "resourceName": resource_name,
    }

def load_existing_index(output_dir: Path) -> dict:
    index_file = output_dir / "changelog_index.json"
    if not index_file.exists():
        return {"schemaVersion": 1, "releases": []}
    with index_file.open("r", encoding="utf-8") as f:
        return json.load(f)

def update_index(index: dict, release_data: dict, resource_name: str) -> dict:
    releases = [
        r for r in index["releases"]
        if r["version"] != release_data["version"]
    ]
    releases.append(create_index_entry(release_data, resource_name))
    # Keep release families together even when an older version was published later.
    # Tokenize numbers to avoid lexicographic ordering (e.g. b10 before b9).
    # A final release follows its prereleases (5.200 > 5.200-RC2).
    def version_key(entry):
        parts = re.findall(r"\d+|[a-zA-Z]+", entry["version"])
        return tuple(
            (1, int(part)) if part.isdigit() else (0, part.lower())
            for part in parts
        ) + ((1, 0),)

    releases.sort(key=version_key, reverse=True)
    index["releases"] = releases
    return index

def write_index_file(index: dict, output_dir: Path) -> None:
    with (output_dir / "changelog_index.json").open("w", encoding="utf-8") as f:
        json.dump(index, f, indent=2, ensure_ascii=False)
        f.write("\n")

def main():
    parser = argparse.ArgumentParser()
    parser.add_argument(
        "applicationid",
        choices=[
            "net.thunderbird.android",
            "net.thunderbird.android.beta",
            "com.fsck.k9",
        ],
    )
    parser.add_argument("version")
    parser.add_argument("versioncode", type=int)
    parser.add_argument(
        "--repository", "-r",
        default="thunderbird/thunderbird-notes",
    )
    parser.add_argument("--branch", "-b", default="master")
    args = parser.parse_args()

    config = APP_CONFIG[args.applicationid]
    application = "k9mail" if args.applicationid == "com.fsck.k9" else "thunderbird"

    repo_root = Path(__file__).resolve().parents[2]

    output_dir = (
        repo_root / config["app_dir"] / "src" / config["build_type"]
        / "res" / "raw"
    )

    schema_dir = repo_root / "schemas"
    release_schema = load_schema(schema_dir / "changelog-release.schema.json")
    index_schema = load_schema(schema_dir / "changelog-index.schema.json")

    output_dir.mkdir(parents=True, exist_ok=True)

    yaml_content = load_release_notes(
        args.version,
        args.repository,
        args.branch,
    )

    release_data = extract_release(args.version, args.versioncode, application, yaml_content)
    validate_json(release_data, release_schema, f"Release {args.version}")
    resource_name = to_resource_name(args.version)
    index = update_index(load_existing_index(output_dir), release_data, resource_name)
    validate_json(index, index_schema, "Changelog index")

    write_release_file(release_data, output_dir)
    write_index_file(index, output_dir)

    print(f"Generated {resource_name}.json and updated changelog_index.json")

if __name__ == "__main__":
    main()

Messung
CHZG
86959290

¤ Dauer der Verarbeitung: 0.1 Sekunden  (vorverarbeitet am  2026-11-04) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover


Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.