Skip to content
This repository was archived by the owner on Nov 14, 2024. It is now read-only.

Commit

Permalink
fixup! [nrf noup] ci: Add compliance test
Browse files Browse the repository at this point in the history
Dummy.

Signed-off-by: Chaitanya Tata <[email protected]>
  • Loading branch information
krish2718 committed Sep 8, 2023
1 parent f4d6b0a commit 69a8cb4
Show file tree
Hide file tree
Showing 4 changed files with 279 additions and 26 deletions.
54 changes: 28 additions & 26 deletions .github/workflows/compliance.yml
Original file line number Diff line number Diff line change
@@ -1,16 +1,20 @@
name: Compliance
name: Compliance Checks

on: pull_request

jobs:
compliance_job:
runs-on: ubuntu-latest
check_compliance:
runs-on: ubuntu-22.04
name: Run compliance checks on patch series (PR)
steps:
- name: Update PATH for west
run: |
echo "$HOME/.local/bin" >> $GITHUB_PATH
- name: Checkout the code
uses: actions/checkout@v3
with:
path: wfa_qt_app
path: sdk-hostap
ref: ${{ github.event.pull_request.head.sha }}
fetch-depth: 0

Expand All @@ -21,25 +25,28 @@ jobs:
key: ${{ runner.os }}-doc-pip

- name: Install python dependencies
working-directory: wfa_qt_app
working-directory: sdk-hostap
run: |
pip3 install -U pip
pip3 install -U setuptools
pip3 install -U wheel
grep -E "python-magic|junitparser|lxml|gitlint|pylint|pykwalify|yamllint" scripts/requirements-fixed.txt | xargs pip3 install -U
pip3 install setuptools
pip3 install wheel
pip3 install python-magic lxml junitparser gitlint pylint pykwalify yamllint
pip3 install west
- name: Clone Zephyr downstream
env:
BASE_REF: ${{ github.base_ref }}
working-directory: wfa_qt_app
working-directory: sdk-hostap
run: |
git config --global user.email "[email protected]"
git config --global user.name "Your Name"
git remote -v
# Ensure there's no merge commits in the PR
[[ "$(git rev-list --merges --count origin/${BASE_REF}..)" == "0" ]] || \
(echo "::error ::Merge commits not allowed, rebase instead";false)
#[[ "$(git rev-list --merges --count origin/${BASE_REF}..)" == "0" ]] || \
#(echo "::error ::Merge commits not allowed, rebase instead";false)
# Sauce tag checks before rebasing
git rev-list --first-parent origin/${BASE_REF}..HEAD | tr '\n' ',' | \
xargs gitlint -c ncs-sauce-tags.enable=true \
-c title-starts-with-subsystem.regex=".*" --commits
git rebase origin/${BASE_REF}
# debug
git log --pretty=oneline | head -n 10
Expand All @@ -50,7 +57,7 @@ jobs:
id: codeowners
env:
BASE_REF: ${{ github.base_ref }}
working-directory: wfa_qt_app
working-directory: sdk-hostap
if: contains(github.event.pull_request.user.login, 'dependabot[bot]') != true
run: |
./scripts/ci/codeowners.py -c origin/${BASE_REF}..
Expand All @@ -60,19 +67,14 @@ jobs:
id: compliance
env:
BASE_REF: ${{ github.base_ref }}
working-directory: wfa_qt_app
working-directory: sdk-hostap
if: contains(github.event.pull_request.user.login, 'dependabot[bot]') != true
run: |
export ZEPHYR_BASE="$(dirname "$(pwd)")/wfa_qt_app/sdk-zephyr"
export ZEPHYR_BASE="$(dirname "$(pwd)")/sdk-hostap/sdk-zephyr"
# debug
ls -la
git log --pretty=oneline | head -n 10
# For now we run KconfigBasic, but we should transition to Kconfig
$ZEPHYR_BASE/scripts/ci/check_compliance.py --annotate \
-e KconfigBasic \
-e checkpatch \
-e Kconfig \
-e DevicetreeBindings \
git log --pretty=oneline | head -n 10
./scripts/ci/check_compliance.py --annotate -e KconfigBasic -e Kconfig \
-c origin/${BASE_REF}..
- name: upload-results
Expand All @@ -81,13 +83,13 @@ jobs:
if: contains(github.event.pull_request.user.login, 'dependabot[bot]') != true
with:
name: compliance.xml
path: wfa_qt_app/compliance.xml
path: sdk-hostap/compliance.xml

