Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fixes for bugs noticed during usage #8

Merged
merged 5 commits into from
Jan 14, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
104 changes: 81 additions & 23 deletions AliceCli/install/install.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
import zipfile
from pathlib import Path
from shutil import which
import json
import tempfile

import click
import psutil
Expand Down Expand Up @@ -393,9 +395,7 @@ def prepareSdCard(ctx: click.Context): # NOSONAR

operatingSystem = platform.system().lower()

balenaExecutablePath = which('balena')
if balenaExecutablePath is None and operatingSystem == 'linux':
balenaExecutablePath = str(Path.joinpath(Path.cwd(), 'balena-cli', 'balena')) # default install path
balenaExecutablePath = getBalenaPath()

flasherAvailable = Path(balenaExecutablePath).exists()
downloadsPath = Path.home() / 'Downloads'
Expand All @@ -406,16 +406,16 @@ def prepareSdCard(ctx: click.Context): # NOSONAR
).execute()

installBalena = False
if doFlash and not flasherAvailable:
if not flasherAvailable:
installBalena = inquirer.confirm(
message='balena-cli was not found on your system. It is required for flashing SD cards, do you want to install it?',
message='balena-cli was not found on your system. It is required for working with SD cards, do you want to install it?',
default=True
)

if doFlash and not flasherAvailable and not installBalena:
commons.returnToMainMenu(ctx, pause=True, message='Well then, I cannot flash your SD card without the appropriate tool to do it')
if not flasherAvailable and not installBalena:
commons.returnToMainMenu(ctx, pause=True, message='Well then, I cannot work with your SD card without the appropriate tool to do it')
return
elif doFlash and not flasherAvailable and installBalena:
elif not flasherAvailable and installBalena:
commons.printInfo('Installing Balena-cli, please wait...')
balenaVersion = 'v13.1.1'
if operatingSystem == 'windows':
Expand Down Expand Up @@ -494,12 +494,10 @@ def prepareSdCard(ctx: click.Context): # NOSONAR
commons.printInfo('Checking for available SD card drives, please wait....')
drives = list()

output = subprocess.run(balenaCommand.split(), capture_output=True, shell=True).stdout.decode()
for line in output.split('\n'):
if not line.startswith(driveSep):
continue

drives.append(Choice(line.split(' ')[0], name=line))
output = subprocess.run(balenaCommand, capture_output=True, shell=True).stdout.decode()
sd_cards = getSdCards()
for sd_card in sd_cards:
drives.append(Choice(sd_card, name=sd_card))

if not drives:
commons.returnToMainMenu(ctx, pause=True, message='Please insert your SD card first')
Expand Down Expand Up @@ -560,17 +558,23 @@ def prepareSdCard(ctx: click.Context): # NOSONAR
# e.g. on /dev/sda drive /dev/sda1 is "boot" and /dev/sda2 is "rootfs"
# Lookup up the boot mount point path via lsblk

command = f'sudo lsblk --noheadings --list {drive}'
sd_cards = getSdCards()
command = f'sudo lsblk -o PATH,FSTYPE,LABEL,MOUNTPOINT --json'
output = subprocess.run(command, capture_output=True, shell=True).stdout.decode()
for line in output.split('\n'):
mountPoint = line.split(' ')[-1]
if not mountPoint.startswith(driveSep):
continue
drive = mountPoint
break # just take the first one
if not drive or not Path(drive).exists():
blk_devices = json.loads(output)
for device in blk_devices["blockdevices"]:
if device["path"].startswith(tuple(sd_cards)) and device["fstype"] == "vfat" and device["label"] == "boot":
drives.append(Choice(value=device, name=device['path']))

if len(drives) == 0:
commons.printError(f'For some reason I cannot find the SD boot partition mount point {drive}.')
commons.returnToMainMenu(ctx, pause=True, message="I'm really sorry, but I just can't continue without this info, sorry for wasting your time...")

if len(drives) == 1:
device = drives[0].value
commons.printInfo(f'Auto-selected {device["path"]}.')
drive = device

else:
j = 0
while len(drives) <= 0:
Expand All @@ -591,10 +595,27 @@ def prepareSdCard(ctx: click.Context): # NOSONAR

if not drive:
drive = inquirer.select(
message='Please select the SD `boot` partition',
message='Please select the correct SD `boot` partition',
choices=drives
).execute()

need_to_unmount = False
if operatingSystem == 'linux':
# if device has not been mounted yet, mount in temp directory
if drive["mountpoint"] is None:
need_to_unmount = True
mount_dir = tempfile.mkdtemp(prefix="alice-cli-mount-")
command = f"sudo mount {drive['path']} {mount_dir}"
result = subprocess.run(command, capture_output=True, shell=True)
if not result.returncode == 0:
commons.printError(f"Could not mount {drive['path']} to {mount_dir}.")
commons.returnToMainMenu(ctx, pause=True)
drive["mountpoint"] = mount_dir
commons.printInfo(f"Mounted {drive['path']} to {mount_dir} temporarily.")
else:
commons.printInfo(f"{drive['path']} is already mounted to {drive['mountpoint']}.")
drive = drive["mountpoint"]

