feat: proper commands for git-all

This commit is contained in:
Michele Cereda
2023-09-23 20:10:07 +02:00
parent 817a015df6
commit 10216702b0
2 changed files with 72 additions and 35 deletions

View File

@@ -6,6 +6,7 @@
1. [Dictionaries](#dictionaries)
1. [F-strings](#f-strings)
1. [Logging](#logging)
1. [CLI helpers](#cli-helpers)
1. [Web servers](#web-servers)
1. [Flask](#flask)
1. [WSGI server](#wsgi-server)
@@ -17,7 +18,12 @@
## TL;DR
```py
# Declare a dictionary.
# Declare tuples.
# If only 1 element, it requires the ending ','.
(1, 'string')
('element',)
# Declare dictionaries.
{'spam': 2, 'ham': 1, 'eggs': 3}
dict(spam=2,ham=1,eggs=3)
dict([('spam',2),('ham',1),('eggs',3)])
@@ -28,13 +34,14 @@ F"{name.lower()} is funny."
# Make elements in a list unique.
# Keep the resulting list mutable.
# Sorts the elements (it is a set "feature").
unique_elements = list(set(redundant_elements))
```
## Dictionaries
```py
# Declare a dictionary.
# Declare dictionaries.
d = {'spam': 2, 'ham': 1, 'eggs': 3}
d = dict(spam=2,ham=1,eggs=3)
d = dict([('spam',2),('ham',1),('eggs',3)])
@@ -42,13 +49,13 @@ d = {x: x for x in range(5)}
d = {c.lower(): c + '!' for c in ['SPAM','EGGS','HAM']}
d = dict.fromkeys('abc',0)
# Change an element.
# Change elements.
d['ham'] = ['grill', 'bake', 'fry']
# Add a new element.
# Add new elements.
d['brunch'] = 'bacon'
# Delete an element.
# Delete elements.
del d['eggs']
d.pop('eggs')
@@ -96,6 +103,26 @@ logging.log(level, "{level} level message")
See [logging howto] and [logging library] for more information.
## CLI helpers
See [click]:
```py
import click
@click.command()
@click.option('--count', default=1, help='Number of greetings.')
@click.option('--name', prompt='Your name',
help='The person to greet.')
def hello(count, name):
"""Simple program that greets NAME for a total of COUNT times."""
for x in range(count):
click.echo(f"Hello {name}!")
if __name__ == '__main__':
hello()
```
## Web servers
### Flask
@@ -205,6 +232,7 @@ All the references in the [further readings] section, plus the following:
- [10 python one-liners for dictionaries]
- [Logging library]
- [Subprocess library]
- [Click]
<!--
References
@@ -228,6 +256,7 @@ All the references in the [further readings] section, plus the following:
[*args and **kwargs in python]: https://www.geeksforgeeks.org/args-kwargs-python/
[10 Python One-Liners for Dictionaries]: https://medium.com/codex/10-python-one-liners-for-dictionaries-d58754386a1d
[an intro to threading in python]: https://realpython.com/intro-to-python-threading/
[click]: https://click.palletsprojects.com/en/
[data types]: https://www.w3schools.com/python/python_datatypes.asp
[f-strings]: https://realpython.com/python-f-strings/
[flask at first run: do not use the development server in a production environment]: https://stackoverflow.com/questions/51025893/flask-at-first-run-do-not-use-the-development-server-in-a-production-environmen#54381386

View File

@@ -3,25 +3,21 @@
# Easy, quick & dirty solution to act upon multiple git repositories at once.
# TODO:
# - proper commands
# - use 'gitpython' instead of calling `git`
# - use 'gitpython' instead of calling `git`?
import click
import logging
import subprocess
from concurrent.futures import ThreadPoolExecutor
from os import cpu_count, getcwd, walk
from os.path import basename, dirname, isdir
from sys import argv
from os.path import basename, dirname
dry_run = False
log_level = logging.WARNING
root_directory = getcwd()
threads_count = cpu_count()
logging.basicConfig(level=log_level)
logging.basicConfig(level=logging.WARNING)
def git_command(directory, *args):
def git_command(directory, dry_run = False, *args):
logging.debug(f"thread for {directory}")
logging.debug(f"using args {args}")
@@ -36,36 +32,48 @@ def git_command(directory, *args):
if dry_run is False:
subprocess.call(command)
if __name__ == "__main__":
if "--debug" in argv:
logging.basicConfig(level=logging.DEBUG, force=True)
logging.warning("debug mode")
argv.remove("--debug")
@click.command()
@click.option('--debug', '-d', is_flag=True, default=False, help='Enable debug mode.')
@click.option('--dry-run', '-n', is_flag=True, default=False, help='Simulate actions.')
@click.argument('action')
@click.argument('root_directories', type=click.Path(exists=True, file_okay=False, resolve_path=True), nargs=-1)
def main(action, debug, dry_run, root_directories):
"""
Executes the Git action on all repositories found in the ROOT_DIRECTORIES.
if "--dry-run" in argv:
dry_run = True
logging.warning("dry-run mode")
argv.remove("--dry-run")
ACTION The git action to execute. Quoted if given with arguments.
ROOT_DIRECTORIES The directories to walk while looking for repositories.
"""
action = tuple(action.split(" "))
if len(root_directories) <= 0:
root_directories = (getcwd(),)
if debug:
logging.basicConfig(level=logging.DEBUG, force=True)
logging.warning("debug mode enabled")
if dry_run:
logging.warning("dry-run mode enabled")
logging.debug(f"using globals {globals()}")
logging.debug(f"using locals {locals()}")
logging.debug(f"using cli args {argv[1:]}")
repositories = []
for directory in root_directories:
logging.debug(f"starting from '{directory}'")
if isdir(argv[-1]):
root_directory = argv[-1]
git_args = argv[1:-1]
else:
git_args = argv[1:]
repositories_in_dir = set(dirname(dirpath) for dirpath, _, _ in walk(directory) if basename(dirpath) == '.git')
logging.debug(f"{directory} has repositories {', '.join(repositories_in_dir)}")
logging.debug(f"starting from {root_directory}")
logging.debug(f"using git args {git_args}")
repositories = set([dirname(dirpath) for dirpath, _, _ in walk(root_directory) if basename(dirpath) == ".git"])
logging.debug(f"found repositories {repositories}")
repositories.extend(repositories_in_dir)
repositories = set(repositories)
logging.debug(f"found repositories {', '.join(repositories)}")
logging.debug(f"creating threads")
with ThreadPoolExecutor(max_workers=threads_count) as executor:
for repository in repositories:
logging.debug(f"submitting thread for {repository}")
executor.submit(git_command, repository, *git_args)
executor.submit(git_command, repository, dry_run, *action)
if __name__ == "__main__":
main()