diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index d3695876209..ae317540ade 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -213,15 +213,20 @@ The first line should be a succinct description of what the commit does, startin Commits for RHEL Branches ^^^^^^^^^^^^^^^^^^^^^^^^^^ -If you are submitting a patch for any rhel-branch, the last line of your commit must identify the bugzilla bug id it fixes, using the ``Resolves`` or ``Related`` keyword, e.g.: -``Resolves: rhbz#111111`` +If you are submitting a patch for any rhel-branch, the last line of your commit must identify the `JIRA issue `_ id it fixes, using the ``Resolves``, ``Related`` or ``Reverts`` keyword, e.g.: +``Resolves: RHEL-11111`` or -``Related: rhbz#1234567`` +``Related: RHEL-12345`` + +or + +``Reverts: RHEL-22222`` Use ``Resolves`` if the patch fixes the core issue which caused the bug. Use ``Related`` if the patch fixes an ancillary issue that is related to, but might not actually fix the bug. +Use ``Reverts`` if this patch reverts changes introduced by linked bug. Release Notes ^^^^^^^^^^^^^ diff --git a/docs/commit-log.rst b/docs/commit-log.rst index 0337321a56c..3cf6e53d5d6 100644 --- a/docs/commit-log.rst +++ b/docs/commit-log.rst @@ -5,24 +5,15 @@ git commit messages for anaconda should follow a consistent format. The following are rules to follow when committing a change to the git repo: 1) The first line of the commit message should be a short summary of the - change in the patch. If the alterations pertain to a specific UI deliverable - in Anaconda, begin the line with the lowercase name - gui, tui. We - also place (#BUGNUMBER) at the end of this line to indicate the - bugzilla.redhat.com bug number addressed in this patch. The bug number is - optional since there may be no bug number, but if you have one you are - addressing, please include it on the summary line. Lastly, the summary lines - need to be short. Ideally less than 75 characters, but certainly not longer - than 80. + change in the patch. Ideally less than 75 characters, but certainly not + longer than 80. If the alterations pertain to a specific UI deliverable + in Anaconda, begin the line with the lowercase name - gui, tui. Here are acceptable first lines for git commit messages:: - Check partition and filesystem type on upgrade (#123456) - Fix bootloader configuration setup on ppc64 (#987654) + Fix bootloader configuration setup on ppc64 gui: Introduce a new screen for setting your preferred email client - The last one would be a new feature that we didn't have a bug number - for. - 2) The main body of the commit message should begin TWO LINES below the summary line you just entered (that is, there needs to be a blank line between the one line summary and the start of the long commit message). @@ -32,32 +23,25 @@ following are rules to follow when committing a change to the git repo: wrap long lines. If you don't, the 'git log' output ends up looking stupid on 80 column terminals. -3) For RHEL bugs, all commits need to reference a bug number. You may - follow one of two formats for specifying the bug number in a RHEL commit. - - a) Put the bug number on the summary line in (#BUGNUMBER) format. Bugs - listed this way are treated as 'Resolves' patches in the RHEL - universe. - - b) If you have a patch that is Related to or Conflicts with another bug, - you may add those lines to the end of the long commit message in this - format:: +3) For RHEL or CentOS Stream bugs, all commits need to reference a bug + issue name. These bugs can be filed + `here `_. - Related: rhbz#BUGNUMBER - Conflicts: rhbz#BUGNUMBER - Resolves: rhbz#BUGNUMBER + If you have a patch that is Related to or Reverts another bug, + you may add those line to the end of the long commit message in this + format:: - These entries should come at the end of the long commit message and - must follow the format above. You may have as many of these lines as - appropriate for the patch. + Related: RHEL-ISSUENUMBER + Reverts: RHEL-ISSUENUMBER + Resolves: RHEL-ISSUENUMBER - c) Patches that are 'Resolves' patches have two methods to specify the - bug numbers, but Related and Conflicts can only be listed in the long - commit message. + These entries should come at the end of the long commit message and + must follow the format above. You may have as many of these lines as + appropriate for the patch. On RHEL branches, the 'bumpver' process will verify that each patch for - the release references a RHEL bug number. The scripts/makebumpver script - will extract the bug numbers from RHEL branch commits and do two things. + the release references a RHEL issue. The scripts/makebumpver script + will extract the bug issues from RHEL branch commits and do two things. First, it verifies that the bug referenced is a RHEL bug and in correct - states. Second, it adds the appropriate Resolves/Related/Conflicts line + states. Second, it adds the appropriate Resolves/Related/Reverts line to the RPM spec file changelog. diff --git a/scripts/makebumpver b/scripts/makebumpver index 2125d6a38db..eda81504f90 100755 --- a/scripts/makebumpver +++ b/scripts/makebumpver @@ -18,32 +18,53 @@ # You should have received a copy of the GNU Lesser General Public License # along with this program. If not, see . -import logging -logging.basicConfig(level=logging.DEBUG) -log = logging.getLogger("bugzilla") -log.setLevel(logging.INFO) - import datetime -import argparse -import os -import re -import subprocess -import sys import textwrap +import sys +import subprocess +import re +import os +import argparse +import logging + +from rhel_version import rhel_version +from functools import cache +try: + from jira import JIRA, JIRAError # pylint: disable=import-error +except ModuleNotFoundError: + print("Can't find jira python library. Please install it ideally by:\n", file=sys.stderr) + print("dnf install python3-jira\n", file=sys.stderr) + print("Otherwise it could be installed also by pip:\n", file=sys.stderr) + print("pip3 install jira", file=sys.stderr) + + sys.exit(2) + +log = logging.getLogger("bumpver") +log.setLevel(logging.INFO) VERSION_NUMBER_INCREMENT = 1 DEFAULT_ADDED_VERSION_NUMBER = 1 DEFAULT_MINOR_VERSION_NUMBER = 1 +TOKEN_PATH = "~/.config/anaconda/jira" + def run_program(*args): """Run a program with universal newlines on""" # pylint: disable=no-value-for-parameter return subprocess.Popen(*args, universal_newlines=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate() + +def print_failure(msg, bug, action, commit, summary): + print(f"*** {action} {bug}: {msg}.") + print(f"*** Commit: {commit}") + print(f"*** {summary}\n") + + class ParseCommaSeparatedList(argparse.Action): """A parsing action that parses a comma separated list from the value provided to the option into a list.""" + def __call__(self, parser, namespace, values, option_string=None): value_list = [] for value in values.split(","): @@ -55,6 +76,73 @@ class ParseCommaSeparatedList(argparse.Action): value_list.append(value) setattr(namespace, self.dest, value_list) + +class JIRAValidator: + def __init__(self): + self.jira = None + + self._connect() + + def _connect(self): + """Connect to our JIRA instance by provided token file.""" + token = self._read_token(TOKEN_PATH) + self.jira = JIRA("https://issues.redhat.com/", token_auth=token) + + @staticmethod + def _read_token(token_path): + token_path = os.path.expanduser(token_path) + try: + with open(token_path, "r", encoding="utf-8") as f: + return f.read().strip() + except FileNotFoundError: + print(f"Token can't be found! Please generate your JIRA token in {TOKEN_PATH}", file=sys.stderr) + print("Please generate the token here: https://issues.redhat.com/secure/ViewProfile.jspa?selectedTab=com.atlassian.pats.pats-plugin:jira-user-personal-access-tokens") + sys.exit(1) + + return None + + @cache + def _query_RHEL_issue(self, bug): + try: + issue = self.jira.issue(bug) + except JIRAError as ex: + raise ValueError(f"Error during JIRA query for bug {bug}: {ex.text}") from ex + + return issue + + def check_if_RHEL_issue(self, bug): + if not bug.startswith("RHEL-"): + return False, "is not the 'RHEL' JIRA project - Skipping" + + self._query_RHEL_issue(bug) + + return True, "" + + def validate_RHEL_issue_status(self, bug): + issue = self._query_RHEL_issue(bug) + + valid_statuses = ("Planning", "In Progress") + status = issue.fields.status.name + if status not in valid_statuses: + return False, f"incorrect status '{status}'! Valid values: {valid_statuses}" + + return True, "" + + def validate_RHEL_issue_fixversion(self, bug): + issue = self._query_RHEL_issue(bug) + + valid_fix_version = rhel_version + fix_version = issue.fields.fixVersions + for version in fix_version: + # Let's simplify and check only major version (ignore minor RHEL releases) + if version.name.startswith(valid_fix_version): + return True, "" + + current_versions = ", ".join(map(lambda x: x.name, fix_version)) + msg = f"incorrect fixVersion '{current_versions}'! It has to start with {valid_fix_version}" + return False, msg + + class MakeBumpVer: def __init__(self): cwd = os.getcwd() @@ -77,32 +165,26 @@ class MakeBumpVer: regexp = re.compile(r"ANACONDA_RELEASE, \[(.*)\]") self.current_release = re.search(regexp, config_ac).groups()[0] - #argument parsing + # argument parsing parser = argparse.ArgumentParser(description="Increments version number and adds the RPM spec file changelog block. " "Also runs some checks such as ensuring rhel*-branch commits correctly " "reference RHEL bugs.", epilog="The -i switch is intended for use with utility commits that we do not need to " - "reference in the spec file changelog.\n" - "The -m switch is used to map a Fedora BZ number to a RHEL BZ number for " - "the spec file changelog.\n" - "Use -m if you have a commit that needs to reference a RHEL bug and have cloned " - "the bug, but the original commit was already pushed to the central repo.") + "reference in the spec file changelog.\n") - parser.add_argument("-n", "--name", dest="name", default=current_name, metavar="PACKAGE NAME", help="Package name.") + parser.add_argument("-n", "--name", dest="name", default=current_name, + metavar="PACKAGE NAME", help="Package name.") parser.add_argument("-v", "--version", dest="version", default=current_version, metavar="CURRENT PACKAGE VERSION", help="Current package version number.") parser.add_argument("-r", "--release", dest="release", default=1, metavar="PACKAGE RELEASE NUMBER", help="Package release number.") - parser.add_argument("--newrelease", dest="new_release", default=None, help="Value for release in the .spec file.") + parser.add_argument("--newrelease", dest="new_release", default=None, + help="Value for release in the .spec file.") parser.add_argument("-b", "--bugreport", dest="bugreporting_email", default=current_bug_reporting_mail, metavar="EMAIL ADDRESS", help="Bug reporting email address.") parser.add_argument("-i", "--ignore", dest="ignored_commits", default=[], action=ParseCommaSeparatedList, metavar="COMMA SEAPARATED COMMIT IDS", help="Comma separated list of git commits to ignore.") - parser.add_argument("-m", "--map", dest="fedora_rhel_bz_map", default=[], action=ParseCommaSeparatedList, - metavar="COMMA SEPARATED BZ MAPPINGS", help="Comma separated list of FEDORA_BZ=RHEL_BZ mappings.") - parser.add_argument("-s", "--skip-acks", dest="skip_acks", action="store_true", default=False, - help="Skip checking for rhel-X.X.X ack flags.") - parser.add_argument("-S", "--skip-all", dest="skip_all_acks", action="store_true", default=False, + parser.add_argument("-S", "--skip-checks", dest="skip_all_checks", action="store_true", default=False, help="Skip all checks.") parser.add_argument("-d", "--debug", dest="debug", action="store_true", default=False, help="Enable debug logging to stdout.") @@ -120,31 +202,29 @@ class MakeBumpVer: self.args = parser.parse_args() if self.args.debug: - log.setLevel(logging.DEBUG) + logging.basicConfig(level=logging.DEBUG) if self.args.bump_major_version and self.args.add_version_number: - sys.stderr.write("The --bump-major-version and --add-version-number options can't be used at the same time.\n") + print("The --bump-major-version and --add-version-number options can't be used at the same time.", + file=sys.stderr) sys.exit(1) if self.args.unknown_arguments: parser.print_usage() args_string = " ".join(self.args.unknown_arguments) - sys.stderr.write("unknown arguments: %s\n" % args_string) + print("unknown arguments: %s" % args_string, file=sys.stderr) sys.exit(1) if not os.path.isfile(self.configure) and not os.path.isfile(self.spec): - sys.stderr.write("You must be at the top level of the anaconda source tree.\n") + print("You must be at the top level of the anaconda source tree.", file=sys.stderr) sys.exit(1) # general initialization log.debug("%s", self.args) - self.bzserver = 'bugzilla.redhat.com' - self.bzurl = "https://%s/xmlrpc.cgi" % self.bzserver - self.bz = None - self._bz_cache = {} + self._jira = JIRAValidator() - self.gituser = self._gitConfig('user.name') - self.gitemail = self._gitConfig('user.email') + self.gituser = self._git_config('user.name') + self.gitemail = self._git_config('user.email') self.name = self.args.name self.version = self.args.version @@ -152,31 +232,19 @@ class MakeBumpVer: self.new_release = self.args.new_release or self.release self.bugreport = self.args.bugreporting_email self.ignore = self.args.ignored_commits - - # apply the bug map - self.bugmap = {} - for mapping in self.args.fedora_rhel_bz_map: - bugs = mapping.split('=') - if len(bugs) == 2: - self.bugmap[bugs[0]] = bugs[1] - - self.skip_acks = self.args.skip_acks - self.skip_all = self.args.skip_all_acks + self.skip = self.args.skip_all_checks self.dry_run = self.args.dry_run - if self.skip_all: - self.skip_acks = True - self.git_branch = None # RHEL release number or None (also fills in self.git_branch) - self.rhel = self._isRHEL() + self.rhel = self._is_RHEL() - def _gitConfig(self, field): + def _git_config(self, field): proc = run_program(['git', 'config', field]) return proc[0].strip('\n') - def _incrementVersion(self, bump_major_version=False, add_version_number=False): + def _increment_version(self, bump_major_version=False, add_version_number=False): fields = self.version.split('.') if add_version_number: # add another version number to the end fields.append(str(int(DEFAULT_ADDED_VERSION_NUMBER))) @@ -188,7 +256,7 @@ class MakeBumpVer: new = ".".join(fields) return new - def _isRHEL(self): + def _is_RHEL(self): proc = run_program(['git', 'branch']) lines = [x for x in proc[0].strip('\n').split('\n') if x.startswith('*')] @@ -207,13 +275,13 @@ class MakeBumpVer: m = re.match(branch_pattern, self.git_branch) if m: return m.group(1) - rhel8_branch_pattern = r"^rhel-(\d+)(.*)" - m = re.match(rhel8_branch_pattern, self.git_branch) + rhel_branch_pattern = r"^rhel-(\d+)(.*)" + m = re.match(rhel_branch_pattern, self.git_branch) if m: return m.group(1) return False - def _getCommitDetail(self, commit, field): + def _get_commit_detail(self, commit, field): proc = run_program(['git', 'log', '-1', "--pretty=format:%s" % field, commit]) ret = proc[0].strip('\n').split('\n') @@ -226,80 +294,7 @@ class MakeBumpVer: return ret - def _queryBug(self, bugid): - if not self.bz: - # This is only place where bugzilla library is used. Exclude from dependencies and - # ignore pylint. - import bugzilla # pylint: disable=import-error - sys.stdout.write("Connecting to %s...\n" % self.bzserver) - self.bz = bugzilla.Bugzilla(url=self.bzurl) - - if not self.bz.logged_in: - sys.stdout.write( - "Provide an API key from https://{}/userprefs.cgi?tab=apikey" - "\n".format(self.bzserver) - ) - self.bz.interactive_save_api_key() - - if bugid in self._bz_cache: - return self._bz_cache[bugid] - - bug = self.bz.getbug(bugid, extra_fields="flags") - log.debug("bug = %s", bug) - - if not bug: - return None - else: - self._bz_cache[bugid] = bug - return bug - - def _isRHELBug(self, bug, commit, summary): - bzentry = self._queryBug(bug) - - if not bzentry: - print("*** Bugzilla query for %s failed.\n" % bug) - return False - - if bzentry.product.startswith('Red Hat Enterprise Linux'): - return True - else: - print("*** Bug %s is not a RHEL bug." % bug) - print("*** Commit: %s" % commit) - print("*** %s\n" % summary) - return False - - def _isRHELBugInCorrectState(self, bug, commit, summary): - bzentry = self._queryBug(bug) - - if not bzentry: - print("*** Bugzilla query for %s failed.\n" % bug) - return False - - if bzentry.bug_status in ['POST', 'MODIFIED', 'ON_QA']: - return True - else: - print("*** Bug %s is not in POST, MODIFIED or ON_QA." % bug) - print("*** Commit: %s" % commit) - print("*** %s\n" % summary) - return False - - def _isRHELBugAcked(self, bug, commit, summary): - """ Check the bug's ack state - """ - if not self.rhel or self.skip_acks: - return True - - bzentry = self._queryBug(bug) - for f in bzentry.flags: - if f['name'] == 'release' and f['status'] == '+': - return True - - print("*** Bug %s does not have ACK" % bug) - print("*** Commit: %s" % commit) - print("*** %s\n" % summary) - return False - - def _rpmLog(self, fixedIn): + def _rpm_log(self, fixedIn): git_range = "%s-%s-%s.." % (self.name, self.version, self.release) proc = run_program(['git', 'log', '--no-merges', '--pretty=oneline', git_range]) lines = proc[0].strip('\n').split('\n') @@ -308,7 +303,6 @@ class MakeBumpVer: lines = [x for x in lines if not x.startswith(commit)] rpm_log = [] - bad_bump = False bad = False for line in lines: @@ -317,9 +311,9 @@ class MakeBumpVer: fields = line.split(' ') commit = fields[0] - summary = self._getCommitDetail(commit, "%s")[0] - body = self._getCommitDetail(commit, "%b") - author = self._getCommitDetail(commit, "%aE")[0] + summary = self._get_commit_detail(commit, "%s")[0] + body = self._get_commit_detail(commit, "%b") + author = self._get_commit_detail(commit, "%aE")[0] if re.match(r".*(#infra).*", summary) or re.match(r"infra: .*", summary): print("*** Ignoring (#infra) commit %s\n" % commit) @@ -334,120 +328,71 @@ class MakeBumpVer: continue if self.rhel: - rhbz = set() - bad = False - - # look for a bug in the summary line, validate if found - m = re.search(r"\(#\d+(\,.*)*\)", summary) - if m: - fullbug = summary[m.start():m.end()] - bugstr = summary[m.start() + 2:m.end() - 1] - - bug = '' - for c in bugstr: - if c.isdigit(): - bug += c - else: - break - - if len(bugstr) > len(bug): - tmp = bugstr[len(bug):] - - for c in tmp: - if not c.isalpha(): - tmp = tmp[1:] - else: - break - - if len(tmp) > 0: - author = tmp - - ckbug = self.bugmap.get(bug, bug) - - valid = self.skip_all or self._isRHELBug(ckbug, commit, summary) - - if valid: - summary = summary.replace(fullbug, "(%s)" % author) - rhbz.add("Resolves: rhbz#%s" % ckbug) - - if not self.skip_all: - if not self._isRHELBugInCorrectState(ckbug, commit, - summary): - bad = True - - if not self._isRHELBugAcked(ckbug, commit, summary): - bad = True - else: - bad = True - summary_bug = ckbug - else: - summary = summary.strip() - summary += " (%s)" % author - summary_bug = None - - for bodyline in body: - m = re.match(r"^(Resolves|Related|Conflicts):\ +rhbz#\d+.*$", - bodyline) - if not m: - continue - - actionre = re.search("(Resolves|Related|Conflicts)", - bodyline) - bugre = re.search(r"\d+", bodyline) - if actionre and bugre: - action = actionre.group() - bug = bugre.group() - ckbug = self.bugmap.get(bug, bug) - - valid = self.skip_all or self._isRHELBug(ckbug, commit, summary) - - if valid: - rhbz.add("%s: rhbz#%s" % (action, ckbug)) - - # Remove the summary bug's Resolves action if it is for the same bug - if action != 'Resolves': - summary_str = "Resolves: rhbz#%s" % summary_bug - if summary_bug and ckbug == summary_bug and summary_str in rhbz: - rhbz.remove(summary_str) - else: - bad = True - - if self.skip_all: - print("*** Bug %s Related commit %s is allowed\n" % (bug, commit)) - continue - - not_correct = not self._isRHELBugInCorrectState(ckbug, commit, summary) - not_acked = not self._isRHELBugAcked(ckbug, commit, summary) - - if valid and action == 'Resolves' and (not_correct or not_acked): - bad = True - elif valid and action == 'Related': - # A related bug needs to have acks, and if it is the same as the summary - # It overrides the summary having different fixed-in or state - if self._isRHELBugAcked(ckbug, commit, summary): - print("*** Bug %s Related commit %s is allowed\n" % (bug, commit)) - if ckbug == summary_bug: - bad = False - else: - bad = True - - if len(rhbz) == 0 and not self.skip_all: - print("*** No bugs referenced in commit %s\n" % commit) + bad_commit, bugs = self._check_rhel_issues(commit, summary, body, author) + + # If one of the commits were bad mark whole set as bad + if bad_commit: bad = True - rpm_log.append((summary.strip(), list(rhbz))) + rpm_log.append(("%s (%s)" % (summary.strip(), author), list(bugs))) else: rpm_log.append(("%s (%s)" % (summary.strip(), author), None)) - if bad: - bad_bump = True - - if bad_bump: + if bad: sys.exit(1) return rpm_log - def _writeNewConfigure(self, newVersion): + def _check_rhel_issues(self, commit, summary, body, author): + issues = set() + bad = False + + summary = summary + f" ({author})" + + for bodyline in body: + # based on recommendation + # https://source.redhat.com/groups/public/release-engineering/release_engineering_rcm_wiki/using_gitbz + m = re.search(r"^(Resolves|Related|Reverts):\ +(\w+-\d+).*$", bodyline) + if not m: + continue + + action = m.group(1) + bug = m.group(2) + + if action and bug: + # store the bug to output list if checking is disabled and continue + if self.skip: + issues.add(f"{action}: {bug}") + print(f"*** {action} {bug} commit {commit} is skipping checks\n") + continue + + if not self._jira.check_if_RHEL_issue(bug): + print_failure("is not the 'RHEL' JIRA project", bug, action, commit, summary) + bad = True + continue + + valid_status, msg = self._jira.validate_RHEL_issue_status(bug) + if not valid_status: + print_failure(msg, bug, action, commit, summary) + bad = True + continue + + valid_fixversion, msg = self._jira.validate_RHEL_issue_fixversion(bug) + if not valid_fixversion: + print_failure(msg, bug, action, commit, summary) + bad = True + continue + + print(f"*** {action} {bug} commit {commit} is allowed\n") + issues.add(f"{action}: {bug}") + + if len(issues) == 0 and not self.skip: + print("*** No bugs referenced in commit %s\n" % commit) + bad = True + + return bad, issues + + def _write_new_configure(self, newVersion): f = open(self.configure, 'r') l = f.readlines() f.close() @@ -466,7 +411,7 @@ class MakeBumpVer: f.writelines(l) f.close() - def _writeNewSpec(self, newVersion, rpmlog): + def _write_new_spec(self, newVersion, rpmlog): f = open(self.spec, 'r') l = f.readlines() f.close() @@ -484,7 +429,7 @@ class MakeBumpVer: f.write("* %s %s <%s> - %s-%s\n" % (stamp, self.gituser, self.gitemail, newVersion, self.new_release)) - for msg, rhbz in rpmlog: + for msg, bugs in rpmlog: msg = re.sub('(?. + + +# on Fedora we don't use this +rhel_version = "" diff --git a/scripts/rhel_version.py.j2 b/scripts/rhel_version.py.j2 new file mode 100644 index 00000000000..c9f15660da4 --- /dev/null +++ b/scripts/rhel_version.py.j2 @@ -0,0 +1,26 @@ +# +# This is extension script for makebumpver to extract templating from the +# whole script to just smaller module. +# +# Copyright (C) 2023 Red Hat, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see . + + +{% if distro_name == "fedora" %} +# on Fedora we don't use this +rhel_version = "" +{% else %} +rhel_version = "rhel-{$ distro_release $}" +{% endif %} diff --git a/tests/pylint/runpylint.py b/tests/pylint/runpylint.py index 2d41f748207..39c52dc1096 100755 --- a/tests/pylint/runpylint.py +++ b/tests/pylint/runpylint.py @@ -68,10 +68,13 @@ def _get_py_paths(self, directory): # going to be valid python anyway. continue - # Test any file that either ends in .py or contains #!/usr/bin/python - # in the first line. - if f.endswith(".py") or (line and str(line).startswith("#!/usr/bin/python")): - retval.append(root + "/" + f) + # Test only files which meets these conditions: + # Ignore j2 files which are input for template rendering + if not f.endswith(".j2"): + # Either ends in .py or contains #!/usr/bin/python in the first line. + if f.endswith(".py") or \ + (line and str(line).startswith("#!/usr/bin/python")): + retval.append(root + "/" + f) return retval