diff --git a/pmb/helpers/args.py b/pmb/helpers/args.py index 0b21ac3e..87fc80cf 100644 --- a/pmb/helpers/args.py +++ b/pmb/helpers/args.py @@ -3,6 +3,7 @@ import copy import os import pmb.config +import pmb.helpers.git """ This file constructs the args variable, which is passed to almost all functions in the pmbootstrap code base. Here's a listing of the kind of @@ -131,7 +132,8 @@ def add_cache(args): "find_aport": {}, "pmb.helpers.package.depends_recurse": {}, "pmb.helpers.package.get": {}, - "pmb.helpers.repo.update": repo_update}) + "pmb.helpers.repo.update": repo_update, + "pmb.helpers.git.parse_channels_cfg": {}}) def add_deviceinfo(args): @@ -162,6 +164,8 @@ def init(args): "pull", "shutdown", "zap"]: pmb.config.pmaports.read_config_into_args(args) add_deviceinfo(args) + pmb.helpers.git.parse_channels_cfg(args) + return args diff --git a/pmb/helpers/git.py b/pmb/helpers/git.py index 33d1a0b6..2a32fe59 100644 --- a/pmb/helpers/git.py +++ b/pmb/helpers/git.py @@ -1,5 +1,6 @@ # Copyright 2020 Oliver Smith # SPDX-License-Identifier: GPL-3.0-or-later +import configparser import logging import os import time @@ -100,12 +101,71 @@ def get_upstream_remote(args, name_repo): " repository: {}".format(name_repo, url, path)) +def parse_channels_cfg(args): + """ Parse channels.cfg from pmaports.git, origin/master branch. + Reference: https://postmarketos.org/channels.cfg + :returns: dict like: {"meta": {"recommended": "edge"}, + "channels": {"edge": {"description": ..., + "branch_pmaports": ..., + "branch_aports": ..., + "mirrordir_alpine": ...}, + ...}} """ + # Cache during one pmbootstrap run + cache_key = "pmb.helpers.git.parse_channels_cfg" + if args.cache[cache_key]: + return args.cache[cache_key] + + # Read with configparser + cfg = configparser.ConfigParser() + if args.config_channels: + cfg.read([args.config_channels]) + else: + remote = get_upstream_remote(args, "pmaports") + command = ["git", "show", f"{remote}/master:channels.cfg"] + stdout = pmb.helpers.run.user(args, command, args.aports, + output_return=True, check=False) + try: + cfg.read_string(stdout) + except configparser.MissingSectionHeaderError: + logging.info("NOTE: fix this by fetching your pmaports.git, e.g." + " with 'pmbootstrap pull'") + raise RuntimeError("Failed to read channels.cfg from" + f" '{remote}/master' branch of your local" + " pmaports clone") + + # Meta section + ret = {"channels": {}} + ret["meta"] = {"recommended": cfg.get("channels.cfg", "recommended")} + + # Channels + for channel in cfg.sections(): + if channel == "channels.cfg": + continue # meta section + + ret["channels"][channel] = {} + for key in ["description", "branch_pmaports", "branch_aports", + "mirrordir_alpine"]: + value = cfg.get(channel, key) + ret["channels"][channel][key] = value + + args.cache[cache_key] = ret + return ret + + def get_branches_official(args, name_repo): """ Get all branches that point to official release channels. :returns: list of supported branches, e.g. ["master", "3.11"] """ - # More sophisticated logic to figure out the branches will be added soon: - # https://gitlab.com/postmarketOS/postmarketos/issues/11 - return ["master"] + # This functions gets called with pmaports and aports_upstream, because + # both are displayed in "pmbootstrap status". But it only makes sense + # to display pmaports there, related code will be refactored soon (#1903). + if name_repo != "pmaports": + return ["master"] + + channels_cfg = parse_channels_cfg(args) + ret = [] + for channel, channel_data in channels_cfg["channels"].items(): + ret.append(channel_data["branch_pmaports"]) + return ret def pull(args, name_repo): diff --git a/pmb/parse/arguments.py b/pmb/parse/arguments.py index d8bd4999..d932d781 100644 --- a/pmb/parse/arguments.py +++ b/pmb/parse/arguments.py @@ -342,6 +342,9 @@ def arguments(): default=pmb.config.defaults["config"], help="path to pmbootstrap.cfg file (default in" " ~/.config/)") + parser.add_argument("--config-channels", + help="path to channels.cfg (which is by default" + " read from pmaports.git, origin/master branch)") parser.add_argument("-d", "--port-distccd", dest="port_distccd") parser.add_argument("-mp", "--mirror-pmOS", dest="mirrors_postmarketos", help="postmarketOS mirror, disable with: -mp=''," diff --git a/test/test_helpers_git.py b/test/test_helpers_git.py index 45db1ce9..4861cff6 100644 --- a/test/test_helpers_git.py +++ b/test/test_helpers_git.py @@ -7,6 +7,7 @@ import shutil import time import pmb_test # noqa +import pmb_test.const import pmb_test.git import pmb.helpers.git import pmb.helpers.logging @@ -16,7 +17,8 @@ import pmb.helpers.run @pytest.fixture def args(request): import pmb.parse - sys.argv = ["pmbootstrap", "init"] + cfg = f"{pmb_test.const.testdata}/channels.cfg" + sys.argv = ["pmbootstrap.py", "--config-channels", cfg, "init"] args = pmb.parse.arguments() args.log = args.work + "/log_testsuite.txt" pmb.helpers.logging.init(args) @@ -108,6 +110,19 @@ def test_get_upstream_remote(args, monkeypatch, tmpdir): assert func(args, name_repo) == "hello" +def test_parse_channels_cfg(args): + exp = {"meta": {"recommended": "edge"}, + "channels": {"edge": {"description": "Rolling release channel", + "branch_pmaports": "master", + "branch_aports": "master", + "mirrordir_alpine": "edge"}, + "stable": {"description": "For workgroups", + "branch_pmaports": "v20.05", + "branch_aports": "3.11-stable", + "mirrordir_alpine": "v3.11"}}} + assert pmb.helpers.git.parse_channels_cfg(args) == exp + + def test_pull_non_existing(args): assert pmb.helpers.git.pull(args, "non-existing-repo-name") == 1 diff --git a/test/testdata/channels.cfg b/test/testdata/channels.cfg new file mode 100644 index 00000000..6de251fb --- /dev/null +++ b/test/testdata/channels.cfg @@ -0,0 +1,15 @@ +# Reference: https://postmarketos.org/channels.cfg +[channels.cfg] +recommended=edge + +[edge] +description=Rolling release channel +branch_pmaports=master +branch_aports=master +mirrordir_alpine=edge + +[stable] +description=For workgroups +branch_pmaports=v20.05 +branch_aports=3.11-stable +mirrordir_alpine=v3.11