diff --git a/development.md b/development.md
index 0ca9511f..469f3389 100644
--- a/development.md
+++ b/development.md
@@ -1,4 +1,4 @@
-# Development Setup and Tools
+# Development environment setup
The following packages are required in order to do development for `system-installer`. Most of these packages are available in most distributions of Linux and many come pre-installed in some distros.
@@ -19,8 +19,67 @@ The following are not required but may help out
sudo apt install -y python3 gir1.2-gtk-3.0 p7zip-full python3-parted python3-gnupg arch-install-scripts coreutils squashfs-tools pylint
```
-You can find a number of tests to make sure your programs work correctly in the `tests` directory. These are currently tailored to how these programs are written, i.e. they work Python scripts. None of the UI is tested yet. Feel free to write more tests and request their addition in a pull request.
+# How to get started
+
+Do you know some programming and want to help out, but haven't worked on someone else's codebase before? This section is here for you. These are some recommended steps on how to contribute without diving into specifics.
+
+1. Create a Github account (if necessary)
+ * It is strongly recommended to protect your github account with 2FA to protect us from your account being compromized.
+ * SMS 2FA is not recommended
+2. Download and install a git management tool.
+ * [Gitkraken](https://www.gitkraken.com) is recommended
+ * The rest of the guide assumes you are using Gitkraken.
+3. Go to preferences==>SSH. Generate a new private/public key.
+4. Go to Github SSH settings and add your public key.
+5. Open the Drauger OS Development repository you want to work on.
+6. Click the fork button
+7. Go to your repositories, you should now see your forked repository of the Drauger OS repository
+8. Click on code ==> SSH and copy the URL provided
+9. Go into Gitkraken and clone your forked repository using the SSH URL
+10. When viewing your repository in Gitkraken, find the dev branch (remote) and have Gitkraken checkout. You should be checked out to the current dev branch of your repository
+ * While you are working, changes might be made to the Drauger OS repository you forked.
+ * If changes affect the files you are working on, you may need to rebase from your repository's page. Be aware that your work may be lost, so backup your working files.
+ * If you rebase, don't forget to pull from gitkraken.
+11. Make the desired changes and commits.
+12. Push your commits to your repository as necessary
+13. When you are finished making your changes, run through the pre-pull checklist
+14. Go to the pull requests section of the Drauger OS repository and generate a pull request
+ * Even if you have permissions to do so, don't approve pull requests you yourself generate
+
+# Pre-pull checklist
+- [ ] Run pylint
+- [ ] Run unit tests
+- [ ] Check type hinting
+- [ ] Test the code (run the application)
+
+# Pylint instructions
+_more coming soon_
All Python code should conform to `PEP8` as closely as possible. If you open a pull request `pep8speaks` will provide feedback on your `PEP8` conformance.
A good rule of thumb is if your Python code gets a score of `7.5` or higher from `pylint`, and works correctly, the chances of having your pull request accepted is fairly high, but no pull request is guaranteed to be accepted.
+
+# Unit test instructions
+_more coming soon_
+
+You can find a number of tests to make sure your programs work correctly in the `tests` directory. These are currently tailored to how these programs are written, i.e. they work Python scripts. None of the UI is tested yet. Feel free to write more tests and request their addition in a pull request.
+
+If, after modifying a function, the unit test fails, fix the issue in the function. Don't modify the unit test unless the functions interface has changed
+
+# Type hinting instructions
+_coming soon_
+
+# Recommended coding practices
+This section provides standards agreed upon by senior developers. By following these guidelines, you reduce the likelihood the pull request will be returned.
+
+* Your code should be self documenting (i.e. variable names, function names, etc.)
+* Comment where necessary. Assume someone else is going to read your code
+* Docstrings will describe how others will interface with your function
+ * other users should not need to read the code in your function to know how to use it
+* Harden your function inputs
+* Functions should have high functional cohesion -- doing one thing, and doing it well
+* Functions should have loose coupling -- limited connections to other functions and high portability
+* Minimize the scopes of variables
+* Avoid duplicate code
+* When able, prioritize code readability
+
diff --git a/usr/share/system-installer/UI/main.py b/usr/share/system-installer/UI/main.py
index dc4cb721..20be4af6 100755
--- a/usr/share/system-installer/UI/main.py
+++ b/usr/share/system-installer/UI/main.py
@@ -28,11 +28,17 @@
import json
import os
import common
-from subprocess import Popen, check_output, DEVNULL
+import subprocess
import gi
+import auto_partitioner
+
gi.require_version('Gtk', '3.0')
+
from gi.repository import Gtk
-import auto_partitioner
+
+
+
+
def has_special_character(input_string):
"""Check for special characters"""
@@ -545,7 +551,7 @@ def auto_partition(self, button):
self.data["AUTO_PART"] = True
# Get a list of disks and their capacity
- self.devices = json.loads(check_output(["lsblk", "-n", "-i", "--json",
+ self.devices = json.loads(subprocess.check_output(["lsblk", "-n", "-i", "--json",
"-o", "NAME,SIZE,TYPE"]).decode())
self.devices = self.devices["blockdevices"]
dev = []
@@ -901,8 +907,8 @@ def make_space_parts(self, widget):
for each1 in each["children"]:
self.parts.append(each1["name"],
"%s, filesystem: %s, size: %sGB" % (each1["name"],
- each1["fstype"],
- int(auto_partitioner.bytes_to_gb(each1["size"]))))
+ each1["fstype"],
+ int(auto_partitioner.bytes_to_gb(each1["size"]))))
self.show_all()
def confirm_remove_part(self, widget):
@@ -1051,18 +1057,39 @@ def confirm_auto_part(self, button):
self.main_menu("clicked")
- def input_part(self, button):
- """Manual Partitioning Input Window"""
- self.clear_window()
+ def set_up_partitioner_label(self, additional_message = ""):
+ """prepare top label for display on manual partitioner
+ Keyword arguments:
+ additonal message -- any errors that need to be displayed below original message
+
+ Return value:
+ label -- the top label ready for additional formatting and display
+ """
label = Gtk.Label()
- label.set_markup("""
+
+ input_string = """
What are the mount points for the partitions you wish to be used?
Leave empty the partitions you don't want.
/ MUST BE USED
- """)
+ """
+
+ if additional_message != "":
+ input_string = input_string + "\n " + additional_message
+
+ label.set_markup(input_string)
+
label.set_justify(Gtk.Justification.LEFT)
label = self._set_default_margins(label)
+
+ return label
+
+
+ def input_part(self, button):
+ """Manual Partitioning Input Window"""
+ self.clear_window()
+
+ label = self.set_up_partitioner_label()
self.grid.attach(label, 1, 1, 3, 1)
label2 = Gtk.Label()
@@ -1142,16 +1169,7 @@ def onnext4clicked(self, button):
"""Check device paths provided for manual partitioner"""
if ((self.root.get_text() == "") or (
self.root.get_text()[0:5] != "/dev/")):
- label = Gtk.Label()
- label.set_markup("""
- What are the mount points for the partitions you wish to be used?
- Leave empty the partitions you don't want.
- / MUST BE USED
-
- / NOT SET
- """)
- label.set_justify(Gtk.Justification.LEFT)
- label = self._set_default_margins(label)
+ label = self.set_up_partitioner_label("ERROR: / NOT SET")
try:
self.grid.remove(self.grid.get_child_at(1, 1))
except TypeError:
@@ -1161,16 +1179,7 @@ def onnext4clicked(self, button):
self.show_all()
return
elif not os.path.exists(self.root.get_text()):
- label = Gtk.Label()
- label.set_markup("""
- What are the mount points for the partitions you wish to be used?
- Leave empty the partitions you don't want.
- / MUST BE USED
-
- Not a Valid Device on /
- """)
- label.set_justify(Gtk.Justification.LEFT)
- label = self._set_default_margins(label)
+ label = self.set_up_partitioner_label("ERROR: Not a Valid Device on /")
try:
self.grid.remove(self.grid.get_child_at(1, 1))
except TypeError:
@@ -1181,17 +1190,8 @@ def onnext4clicked(self, button):
return
elif (((self.efi.get_text() == "") or (
self.efi.get_text()[0:5] != "/dev/")) and os.path.isdir("/sys/firmware/efi")):
- label = Gtk.Label()
- label.set_markup("""
- What are the mount points for the partitions you wish to be used?
- Leave empty the partitions you don't want.
- / MUST BE USED
-
- You are using EFI, therefore an EFI partition
- must be set.
- """)
- label.set_justify(Gtk.Justification.LEFT)
- label = self._set_default_margins(label)
+ label = self.set_up_partitioner_label(
+ "ERROR: System is running EFI. An EFI partition must be set.")
try:
self.grid.remove(self.grid.get_child_at(1, 1))
except TypeError:
@@ -1200,17 +1200,10 @@ def onnext4clicked(self, button):
self.show_all()
return
- elif (not os.path.exists(self.efi.get_text()) or (self.efi.get_text() == "")) and os.path.isdir("/sys/firmware/efi"):
+ elif (not os.path.exists(self.efi.get_text()) or (
+ self.efi.get_text() == "")) and os.path.isdir("/sys/firmware/efi"):
label = Gtk.Label()
- label.set_markup("""
- What are the mount points for the partitions you wish to be used?
- Leave empty the partitions you don't want.
- / MUST BE USED
-
- Not a Valid Device on /boot/efi
- """)
- label.set_justify(Gtk.Justification.LEFT)
- label = self._set_default_margins(label)
+ label = self.set_up_partitioner_label("ERROR: Not a Valid Device on /boot/efi")
try:
self.grid.remove(self.grid.get_child_at(1, 1))
except TypeError:
@@ -1223,15 +1216,7 @@ def onnext4clicked(self, button):
self.home.get_text()[0:5] != "/dev/") and (
self.home.get_text() != "NULL")):
label = Gtk.Label()
- label.set_markup("""
- What are the mount points for the partitions you wish to be used?
- Leave empty the partitions you don't want.
- / MUST BE USED
-
- Please input a valid device path for HOME partition.
- """)
- label.set_justify(Gtk.Justification.LEFT)
- label = self._set_default_margins(label)
+ label = self.set_up_partitioner_label("ERROR: invalid HOME partition device path")
try:
self.grid.remove(self.grid.get_child_at(1, 1))
except TypeError:
@@ -1243,16 +1228,7 @@ def onnext4clicked(self, button):
elif (not os.path.exists(self.home.get_text()) and (
self.home.get_text() != "") and (
self.home.get_text() != "NULL")):
- label = Gtk.Label()
- label.set_markup("""
- What are the mount points for the partitions you wish to be used?
- Leave empty the partitions you don't want.
- / MUST BE USED
-
- Not a Valid Device on /home
- """)
- label.set_justify(Gtk.Justification.LEFT)
- label = self._set_default_margins(label)
+ label = self.set_up_partitioner_label("ERROR: Not a Valid Device on /home")
try:
self.grid.remove(self.grid.get_child_at(1, 1))
except TypeError:
@@ -1264,17 +1240,8 @@ def onnext4clicked(self, button):
elif ((self.swap.get_text() != "") and (
self.swap.get_text()[0:5] != "/dev/") and (
self.swap.get_text().upper() != "FILE")):
- label = Gtk.Label()
- label.set_markup("""
- What are the mount points for the partitions you wish to be used?
- Leave empty the partitions you don't want.
- / MUST BE USED
-
- SWAP must be set to a valid partition path, "FILE", or
- left empty.
- """)
- label.set_justify(Gtk.Justification.LEFT)
- label = self._set_default_margins(label)
+ label = self.set_up_partitioner_label(
+ "ERROR: SWAP must be set to a valid path, 'FILE', or empty.")
try:
self.grid.remove(self.grid.get_child_at(1, 1))
except TypeError:
@@ -1285,16 +1252,7 @@ def onnext4clicked(self, button):
elif (not os.path.exists(self.swap.get_text()) and (
self.swap.get_text().upper() != "FILE") and (
self.swap.get_text() != "")):
- label = Gtk.Label()
- label.set_markup("""
- What are the mount points for the partitions you wish to be used?
- Leave empty the partitions you don't want.
- / MUST BE USED
-
- Not a Valid Device on SWAP
- """)
- label.set_justify(Gtk.Justification.LEFT)
- label = self._set_default_margins(label)
+ label = self.set_up_partitioner_label("ERROR: Not a Valid Device on SWAP")
try:
self.grid.remove(self.grid.get_child_at(1, 1))
except TypeError:
@@ -1304,18 +1262,13 @@ def onnext4clicked(self, button):
self.show_all()
return
if ((self.swap.get_text().upper() == "FILE") or (self.swap.get_text() == "")):
- if auto_partitioner.size_of_part(self.root.get_text()) < auto_partitioner.get_min_root_size(bytes=False):
- label = Gtk.Label()
- label.set_markup(f"""
- What are the mount points for the partitions you wish to be used?
- Leave empty the partitions you don't want.
- / MUST BE USED
-
- / is too small. Minimum Root Partition size is { round(auto_partitioner.get_min_root_size(bytes=False)) } GB
+ if auto_partitioner.size_of_part(self.root.get_text()) < \
+ auto_partitioner.get_min_root_size(bytes=False):
+ label_string = \
+ f""" / is too small. Minimum Root Partition size is { round(auto_partitioner.get_min_root_size(bytes=False)) } GB
Make a swap partition to reduce this minimum to { round(auto_partitioner.get_min_root_size(swap=False, bytes=False)) } GB
- """)
- label.set_justify(Gtk.Justification.LEFT)
- label = self._set_default_margins(label)
+ """
+ label = self.set_up_partitioner_label(label_string)
try:
self.grid.remove(self.grid.get_child_at(1, 1))
except TypeError:
@@ -1325,17 +1278,10 @@ def onnext4clicked(self, button):
self.show_all()
return
else:
- if auto_partitioner.size_of_part(self.root.get_text()) < auto_partitioner.get_min_root_size(swap=False, bytes=False):
- label = Gtk.Label()
- label.set_markup(f"""
- What are the mount points for the partitions you wish to be used?
- Leave empty the partitions you don't want.
- / MUST BE USED
-
- / is too small. Minimum Root Partition size is { round(auto_partitioner.get_min_root_size(swap=False, bytes=False)) } GB
- """)
- label.set_justify(Gtk.Justification.LEFT)
- label = self._set_default_margins(label)
+ if auto_partitioner.size_of_part(self.root.get_text()) < \
+ auto_partitioner.get_min_root_size(swap=False, bytes=False):
+ label_string = f"/ is too small. Minimum Root Partition size is { round(auto_partitioner.get_min_root_size(swap=False, bytes=False)) } GB"
+ label = self.set_up_partitioner_label(label_string)
try:
self.grid.remove(self.grid.get_child_at(1, 1))
except TypeError:
@@ -1344,14 +1290,7 @@ def onnext4clicked(self, button):
self.show_all()
return
- label = Gtk.Label()
- label.set_markup("""
- What are the mount points for the partitions you wish to be used?
- Leave empty the partitions you don't want.
- / MUST BE USED
- """)
- label.set_justify(Gtk.Justification.LEFT)
- label = self._set_default_margins(label)
+ label = self.set_up_partitioner_label()
try:
self.grid.remove(self.grid.get_child_at(1, 1))
except TypeError:
@@ -1379,7 +1318,7 @@ def onnext4clicked(self, button):
def opengparted(self, button):
"""Open GParted"""
- Popen("gparted", stdout=DEVNULL, stderr=DEVNULL)
+ subprocess.Popen("gparted", stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
self.data["AUTO_PART"] = False
self.input_part("clicked")