Skip to content
Open
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
39 changes: 39 additions & 0 deletions .clang-tidy
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
---
# AMS clang-tidy configuration
#
# Run manually: clang-tidy -p build/ src/AMSlib/wf/workflow.hpp
# Integrated via the pre-commit git hook.

Checks: >
-*,
bugprone-*,
-bugprone-easily-swappable-parameters,
-bugprone-narrowing-conversions,
misc-*,
-misc-non-private-member-variables-in-classes,
-misc-no-recursion,
modernize-*,
-modernize-use-trailing-return-type,
-modernize-avoid-c-arrays,
-modernize-use-nodiscard,
performance-*,
readability-identifier-naming

WarningsAsErrors: ''

HeaderFilterRegex: 'src/AMSlib/.*'

CheckOptions:
- key: readability-identifier-naming.ClassCase
value: CamelCase
- key: readability-identifier-naming.PrivateMemberPrefix
value: '_'
- key: readability-identifier-naming.FunctionCase
value: camelBack
- key: readability-identifier-naming.NamespaceCase
value: lower_case
- key: modernize-use-override.AllowOverrideAndFinal
value: false
- key: performance-unnecessary-value-param.AllowedTypes
value: 'std::shared_ptr;std::unique_ptr'

197 changes: 197 additions & 0 deletions .githooks/pre-commit
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@
#!/usr/bin/env bash
#
# Git pre-commit hook for AMS:
# Phase 1: clang-format on staged C/C++ files (auto-fixes, re-stages)
# Phase 2: ruff on staged Python files (auto-fixes, re-stages)
# Phase 3: clang-tidy on staged .cpp files (reports errors, blocks commit)
#
# Install (automatic via cmake/setup-git-hooks.cmake):
# .githooks/pre-commit
#
# Skip temporarily:
# git commit --no-verify
#
# Skip only clang-tidy (format + ruff still run):
# AMS_SKIP_TIDY=1 git commit
#
# Specify build directory (for compile_commands.json):
# export AMS_BUILD_DIR=/path/to/build
#
# Requires: clang-format, clang-tidy, ruff on PATH
# clang-tidy requires compile_commands.json (generated by cmake)

set -euo pipefail

find_tool() {
local base="$1"
for candidate in "${base}-18" "${base}-17" "${base}-16" "${base}-15" "${base}-14" "${base}"; do
if command -v "$candidate" &>/dev/null; then
echo "$candidate"
return
fi
done
}

CLANG_FORMAT=$(find_tool clang-format)
CLANG_TIDY=$(find_tool clang-tidy)
RUFF=$(command -v ruff 2>/dev/null || true)

CPP_EXTENSIONS='\.(cpp|hpp|cc|hh|h|c|cxx|hxx)$'
TIDY_EXTENSIONS='\.(cpp|cc|c|cxx)$'
PY_EXTENSIONS='\.py$'

REPO_ROOT=$(git rev-parse --show-toplevel)

ALL_STAGED=$(git diff --cached --name-only --diff-filter=ACM || true)

CPP_FILES=$(echo "$ALL_STAGED" | grep -E "$CPP_EXTENSIONS" | sed "s|^|${REPO_ROOT}/|" || true)
PY_FILES=$(echo "$ALL_STAGED" | grep -E "$PY_EXTENSIONS" | sed "s|^|${REPO_ROOT}/|" || true)

if [[ -z "$CPP_FILES" && -z "$PY_FILES" ]]; then
exit 0
fi

if [[ -n "$CPP_FILES" ]]; then
if [[ -n "$CLANG_FORMAT" ]]; then
REFORMATTED=0

for file in $CPP_FILES; do
"$CLANG_FORMAT" -i -style=file "$file"

if ! git diff --quiet -- "$file"; then
echo " clang-format: reformatted $file"
git add "$file"
REFORMATTED=1
fi
done

if [[ "$REFORMATTED" -eq 1 ]]; then
echo ""
echo "clang-format: reformatted files have been re-staged."
echo ""
fi
else
echo "WARNING: clang-format not found, skipping C/C++ format check."
fi
fi

if [[ -n "$PY_FILES" ]]; then
if [[ -n "$RUFF" ]]; then
PY_REFORMATTED=0

# Format (equivalent to black)
for file in $PY_FILES; do
"$RUFF" format --quiet "$file"
done

# Lint with auto-fix (import sorting, safe fixes)
for file in $PY_FILES; do
"$RUFF" check --fix --quiet "$file" 2>/dev/null || true
done

# Re-stage any modified files
for file in $PY_FILES; do
if ! git diff --quiet -- "$file"; then
echo " ruff: reformatted $file"
git add "$file"
PY_REFORMATTED=1
fi
done

