Skip to content

Commit 9ccd9ad

Browse files
committed
github action: move the clippy management into a script
1 parent 65596fe commit 9ccd9ad

4 files changed

Lines changed: 169 additions & 35 deletions

File tree

.github/workflows/code-quality.yml

Lines changed: 8 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -127,37 +127,18 @@ jobs:
127127
shell: bash
128128
command: |
129129
## `cargo clippy` lint testing
130-
unset fault
131-
fault_type="${{ steps.vars.outputs.FAULT_TYPE }}"
132-
fault_prefix=$(echo "$fault_type" | tr '[:lower:]' '[:upper:]')
133-
# * convert any warnings to GHA UI annotations; ref: <https://help.github.com/en/actions/reference/workflow-commands-for-github-actions#setting-a-warning-message>
134-
if [[ "${{ matrix.job.features }}" == "all" ]]; then
135-
extra="--all-features"
136-
else
137-
extra="--features ${{ matrix.job.features }}"
130+
ARGS="--features ${{ matrix.job.features }}"
131+
ARGS="${ARGS} --fault-type ${{ steps.vars.outputs.FAULT_TYPE }}"
132+
if [[ "${{ matrix.job.workspace }}" =~ ^(1|t|true|y|yes)$ ]]; then
133+
ARGS="${ARGS} --workspace"
138134
fi
139-
case '${{ matrix.job.workspace }}' in
140-
1|t|true|y|yes)
141-
extra="${extra} --workspace"
142-
;;
143-
esac
144135
if [[ -n "${{ matrix.job.target }}" ]]; then
145-
extra="${extra} --no-default-features --target ${{ matrix.job.target }}"
136+
ARGS="${ARGS} --target ${{ matrix.job.target }}"
146137
fi
147-
# * determine sub-crate utility list (similar to FreeBSD workflow)
148-
if [[ -n "${{ matrix.job.target }}" ]]; then
149-
# For cross-compilation targets, just check -pcoreutils (show-utils.sh over-resolves due to default features)
150-
S=$(cargo clippy $extra -pcoreutils -- -D warnings 2>&1) && printf "%s\n" "$S" || { printf "%s\n" "$S" ; printf "%s" "$S" | sed -E -n -e '/^error:/{' -e "N; s/^error:[[:space:]]+(.*)\\n[[:space:]]+-->[[:space:]]+(.*):([0-9]+):([0-9]+).*$/::${fault_type} file=\2,line=\3,col=\4::${fault_prefix}: \`cargo clippy\`: \1 (file:'\2', line:\3)/p;" -e '}' ; fault=true ; }
151-
else
152-
if [[ "${{ matrix.job.features }}" == "all" ]]; then
153-
UTILITY_LIST="$(./util/show-utils.sh --all-features)"
154-
else
155-
UTILITY_LIST="$(./util/show-utils.sh --features ${{ matrix.job.features }})"
156-
fi
157-
CARGO_UTILITY_LIST_OPTIONS="$(for u in ${UTILITY_LIST}; do echo -n "-puu_${u} "; done;)"
158-
S=$(cargo clippy --all-targets $extra --tests --benches -pcoreutils ${CARGO_UTILITY_LIST_OPTIONS} -- -D warnings 2>&1) && printf "%s\n" "$S" || { printf "%s\n" "$S" ; printf "%s" "$S" | sed -E -n -e '/^error:/{' -e "N; s/^error:[[:space:]]+(.*)\\n[[:space:]]+-->[[:space:]]+(.*):([0-9]+):([0-9]+).*$/::${fault_type} file=\2,line=\3,col=\4::${fault_prefix}: \`cargo clippy\`: \1 (file:'\2', line:\3)/p;" -e '}' ; fault=true ; }
138+
if [[ -n "${{ steps.vars.outputs.FAIL_ON_FAULT }}" ]]; then
139+
ARGS="${ARGS} --fail-on-fault"
159140
fi
160-
if [ -n "${{ steps.vars.outputs.FAIL_ON_FAULT }}" ] && [ -n "$fault" ]; then exit 1 ; fi
141+
python3 util/run-clippy.py ${ARGS}
161142
- name: "cargo clippy on fuzz dir"
162143
if: runner.os != 'Windows' && !matrix.job.target
163144
shell: bash

