1
0
mirror of https://github.com/HorlogeSkynet/archey4 synced 2025-04-10 00:00:19 +02:00

[DISTRO] [NEW] Adds (basic) support for Alpine Linux

> Alpine Linux ASCII logo has been borrowed from Neofetch (see <404c955e8f>).

- Known bug :
-   The `Disk` entry is not compatible against the BusyBox `df` implementation.
-   Alpine users are advised to **disable** it from configuration.
This commit is contained in:
Samuel FORESTIER 2020-04-24 10:20:00 +02:00
parent 3ff70faae9
commit d676b0b2d5
10 changed files with 128 additions and 19 deletions

@ -10,6 +10,7 @@ from archey.distributions import Distributions
# 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],
@ -32,6 +33,7 @@ COLOR_DICT = {
# 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,

@ -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'

@ -11,18 +11,26 @@ from archey.configuration import Configuration
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,
@ -56,6 +64,7 @@ class Disk:
).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

@ -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']},
@ -25,7 +28,12 @@ class Packages:
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):
@ -33,7 +41,7 @@ class Packages:
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']

@ -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

@ -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. \
"""

@ -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
@ -19,9 +19,15 @@ class Processes(metaclass=Singleton):
'-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.",

@ -232,6 +232,21 @@ System,RAID1: Size:0.01GiB, Used:0.00GiB
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=[
@ -241,17 +256,11 @@ System,RAID1: Size:0.01GiB, Used:0.00GiB
)
@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__':

@ -15,6 +15,24 @@ 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]
@ -22,7 +40,7 @@ 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)
@ -30,6 +48,7 @@ alsa-utils/stable,now 1.1.8-2 amd64 [installed,automatic]
@patch(
'archey.entries.packages.check_output',
side_effect=[
FileNotFoundError(),
FileNotFoundError(),
"""\
Installed Packages
@ -45,6 +64,7 @@ GraphicsMagick.x86_64 1.3.26-3.fc26 @@commandline
@patch(
'archey.entries.packages.check_output',
side_effect=[
FileNotFoundError(),
FileNotFoundError(),
FileNotFoundError(),
"""\
@ -66,6 +86,7 @@ alien install
FileNotFoundError(),
FileNotFoundError(),
FileNotFoundError(),
FileNotFoundError(),
"""\
These are the packages that would be merged, in order:
@ -90,6 +111,7 @@ USE="pam -static-libs" ABI_X86="(64) -32 (-x32)" \n\
FileNotFoundError(),
FileNotFoundError(),
FileNotFoundError(),
FileNotFoundError(),
"""\
acl 2.2.52-4
archey4 v4.3.3-1
@ -108,6 +130,7 @@ argon2 20171227-3
FileNotFoundError(),
FileNotFoundError(),
FileNotFoundError(),
FileNotFoundError(),
"""\
cdrecord-2.01-10.7.el5
bluez-libs-3.7-1.1
@ -127,6 +150,7 @@ MySQL-client-3.23.57-1
FileNotFoundError(),
FileNotFoundError(),
FileNotFoundError(),
FileNotFoundError(),
"""\
Loaded plugins: fastestmirror, langpacks
Installed Packages
@ -149,6 +173,7 @@ ModemManager-glib.x86_64 1.6.0-2.el7 @base \n\
FileNotFoundError(),
FileNotFoundError(),
FileNotFoundError(),
FileNotFoundError(),
"""\
Loading repository data...
Reading installed packages...
@ -175,6 +200,7 @@ i | at | A Job Manager | package \n\
FileNotFoundError(),
FileNotFoundError(),
FileNotFoundError(),
FileNotFoundError(),
FileNotFoundError()
]
)

@ -1,5 +1,7 @@
"""Test module for `archey.processes`"""
from subprocess import CalledProcessError
import unittest
from unittest.mock import patch
@ -34,7 +36,7 @@ there
processes_1 = Processes()
_ = Processes()
self.assertEqual(
self.assertListEqual(
processes_1.get(),
['what', 'an', 'awesome', 'processes', 'list', 'you', 'got', 'there']
)
@ -43,6 +45,27 @@ there
# `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