if [[ "$PY_REFORMATTED" -eq 1 ]]; then
echo ""
echo "ruff: reformatted files have been re-staged."
echo ""
fi

# Lint check (report remaining issues — unfixable ones)
RUFF_ERRORS=0
for file in $PY_FILES; do
if ! "$RUFF" check --quiet "$file" 2>/dev/null; then
RUFF_ERRORS=1
fi
done

if [[ "$RUFF_ERRORS" -eq 1 ]]; then
echo ""
echo "ruff: lint errors found (see above). Fix them before committing."
echo "Or skip all hooks with: git commit --no-verify"
exit 1
fi
else
echo "WARNING: ruff not found, skipping Python format/lint check."
echo " Install with: pip install ruff"
fi
fi

if [[ -z "$CPP_FILES" ]]; then
exit 0
fi

# Allow skipping clang-tidy via environment variable
if [[ "${AMS_SKIP_TIDY:-0}" == "1" ]]; then
exit 0
fi

if [[ -z "$CLANG_TIDY" ]]; then
echo "WARNING: clang-tidy not found, skipping C++ lint check."
exit 0
fi

# Find compile_commands.json
COMPILE_DB=""

if [[ -n "${AMS_BUILD_DIR:-}" && -f "${AMS_BUILD_DIR}/compile_commands.json" ]]; then
COMPILE_DB="${AMS_BUILD_DIR}/compile_commands.json"
else
for dir in build build-* cmake-build-* out; do
candidate=$(find "${REPO_ROOT}" -maxdepth 2 -path "${REPO_ROOT}/${dir}/compile_commands.json" -print -quit 2>/dev/null)
if [[ -n "$candidate" ]]; then
COMPILE_DB="$candidate"
break
fi
done
fi

if [[ -z "$COMPILE_DB" ]]; then
echo "WARNING: compile_commands.json not found, skipping clang-tidy."
echo " Run cmake first, or set AMS_BUILD_DIR to your build directory."
exit 0
fi

COMPILE_DB_DIR=$(dirname "$COMPILE_DB")

# Filter to translation units only
TIDY_FILES=$(echo "$CPP_FILES" | grep -E "$TIDY_EXTENSIONS" || true)

if [[ -z "$TIDY_FILES" ]]; then
exit 0
fi

TIDY_ERRORS=0

for file in $TIDY_FILES; do
# Only run on files that appear in compile_commands.json
if ! grep -q "\"file\".*$(basename "$file")\"" "$COMPILE_DB" 2>/dev/null; then
continue
fi

echo " clang-tidy: checking $file"

# clang-tidy exits non-zero when WarningsAsErrors checks fire.
# Warnings (not promoted to errors) print but don't affect the exit code.
if ! "$CLANG_TIDY" -p "$COMPILE_DB_DIR" --quiet "$file"; then
TIDY_ERRORS=1
fi
done

if [[ "$TIDY_ERRORS" -eq 1 ]]; then
echo ""
echo "clang-tidy: errors found (see above)."
echo "Warnings are informational and do not block the commit."
echo "Fix errors, or skip with: AMS_SKIP_TIDY=1 git commit"
echo "Or skip all hooks with: git commit --no-verify"
exit 1
fi

exit 0
12 changes: 6 additions & 6 deletions .gitlab/custom-jobs-and-variables.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,25 +14,25 @@ variables:

# Dane
# Arguments for top level allocation
DANE_SHARED_ALLOC: "--exclusive --reservation=ci --time=45 --nodes=1"
DANE_SHARED_ALLOC: "--account=lpbf --exclusive --reservation=ci --time=45 --nodes=1"
# Arguments for job level allocation
DANE_JOB_ALLOC: "--mpi=none --reservation=ci --nodes=1"
DANE_JOB_ALLOC: "--account=lpbf --mpi=none --reservation=ci --nodes=1"
# Add variables that should apply to all the jobs on a machine:
# DANE_MY_VAR: "..."

# Tioga
# Arguments for top level allocation
# OPTIONAL: "-o per-resource.count=2" allows to get 2 jobs running on each node.
TIOGA_SHARED_ALLOC: "--queue=pci --exclusive --time-limit=1h --nodes=1 -o per-resource.count=4"
TIOGA_SHARED_ALLOC: "--bank=asccasc --queue=pci --exclusive --time-limit=1h --nodes=1 -o per-resource.count=4"
# Arguments for job level allocation, we need to match the time limit. Otherwise it immediately exits
TIOGA_JOB_ALLOC: "--nodes=1 --begin-time=+5s --time-limit=1h"
TIOGA_JOB_ALLOC: "--bank=asccasc --nodes=1 --begin-time=+5s --time-limit=1h"
# Add variables that should apply to all the jobs on a machine:
# TIOGA_MY_VAR: "..."