.github/workflows/freebsd.yml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ jobs:
4141
sync: rsync
4242
copyback: false
4343
# We need jq and GNU coreutils to run show-utils.sh and bash to use inline shell string replacement
44-
prepare: pkg install -y curl sudo jq coreutils bash
44+
prepare: pkg install -y curl sudo jq coreutils bash python3
4545
run: |
4646
## Prepare, build, and test
4747
# implementation modelled after ref: <https://github.com/rust-lang/rustup/pull/2783>
@@ -73,7 +73,6 @@ jobs:
7373
FAULT_PREFIX=\$(echo "\${FAULT_TYPE}" | tr '[:lower:]' '[:upper:]')
7474
# * determine sub-crate utility list
7575
UTILITY_LIST="\$(./util/show-utils.sh --features ${{ matrix.job.features }})"
76-
CARGO_UTILITY_LIST_OPTIONS="\$(for u in \${UTILITY_LIST}; do echo -n "-puu_\${u} "; done;)"
7776
## Info
7877
# environment
7978
echo "## environment"
@@ -101,8 +100,9 @@ jobs:
101100
## cargo clippy lint testing
102101
if [ -z "\${FAULT}" ]; then
103102
echo "## cargo clippy lint testing"
104-
# * convert any warnings to GHA UI annotations; ref: <https://help.github.com/en/actions/reference/workflow-commands-for-github-actions#setting-a-warning-message>
105-
S=\$(cargo clippy --all-targets \${CARGO_UTILITY_LIST_OPTIONS} -- -D warnings 2>&1) && printf "%s\n" "\$S" || { printf "%s\n" "\$S" ; printf "%s" "\$S" | sed -E -n -e '/^error:/{' -e "N; s/^error:[[:space:]]+(.*)\\n[[:space:]]+-->[[:space:]]+(.*):([0-9]+):([0-9]+).*\$/::\${FAULT_TYPE} file=\2,line=\3,col=\4::\${FAULT_PREFIX}: \\\`cargo clippy\\\`: \1 (file:'\2', line:\3)/p;" -e '}' ; FAULT=true ; }
103+
CLIPPY_ARGS="--features ${{ matrix.job.features }} --fault-type \${FAULT_TYPE}"
104+
if [ -n "\${FAIL_ON_FAULT}" ]; then CLIPPY_ARGS="\${CLIPPY_ARGS} --fail-on-fault"; fi
105+
python3 util/run-clippy.py \${CLIPPY_ARGS} || FAULT=true
106106
fi
107107
# Clean to avoid to rsync back the files
108108
cargo clean

