Skip to content

Commit ce7d6cb

Browse files
JennyPngscbedd
andauthored
Use new Black check in CI (#43303)
* cut * debug: log * missing black in pyproject should default to false...dunno why this worked for tox without this change tho * check exclusions in dispatch * Update eng/tools/azure-sdk-tools/azpysdk/black.py Co-authored-by: Scott Beddall <[email protected]> --------- Co-authored-by: Scott Beddall <[email protected]>
1 parent cb3227a commit ce7d6cb

File tree

3 files changed

+64
-23
lines changed

3 files changed

+64
-23
lines changed

eng/pipelines/templates/steps/run_black.yml

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,15 @@ steps:
99
- task: PythonScript@0
1010
displayName: 'Run Black'
1111
inputs:
12-
scriptPath: 'scripts/devops_tasks/dispatch_tox.py'
12+
scriptPath: 'eng/scripts/dispatch_checks.py'
1313
arguments: >-
1414
"$(TargetingString)"
15-
--mark_arg="${{ parameters.TestMarkArgument }}"
1615
--service="${{ parameters.ServiceDirectory }}"
17-
--toxenv="black"
16+
--checks="black"
1817
--filter-type="Omit_management"
1918
${{ parameters.AdditionalTestArgs }}
20-
env: ${{ parameters.EnvVars }}
19+
env:
20+
TOX_PIP_IMPL: "uv"
21+
VIRTUAL_ENV: ""
22+
PYTHONHOME: ""
2123
condition: and(succeededOrFailed(), ne(variables['Skip.Black'],'true'))

eng/scripts/dispatch_checks.py

Lines changed: 56 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,11 @@
1212
from ci_tools.variables import in_ci
1313
from ci_tools.scenario.generation import build_whl_for_req
1414
from ci_tools.logging import configure_logging, logger
15-
from ci_tools.environment_exclusions import is_check_enabled
15+
from ci_tools.environment_exclusions import is_check_enabled, CHECK_DEFAULTS
1616

1717
root_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", ".."))
1818

19+
1920
@dataclass
2021
class CheckResult:
2122
package: str
@@ -26,7 +27,14 @@ class CheckResult:
2627
stderr: str
2728

2829

29-
async def run_check(semaphore: asyncio.Semaphore, package: str, check: str, base_args: List[str], idx: int, total: int) -> CheckResult:
30+
async def run_check(
31+
semaphore: asyncio.Semaphore,
32+
package: str,
33+
check: str,
34+
base_args: List[str],
35+
idx: int,
36+
total: int,
37+
) -> CheckResult:
3038
"""Run a single check (subprocess) within a concurrency semaphore, capturing output and timing.
3139
3240
:param semaphore: Concurrency limiter used to bound concurrent checks.
@@ -65,7 +73,9 @@ async def run_check(semaphore: asyncio.Semaphore, package: str, check: str, base
6573
stderr = stderr_b.decode(errors="replace")
6674
exit_code = proc.returncode or 0
6775
status = "OK" if exit_code == 0 else f"FAIL({exit_code})"
68-
logger.info(f"[END {idx}/{total}] {check} :: {package} -> {status} in {duration:.2f}s")
76+
logger.info(
77+
f"[END {idx}/{total}] {check} :: {package} -> {status} in {duration:.2f}s"
78+
)
6979
# Print captured output after completion to avoid interleaving
7080
header = f"===== OUTPUT: {check} :: {package} (exit {exit_code}) ====="
7181
trailer = "=" * len(header)
@@ -74,7 +84,7 @@ async def run_check(semaphore: asyncio.Semaphore, package: str, check: str, base
7484
print(stdout.rstrip())
7585
print(trailer)
7686
if stderr:
77-
print(header.replace('OUTPUT', 'STDERR'))
87+
print(header.replace("OUTPUT", "STDERR"))
7888
print(stderr.rstrip())
7989
print(trailer)
8090

@@ -86,7 +96,9 @@ async def run_check(semaphore: asyncio.Semaphore, package: str, check: str, base
8696
try:
8797
shutil.rmtree(isolate_dir)
8898
except:
89-
logger.warning(f"Failed to remove isolate dir {isolate_dir} for {package} / {check}")
99+
logger.warning(
100+
f"Failed to remove isolate dir {isolate_dir} for {package} / {check}"
101+
)
90102
return CheckResult(package, check, exit_code, duration, stdout, stderr)
91103

92104

@@ -110,10 +122,14 @@ def summarize(results: List[CheckResult]) -> int:
110122
print("-" * len(header))
111123
for r in sorted(results, key=lambda x: (x.exit_code != 0, x.package, x.check)):
112124
status = "OK" if r.exit_code == 0 else f"FAIL({r.exit_code})"
113-
print(f"{r.package.ljust(pkg_w)} {r.check.ljust(chk_w)} {status.ljust(8)} {r.duration:>10.2f}")
125+
print(
126+
f"{r.package.ljust(pkg_w)} {r.check.ljust(chk_w)} {status.ljust(8)} {r.duration:>10.2f}"
127+
)
114128
worst = max((r.exit_code for r in results), default=0)
115129
failed = [r for r in results if r.exit_code != 0]
116-
print(f"\nTotal checks: {len(results)} | Failed: {len(failed)} | Worst exit code: {worst}")
130+
print(
131+
f"\nTotal checks: {len(results)} | Failed: {len(failed)} | Worst exit code: {worst}"
132+
)
117133
return worst
118134

119135

@@ -135,10 +151,16 @@ async def run_all_checks(packages, checks, max_parallel):
135151
combos = [(p, c) for p in packages for c in checks]
136152
total = len(combos)
137153
for idx, (package, check) in enumerate(combos, start=1):
138-
if not is_check_enabled(package, check):
139-
logger.warning(f"Skipping disabled check {check} ({idx}/{total}) for package {package}")
154+
if not is_check_enabled(package, check, CHECK_DEFAULTS.get(check, True)):
155+
logger.warning(
156+
f"Skipping disabled check {check} ({idx}/{total}) for package {package}"
157+
)
140158
continue
141-
tasks.append(asyncio.create_task(run_check(semaphore, package, check, base_args, idx, total)))
159+
tasks.append(
160+
asyncio.create_task(
161+
run_check(semaphore, package, check, base_args, idx, total)
162+
)
163+
)
142164

143165
# Handle Ctrl+C gracefully
144166
pending = set(tasks)
@@ -157,7 +179,9 @@ async def run_all_checks(packages, checks, max_parallel):
157179
elif isinstance(res, Exception):
158180
norm_results.append(CheckResult(package, check, 99, 0.0, "", str(res)))
159181
else:
160-
norm_results.append(CheckResult(package, check, 98, 0.0, "", f"Unknown result type: {res}"))
182+
norm_results.append(
183+
CheckResult(package, check, 98, 0.0, "", f"Unknown result type: {res}")
184+
)
161185
return summarize(norm_results)
162186

163187

@@ -233,11 +257,15 @@ def handler(signum, frame):
233257
),
234258
)
235259

236-
parser.add_argument("--disablecov", help=("Flag. Disables code coverage."), action="store_true")
260+
parser.add_argument(
261+
"--disablecov", help=("Flag. Disables code coverage."), action="store_true"
262+
)
237263

238264
parser.add_argument(
239265
"--service",
240-
help=("Name of service directory (under sdk/) to test. Example: --service applicationinsights"),
266+
help=(
267+
"Name of service directory (under sdk/) to test. Example: --service applicationinsights"
268+
),
241269
)
242270

243271
parser.add_argument(
@@ -297,7 +325,9 @@ def handler(signum, frame):
297325
else:
298326
target_dir = root_dir
299327

300-
logger.info(f"Beginning discovery for {args.service} and root dir {root_dir}. Resolving to {target_dir}.")
328+
logger.info(
329+
f"Beginning discovery for {args.service} and root dir {root_dir}. Resolving to {target_dir}."
330+
)
301331

302332
# ensure that recursive virtual envs aren't messed with by this call
303333
os.environ.pop("VIRTUAL_ENV", None)
@@ -314,7 +344,9 @@ def handler(signum, frame):
314344
)
315345

316346
if len(targeted_packages) == 0:
317-
logger.info(f"No packages collected for targeting string {args.glob_string} and root dir {root_dir}. Exit 0.")
347+
logger.info(
348+
f"No packages collected for targeting string {args.glob_string} and root dir {root_dir}. Exit 0."
349+
)
318350
exit(0)
319351

320352
logger.info(f"Executing checks with the executable {sys.executable}.")
@@ -329,7 +361,9 @@ def handler(signum, frame):
329361
if in_ci():
330362
# prepare a build of eng/tools/azure-sdk-tools
331363
# todo: ensure that we honor this .wheels directory when replacing for dev reqs
332-
build_whl_for_req("eng/tools/azure-sdk-tools", root_dir, os.path.join(root_dir, ".wheels"))
364+
build_whl_for_req(
365+
"eng/tools/azure-sdk-tools", root_dir, os.path.join(root_dir, ".wheels")
366+
)
333367

334368
# so if we have checks whl,import_all and selected package paths `sdk/core/azure-core`, `sdk/storage/azure-storage-blob` we should
335369
# shell out to `azypysdk <checkname>` with cwd of the package directory, which is what is in `targeted_packages` array
@@ -342,11 +376,15 @@ def handler(signum, frame):
342376
logger.error("No valid checks provided via -c/--checks.")
343377
sys.exit(2)
344378

345-
logger.info(f"Running {len(checks)} check(s) across {len(targeted_packages)} packages (max_parallel={args.max_parallel}).")
379+
logger.info(
380+
f"Running {len(checks)} check(s) across {len(targeted_packages)} packages (max_parallel={args.max_parallel})."
381+
)
346382

347383
configure_interrupt_handling()
348384
try:
349-
exit_code = asyncio.run(run_all_checks(targeted_packages, checks, args.max_parallel))
385+
exit_code = asyncio.run(
386+
run_all_checks(targeted_packages, checks, args.max_parallel)
387+
)
350388
except KeyboardInterrupt:
351389
logger.error("Aborted by user.")
352390
exit_code = 130

eng/tools/azure-sdk-tools/azpysdk/black.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ def run(self, args: argparse.Namespace) -> int:
6060
config_file_location = os.path.join(REPO_ROOT, "eng/black-pyproject.toml")
6161

6262
if in_ci():
63-
if not is_check_enabled(package_dir, "black"):
63+
if not is_check_enabled(package_dir, "black", default=False):
6464
logger.info(f"Package {package_name} opts-out of black check.")
6565
continue
6666
try:
@@ -76,6 +76,7 @@ def run(self, args: argparse.Namespace) -> int:
7676
stderr=subprocess.PIPE,
7777
check=True,
7878
)
79+
7980
if run_result.stderr and "reformatted" in run_result.stderr.decode("utf-8"):
8081
if in_ci():
8182
logger.info(f"The package {package_name} needs reformat. Run `black` locally to reformat.")

0 commit comments

Comments
 (0)