# Now let's enable SSH and Wi-Fi on boot.
commons.printInfo('Adding ssh & wifi to SD boot....')
sshFile = Path(drive, 'ssh')
Expand All @@ -612,10 +633,21 @@ def prepareSdCard(ctx: click.Context): # NOSONAR
content += '}'
Path(drive, 'wpa_supplicant.conf').write_text(content)

if need_to_unmount:
command = f"sudo umount {drive}"
result = subprocess.run(command, capture_output=True, shell=True)
if not result.returncode == 0:
commons.printError(f"Could not unmount {drive}.")
commons.returnToMainMenu(ctx, pause=True)
commons.printInfo(f"Unmounted {drive}")
# only deletes empty dirs, so if unmounting failed for whatever reasons, we don't destroy anything
os.rmdir(mount_dir)

commons.returnToMainMenu(ctx, pause=True, message='SD card is ready. Please plug it in your device and boot it!')


def doDownload(url: str, destination: Path):
os.makedirs(os.path.dirname(destination), exist_ok = True)
with destination.open(mode='wb') as f:
response = requests.get(url, stream=True)
size = int(response.headers.get('content-length'))
Expand All @@ -624,3 +656,29 @@ def doDownload(url: str, destination: Path):
for data in response.iter_content(chunk_size=4096):
f.write(data)
progress.update(len(data))
def getSdCards():
operatingSystem = platform.system().lower()
if operatingSystem == 'linux':
balenaExecutablePath = getBalenaPath()
balenaCommand = f'{balenaExecutablePath} util available-drives'
driveSep = os.path.sep # typically '/'
else:
balenaCommand = 'balena util available-drives'
driveSep = '\\'

drives = list()

output = subprocess.run(balenaCommand, capture_output=True, shell=True).stdout.decode()
for line in output.split('\n'):
if not line.startswith(driveSep):
continue
drives.append(line.split()[0])

return drives

def getBalenaPath():
operatingSystem = platform.system().lower()
balenaExecutablePath = which('balena')
if balenaExecutablePath is None and operatingSystem == 'linux':
balenaExecutablePath = str(Path.joinpath(Path.cwd(), 'balena-cli', 'balena')) # default install path
return balenaExecutablePath
2 changes: 1 addition & 1 deletion AliceCli/utils/commons.py
Original file line number Diff line number Diff line change
Expand Up @@ -761,7 +761,7 @@ def validateHostname(hostname: str) -> str:
if len(hostname) > 253:
raise click.BadParameter('Hostname maximum length is 253')

allowed = re.compile(r'^([\w]*)$', re.IGNORECASE)
allowed = re.compile(r'^\w([\w-]*\w)?$', re.IGNORECASE)
if allowed.match(hostname):
return hostname
else:
Expand Down
3 changes: 2 additions & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,8 @@
'pytest~=6.2.5',
'coverage~=6.2',
'pytest-cov~=3.0.0',
'coveralls~=3.3.1'
'coveralls~=3.3.1',
'tomli=2.0.0'
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

mmh? What requires tomli?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not too versed in Python packaging, but this is the message I get when trying to start alice from a fresh venv after doing pip install --editable .:

(venv) user@box:/usr/src/AliceCLI-orig# alice
Traceback (most recent call last):
  File "/usr/src/AliceCLI-orig/venv/bin/alice", line 6, in <module>
    from pkg_resources import load_entry_point
  File "/usr/src/AliceCLI-orig/venv/lib/python3.8/site-packages/pkg_resources/__init__.py", line 3252, in <module>
    def _initialize_master_working_set():
  File "/usr/src/AliceCLI-orig/venv/lib/python3.8/site-packages/pkg_resources/__init__.py", line 3235, in _call_aside
    f(*args, **kwargs)
  File "/usr/src/AliceCLI-orig/venv/lib/python3.8/site-packages/pkg_resources/__init__.py", line 3264, in _initialize_master_working_set
    working_set = WorkingSet._build_master()
  File "/usr/src/AliceCLI-orig/venv/lib/python3.8/site-packages/pkg_resources/__init__.py", line 583, in _build_master
    ws.require(__requires__)
  File "/usr/src/AliceCLI-orig/venv/lib/python3.8/site-packages/pkg_resources/__init__.py", line 900, in require
    needed = self.resolve(parse_requirements(requirements))
  File "/usr/src/AliceCLI-orig/venv/lib/python3.8/site-packages/pkg_resources/__init__.py", line 786, in resolve
    raise DistributionNotFound(req, requirers)
pkg_resources.DistributionNotFound: The 'tomli; extra == "toml"' distribution was not found and is required by coverage

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's.... weird, coverage should install it as it is its own dep. Whatever, not a big deal :-)

],
classifiers=[
"Development Status :: 5 - Production/Stable",
Expand Down