diff --git a/pmb/build/_package.py b/pmb/build/_package.py index 245ad0f1..006bc5a5 100644 --- a/pmb/build/_package.py +++ b/pmb/build/_package.py @@ -478,7 +478,7 @@ def package(args, pkgname, arch=None, force=False, strict=False, :param pkgname: package name to be built, as specified in the APKBUILD :param arch: architecture we're building for (default: native) - :param force: even build, if not necessary + :param force: allways build, even if not necessary :param strict: avoid building with irrelevant dependencies installed by letting abuild install and uninstall all dependencies. :param skip_init_buildenv: can be set to False to avoid initializing the diff --git a/pmb/config/__init__.py b/pmb/config/__init__.py index 6e6f16ac..fff1a5ac 100644 --- a/pmb/config/__init__.py +++ b/pmb/config/__init__.py @@ -616,3 +616,8 @@ newapkbuild_arguments_switches_other = [ # # Patterns of package names to ignore for automatic pmaport upgrading ("pmbootstrap aportupgrade --all") upgrade_ignore = ["device-*", "firmware-*", "linux-*", "postmarketos-*", "*-aarch64", "*-armhf", "*-armv7"] + +# +# SIDELOAD +# +sideload_sudo_prompt = "[sudo] password for %u@%h: " diff --git a/pmb/helpers/frontend.py b/pmb/helpers/frontend.py index 8986bffe..7a42b567 100644 --- a/pmb/helpers/frontend.py +++ b/pmb/helpers/frontend.py @@ -9,6 +9,7 @@ import sys import pmb.aportgen import pmb.build import pmb.build.autodetect +import pmb.sideload import pmb.chroot import pmb.chroot.initfs import pmb.chroot.other @@ -113,6 +114,15 @@ def checksum(args): pmb.build.checksum.update(args, package) +def sideload(args): + arch = args.deviceinfo["arch"] + if args.arch: + arch = args.arch + user = args.user + host = args.host + pmb.sideload.sideload(args, user, host, arch, args.install_key, args.packages) + + def chroot(args): # Suffix suffix = _parse_suffix(args) diff --git a/pmb/parse/arguments.py b/pmb/parse/arguments.py index 0d7a8dae..b58702a4 100644 --- a/pmb/parse/arguments.py +++ b/pmb/parse/arguments.py @@ -135,6 +135,23 @@ def arguments_export(subparser): return ret +def arguments_sideload(subparser): + ret = subparser.add_parser("sideload", help="Push packages to a running" + " phone connected over usb or wifi") + add_packages_arg(ret, nargs="+") + ret.add_argument("--host", help="ip of the device over wifi" + " (defaults to 172.16.42.1)", + default="172.16.42.1") + ret.add_argument("--user", help="use a different username than the" + " one set in init") + ret.add_argument("--arch", help="use a different architecture than the one" + " set in init") + ret.add_argument("--install-key", help="install the apk key from this" + " machine if needed", + action="store_true", dest="install_key") + return ret + + def arguments_flasher(subparser): ret = subparser.add_parser("flasher", help="flash something to the" " target device") @@ -533,6 +550,7 @@ def arguments(): arguments_repo_missing(sub) arguments_kconfig(sub) arguments_export(sub) + arguments_sideload(sub) arguments_flasher(sub) arguments_initfs(sub) arguments_qemu(sub) diff --git a/pmb/sideload/__init__.py b/pmb/sideload/__init__.py new file mode 100644 index 00000000..50173c4e --- /dev/null +++ b/pmb/sideload/__init__.py @@ -0,0 +1,88 @@ +# Copyright 2020 Martijn Braam +# SPDX-License-Identifier: GPL-3.0-or-later +import glob +import os +import logging + +import pmb.helpers.run +import pmb.parse.apkindex +import pmb.config.pmaports +import pmb.build + + +def scp_abuild_key(args, user, host): + """ Copy the building key of the local installation to the target device, + so it trusts the apks that were signed here. + :param user: target device ssh username + :param host: target device ssh hostname """ + + keys = glob.glob(os.path.join(args.work, "config_abuild", "*.pub")) + key = keys[0] + key_name = os.path.basename(key) + + logging.info(f"Copying signing key ({key_name}) to {user}@{host}") + command = ['scp', key, f'{user}@{host}:/tmp'] + pmb.helpers.run.user(args, command, output="interactive") + + logging.info(f"Installing signing key at {user}@{host}") + keyname = os.path.join("/tmp", os.path.basename(key)) + remote_cmd = ['sudo', '-p', pmb.config.sideload_sudo_prompt, + '-S', 'mv', '-n', keyname, "/etc/apk/keys/"] + remote_cmd = pmb.helpers.run.flat_cmd(remote_cmd) + command = ['ssh', '-t', f'{user}@{host}', remote_cmd] + pmb.helpers.run.user(args, command, output="tui") + + +def ssh_install_apks(args, user, host, paths): + """ Copy binary packages via SCP and install them via SSH. + :param user: target device ssh username + :param host: target device ssh hostname + :param paths: list of absolute paths to locally stored apks + :type paths: list """ + + remote_paths = [] + for path in paths: + remote_paths.append(os.path.join('/tmp', os.path.basename(path))) + + logging.info(f"Copying packages to {user}@{host}") + command = ['scp'] + paths + [f'{user}@{host}:/tmp'] + pmb.helpers.run.user(args, command, output="interactive") + + logging.info(f"Installing packages at {user}@{host}") + add_cmd = ['sudo', '-p', pmb.config.sideload_sudo_prompt, + '-S', 'apk', 'add'] + remote_paths + add_cmd = pmb.helpers.run.flat_cmd(add_cmd) + clean_cmd = pmb.helpers.run.flat_cmd(['rm'] + remote_paths) + command = ['ssh', '-t', f'{user}@{host}', f'{add_cmd}; {clean_cmd}'] + pmb.helpers.run.user(args, command, output="tui") + + +def sideload(args, user, host, arch, copy_key, pkgnames): + """ Build packages if necessary and install them via SSH. + + :param user: target device ssh username + :param host: target device ssh hostname + :param arch: target device architecture + :param copy_key: copy the abuild key too + :param pkgnames: list of pkgnames to be built """ + + paths = [] + channel = pmb.config.pmaports.read_config(args)["channel"] + + for pkgname in pkgnames: + data_repo = pmb.parse.apkindex.package(args, pkgname, arch, True) + apk_file = f"{pkgname}-{data_repo['version']}.apk" + host_path = os.path.join(args.work, "packages", channel, arch, + apk_file) + if not os.path.isfile(host_path): + pmb.build.package(args, pkgname, arch, force=True) + + if not os.path.isfile(host_path): + raise RuntimeError(f"The package '{pkgname}' could not be built") + + paths.append(host_path) + + if copy_key: + scp_abuild_key(args, user, host) + + ssh_install_apks(args, user, host, paths) diff --git a/test/static_code_analysis.sh b/test/static_code_analysis.sh index 0bd7d48c..40e08bc4 100755 --- a/test/static_code_analysis.sh +++ b/test/static_code_analysis.sh @@ -95,6 +95,7 @@ py_files=" pmb/parse/cpuinfo.py pmb/parse/version.py pmb/qemu/__init__.py + pmb/sideload/__init__.py pmbootstrap.py test/pmb_test/__init__.py test/pmb_test/const.py