- name: check-warns
working-directory: wfa_qt_app
working-directory: sdk-hostap
if: contains(github.event.pull_request.user.login, 'dependabot[bot]') != true
run: |
export ZEPHYR_BASE="$(dirname "$(pwd)")/wfa_qt_app/sdk-zephyr"
export ZEPHYR_BASE="$(dirname "$(pwd)")/sdk-hostap/sdk-zephyr"
if [[ ! -s "compliance.xml" ]]; then
exit 1;
fi
Expand Down
60 changes: 60 additions & 0 deletions .gitlint
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# All these sections are optional, edit this file as you like.
[general]
ignore=title-trailing-punctuation, T3, title-max-length, T1, body-hard-tab, B3, B1
# verbosity should be a value between 1 and 3, the commandline -v flags take precedence over this
verbosity = 3
# By default gitlint will ignore merge commits. Set to 'false' to disable.
ignore-merge-commits=false
ignore-revert-commits=false
ignore-fixup-commits=false
ignore-squash-commits=false
# Enable debug mode (prints more output). Disabled by default
debug = false

# Set the extra-path where gitlint will search for user defined rules
# See http://jorisroovers.github.io/gitlint/user_defined_rules for details
extra-path=scripts/gitlint

[title-max-length-no-revert]
line-length=120

[body-min-line-count]
min-line-count=1

[body-max-line-count]
max-line-count=200

[title-starts-with-subsystem]
regex = ^(?!subsys:)(([^:]+):)(\s([^:]+):)*\s(.+)$

[title-must-not-contain-word]
# Comma-separated list of words that should not occur in the title. Matching is case
# insensitive. It's fine if the keyword occurs as part of a larger word (so "WIPING"
# will not cause a violation, but "WIP: my title" will.
words=wip

[title-match-regex]
# python like regex (https://docs.python.org/2/library/re.html) that the
# commit-msg title must be matched to.
# Note that the regex can contradict with other rules if not used correctly
# (e.g. title-must-not-contain-word).
#regex=^US[0-9]*

[max-line-length-with-exceptions]
# B1 = body-max-line-length
line-length=120

[body-min-length]
min-length=3

[body-is-missing]
# Whether to ignore this rule on merge commits (which typically only have a title)
# default = True
ignore-merge-commits=false

[body-changed-file-mention]
# List of files that need to be explicitly mentioned in the body when they are changed
# This is useful for when developers often erroneously edit certain files or git submodules.
# By specifying this rule, developers can only change the file when they explicitly reference
# it in the commit message.
#files=gitlint/rules.py,README.md
59 changes: 59 additions & 0 deletions scripts/gitlint/ncs.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# Copyright (c) 2022 Nordic Semiconductor ASA
# SPDX-License-Identifier: Apache-2.0

from gitlint.rules import BoolOption, CommitRule, RuleViolation
import re

class NCSSauceTags(CommitRule):
"""This rule enforces the NCS sauce tag specification.
https://developer.nordicsemi.com/nRF_Connect_SDK/doc/latest/nrf/dm_code_base.html#oss-repositories-downstream-project-history
"""

# A rule MUST have a human friendly name
name = "ncs-sauce-tags"

# A rule MUST have a *unique* id, we recommend starting with UC (for User-defined Commit-rule).
id = "UC100"

options_spec = [BoolOption('enable', False, 'Enable the Sauce Tags Rule')]

def validate(self, commit):
self.log.debug(f'NCSSauceTags: enable:{self.options["enable"].value} ' \
f'sha:{commit.sha}')

if not self.options['enable'].value:
self.log.debug(f'Disabled, skipping')
return

m = re.match(r'^(Revert\s+\")?\[nrf (mergeup|fromtree|fromlist|noup)\]\s+',
commit.message.title)
if not m:
return [RuleViolation(self.id, 'Title does not contain a sauce tag',
line_nr=1)]

tag = m.group(2)

if not tag:
return [RuleViolation(self.id, 'Title does not contain a sauce tag',
line_nr=1)]
self.log.debug(f'Matched sauce tag {tag}')

if tag == 'mergeup':
if not commit.is_merge_commit:
return [RuleViolation(self.id,
'mergeup used in a non-merge commit')]
if not re.match(r'^\[nrf mergeup\] Merge upstream up to commit \b([a-f0-9]{40})\b',
commit.message.title):
return [RuleViolation(self.id, 'Invalid mergeup commit title')]
elif tag == 'fromlist':
regex = r'^Upstream PR: https://github\.com/.*/pull/\d+'
matches = re.findall(regex, '\n'.join(commit.message.body), re.MULTILINE)
if len(matches) == 0:
return [RuleViolation(self.id,
'Missing Upstream PR reference in fromlist commit')]
elif tag == 'fromtree':
regex = r'^\(cherry picked from commit \b([a-f0-9]{40})\b\)'
matches = re.findall(regex, '\n'.join(commit.message.body), re.MULTILINE)
if len(matches) == 0:
return [RuleViolation(self.id,
'Missing cherry-pick reference in fromtree commit')]
132 changes: 132 additions & 0 deletions scripts/gitlint/zephyr_commit_rules.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
# SPDX-License-Identifier: Apache-2.0

