From 81fb25d29a76c0584944f7dc79e62bb786eca4bb Mon Sep 17 00:00:00 2001 From: Roman Prilipskii Date: Mon, 27 Apr 2026 00:59:32 -0400 Subject: [PATCH 1/5] Remove BOM file markers from files that had them introduced previously Likely caused by editor settings that were not noticed at the time. --- .../addcustomrepositories/libraries/addcustomrepositories.py | 2 +- repos/system_upgrade/cloudlinux/libraries/cl_repofileutils.py | 2 +- repos/system_upgrade/cloudlinux/libraries/detectcontrolpanel.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/repos/system_upgrade/cloudlinux/actors/addcustomrepositories/libraries/addcustomrepositories.py b/repos/system_upgrade/cloudlinux/actors/addcustomrepositories/libraries/addcustomrepositories.py index 74ba425fb1..198568eeaf 100644 --- a/repos/system_upgrade/cloudlinux/actors/addcustomrepositories/libraries/addcustomrepositories.py +++ b/repos/system_upgrade/cloudlinux/actors/addcustomrepositories/libraries/addcustomrepositories.py @@ -1,4 +1,4 @@ -import os +import os import os.path import shutil import logging diff --git a/repos/system_upgrade/cloudlinux/libraries/cl_repofileutils.py b/repos/system_upgrade/cloudlinux/libraries/cl_repofileutils.py index 3c1e68e6a2..bdc8f7b994 100644 --- a/repos/system_upgrade/cloudlinux/libraries/cl_repofileutils.py +++ b/repos/system_upgrade/cloudlinux/libraries/cl_repofileutils.py @@ -1,4 +1,4 @@ -import os +import os import os.path from leapp.libraries.stdlib import api diff --git a/repos/system_upgrade/cloudlinux/libraries/detectcontrolpanel.py b/repos/system_upgrade/cloudlinux/libraries/detectcontrolpanel.py index 7c92f10282..fe8594cf54 100644 --- a/repos/system_upgrade/cloudlinux/libraries/detectcontrolpanel.py +++ b/repos/system_upgrade/cloudlinux/libraries/detectcontrolpanel.py @@ -1,4 +1,4 @@ -import os +import os import os.path from leapp.libraries.stdlib import api From 92aee84b1153aaf1a2e92cc28670ff811a165db6 Mon Sep 17 00:00:00 2001 From: Roman Prilipskii Date: Mon, 27 Apr 2026 00:59:32 -0400 Subject: [PATCH 2/5] Add non-ASCII check to make lint command After the fix in b24a939073f1b130ed1d0785fbece476ad3dca1c, introducing additional check to ensure the same problem doesn't happen again. --- Makefile | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Makefile b/Makefile index bdb93b3648..56603398e5 100644 --- a/Makefile +++ b/Makefile @@ -333,6 +333,11 @@ lint: echo "--- Linting ... ---" && \ SEARCH_PATH="$(TEST_PATHS)" && \ echo "Using search path '$${SEARCH_PATH}'" && \ + echo "--- Checking for non-ASCII characters (Python 2.7 compat) ---" && \ + bash -c "[[ ! -z '$${SEARCH_PATH}' ]] && { HITS=\$$(grep -rPn '[^\x00-\x7F]' --include='*.py' $${SEARCH_PATH} 2>/dev/null); \ + if [[ -n \"\$$HITS\" ]]; then echo \"\$$HITS\"; \ + echo 'ERROR: Non-ASCII characters found in Python files. Python 2.7 rejects these without an encoding declaration. Replace em-dashes, smart quotes, BOMs, etc. with ASCII equivalents.'; \ + exit 1; fi; }" && \ echo "--- Running pylint ---" && \ bash -c "[[ ! -z '$${SEARCH_PATH}' ]] && find $${SEARCH_PATH} -name '*.py' | sort -u | xargs pylint -j0 $(PYLINT_ARGS)" && \ echo "--- Running flake8 ---" && \ From 9da49d072ccbe801cbb1079a0e3eb5ee68467f1e Mon Sep 17 00:00:00 2001 From: Roman Prilipskii Date: Mon, 27 Apr 2026 02:15:33 -0400 Subject: [PATCH 3/5] Remove leftover BOM from repos/system_upgrade/common/libraries/repomaputils.py The earlier BOM cleanup (commit 81fb25d2 "Remove BOM file markers from files that had them introduced previously") missed this one file. The new lint-cloudlinux GitHub Action surfaced it on a dry run. --- repos/system_upgrade/common/libraries/repomaputils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/repos/system_upgrade/common/libraries/repomaputils.py b/repos/system_upgrade/common/libraries/repomaputils.py index dc5cdce9f9..44ed2fc339 100644 --- a/repos/system_upgrade/common/libraries/repomaputils.py +++ b/repos/system_upgrade/common/libraries/repomaputils.py @@ -1,4 +1,4 @@ -import json +import json from collections import defaultdict from leapp.models import PESIDRepositoryEntry, RepoMapEntry, RepositoriesMapping From c38995fc896deec71a657a22e47286d467dfda7d Mon Sep 17 00:00:00 2001 From: Roman Prilipskii Date: Mon, 27 Apr 2026 02:27:16 -0400 Subject: [PATCH 4/5] lint: factor non-ASCII check into utils/check-non-ascii.py + sub-target The non-ASCII gate added in commit 92aee84b lived inline in the lint target and used a plain grep that did not honor PEP 263 encoding declarations. Two tradeoffs followed: - CI cannot easily reuse just that step without running the full lint pipeline (pylint + flake8), and the full pipeline is currently noisy on the cloudlinux tree. - Test fixtures that legitimately contain non-ASCII (e.g. rootscanner/tests/test_rootscanner.py with Cyrillic strings declared via '# -*- coding: utf-8 -*-') trip the grep even though Python accepts them. Move the check to utils/check-non-ascii.py, a small stdlib-only python3 script that walks the supplied paths, skips files with a PEP 263 declaration on line 1 or 2, and reports lineno + line text for every remaining offender. Wire the script into the Makefile as a new lint-non-ascii target and make it a prerequisite of lint, so the existing developer-facing behavior is unchanged: `make lint` still does the non-ASCII check plus pylint plus flake8, just via a script instead of an inline grep. A standalone target also lets CI invoke just this gate without paying for the full lint venv setup. --- Makefile | 17 +++++--- utils/check-non-ascii.py | 91 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 102 insertions(+), 6 deletions(-) create mode 100755 utils/check-non-ascii.py diff --git a/Makefile b/Makefile index 56603398e5..b8fe875429 100644 --- a/Makefile +++ b/Makefile @@ -328,16 +328,21 @@ install-deps-fedora: $(VENVNAME)/bin/pip install -I "git+https://github.com/oamg/leapp.git@refs/pull/$(REQ_LEAPP_PR)/head"; \ fi -lint: +# Reject undeclared non-ASCII bytes in Python source. Standalone target so +# CI can call it without bringing up the full lint venv (the script is +# pure stdlib python3) and so the rule is the single source of truth for +# both `make lint` and the lint-cloudlinux GitHub Action. +lint-non-ascii: + @echo "--- Checking for non-ASCII characters (Python 2.7 compat, PEP 263 aware) ---" + @SEARCH_PATH="$(TEST_PATHS)"; \ + if [ -z "$${SEARCH_PATH}" ]; then echo "TEST_PATHS is empty; nothing to scan." >&2; exit 0; fi; \ + python3 utils/check-non-ascii.py $${SEARCH_PATH} + +lint: lint-non-ascii . $(VENVNAME)/bin/activate; \ echo "--- Linting ... ---" && \ SEARCH_PATH="$(TEST_PATHS)" && \ echo "Using search path '$${SEARCH_PATH}'" && \ - echo "--- Checking for non-ASCII characters (Python 2.7 compat) ---" && \ - bash -c "[[ ! -z '$${SEARCH_PATH}' ]] && { HITS=\$$(grep -rPn '[^\x00-\x7F]' --include='*.py' $${SEARCH_PATH} 2>/dev/null); \ - if [[ -n \"\$$HITS\" ]]; then echo \"\$$HITS\"; \ - echo 'ERROR: Non-ASCII characters found in Python files. Python 2.7 rejects these without an encoding declaration. Replace em-dashes, smart quotes, BOMs, etc. with ASCII equivalents.'; \ - exit 1; fi; }" && \ echo "--- Running pylint ---" && \ bash -c "[[ ! -z '$${SEARCH_PATH}' ]] && find $${SEARCH_PATH} -name '*.py' | sort -u | xargs pylint -j0 $(PYLINT_ARGS)" && \ echo "--- Running flake8 ---" && \ diff --git a/utils/check-non-ascii.py b/utils/check-non-ascii.py new file mode 100755 index 0000000000..cbd1d216f0 --- /dev/null +++ b/utils/check-non-ascii.py @@ -0,0 +1,91 @@ +#!/usr/bin/env python3 +"""Reject undeclared non-ASCII bytes in Python source. + +Python 2.7 source is ASCII-only unless the file declares its encoding via +PEP 263 (a `# -*- coding: -*-` or `# coding=` comment on the +first or second line). This script enforces that rule on the paths given +as positional arguments: any *.py file that contains a byte > 0x7F and +does NOT carry a PEP 263 declaration is reported and the script exits 1. + +Used both by `make lint-non-ascii` (and therefore `make lint`) and by +the lint-cloudlinux GitHub Action so the two stay in sync. +""" + +from __future__ import print_function + +import os +import re +import sys + + +# PEP 263: an encoding declaration must appear on line 1 or 2 and match +# the regex below. https://peps.python.org/pep-0263/ +_CODING_RE = re.compile(rb"^[ \t\f]*#.*?coding[=:][ \t]*([-_.a-zA-Z0-9]+)") + + +def _file_has_encoding_declaration(data): + head = data.split(b"\n", 2)[:2] + return any(_CODING_RE.match(line) for line in head) + + +def _scan_file(path): + """Return a list of (lineno, decoded_line) for lines with non-ASCII + bytes. Empty list means the file is clean OR has a PEP 263 declaration. + """ + with open(path, "rb") as fp: + data = fp.read() + if not any(b > 0x7F for b in bytearray(data)): + return [] + if _file_has_encoding_declaration(data): + return [] + hits = [] + for i, line in enumerate(data.splitlines(), start=1): + if any(b > 0x7F for b in bytearray(line)): + hits.append((i, line.decode("utf-8", "replace"))) + return hits + + +def _walk_paths(roots): + for root in roots: + if os.path.isfile(root): + if root.endswith(".py"): + yield root + continue + for dirpath, _dirs, files in os.walk(root): + for name in files: + if name.endswith(".py"): + yield os.path.join(dirpath, name) + + +def main(argv): + paths = argv[1:] + if not paths: + print("usage: {} [path ...]".format(argv[0]), file=sys.stderr) + return 2 + paths = [p for p in paths if os.path.exists(p)] + if not paths: + print("warning: no provided paths exist; nothing to scan", file=sys.stderr) + return 0 + + bad = 0 + for path in _walk_paths(paths): + hits = _scan_file(path) + for lineno, text in hits: + print("{}:{}:{}".format(path, lineno, text)) + bad += 1 + + if bad: + print( + "\nERROR: Non-ASCII bytes found in Python source without a PEP 263 " + "encoding declaration. Replace em-dashes (U+2014), smart quotes, " + "ellipsis, etc. with ASCII equivalents, or add " + "'# -*- coding: utf-8 -*-' on line 1 or 2 if the non-ASCII content " + "is intentional.", + file=sys.stderr, + ) + return 1 + return 0 + + +if __name__ == "__main__": + sys.exit(main(sys.argv)) From 11d3734a4e86e156518054ffc8b945881a66f70e Mon Sep 17 00:00:00 2001 From: Roman Prilipskii Date: Mon, 27 Apr 2026 02:27:16 -0400 Subject: [PATCH 5/5] ci: add lint-cloudlinux GitHub Action The cloudlinux fork uses the cloudlinux branch as trunk; the inherited upstream workflows (unit-tests.yml, codespell.yml, etc.) only fire on master so they never run for PRs into this fork. Add a narrow workflow that runs on every push and PR targeting cloudlinux and invokes `make lint-non-ascii` to gate against stray non-ASCII bytes in Python source. Calling the make target keeps a single source of truth: local `make lint` runs the same check via the same script, so what passes locally is exactly what passes in CI. Resurrecting the full upstream test+lint matrix on this fork is a separate, larger task. --- .github/workflows/lint-cloudlinux.yml | 36 +++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 .github/workflows/lint-cloudlinux.yml diff --git a/.github/workflows/lint-cloudlinux.yml b/.github/workflows/lint-cloudlinux.yml new file mode 100644 index 0000000000..1ac0adbfa7 --- /dev/null +++ b/.github/workflows/lint-cloudlinux.yml @@ -0,0 +1,36 @@ +name: Lint (cloudlinux) + +# CloudLinux fork uses the `cloudlinux` branch as trunk; the inherited +# upstream workflows (unit-tests.yml, codespell.yml, etc.) only fire on +# `master` and so do not run on this fork. This narrow workflow guards +# the cloudlinux branch against the most common regression we have hit: +# stray non-ASCII bytes in Python source (em-dashes, smart quotes, +# ellipsis from copy/paste, leftover BOMs). +# +# The check is just `make lint-non-ascii`, which delegates to +# utils/check-non-ascii.py. That same target is also invoked as a +# prerequisite of `make lint`, so local developers get the identical +# rule when they run the full lint locally. +# +# Broader fixes (running the full upstream test+lint matrix on cloudlinux +# PRs) are tracked separately. + +on: + pull_request: + branches: [cloudlinux] + push: + branches: [cloudlinux] + +permissions: + contents: read + +jobs: + lint-non-ascii: + name: Reject undeclared non-ASCII in Python source + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: make lint-non-ascii + run: make lint-non-ascii