# Tuo
TUOLUMNE_SHARED_ALLOC: "--queue=pci --exclusive --time-limit=1h --nodes=1 -o per-resource.count=4"
TUOLUMNE_SHARED_ALLOC: "--bank=lpbf --queue=pci --exclusive --time-limit=1h --nodes=1 -o per-resource.count=4"
# Arguments for job level allocation
TUOLUMNE_JOB_ALLOC: "--nodes=1 --begin-time=+5s --time-limit=1h"
TUOLUMNE_JOB_ALLOC: "--bank=lpbf --nodes=1 --begin-time=+5s --time-limit=1h"

# Configuration shared by build and test jobs specific to this project.
# Not all configuration can be shared. Here projects can fine tune the
Expand Down
2 changes: 2 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ cmake_policy(SET CMP0074 NEW)

project(AMS VERSION 0.1.1 LANGUAGES CXX C)

# We insall AMS githook to run clang-format etc when commiting
include(cmake/setup-git-hooks.cmake)

# NOTE: This may break some of our integrations with the applications. But flux requires > C++20, RMQ requires C++17 and although AMS does not have
# any restrictions on the CXX standard the application may impose those. The solution would be to compile RMQ with an older GCC version (8) and
Expand Down
23 changes: 23 additions & 0 deletions cmake/setup-git-hooks.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Automatically configure git hooks when the project is configured.
# Place in cmake/setup-git-hooks.cmake and include from the top-level CMakeLists.txt.

find_package(Git QUIET)

if(GIT_FOUND AND EXISTS "${PROJECT_SOURCE_DIR}/.git")
# Only set if not already configured (respects developer overrides)
execute_process(
COMMAND ${GIT_EXECUTABLE} config --get core.hooksPath
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
OUTPUT_VARIABLE _current_hooks_path
OUTPUT_STRIP_TRAILING_WHITESPACE
RESULT_VARIABLE _rc
)

if(NOT _rc EQUAL 0 OR NOT _current_hooks_path STREQUAL ".githooks")
message(STATUS "Setting git hooks path to .githooks/")
execute_process(
COMMAND ${GIT_EXECUTABLE} config core.hooksPath .githooks
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
)
endif()
endif()
60 changes: 13 additions & 47 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ classifiers = [
"Programming Language :: Python :: 3 :: Only",
]
dependencies = [
"ruff",
"h5py",
"argparse",
"SQLAlchemy",
Expand All @@ -56,62 +57,27 @@ AMSDeploy = "ams_wf.AMSDeploy:main"
package-dir = {"" = "src/AMSWorkflow"}
packages = ["ams_wf", "ams"]

# Black formatting
[tool.black]
line-length = 120
include = '\.pyi?$'
exclude = '''
/(
.eggs # exclude a few common directories in the
| .git # root of the project
| .hg
| .mypy_cache
| .tox
| venv
| _build
| buck-out
| build
| dist
)/
'''

# iSort
[tool.isort]
profile = "black"
line_length = 120
multi_line_output = 3
include_trailing_comma = true
virtual_env = "venv"

# flake8
[tool.flake8]
ignore = ["E501", "W503", "E226", "BLK100", "E203"]
max-line-length = 120
exclude = [
# No need to traverse our git directory
".git",
# There's no value in checking cache directories
"__pycache__",
"*.egg-info",
"build"
]
# E501: Line too long
# W503: Line break occurred before binary operator
# E226: Missing white space around arithmetic operator

[tool.ruff]
lint.ignore = ["E501", "E226", "E203"]
line-length = 120
show-fixes = true
exclude = [
".git",
"__pycache__",
"*.egg-info",
"build"
]
# change the default line length number or characters.
line-length = 120
lint.select = ['E', 'F', 'W', 'A', 'PLC', 'PLE', 'PLW', 'I', 'N', 'Q']

[tool.yapf]
ignore = ["E501", "W503", "E226", "BLK100", "E203"]
column_limit = 120
[tool.ruff.lint]
select = ['E', 'F', 'W', 'A', 'PLC', 'PLE', 'PLW', 'I', 'N', 'Q', 'B', 'UP', 'SIM', 'RUF']
ignore = ["E501", "E226", "E203"]

[tool.ruff.lint.isort]
known-first-party = ["ams", "ams_wf"]

[tool.ruff.format]
quote-style = "double"
indent-style = "space"

Loading