"""
The classes below are examples of user-defined CommitRules. Commit rules are gitlint rules that
act on the entire commit at once. Once the rules are discovered, gitlint will automatically take care of applying them
to the entire commit. This happens exactly once per commit.
A CommitRule contrasts with a LineRule (see examples/my_line_rules.py) in that a commit rule is only applied once on
an entire commit. This allows commit rules to implement more complex checks that span multiple lines and/or checks
that should only be done once per gitlint run.
While every LineRule can be implemented as a CommitRule, it's usually easier and more concise to go with a LineRule if
that fits your needs.
"""

from gitlint.rules import CommitRule, RuleViolation, CommitMessageTitle, LineRule, CommitMessageBody
from gitlint.options import IntOption, StrOption
import re

class BodyMinLineCount(CommitRule):
# A rule MUST have a human friendly name
name = "body-min-line-count"

# A rule MUST have an *unique* id, we recommend starting with UC (for User-defined Commit-rule).
id = "UC6"

# A rule MAY have an option_spec if its behavior should be configurable.
options_spec = [IntOption('min-line-count', 2, "Minimum body line count excluding Signed-off-by")]

def validate(self, commit):
filtered = [x for x in commit.message.body if not x.lower().startswith("signed-off-by") and x != '']
line_count = len(filtered)
min_line_count = self.options['min-line-count'].value
if line_count < min_line_count:
message = "Commit message body is empty, should at least have {} line(s).".format(min_line_count)
return [RuleViolation(self.id, message, line_nr=1)]

class BodyMaxLineCount(CommitRule):
# A rule MUST have a human friendly name
name = "body-max-line-count"

# A rule MUST have an *unique* id, we recommend starting with UC (for User-defined Commit-rule).
id = "UC1"

# A rule MAY have an option_spec if its behavior should be configurable.
options_spec = [IntOption('max-line-count', 3, "Maximum body line count")]

def validate(self, commit):
line_count = len(commit.message.body)
max_line_count = self.options['max-line-count'].value
if line_count > max_line_count:
message = "Commit message body contains too many lines ({0} > {1})".format(line_count, max_line_count)
return [RuleViolation(self.id, message, line_nr=1)]

class SignedOffBy(CommitRule):
""" This rule will enforce that each commit contains a "Signed-off-by" line.
We keep things simple here and just check whether the commit body contains a line that starts with "Signed-off-by".
"""

# A rule MUST have a human friendly name
name = "body-requires-signed-off-by"

# A rule MUST have an *unique* id, we recommend starting with UC (for User-defined Commit-rule).
id = "UC2"

def validate(self, commit):
flags = re.UNICODE
flags |= re.IGNORECASE
for line in commit.message.body:
if line.lower().startswith("signed-off-by"):
if not re.search(r"(^)Signed-off-by: ([-'\w.]+) ([-'\w.]+) (.*)", line, flags=flags):
return [RuleViolation(self.id, "Signed-off-by: must have a full name", line_nr=1)]
else:
return
return [RuleViolation(self.id, "Commit message does not contain a 'Signed-off-by:' line", line_nr=1)]

class TitleMaxLengthRevert(LineRule):
name = "title-max-length-no-revert"
id = "UC5"
target = CommitMessageTitle
options_spec = [IntOption('line-length', 72, "Max line length")]
violation_message = "Commit title exceeds max length ({0}>{1})"

def validate(self, line, _commit):
max_length = self.options['line-length'].value
if len(line) > max_length and not line.startswith("Revert"):
return [RuleViolation(self.id, self.violation_message.format(len(line), max_length), line)]

class TitleStartsWithSubsystem(LineRule):
name = "title-starts-with-subsystem"
id = "UC3"
target = CommitMessageTitle
options_spec = [StrOption('regex', ".*", "Regex the title should match")]

def validate(self, title, _commit):
regex = self.options['regex'].value
pattern = re.compile(regex, re.UNICODE)
violation_message = "Commit title does not follow [subsystem]: [subject] (and should not start with literal subsys:)"
if not pattern.search(title):
return [RuleViolation(self.id, violation_message, title)]

class MaxLineLengthExceptions(LineRule):
name = "max-line-length-with-exceptions"
id = "UC4"
target = CommitMessageBody
options_spec = [IntOption('line-length', 80, "Max line length")]
violation_message = "Commit message body line exceeds max length ({0}>{1})"

def validate(self, line, _commit):
max_length = self.options['line-length'].value
urls = re.findall(r'http[s]?://(?:[a-zA-Z]|[0-9]|[$-_@.&+]|[!*\(\),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+', line)
if line.startswith('Signed-off-by'):
return

if urls:
return

if len(line) > max_length:
return [RuleViolation(self.id, self.violation_message.format(len(line), max_length), line)]

class BodyContainsBlockedTags(LineRule):
name = "body-contains-blocked-tags"
id = "UC7"
target = CommitMessageBody
tags = ["Change-Id"]

def validate(self, line, _commit):
flags = re.IGNORECASE
for tag in self.tags:
if re.search(rf"^\s*{tag}:", line, flags=flags):
return [RuleViolation(self.id, f"Commit message contains a blocked tag: {tag}")]
return

0 comments on commit 69a8cb4

Please sign in to comment.