Skip to content

Commit

Permalink
Enable to trigger code guideline checks when PR is created (bytecodea…
Browse files Browse the repository at this point in the history
…lliance#790)

Enable to trigger code guideline checks for C/C++ source codes when PR is created
  • Loading branch information
lum1n0us authored Oct 19, 2021
1 parent 68d72c3 commit 225f5d0
Show file tree
Hide file tree
Showing 3 changed files with 313 additions and 263 deletions.
39 changes: 39 additions & 0 deletions .github/workflows/codeing_guildelines.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# Copyright (C) 2019 Intel Corporation. All rights reserved.
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
name: Coding Guidelines

on:
# will be triggered on PR events
pull_request:
# allow to be triggered manually
workflow_dispatch:

# Cancel any in-flight jobs for the same PR/branch so there's only one active
# at a time
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
# Cancel any in-flight jobs for the same PR/branch so there's only one active
# at a time
cancel_previous:
runs-on: ubuntu-latest
steps:
- name: Cancel Workflow Action
uses: styfle/cancel-workflow-action@0.6.0
with:
access_token: ${{ github.token }}

complinace_job:
needs: cancel_previous
runs-on: ubuntu-latest
steps:
- name: checkout
uses: actions/checkout@v2
with:
fetch-depth: 0

# github.event.pull_request.base.label = ${{github.repository}}/${{github.base_ref}}
- name: Run Coding Guidelines Checks
run: /usr/bin/env python3 ./ci/coding_guidelines_check.py --commits ${{ github.event.pull_request.base.sha }}..HEAD
274 changes: 274 additions & 0 deletions ci/coding_guidelines_check.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,274 @@
#!/usr/bin/env python3
#
# Copyright (C) 2019 Intel Corporation. All rights reserved.
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
#
import argparse
import re
import pathlib
import re
import shlex
import shutil
import subprocess
import sys

CLANG_FORMAT_CMD = "clang-format-12"
GIT_CLANG_FORMAT_CMD = "git-clang-format-12"

# glob style patterns
EXCLUDE_PATHS = [
"**/.git/*",
"**/.github/*",
"**/.vscode/*",
"**/assembly-script/*",
"**/build/*",
"**/build-scripts/*",
"**/ci/*",
"**/core/deps/*",
"**/doc/*",
"**/samples/wasm-c-api/src/*.*",
"**/samples/workload/*",
"**/test-tools/wasi-sdk/*",
"**/tests/wamr-test-suites/workspace/*",
"**/wamr-sdk/*",
]

C_SUFFIXES = [".c", ".cpp", ".h"]
INVALID_DIR_NAME_SEGMENT = r"([a-zA-Z0-9]+\_[a-zA-Z0-9]+)"
INVALID_FILE_NAME_SEGMENT = r"([a-zA-Z0-9]+\-[a-zA-Z0-9]+)"


def locate_command(command: str) -> bool:
if not shutil.which(command):
print(f"Command '{command}'' not found")
return False

return True


def is_excluded(path: str) -> bool:
path = pathlib.Path(path).resolve()
for exclude_path in EXCLUDE_PATHS:
if path.match(exclude_path):
return True
return False


def pre_flight_check(root: pathlib) -> bool:
def check_aspell(root):
return True

def check_clang_foramt(root: pathlib) -> bool:
if not locate_command(CLANG_FORMAT_CMD):
return False

# Quick syntax check for .clang-format
try:
subprocess.check_output(
shlex.split(f"{CLANG_FORMAT_CMD} --dump-config"), cwd=root
)
except subprocess.CalledProcessError:
print(f"Might have a typo in .clang-format")
return False
return True

def check_git_clang_format() -> bool:
return locate_command(GIT_CLANG_FORMAT_CMD)

return check_aspell(root) and check_clang_foramt(root) and check_git_clang_format()


def run_clang_format(file_path: pathlib, root: pathlib) -> bool:
try:
subprocess.check_call(
shlex.split(
f"{CLANG_FORMAT_CMD} --style=file --Werror --dry-run {file_path}"
),
cwd=root,
)
return True
except subprocess.CalledProcessError:
print(f"{file_path} failed the check of {CLANG_FORMAT_CMD}")
return False


def run_clang_format_diff(root: pathlib, commits: str) -> bool:
"""
Use `clang-format-12` and `git-clang-format-12` to check code
format of the PR, which specificed a commit range. It is required to
format code before `git commit` or when failed the PR check:
``` shell
cd path/to/wamr/root
clang-format-12 --style file -i path/to/file
```
The code wrapped by `/* clang-format off */` and `/* clang-format on */`
will not be formatted, you shall use them when the formatted code is not
readable or friendly:
``` cc
/* clang-format off */
code snippets
/* clang-format on */
```
"""
try:
before, after = commits.split("..")
after = after if after else "HEAD"
COMMAND = (
f"{GIT_CLANG_FORMAT_CMD} -v --binary "
f"{shutil.which(CLANG_FORMAT_CMD)} --style file "
f"--extensions c,cpp,h --diff {before} {after}"
)

p = subprocess.Popen(
shlex.split(COMMAND),
stdout=subprocess.PIPE,
stderr=None,
stdin=None,
universal_newlines=True,
)

stdout, _ = p.communicate()
if not stdout.startswith("diff --git"):
return True

diff_content = stdout.split("\n")
found = False
for summary in [x for x in diff_content if x.startswith("diff --git")]:
# b/path/to/file -> path/to/file
with_invalid_format = re.split("\s+", summary)[-1][2:]
if not is_excluded(with_invalid_format):
print(f"--- {with_invalid_format} failed on code style checking.")
found = True
else:
return not found
except subprocess.subprocess.CalledProcessError:
return False


def run_aspell(file_path: pathlib, root: pathlib) -> bool:
return True


def check_dir_name(path: pathlib, root: pathlib) -> bool:
m = re.search(INVALID_DIR_NAME_SEGMENT, str(path.relative_to(root)))
if m:
print(f"--- found a character '_' in {m.groups()} in {path}")

return not m


def check_file_name(path: pathlib) -> bool:
m = re.search(INVALID_FILE_NAME_SEGMENT, path.stem)
if m:
print(f"--- found a character '-' in {m.groups()} in {path}")

return not m


def parse_commits_range(root: pathlib, commits: str) -> list:
GIT_LOG_CMD = f"git log --pretty='%H' {commits}"
try:
ret = subprocess.check_output(
shlex.split(GIT_LOG_CMD), cwd=root, universal_newlines=True
)
return [x for x in ret.split("\n") if x]
except subprocess.CalledProcessError:
print(f"can not parse any commit from the range {commits}")
return []


def analysis_new_item_name(root: pathlib, commit: str) -> bool:
"""
For any file name in the repo, it is required to use '_' to replace '-'.
For any directory name in the repo, it is required to use '-' to replace '_'.
"""
GIT_SHOW_CMD = f"git show --oneline --name-status --diff-filter A {commit}"
try:
invalid_items = True
output = subprocess.check_output(
shlex.split(GIT_SHOW_CMD), cwd=root, universal_newlines=True
)
if not output:
return True

NEW_FILE_PATTERN = "^A\s+(\S+)"
for line_no, line in enumerate(output.split("\n")):
# bypass the first line, usually it is the commit description
if line_no == 0:
continue

if not line:
continue

match = re.match(NEW_FILE_PATTERN, line)
if not match:
continue

new_item = match.group(1)
new_item = pathlib.Path(new_item).resolve()

if new_item.is_file():
if not check_file_name(new_item):
invalid_items = False
continue

new_item = new_item.parent

if not check_dir_name(new_item, root):
invalid_items = False
continue
else:
return invalid_items

except subprocess.CalledProcessError:
return False


def process_entire_pr(root: pathlib, commits: str) -> bool:
if not commits:
print("Please provide a commits range")
return False

commit_list = parse_commits_range(root, commits)
if not commit_list:
print(f"Quit since there is no commit to check with")
return True

print(f"there are {len(commit_list)} commits in the PR")

found = False
if not analysis_new_item_name(root, commits):
print(f"{analysis_new_item_name.__doc__}")
found = True

if not run_clang_format_diff(root, commits):
print(f"{run_clang_format_diff.__doc__}")
found = True

return not found


def main() -> int:
parser = argparse.ArgumentParser(
description="Check if change meets all coding guideline requirements"
)
parser.add_argument(
"-c", "--commits", default=None, help="Commit range in the form: a..b"
)
options = parser.parse_args()

wamr_root = pathlib.Path(__file__).parent.joinpath("..").resolve()

if not pre_flight_check(wamr_root):
return False

return process_entire_pr(wamr_root, options.commits)


if __name__ == "__main__":
sys.exit(0 if main() else 1)
Loading

0 comments on commit 225f5d0

Please sign in to comment.