Skip to content
This repository has been archived by the owner on Apr 17, 2024. It is now read-only.

Migrate to Python #158

Closed
wants to merge 33 commits into from
Closed

Conversation

timmypidashev
Copy link
Contributor

@timmypidashev timmypidashev commented Apr 12, 2022

Status

  • Ready

Summary

A migration to a python code base.
Supports running as a module with python -m breath. Another thought later long-term is to maintain PKGBUILD's of a packaged breath with for several various distributions. This would allows users to have a maintained version of breath's latest release without having to use the master branch for installing. This would also minimize issues since most users would be using stable versions tested to work.

  • Argparse command-line parser. Currently looks a little something like this:
$ python -m breath -h
usage: breath [options...]

A way to natively run Linux on modern Chromebooks without replacing firmware

options:
  -h, --help                                                                  show this help message and exit
  -t, --installtype {usb,iso}                                                 choose installation type (default: usb)
  -k, --keymap                                                                map keys to chromebook actions
  -d, --distro {arch,debian,fedora,ubuntu}                                    choose distro (default: ubuntu)
  -de, --desktop {cli,gnome,kde,minimal,deepin,budgie,fce,lxqt,mate,openbox}  choose desktop environment (default: cli)
  -hn, --hostname HOSTNAME                                                    set hostname (default: chromebook)
  -u, --username USERNAME                                                     set username (default: breath_user)
  -p, --password PASSWORD                                                     set password (default: breath_passwd)
  -sp, --systempasswd SYSTEMPASSWD                                            input system password for root access, needed to run breath!
  -vv, --verbose                                                              set installer output to verbose
  -v, --version                                                               output version information and exit

The command-line args are put into a dictionary: installation_options. Default looks a little something like this:

{'installtype': 'usb', 'keymap': False, 'distro': 'ubuntu', 'desktop': 'cli', 'hostname': 'chromebook', 'username': 'breath_user', 'password': 'breath_passwd', 'systempasswd': None, 'verbose': None, 'version': False}

This allows for a input-less installation, as all necessary features/settings are configured in one command. This, however, may get tedious and rather large, which is why the installer will ask all necessary input questions along with the toilet flare if no args are supplied.

  • Finally, ease of development. Using separate files for different common installation steps while managing everything in init is a very simple and common way to make python packages. The benefit at the end is the code base will be very similar in structure to the current one, while also being extremely simple to expand.

Tasks

  • Add argparse parser
  • Add user input menu(inquirer)
  • Add exception/error handler(errors.py)
  • Add system abstraction(system.py)
  • Add keymap feature
  • Add distributions support
  • Add desktop support
  • Add proper typehinting
  • Add support for Apple
  • Update this tasklist as developed.

I know this is a pretty hefty pr, and I also really don't have much time on my hands these next few coming weeks due to school, but I will do my best to continue working. It isn't as important as fixing up audio issues, bootloader, etc., but hopefully once all that is done we can make a nice repo that is maintainable longterm. @MilkyDeveloper please let me know if this is the direction you wish to take the repository, It's not an easy answer so take your time. If you like it but feel there are certain directions you wish to take differently, by all means let me know, I have opened access to maintainers for that very reason.

@MilkyDeveloper
Copy link
Collaborator

Wow!!! This is great. Once we have an initial Python branch, we can use transpilers like Js2Py, Rusthon, Grumpy, and py2many. I completely understand about slower work, I'm also slowing my work on this specific repo (I'm shifting focuses to cb-linux/bootloader). I think this is a good direction; once we have python as our base language, we can use transpilers or manually adapt to other languages.

Again, Huge thanks!!! No pressure, I completely understand school stuff.

@timmypidashev
Copy link
Contributor Author

@MilkyDeveloper Alright sounds good. Take your time, for now this branch really needs no rush, as it's not the main focus. I'll continue working on it several commits a week until its complete. From there we can keep going!

@timmypidashev
Copy link
Contributor Author

timmypidashev commented Apr 14, 2022

I have been trying to keep external libraries to a minimum(currently only using argparse, platform, distro, and inquirer), but I have to admit sometimes using a library is more efficient and simple if it does exactly what you want it to do without a hassle.

questions = [
      inquirer.List('install_type',
          message='Installation type',
          choices=['usb', 'iso'],
          default=defaults['install_type'],
      ),
      inquirer.List('distro',
          message='Distro of choice',
          choices=['arch', 'debian', 'fedora', 'ubuntu'],
          default=defaults['distro'],
      ),
      inquirer.List('desktop',
          message='Desktop environment',
          choices=['cli', 'gnome', 'kde', 'minimal', 'deepin', 'budgie', 'xfce', 'lxqt', 'mate', 'openbox'],
          default=defaults['desktop'],
      ),
      inquirer.Text('hostname',
          message='Breath Hostname',
          validate=lambda _, x: re.match('(?:^|\\s)[a-z]+(?:\\s|$)', x),
          default=defaults['hostname'],
      ),
      inquirer.Text('username',
          message='Breath Username',
          validate=lambda _, x: re.match('(?:^|\\s)[a-z]+(?:\\s|$)', x),
          default=defaults['username'],
      ),
      inquirer.Password('password',
          message='Breath Password',
          validate=lambda _, x: re.match('^\S+$', x),
      ),
      inquirer.Password('system_passwd',
          message=f'Root password for {os.getlogin()}',
          validate=lambda _, x: re.match('^\S+$', x),
      ),
      inquirer.Confirm('keymap',
          message='Map keys to chromebook actions?',
          default=True,
      ),
      inquirer.Confirm('verbose',
          message='Set verbose installation output?',
          default=True,
      ),
  ]

  user_input = inquirer.prompt(questions, theme=BreathInquirerTheme())

image

@MilkyDeveloper
Copy link
Collaborator

I'm completely fine with libraries (I abundantly use them on my private Python projects :D). This looks phenomenal ATM. Huge thanks for making this!

@timmypidashev
Copy link
Contributor Author

timmypidashev commented Apr 14, 2022

@MilkyDeveloper No problem :) The idea is basically argparse for speedy install options, inquirer for first time installations.
Everything is put into a dictionary, which is modified along the way based on whatever the user is doing, then passed on to the installer. So far I'm liking how its working.

@timmypidashev
Copy link
Contributor Author

timmypidashev commented Apr 19, 2022

@MilkyDeveloper It might be very useful to write the python implementation in such a way that it can also be used as a library. What I mean is any user can import a specific part and use it for a custom installation, etc. A very good example and my inspiration for this idea is the archinstall installer. If this is something you would like to allow breath to do, I will try my best and see how that goes. The code doesnt have to change much, just the runtime structure. __init__has to be independent of the rest of the program basically. I am not well-versed in the making of python libraries either, so any input is appreciated. That being said, this type of structure would allow breath to be scripted into other programs for custom installations. I'm going to build the installer with this concept in mind, but not implement it as of now, since this is too much overhead at the moment. EDIT: Ok so turns out, most of the structure is already properly set up. Just need to run the parser in the run_as_a_module() function and everything will work just fine as a library, unless breath is run as a module, at which point the default installation is followed in init.

@timmypidashev
Copy link
Contributor Author

Woo code compacting, 400 to 386 and more functional. Feels good :D

@timmypidashev
Copy link
Contributor Author

Alrighty where pr #133 failed with password autoprompts, the subprocess module has fixed quite elegantly as of 7ba64ca

@timmypidashev
Copy link
Contributor Author

timmypidashev commented Apr 27, 2022

Turns out 'system_passwd' isn't needed as the run_root_cmd() will cache the root passwd in stdin, which means a password prompt for root only shows up once. The password is also removed from cache once the terminal instance is closed, just as it should.

@ArtemPozharov
Copy link

Will this program be cross-platform due to using Python?

@timmypidashev
Copy link
Contributor Author

timmypidashev commented Apr 27, 2022

@ArtemPozharov Windows and Mac systems are not currently supported, though I have already added checks for both unsupported platforms(see system.py), so supporting them is just a matter of trial and error, but I'm confident it can be done. I have added these to my task list above, so I'll see what I can do.

@MilkyDeveloper
Copy link
Collaborator

MilkyDeveloper commented Apr 27, 2022

Bash even runs on Windows/MacOS. The main issue is partitioned drives and running chroot, the former of which is just impossible. We already have Crostini support, so I think it would be feasible to exclude cross platform support for Windows. This could easily be dockerized, though.

@timmypidashev
Copy link
Contributor Author

@MilkyDeveloper I didn't know about Windows not supporting chroot. This makes sense though, I suggest we just point Windows users to create a virtual Ubuntu install on Windows if they really can't use another os.

@MilkyDeveloper
Copy link
Collaborator

I think WSL2 would work well too. Windows 11 and 10 Insider has direct USB passthrough.

@MilkyDeveloper
Copy link
Collaborator

Also, when you get to the more complicated logic in utils/download.sh, you could look in my incomplete Powershell (Linux version) port.

@timmypidashev
Copy link
Contributor Author

timmypidashev commented Apr 27, 2022

Also, when you get to the more complicated logic in utils/download.sh, you could look in my incomplete Powershell (Linux version) port.

Will do :)

@timmypidashev
Copy link
Contributor Author

I think WSL2 would work well too. Windows 11 and 10 Insider has direct USB passthrough.

Depending on what the user installs, wsl would probably be the best option for a Windows user without breath having to do any extra logic.

@MilkyDeveloper
Copy link
Collaborator

MilkyDeveloper commented Apr 27, 2022

Nice progress. You could use the python library Plumbum if you like the syntax.

Here's an example:

from plumbum import local
wget = local['wget']
wget['https://example.com']()

@timmypidashev
Copy link
Contributor Author

@MilkyDeveloper Thanks, I will. What I'm finding is that most commands that need to be run are better off when abstracted by python pre-configured functions that can catch errors, etc. In terms of the current progress, what I'm probably going to do is create static methods for the installer and figure it all out like a scratchpad, then sort and restructure to match something along the likes of BreathSystem, because quite honestly BreathInstaller is becoming a mess.

@timmypidashev
Copy link
Contributor Author

Ooh plumbum seems to have it all in terms of shell scripting. I think this is a good idea, thanks for the suggestion.

@timmypidashev
Copy link
Contributor Author

timmypidashev commented Apr 28, 2022

Plumbum also provides easy color access, which eliminates the need for cprint and the like. Overall it seems like a great library to build breath up with. Even argparse can be stripped out with plumbum's cli module. I'm going to give it a try and see how it goes.

@timmypidashev
Copy link
Contributor Author

timmypidashev commented Apr 28, 2022

from plumbum import cli

class MyApp(cli.Application):
    verbose = cli.Flag(["v", "verbose"], help = "If given, I will be very talkative")

    def main(self, filename):
        print(f"I will now read {filename}")
        if self.verbose:
            print("Yadda " * 200)

if __name__ == "__main__":
    MyApp.run()

Compared to the current parser implementation, this is a: more pythonic, and b: way more simple.

image
Time to iterate.

@timmypidashev
Copy link
Contributor Author

I'm going to close this pr as it has gotten quite tedious trying to get a CLI interface and use the core of breath. I have found it even more frustrating to keep an elegant standard as there really is no proper way to maintain Python and Bash scripts together. Will keep looking for a solution in my spare time however.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants