Skip to content

Commit

Permalink
first commit
Browse files Browse the repository at this point in the history
  • Loading branch information
jpihl committed Dec 22, 2023
0 parents commit dcef8e3
Show file tree
Hide file tree
Showing 16 changed files with 780 additions and 0 deletions.
30 changes: 30 additions & 0 deletions .github/workflows/black.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
name: Black
'on':
workflow_dispatch:
inputs:
extra_resolve_options:
description: Extra Resolve Options
required: false
schedule:
- cron: 0 1 * * *
push:
branches:
- master
pull_request:
jobs:
black:
name: Black Format
runs-on:
- self-hosted
- docker
container:
image: kiwicom/black
options: --user 0:0
steps:
- name: Checkout source code
uses: actions/checkout@v2
- name: Run Black Check
run: black . --check
concurrency:
group: ${{ github.workflow }}-${{ github.ref || github.run_id }}
cancel-in-progress: true
36 changes: 36 additions & 0 deletions .github/workflows/flake.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
name: Flake8
'on':
workflow_dispatch:
inputs:
extra_resolve_options:
description: Extra Resolve Options
required: false
schedule:
- cron: 0 1 * * *
push:
branches:
- master
pull_request:
jobs:
flake8:
name: Flake8 Syntax Check
runs-on:
- self-hosted
- docker
container:
image: python:3.9.5
options: --user 0:0
volumes:
- /home/buildbot/.ssh:/root/.ssh
steps:
- name: Install SSH client
run: apt update && apt -y install openssh-client
- name: Checkout source code
uses: actions/checkout@v2
- name: Create flake file
run: echo "[flake8]\n\nignore = F632" >> .flake8
- name: Check python syntax
uses: cclauss/Find-Python-syntax-errors-action@master
concurrency:
group: ${{ github.workflow }}-${{ github.ref || github.run_id }}
cancel-in-progress: true
43 changes: 43 additions & 0 deletions .github/workflows/python-waf.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
name: Waf Python Tests
'on':
workflow_dispatch:
inputs:
extra_resolve_options:
description: Extra Resolve Options
required: false
schedule:
- cron: 0 1 * * *
push:
branches:
- master
pull_request:
jobs:
waf:
strategy:
fail-fast: false
matrix:
os:
- Windows
- Linux
runs-on:
- self-hosted
- docker
- linux
- builder
name: ${{ matrix.os }} Waf
container:
image: ghcr.io/steinwurf/twine:latest
env:
python: python3
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Configure
run: ${{ env.python }} waf configure
- name: Build
run: ${{ env.python }} waf
- name: Test
run: ${{ env.python }} waf --run_tests
concurrency:
group: ${{ github.workflow }}-${{ github.ref || github.run_id }}
cancel-in-progress: true
29 changes: 29 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# waf projects
waf-*
waf3-*
.waf-*
.waf3-*
.lock-*
build
build_current
resolve_symlinks
resolved_dependencies

# Python
*.pyc
.cache
.tox
pytest_environment_*
pytest_*
virtualenv-*
.venv
.test_venv
dist
versjon.egg-info
pip_packages
.pytest_cache
__pycache__
bughawk.egg-info/

# Other
.vscode
9 changes: 9 additions & 0 deletions NEWS.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
News for bughawk
================

This file lists the major changes between versions. For a more detailed list of
every change, see the Git log.

Latest
------
* tbd
60 changes: 60 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
=======
BugHawk
=======

|PyPi| |Waf Python Tests| |Black| |Flake8|


.. |PyPi| image:: https://badge.fury.io/py/bughawk.svg
:target: https://badge.fury.io/py/bughawk

.. |Waf Python Tests| image:: https://github.com/steinwurf/bughawk/actions/workflows/python-waf.yml/badge.svg
:target: https://github.com/steinwurf/bughawk/actions/workflows/python-waf.yml

.. |Flake8| image:: https://github.com/steinwurf/bughawk/actions/workflows/flake.yml/badge.svg
:target: https://github.com/steinwurf/bughawk/actions/workflows/flake.yml

.. |Black| image:: https://github.com/steinwurf/bughawk/actions/workflows/black.yml/badge.svg
:target: https://github.com/steinwurf/bughawk/actions/workflows/black.yml

BugHawk is a tool for finding common bugs and issues in Steinwurf projects.

Installation
------------

Install the ``bughawk`` tool using ``pip``::

python -m pip install bughawk

Alternatively you can also use ``pipx`` for the installation (this works in ubuntu 23.04)::

pipx install bughawk

Usage
-----
You'll now be able to use the `bughawk` command line tool. The following will list
the available sw commands::

bughawk -l

Update
------
To update BugHawk use pip::

python -m pip install bughawk

Please make sure to also extend the config file with required information.

Development
-----------
When developing a new feature for BugHawk it can be nice to install
the local version in editable mode. This can done using the following command::

