Skip to content

Commit

Permalink
[DISTRO] [NEW] Adds (basic) support for Alpine Linux
Browse files Browse the repository at this point in the history
> Alpine Linux ASCII logo has been borrowed from Neofetch (see <dylanaraps/neofetch@404c955>).

- Known bug :
-   The `Disk` entry is not compatible against the BusyBox `df` implementation.
-   Alpine users are advised to **disable** it from configuration.
  • Loading branch information
Samuel FORESTIER committed Apr 24, 2020
1 parent 3ff70fa commit d676b0b
Show file tree
Hide file tree
Showing 10 changed files with 128 additions and 19 deletions.
2 changes: 2 additions & 0 deletions archey/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
# The first element (`[0]`) of each list will be used to display text entries.
COLOR_DICT = {
Distributions.ARCH_LINUX: [Colors.CYAN_BRIGHT, Colors.CYAN_NORMAL],
Distributions.ALPINE_LINUX: [Colors.BLUE_BRIGHT],
Distributions.BUNSENLABS: [Colors.WHITE_BRIGHT, Colors.YELLOW_BRIGHT, Colors.YELLOW_NORMAL],
Distributions.CRUNCHBANG: [Colors.WHITE_BRIGHT],
Distributions.DEBIAN: [Colors.RED_BRIGHT, Colors.RED_NORMAL],
Expand All @@ -32,6 +33,7 @@

# This dictionary contains which logo should be used for each supported distribution.
LOGOS_DICT = {
Distributions.ALPINE_LINUX: logos.ALPINE_LINUX,
Distributions.ARCH_LINUX: logos.ARCH_LINUX,
Distributions.BUNSENLABS: logos.BUNSENLABS,
Distributions.CRUNCHBANG: logos.CRUNCHBANG,
Expand Down
1 change: 1 addition & 0 deletions archey/distributions.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ class Distributions(Enum):
Values contain their respective `distro` identifier.
See <https://distro.readthedocs.io/en/latest/#distro.id>.
"""
ALPINE_LINUX = 'alpine'
ARCH_LINUX = 'arch'
BUNSENLABS = 'bunsenlabs'
CRUNCHBANG = 'crunchbang'
Expand Down
15 changes: 12 additions & 3 deletions archey/entries/disk.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,18 +11,26 @@
class Disk:
"""Uses `df` and `btrfs` commands to compute the total disk usage across devices"""
def __init__(self):
# The configuration object is needed to retrieve some settings below.
configuration = Configuration()

# This dictionary will store values obtained from sub-processes calls.
self._usage = {
'used': 0.0,
'total': 0.0
}

# Fetch the user-defined disk limits from configuration.
disk_limits = Configuration().get('limits')['disk']

self._run_df_usage()
self._run_btrfs_usage()

# Check whether at least one media could be found.
if not self._usage['total']:
self.value = configuration.get('default_strings')['not_detected']
return

# Fetch the user-defined disk limits from configuration.
disk_limits = configuration.get('limits')['disk']

# Based on the disk percentage usage, select the corresponding level color.
level_color = Colors.get_level_color(
(self._usage['used'] / (self._usage['total'] or 1)) * 100,
Expand Down Expand Up @@ -56,6 +64,7 @@ def _run_df_usage(self):
).splitlines()[-1].split()
except CalledProcessError:
# It looks like there is not any file system matching our types.
# Known bug : `df` available in BusyBox does not support our flags.
return

self._usage['used'] += float(df_output[2].rstrip('MB')) / 1024
Expand Down
12 changes: 10 additions & 2 deletions archey/entries/packages.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
"""Number of installed packages detection class"""

import os

from subprocess import check_output, DEVNULL, CalledProcessError

from archey.configuration import Configuration


PACKAGES_TOOLS = (
{'cmd': ['apk', 'list', '--installed']},
{'cmd': ['apt', 'list', '-qq', '--installed']},
{'cmd': ['dnf', 'list', 'installed'], 'skew': 1},
{'cmd': ['dpkg', '--get-selections']},
Expand All @@ -25,15 +28,20 @@ def __init__(self):
results = check_output(
packages_tool['cmd'],
stderr=DEVNULL,
env={'LANG': 'C'},
env={
'LANG': 'C',
# Alpine Linux: We have to manually propagate `PATH`.
# `apk` wouldn't be found otherwise.
'PATH': os.getenv('PATH')
},
universal_newlines=True
)
except (FileNotFoundError, CalledProcessError):
continue

packages = results.count('\n')

# If any, deduct any skew present due to the packages tool output.
# If any, deduct output skew present due to the packages tool.
if 'skew' in packages_tool:
packages -= packages_tool['skew']

Expand Down
1 change: 1 addition & 0 deletions archey/logos/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
"""Simple `__init__` file for the `logos` module, to load each distribution logo"""

from archey.logos.alpine_linux import ALPINE_LINUX
from archey.logos.arch_linux import ARCH_LINUX
from archey.logos.bunsenlabs import BUNSENLABS
from archey.logos.crunchbang import CRUNCHBANG
Expand Down
24 changes: 24 additions & 0 deletions archey/logos/alpine_linux.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
"""Alpine Linux logo"""

ALPINE_LINUX = """\
{c[0]} .hddddddddddddddddddddddh. \n\
{c[0]} :dddddddddddddddddddddddddd: {r[0]}
{c[0]} /dddddddddddddddddddddddddddd/ {r[1]}
{c[0]} +dddddddddddddddddddddddddddddd+ {r[2]}
{c[0]} `sdddddddddddddddddddddddddddddddds` {r[3]}
{c[0]} `ydddddddddddd++hdddddddddddddddddddy` {r[4]}
{c[0]} .hddddddddddd+` `+ddddh:-sdddddddddddh. {r[5]}
{c[0]} hdddddddddd+` `+y: .sddddddddddh {r[6]}
{c[0]} ddddddddh+` `//` `.` -sddddddddd {r[7]}
{c[0]} ddddddh+` `/hddh/` `:s- -sddddddd {r[8]}
{c[0]} ddddh+` `/+/dddddh/` `+s- -sddddd {r[9]}
{c[0]} ddd+` `/o` :dddddddh/` `oy- .yddd {r[10]}
{c[0]} hdddyo+ohddyosdddddddddho+oydddy++ohdddh {r[11]}
{c[0]} .hddddddddddddddddddddddddddddddddddddh. {r[12]}
{c[0]} `yddddddddddddddddddddddddddddddddddy` {r[13]}
{c[0]} `sdddddddddddddddddddddddddddddddds` {r[14]}
{c[0]} +dddddddddddddddddddddddddddddd+ {r[15]}
{c[0]} /dddddddddddddddddddddddddddd/ {r[16]}
{c[0]} :dddddddddddddddddddddddddd: {r[17]}
{c[0]} .hddddddddddddddddddddddh. \
"""
12 changes: 9 additions & 3 deletions archey/processes.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import os
import sys

from subprocess import check_output
from subprocess import CalledProcessError, DEVNULL, check_output

from archey.singleton import Singleton

Expand All @@ -19,9 +19,15 @@ def __init__(self):
'-o', 'comm',
'--no-headers'
],
universal_newlines=True
universal_newlines=True, stderr=DEVNULL
).splitlines()

except CalledProcessError:
# The available `ps` implementation may not support passed parameters (hello BusyBox).
# Let's fall-back on a much simpler approach.
self._processes = check_output(
['ps', '-o', 'comm'],
universal_newlines=True
).splitlines()[1:]
except FileNotFoundError:
print(
"Please, install first `procps` on your distribution.",
Expand Down
27 changes: 18 additions & 9 deletions archey/test/test_archey_disk.py
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,21 @@ def test_btrfs_only_with_raid_configuration(self, _, __):
disk = Disk().value
self.assertTrue(all(i in disk for i in [str(Colors.GREEN_NORMAL), '943.4', '4202.5']))

@patch(
'archey.entries.disk.check_output',
side_effect=[
CalledProcessError(1, 'df', "df: unrecognized option: l\n"),
CalledProcessError(1, 'df', "df: unrecognized option: l\n")
]
)
@patch(
'archey.entries.disk.Configuration.get',
return_value={'not_detected': 'Not detected'}
)
def test_df_failing(self, _, __):
"""Test df call failing against the BusyBox implementation"""
self.assertEqual(Disk().value, 'Not detected')

@patch(
'archey.entries.disk.check_output',
side_effect=[
Expand All @@ -241,17 +256,11 @@ def test_btrfs_only_with_raid_configuration(self, _, __):
)
@patch(
'archey.entries.disk.Configuration.get',
return_value={
'disk': {
'warning': 50,
'danger': 75
}
}
return_value={'not_detected': 'Not detected'}
)
def test_no_recognised_disks(self, _, __):
"""Test df failing to detect any valid filesystems"""
disk = Disk().value
self.assertTrue(all(i in disk for i in [str(Colors.GREEN_NORMAL), '0.0']))
"""Test df failing to detect any valid file-systems"""
self.assertEqual(Disk().value, 'Not detected')


if __name__ == '__main__':
Expand Down
28 changes: 27 additions & 1 deletion archey/test/test_archey_packages.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,21 +15,40 @@ class TestPackagesEntry(unittest.TestCase):
@patch(
'archey.entries.packages.check_output',
return_value="""\
sqlite-libs-3.30.1-r1 x86_64 {{sqlite}} (Public-Domain) [installed]
musl-1.1.24-r2 x86_64 {{musl}} (MIT) [installed]
libbz2-1.0.8-r1 x86_64 {{bzip2}} (bzip2-1.0.6) [installed]
gdbm-1.13-r1 x86_64 {{gdbm}} (GPL) [installed]
ncurses-libs-6.1_p20200118-r3 x86_64 {{ncurses}} (MIT) [installed]
zlib-1.2.11-r3 x86_64 {{zlib}} (Zlib) [installed]
apk-tools-2.10.4-r3 x86_64 {{apk}-tools} (GPL2) [installed]
readline-8.0.1-r0 x86_64 {{readline}} (GPL-2.0-or-later) [installed]
""")
def test_match_with_apk(self, _):
"""Simple test for the APK packages manager"""
self.assertEqual(Packages().value, 8)

@patch(
'archey.entries.packages.check_output',
side_effect=[
FileNotFoundError(),
"""\
accountsservice/stable,now 0.6.45-2 amd64 [installed,automatic]
acl/stable,now 2.2.53-4 amd64 [installed,automatic]
adb/stable,now 1:8.1.0+r23-5 amd64 [installed]
adduser/stable,now 3.118 all [installed]
adwaita-icon-theme/stable,now 3.30.1-1 all [installed,automatic]
albatross-gtk-theme/stable,now 1.7.4-1 all [installed,automatic]
alsa-utils/stable,now 1.1.8-2 amd64 [installed,automatic]
""")
"""])
def test_match_with_apt(self, _):
"""Simple test for the APT packages manager"""
self.assertEqual(Packages().value, 7)

@patch(
'archey.entries.packages.check_output',
side_effect=[
FileNotFoundError(),
FileNotFoundError(),
"""\
Installed Packages
Expand All @@ -45,6 +64,7 @@ def test_match_with_dnf(self, _):
@patch(
'archey.entries.packages.check_output',
side_effect=[
FileNotFoundError(),
FileNotFoundError(),
FileNotFoundError(),
"""\
Expand All @@ -66,6 +86,7 @@ def test_match_with_dpkg(self, _):
FileNotFoundError(),
FileNotFoundError(),
FileNotFoundError(),
FileNotFoundError(),
"""\
These are the packages that would be merged, in order:
Expand All @@ -90,6 +111,7 @@ def test_match_with_emerge(self, _):
FileNotFoundError(),
FileNotFoundError(),
FileNotFoundError(),
FileNotFoundError(),
"""\
acl 2.2.52-4
archey4 v4.3.3-1
Expand All @@ -108,6 +130,7 @@ def test_match_with_pacman(self, _):
FileNotFoundError(),
FileNotFoundError(),
FileNotFoundError(),
FileNotFoundError(),
"""\
cdrecord-2.01-10.7.el5
bluez-libs-3.7-1.1
Expand All @@ -127,6 +150,7 @@ def test_match_with_rpm(self, _):
FileNotFoundError(),
FileNotFoundError(),
FileNotFoundError(),
FileNotFoundError(),
"""\
Loaded plugins: fastestmirror, langpacks
Installed Packages
Expand All @@ -149,6 +173,7 @@ def test_match_with_yum(self, _):
FileNotFoundError(),
FileNotFoundError(),
FileNotFoundError(),
FileNotFoundError(),
"""\
Loading repository data...
Reading installed packages...
Expand All @@ -175,6 +200,7 @@ def test_match_with_zypper(self, _):
FileNotFoundError(),
FileNotFoundError(),
FileNotFoundError(),
FileNotFoundError(),
FileNotFoundError()
]
)
Expand Down
25 changes: 24 additions & 1 deletion archey/test/test_archey_processes.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
"""Test module for `archey.processes`"""

from subprocess import CalledProcessError

import unittest
from unittest.mock import patch

Expand Down Expand Up @@ -34,7 +36,7 @@ def test_ps_ok(self, check_output_mock):
processes_1 = Processes()
_ = Processes()

self.assertEqual(
self.assertListEqual(
processes_1.get(),
['what', 'an', 'awesome', 'processes', 'list', 'you', 'got', 'there']
)
Expand All @@ -43,6 +45,27 @@ def test_ps_ok(self, check_output_mock):
# `unittest.mock.Mock.assert_called_once` is not available against Python < 3.6.
self.assertEqual(check_output_mock.call_count, 1)

@patch.dict(
'archey.singleton.Singleton._instances',
clear=True
)
@patch(
'archey.processes.check_output',
side_effect=[
CalledProcessError(1, 'ps', "ps: unrecognized option: u\n"),
"""\
COMMAND
sh
top
ps
"""])
def test_ps_failed(self, _):
"""Verifies that the program correctly handles first crashing `ps` call"""
self.assertListEqual(
Processes().get(),
['sh', 'top', 'ps']
)

@patch.dict(
'archey.singleton.Singleton._instances',
clear=True
Expand Down

0 comments on commit d676b0b

Please sign in to comment.