Browse Source

Add "run" command and refactor grep

Add a run command for running shell commands in projects and refactor
grep to use the same base class.
Doug Hellmann 4 years ago
parent
commit
820caf5555
4 changed files with 81 additions and 19 deletions
  1. 4
    9
      aeromancer/cli/grep.py
  2. 68
    0
      aeromancer/cli/run.py
  3. 8
    10
      aeromancer/project.py
  4. 1
    0
      setup.cfg

+ 4
- 9
aeromancer/cli/grep.py View File

@@ -6,26 +6,21 @@ import os
6 6
 from aeromancer import project
7 7
 from aeromancer import project_filter
8 8
 
9
-from cliff.command import Command
9
+from aeromancer.cli.run import ProjectShellCommandBase
10 10
 
11 11
 
12
-class Grep(Command):
12
+class Grep(ProjectShellCommandBase):
13 13
     """Search the contents of files"""
14 14
 
15 15
     log = logging.getLogger(__name__)
16 16
 
17 17
     def get_parser(self, prog_name):
18 18
         parser = super(Grep, self).get_parser(prog_name)
19
-        project_filter.ProjectFilter.add_arguments(parser)
20 19
         parser.add_argument('pattern',
21 20
                             action='store',
22 21
                             help='regular expression',
23 22
                             )
24 23
         return parser
25 24
 
26
-    def take_action(self, parsed_args):
27
-        session = self.app.get_db_session()
28
-        pm = project.ProjectManager(session)
29
-        prj_filt = project_filter.ProjectFilter.from_parsed_args(parsed_args)
30
-        for l in pm.grep(parsed_args.pattern, prj_filt):
31
-            print(l)
25
+    def _get_command(self, parsed_args):
26
+        return ['git', 'grep', parsed_args.pattern]

+ 68
- 0
aeromancer/cli/run.py View File

@@ -0,0 +1,68 @@
1
+from __future__ import print_function
2
+
3
+import logging
4
+import os
5
+import shlex
6
+
7
+from aeromancer import project
8
+from aeromancer import project_filter
9
+
10
+from cliff.command import Command
11
+
12
+
13
+class ProjectShellCommandBase(Command):
14
+    """Run a command for each project"""
15
+
16
+    log = logging.getLogger(__name__)
17
+
18
+    DEFAULT_SEP = ''
19
+
20
+    def get_parser(self, prog_name):
21
+        parser = super(ProjectShellCommandBase, self).get_parser(prog_name)
22
+        project_filter.ProjectFilter.add_arguments(parser)
23
+        parser.add_argument(
24
+            '--sep',
25
+            action='store',
26
+            default=self.DEFAULT_SEP,
27
+            help=('separator between project name and command output, '
28
+                  'defaults to %(default)r'),
29
+        )
30
+        return parser
31
+
32
+    def _show_text_output(self, parsed_args, project, out):
33
+        for line in out.decode('utf-8').splitlines():
34
+            print(project.name + parsed_args.sep + line)
35
+
36
+    def _get_command(self, parsed_args):
37
+        raise NotImplementedError()
38
+
39
+    def _show_output(self, parsed_args, proj_obj, out, err):
40
+        self._show_text_output(parsed_args, proj_obj, err or out)
41
+
42
+    def take_action(self, parsed_args):
43
+        session = self.app.get_db_session()
44
+        pm = project.ProjectManager(session)
45
+        prj_filt = project_filter.ProjectFilter.from_parsed_args(parsed_args)
46
+        command = self._get_command(parsed_args)
47
+        results = pm.run(command, prj_filt)
48
+        for proj_obj, out, err in results:
49
+            self._show_output(parsed_args, proj_obj, out, err)
50
+
51
+
52
+class Run(ProjectShellCommandBase):
53
+    """Run a command for each project"""
54
+
55
+    log = logging.getLogger(__name__)
56
+
57
+    DEFAULT_SEP = ':'
58
+
59
+    def get_parser(self, prog_name):
60
+        parser = super(Run, self).get_parser(prog_name)
61
+        parser.add_argument('command',
62
+                            action='store',
63
+                            help='the command to run, probably quoted',
64
+                            )
65
+        return parser
66
+
67
+    def _get_command(self, parsed_args):
68
+        return shlex.shlex(parsed_args.command)

+ 8
- 10
aeromancer/project.py View File

@@ -194,11 +194,11 @@ class ProjectManager(object):
194 194
                 self._remove_file_data(obj, reason='file no longer exists')
195 195
                 self.session.flush()
196 196
 
197
-    def grep(self, pattern, prj_filter):
198
-        """Given a pattern, search for lines in files in all projects that match it.
197
+    def run(self, command, prj_filter):
198
+        """Given a command, run it for all projects.
199 199
 
200
-        Returns results of the query, including the four columns line
201
-        number, line content, filename, and project name.
200
+        Returns sequence of tuples containing project objects, the
201
+        output, and the errors from the command.
202 202
 
203 203
         """
204 204
         # TODO: Would it be more efficient to register the regexp
@@ -212,13 +212,11 @@ class ProjectManager(object):
212 212
         #return query.yield_per(20).all()
213 213
         for project in query.all():
214 214
             cmd = subprocess.Popen(
215
-                ['git', 'grep', pattern],
215
+                command,
216 216
                 stdout=subprocess.PIPE,
217
+                stderr=subprocess.PIPE,
217 218
                 cwd=project.path,
218
-                env={'PAGER': ''},
219
+                env={'PAGER': ''},  # override pager for git commands
219 220
             )
220 221
             out, err = cmd.communicate()
221
-            if not out:
222
-                continue
223
-            for line in out.decode('utf-8').splitlines():
224
-                yield project.name + line
222
+            yield (project, out, err)

+ 1
- 0
setup.cfg View File

@@ -62,6 +62,7 @@ aeromancer.cli =
62 62
 	oslo list = aeromancer.oslo.cli:List
63 63
 	oslo uses = aeromancer.oslo.cli:Uses
64 64
 	grep = aeromancer.cli.grep:Grep
65
+	run = aeromancer.cli.run:Run
65 66
 
66 67
 aeromancer.filehandler =
67 68
     requirements = aeromancer.requirements.handler:RequirementsHandler

Loading…
Cancel
Save