Skip to content

Commit f282eec

Browse files
authored
chore: Improve runformat script and integrate CI checks (danmar#520)
1 parent cfd1797 commit f282eec

File tree

3 files changed

+232
-32
lines changed

3 files changed

+232
-32
lines changed

.github/workflows/format.yml

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
# Syntax reference https://help.github.com/en/actions/reference/workflow-syntax-for-github-actions
2+
# Environment reference https://help.github.com/en/actions/reference/virtual-environments-for-github-hosted-runners
3+
name: format
4+
5+
on:
6+
push:
7+
branches:
8+
- 'master'
9+
- 'releases/**'
10+
- '1.*'
11+
tags:
12+
- '1.*'
13+
pull_request:
14+
15+
permissions:
16+
contents: read
17+
18+
jobs:
19+
format:
20+
21+
runs-on: ubuntu-22.04
22+
23+
defaults:
24+
run:
25+
shell: bash -euo pipefail {0}
26+
27+
env:
28+
UNCRUSTIFY_INSTALL_DIR: ${{ github.workspace }}/runformat-uncrustify
29+
30+
steps:
31+
- uses: actions/checkout@v5
32+
with:
33+
persist-credentials: false
34+
35+
- name: Determine uncrustify version
36+
id: get-uncrustify-version
37+
run: |
38+
version="$(./runformat --expected-uncrustify-version)"
39+
echo "Expected uncrustify version: $version"
40+
echo "version=$version" >> "$GITHUB_OUTPUT"
41+
42+
- name: Set UNCRUSTIFY_VERSION env variable
43+
run: |
44+
version=$(./runformat --expected-uncrustify-version)
45+
echo "version [$version]"
46+
echo "UNCRUSTIFY_VERSION=${version}" >> "$GITHUB_ENV"
47+
48+
- name: Cache uncrustify
49+
uses: actions/cache@v4
50+
id: cache-uncrustify
51+
with:
52+
path: ${{ env.UNCRUSTIFY_INSTALL_DIR }}
53+
key: ${{ runner.os }}-uncrustify-${{ steps.get-uncrustify-version.outputs.version }}
54+
55+
- name: Install uncrustify
56+
if: steps.cache-uncrustify.outputs.cache-hit != 'true'
57+
run: |
58+
./runformat --install --install-dir "${UNCRUSTIFY_INSTALL_DIR}"
59+
60+
- name: Uncrustify check
61+
run: |
62+
./runformat

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
*.app
2929
simplecpp
3030
testrunner
31+
/.runformat-uncrustify
3132

3233
# CLion
3334
/.idea

runformat

Lines changed: 169 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,176 @@
11
#!/bin/bash
22
#
3-
# uncrustify-0.72 is used to format simplecpp and cppcheck source code.
3+
# runformat - format this project's C++ sources with Uncrustify.
44
#
5-
# 1. Download source code: https://github.com/uncrustify/uncrustify/archive/refs/tags/uncrustify-0.72.0.zip
6-
# It's important that all developers use the exact same version so we don't get a "format battle".
7-
# 2. Building:
8-
# - Linux: mkdir build && cd build && cmake -DCMAKE_BUILD_TYPE=Release .. && make
9-
# - Windows: mkdir build && cd build && cmake -G"NMake Makefiles" -DCMAKE_BUILD_TYPE=Release .. && nmake
10-
# 3. Ensure that the binary "uncrustify" is found by runformat. Either:
11-
# - you can put uncrustify in your PATH
12-
# - you can create an environment variable UNCRUSTIFY that has the full path of the binary
13-
14-
UNCRUSTIFY_VERSION="0.72.0"
15-
UNCRUSTIFY="${UNCRUSTIFY-uncrustify}"
16-
17-
DETECTED_VERSION=$("$UNCRUSTIFY" --version 2>&1 | grep -o -E '[0-9.]+')
18-
if [ "$DETECTED_VERSION" != "${UNCRUSTIFY_VERSION}" ]; then
19-
echo "You should use version: ${UNCRUSTIFY_VERSION}"
20-
echo "Detected version: ${DETECTED_VERSION}"
21-
exit 1
5+
# Usage:
6+
# ./runformat # format using the configured Uncrustify
7+
# ./runformat --install # download, build, and use Uncrustify locally
8+
# ./runformat --install --install-dir /abs/path
9+
# ./runformat --expected-uncrustify-version # print ONLY the expected Uncrustify version
10+
#
11+
# You may also set:
12+
# UNCRUSTIFY=/abs/path/to/uncrustify # use a specific binary
13+
# UNCRUSTIFY_INSTALL_DIR=/abs/path # where `--install` will install
14+
#
15+
# Requirements:
16+
# - All developers must use the *exact* same Uncrustify version to avoid format churn.
17+
# - Either:
18+
# * Have `uncrustify` in PATH, or
19+
# * Set env var UNCRUSTIFY=/absolute/path/to/uncrustify, or
20+
# * Run `./runformat --install` to fetch & build the pinned version locally.
21+
#
22+
# Notes:
23+
# - The local install lives under: ./.runformat-uncrustify/uncrustify-<version>-install
24+
# - The config file is expected at: ./.uncrustify.cfg
25+
#
26+
27+
set -euo pipefail
28+
29+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
30+
cd "$SCRIPT_DIR"
31+
32+
UNCRUSTIFY_VERSION="0.80.1"
33+
UNCRUSTIFY_HASH="6bf662e05c4140dd4df5e45d6690cad96b4ef23c293b85813f5c725bbf1894d0"
34+
35+
UNCRUSTIFY_WORK_DIR="${SCRIPT_DIR}/.runformat-uncrustify"
36+
37+
# Allow external install dir override (arg or env). If not set, default under work dir.
38+
DEFAULT_INSTALL_DIR="${UNCRUSTIFY_WORK_DIR}/uncrustify-${UNCRUSTIFY_VERSION}-install"
39+
UNCRUSTIFY_INSTALL_DIR="${UNCRUSTIFY_INSTALL_DIR:-$DEFAULT_INSTALL_DIR}"
40+
UNCRUSTIFY_BIN="${UNCRUSTIFY_INSTALL_DIR}/bin/uncrustify"
41+
42+
# Allow override via env; default to local pinned build path.
43+
UNCRUSTIFY="${UNCRUSTIFY:-$UNCRUSTIFY_BIN}"
44+
UNCRUSTIFY_CONFIG="${SCRIPT_DIR}/.uncrustify.cfg"
45+
46+
err() { echo -e >&2 "ERROR: $@\n"; }
47+
die() { err $@; exit 1; }
48+
49+
install_uncrustify() {
50+
local root="uncrustify-${UNCRUSTIFY_VERSION}"
51+
local file="${root}.tar.gz"
52+
local url="https://github.com/uncrustify/uncrustify/releases/download/${root}/${file}"
53+
54+
mkdir -p "${UNCRUSTIFY_WORK_DIR}"
55+
56+
echo "Downloading ${file}..."
57+
curl -fsSL -o "${UNCRUSTIFY_WORK_DIR}/${file}" "${url}"
58+
59+
(
60+
cd "${UNCRUSTIFY_WORK_DIR}"
61+
62+
echo "${UNCRUSTIFY_HASH} ${file}" > "${file}.sha256"
63+
sha256sum -c "${file}.sha256"
64+
rm -f "${file}.sha256"
65+
66+
command -v cmake >/dev/null 2>&1 || die "cmake executable not found."
67+
68+
echo "Extracting archive..."
69+
rm -rf "${root}" "${root}-build"
70+
mkdir -p "${root}"
71+
tar -xzf "${file}" --strip-components=1 -C "${root}"
72+
73+
echo "Configuring (prefix: ${UNCRUSTIFY_INSTALL_DIR})..."
74+
cmake \
75+
-DCMAKE_BUILD_TYPE:STRING=Release \
76+
-DCMAKE_INSTALL_PREFIX:PATH="${UNCRUSTIFY_INSTALL_DIR}" \
77+
-S "${root}" -B "${root}-build"
78+
79+
echo "Building & installing..."
80+
cmake --build "${root}-build" --config Release --target install --parallel
81+
)
82+
83+
echo "Installed Uncrustify to: ${UNCRUSTIFY_INSTALL_DIR}"
84+
}
85+
86+
print_usage_and_exit() {
87+
sed -n '2,25p' "$0" | sed 's/^# \{0,1\}//'
88+
exit 0
89+
}
90+
91+
# Print ONLY expected Uncrustify version (no extra text).
92+
print_expected_uncrustify_version_and_exit() {
93+
printf '%s\n' "$UNCRUSTIFY_VERSION"
94+
exit 0
95+
}
96+
97+
# --------------------------
98+
# Argument parsing
99+
# --------------------------
100+
DO_INSTALL=0
101+
PRINT_EXPECTED_UNCRUSTIFY_VERSION=0
102+
# Accept: --install, --install-dir <dir>, -h/--help
103+
while [[ $# -gt 0 ]]; do
104+
case "$1" in
105+
-h|--help)
106+
print_usage_and_exit
107+
;;
108+
--install)
109+
DO_INSTALL=1
110+
shift
111+
;;
112+
--install-dir)
113+
[[ $# -ge 2 ]] || die "$1 requires a path argument"
114+
UNCRUSTIFY_INSTALL_DIR="$(readlink -m "$2" 2>/dev/null || realpath -m "$2")"
115+
UNCRUSTIFY_BIN="${UNCRUSTIFY_INSTALL_DIR}/bin/uncrustify"
116+
# Only update UNCRUSTIFY default if user hasn't explicitly set it
117+
if [[ "${UNCRUSTIFY:-}" != "${UNCRUSTIFY_BIN}" ]]; then
118+
UNCRUSTIFY="${UNCRUSTIFY_BIN}"
119+
fi
120+
shift 2
121+
;;
122+
--expected-uncrustify-version)
123+
PRINT_EXPECTED_UNCRUSTIFY_VERSION=1
124+
shift
125+
;;
126+
*)
127+
# ignore unrecognized positional args for now
128+
shift
129+
;;
130+
esac
131+
done
132+
133+
if [[ "$DO_INSTALL" -eq 1 ]]; then
134+
install_uncrustify
135+
# Ensure we use the freshly installed binary for this run
136+
UNCRUSTIFY="$UNCRUSTIFY_BIN"
22137
fi
23138

