Add sideload command

The sideload command runs the supplied names through the pmbootstrap
buildsystem to make sure they're up-to-date, then uses scp from the host
to copy the built apks to /tmp on the phone and installs them through
ssh.

If the --install-key option is set then it will also copy over the apk
key that's used for signing the packages built by pmbootstrap in case
the postmarketOS install on the device isn't build by the same machine
as you're sideloading from.
This commit is contained in:
Martijn Braam 2020-04-03 18:09:36 +02:00
parent 5cbde81842
commit 1921fc5f27
No known key found for this signature in database
GPG Key ID: C4280ACB000B060F
6 changed files with 123 additions and 1 deletions

View File

@ -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

View File

@ -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: "

View File

@ -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)

View File

@ -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)

88
pmb/sideload/__init__.py Normal file
View File

@ -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)

View File

@ -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