Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 42 additions & 23 deletions .github/workflows/platformio.yml
Original file line number Diff line number Diff line change
@@ -1,21 +1,27 @@
name: PlatformIO Library CI
name: PlatformIO CI

on:
push:
branches:
- '**'
pull_request:
branches:
- master
- dev
types:
- closed

jobs:
test:
strategy:
matrix:
example: [examples/ring-buffer-basic.c]

name: Unit Tests
runs-on: ubuntu-latest
runs-on:
group: ci-runners
labels: Linux

steps:
- name: Checkout Repository
- name: Checkout
uses: actions/checkout@v4

- name: Cache PlatformIO + pip
Expand All @@ -29,59 +35,72 @@ jobs:
- name: Setup Python
uses: actions/setup-python@v5
with:
python-version: '3.11'
python-version: "3.11"

- name: Install PlatformIO Core
run: pip install --upgrade platformio

- name: Run Unit Tests
- name: Run Unit Tests (or generate minimal .ini)
run: |
if [ ! -f "platformio.ini" ]; then
printf "[env:native]\nplatform = native\nlib_deps = Unity, ./\n" > platformio.ini
printf "[env:latest_stable]\n" > platformio.ini
printf "platform = native\n" >> platformio.ini
printf "lib_deps = Unity, ./\n" >> platformio.ini
echo "Created platformio.ini"
fi
pio test -e native
pio test -e latest_stable

- name: Compile Examples
- name: Run Example
run: |
for example in examples/*/; do
if [ -d "$example" ]; then
echo "Building $example"
pio ci "$example" --board native
fi
done
pio ci -l . --exclude=src/test --exclude=src/examples -O "framework=stm32cube" ${{ matrix.example }} -b nucleo_h723zg

check:
name: Static Code Analysis
runs-on: ubuntu-latest
# Run only when:
# - push to branch dev (e.g. merge commit pushed to dev), OR
# - pull_request event with action closed and merged == true targeting dev
if: >
(github.event_name == 'push' && (github.ref == 'refs/heads/dev' || github.ref == 'refs/heads/master')) ||
(github.event_name == 'pull_request' && github.event.pull_request.merged == true)
(github.event_name == 'push' && github.ref == 'refs/heads/dev') ||
(github.event_name == 'pull_request' && github.action == 'closed' && github.event.pull_request.merged == true && github.event.pull_request.base.ref == 'dev')

steps:
- name: Checkout Repository
- name: Checkout
uses: actions/checkout@v4

- name: Cache PlatformIO + pip
uses: actions/cache@v4
with:
path: |
~/.cache/pip
~/.platformio
~/.platformio/.cache
key: ${{ runner.os }}-pio-${{ hashFiles('**/platformio.ini') }}

- name: Setup Python
uses: actions/setup-python@v5
with:
python-version: '3.11'
python-version: "3.11"

- name: Install PlatformIO Core
run: pip install --upgrade platformio

- name: Run Static Code Analysis
run: |
if [ ! -f "platformio.ini" ]; then
printf "[env:native]\nplatform = native\nlib_deps = Unity, ./\n" > platformio.ini
printf "[env:latest_stable]\n" > platformio.ini
printf "platform = native\n" >> platformio.ini
printf "lib_deps = Unity, ./\n" >> platformio.ini
printf "check_tool = clangtidy\n" >> platformio.ini
printf "check_flags =\n" >> platformio.ini
printf " clangtidy: --checks=-*,cert-*,clang-analyzer-*,bugprone-*,performance-*,readability-*,-bugprone-easily-swappable-parameters\n" >> platformio.ini
printf " clangtidy: --warnings-as-errors=clang-analyzer-*,bugprone-*\n" >> platformio.ini
printf " clangtidy: --header-filter=^(?:.*/)?(?:include|src)/.*\.[ch]$\n" >> platformio.ini
printf " clangtidy: --system-headers=false\n" >> platformio.ini
printf "check_src_filters =\n" >> platformio.ini
printf " -<**>\n" >> platformio.ini
printf " +<src/**>\n" >> platformio.ini
printf " +<include/**>\n" >> platformio.ini
echo "Created platformio.ini"
fi
pio check
pio check

1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -420,5 +420,6 @@ STM32Make.make
.local_gitignore

docs/
platformio.ini

## ---------------- END MANUALLY ADDED ---------------- ##
6 changes: 3 additions & 3 deletions Doxyfile
Original file line number Diff line number Diff line change
Expand Up @@ -42,13 +42,13 @@ DOXYFILE_ENCODING = UTF-8
# title of most generated pages and in a few other places.
# The default value is: My Project.

PROJECT_NAME = "LIBRING-BUFFER-SW"
PROJECT_NAME = "RingBuffer"

# The PROJECT_NUMBER tag can be used to enter a project or revision number. This
# could be handy for archiving the generated documentation or if some version
# control system is used.

PROJECT_NUMBER = "v0.2.0"
PROJECT_NUMBER = "v0.2.1"

# Using the PROJECT_BRIEF tag one can provide an optional one line description
# for a project that appears at the top of each page and should give viewers a
Expand Down Expand Up @@ -2897,4 +2897,4 @@ MSCGEN_TOOL =
# contain msc files that are included in the documentation (see the \mscfile
# command).

