| #!/usr/bin/env python3 |
| # |
| # This file defines is used in doc/conf.py to setup the version information for |
| # the documentation: |
| # - get_current_version() used in doc/conf.py computes the current version by |
| # trying to guess the approximate versions we're at using git tags and |
| # branches from the repository. |
| # - write_switchers_js() write the switchers.js file used for switching between |
| # versions of the documentation. |
| # |
| # Copyright (c) 2026 Antonin Godard <antonin.godard@bootlin.com> |
| # |
| # SPDX-License-Identifier: MIT |
| # |
| |
| import argparse |
| import itertools |
| import json |
| import os |
| import re |
| import subprocess |
| import sys |
| import textwrap |
| |
| from urllib.request import urlopen, URLError |
| |
| # NOTE: the following variables contain default values in case we are not able to fetch |
| # the releases.json file from https://dashboard.yoctoproject.org/releases.json |
| DEVBRANCH = "2.18" |
| LTSSERIES = ["2.8", "2.0"] |
| ACTIVERELEASES = ["2.16"] + LTSSERIES |
| YOCTO_MAPPING = { |
| "2.18": "wrynose", |
| "2.16": "whinlatter", |
| "2.8": "scarthgap", |
| "2.0": "kirkstone", |
| } |
| |
| RELEASES_FROM_JSON = {} |
| |
| # Use the local releases.json file if found, fetch it from the dashboard otherwise |
| releases_json_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "releases.json") |
| try: |
| with open(releases_json_path, "r") as f: |
| RELEASES_FROM_JSON = json.load(f) |
| except FileNotFoundError: |
| print("Fetching releases.json from https://dashboard.yoctoproject.org/releases.json...", |
| file=sys.stderr) |
| try: |
| with urlopen("https://dashboard.yoctoproject.org/releases.json") as r, \ |
| open(releases_json_path, "w") as f: |
| RELEASES_FROM_JSON = json.load(r) |
| json.dump(RELEASES_FROM_JSON, f) |
| except URLError: |
| print("WARNING: tried to fetch https://dashboard.yoctoproject.org/releases.json " |
| "but failed, using default values for active releases", file=sys.stderr) |
| pass |
| |
| if RELEASES_FROM_JSON: |
| ACTIVERELEASES = [] |
| DEVBRANCH = "" |
| LTSSERIES = [] |
| YOCTO_MAPPING = {} |
| |
| for release in RELEASES_FROM_JSON: |
| bb_ver = release["bitbake_version"] |
| if release["status"] == "Active Development": |
| DEVBRANCH = bb_ver |
| if "LTS until" in release["status"]: |
| LTSSERIES.append(bb_ver) |
| if release["bitbake_version"]: |
| YOCTO_MAPPING[bb_ver] = release["release_codename"] |
| |
| # Find the first non-dev release, which should be displayed as the default |
| # page on the docs website. |
| current_branch = "" |
| for release in RELEASES_FROM_JSON: |
| if release["status"] != "Active Development": |
| current_branch = release["bitbake_version"] |
| break |
| |
| if not current_branch: |
| sys.exit("Unable to find a current release! Exiting...") |
| |
| # make the list of releases unique, there can be duplication when the |
| # current releases is also an LTS |
| ACTIVERELEASES = list(dict.fromkeys([current_branch] + LTSSERIES)) |
| |
| print(f"ACTIVERELEASES calculated to be {ACTIVERELEASES}", file=sys.stderr) |
| print(f"DEVBRANCH calculated to be {DEVBRANCH}", file=sys.stderr) |
| print(f"LTSSERIES calculated to be {LTSSERIES}", file=sys.stderr) |
| |
| BB_RELEASE_TAG_RE = re.compile(r"^[0-9]+\.[0-9]+\.[0-9]+$") |
| |
| def main(): |
| parser = argparse.ArgumentParser( |
| description="Parse https://dashboard.yoctoproject.org/releases.json to get current releases information" |
| ) |
| parser.add_argument("--get-latest-branch", |
| help="Print current latest branch and exit", |
| action="store_true", |
| default=False) |
| args = parser.parse_args() |
| |
| if args.get_latest_branch: |
| print(ACTIVERELEASES[0]) |
| sys.exit(0) |
| |
| def get_current_version(): |
| # Test tags exist and inform the user to fetch if not |
| try: |
| subprocess.run(["git", "show", f"{LTSSERIES[-1]}.0"], |
| stdout=subprocess.PIPE, stderr=subprocess.PIPE, check=True) |
| except subprocess.CalledProcessError: |
| sys.exit("Please run 'git fetch --tags' before building the documentation") |
| |
| # Try and figure out what we are |
| tags = subprocess.run(["git", "tag", "--points-at", "HEAD"], |
| stdout=subprocess.PIPE, stderr=subprocess.PIPE, |
| universal_newlines=True).stdout |
| for t in tags.split(): |
| if re.match(BB_RELEASE_TAG_RE, t): |
| return t |
| |
| # We're floating on a branch |
| branch = subprocess.run(["git", "branch", "--show-current"], |
| stdout=subprocess.PIPE, stderr=subprocess.PIPE, |
| universal_newlines=True).stdout.strip() |
| |
| if branch == "" or branch not in list(YOCTO_MAPPING.keys()) + ["master", "master-next"]: |
| # We're not on a known release branch so we have to guess. Compare the |
| # numbers of commits from each release branch and assume the smallest |
| # number of commits is the one we're based off |
| possible_branch = None |
| branch_count = 0 |
| for b in itertools.chain(YOCTO_MAPPING.keys(), ["master"]): |
| result = subprocess.run(["git", "log", "--format=oneline", "HEAD..origin/" + b], |
| stdout=subprocess.PIPE, stderr=subprocess.PIPE, |
| universal_newlines=True) |
| if result.returncode == 0: |
| count = result.stdout.count('\n') |
| if not possible_branch or count < branch_count: |
| print("Branch %s has count %s" % (b, count)) |
| possible_branch = b |
| branch_count = count |
| if possible_branch: |
| branch = possible_branch |
| else: |
| branch = "master" |
| print("Nearest release branch estimated to be %s" % branch) |
| |
| if branch == "master": |
| return "dev" |
| |
| if branch == "master-next": |
| return "next" |
| |
| ourversion = branch |
| head_commit = subprocess.run(["git", "rev-parse", "--short", "HEAD"], |
| stdout=subprocess.PIPE, stderr=subprocess.PIPE, |
| universal_newlines=True).stdout.strip() |
| branch_commit = subprocess.run(["git", "rev-parse", "--short", branch], |
| stdout=subprocess.PIPE, stderr=subprocess.PIPE, |
| universal_newlines=True).stdout.strip() |
| if head_commit != branch_commit: |
| ourversion += f" ({head_commit})" |
| |
| return ourversion |
| |
| def write_switchers_js(js_in, js_out, current_version): |
| with open(js_in, "r") as r, open(js_out, "w") as w: |
| lines = r.readlines() |
| for line in lines: |
| if "VERSIONS_PLACEHOLDER" in line: |
| if current_version != "dev": |
| w.write(" 'dev': 'Unstable (dev)',\n") |
| for series in ACTIVERELEASES: |
| w.write(f" '{series}': '{series} ({YOCTO_MAPPING[series]})',\n") |
| else: |
| w.write(line) |
| print("switchers.js generated from switchers.js.in") |
| |
| def _release_section(series_version: str, codename: str, bitbake_version: str) -> str: |
| """ |
| Helper function to generate a release section, as: |
| |
| ******************** |
| Release Series xxxxx |
| ******************** |
| |
| - <link to manual> |
| """ |
| section_length = len(series_version) + len(codename) + 18 |
| return textwrap.dedent( |
| f"""\ |
| {'*' * section_length} |
| Release Series {series_version} ({codename}) |
| {'*' * section_length} |
| |
| - :yocto_docs:`BitBake {bitbake_version} User Manual </bitbake/{bitbake_version}/>` |
| |
| """) |
| |
| def write_releases_rst(releases_rst_out: str): |
| """ |
| Generates the releases.rst file automatically, based on what is found |
| in the releases.json file. |
| """ |
| with open(releases_rst_out, "w") as f: |
| f.write(textwrap.dedent("""\ |
| .. SPDX-License-Identifier: CC-BY-2.5 |
| |
| ================================= |
| BitBake Supported Release Manuals |
| ================================= |
| |
| """)) |
| |
| for release in RELEASES_FROM_JSON: |
| if release["status"] == "Active Development": |
| continue |
| |
| if not release["bitbake_version"]: |
| continue |
| |
| if release["series"] == "current": |
| f.write(_release_section( |
| release["series_version"], |
| release["release_codename"], |
| release["bitbake_version"])) |
| |
| f.write(textwrap.dedent("""\ |
| ================================ |
| BitBake Outdated Release Manuals |
| ================================ |
| |
| """)) |
| |
| for release in RELEASES_FROM_JSON: |
| if not release["series"] == "previous": |
| continue |
| |
| if not release["bitbake_version"]: |
| continue |
| |
| f.write(_release_section( |
| release["series_version"], |
| release["release_codename"], |
| release["bitbake_version"])) |
| |
| # old legacy links, which cannot be auto-generated |
| f.write(textwrap.dedent( |
| """\ |
| - :yocto_docs:`3.1.2 BitBake User Manual </3.1.2/bitbake-user-manual/bitbake-user-manual.html>` |
| - :yocto_docs:`3.1 BitBake User Manual </3.1/bitbake-user-manual/bitbake-user-manual.html>` |
| - :yocto_docs:`3.1.1 BitBake User Manual </3.1.1/bitbake-user-manual/bitbake-user-manual.html>` |
| - :yocto_docs:`3.1.3 BitBake User Manual </3.1.3/bitbake-user-manual/bitbake-user-manual.html>` |
| |
| ************************* |
| Release Series 3.0 (Zeus) |
| ************************* |
| |
| - :yocto_docs:`3.0 BitBake User Manual </3.0/bitbake-user-manual/bitbake-user-manual.html>` |
| - :yocto_docs:`3.0.1 BitBake User Manual </3.0.1/bitbake-user-manual/bitbake-user-manual.html>` |
| - :yocto_docs:`3.0.2 BitBake User Manual </3.0.2/bitbake-user-manual/bitbake-user-manual.html>` |
| - :yocto_docs:`3.0.3 BitBake User Manual </3.0.3/bitbake-user-manual/bitbake-user-manual.html>` |
| - :yocto_docs:`3.0.4 BitBake User Manual </3.0.4/bitbake-user-manual/bitbake-user-manual.html>` |
| |
| **************************** |
| Release Series 2.7 (Warrior) |
| **************************** |
| |
| - :yocto_docs:`2.7 BitBake User Manual </2.7/bitbake-user-manual/bitbake-user-manual.html>` |
| - :yocto_docs:`2.7.1 BitBake User Manual </2.7.1/bitbake-user-manual/bitbake-user-manual.html>` |
| - :yocto_docs:`2.7.2 BitBake User Manual </2.7.2/bitbake-user-manual/bitbake-user-manual.html>` |
| - :yocto_docs:`2.7.3 BitBake User Manual </2.7.3/bitbake-user-manual/bitbake-user-manual.html>` |
| - :yocto_docs:`2.7.4 BitBake User Manual </2.7.4/bitbake-user-manual/bitbake-user-manual.html>` |
| |
| ************************* |
| Release Series 2.6 (Thud) |
| ************************* |
| |
| - :yocto_docs:`2.6 BitBake User Manual </2.6/bitbake-user-manual/bitbake-user-manual.html>` |
| - :yocto_docs:`2.6.1 BitBake User Manual </2.6.1/bitbake-user-manual/bitbake-user-manual.html>` |
| - :yocto_docs:`2.6.2 BitBake User Manual </2.6.2/bitbake-user-manual/bitbake-user-manual.html>` |
| - :yocto_docs:`2.6.3 BitBake User Manual </2.6.3/bitbake-user-manual/bitbake-user-manual.html>` |
| - :yocto_docs:`2.6.4 BitBake User Manual </2.6.4/bitbake-user-manual/bitbake-user-manual.html>` |
| |
| ************************* |
| Release Series 2.5 (Sumo) |
| ************************* |
| |
| - :yocto_docs:`2.5 Documentation </2.5>` |
| - :yocto_docs:`2.5.1 Documentation </2.5.1>` |
| - :yocto_docs:`2.5.2 Documentation </2.5.2>` |
| - :yocto_docs:`2.5.3 Documentation </2.5.3>` |
| |
| ************************** |
| Release Series 2.4 (Rocko) |
| ************************** |
| |
| - :yocto_docs:`2.4 BitBake User Manual </2.4/bitbake-user-manual/bitbake-user-manual.html>` |
| - :yocto_docs:`2.4.1 BitBake User Manual </2.4.1/bitbake-user-manual/bitbake-user-manual.html>` |
| - :yocto_docs:`2.4.2 BitBake User Manual </2.4.2/bitbake-user-manual/bitbake-user-manual.html>` |
| - :yocto_docs:`2.4.3 BitBake User Manual </2.4.3/bitbake-user-manual/bitbake-user-manual.html>` |
| - :yocto_docs:`2.4.4 BitBake User Manual </2.4.4/bitbake-user-manual/bitbake-user-manual.html>` |
| |
| ************************* |
| Release Series 2.3 (Pyro) |
| ************************* |
| |
| - :yocto_docs:`2.3 BitBake User Manual </2.3/bitbake-user-manual/bitbake-user-manual.html>` |
| - :yocto_docs:`2.3.1 BitBake User Manual </2.3.1/bitbake-user-manual/bitbake-user-manual.html>` |
| - :yocto_docs:`2.3.2 BitBake User Manual </2.3.2/bitbake-user-manual/bitbake-user-manual.html>` |
| - :yocto_docs:`2.3.3 BitBake User Manual </2.3.3/bitbake-user-manual/bitbake-user-manual.html>` |
| - :yocto_docs:`2.3.4 BitBake User Manual </2.3.4/bitbake-user-manual/bitbake-user-manual.html>` |
| |
| ************************** |
| Release Series 2.2 (Morty) |
| ************************** |
| |
| - :yocto_docs:`2.2 BitBake User Manual </2.2/bitbake-user-manual/bitbake-user-manual.html>` |
| - :yocto_docs:`2.2.1 BitBake User Manual </2.2.1/bitbake-user-manual/bitbake-user-manual.html>` |
| - :yocto_docs:`2.2.2 BitBake User Manual </2.2.2/bitbake-user-manual/bitbake-user-manual.html>` |
| - :yocto_docs:`2.2.3 BitBake User Manual </2.2.3/bitbake-user-manual/bitbake-user-manual.html>` |
| |
| **************************** |
| Release Series 2.1 (Krogoth) |
| **************************** |
| |
| - :yocto_docs:`2.1 BitBake User Manual </2.1/bitbake-user-manual/bitbake-user-manual.html>` |
| - :yocto_docs:`2.1.1 BitBake User Manual </2.1.1/bitbake-user-manual/bitbake-user-manual.html>` |
| - :yocto_docs:`2.1.2 BitBake User Manual </2.1.2/bitbake-user-manual/bitbake-user-manual.html>` |
| - :yocto_docs:`2.1.3 BitBake User Manual </2.1.3/bitbake-user-manual/bitbake-user-manual.html>` |
| |
| *************************** |
| Release Series 2.0 (Jethro) |
| *************************** |
| |
| - :yocto_docs:`1.9 BitBake User Manual </1.9/bitbake-user-manual/bitbake-user-manual.html>` |
| - :yocto_docs:`2.0 BitBake User Manual </2.0/bitbake-user-manual/bitbake-user-manual.html>` |
| - :yocto_docs:`2.0.1 BitBake User Manual </2.0.1/bitbake-user-manual/bitbake-user-manual.html>` |
| - :yocto_docs:`2.0.2 BitBake User Manual </2.0.2/bitbake-user-manual/bitbake-user-manual.html>` |
| - :yocto_docs:`2.0.3 BitBake User Manual </2.0.3/bitbake-user-manual/bitbake-user-manual.html>` |
| |
| ************************* |
| Release Series 1.8 (Fido) |
| ************************* |
| |
| - :yocto_docs:`1.8 BitBake User Manual </1.8/bitbake-user-manual/bitbake-user-manual.html>` |
| - :yocto_docs:`1.8.1 BitBake User Manual </1.8.1/bitbake-user-manual/bitbake-user-manual.html>` |
| - :yocto_docs:`1.8.2 BitBake User Manual </1.8.2/bitbake-user-manual/bitbake-user-manual.html>` |
| |
| ************************** |
| Release Series 1.7 (Dizzy) |
| ************************** |
| |
| - :yocto_docs:`1.7 BitBake User Manual </1.7/bitbake-user-manual/bitbake-user-manual.html>` |
| - :yocto_docs:`1.7.1 BitBake User Manual </1.7.1/bitbake-user-manual/bitbake-user-manual.html>` |
| - :yocto_docs:`1.7.2 BitBake User Manual </1.7.2/bitbake-user-manual/bitbake-user-manual.html>` |
| - :yocto_docs:`1.7.3 BitBake User Manual </1.7.3/bitbake-user-manual/bitbake-user-manual.html>` |
| |
| ************************** |
| Release Series 1.6 (Daisy) |
| ************************** |
| |
| - :yocto_docs:`1.6 BitBake User Manual </1.6/bitbake-user-manual/bitbake-user-manual.html>` |
| - :yocto_docs:`1.6.1 BitBake User Manual </1.6.1/bitbake-user-manual/bitbake-user-manual.html>` |
| - :yocto_docs:`1.6.2 BitBake User Manual </1.6.2/bitbake-user-manual/bitbake-user-manual.html>` |
| - :yocto_docs:`1.6.3 BitBake User Manual </1.6.3/bitbake-user-manual/bitbake-user-manual.html>` |
| """)) |
| |
| if __name__ == "__main__": |
| main() |