.github/workflows/openbsd.yml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ jobs:
4747
prepare: |
4848
# Clean up disk space before installing packages
4949
df -h
50-
pkg_add curl sudo-- jq coreutils bash rust rust-clippy rust-rustfmt llvm--
50+
pkg_add curl sudo-- jq coreutils bash rust rust-clippy rust-rustfmt llvm-- python3
5151
rm -rf /usr/share/relink/* /usr/X11R6/* /usr/share/doc/* /usr/share/man/* &
5252
# Clean up package cache after installation
5353
pkg_delete -a &
@@ -84,7 +84,6 @@ jobs:
8484
FAULT_PREFIX=\$(echo "\${FAULT_TYPE}" | tr '[:lower:]' '[:upper:]')
8585
# * determine sub-crate utility list
8686
UTILITY_LIST="\$(./util/show-utils.sh --features ${{ matrix.job.features }})"
87-
CARGO_UTILITY_LIST_OPTIONS="\$(for u in \${UTILITY_LIST}; do echo -n "-puu_\${u} "; done;)"
8887
## Info
8988
# environment
9089
echo "## environment"
@@ -111,8 +110,9 @@ jobs:
111110
## cargo clippy lint testing
112111
if [ -z "\${FAULT}" ]; then
113112
echo "## cargo clippy lint testing"
114-
# * convert any warnings to GHA UI annotations; ref: <https://help.github.com/en/actions/reference/workflow-commands-for-github-actions#setting-a-warning-message>
115-
S=\$(cargo clippy --all-targets \${CARGO_UTILITY_LIST_OPTIONS} -- -D warnings 2>&1) && printf "%s\n" "\$S" || { printf "%s\n" "\$S" ; printf "%s" "\$S" | sed -E -n -e '/^error:/{' -e "N; s/^error:[[:space:]]+(.*)\\n[[:space:]]+-->[[:space:]]+(.*):([0-9]+):([0-9]+).*\$/::\${FAULT_TYPE} file=\2,line=\3,col=\4::\${FAULT_PREFIX}: \\\`cargo clippy\\\`: \1 (file:'\2', line:\3)/p;" -e '}' ; FAULT=true ; }
113+
CLIPPY_ARGS="--features ${{ matrix.job.features }} --fault-type \${FAULT_TYPE}"
114+
if [ -n "\${FAIL_ON_FAULT}" ]; then CLIPPY_ARGS="\${CLIPPY_ARGS} --fail-on-fault"; fi
115+
python3 util/run-clippy.py \${CLIPPY_ARGS} || FAULT=true
116116
fi
117117
# Clean to avoid to rsync back the files and free up disk space
118118
cargo clean

util/run-clippy.py

Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
#!/usr/bin/env python3
2+
# SPDX-License-Identifier: MIT
3+
# spell-checker:ignore pcoreutils
4+
"""Run cargo clippy with appropriate flags and emit GitHub Actions annotations."""
5+
6+
from __future__ import annotations
7+
8+
import argparse
9+
import json
10+
import os
11+
import re
12+
import subprocess
13+
import sys
14+
15+
16+
def run_cmd(
17+
cmd: list[str],
18+
*,
19+
check: bool = False,
20+
) -> subprocess.CompletedProcess[str]:
21+
"""Run a command with UTF-8 encoding (avoids cp1252 issues on Windows)."""
22+
env = {**os.environ, "PYTHONUTF8": "1"}
23+
return subprocess.run(
24+
cmd,
25+
capture_output=True,
26+
text=True,
27+
encoding="utf-8",
28+
errors="replace",
29+
check=check,
30+
env=env,
31+
)
32+
33+
34+
def get_utility_list(features: str) -> list[str]:
35+
"""Get list of utilities from cargo metadata."""
36+
if features == "all":
37+
cmd = ["cargo", "metadata", "--all-features", "--format-version", "1"]
38+
else:
39+
cmd = ["cargo", "metadata", "--features", features, "--format-version", "1"]
40+
result = run_cmd(cmd, check=True)
41+
metadata = json.loads(result.stdout)
42+
# Find the coreutils root node and collect uu_ dependencies
43+
utilities = []
44+
for node in metadata["resolve"]["nodes"]:
45+
if re.search(r"coreutils[ @#]\d+\.\d+\.\d+", node["id"]):
46+
for dep in node["deps"]:
47+
# The pkg field contains the crate name (uu_<util>),
48+
# while name is the renamed dependency alias
49+
pkg = dep["pkg"]
50+
match = re.search(r"uu_(\w+)[@#]", pkg)
51+
if match:
52+
utilities.append(match.group(1))
53+
break
54+
return sorted(utilities)
55+
56+
57+
def build_clippy_command(
58+
features: str,
59+
*,
60+
workspace: bool,
61+
target: str | None,
62+
) -> list[str]:
63+
"""Build the cargo clippy command line."""
64+
cmd = ["cargo", "clippy"]
65+
66+
extra = []
67+
if features == "all":
68+
extra.append("--all-features")
69+
else:
70+
extra.extend(["--features", features])
71+
72+
if workspace:
73+
extra.append("--workspace")
74+
75+
if target:
76+
extra.extend(["--no-default-features", "--target", target])
77+
# For cross-compilation targets, just check -pcoreutils
78+
# (show-utils.sh over-resolves due to default features)
79+
extra.append("-pcoreutils")
80+
else:
81+
extra.extend(["--all-targets", "--tests", "--benches", "-pcoreutils"])
82+
utilities = get_utility_list(features)
83+
extra.extend(f"-puu_{u}" for u in utilities)
84+
85+
cmd.extend(extra)
86+
cmd.extend(["--", "-D", "warnings"])
87+
return cmd
88+
89+
90+
# Pattern to match clippy/rustc errors for GHA annotations
91+
ERROR_PATTERN = re.compile(
92+
r"^error:\s+(.*)\n\s+-->\s+(.*):(\d+):(\d+)",
93+
re.MULTILINE,
94+
)
95+
96+
97+
def emit_annotations(output: str, fault_type: str) -> None:
98+
"""Emit GitHub Actions annotations from cargo clippy errors."""
99+
fault_prefix = fault_type.upper()
100+
for m in ERROR_PATTERN.finditer(output):
101+
message, file, line, col = m.groups()
102+
print(
103+
f"::{fault_type} file={file},line={line},col={col}"
104+
f"::{fault_prefix}: `cargo clippy`: {message} (file:'{file}', line:{line})",
105+
)
106+
107+
108+
def main() -> int:
109+
"""Run cargo clippy and emit GHA annotations on failure."""
110+
parser = argparse.ArgumentParser(description="Run cargo clippy for CI")
111+
parser.add_argument("--features", required=True, help="Feature set to use")
112+
parser.add_argument(
113+
"--workspace",
114+
action="store_true",
115+
help="Include --workspace flag",
116+
)
117+
parser.add_argument("--target", default=None, help="Cross-compilation target")
118+
parser.add_argument(
119+
"--fault-type",
120+
default="warning",
121+
choices=["warning", "error"],
122+
help="GHA annotation type",
123+
)
124+
parser.add_argument(
125+
"--fail-on-fault",
126+
action="store_true",
127+
help="Exit with error code on clippy failures",
128+
)
129+
args = parser.parse_args()
130+
131+
cmd = build_clippy_command(
132+
args.features,
133+
workspace=args.workspace,
134+
target=args.target,
135+
)
136+
print(f"Running: {' '.join(cmd)}", file=sys.stderr)
137+
138+
result = run_cmd(cmd)
139+
output = result.stdout + result.stderr
140+
141+
# Always print the full output
142+
print(output)
143+
144+
if result.returncode != 0:
145+
emit_annotations(output, args.fault_type)
146+
if args.fail_on_fault:
147+
return 1
148+
149+
return 0
150+
151+
152+
if __name__ == "__main__":
153+
sys.exit(main())

0 commit comments

Comments
 (0)