MSCFILE_DIRS =
MSCFILE_DIRS =
2 changes: 1 addition & 1 deletion hooks/commit-msg
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,4 @@ if [[ ! $COMMIT_MSG =~ $REGEX ]]; then
fi

echo "Commit message is valid."
exit 0
exit 0
51 changes: 34 additions & 17 deletions hooks/pre-commit
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#!/usr/bin/env python3
import subprocess
import logging
from pathlib import Path

# --- CONFIGURATION ---
Expand All @@ -12,6 +13,7 @@ EXTENSIONS = {".c", ".cpp", ".cc", ".h", ".hpp", ".hh"}
# Path to clang-format executable (can just be "clang-format" if it's in PATH)
CLANG_FORMAT = "clang-format"

LOGGER = logging.getLogger(__name__)

def find_git_root() -> Path:
try:
Expand All @@ -26,27 +28,31 @@ def find_git_root() -> Path:
if path_str:
path = Path(path_str)
if path.is_dir():
LOGGER.info(f"Git root path: {path}")
return path
print("[err] Received an invalid path.")
LOGGER.error("[find_git_root] Received an invalid path.")
except subprocess.CalledProcessError as e:
print(f"[err] {e.stderr.strip()}")
LOGGER.error(f"[find_git_root] {e.stderr.strip()}")
except Exception as e:
print(f"[err] {e}")
LOGGER.error(f"[find_git_root] {e}")
return None


def format_files(files: list[Path]) -> list[Path]:
formatted = []
if len(files)>0:
LOGGER.info("Formatting...")
for file in files:
if file.is_file():
LOGGER.info(f"Formatting: {file}")
try:
subprocess.run([CLANG_FORMAT, "-i", str(file)], check=True)
formatted.append(file)
except FileNotFoundError:
print(f"[err] clang-format executable not found: '{CLANG_FORMAT}'")
LOGGER.error(f"[format_files] clang-format executable not found: '{CLANG_FORMAT}'")
return None
except subprocess.CalledProcessError:
print(f"[err] Failed to format: {file}")
LOGGER.error(f"[format_files] Failed to format: {file}")
# keep going, but do not mark success
return formatted

Expand All @@ -59,24 +65,28 @@ def get_staged_files() -> list[Path]:
text=True,
check=True
)
return [Path(line.strip()) for line in result.stdout.splitlines() if Path(line.strip()).exists()]
files = [Path(line.strip()) for line in result.stdout.splitlines() if Path(line.strip()).exists()]
LOGGER.info(f"Staged files found: {bool(files)}")
LOGGER.debug(f"Staged files: {files}")
return files
except subprocess.CalledProcessError as e:
print(f"[err] {e.stderr.strip()}")
LOGGER.error(f"[get_staged_files] {e.stderr.strip()}")
except Exception as e:
print(f"[err] {e}")
LOGGER.error(f"[get_staged_files] {e}")
return []


def stage_files(files: list[Path]) -> bool:
if not files:
return True
try:
LOGGER.info("Staging files")
subprocess.run(["git", "add"] + [str(f) for f in files], check=True)
return True
except subprocess.CalledProcessError as e:
print(f"[err] {e.stderr.strip()}")
LOGGER.error(f"[stage_files] {e.stderr.strip()}")
except Exception as e:
print(f"[err] {e}")
LOGGER.error(f"[stage_files] {e}")
return False

def detect_partial_staging(file: Path) -> bool:
Expand All @@ -87,7 +97,10 @@ def detect_partial_staging(file: Path) -> bool:
stdout=subprocess.PIPE,
text=True
)
return bool(result.stdout.strip())
res = result.stdout.strip()
LOGGER.info(f"Detect partial staging result: {bool(res)}")
LOGGER.debug(f"Partial staging result string: {res}")
return bool(res)

def is_file_in_folders(file_path: Path, root: Path, folder_names) -> bool:
file_path = file_path.resolve()
Expand All @@ -97,11 +110,13 @@ def is_file_in_folders(file_path: Path, root: Path, folder_names) -> bool:
return True
return False

if __name__ == "__main__":

def main():
logging.basicConfig(filename='formatter.log', level=logging.DEBUG, format='%(asctime)s - %(levelname)s - %(message)s',)
LOGGER.info("Start")
root = find_git_root()
if not root:
print("[err] Not in a Git repository or an error occurred.")
LOGGER.error("Not in a Git repository or an error occurred.")
exit(1)

staged = get_staged_files()
Expand All @@ -110,17 +125,19 @@ if __name__ == "__main__":
f for f in staged
if f.suffix in EXTENSIONS and is_file_in_folders(f, root, TARGET_DIRS)
]

for f in staged_targeted:
if detect_partial_staging(f):
print(f"[warn] Partially staged file detected: {f}, script will commit the whole file")
LOGGER.warning(f"Partially staged file detected: {f}, script will commit the whole file")

formatted = format_files(staged_targeted)

if formatted is None:
print("[err] error occurred when formatting files")
LOGGER.error("error occurred when formatting files")
exit(1)

if not stage_files(formatted):
print("[err] Failed to stage formatted files")
LOGGER.error("Failed to stage formatted files")
exit(1)

if __name__ == "__main__":
main()
Loading
Loading