144 lines
3.4 KiB
Text
144 lines
3.4 KiB
Text
|
#!/usr/bin/env python3
|
||
|
|
||
|
import shlex
|
||
|
import subprocess
|
||
|
import sys
|
||
|
from shutil import which
|
||
|
|
||
|
|
||
|
def is_tool(name):
|
||
|
"""Check whether `name` is on PATH and marked as executable."""
|
||
|
if not which(name):
|
||
|
print(f"ERROR: {name} is not found in your PATH")
|
||
|
sys.exit(1)
|
||
|
|
||
|
|
||
|
def run(cmd, capture=True, returncode=False):
|
||
|
result = subprocess.run(
|
||
|
shlex.split(cmd),
|
||
|
stdout=subprocess.PIPE if capture else None,
|
||
|
text=True,
|
||
|
shell=False,
|
||
|
)
|
||
|
|
||
|
if returncode:
|
||
|
return result.returncode
|
||
|
elif capture:
|
||
|
return result.stdout.strip()
|
||
|
|
||
|
|
||
|
def confirm(q):
|
||
|
return run(
|
||
|
"gum confirm "
|
||
|
"--selected.bold "
|
||
|
"--selected.background 11 "
|
||
|
"--selected.foreground 8 "
|
||
|
f"'{q}'",
|
||
|
returncode=True,
|
||
|
)
|
||
|
|
||
|
|
||
|
# TODO: make gum a class an obsufcate the command commands
|
||
|
|
||
|
|
||
|
def color(text, color):
|
||
|
return run(f"gum style --foreground {color} '{text}'")
|
||
|
|
||
|
|
||
|
def nl_to_ol(text):
|
||
|
return " ".join([f"'{f}'" for f in text.splitlines()])
|
||
|
|
||
|
|
||
|
def check_status():
|
||
|
files = nl_to_ol(run("git ls-files -dmo"))
|
||
|
if not files:
|
||
|
print(color("nothing to stage", "red"))
|
||
|
sys.exit(1)
|
||
|
else:
|
||
|
return files
|
||
|
|
||
|
|
||
|
def stage_files(files):
|
||
|
to_add = run(f"gum choose --no-limit {files} ")
|
||
|
run(f"git add {to_add}")
|
||
|
|
||
|
|
||
|
def get_staged_info():
|
||
|
|
||
|
staged = run("git diff --cached --stat")
|
||
|
fstaged = []
|
||
|
if not staged:
|
||
|
files = check_status()
|
||
|
print(color("nothing staged..", 9))
|
||
|
if files and confirm("Would you like to stage files?") == 0:
|
||
|
stage_files(files)
|
||
|
return get_staged_info()
|
||
|
else:
|
||
|
print("exiting...")
|
||
|
sys.exit(0)
|
||
|
|
||
|
for text in staged.splitlines()[-1].split(","):
|
||
|
if "changed" in text:
|
||
|
fstaged.append(color(text.strip(), 11))
|
||
|
elif "insertion" in text:
|
||
|
fstaged.append(color(text.strip(), 10))
|
||
|
elif "deletion" in text:
|
||
|
fstaged.append(color(text.strip(), 9))
|
||
|
else:
|
||
|
print(f"Unexpected output from git diff: {text}")
|
||
|
|
||
|
return "\n\t".join(fstaged)
|
||
|
|
||
|
|
||
|
def main():
|
||
|
is_tool("gum")
|
||
|
is_tool("git")
|
||
|
|
||
|
run(
|
||
|
"gum style "
|
||
|
"--foreground 212 --border-foreground 212 --border rounded "
|
||
|
'--align center --width 30 --margin "1 4" '
|
||
|
f"'{color('ConCom',11)}'",
|
||
|
capture=False,
|
||
|
)
|
||
|
|
||
|
print(f" staged: \n\t{get_staged_info()}\n")
|
||
|
|
||
|
commit_type = run(
|
||
|
"gum choose fix feat docs style refactor test chore revert custom"
|
||
|
)
|
||
|
|
||
|
if not commit_type:
|
||
|
print("exiting...")
|
||
|
sys.exit(1)
|
||
|
|
||
|
elif commit_type == "custom":
|
||
|
commit_type = run('gum input --placeholder "type"')
|
||
|
|
||
|
scope = run('gum input --placeholder "scope"')
|
||
|
scope = f"({scope})" if scope else ""
|
||
|
|
||
|
summary = run(
|
||
|
f'gum input --value "{commit_type}{scope}: " --placeholder "Summary of this change"'
|
||
|
)
|
||
|
|
||
|
print(f"{color('message:',11)}\n {summary}")
|
||
|
|
||
|
actions = {
|
||
|
"commit": lambda: run(f"git commit -m '{summary}'", capture=False),
|
||
|
"commit(with message)": lambda: run(
|
||
|
f"git commit -m '{summary}' -m '# note: abort with :cq!' -e", capture=False
|
||
|
),
|
||
|
"quit": lambda: (print("exiting..."), sys.exit()),
|
||
|
}
|
||
|
|
||
|
print("\nHow to proceed?\n")
|
||
|
choices = " ".join([f"'{choice}'" for choice in actions])
|
||
|
choice = str(run(f"gum choose {choices}"))
|
||
|
|
||
|
actions[choice]()
|
||
|
|
||
|
|
||
|
if __name__ == "__main__":
|
||
|
main()
|