Skip to content

DAOS-18933 ci: fix PermissionError in pre-commit clang-format hook#18183

Open
knard38 wants to merge 1 commit intomasterfrom
ckochhof/fix/master/daos-18933/patch-001
Open

DAOS-18933 ci: fix PermissionError in pre-commit clang-format hook#18183
knard38 wants to merge 1 commit intomasterfrom
ckochhof/fix/master/daos-18933/patch-001

Conversation

@knard38
Copy link
Copy Markdown
Contributor

@knard38 knard38 commented May 6, 2026

Description

Fixes DAOS-18933 — the pre-commit clang-format hook crashes with a
PermissionError on any machine where the SCons WhereIs stub is active.

Problem

extra.py is used in two contexts:

  1. Inside a SCons build — where SCons.Script.WhereIs is the real implementation that searches PATH.
  2. Standalone, invoked by the pre-commit hook — where SCons replaces WhereIs with a no-op stub:
def WhereIs(path):
    """Fake WhereIs"""
    return ''

The previous guard only checked for None:

clang_exe = WhereIs('clang-format')
if clang_exe is None:   # '' slips through this check
    return None
subprocess.check_output([clang_exe, "-version"])  # runs ['', '-version'] → PermissionError

Because '' is not None, the guard was bypassed and subprocess.check_output
was called with an empty string as the executable, raising:

PermissionError: [Errno 13] Permission denied: ''

This hit every developer running git commit when the stub WhereIs was in effect.
The clang-format check was silently skipped, but the alarming traceback caused confusion.

Fix

  • Fall back to shutil.which('clang-format') (Python stdlib, no SCons dependency)
    when WhereIs returns a falsy value.
  • Replace the is None guard with a falsy guard (if not clang_exe) to cover both
    None and empty-string cases.
clang_exe = WhereIs('clang-format') or shutil.which('clang-format')
if not clang_exe:
    return None

shutil.which correctly searches os.environ['PATH'] in both contexts.

Changed files

File Change
site_scons/site_tools/extra/extra.py Add shutil import; use or shutil.which() fallback; replace is None with falsy guard

Steps for the author:

  • Commit message follows the guidelines.
  • Appropriate Features or Test-tag pragmas were used.
  • Appropriate Functional Test Stages were run.
  • At least two positive code reviews including at least one code owner from each category referenced in the PR.
  • Testing is complete. If necessary, forced-landing label added and a reason added in a comment.

After all prior steps are complete:

  • Gatekeeper requested (daos-gatekeeper added as a reviewer).

When extra.py is invoked standalone by the pre-commit hook, SCons
replaces WhereIs() with a stub that returns '' instead of None.
The previous guard (if clang_exe is None) let the empty string
through, causing subprocess.check_output(['', '-version']) to raise:

  PermissionError: [Errno 13] Permission denied: ''

Fix this by falling back to shutil.which() when WhereIs() returns a
falsy value, and replacing the None check with a falsy guard
(if not clang_exe) to cover both None and empty-string cases.

Signed-off-by: Cedric Koch-Hofer <cedric.koch-hofer@hpe.com>
@knard38 knard38 self-assigned this May 6, 2026
@knard38 knard38 marked this pull request as ready for review May 6, 2026 09:07
@knard38 knard38 requested review from a team as code owners May 6, 2026 09:07
@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 6, 2026

Ticket title is 'Pre-commit clang-format hook crashes with PermissionError'
Status is 'Open'
https://daosio.atlassian.net/browse/DAOS-18933

def _get_version_string():
clang_exe = WhereIs('clang-format')
if clang_exe is None:
clang_exe = WhereIs('clang-format') or shutil.which('clang-format')
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why you decided to use both? Why not just replace the unreliable one with a reliable one?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SCons.Script ships a no-op stub for WhereIs that is active whenever the module is imported outside a full SCons build (i.e. the pre-commit hook context).

On my machine:

>>> from SCons.Script import WhereIs; WhereIs('clang-format')
''                          # stub always returns ''

>>> import shutil; shutil.which('clang-format')
'/usr/bin/clang-format'     # stdlib finds it correctly

Replacing WhereIs entirely would fix the crash but lose the SCons-environment-aware lookup during actual builds. The or keeps WhereIs when it works and falls back to shutil.which when the stub is in effect.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is a little messy. 😅

Another thought. Why not use shutil.which('clang-format') in the WhereIs stub?

Copy link
Copy Markdown
Contributor Author

@knard38 knard38 May 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is a little messy. 😅

Another thought. Why not use shutil.which('clang-format') in the WhereIs stub?

Did not wanted to touch to the fake_scons stubs as I am not sure of their purpose.
I am going to investigate as I agree that it should be a better solution: I have to check that I am not breaking anything (test, etc.).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

3 participants