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

Add Support for NixOS #47

Open
thw26 opened this issue Jan 15, 2024 · 15 comments · May be fixed by #80
Open

Add Support for NixOS #47

thw26 opened this issue Jan 15, 2024 · 15 comments · May be fixed by #80
Labels
enhancement New feature or request help wanted Extra attention is needed installer This issue is related to the install routine

Comments

@thw26
Copy link
Collaborator

thw26 commented Jan 15, 2024

No description provided.

@thw26 thw26 added enhancement New feature or request help wanted Extra attention is needed installer This issue is related to the install routine labels Jan 15, 2024
@thw26 thw26 added this to the Broad OS Support milestone Jan 15, 2024
@eric-hansen
Copy link

Since I run NixOS I can try to help on this.

@thw26
Copy link
Collaborator Author

thw26 commented Feb 19, 2024

Great! There are some basic things I need to know. Here is what is used for other distros as an example:

if shutil.which('apt') is not None: # debian, ubuntu
config.PACKAGE_MANAGER_COMMAND_INSTALL = "apt install -y"
config.PACKAGE_MANAGER_COMMAND_REMOVE = "apt remove -y"
config.PACKAGE_MANAGER_COMMAND_QUERY = "dpkg -l | grep -E '^.i '" # IDEA: Switch to Python APT library? See https://github.com/FaithLife-Community/LogosLinuxInstaller/pull/33#discussion_r1443623996
config.PACKAGES = "binutils cabextract fuse wget winbind"
config.L9PACKAGES = "" # FIXME: Missing Logos 9 Packages
config.BADPACKAGES = "appimagelauncher"

I need the NixOS equivalents for these variables.

@eric-hansen
Copy link

So I'm not sure how to explain this on a super-high level, but Nix (& NixOS since it just is an OS built around the Nix packager) doesn't operate in the same way as things like apt, yum, etc... Technically you can do it that way, but it wouldn't persist.

Instead, the Nix way would be creating a default.nix file in the root directory of the project and configuring it that way. It provides outputs for systems which basically is what gets translated to installed packages/binaries.

You can think of it in a way as a atomic-structured zip/tarball file, containing all the things needed to run Logos on Linux in Nix. The difference being (outside of the config) is it will pull down the depedencies during the build/install process.

If there's any questions about this let me know. It's a rather niche tooling.

@thw26
Copy link
Collaborator Author

thw26 commented Feb 20, 2024

I will need whatever is required to replace these functions for NixOS then:

def query_packages(packages, mode="install"):
if config.SKIP_DEPENDENCIES:
return
missing_packages = []
conflicting_packages = []
for p in packages:
command = f"{config.PACKAGE_MANAGER_COMMAND_QUERY}{p}"
logging.debug(f"pkg query command: \"{command}\"")
result = subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True, text=True)
logging.debug(f"pkg query result: {result.returncode}")
if result.returncode != 0 and mode == "install":
missing_packages.append(p)
elif result.returncode == 0 and mode == "remove":
conflicting_packages.append(p)
msg = 'None'
if mode == "install":
if missing_packages:
msg = f"Missing packages: {' '.join(missing_packages)}"
logging.info(f"Missing packages: {msg}")
return missing_packages
if mode == "remove":
if conflicting_packages:
msg = f"Conflicting packages: {' '.join(conflicting_packages)}"
logging.info(f"Conflicting packages: {msg}")
return conflicting_packages
def install_packages(packages):
if config.SKIP_DEPENDENCIES:
return
if packages:
command = f"{config.SUPERUSER_COMMAND} {config.PACKAGE_MANAGER_COMMAND_INSTALL} {' '.join(packages)}"
logging.debug(f"install_packages cmd: {command}")
subprocess.run(command, shell=True, check=True)
def remove_packages(packages):
if config.SKIP_DEPENDENCIES:
return
if packages:
command = f"{config.SUPERUSER_COMMAND} {config.PACKAGE_MANAGER_COMMAND_REMOVE} {' '.join(packages)}"
logging.debug(f"remove_packages cmd: {command}")
subprocess.run(command, shell=True, check=True)

