Skip to content

Commit 01eb8f0

Browse files
authored
[build] add smart targeting and --lint flag to format.sh (#17035)
* [build] add smart targeting and --lint flag to format.sh * [build] include staged changes for pre-commit support * [build] add --pre-commit and --pre-push flags to format.sh * [build] include untracked files in default mode * [build] add --all flag to skip change detection * [build] update format.ps1 to match format.sh
1 parent 36b25ca commit 01eb8f0

2 files changed

Lines changed: 251 additions & 42 deletions

File tree

scripts/format.ps1

Lines changed: 123 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,142 @@
1-
# Code formatter.
1+
# Code formatter - runs targeted formatters based on what changed from trunk.
2+
# Usage: format.ps1 [-All] [-PreCommit] [-PrePush] [-Lint]
3+
# (default) Check all changes relative to trunk including uncommitted work
4+
# -All Format everything, skip change detection
5+
# -PreCommit Only check staged changes
6+
# -PrePush Only check committed changes relative to trunk
7+
# -Lint Also run linters before formatting
8+
9+
param(
10+
[switch]$All,
11+
[switch]$PreCommit,
12+
[switch]$PrePush,
13+
[switch]$Lint
14+
)
215

316
Set-StrictMode -Version 'Latest'
417
$ErrorActionPreference = 'Stop'
518

19+
# Validate mutually exclusive flags
20+
if ($PreCommit -and $PrePush) {
21+
Write-Error "Cannot use both -PreCommit and -PrePush"
22+
exit 1
23+
}
24+
625
function section($message) {
726
Write-Host "- $message" -ForegroundColor Green
827
}
928

29+
# Find what's changed compared to trunk (skip if -All)
30+
$formatAll = $All
31+
$trunkRef = git rev-parse --verify trunk 2>$null
32+
33+
if (-not $formatAll -and $trunkRef) {
34+
$base = git merge-base HEAD $trunkRef 2>$null
35+
if ($base) {
36+
if ($PreCommit) {
37+
$changed = git diff --name-only --cached
38+
} elseif ($PrePush) {
39+
$changed = git diff --name-only $base HEAD
40+
} else {
41+
$committed = git diff --name-only $base HEAD
42+
$staged = git diff --name-only --cached
43+
$unstaged = git diff --name-only
44+
$untracked = git ls-files --others --exclude-standard
45+
$changed = ($committed + $staged + $unstaged + $untracked) | Sort-Object -Unique
46+
}
47+
} else {
48+
$formatAll = $true
49+
}
50+
} elseif (-not $formatAll) {
51+
# No trunk ref found, format everything
52+
$formatAll = $true
53+
}
54+
55+
# Helper to check if a pattern matches changed files
56+
function changedMatches($pattern) {
57+
if ($formatAll) { return $true }
58+
return ($changed | Where-Object { $_ -match $pattern }).Count -gt 0
59+
}
60+
1061
$WORKSPACE_ROOT = (bazel info workspace)
11-
$GOOGLE_JAVA_FORMAT = (bazel run --run_under=echo //scripts:google-java-format)
1262

63+
# Capture baseline to detect formatter-introduced changes
64+
$baseline = git status --porcelain
65+
66+
# Always run buildifier and copyright
1367
section "Buildifier"
14-
Write-Host " buildifier" -ForegroundColor Green
68+
Write-Host " buildifier"
1569
bazel run //:buildifier
1670

17-
section "Java"
18-
Write-Host " google-java-format" -ForegroundColor Green
19-
Get-ChildItem -Path "$PWD/java" -Include "*.java" -Recurse | ForEach-Object {
20-
&"$GOOGLE_JAVA_FORMAT" --replace $_.FullName
71+
section "Copyright"
72+
Write-Host " update_copyright"
73+
bazel run //scripts:update_copyright
74+
75+
# Run language formatters only if those files changed
76+
if (changedMatches '^java/') {
77+
section "Java"
78+
Write-Host " google-java-format"
79+
$GOOGLE_JAVA_FORMAT = (bazel run --run_under=echo //scripts:google-java-format)
80+
Get-ChildItem -Path "$WORKSPACE_ROOT/java" -Include "*.java" -Recurse | ForEach-Object {
81+
& "$GOOGLE_JAVA_FORMAT" --replace $_.FullName
82+
}
2183
}
2284

23-
section "Javascript"
24-
Write-Host " javascript/selenium-webdriver - prettier" -ForegroundColor Green
25-
$NODE_WEBDRIVER = "$WORKSPACE_ROOT/javascript/selenium-webdriver"
26-
bazel run //javascript:prettier -- "$NODE_WEBDRIVER" --write "$NODE_WEBDRIVER/.prettierrc" --log-level=warn
85+
if (changedMatches '^javascript/selenium-webdriver/') {
86+
section "JavaScript"
87+
Write-Host " prettier"
88+
$NODE_WEBDRIVER = "$WORKSPACE_ROOT/javascript/selenium-webdriver"
89+
bazel run //javascript:prettier -- "$NODE_WEBDRIVER" --write "$NODE_WEBDRIVER/.prettierrc" --log-level=warn
90+
}
2791

28-
section "Ruby"
29-
Write-Host " rubocop" -ForegroundColor Green
30-
bazel run //rb:lint
92+
if (changedMatches '^rb/|^rake_tasks/|^Rakefile') {
93+
section "Ruby"
94+
Write-Host " rubocop -a"
95+
if ($Lint) {
96+
bazel run //rb:rubocop -- -a
97+
} else {
98+
bazel run //rb:rubocop -- -a --fail-level F
99+
}
100+
}
31101

32-
section "Rust"
33-
Write-Host " rustfmt" -ForegroundColor Green
34-
bazel run @rules_rust//:rustfmt
102+
if (changedMatches '^rust/') {
103+
section "Rust"
104+
Write-Host " rustfmt"
105+
bazel run @rules_rust//:rustfmt
106+
}
35107

36-
section "Python"
37-
Write-Host " python - ruff" -ForegroundColor Green
38-
bazel run //py:ruff-format
108+
if (changedMatches '^py/') {
109+
section "Python"
110+
if ($Lint) {
111+
Write-Host " ruff check"
112+
bazel run //py:ruff-check
113+
}
114+
Write-Host " ruff format"
115+
bazel run //py:ruff-format
116+
}
39117

40-
section "Copyright"
41-
bazel run //scripts:update_copyright
118+
if (changedMatches '^dotnet/') {
119+
section ".NET"
120+
Write-Host " dotnet format"
121+
bazel run //dotnet:format -- style --severity warn
122+
bazel run //dotnet:format -- whitespace
123+
}
124+
125+
# Run shellcheck and actionlint when -Lint is passed
126+
if ($Lint) {
127+
section "Shell/Actions"
128+
Write-Host " actionlint (with shellcheck)"
129+
$SHELLCHECK = (bazel run --run_under=echo @multitool//tools/shellcheck)
130+
bazel run @multitool//tools/actionlint:cwd -- -shellcheck "$SHELLCHECK"
131+
}
132+
133+
# Check if formatting introduced new changes (comparing to baseline)
134+
$after = git status --porcelain
135+
if ($after -ne $baseline) {
136+
Write-Host ""
137+
Write-Host "Formatters modified files:" -ForegroundColor Red
138+
git diff --name-only
139+
exit 1
140+
}
141+
142+
Write-Host "Format check passed." -ForegroundColor Green

scripts/format.sh

Lines changed: 128 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,147 @@
11
#!/usr/bin/env bash
2-
# Code formatter.
2+
# Code formatter - runs targeted formatters based on what changed from trunk.
3+
# Usage: format.sh [--all] [--pre-commit] [--pre-push] [--lint]
4+
# (default) Check all changes relative to trunk including uncommitted work
5+
# --all Format everything, skip change detection (previous behavior)
6+
# --pre-commit Only check staged changes
7+
# --pre-push Only check committed changes relative to trunk
8+
# --lint Also run linters before formatting
39
set -eufo pipefail
410

5-
echo "Note: for more flexibility, use './go format' or './go dotnet:format' or './go format -dotnet', etc" >&2
6-
echo "" >&2
11+
run_lint=false
12+
format_all=false
13+
mode="default"
14+
for arg in "$@"; do
15+
case "$arg" in
16+
--lint) run_lint=true ;;
17+
--all) format_all=true ;;
18+
19+
--pre-commit|--pre-push)
20+
[[ "$mode" == "default" ]] || { echo "Cannot use both --pre-commit and --pre-push" >&2; exit 1; }
21+
mode="${arg#--}"
22+
;;
23+
*)
24+
echo "Unknown option: $arg" >&2
25+
echo "Usage: $0 [--all] [--pre-commit] [--pre-push] [--lint]" >&2
26+
exit 1
27+
;;
28+
esac
29+
done
730

831
section() {
932
echo "- $*" >&2
1033
}
1134

35+
# Find what's changed compared to trunk (skip if --all)
36+
trunk_ref="$(git rev-parse --verify trunk 2>/dev/null || echo "")"
37+
38+
if [[ "$format_all" == "false" && -n "$trunk_ref" ]]; then
39+
base="$(git merge-base HEAD "$trunk_ref" 2>/dev/null || echo "")"
40+
if [[ -n "$base" ]]; then
41+
case "$mode" in
42+
pre-commit)
43+
changed="$(git diff --name-only --cached)"
44+
;;
45+
pre-push)
46+
changed="$(git diff --name-only "$base" HEAD)"
47+
;;
48+
default)
49+
committed="$(git diff --name-only "$base" HEAD)"
50+
staged="$(git diff --name-only --cached)"
51+
unstaged="$(git diff --name-only)"
52+
untracked="$(git ls-files --others --exclude-standard)"
53+
changed="$(printf '%s\n%s\n%s\n%s' "$committed" "$staged" "$unstaged" "$untracked" | sort -u)"
54+
;;
55+
esac
56+
else
57+
format_all=true
58+
fi
59+
elif [[ "$format_all" == "false" ]]; then
60+
# No trunk ref found, format everything
61+
format_all=true
62+
fi
63+
64+
# Helper to check if a pattern matches changed files
65+
changed_matches() {
66+
[[ "$format_all" == "true" ]] || echo "$changed" | grep -qE "$1"
67+
}
68+
1269
WORKSPACE_ROOT="$(bazel info workspace)"
1370

14-
GOOGLE_JAVA_FORMAT="$(bazel run --run_under=echo //scripts:google-java-format)"
71+
# Capture baseline to detect formatter-introduced changes (allows pre-existing uncommitted work)
72+
baseline="$(git status --porcelain)"
1573

74+
# Always run buildifier and copyright
1675
section "Buildifier"
1776
echo " buildifier" >&2
1877
bazel run //:buildifier
1978

20-
section "Java"
21-
echo " google-java-format" >&2
22-
find "$PWD/java" -type f -name '*.java' | xargs "$GOOGLE_JAVA_FORMAT" --replace
79+
section "Copyright"
80+
echo " update_copyright" >&2
81+
bazel run //scripts:update_copyright
82+
83+
# Run language formatters only if those files changed
84+
if changed_matches '^java/'; then
85+
section "Java"
86+
echo " google-java-format" >&2
87+
GOOGLE_JAVA_FORMAT="$(bazel run --run_under=echo //scripts:google-java-format)"
88+
find "${WORKSPACE_ROOT}/java" -type f -name '*.java' -exec "$GOOGLE_JAVA_FORMAT" --replace {} +
89+
fi
90+
91+
if changed_matches '^javascript/selenium-webdriver/'; then
92+
section "JavaScript"
93+
echo " prettier" >&2
94+
NODE_WEBDRIVER="${WORKSPACE_ROOT}/javascript/selenium-webdriver"
95+
bazel run //javascript:prettier -- "${NODE_WEBDRIVER}" --write "${NODE_WEBDRIVER}/.prettierrc" --log-level=warn
96+
fi
97+
98+
if changed_matches '^rb/|^rake_tasks/|^Rakefile'; then
99+
section "Ruby"
100+
echo " rubocop -a" >&2
101+
if [[ "$run_lint" == "true" ]]; then
102+
bazel run //rb:rubocop -- -a
103+
else
104+
bazel run //rb:rubocop -- -a --fail-level F
105+
fi
106+
fi
107+
108+
if changed_matches '^rust/'; then
109+
section "Rust"
110+
echo " rustfmt" >&2
111+
bazel run @rules_rust//:rustfmt
112+
fi
113+
114+
if changed_matches '^py/'; then
115+
section "Python"
116+
if [[ "$run_lint" == "true" ]]; then
117+
echo " ruff check" >&2
118+
bazel run //py:ruff-check
119+
fi
120+
echo " ruff format" >&2
121+
bazel run //py:ruff-format
122+
fi
23123

