I'm in the process of implementing a little pre-commit hook that calls gitleaks protect prior to every commit.
This works well in a terminal but when trying to commit from within VSCode, a non-descriptive "Git: O" is returned (I assume this is simply the first line of gitleaks, part of its ascii logo).
As you can tell, I've tried multiple ways to have VSCode's Git module return a proper message upon exit of the submodule. However, nothing seems to work in that regard.
def eprint(*args, **kwargs):
print(*args, file=sys.stderr, **kwargs)
exit_code = subprocess.run("gitleaks protect -v --staged -c gitleaks.toml",shell=True)
if exit_code.returncode == 1:
eprint("This is a test")
sys.exit("TEST")
How do I return an alert window in VSCode that displays a message whenever the subprocess is exiting with exit code 1?
EDIT:
Ok. This works somehow, but it fails in so far as that
subprocess.run("gitleaks version", shell=True, stdout=dev_null, stderr=dev_null) only works with my WSL Bash whereas subprocess.run("gitleaks version", stdout=dev_null, stderr=dev_null) (without the shell=True) only works for my VSCode with Windows Git Bash.
Any way to make this portable, so FileNotFoundError is correctly thrown on both systems?
#!/usr/bin/env python3
# pylint: disable=C0116,W0613
import sys
import warnings
import subprocess
dev_null = subprocess.DEVNULL
def eprint(*args, **kwargs):
print(*args, file=sys.stderr, **kwargs)
def gitleaks_installed():
try:
subprocess.run("gitleaks version", shell=True, stdout=dev_null, stderr=dev_null)
return True
except FileNotFoundError:
return False
if gitleaks_installed():
exit_code = subprocess.run("gitleaks protect -v --staged -c gitleaks.toml", shell=True, stdout=dev_null, stderr=dev_null)
if exit_code.returncode == 1:
eprint("gitleaks has detected sensitive information in your changes. Commit aborted.")
subprocess.run("gitleaks protect -v --staged -c gitleaks.toml", shell=True)
sys.exit(1)
else:
eprint("gitleaks is not installed or in the PATH.")
sys.exit(1)
EDIT2: NVM. The gitleaks_installed part doesn't work at all under WSL Bash. It either always True or always False, depending on whether I include shell=True.
Is there a better way to detect whether gitleaks is installed/in the PATH or not?
CodePudding user response:
Not the most elegant solution, but this works.
Instead of the FileNotFoundError exception, we're using the subprocess.run returncode.
#!/usr/bin/env python3
# pylint: disable=C0116,W0613
import sys
import subprocess
dev_null = subprocess.DEVNULL
def eprint(*args, **kwargs):
print(*args, file=sys.stderr, **kwargs)
def gitleaks_installed():
exit_code = subprocess.run("gitleaks version", shell=True, stdout=dev_null, stderr=dev_null)
if exit_code.returncode == 0:
return True
else:
return False
if gitleaks_installed():
exit_code = subprocess.run("gitleaks protect -v --staged -c gitleaks.toml", shell=True, stdout=dev_null, stderr=dev_null)
if exit_code.returncode == 1:
eprint("gitleaks has detected sensitive information in your changes. Commit aborted.")
subprocess.run("gitleaks protect -v --staged -c gitleaks.toml", shell=True)
sys.exit(1)
else:
eprint("gitleaks is not installed or in the PATH.")
sys.exit(1)
CodePudding user response:
The object returned by subprocess.run is a CompletedProcess object, not the return code. You have to access its .returncode attribute to examine what it returned.
However, you might instead add check=True to have Python throw an error on failure.
You should also almost certainly get rid of the superfluous shell=True by parsing the command line into tokens yourself.
try:
subprocess.run(
["gitleaks", "protect", "-v", "--staged", "-c", "gitleaks.toml"],
check=True)
except subprocess.CalledProcessError:
eprint("gitleaks has detected sensitive information in your changes. Commit aborted.")
sys.exit(1)
except FileNotFoundError:
eprint("gitleaks is not installed or in the PATH.")
sys.exit(1)
This also avoids running the process a second time just to obtain the output. If you want to force your own message to go on top, capture the output and print it after your message (add capture_output=True and text=True).
You might also want to replace eprint with logging.warn, and perhaps return different exit codes in the different error scenarios. (Bash conventionally returns 127 when it can't find a binary, but that's just Bash.)
Running subprocess.run on a string, instead of a list, without shell=True weirdly happens to work on Windows, but my recommendation would be to always avoid trying to exploit that for convenience. Perhaps see also Actual meaning of shell=True in subprocess