Please don't hesitate to tell me what's necessary; I have a background in Linux systems administration. I simply haven't used NixOS, but have installed via tar.

When I query for the distro I will need to know what the NixOS equivalent is in /etc/os-release if it is not standard.

config.OS_NAME = distro.id() # FIXME: Not working. Returns "Linux".
logging.info(f"OS name: {config.OS_NAME}")
config.OS_RELEASE = distro.version()
logging.info(f"OS release: {config.OS_RELEASE}")
return config.OS_NAME, config.OS_RELEASE

Then all I will do is initiate custom functions for the distro's install routine like I do for SteamOS:

def steam_preinstall_dependencies():

def steam_postinstall_dependencies():

Basically, I need to know how to query installed packages (to automate installs), install packages, and remove packages.

@eric-hansen
Copy link

Understand. But, the thing is that's not how Nix works.

In Nix, you declare what you want installed in a configuration file, and then tell Nix to rebuild the OS with those packages (or home-manager if the end user is using that as a per-user declarative installer).

To give you an example, wine-staging is typically required for Logos to be installed. To do so in Nix-land, you can search for the package (which I'll demonstrate from the web UI):

https://search.nixos.org/packages?channel=23.11&show=wine-staging&from=0&size=50&sort=relevance&type=packages&query=wine-staging

Then if you click on the "wine-staging" package, you'll see the various ways to install the package.

There is the option of "nix-env" installing, but is not the recommended nor Nix-way of installing things.

As mentioned before, there's "nix-shell" which will let you install it as well, but is destroyed either on shell exit or garbage collection as it's meant to be used as a playground.

Essentially if the idea is to do this the proper way, then a default.nix or flake.nix (or both) need to be created in the root. When someone running Nix wants to install Logos they would define it as an input in their own configuration via github:FaithLife-Community/LogosLinuxInstaller and it will pull down the relevant .nix file and build the Wine application up from that. Ideally this would also be converted to a nixpkgs option so it will show up when querying for packages like above, but would definitely require following the Nix way and so is more long-term.

Nix doesn't encourage or endorse standard package manager flow as it's anti-thesis to what Nix tries to do. Atomic, reproducible builds with no dependency conflicts.

Also, I really don't know if all those dependencies are needed for Nix like cabextract. I just installed wine-staging-Full and ran the 29.x MSI installer and Logos starts up fine for me. So I feel integrating this into the Nix ecosystem will be less troublesome than systems like Ubuntu where a bunch of externals are also required to be maintained.

@thw26
Copy link
Collaborator Author

thw26 commented Feb 20, 2024

cabextract is used for Logos 9, but the plan is to remove support for Logos 9 in the future (#53). We still have to rework the required packages for the Python program (#1).

Okay. So if there is a way for us then to detect the OS and have a target for a *.nix file for the build (since this is just Python code), we can provide that in repo or in the program to facilitate installing for NixOS, whether the code is built locally by the system or if the binary can be used.. I'm happy to have a custom function for this for automated installation, even if it is just downloading a file and placing it in the proper directory.

Also, as of Wine 8.16+, we can now use Wine-devel rather than Wine-staging.

If you have an idea how this can be accomplished in program, I am happy to provide this, or I welcome PRs! Most of what I do in program is test whether a package is installed or not, in order to verify that we can proceed—a quick search query says that we should create configuration.nix:

environment.etc."current-system-packages".text =

let

packages = builtins.map (p: "${[p.name](https://p.name/)}") config.environment.systemPackages;

sortedUnique = builtins.sort builtins.lessThan (lib.unique packages);

formatted = builtins.concatStringsSep "\n" sortedUnique;

in

formatted;

which creates the file /etc/current-system-packages. If this will work, we can then just parse that file to determine if we have what we need, e.g., libfuse3 for AppImage installations.

@eric-hansen
Copy link

I don't normally do things with Python, so I had to do some research on the Nix-way of packaging Python.

With that said, locally I created a shell.nix file and am able to get into the TUI installer just fine. But since the script is determined to install everything on the OS level, it bombs:

[nix-shell:~/Code/personal/LogosLinuxInstaller]$ ./LogosLinuxInstaller.py -C
2024-03-03 15:51:29 CRITICAL: The script could not determine your nixos install's package manager or it is unsupported. Your computer is missing the command(s) []. Please install your distro's package(s) associated with [] for nixos.
If you need help, please consult:
https://github.com/FaithLife-Community/LogosLinuxInstaller/wiki
https://t.me/linux_logos
https://matrix.to/#/#logosbible:matrix.org
Killed

I think first we need to determine how to handle scenarios where package installs should not be determined by the package itself.

The things Logos install needs is just Wine, as I was able to install it manually fine with a fresh wine prefix and no special packages like cabextract. I get that's needed for Logos 9, but for Logos 10 that hasn't been the case. So the question currently is what is an acceptable solution to the FaithLife-Community to handle this "edge" case?

If you want, I can create a WIP PR with my changes even as it's not specific to NixOS so anyone even just using Nix itself will be able to run this. Just, as it currently stands, the scripts in this repo don't allow for more esoteric ways of installing Logos.

@thw26
Copy link
Collaborator Author

thw26 commented Mar 4, 2024

The flag I use is the distro name. So what I need to know is how we can determine the distro name.

This is done here at present.

config.OS_NAME = distro.id() # FIXME: Not working. Returns "Linux".
logging.info(f"OS name: {config.OS_NAME}")
config.OS_RELEASE = distro.version()
logging.info(f"OS release: {config.OS_RELEASE}")
return config.OS_NAME, config.OS_RELEASE

If we can get that, I can set up a special case for NixOS.

Normally I do this through the package manager, but in the case of Debian, I have to do special things just for it, therefore I can't just use apt for instance but need Debian specifically. Same for SteamOS.

@eric-hansen
Copy link

Gotcha. Sorry for the confusion earlier.

So the best way to know if it's a NixOS install is by checking any/one of the following:

  • /etc/NIXOS file exists (it'll be empty)
  • /etc/nixos directory exists (houses system config)

You can check /etc/nix directory exists but that's more specific to the Nix pkg manager and not OS itself, so YMMV.

@thw26
Copy link
Collaborator Author

thw26 commented Mar 6, 2024

Installed NixOS in a virtual machine.

Python's distro library works. Install Python Environment and the distro library.

>>> import distro
>>> distro.id()
'nixos'
>>> distro.version()
'23.11'

It appears to be reading /etc/lsb-release.

With that out of the way then, what we need to do is figure out what now needs to be installed in NixOS to make the magic happen. Would you happen to have a sample file which can be used to install the dependencies?

@thw26 thw26 linked a pull request Mar 12, 2024 that will close this issue
@thw26
Copy link
Collaborator Author

thw26 commented Mar 12, 2024

@eric-hansen: I opened #80 to begin accepting pushes. Based on what I found, I have added it to the distro-detecting code. Now we just need to figure out a proper way to install everything through the program.

@eric-hansen
Copy link

@eric-hansen: I opened #80 to begin accepting pushes. Based on what I found, I have added it to the distro-detecting code. Now we just need to figure out a proper way to install everything through the program.

If you plan on using AppImages for this, then there's no install, you need to use a Nix (preferably a Flake) file to wrap the *.AppImage around.

For example, and as a minimum-requirement use-case, here is how I install Tinkerwell, another AppImage app:

    (appimageTools.wrapType2 {
      name = "Tinkerwell";
      src = fetchurl {
        url = "https://download.tinkerwell.app/tinkerwell/Tinkerwell-4.7.0.AppImage";
        sha256 = "xBr9Irtg/R3mlIY//2SCrkivP3gMvJCa7AVQverXQSU=";
      };
    })

There's more boilerplate around it that I removed, but that's for another point/question if you want this done properly.

As I stated before, Nix and NixOS shouldn't be thought of as a usual Linux package manager or distro. It has it's own unique and specific way of going about things. I've also found that, at least in Wine 9.3-staging, Logos will install and show the splash screen on start but then crash (and this is with 29.1.x Logos). I've not had an issue yet outside of some graphical weirdness using Proton GE.

With that said, as it stands currently the following questions need to be answered in order to determine how to approach this:

  • What is the minimum & maximum supported version of Wine (or preferably Proton in this case) that this project will support?
  • Is the desire to install this on the system level or on a per-user level? As there's specific approaches to both
  • Is the intent to do it the best/correct way or to imagine Nix as like any other package manager?
  • Is the intent to also support Logos 9 even though supporting Nix(OS) would be introducing new functionality and there's (from my understanding) active interest in removing Logos 9 support from this project?

Not trying to be difficult but it feels as though there's this misconception of how Nix (the package manager) and NixOS (the distro) function. To if this is going to be introduced into this project then it's best to understand the nuances that these platforms bring into the mix, especially when it goes against the status quo of the typical and other-supported package managers and distros.

@thw26
Copy link
Collaborator Author

thw26 commented Mar 12, 2024

I want to do this proper.

We plan to utilize whatever version of Wine is newest. I got word that 9.2 works but 9.0–9.1 and 9.3 don't.

At present, the script is supposed to support L9 but doesn't. It wouldn't be hard to wire this up.

The PR I made was just to assign variables the script checks for later, so as not to throw errors. It is not intended to communicate how I think the OS installs software. I got a good feel for that after installing the VM.

At this point, I need either the nix.shell files or otherwise to move forward.

I don't have time to figure that portion out myself, but if you can provide the proper things needed, I can create routines within the code to support these things. There will be a flag within code to initiate a different install routine for NixOS based on distro, basically, which this PR was meant to indicate we can acquire.

Because of the extent of difference, this will be a larger than expected PR. We do want to support ChromeOS, for instance, which may require additional steps due to its containerization. I bring this up to say we want to support things like this since we are an installer for Linux distros. I even want to add the BSDs and ARM support.

So please, let me know what you think is needed. I'm resting on your judgment for things like whether it is system wide or not. If you think we should break it up in two parts, i.e., one for Wine and one for Logos, I support that as that's how I view our current routines. If the install architecture can specify dependencies, for instance, then let's do it proper.

Again, I'm relying on your judgment. Give me what I need to copy/pasta an install at CLI and I can extrapolate that into the script.

@eric-hansen
Copy link

Understand. I don't know if I'll have time during the week to provide what is needed, but I should be able to help towards this once the weekend hits.

Given the differences of things between other distros and Nix, I'd recommend not worrying about the GUI as that will pollute a user's system/setup. So I'll move forward with this under the assumption the Nix file will handle the various aspects on it's own w/o a UI.

For context, the reason for this assumption and path is that Nix is meant to be declarative. So you tell it what you want the final result to be and it does it. Versus things like apt that needs you to tell it what the starting path is and it finishes it for you.

@eric-hansen
Copy link

I wasn't able to spend any time on this over the weekend, but I was thinking about what you said earlier about how in the 9.x series only 9.2 works. It'll be easier to break this approach down a little bit more:

  1. Set up a flake file (Nix config file)
  2. Find the appropriate hash/history of the packages repo that has Wine 9.2 (did some digging into this last week)
  3. Configure the Flake to use the repo from above to install Wine, winetricks and needed other files

This will also be a little easier once Logos v30 works on Wine, but is not a showstopper on getting this going.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request help wanted Extra attention is needed installer This issue is related to the install routine
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants