mirror of
https://gitlab.postmarketos.org/postmarketOS/pmbootstrap.git
synced 2025-07-13 03:19:47 +03:00
254 lines
7.9 KiB
Python
254 lines
7.9 KiB
Python
# Copyright 2023 Oliver Smith
|
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
|
import configparser
|
|
from pathlib import Path
|
|
from pmb.core.pkgrepo import (
|
|
pkgrepo_default_path,
|
|
pkgrepo_name,
|
|
pkgrepo_paths,
|
|
pkgrepo_relative_path,
|
|
)
|
|
from pmb.helpers import logging
|
|
import os
|
|
import sys
|
|
|
|
import pmb.config
|
|
from pmb.meta import Cache
|
|
import pmb.helpers.git
|
|
import pmb.helpers.pmaports
|
|
import pmb.parse.version
|
|
|
|
|
|
def clone() -> None:
|
|
logging.info("Setting up the native chroot and cloning the package build recipes (pmaports)...")
|
|
|
|
# Set up the native chroot and clone pmaports
|
|
pmb.helpers.git.clone("pmaports")
|
|
|
|
|
|
def check_version_pmaports(real: str) -> None:
|
|
# Compare versions
|
|
min = pmb.config.pmaports_min_version
|
|
if pmb.parse.version.compare(real, min) >= 0:
|
|
return
|
|
|
|
# Outated error
|
|
logging.info(
|
|
"NOTE: your pmaports folder has version "
|
|
+ real
|
|
+ ", but"
|
|
+ " version "
|
|
+ min
|
|
+ " is required."
|
|
)
|
|
raise RuntimeError("Run 'pmbootstrap pull' to update your pmaports.")
|
|
|
|
|
|
def check_version_pmbootstrap(min_ver: str) -> None:
|
|
# Compare versions
|
|
real = pmb.__version__
|
|
if pmb.parse.version.compare(real, min_ver) >= 0:
|
|
return
|
|
|
|
# Show versions
|
|
logging.info(
|
|
f"NOTE: you are using pmbootstrap version {real}, but version {min_ver} is required."
|
|
)
|
|
|
|
# Error for git clone
|
|
pmb_src = pmb.config.pmb_src
|
|
if os.path.exists(pmb_src / ".git"):
|
|
raise RuntimeError(
|
|
"Please update your local pmbootstrap repository."
|
|
f" Usually with: 'git -C \"{pmb_src}\" pull'"
|
|
)
|
|
|
|
# Error for package manager installation
|
|
raise RuntimeError(
|
|
"Please update your pmbootstrap version (with your"
|
|
" distribution's package manager, or with pip, "
|
|
" depending on how you have installed it). If that is"
|
|
" not possible, consider cloning the latest version"
|
|
" of pmbootstrap from git."
|
|
)
|
|
|
|
|
|
@Cache()
|
|
def read_config_repos() -> dict[str, configparser.SectionProxy]:
|
|
"""Read the sections starting with "repo:" from pmaports.cfg."""
|
|
|
|
cfg = configparser.ConfigParser()
|
|
cfg.read(f"{pkgrepo_default_path()}/pmaports.cfg")
|
|
|
|
ret = {}
|
|
for section in cfg.keys():
|
|
if not section.startswith("repo:"):
|
|
continue
|
|
repo = section.split("repo:", 1)[1]
|
|
ret[repo] = cfg[section]
|
|
|
|
return ret
|
|
|
|
|
|
@Cache("aports", "add_systemd_prefix")
|
|
def read_config(
|
|
aports: Path | None = None, add_systemd_prefix: bool = True
|
|
) -> configparser.SectionProxy:
|
|
"""Read and verify pmaports.cfg. If aports is not
|
|
specified and systemd is enabled, the returned channel
|
|
will be the systemd one (e.g. systemd-edge instead of edge)
|
|
since we'll use the first pkgrepo which is systemd."""
|
|
if aports is None:
|
|
aports = pkgrepo_paths()[0]
|
|
|
|
systemd = pkgrepo_name(aports) == "systemd"
|
|
# extra-repos don't have a pmaports.cfg
|
|
# so jump up the main aports dir
|
|
if "extra-repos" in aports.parts:
|
|
aports = pkgrepo_relative_path(aports)[0]
|
|
|
|
# Migration message
|
|
if not os.path.exists(aports):
|
|
logging.error(f"ERROR: pmaports dir not found: {aports}")
|
|
logging.error("Did you run 'pmbootstrap init'?")
|
|
sys.exit(1)
|
|
|
|
# Require the config
|
|
path_cfg = aports / "pmaports.cfg"
|
|
if not os.path.exists(path_cfg):
|
|
raise RuntimeError(f"Invalid pmaports repository, could not find the config: {path_cfg}")
|
|
|
|
# Load the config
|
|
cfg = configparser.ConfigParser()
|
|
cfg.read(path_cfg)
|
|
ret = cfg["pmaports"]
|
|
|
|
# Version checks
|
|
check_version_pmaports(ret["version"])
|
|
check_version_pmbootstrap(ret["pmbootstrap_min_version"])
|
|
|
|
# Translate legacy channel names
|
|
ret["channel"] = pmb.helpers.pmaports.get_channel_new(ret["channel"])
|
|
|
|
if systemd and add_systemd_prefix:
|
|
ret["channel"] = "systemd-" + ret["channel"]
|
|
|
|
return ret
|
|
|
|
|
|
def all_channels() -> list[str]:
|
|
"""Get a list of all channels for all pkgrepos."""
|
|
ret = set()
|
|
for repo in pkgrepo_paths():
|
|
ret.add(read_config(repo)["channel"])
|
|
|
|
logging.verbose(f"all_chanels: {ret}")
|
|
return list(ret)
|
|
|
|
|
|
def read_config_channel() -> dict[str, str]:
|
|
"""Get the properties of the currently active channel in pmaports.git.
|
|
|
|
As specified in channels.cfg (https://postmarketos.org/channels.cfg).
|
|
|
|
:returns: {"description: ...,
|
|
"branch_pmaports": ...,
|
|
"branch_aports": ...,
|
|
"mirrordir_alpine": ...}
|
|
|
|
"""
|
|
aports = pkgrepo_default_path()
|
|
channel = read_config(aports)["channel"]
|
|
channels_cfg = pmb.helpers.git.parse_channels_cfg(aports)
|
|
|
|
if channel in channels_cfg["channels"]:
|
|
return channels_cfg["channels"][channel]
|
|
|
|
# Channel not in channels.cfg, try to be helpful
|
|
branch = pmb.helpers.git.rev_parse(aports, extra_args=["--abbrev-ref"])
|
|
remote = pmb.helpers.git.get_upstream_remote(aports)
|
|
logging.info(
|
|
"NOTE: fix the error by rebasing or cherry picking relevant"
|
|
" commits from this branch onto a branch that is on a"
|
|
" supported channel: master, v24.06, …"
|
|
)
|
|
logging.info(
|
|
"NOTE: as workaround, you may pass --config-channels with a"
|
|
" custom channels.cfg. Reference:"
|
|
" https://postmarketos.org/channels.cfg"
|
|
)
|
|
raise RuntimeError(
|
|
f"Current branch '{branch}' of pmaports.git is on"
|
|
f" channel '{channel}', but this channel was not"
|
|
f" found in channels.cfg (of {remote}/master"
|
|
" branch). Looks like a very old branch."
|
|
)
|
|
|
|
|
|
def init() -> None:
|
|
if not os.path.exists(pkgrepo_default_path()):
|
|
clone()
|
|
read_config()
|
|
|
|
|
|
def switch_to_channel_branch(channel_new: str) -> bool:
|
|
"""Checkout the channel's branch in pmaports.git.
|
|
|
|
:channel_new: channel name (e.g. "edge", "v21.03")
|
|
|
|
:returns: True if another branch was checked out, False otherwise
|
|
"""
|
|
aports = pkgrepo_default_path()
|
|
|
|
# Check current pmaports branch channel
|
|
channel_current = read_config(aports)["channel"]
|
|
if channel_current == channel_new:
|
|
return False
|
|
|
|
# list current and new branches/channels
|
|
channels_cfg = pmb.helpers.git.parse_channels_cfg(aports)
|
|
branch_new = channels_cfg["channels"][channel_new]["branch_pmaports"]
|
|
branch_current = pmb.helpers.git.rev_parse(aports, extra_args=["--abbrev-ref"])
|
|
if (
|
|
branch_current == "master_staging_systemd"
|
|
and channel_new == "edge"
|
|
and pmb.config.is_systemd_selected()
|
|
):
|
|
logging.info("NOTE: master_staging_systemd was merged into master, switching to it")
|
|
|
|
logging.info(
|
|
f"Currently checked out branch '{branch_current}' of"
|
|
f" pmaports.git is on channel '{channel_current}'."
|
|
)
|
|
logging.info(f"Switching to branch '{branch_new}' on channel '{channel_new}'...")
|
|
|
|
# Make sure we don't have mounts related to the old channel
|
|
pmb.chroot.shutdown()
|
|
|
|
# Attempt to switch branch (git gives a nice error message, mentioning
|
|
# which files need to be committed/stashed, so just pass it through)
|
|
if pmb.helpers.run.user(["git", "checkout", branch_new], aports, "interactive", check=False):
|
|
raise RuntimeError(
|
|
"Failed to switch branch. Go to your pmaports and"
|
|
" fix what git complained about, then try again: "
|
|
f"{aports}"
|
|
)
|
|
|
|
# Verify pmaports.cfg on new branch
|
|
read_config()
|
|
return True
|
|
|
|
|
|
def install_githooks() -> None:
|
|
aports = pkgrepo_default_path()
|
|
hooks_dir = aports / ".githooks"
|
|
if not hooks_dir.exists():
|
|
logging.info("No .githooks dir found")
|
|
return
|
|
for h in os.listdir(hooks_dir):
|
|
src = os.path.join(hooks_dir, h)
|
|
# Use git default hooks dir so users can ignore our hooks
|
|
# if they dislike them by setting "core.hooksPath" git config
|
|
dst = aports / ".git/hooks" / h
|
|
if pmb.helpers.run.user(["cp", src, dst], check=False):
|
|
logging.warning(f"WARNING: Copying git hook failed: {dst}")
|