From 7ce285cb4decab9f2b52763a5510c2ac11189b55 Mon Sep 17 00:00:00 2001 From: Pedro Lamas Date: Wed, 17 Apr 2024 20:15:16 +0100 Subject: [PATCH 1/2] Fix Armbian release info (#366) Co-authored-by: Samuel FORESTIER --- src/distro/distro.py | 22 ++++++++++++++ .../distros/armbian/etc/armbian-release | 14 +++++++++ .../resources/distros/armbian/etc/os-release | 1 + .../distros/armbian/usr/lib/os-release | 9 ++++++ tests/test_distro.py | 30 ++++++++++++++++++- 5 files changed, 75 insertions(+), 1 deletion(-) create mode 100644 tests/resources/distros/armbian/etc/armbian-release create mode 120000 tests/resources/distros/armbian/etc/os-release create mode 100644 tests/resources/distros/armbian/usr/lib/os-release diff --git a/src/distro/distro.py b/src/distro/distro.py index 78ccdfa4..f83d7878 100755 --- a/src/distro/distro.py +++ b/src/distro/distro.py @@ -127,6 +127,7 @@ class InfoDict(TypedDict): "SuSE-release", "altlinux-release", "arch-release", + "armbian-release", "base-release", "centos-release", "fedora-release", @@ -905,6 +906,9 @@ def version(self, pretty: bool = False, best: bool = False) -> str: elif self.id() == "debian" or "debian" in self.like().split(): # On Debian-like, add debian_version file content to candidates list. versions.append(self._debian_version) + if self._distro_release_info.get("id") == "armbian": + # On Armbian, add version from armbian-release file to candidates list. + versions.append(self._armbian_version) version = "" if best: # This algorithm uses the last version in priority order that has @@ -1225,6 +1229,16 @@ def _debian_version(self) -> str: except FileNotFoundError: return "" + @cached_property + def _armbian_version(self) -> str: + try: + with open( + os.path.join(self.etc_dir, "armbian-release"), encoding="ascii" + ) as fp: + return self._parse_os_release_content(fp).get("version", "") + except FileNotFoundError: + return "" + @staticmethod def _parse_uname_content(lines: Sequence[str]) -> Dict[str, str]: if not lines: @@ -1302,6 +1316,14 @@ def _distro_release_info(self) -> Dict[str, str]: if match is not None: distro_info["id"] = match.group(1) + # Armbian release files are not standard as they start with a + # comment. Manually set name if it has not been inferred. + if distro_info["id"] == "armbian" and distro_info.get( + "name", "" + ).startswith("#"): + distro_info["name"] = "Armbian" + distro_info["version_id"] = self._armbian_version + # CloudLinux < 7: manually enrich info with proper id. if "cloudlinux" in distro_info.get("name", "").lower(): distro_info["id"] = "cloudlinux" diff --git a/tests/resources/distros/armbian/etc/armbian-release b/tests/resources/distros/armbian/etc/armbian-release new file mode 100644 index 00000000..342a882d --- /dev/null +++ b/tests/resources/distros/armbian/etc/armbian-release @@ -0,0 +1,14 @@ +# PLEASE DO NOT EDIT THIS FILE +BOARD=nanopim4v2 +BOARD_NAME="NanoPi M4V2" +BOARDFAMILY=rk3399 +BUILD_REPOSITORY_URL=https://github.com/armbian/build +BUILD_REPOSITORY_COMMIT=1a8daf0 +VERSION=23.02.2 +LINUXFAMILY=rockchip64 +ARCH=arm64 +IMAGE_TYPE=nightly +BOARD_TYPE=conf +INITRD_ARCH=arm64 +KERNEL_IMAGE_TYPE=Image +BRANCH=current \ No newline at end of file diff --git a/tests/resources/distros/armbian/etc/os-release b/tests/resources/distros/armbian/etc/os-release new file mode 120000 index 00000000..c4c75b41 --- /dev/null +++ b/tests/resources/distros/armbian/etc/os-release @@ -0,0 +1 @@ +../usr/lib/os-release \ No newline at end of file diff --git a/tests/resources/distros/armbian/usr/lib/os-release b/tests/resources/distros/armbian/usr/lib/os-release new file mode 100644 index 00000000..9b5419df --- /dev/null +++ b/tests/resources/distros/armbian/usr/lib/os-release @@ -0,0 +1,9 @@ +PRETTY_NAME="Debian GNU/Linux 10 (buster)" +NAME="Debian GNU/Linux" +VERSION_ID="10" +VERSION="10 (buster)" +VERSION_CODENAME=buster +ID=debian +HOME_URL="https://www.debian.org/" +SUPPORT_URL="https://www.debian.org/support" +BUG_REPORT_URL="https://bugs.debian.org/" diff --git a/tests/test_distro.py b/tests/test_distro.py index 2114d904..78c173ef 100644 --- a/tests/test_distro.py +++ b/tests/test_distro.py @@ -1919,6 +1919,28 @@ def test_altlinux10_release(self) -> None: } self._test_release_file_info("altlinux-release", desired_info) + def test_armbian_release(self) -> None: + desired_outcome = { + "id": "debian", + "codename": "buster", + "name": "Debian GNU/Linux", + "pretty_name": "Debian GNU/Linux 10 (buster)", + "like": "", + "version": "10", + "pretty_version": "10 (buster)", + "best_version": "23.02.2", + "major_version": "10", + "minor_version": "", + } + self._test_outcome(desired_outcome) + + desired_info = { + "id": "armbian", + "name": "Armbian", + "version_id": "23.02.2", + } + self._test_release_file_info("armbian-release", desired_info) + def _bad_os_listdir(path: str = ".") -> NoReturn: """This function is used by TestOverallWithEtcNotReadable to simulate @@ -2362,6 +2384,12 @@ def test_repr(self) -> None: repr_str = repr(distro._distro) assert "LinuxDistribution" in repr_str for attr in MODULE_DISTRO.__dict__.keys(): - if attr in ("root_dir", "etc_dir", "usr_lib_dir", "_debian_version"): + if attr in ( + "root_dir", + "etc_dir", + "usr_lib_dir", + "_debian_version", + "_armbian_version", + ): continue assert f"{attr}=" in repr_str From 3bd19e61fcb7f8d2bf3d45d9e40d69c92e05d241 Mon Sep 17 00:00:00 2001 From: Manuel Muradas Date: Thu, 24 Oct 2024 14:41:39 +0200 Subject: [PATCH 2/2] Handle `CalledProcessError` when calling `uname` Usually, when `distro` is used in Windows, it returns nothing. Any call to `lsb_release`, `uname', etc, raises a `[[WinError 2] The system cannot find the file specified` error, which is properly handled. However, in a particular environment where I've run it, I've found that the call to `uname` raises a `CalledProcessError`, because a Policy or a Security Control is injecting a non-zero return code: ``` t=2024-10-04T15:13:39+0200 Command '('uname', '-rs')' returned non-zero exit status 3221225794. Traceback (most recent call last): File "C:\...\main.py", line 123, in __check_supported_platform distro_id = distro.id() ^^^^^^^^^^^ File "C:\...\site-packages\distro\distro.py", line 284, in id return _distro.id() ^^^^^^^^^^^^ File "C:\...\site-packages\distro\distro.py", line 855, in id distro_id = self.uname_attr("id") ^^^^^^^^^^^^^^^^^^^^^ File "C:\...\site-packages\distro\distro.py", line 1088, in uname_attr return self._uname_info.get(attribute, "") ^^^^^^^^^^^^^^^^ File "C:\...\lib\functools.py", line 995, in __get__ val = self.func(instance) ^^^^^^^^^^^^^^^^^^^ File "C:\...\site-packages\distro\distro.py", line 1202, in _uname_info stdout = subprocess.check_output(cmd, stderr=subprocess.DEVNULL) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "C:\...\lib\subprocess.py", line 466, in check_output return run(*popenargs, stdout=PIPE, timeout=timeout, check=True, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "C:\...\lib\subprocess.py", line 571, in run raise CalledProcessError(retcode, process.args, subprocess.CalledProcessError: Command '('uname', '-rs')' returned non-zero exit status 3221225794. ``` We do handle `CalledProcessError` in other calls, so I'm adding to `uname` too. --- src/distro/distro.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/distro/distro.py b/src/distro/distro.py index f83d7878..77e2c685 100755 --- a/src/distro/distro.py +++ b/src/distro/distro.py @@ -1204,7 +1204,7 @@ def _uname_info(self) -> Dict[str, str]: try: cmd = ("uname", "-rs") stdout = subprocess.check_output(cmd, stderr=subprocess.DEVNULL) - except OSError: + except (OSError, subprocess.CalledProcessError): return {} content = self._to_str(stdout).splitlines() return self._parse_uname_content(content)