3
3
import subprocess # noqa: S404
4
4
import tempfile
5
5
import typing
6
- import logging
6
+ import pathlib
7
7
8
8
import flask
9
9
10
10
from lms .lmsdb import models
11
11
from lms .models import upload
12
12
from lms .utils import hashing
13
-
14
- _logger = logging .getLogger (__name__ )
13
+ from lms .utils .log import log
15
14
16
15
17
16
class _GitOperation (typing .NamedTuple ):
@@ -23,6 +22,9 @@ class _GitOperation(typing.NamedTuple):
23
22
24
23
25
24
class GitService :
25
+ _GIT_PROCESS_TIMEOUT = 20
26
+ _GIT_VALID_EXIT_CODE = 0
27
+
26
28
def __init__ (
27
29
self ,
28
30
user : models .User ,
@@ -40,19 +42,19 @@ def project_name(self) -> str:
40
42
return f'{ self ._exercise_id } -{ self ._user .id } '
41
43
42
44
@property
43
- def repository_folder (self ) -> str :
44
- return os . path . join (self ._base_repository_folder , self .project_name )
45
+ def repository_folder (self ) -> pathlib . Path :
46
+ return pathlib . Path (self ._base_repository_folder ) / self .project_name
45
47
46
48
def handle_operation (self ) -> flask .Response :
47
49
git_operation = self ._extract_git_operation ()
48
- git_repository_folder = os . path . join ( self .repository_folder , 'config' )
50
+ repository_folder = self .repository_folder / 'config'
49
51
50
- first_time_repository = not os . path . exists (git_repository_folder )
51
- if first_time_repository :
52
+ new_repository = not repository_folder . exists ()
53
+ if new_repository :
52
54
self ._initialize_bare_repository ()
53
55
54
56
if not git_operation .supported :
55
- raise EnvironmentError
57
+ raise OSError
56
58
57
59
data_out = self ._execute_git_operation (git_operation )
58
60
@@ -71,38 +73,42 @@ def handle_operation(self) -> flask.Response:
71
73
72
74
return self .build_response (data_out , git_operation )
73
75
74
- def _execute_git_operation (self , git_operation : _GitOperation ) -> bytes :
76
+ def _execute_command (
77
+ self ,
78
+ args : typing .List [str ],
79
+ cwd : typing .Union [str , pathlib .Path ],
80
+ proc_input : typing .Optional [bytes ] = None ,
81
+ ):
75
82
proc = subprocess .Popen ( # noqa: S603
76
- args = git_operation . service_command ,
83
+ args = args ,
77
84
stdin = subprocess .PIPE ,
78
85
stdout = subprocess .PIPE ,
79
86
stderr = subprocess .PIPE ,
80
- cwd = self . _base_repository_folder ,
87
+ cwd = cwd ,
81
88
)
82
- data_out , _ = proc .communicate (self ._request .data , 20 )
83
- if proc .wait () != 0 :
84
- _logger .error (
85
- 'Failed to execute command. stdout=%s\n stderr=%s' ,
86
- proc .stdout .read (), proc .stderr .read (),
89
+ data_out , _ = proc .communicate (proc_input , self ._GIT_PROCESS_TIMEOUT )
90
+ operation_failed = proc .wait () != self ._GIT_VALID_EXIT_CODE
91
+ if operation_failed :
92
+ log .error (
93
+ 'Failed to execute command %s. stdout=%s\n stderr=%s' ,
94
+ args , proc .stdout .read (), proc .stderr .read (),
87
95
)
88
- raise EnvironmentError
96
+ raise OSError
89
97
return data_out
90
98
99
+ def _execute_git_operation (self , git_operation : _GitOperation ) -> bytes :
100
+ return self ._execute_command (
101
+ args = git_operation .service_command ,
102
+ cwd = self ._base_repository_folder ,
103
+ proc_input = self ._request .data ,
104
+ )
105
+
91
106
def _initialize_bare_repository (self ) -> None :
92
107
os .makedirs (self .repository_folder , exist_ok = True )
93
- proc = subprocess . Popen ( # noqa: S603
108
+ self . _execute_command (
94
109
args = ['git' , 'init' , '--bare' ],
95
- stdin = subprocess .PIPE ,
96
- stdout = subprocess .PIPE ,
97
- stderr = subprocess .PIPE ,
98
110
cwd = self .repository_folder ,
99
111
)
100
- if proc .wait () != 0 :
101
- _logger .error (
102
- 'Failed to execute command. stdout=%s\n stderr=%s' ,
103
- proc .stdout .read (), proc .stderr .read (),
104
- )
105
- raise EnvironmentError
106
112
107
113
@staticmethod
108
114
def build_response (
@@ -167,7 +173,7 @@ def format_response_callback(response_bytes: bytes) -> bytes:
167
173
format_response = format_response_callback
168
174
169
175
else :
170
- _logger .error (
176
+ log .error (
171
177
'Failed to find the git command for route %s' ,
172
178
self ._request .path ,
173
179
)
@@ -182,33 +188,29 @@ def format_response_callback(response_bytes: bytes) -> bytes:
182
188
)
183
189
184
190
def _load_files_from_repository (self ) -> typing .List [upload .File ]:
191
+ """
192
+ Since the remote server is a git bare repository
193
+ we need to 'clone' the bare repository to resolve the files.
194
+ We are not changing the remote at any end - that is why we
195
+ don't care about git files here.
196
+ """
185
197
with tempfile .TemporaryDirectory () as tempdir :
186
- proc = subprocess . Popen ( # noqa: S603
198
+ self . _execute_command (
187
199
args = ['git' , 'clone' , self .repository_folder , '.' ],
188
- stdin = subprocess .PIPE ,
189
- stdout = subprocess .PIPE ,
190
- stderr = subprocess .PIPE ,
191
200
cwd = tempdir ,
192
201
)
193
- return_code = proc .wait ()
194
- if return_code != 0 :
195
- _logger .error (
196
- 'Failed to execute command. stdout=%s\n stderr=%s' ,
197
- proc .stdout .read (), proc .stderr .read (),
198
- )
199
- raise EnvironmentError
200
202
to_return = []
201
203
# remove git internal files
202
- shutil .rmtree (os . path . join (tempdir , '.git' ) )
204
+ shutil .rmtree (pathlib . Path (tempdir ) / '.git' )
203
205
for root , _ , files in os .walk (tempdir ):
204
206
for file in files :
205
207
upload_file = self ._load_file (file , root , tempdir )
206
208
to_return .append (upload_file )
207
209
return to_return
208
210
209
211
@staticmethod
210
- def _load_file (file_path : str , root : str , tempdir : str ) -> upload .File :
211
- with open ( os . path . join (root , file_path )) as f :
212
- file_path = os . path . join ( os . path . relpath (root , tempdir ), file_path )
212
+ def _load_file (file_name : str , root : str , tempdir : str ) -> upload .File :
213
+ file_path = str ( pathlib . Path (root ). relative_to ( tempdir ) / file_name )
214
+ with open ( pathlib . Path (root ) / file_name ) as f :
213
215
upload_file = upload .File (path = file_path , code = f .read ())
214
216
return upload_file
0 commit comments