Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

--command= argument is introduced to run complex commands. (Resolves #62) #64

Merged
merged 2 commits into from
Sep 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ All entries are stored in `.mudconfig` in TSV format. After making your first en
- `-nl=<label>` or `--not-label=<label>` - excludes repositories with provided label.
- `-b=<branch>` or `--branch=<branch>` - includes repositories with provided branch.
- `-nb=<branch>` or `--not-branch=<branch>` - excludes repositories with provided label.
- `-c` or `--command` - explicit command argument. Use this whenever you're trying to run a complex command.
- `-m` or `--modified` - filters out modified repositories.
- `-d` or `--diverged` - filters repositories with diverged branches.
- `-t` or `--table` - toggles default table view setting for run.
Expand Down
30 changes: 18 additions & 12 deletions app.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
class App:
def __init__(self):
self.cmd_runner = None
self.command = None
self.config = None
self.parser = self._create_parser()

Expand All @@ -42,6 +43,7 @@ def _create_parser() -> ArgumentParser:
remove_parser.add_argument('label', help='Label to remove from repository (optional).', nargs='?', default='', type=str)
remove_parser.add_argument('path', help='Repository to remove (optional).', nargs='?', type=str)

parser.add_argument(*COMMAND_ATTR, metavar='COMMAND', nargs='?', default='', type=str, help=f'Explicit command argument. Use this when you want to run a command that has a special characters.')
parser.add_argument(*TABLE_ATTR, metavar='TABLE', nargs='?', default='', type=str, help=f'Switches table view, runs in table view it is disabled in {BOLD}.mudsettings{RESET}.')
parser.add_argument(*LABEL_PREFIX, metavar='LABEL', nargs='?', default='', type=str, help='Includes repositories with provided label.')
parser.add_argument(*NOT_LABEL_PREFIX, metavar='NOT_LABEL', nargs='?', default='', type=str, help=f'Excludes repositories with provided label..')
Expand Down Expand Up @@ -122,20 +124,22 @@ def run(self) -> None:
# Handling subcommands
else:
del sys.argv[0]
if len(sys.argv) == 0:
self.parser.print_help()
return
self._parse_aliases()
if self.run_async:
try:
if self.command is None:
if len(sys.argv) == 0:
self.parser.print_help()
return
self.command = ' '.join(sys.argv)
self._parse_aliases()
try:
if self.run_async:
if self.table:
asyncio.run(self.cmd_runner.run_async_table_view(self.repos.keys(), sys.argv))
asyncio.run(self.cmd_runner.run_async_table_view(self.repos.keys(), self.command))
else:
asyncio.run(self.cmd_runner.run_async(self.repos.keys(), sys.argv))
except Exception as exception:
utils.print_error(f'Invalid command. {exception}', 2)
else:
self.cmd_runner.run_ordered(self.repos.keys(), sys.argv)
asyncio.run(self.cmd_runner.run_async(self.repos.keys(), self.command))
else:
self.cmd_runner.run_ordered(self.repos.keys(), self.command)
except Exception as exception:
utils.print_error(f'Invalid command. {exception}', 2)

def init(self, args) -> None:
table = utils.get_table()
Expand Down Expand Up @@ -202,6 +206,8 @@ def _filter_with_arguments(self) -> None:
self.table = not self.table
elif arg in ASYNC_ATTR:
self.run_async = not self.run_async
elif any(arg.startswith(prefix) for prefix in COMMAND_ATTR):
self.command = arg.split('=', 1)[1]
else:
index += 1
continue
Expand Down
1 change: 1 addition & 0 deletions commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
# Filters
ASYNC_ATTR = '-a', '--async'
TABLE_ATTR = '-t', '--table'
COMMAND_ATTR = '-c', '--command'
MODIFIED_ATTR = '-m', '--modified'
DIVERGED_ATTR = '-d', '--diverged'
LABEL_PREFIX = '-l=', '--label='
Expand Down
25 changes: 12 additions & 13 deletions runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -186,25 +186,24 @@ def tags(self, repos: Dict[str, List[str]]) -> None:
utils.print_table(table)

# `mud <COMMAND>` when run_async = 0 and run_table = 0
def run_ordered(self, repos: List[str], command: [str]) -> None:
command_str = ' '.join(command)
def run_ordered(self, repos: List[str], command: str) -> None:
for path in repos:
process = subprocess.run(command_str, shell=True, cwd=path, capture_output=True, text=True)
self._print_process_header(path, ' '.join(command), process.returncode != 0, process.returncode)
process = subprocess.run(command, shell=True, cwd=path, capture_output=True, text=True)
self._print_process_header(path, command, process.returncode != 0, process.returncode)
if process.stdout and not process.stdout.isspace():
print(process.stdout)
if process.stderr and not process.stderr.isspace():
print(process.stderr)

# `mud <COMMAND>` when run_async = 1 and run_table = 0
async def run_async(self, repos: List[str], command: List[str]) -> None:
async def run_async(self, repos: List[str], command: str) -> None:
sem = asyncio.Semaphore(len(repos))

async def run_process(path: str) -> None:
async with sem:
process = await asyncio.create_subprocess_exec(*command, cwd=path, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
stdout, stderr = await process.communicate()
self._print_process_header(path, ' '.join(command), process.returncode != 0, process.returncode)
self._print_process_header(path, command, process.returncode != 0, process.returncode)
if stderr:
print(stderr.decode())
if stdout and not stdout.isspace():
Expand All @@ -213,7 +212,7 @@ async def run_process(path: str) -> None:
await asyncio.gather(*(run_process(path) for path in repos))

# `mud <COMMAND>` when run_async = 1 and run_table = 1
async def run_async_table_view(self, repos: List[str], command: List[str]) -> None:
async def run_async_table_view(self, repos: List[str], command: str) -> None:
sem = asyncio.Semaphore(len(repos))
table = {repo: ['', ''] for repo in repos}

Expand All @@ -224,9 +223,9 @@ async def task(repo: str) -> None:
tasks = [asyncio.create_task(task(repo)) for repo in repos]
await asyncio.gather(*tasks)

async def _run_process(self, repo_path: str, table: Dict[str, List[str]], command: List[str]) -> None:
process = await asyncio.create_subprocess_exec(*command, cwd=repo_path, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
table[repo_path] = ['', f'{YELLOW}{glyphs("running")}{RESET}']
async def _run_process(self, path: str, table: Dict[str, List[str]], command: str) -> None:
process = await asyncio.create_subprocess_shell(command, cwd=path, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
table[path] = ['', f'{YELLOW}{glyphs("running")}{RESET}']

while True:
line = await process.stdout.readline()
Expand All @@ -235,8 +234,8 @@ async def _run_process(self, repo_path: str, table: Dict[str, List[str]], comman
if not line:
break
line = line.decode().strip()
line = table[repo_path][0] if not line.strip() else line
table[repo_path] = [line, f'{YELLOW}{glyphs("running")}{RESET}']
line = table[path][0] if not line.strip() else line
table[path] = [line, f'{YELLOW}{glyphs("running")}{RESET}']
self._print_process(table)

return_code = await process.wait()
Expand All @@ -246,7 +245,7 @@ async def _run_process(self, repo_path: str, table: Dict[str, List[str]], comman
else:
status = f'{RED}{glyphs("failed")} Code: {return_code}{RESET}'

table[repo_path] = [table[repo_path][0], status]
table[path] = [table[path][0], status]
self._print_process(table)

def _print_process(self, info: Dict[str, List[str]]) -> None:
Expand Down
Loading