Skip to content

Commit 2149ace

Browse files
committed
Add github actions clang-format check
Signed-off-by: mdouglas47 <mdouglas47@bloomberg.net>
1 parent e5c131f commit 2149ace

File tree

3 files changed

+244
-1
lines changed

3 files changed

+244
-1
lines changed

.github/workflows/build.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ on:
1717
# is built to avoid wasting runner minutes. Treat "main" as an exception,
1818
# probably best to test all commits to it.
1919
concurrency:
20-
group: ${{ github.ref }}
20+
group: ${{ github.ref }}-build-check
2121
cancel-in-progress: ${{ github.ref != 'refs/heads/main' }}
2222

2323
# We can have multiple jobs. Jobs run in parallel unless they explicitly declare

.github/workflows/clang-format.yml

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
name: clang-format (check)
2+
3+
on:
4+
pull_request: { branches: ["main"] }
5+
6+
# If someone pushes a new commit to a branch, ensure that only the latest commit
7+
# is built to avoid wasting runner minutes.
8+
concurrency:
9+
group: ${{ github.ref }}-format-check
10+
cancel-in-progress: true
11+
12+
jobs:
13+
clang-format:
14+
runs-on: ubuntu-latest
15+
continue-on-error: true
16+
env:
17+
CLANG_FORMAT: "clang-format"
18+
defaults:
19+
run:
20+
# Make sure to fail any "run" step early on so that steps don't silently
21+
# pass even when errors may have occurred.
22+
shell: bash -o errexit -o nounset -o pipefail {0}
23+
steps:
24+
- name: Checkout code
25+
uses: actions/checkout@v4
26+
with: { fetch-depth: 0 }
27+
28+
- name: Install clang-format
29+
run: |
30+
sudo apt-get update
31+
sudo apt-get install -y clang-format
32+
$CLANG_FORMAT --version
33+
34+
- name: Run format check
35+
run: |
36+
base_repo="${{ github.event.pull_request.base.repo.full_name || github.repository }}"
37+
base_url="https://github.com/${base_repo}.git"
38+
39+
# Fetch the base branch into a unique namespace
40+
git fetch --no-tags --prune --depth=1 "${base_url}" \
41+
"refs/heads/${GITHUB_BASE_REF}:refs/remotes/__base__/${GITHUB_BASE_REF}"
42+
43+
# Compute merge-base vs the fetched ref
44+
base_commit="$(git merge-base HEAD "refs/remotes/__base__/${GITHUB_BASE_REF}")"
45+
46+
./ci/scripts/format --verbose --dry-run --compare-base "${base_commit}"

ci/scripts/format