24-
section "Javascript"
25-
echo " javascript/selenium-webdriver - prettier" >&2
26-
NODE_WEBDRIVER="${WORKSPACE_ROOT}/javascript/selenium-webdriver"
27-
bazel run //javascript:prettier -- "${NODE_WEBDRIVER}" --write "${NODE_WEBDRIVER}/.prettierrc" --log-level=warn
124+
if changed_matches '^dotnet/'; then
125+
section ".NET"
126+
echo " dotnet format" >&2
127+
bazel run //dotnet:format -- style --severity warn
128+
bazel run //dotnet:format -- whitespace
129+
fi
28130

29-
section "Ruby"
30-
echo " rubocop" >&2
31-
bazel run //rb:rubocop -- -a --fail-level F
131+
# Run shellcheck and actionlint when --lint is passed
132+
if [[ "$run_lint" == "true" ]]; then
133+
section "Shell/Actions"
134+
echo " actionlint (with shellcheck)" >&2
135+
SHELLCHECK="$(bazel run --run_under=echo @multitool//tools/shellcheck)"
136+
bazel run @multitool//tools/actionlint:cwd -- -shellcheck "$SHELLCHECK"
137+
fi
32138

33-
section "Rust"
34-
echo " rustfmt" >&2
35-
bazel run @rules_rust//:rustfmt
139+
# Check if formatting introduced new changes (comparing to baseline)
140+
if [[ "$(git status --porcelain)" != "$baseline" ]]; then
141+
echo "" >&2
142+
echo "Formatters modified files:" >&2
143+
git diff --name-only >&2
144+
exit 1
145+
fi
36146

37-
section "Python"
38-
echo " python - ruff" >&2
39-
bazel run //py:ruff-format
147+
echo "Format check passed." >&2

0 commit comments

Comments
 (0)