24-
# OS variables
25-
[ $(uname -s) = "Darwin" ] && export OSX=1 && export UNIX=1
26-
[ $(uname -s) = "Linux" ] && export LINUX=1 && export UNIX=1
27-
uname -s | grep -q "_NT-" && export WINDOWS=1
28-
29-
if [ $OSX ]
30-
then
31-
export CPUCOUNT=$(sysctl -n hw.ncpu)
32-
elif [ $LINUX ]
33-
then
34-
export CPUCOUNT=$(nproc)
35-
else
36-
export CPUCOUNT="1"
139+
# If requested, print ONLY the expected Uncrustify version and exit.
140+
if [[ "$PRINT_EXPECTED_UNCRUSTIFY_VERSION" -eq 1 ]]; then
141+
print_expected_uncrustify_version_and_exit
37142
fi
38143

39-
$UNCRUSTIFY -c .uncrustify.cfg --no-backup *.cpp *.h
144+
# --------------------------
145+
# Validate & run
146+
# --------------------------
147+
148+
# Check Uncrustify availability
149+
if ! command -v "$UNCRUSTIFY" >/dev/null 2>&1; then
150+
err "Uncrustify executable not found: $UNCRUSTIFY"
151+
die "Add it to PATH, set UNCRUSTIFY=/path/to/uncrustify, or run: $0 --install [--install-dir DIR]"
152+
fi
153+
154+
# Version check
155+
DETECTED_VERSION="$("$UNCRUSTIFY" --version 2>&1 | grep -oE '[0-9]+(\.[0-9]+)*' | head -n1 || true)"
156+
echo "Detected Uncrustify: ${DETECTED_VERSION:-unknown}"
157+
if [[ "$DETECTED_VERSION" != "${UNCRUSTIFY_VERSION}" ]]; then
158+
die "Expected Uncrustify ${UNCRUSTIFY_VERSION}. Re-run with --install (and optionally --install-dir) or set UNCRUSTIFY."
159+
fi
160+
161+
# Config check
162+
[[ -f "$UNCRUSTIFY_CONFIG" ]] || die "Uncrustify config not found at: $UNCRUSTIFY_CONFIG"
163+
164+
# Run formatter
165+
echo "Running formatter..."
166+
$UNCRUSTIFY -c "$UNCRUSTIFY_CONFIG" -l CPP --no-backup --replace *.cpp *.h
167+
168+
# Show diff and fail if changes exist
169+
echo "Checking for formatting changes..."
170+
git diff --exit-code || {
171+
echo
172+
echo "Formatting changes were applied. Please review and commit."
173+
exit 1
174+
}
175+
176+
echo "Formatting is clean."

0 commit comments

Comments
 (0)