Lines changed: 197 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,197 @@
1+
#!/usr/bin/env bash
2+
3+
readonly clang_style="{BasedOnStyle: llvm, \
4+
IndentWidth: 4, \
5+
UseTab: Never, \
6+
BreakBeforeBraces: Linux, \
7+
SortIncludes: false, \
8+
IndentCaseLabels: false, \
9+
AlwaysBreakTemplateDeclarations: true, \
10+
AllowShortFunctionsOnASingleLine: false, \
11+
AllowShortCaseLabelsOnASingleLine: true, \
12+
AllowShortIfStatementsOnASingleLine: true}"
13+
14+
readonly dirs_to_style_check="util \
15+
bbinc \
16+
bdb \
17+
cdb2api \
18+
comdb2rle \
19+
csc2 \
20+
db \
21+
net \
22+
plugins \
23+
schemachange \
24+
sockpool \
25+
tools/pmux \
26+
tools/cdb2sql \
27+
tools/cdb2sockpool \
28+
plugins/logdelete \
29+
plugins/newsql \
30+
plugins/repopnewlrl"
31+
32+
readonly extensions_to_style_check="c cpp h"
33+
34+
set -euo pipefail
35+
36+
if ! which clang-format-diff &> /dev/null; then
37+
echo "clang-format-diff not found. Please install clang-format."
38+
exit 1
39+
fi
40+
41+
compare_base=$(git rev-parse --short HEAD^)
42+
dry_run=0
43+
only_staged=0
44+
verbose=0
45+
46+
print_help() {
47+
echo "Usage: $0 [options]"
48+
echo ""
49+
echo "Options:"
50+
echo " -d, --dry-run Perform a dry run (default: false)"
51+
echo " -b, --compare-base <ref> Git reference to compare against (default: HEAD^)"
52+
echo " -s, --only-staged Only format staged files"
53+
echo " -v, --verbose Enable verbose output"
54+
echo " -h, --help Show this help message"
55+
}
56+
57+
while [[ $# -gt 0 ]]; do
58+
case "$1" in
59+
-d|--dry-run)
60+
dry_run=1
61+
shift ;;
62+
-b|--compare-base)
63+
compare_base="$2"
64+
shift 2 ;;
65+
-s|--only-staged)
66+
only_staged=1
67+
shift ;;
68+
-v|--verbose)
69+
verbose=1
70+
shift ;;
71+
-h|--help)
72+
print_help
73+
exit 0 ;;
74+
*)
75+
echo "Unknown option: $1" >&2
76+
print_help
77+
exit 1 ;;
78+
esac
79+
done
80+
81+
# print errors in red
82+
print_error() {
83+
local message="$1"
84+
echo -e "\033[31mError: ${message}\033[0m"
85+
}
86+
87+
# print info in blue
88+
print_info() {
89+
local message="$1"
90+
echo -e "\033[34m${message}\033[0m"
91+
}
92+
93+
# print success messages in green
94+
print_success() {
95+
local message="$1"
96+
echo -e "\033[32m${message}\033[0m"
97+
}
98+
99+
print_summary() {
100+
local -r files_to_format=$1
101+
102+
print_info "========================================"
103+
print_info "Clang-Format Check"
104+
print_info "========================================"
105+
106+
if [ -z "${files_to_format}" ]; then
107+
print_info "No changed files to format check."
108+
else
109+
print_info "Files to format check:"
110+
for file in ${files_to_format}; do
111+
print_info " $file"
112+
done
113+
print_info "Compare base: ${compare_base}"
114+
if (( dry_run )); then
115+
print_info "Dry run mode enabled. No files will be modified."
116+
else
117+
print_info "Files will be modified in place."
118+
fi
119+
fi
120+
121+
print_info "========================================\n"
122+
}
123+
124+
get_files_matching_extensions() {
125+
local name_args=""
126+
for ext in ${extensions_to_style_check}; do
127+
if [ -z "$name_args" ]; then
128+
name_args="-name '*.${ext}'"
129+
else
130+
name_args="${name_args} -o -name '*.${ext}'"
131+
fi
132+
done
133+
134+
eval "find ${dirs_to_style_check} \\( ${name_args} \\)"
135+
}
136+
137+
get_files_to_format() {
138+
local files_matching_extensions
139+
files_matching_extensions=$(get_files_matching_extensions)
140+
141+
# Filter out files that haven't changed
142+
143+
local diff_flags=(--name-only -U0)
144+
if (( only_staged )); then
145+
diff_flags+=(--cached)
146+
fi
147+
148+
echo ${files_matching_extensions} \
149+
| xargs git diff "${diff_flags[@]}" ${compare_base} \
150+
| grep -v "^\-" || true
151+
}
152+
153+
clang_format_diff_repo() {
154+
local rc=0
155+
156+
local files_to_format
157+
if ! files_to_format=$(get_files_to_format); then
158+
print_error "Error determining files to format check."
159+
return 1
160+
fi
161+
162+
local diff_flags=(-U0 --no-color)
163+
if (( only_staged == 1)); then
164+
diff_flags+=(--cached)
165+
fi
166+
167+
local clang_format_diff_flags=(-style "${clang_style}" -p1)
168+
if (( dry_run != 1 )); then
169+
clang_format_diff_flags+=(-i)
170+
fi
171+
172+
if (( verbose == 1)); then
173+
clang_format_diff_flags+=(-v)
174+
print_summary "${files_to_format}"
175+
fi
176+
177+
local rc=0
178+
for file in ${files_to_format}; do
179+
if (( verbose == 1 )); then
180+
print_info "Processing file: ${file}"
181+
fi
182+
if ! git diff "${diff_flags[@]}" "${compare_base}" -- "${file}" \
183+
| grep -v "^\-" \
184+
| clang-format-diff "${clang_format_diff_flags[@]}"; then
185+
rc=1
186+
print_error "${file} has incorrect formatting."
187+
fi
188+
done
189+
190+
if (( verbose == 1 && rc == 0 )); then
191+
print_success "All files are correctly formatted."
192+
fi
193+
194+
return ${rc}
195+
}
196+
197+
clang_format_diff_repo

0 commit comments

Comments
 (0)