cd bughawk
python3 -m pip install -e .

To revert this and use the pip package use this command::

cd bughawk
python3 -m pip uninstall bughawk
python -m pip install bughawk
1 change: 1 addition & 0 deletions bughawk/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from ._version import __version__
152 changes: 152 additions & 0 deletions bughawk/__main__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
import argparse
import glob
import os
import fnmatch
import gitignorefile

BOLD = "\033[1m"
RESET = "\033[0m"
GREEN = "\033[92m"
RED = "\033[91m"

def get_all_sources(project_directory):
headers = glob.glob(os.path.join(project_directory, "**/*.hpp"), recursive=True)
sources = glob.glob(os.path.join(project_directory, "**/*.cpp"), recursive=True)

# Make relative to project directory
headers = [os.path.relpath(header, project_directory) for header in headers]
sources = [os.path.relpath(source, project_directory) for source in sources]

gitignore_path = os.path.join(project_directory, ".gitignore")
matches = gitignorefile.parse(gitignore_path)

filtered_headers = [header for header in headers if not matches(header)]
filtered_sources = [source for source in sources if not matches(source)]

return filtered_headers, filtered_sources

# Function to check headers for self-inclusion
def check_self_inclusion(project_directory):
result = {}
headers, _ = get_all_sources(project_directory)
if not headers:
print("No header files found")
return return_code

for header in headers:
header = os.path.relpath(header, project_directory)
with open(header, "r") as f:
result[header] = False
lines = f.readlines()
for line in lines:
if line.startswith("#include"):
# determine include type
if line.startswith("#include <"):
# system include
continue
elif line.startswith("#include \""):
# local include
include = line[10:-2]

# If the include is relative check if it ends up in the same file
old_cwd = os.getcwd()
os.chdir(os.path.dirname(header))
include = os.path.relpath(os.path.abspath(include))
os.chdir(old_cwd)

if include == os.path.basename(header):
result[header] = True
break
else:
raise Exception(f"Unknown include type in {header}")

return result

# Function to check project for unused headers
def check_unused_headers(project_directory):
results = {}
headers, _ = get_all_sources(project_directory)

return results


# Function to check if version.hpp is included in all source files
def check_version_hpp_inclusion(project_directory):
print("📝 Checking if version.hpp is included in all source files...")

# Function to check that all headers have pragma once and that the pragma once is
# before any other includes
def check_pragma_once(project_directory):
print("📝 Checking that all headers have pragma once and that the pragma once is before any other includes...")

# Function to check that all headers and sources have a license header
def check_license_header(project_directory):
print("📝 Checking that all headers and sources have a license header...")


def main():
parser = argparse.ArgumentParser(description="Bughawk - Code Quality Tool")
parser.add_argument(
"--project_directory", "-p",
default=os.getcwd(),
help="Path to the project directory to be analyzed (default: current directory)"
)
subparsers = parser.add_subparsers(title="Available Tools", dest="tool")

# Tool to check headers for self include
parser_self_include = subparsers.add_parser(
"self_include",
help="Check headers for self-inclusion"
)

# Tool to check project for unused headers
parser_unused_headers = subparsers.add_parser(
"unused_headers",
help="Check project for unused headers"
)

# Tool to check if version.hpp is included in all source files
parser_version_hpp = subparsers.add_parser(
"version_hpp",
help="Check if version.hpp is included in all source files"
)

parser_pragma_once = subparsers.add_parser(
"pragma_once",
help="Check that all headers have pragma once and that the pragma once is before any other includes"
)

parser_license_header = subparsers.add_parser(
"license_header",
help="Check that all headers and sources have a license header"
)

args = parser.parse_args()

if args.tool == "self_include":
print("Checking for self-inclusion in headers... 🔍")
results = check_self_inclusion(args.project_directory)

# Sort result so that errors are printed last
results = {k: v for k, v in sorted(results.items(), key=lambda item: item[1])}

max_header_length = max(len(header) for header in results.keys())

for header, self_included in results.items():
status = "✅ No self-inclusion detected." if not self_included else "❌ Self-inclusion detected!"
color = GREEN if not self_included else RED
padding = " " * (max_header_length - len(header))
print(f"{BOLD}{header}:{padding}{RESET} {color}{BOLD}{status}{RESET}")

return any([self_included for self_included in results.values()])

elif args.tool == "unused_headers":
print("Checking for unused headers in the project... 🧹")
check_unused_headers(args.project_directory)
elif args.tool == "version_hpp":
check_version_hpp_inclusion(args.project_directory)
else:
parser.print_help()

if __name__ == "__main__":
main()
1 change: 1 addition & 0 deletions bughawk/_version.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
__version__ = "0.0.0"
3 changes: 3 additions & 0 deletions pytest.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[pytest]
filterwarnings =
ignore::DeprecationWarning:invoke.loader:
Loading

0 comments on commit dcef8e3

Please sign in to comment.