batchfetch 1.0.4__tar.gz → 1.0.6__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {batchfetch-1.0.4/batchfetch.egg-info → batchfetch-1.0.6}/PKG-INFO +8 -11
- {batchfetch-1.0.4 → batchfetch-1.0.6}/README.md +7 -6
- {batchfetch-1.0.4 → batchfetch-1.0.6}/batchfetch/__init__.py +0 -5
- {batchfetch-1.0.4 → batchfetch-1.0.6}/batchfetch/batchfetch_base.py +5 -10
- {batchfetch-1.0.4 → batchfetch-1.0.6}/batchfetch/batchfetch_cli.py +115 -93
- {batchfetch-1.0.4 → batchfetch-1.0.6}/batchfetch/batchfetch_git.py +61 -44
- {batchfetch-1.0.4 → batchfetch-1.0.6}/batchfetch/helpers.py +4 -2
- {batchfetch-1.0.4 → batchfetch-1.0.6/batchfetch.egg-info}/PKG-INFO +8 -11
- {batchfetch-1.0.4 → batchfetch-1.0.6}/batchfetch.egg-info/SOURCES.txt +1 -6
- batchfetch-1.0.6/batchfetch.egg-info/entry_points.txt +2 -0
- {batchfetch-1.0.4 → batchfetch-1.0.6}/setup.py +2 -2
- batchfetch-1.0.4/.gitignore +0 -138
- batchfetch-1.0.4/batchfetch.egg-info/entry_points.txt +0 -2
- batchfetch-1.0.4/run_tests.sh +0 -23
- batchfetch-1.0.4/tests/data/test-md5sum.txt +0 -3
- batchfetch-1.0.4/tests/data/test-run_simple.sh +0 -5
- batchfetch-1.0.4/tests/test_helpers.py +0 -56
- {batchfetch-1.0.4 → batchfetch-1.0.6}/LICENSE +0 -0
- {batchfetch-1.0.4 → batchfetch-1.0.6}/batchfetch.egg-info/dependency_links.txt +0 -0
- {batchfetch-1.0.4 → batchfetch-1.0.6}/batchfetch.egg-info/requires.txt +1 -1
- {batchfetch-1.0.4 → batchfetch-1.0.6}/batchfetch.egg-info/top_level.txt +0 -0
- {batchfetch-1.0.4 → batchfetch-1.0.6}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: batchfetch
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.6
|
|
4
4
|
Summary: Efficiently clone and pull multiple Git repositories.
|
|
5
5
|
Home-page: https://github.com/jamescherti/batchfetch
|
|
6
6
|
Author: James Cherti
|
|
@@ -17,10 +17,6 @@ Classifier: Topic :: Utilities
|
|
|
17
17
|
Requires-Python: >=3.6, <4
|
|
18
18
|
Description-Content-Type: text/markdown
|
|
19
19
|
License-File: LICENSE
|
|
20
|
-
Requires-Dist: colorama
|
|
21
|
-
Requires-Dist: schema
|
|
22
|
-
Requires-Dist: setproctitle
|
|
23
|
-
Requires-Dist: PyYAML
|
|
24
20
|
|
|
25
21
|
# Batchfetch - Efficiently clone or pull multiple Git repositories in parallel
|
|
26
22
|
|
|
@@ -61,12 +57,12 @@ tasks:
|
|
|
61
57
|
# Clone the tag 1.5 of the consult repository to the './consult'
|
|
62
58
|
# directory
|
|
63
59
|
- git: https://github.com/minad/consult
|
|
64
|
-
|
|
60
|
+
reference: "1.5"
|
|
65
61
|
|
|
66
62
|
# Clone the s.el repository to the './another-name.el' directory
|
|
67
63
|
- git: https://github.com/magnars/s.el
|
|
68
64
|
path: another-name.el
|
|
69
|
-
|
|
65
|
+
reference: dda84d38fffdaf0c9b12837b504b402af910d01d
|
|
70
66
|
|
|
71
67
|
# Delete './impatient-mode'
|
|
72
68
|
- git: https://github.com/skeeto/impatient-mode
|
|
@@ -84,14 +80,15 @@ usage: batchfetch [--option] [args]
|
|
|
84
80
|
|
|
85
81
|
Command line interface.
|
|
86
82
|
|
|
83
|
+
positional arguments:
|
|
84
|
+
N Specify the batchfetch YAML file(s) (default: './batchfetch.yaml').
|
|
85
|
+
|
|
87
86
|
options:
|
|
88
87
|
-h, --help show this help message and exit
|
|
89
|
-
-j JOBS, --jobs JOBS
|
|
90
|
-
Run up to N Number of parallel git processes (Default: 5).
|
|
88
|
+
-j JOBS, --jobs JOBS Run up to N Number of parallel processes (Default: 5).
|
|
91
89
|
-v, --verbose Enable verbose mode.
|
|
92
|
-
-f BATCHFETCH_FILE, --batchfetch-file BATCHFETCH_FILE
|
|
93
|
-
Specify the batchfetch YAML file (default: './batchfetch.yaml').
|
|
94
90
|
```
|
|
91
|
+
|
|
95
92
|
## License
|
|
96
93
|
|
|
97
94
|
Copyright (c) [James Cherti](https://www.jamescherti.com)
|
|
@@ -37,12 +37,12 @@ tasks:
|
|
|
37
37
|
# Clone the tag 1.5 of the consult repository to the './consult'
|
|
38
38
|
# directory
|
|
39
39
|
- git: https://github.com/minad/consult
|
|
40
|
-
|
|
40
|
+
reference: "1.5"
|
|
41
41
|
|
|
42
42
|
# Clone the s.el repository to the './another-name.el' directory
|
|
43
43
|
- git: https://github.com/magnars/s.el
|
|
44
44
|
path: another-name.el
|
|
45
|
-
|
|
45
|
+
reference: dda84d38fffdaf0c9b12837b504b402af910d01d
|
|
46
46
|
|
|
47
47
|
# Delete './impatient-mode'
|
|
48
48
|
- git: https://github.com/skeeto/impatient-mode
|
|
@@ -60,14 +60,15 @@ usage: batchfetch [--option] [args]
|
|
|
60
60
|
|
|
61
61
|
Command line interface.
|
|
62
62
|
|
|
63
|
+
positional arguments:
|
|
64
|
+
N Specify the batchfetch YAML file(s) (default: './batchfetch.yaml').
|
|
65
|
+
|
|
63
66
|
options:
|
|
64
67
|
-h, --help show this help message and exit
|
|
65
|
-
-j JOBS, --jobs JOBS
|
|
66
|
-
Run up to N Number of parallel git processes (Default: 5).
|
|
68
|
+
-j JOBS, --jobs JOBS Run up to N Number of parallel processes (Default: 5).
|
|
67
69
|
-v, --verbose Enable verbose mode.
|
|
68
|
-
-f BATCHFETCH_FILE, --batchfetch-file BATCHFETCH_FILE
|
|
69
|
-
Specify the batchfetch YAML file (default: './batchfetch.yaml').
|
|
70
70
|
```
|
|
71
|
+
|
|
71
72
|
## License
|
|
72
73
|
|
|
73
74
|
Copyright (c) [James Cherti](https://www.jamescherti.com)
|
|
@@ -10,7 +10,8 @@
|
|
|
10
10
|
#
|
|
11
11
|
# This program is distributed in the hope that it will be useful, but WITHOUT
|
|
12
12
|
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
|
13
|
-
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
|
13
|
+
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
|
14
|
+
# details.
|
|
14
15
|
#
|
|
15
16
|
# You should have received a copy of the GNU General Public License along with
|
|
16
17
|
# this program. If not, see <https://www.gnu.org/licenses/>.
|
|
@@ -31,19 +32,13 @@ class BatchFetchError(Exception):
|
|
|
31
32
|
"""Exception raised by Downloader()."""
|
|
32
33
|
|
|
33
34
|
|
|
34
|
-
class GitBranchDoesNotExist(Exception):
|
|
35
|
-
"""Exception raised by Name()."""
|
|
36
|
-
|
|
37
|
-
|
|
38
35
|
class BatchFetchBase:
|
|
39
36
|
"""Plugin downloader base class."""
|
|
40
37
|
|
|
41
|
-
env = os.environ.copy()
|
|
42
|
-
env["GIT_TERMINAL_PROMPT"] = "0"
|
|
43
|
-
|
|
44
|
-
indent = 4
|
|
45
|
-
|
|
46
38
|
def __init__(self, data: Dict[str, Any], options: Dict[str, Any]):
|
|
39
|
+
self.indent = 4
|
|
40
|
+
self.env = os.environ.copy()
|
|
41
|
+
|
|
47
42
|
# Default
|
|
48
43
|
self.global_options_schema: Dict[Any, Any] = {
|
|
49
44
|
Optional("pre_exec"): Or([str], str),
|
|
@@ -10,7 +10,8 @@
|
|
|
10
10
|
#
|
|
11
11
|
# This program is distributed in the hope that it will be useful, but WITHOUT
|
|
12
12
|
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
|
13
|
-
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
|
13
|
+
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
|
14
|
+
# details.
|
|
14
15
|
#
|
|
15
16
|
# You should have received a copy of the GNU General Public License along with
|
|
16
17
|
# this program. If not, see <https://www.gnu.org/licenses/>.
|
|
@@ -24,7 +25,7 @@ import subprocess
|
|
|
24
25
|
import sys
|
|
25
26
|
from concurrent.futures import ThreadPoolExecutor, as_completed
|
|
26
27
|
from pathlib import Path
|
|
27
|
-
from typing import Set
|
|
28
|
+
from typing import Any, Dict, Set
|
|
28
29
|
|
|
29
30
|
import colorama
|
|
30
31
|
import yaml # type: ignore
|
|
@@ -32,31 +33,13 @@ from colorama import Fore
|
|
|
32
33
|
from schema import Optional, Or, Schema, SchemaError
|
|
33
34
|
from setproctitle import setproctitle
|
|
34
35
|
|
|
35
|
-
from .batchfetch_base import BatchFetchError
|
|
36
|
+
from .batchfetch_base import BatchFetchBase, BatchFetchError
|
|
36
37
|
from .batchfetch_git import BatchFetchGit
|
|
37
38
|
|
|
38
39
|
|
|
39
40
|
class BatchFetchCli:
|
|
40
41
|
"""Command-line-interface that downloads."""
|
|
41
42
|
|
|
42
|
-
main_key = "git"
|
|
43
|
-
|
|
44
|
-
# TODO: Make this automatic
|
|
45
|
-
empty_downloader_git = BatchFetchGit(
|
|
46
|
-
data={main_key: "http://www.domain.com"},
|
|
47
|
-
options={},
|
|
48
|
-
)
|
|
49
|
-
empty_downloader_git.validate_schema() # Gen structure
|
|
50
|
-
|
|
51
|
-
cfg_schema = {
|
|
52
|
-
Optional("options"):
|
|
53
|
-
empty_downloader_git.global_options_schema, # type: ignore
|
|
54
|
-
# TODO: Make this added automatically
|
|
55
|
-
Optional("tasks"): [
|
|
56
|
-
Or(str, empty_downloader_git.item_schema)
|
|
57
|
-
]
|
|
58
|
-
}
|
|
59
|
-
|
|
60
43
|
def __init__(self, max_workers: int, verbose: bool = False):
|
|
61
44
|
self.cfg: dict = {}
|
|
62
45
|
self.folder = Path(".")
|
|
@@ -66,16 +49,55 @@ class BatchFetchCli:
|
|
|
66
49
|
self._logger = logging.getLogger(self.__class__.__name__)
|
|
67
50
|
self.dirs_relative_to_batchfetch: Set[str] = set()
|
|
68
51
|
|
|
52
|
+
# Plugin
|
|
53
|
+
self.batchfetch_schemas: Dict[Any, Any] = {}
|
|
54
|
+
self.batchfetch_classes: Dict[str, BatchFetchBase] = {}
|
|
55
|
+
self.cfg_schema: Dict[Any, Any] = {}
|
|
56
|
+
self._plugin_add("git", BatchFetchGit)
|
|
57
|
+
|
|
58
|
+
def _plugin_add(self, keyword: str, batchfetch_class: Any):
|
|
59
|
+
batchfetch_instance = batchfetch_class(data={},
|
|
60
|
+
options={})
|
|
61
|
+
batchfetch_instance.validate_schema()
|
|
62
|
+
|
|
63
|
+
self.batchfetch_schemas[keyword] = batchfetch_instance.item_schema
|
|
64
|
+
self.batchfetch_classes[keyword] = batchfetch_class
|
|
65
|
+
self.cfg_schema = {
|
|
66
|
+
Optional("options"):
|
|
67
|
+
batchfetch_instance.global_options_schema, # type: ignore
|
|
68
|
+
Optional("tasks"): [
|
|
69
|
+
Or(*list(self.batchfetch_schemas.values()))
|
|
70
|
+
]
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
def _plugin_get(self, raw_data: dict) -> str:
|
|
74
|
+
keyword_found = None
|
|
75
|
+
for keyword in self.batchfetch_classes:
|
|
76
|
+
if keyword in raw_data:
|
|
77
|
+
if keyword_found is not None:
|
|
78
|
+
err_str = (f"The keywords {keyword} and {keyword_found} "
|
|
79
|
+
f"are mutually exclusive. Error in: {raw_data}")
|
|
80
|
+
raise BatchFetchError(err_str)
|
|
81
|
+
keyword_found = keyword
|
|
82
|
+
|
|
83
|
+
if not keyword_found:
|
|
84
|
+
err_str = "None of the keywords " + \
|
|
85
|
+
", ".join(self.batchfetch_classes.keys()) + \
|
|
86
|
+
f" have been found in: {raw_data}"
|
|
87
|
+
raise BatchFetchError(err_str)
|
|
88
|
+
|
|
89
|
+
return keyword_found
|
|
90
|
+
|
|
69
91
|
def load(self, path: Path):
|
|
70
92
|
try:
|
|
71
93
|
with open(path, "r", encoding="utf-8") as fhandler:
|
|
72
94
|
yaml_dict = yaml.load(fhandler, Loader=yaml.FullLoader)
|
|
73
|
-
self.
|
|
95
|
+
self._loads(dict(yaml_dict))
|
|
74
96
|
except OSError as err:
|
|
75
97
|
raise BatchFetchError(str(err)) from err
|
|
76
98
|
|
|
77
|
-
def
|
|
78
|
-
schema = Schema(
|
|
99
|
+
def _loads(self, data: dict):
|
|
100
|
+
schema = Schema(self.cfg_schema)
|
|
79
101
|
try:
|
|
80
102
|
schema.validate(data)
|
|
81
103
|
except SchemaError as err:
|
|
@@ -84,47 +106,45 @@ class BatchFetchCli:
|
|
|
84
106
|
|
|
85
107
|
self.cfg = {
|
|
86
108
|
"options": {"clone_args": []},
|
|
87
|
-
# TODO: Make this added automatically
|
|
88
109
|
"tasks": [],
|
|
89
110
|
}
|
|
90
111
|
|
|
91
112
|
if "options" in data:
|
|
92
113
|
self.cfg["options"].update(data["options"])
|
|
93
114
|
|
|
94
|
-
|
|
95
|
-
self._loads_git(data)
|
|
115
|
+
self._loads_tasks(data)
|
|
96
116
|
|
|
97
|
-
def
|
|
117
|
+
def _loads_tasks(self, data: dict):
|
|
98
118
|
if "tasks" not in data:
|
|
99
119
|
return
|
|
100
120
|
|
|
101
121
|
dict_local_dir = {} # type: ignore
|
|
102
|
-
for
|
|
103
|
-
|
|
104
|
-
|
|
122
|
+
for task in data["tasks"]:
|
|
123
|
+
keyword = self._plugin_get(task)
|
|
124
|
+
batchfetch_class = self.batchfetch_classes[keyword]
|
|
105
125
|
|
|
106
126
|
try:
|
|
107
|
-
|
|
108
|
-
data=
|
|
127
|
+
batchfetch_instance = batchfetch_class( # type: ignore
|
|
128
|
+
data=task,
|
|
109
129
|
options=self.cfg["options"],
|
|
110
130
|
)
|
|
111
|
-
self.cfg["tasks"].append(
|
|
131
|
+
self.cfg["tasks"].append(batchfetch_instance)
|
|
112
132
|
except SchemaError as err:
|
|
113
133
|
print(f"Schema error: {err}.", file=sys.stderr)
|
|
114
134
|
sys.exit(1)
|
|
115
135
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
str(
|
|
121
|
-
str(dict_local_dir[
|
|
136
|
+
dest_path = Path(batchfetch_instance["path"]).resolve()
|
|
137
|
+
if str(dest_path) in dict_local_dir:
|
|
138
|
+
err_str = ("More than one task have the " +
|
|
139
|
+
f"destination path '{dest_path}' (" +
|
|
140
|
+
str(task[keyword]) + " and " +
|
|
141
|
+
str(dict_local_dir[(str(dest_path))]) +
|
|
122
142
|
")")
|
|
123
143
|
raise BatchFetchError(err_str)
|
|
124
|
-
dict_local_dir[plugin_downloader_git["path"]] = \
|
|
125
|
-
plugin_downloader_git[BatchFetchCli.main_key]
|
|
126
144
|
|
|
127
|
-
|
|
145
|
+
dict_local_dir[str(dest_path)] = batchfetch_instance[keyword]
|
|
146
|
+
|
|
147
|
+
def run_tasks(self) -> bool:
|
|
128
148
|
failed = []
|
|
129
149
|
error = False
|
|
130
150
|
threads = []
|
|
@@ -136,13 +156,12 @@ class BatchFetchCli:
|
|
|
136
156
|
try:
|
|
137
157
|
self.dirs_relative_to_batchfetch = set()
|
|
138
158
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
threads.append(executor_update.submit(download_item.update))
|
|
159
|
+
all_tasks = self.cfg["tasks"]
|
|
160
|
+
for task in all_tasks:
|
|
161
|
+
self.dirs_relative_to_batchfetch.add(str(task["path"]))
|
|
162
|
+
if not task["delete"]:
|
|
163
|
+
self.managed_filenames.add(task["path"])
|
|
164
|
+
threads.append(executor_update.submit(task.update))
|
|
146
165
|
|
|
147
166
|
for future in as_completed(threads):
|
|
148
167
|
data = future.result()
|
|
@@ -174,29 +193,20 @@ class BatchFetchCli:
|
|
|
174
193
|
if error:
|
|
175
194
|
if failed:
|
|
176
195
|
print("Failed:")
|
|
177
|
-
for
|
|
178
|
-
print(" -
|
|
179
|
-
print(" dir:", git_update_result["path"])
|
|
196
|
+
for failed_result in failed:
|
|
197
|
+
print(" -", failed_result["path"])
|
|
180
198
|
else:
|
|
181
199
|
print("Failed.")
|
|
182
200
|
|
|
183
201
|
return False
|
|
184
202
|
else:
|
|
185
203
|
if num_success == 0:
|
|
186
|
-
print("
|
|
204
|
+
print("Nothing to do.")
|
|
187
205
|
elif not self.verbose:
|
|
188
206
|
print("Success.")
|
|
189
207
|
|
|
190
208
|
return True
|
|
191
209
|
|
|
192
|
-
# def check_managed_directories(self):
|
|
193
|
-
# # managed_downloads = {Path(item).resolve()
|
|
194
|
-
# # for item in self.managed_filenames}
|
|
195
|
-
# # managed_directories = {item.parent
|
|
196
|
-
# # for item in managed_downloads}
|
|
197
|
-
# # TODO Implement checker
|
|
198
|
-
# pass
|
|
199
|
-
|
|
200
210
|
|
|
201
211
|
def parse_args():
|
|
202
212
|
"""Parse the command line arguments."""
|
|
@@ -204,9 +214,13 @@ def parse_args():
|
|
|
204
214
|
usage = "%(prog)s [--option] [args]"
|
|
205
215
|
parser = argparse.ArgumentParser(description=desc, usage=usage)
|
|
206
216
|
|
|
217
|
+
parser.add_argument("batchfetch_files", metavar="N", type=str, nargs="*",
|
|
218
|
+
help=("Specify the batchfetch YAML file(s) "
|
|
219
|
+
"(default: './batchfetch.yaml')."))
|
|
220
|
+
|
|
207
221
|
parser.add_argument(
|
|
208
222
|
"-j", "--jobs", default="5", required=False,
|
|
209
|
-
help="Run up to N Number of parallel
|
|
223
|
+
help="Run up to N Number of parallel processes (Default: 5).",
|
|
210
224
|
)
|
|
211
225
|
|
|
212
226
|
parser.add_argument(
|
|
@@ -214,43 +228,26 @@ def parse_args():
|
|
|
214
228
|
help="Enable verbose mode.",
|
|
215
229
|
)
|
|
216
230
|
|
|
217
|
-
parser.add_argument(
|
|
218
|
-
"-f",
|
|
219
|
-
"--batchfetch-file",
|
|
220
|
-
default="./batchfetch.yaml",
|
|
221
|
-
required=False,
|
|
222
|
-
help="Specify the batchfetch YAML file (default: './batchfetch.yaml').",
|
|
223
|
-
)
|
|
224
|
-
|
|
225
231
|
args = parser.parse_args()
|
|
226
232
|
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
233
|
+
for batchfetch_file in args.batchfetch_files:
|
|
234
|
+
if not Path(batchfetch_file).is_file():
|
|
235
|
+
print(f"Error: File not found: {batchfetch_file}",
|
|
236
|
+
file=sys.stderr)
|
|
237
|
+
sys.exit(1)
|
|
231
238
|
|
|
232
239
|
return args
|
|
233
240
|
|
|
234
241
|
|
|
235
|
-
def
|
|
236
|
-
"""Command line interface."""
|
|
242
|
+
def run_batchfetch_procedure(batchfetch_file: Path, args) -> int:
|
|
237
243
|
errno = 0
|
|
238
|
-
|
|
239
|
-
format="%(asctime)s %(name)s: %(message)s")
|
|
240
|
-
|
|
241
|
-
colorama.init()
|
|
242
|
-
setproctitle(subprocess.list2cmdline([Path(sys.argv[0]).name] +
|
|
243
|
-
sys.argv[1:]))
|
|
244
|
-
|
|
245
|
-
args = parse_args()
|
|
246
|
-
downloader_cli = BatchFetchCli(verbose=args.verbose,
|
|
244
|
+
batchfetch_cli = BatchFetchCli(verbose=args.verbose,
|
|
247
245
|
max_workers=int(args.jobs))
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
os.chdir(os.path.dirname(args.batchfetch_file))
|
|
246
|
+
batchfetch_cli.load(batchfetch_file)
|
|
247
|
+
os.chdir(batchfetch_file.parent)
|
|
251
248
|
|
|
252
249
|
try:
|
|
253
|
-
if not
|
|
250
|
+
if not batchfetch_cli.run_tasks():
|
|
254
251
|
errno = 1
|
|
255
252
|
except KeyboardInterrupt:
|
|
256
253
|
print("Interrupted.", file=sys.stderr)
|
|
@@ -258,7 +255,32 @@ def command_line_interface():
|
|
|
258
255
|
except BatchFetchError as err:
|
|
259
256
|
print(f"Error: {err}.", file=sys.stderr)
|
|
260
257
|
errno = 1
|
|
261
|
-
# else:
|
|
262
|
-
# downloader_cli.check_managed_directories()
|
|
263
258
|
|
|
264
|
-
|
|
259
|
+
return errno
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
def command_line_interface():
|
|
263
|
+
"""Command line interface."""
|
|
264
|
+
try:
|
|
265
|
+
errno = 0
|
|
266
|
+
logging.basicConfig(level=logging.INFO, stream=sys.stdout,
|
|
267
|
+
format="%(asctime)s %(name)s: %(message)s")
|
|
268
|
+
|
|
269
|
+
colorama.init()
|
|
270
|
+
setproctitle(subprocess.list2cmdline([Path(sys.argv[0]).name] +
|
|
271
|
+
sys.argv[1:]))
|
|
272
|
+
|
|
273
|
+
args = parse_args()
|
|
274
|
+
done = []
|
|
275
|
+
for batchfetch_file in args.batchfetch_files:
|
|
276
|
+
batchfetch_file = Path(batchfetch_file)
|
|
277
|
+
batchfetch_file_resolved = batchfetch_file.resolve()
|
|
278
|
+
if batchfetch_file_resolved in done:
|
|
279
|
+
continue
|
|
280
|
+
|
|
281
|
+
done.append(batchfetch_file_resolved)
|
|
282
|
+
errno |= run_batchfetch_procedure(batchfetch_file, args)
|
|
283
|
+
|
|
284
|
+
sys.exit(errno)
|
|
285
|
+
except BrokenPipeError:
|
|
286
|
+
pass
|
|
@@ -10,7 +10,8 @@
|
|
|
10
10
|
#
|
|
11
11
|
# This program is distributed in the hope that it will be useful, but WITHOUT
|
|
12
12
|
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
|
13
|
-
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
|
13
|
+
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
|
14
|
+
# details.
|
|
14
15
|
#
|
|
15
16
|
# You should have received a copy of the GNU General Public License along with
|
|
16
17
|
# this program. If not, see <https://www.gnu.org/licenses/>.
|
|
@@ -27,25 +28,28 @@ from typing import List, Union
|
|
|
27
28
|
|
|
28
29
|
from schema import Optional
|
|
29
30
|
|
|
30
|
-
from .batchfetch_base import
|
|
31
|
-
GitBranchDoesNotExist)
|
|
31
|
+
from .batchfetch_base import BatchFetchBase, BatchFetchError
|
|
32
32
|
from .helpers import run_simple
|
|
33
33
|
|
|
34
34
|
|
|
35
|
+
class GitReferenceDoesNotExist(Exception):
|
|
36
|
+
"""Exception raised by Name()."""
|
|
37
|
+
|
|
38
|
+
|
|
35
39
|
class BatchFetchGit(BatchFetchBase):
|
|
36
40
|
"""Clone or update a Git repository."""
|
|
37
41
|
|
|
38
|
-
indent_spaces = " " * BatchFetchBase.indent
|
|
39
|
-
main_key = "git"
|
|
40
|
-
|
|
41
42
|
def __init__(self, *args, **kwargs):
|
|
42
43
|
super().__init__(*args, **kwargs)
|
|
44
|
+
self.env["GIT_TERMINAL_PROMPT"] = "0"
|
|
45
|
+
self.indent_spaces = " " * self.indent
|
|
46
|
+
self.main_key = "git"
|
|
43
47
|
|
|
44
48
|
# Schema
|
|
45
49
|
self.item_schema.update({
|
|
46
50
|
# Local options
|
|
47
|
-
|
|
48
|
-
Optional("
|
|
51
|
+
self.main_key: str,
|
|
52
|
+
Optional("reference"): str,
|
|
49
53
|
|
|
50
54
|
# Same as global options
|
|
51
55
|
Optional("clone_args"): [str],
|
|
@@ -63,8 +67,8 @@ class BatchFetchGit(BatchFetchBase):
|
|
|
63
67
|
"git_pull": True})
|
|
64
68
|
|
|
65
69
|
self.item_default_values.update({
|
|
66
|
-
|
|
67
|
-
"
|
|
70
|
+
self.main_key: "",
|
|
71
|
+
"reference": "",
|
|
68
72
|
"delete": False,
|
|
69
73
|
})
|
|
70
74
|
|
|
@@ -75,16 +79,19 @@ class BatchFetchGit(BatchFetchBase):
|
|
|
75
79
|
def _initialize_data(self):
|
|
76
80
|
super()._initialize_data()
|
|
77
81
|
|
|
78
|
-
self.values[
|
|
79
|
-
self.values[
|
|
82
|
+
self.values[self.main_key] = \
|
|
83
|
+
self.values[self.main_key].rstrip("/")
|
|
80
84
|
|
|
81
85
|
if "path" not in self.values:
|
|
82
86
|
self.values["path"] = \
|
|
83
87
|
posixpath.basename(
|
|
84
|
-
self.values[
|
|
88
|
+
self.values[self.main_key]) # type: ignore
|
|
85
89
|
|
|
86
90
|
def _git_ref(self, cwd: Union[None, Path] = None) -> str:
|
|
87
|
-
"""Get the commit reference of HEAD.
|
|
91
|
+
"""Get the commit reference of HEAD.
|
|
92
|
+
|
|
93
|
+
The command will fail if the branch is detached.
|
|
94
|
+
"""
|
|
88
95
|
cmd = "git show-ref --head --verify HEAD"
|
|
89
96
|
try:
|
|
90
97
|
stdout, _ = run_simple(cmd, cwd=cwd, env=self.env)
|
|
@@ -112,8 +119,8 @@ class BatchFetchGit(BatchFetchBase):
|
|
|
112
119
|
|
|
113
120
|
self.add_output(
|
|
114
121
|
f"[GIT {update_type}] {self[self.main_key]}" +
|
|
115
|
-
(f" (
|
|
116
|
-
if self["
|
|
122
|
+
(f" (Reference: {self['reference']})"
|
|
123
|
+
if self["reference"] else "") + "\n"
|
|
117
124
|
)
|
|
118
125
|
|
|
119
126
|
try:
|
|
@@ -152,7 +159,7 @@ class BatchFetchGit(BatchFetchBase):
|
|
|
152
159
|
textwrap.indent(err.stderr, " " * self.indent))
|
|
153
160
|
|
|
154
161
|
if not self.is_error() and not self.is_changed():
|
|
155
|
-
self.add_output(self.indent_spaces + "
|
|
162
|
+
self.add_output(self.indent_spaces + "[INFO] Nothing to do.\n")
|
|
156
163
|
|
|
157
164
|
return self.values
|
|
158
165
|
|
|
@@ -169,7 +176,7 @@ class BatchFetchGit(BatchFetchBase):
|
|
|
169
176
|
|
|
170
177
|
def _repo_delete(self):
|
|
171
178
|
if not self.git_local_dir.exists():
|
|
172
|
-
self.add_output(self.indent_spaces + "
|
|
179
|
+
self.add_output(self.indent_spaces + "[INFO] Already deleted\n")
|
|
173
180
|
elif not self.git_local_dir.joinpath(".git").is_dir():
|
|
174
181
|
self.add_output(
|
|
175
182
|
self.indent_spaces +
|
|
@@ -180,7 +187,7 @@ class BatchFetchGit(BatchFetchBase):
|
|
|
180
187
|
elif self.git_local_dir.is_dir():
|
|
181
188
|
shutil.rmtree(str(self.git_local_dir))
|
|
182
189
|
self.add_output(self.indent_spaces +
|
|
183
|
-
f"
|
|
190
|
+
f"[INFO] Deleted: '{self.git_local_dir}'")
|
|
184
191
|
self.set_changed(True)
|
|
185
192
|
|
|
186
193
|
def _repo_clone(self):
|
|
@@ -188,7 +195,7 @@ class BatchFetchGit(BatchFetchBase):
|
|
|
188
195
|
git_clone_args += ["--recurse-submodules"]
|
|
189
196
|
|
|
190
197
|
cmd = ["git", "clone"] + git_clone_args + \
|
|
191
|
-
[self[
|
|
198
|
+
[self[self.main_key], str(self.git_local_dir)]
|
|
192
199
|
self._run(cmd, env=self.env)
|
|
193
200
|
self.set_changed(True)
|
|
194
201
|
|
|
@@ -204,28 +211,37 @@ class BatchFetchGit(BatchFetchBase):
|
|
|
204
211
|
ignore_git_pull = False
|
|
205
212
|
if not self["git_pull"]:
|
|
206
213
|
ignore_git_pull = True
|
|
207
|
-
|
|
214
|
+
|
|
215
|
+
if self["reference"]:
|
|
216
|
+
ignore_git_pull = False
|
|
208
217
|
# Check if the new branch exists
|
|
209
218
|
try:
|
|
210
|
-
self._git_tags(self["
|
|
211
|
-
except
|
|
219
|
+
self._git_tags(self["reference"])
|
|
220
|
+
except GitReferenceDoesNotExist:
|
|
212
221
|
pass
|
|
213
222
|
else:
|
|
214
|
-
# The branch exists
|
|
215
|
-
|
|
216
|
-
#
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
223
|
+
# The branch exists:
|
|
224
|
+
# 1. Ignore Git pull when the git reference is the same
|
|
225
|
+
# as the "branch:" key
|
|
226
|
+
try:
|
|
227
|
+
commit_ref = self._git_ref(cwd=self.git_local_dir)
|
|
228
|
+
except subprocess.CalledProcessError:
|
|
229
|
+
# Ignore git pull because the head is detached
|
|
230
|
+
pass
|
|
231
|
+
else:
|
|
232
|
+
if (self.current_branch and
|
|
233
|
+
(commit_ref == self["reference"] or
|
|
234
|
+
self.current_branch == self["reference"])):
|
|
235
|
+
ignore_git_pull = True
|
|
236
|
+
|
|
237
|
+
# 2. Ignore Git pull if it is not a local branch
|
|
238
|
+
if (not ignore_git_pull and
|
|
239
|
+
not self._git_is_local_branch(self["reference"])):
|
|
224
240
|
ignore_git_pull = True
|
|
225
241
|
|
|
226
242
|
if ignore_git_pull:
|
|
227
243
|
self.add_output(self.indent_spaces +
|
|
228
|
-
"
|
|
244
|
+
"[INFO] git pull ignored\n")
|
|
229
245
|
else:
|
|
230
246
|
cmd = ["git", "fetch", "origin"]
|
|
231
247
|
self._run(cmd, cwd=str(self.git_local_dir), env=self.env)
|
|
@@ -272,15 +288,15 @@ class BatchFetchGit(BatchFetchBase):
|
|
|
272
288
|
env=self.env,
|
|
273
289
|
cwd=self.git_local_dir)
|
|
274
290
|
except subprocess.CalledProcessError as err:
|
|
275
|
-
raise
|
|
276
|
-
f"The
|
|
291
|
+
raise GitReferenceDoesNotExist(
|
|
292
|
+
f"The reference '{branch}' does not exist.") from err
|
|
277
293
|
|
|
278
294
|
return stdout
|
|
279
295
|
|
|
280
296
|
def _repo_fix_branch(self) -> bool:
|
|
281
297
|
git_ref_after_merge = self._git_ref(cwd=self.git_local_dir)
|
|
282
298
|
branch_changed = False
|
|
283
|
-
if self["
|
|
299
|
+
if self["reference"]:
|
|
284
300
|
# We also need tags because sometimes, a branch
|
|
285
301
|
# returns a different commit reference
|
|
286
302
|
git_tags, _ = run_simple(
|
|
@@ -292,18 +308,19 @@ class BatchFetchGit(BatchFetchBase):
|
|
|
292
308
|
# Also check the commit reference in case
|
|
293
309
|
# branch is a commit reference instead of a tag
|
|
294
310
|
try:
|
|
295
|
-
git_ref_branch = self._git_tags(self["
|
|
296
|
-
except
|
|
311
|
+
git_ref_branch = self._git_tags(self["reference"])[0]
|
|
312
|
+
except GitReferenceDoesNotExist as err:
|
|
297
313
|
raise BatchFetchError(f"The branch '{self['branch']}' "
|
|
298
314
|
"does not exist.") from err
|
|
299
315
|
|
|
300
316
|
if git_ref_after_merge != git_ref_branch and \
|
|
301
|
-
self["
|
|
317
|
+
self["reference"] not in git_tags:
|
|
302
318
|
# Update the branch
|
|
303
|
-
self._run(["git", "checkout"] + [self["
|
|
319
|
+
self._run(["git", "checkout"] + [self["reference"]],
|
|
304
320
|
cwd=str(self.git_local_dir), env=self.env)
|
|
305
|
-
self.add_output(self.indent_spaces +
|
|
306
|
-
|
|
321
|
+
self.add_output(self.indent_spaces +
|
|
322
|
+
"[INFO] Branch changed to " +
|
|
323
|
+
self["reference"] + "\n")
|
|
307
324
|
self.set_changed(True)
|
|
308
325
|
branch_changed = True
|
|
309
326
|
|
|
@@ -339,7 +356,7 @@ class BatchFetchGit(BatchFetchBase):
|
|
|
339
356
|
except IndexError:
|
|
340
357
|
origin_url = ""
|
|
341
358
|
|
|
342
|
-
if origin_url != self[
|
|
359
|
+
if origin_url != self[self.main_key]:
|
|
343
360
|
self.set_error(True)
|
|
344
361
|
self.add_output(
|
|
345
362
|
self.indent_spaces +
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
1
2
|
#
|
|
2
3
|
# Copyright (c) James Cherti
|
|
3
4
|
# URL: https://github.com/jamescherti/batchfetch
|
|
@@ -9,7 +10,8 @@
|
|
|
9
10
|
#
|
|
10
11
|
# This program is distributed in the hope that it will be useful, but WITHOUT
|
|
11
12
|
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
|
12
|
-
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
|
13
|
+
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
|
14
|
+
# details.
|
|
13
15
|
#
|
|
14
16
|
# You should have received a copy of the GNU General Public License along with
|
|
15
17
|
# this program. If not, see <https://www.gnu.org/licenses/>.
|
|
@@ -28,7 +30,7 @@ def md5sum(filename: os.PathLike):
|
|
|
28
30
|
Calculate and return the MD5 checksum of a file.
|
|
29
31
|
|
|
30
32
|
Args:
|
|
31
|
-
filename:
|
|
33
|
+
filename: Path to the file for which the MD5 checksum is calculated.
|
|
32
34
|
|
|
33
35
|
Returns:
|
|
34
36
|
str: The MD5 checksum of the file.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: batchfetch
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.6
|
|
4
4
|
Summary: Efficiently clone and pull multiple Git repositories.
|
|
5
5
|
Home-page: https://github.com/jamescherti/batchfetch
|
|
6
6
|
Author: James Cherti
|
|
@@ -17,10 +17,6 @@ Classifier: Topic :: Utilities
|
|
|
17
17
|
Requires-Python: >=3.6, <4
|
|
18
18
|
Description-Content-Type: text/markdown
|
|
19
19
|
License-File: LICENSE
|
|
20
|
-
Requires-Dist: colorama
|
|
21
|
-
Requires-Dist: schema
|
|
22
|
-
Requires-Dist: setproctitle
|
|
23
|
-
Requires-Dist: PyYAML
|
|
24
20
|
|
|
25
21
|
# Batchfetch - Efficiently clone or pull multiple Git repositories in parallel
|
|
26
22
|
|
|
@@ -61,12 +57,12 @@ tasks:
|
|
|
61
57
|
# Clone the tag 1.5 of the consult repository to the './consult'
|
|
62
58
|
# directory
|
|
63
59
|
- git: https://github.com/minad/consult
|
|
64
|
-
|
|
60
|
+
reference: "1.5"
|
|
65
61
|
|
|
66
62
|
# Clone the s.el repository to the './another-name.el' directory
|
|
67
63
|
- git: https://github.com/magnars/s.el
|
|
68
64
|
path: another-name.el
|
|
69
|
-
|
|
65
|
+
reference: dda84d38fffdaf0c9b12837b504b402af910d01d
|
|
70
66
|
|
|
71
67
|
# Delete './impatient-mode'
|
|
72
68
|
- git: https://github.com/skeeto/impatient-mode
|
|
@@ -84,14 +80,15 @@ usage: batchfetch [--option] [args]
|
|
|
84
80
|
|
|
85
81
|
Command line interface.
|
|
86
82
|
|
|
83
|
+
positional arguments:
|
|
84
|
+
N Specify the batchfetch YAML file(s) (default: './batchfetch.yaml').
|
|
85
|
+
|
|
87
86
|
options:
|
|
88
87
|
-h, --help show this help message and exit
|
|
89
|
-
-j JOBS, --jobs JOBS
|
|
90
|
-
Run up to N Number of parallel git processes (Default: 5).
|
|
88
|
+
-j JOBS, --jobs JOBS Run up to N Number of parallel processes (Default: 5).
|
|
91
89
|
-v, --verbose Enable verbose mode.
|
|
92
|
-
-f BATCHFETCH_FILE, --batchfetch-file BATCHFETCH_FILE
|
|
93
|
-
Specify the batchfetch YAML file (default: './batchfetch.yaml').
|
|
94
90
|
```
|
|
91
|
+
|
|
95
92
|
## License
|
|
96
93
|
|
|
97
94
|
Copyright (c) [James Cherti](https://www.jamescherti.com)
|
|
@@ -1,7 +1,5 @@
|
|
|
1
|
-
.gitignore
|
|
2
1
|
LICENSE
|
|
3
2
|
README.md
|
|
4
|
-
run_tests.sh
|
|
5
3
|
setup.py
|
|
6
4
|
batchfetch/__init__.py
|
|
7
5
|
batchfetch/batchfetch_base.py
|
|
@@ -13,7 +11,4 @@ batchfetch.egg-info/SOURCES.txt
|
|
|
13
11
|
batchfetch.egg-info/dependency_links.txt
|
|
14
12
|
batchfetch.egg-info/entry_points.txt
|
|
15
13
|
batchfetch.egg-info/requires.txt
|
|
16
|
-
batchfetch.egg-info/top_level.txt
|
|
17
|
-
tests/test_helpers.py
|
|
18
|
-
tests/data/test-md5sum.txt
|
|
19
|
-
tests/data/test-run_simple.sh
|
|
14
|
+
batchfetch.egg-info/top_level.txt
|
|
@@ -22,7 +22,7 @@ from setuptools import find_packages, setup
|
|
|
22
22
|
|
|
23
23
|
setup(
|
|
24
24
|
name="batchfetch",
|
|
25
|
-
version="1.0.
|
|
25
|
+
version="1.0.6",
|
|
26
26
|
packages=find_packages(),
|
|
27
27
|
description="Efficiently clone and pull multiple Git repositories.",
|
|
28
28
|
license="GPLv3",
|
|
@@ -51,7 +51,7 @@ setup(
|
|
|
51
51
|
],
|
|
52
52
|
entry_points={
|
|
53
53
|
"console_scripts": [
|
|
54
|
-
"batchfetch=batchfetch.
|
|
54
|
+
"batchfetch=batchfetch.batchfetch_cli:command_line_interface",
|
|
55
55
|
],
|
|
56
56
|
},
|
|
57
57
|
)
|
batchfetch-1.0.4/.gitignore
DELETED
|
@@ -1,138 +0,0 @@
|
|
|
1
|
-
# Byte-compiled / optimized / DLL files
|
|
2
|
-
__pycache__/
|
|
3
|
-
*.py[cod]
|
|
4
|
-
*$py.class
|
|
5
|
-
|
|
6
|
-
# C extensions
|
|
7
|
-
*.so
|
|
8
|
-
|
|
9
|
-
# Distribution / packaging
|
|
10
|
-
.Python
|
|
11
|
-
build/
|
|
12
|
-
develop-eggs/
|
|
13
|
-
dist/
|
|
14
|
-
downloads/
|
|
15
|
-
eggs/
|
|
16
|
-
.eggs/
|
|
17
|
-
lib/
|
|
18
|
-
lib64/
|
|
19
|
-
parts/
|
|
20
|
-
sdist/
|
|
21
|
-
var/
|
|
22
|
-
wheels/
|
|
23
|
-
share/python-wheels/
|
|
24
|
-
*.egg-info/
|
|
25
|
-
.installed.cfg
|
|
26
|
-
*.egg
|
|
27
|
-
MANIFEST
|
|
28
|
-
|
|
29
|
-
# PyInstaller
|
|
30
|
-
# Usually these files are written by a python script from a template
|
|
31
|
-
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
|
32
|
-
*.manifest
|
|
33
|
-
*.spec
|
|
34
|
-
|
|
35
|
-
# Installer logs
|
|
36
|
-
pip-log.txt
|
|
37
|
-
pip-delete-this-directory.txt
|
|
38
|
-
|
|
39
|
-
# Unit test / coverage reports
|
|
40
|
-
htmlcov/
|
|
41
|
-
.tox/
|
|
42
|
-
.nox/
|
|
43
|
-
.coverage
|
|
44
|
-
.coverage.*
|
|
45
|
-
.cache
|
|
46
|
-
nosetests.xml
|
|
47
|
-
coverage.xml
|
|
48
|
-
*.cover
|
|
49
|
-
*.py,cover
|
|
50
|
-
.hypothesis/
|
|
51
|
-
.pytest_cache/
|
|
52
|
-
cover/
|
|
53
|
-
|
|
54
|
-
# Translations
|
|
55
|
-
*.mo
|
|
56
|
-
*.pot
|
|
57
|
-
|
|
58
|
-
# Django stuff:
|
|
59
|
-
*.log
|
|
60
|
-
local_settings.py
|
|
61
|
-
db.sqlite3
|
|
62
|
-
db.sqlite3-journal
|
|
63
|
-
|
|
64
|
-
# Flask stuff:
|
|
65
|
-
instance/
|
|
66
|
-
.webassets-cache
|
|
67
|
-
|
|
68
|
-
# Scrapy stuff:
|
|
69
|
-
.scrapy
|
|
70
|
-
|
|
71
|
-
# Sphinx documentation
|
|
72
|
-
docs/_build/
|
|
73
|
-
|
|
74
|
-
# PyBuilder
|
|
75
|
-
.pybuilder/
|
|
76
|
-
target/
|
|
77
|
-
|
|
78
|
-
# Jupyter Notebook
|
|
79
|
-
.ipynb_checkpoints
|
|
80
|
-
|
|
81
|
-
# IPython
|
|
82
|
-
profile_default/
|
|
83
|
-
ipython_config.py
|
|
84
|
-
|
|
85
|
-
# pyenv
|
|
86
|
-
# For a library or package, you might want to ignore these files since the code is
|
|
87
|
-
# intended to run in multiple environments; otherwise, check them in:
|
|
88
|
-
# .python-version
|
|
89
|
-
|
|
90
|
-
# pipenv
|
|
91
|
-
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
|
92
|
-
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
|
93
|
-
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
|
94
|
-
# install all needed dependencies.
|
|
95
|
-
#Pipfile.lock
|
|
96
|
-
|
|
97
|
-
# PEP 582; used by e.g. github.com/David-OConnor/pyflow
|
|
98
|
-
__pypackages__/
|
|
99
|
-
|
|
100
|
-
# Celery stuff
|
|
101
|
-
celerybeat-schedule
|
|
102
|
-
celerybeat.pid
|
|
103
|
-
|
|
104
|
-
# SageMath parsed files
|
|
105
|
-
*.sage.py
|
|
106
|
-
|
|
107
|
-
# Environments
|
|
108
|
-
.env
|
|
109
|
-
.venv
|
|
110
|
-
env/
|
|
111
|
-
venv/
|
|
112
|
-
ENV/
|
|
113
|
-
env.bak/
|
|
114
|
-
venv.bak/
|
|
115
|
-
|
|
116
|
-
# Spyder project settings
|
|
117
|
-
.spyderproject
|
|
118
|
-
.spyproject
|
|
119
|
-
|
|
120
|
-
# Rope project settings
|
|
121
|
-
.ropeproject
|
|
122
|
-
|
|
123
|
-
# mkdocs documentation
|
|
124
|
-
/site
|
|
125
|
-
|
|
126
|
-
# mypy
|
|
127
|
-
.mypy_cache/
|
|
128
|
-
.dmypy.json
|
|
129
|
-
dmypy.json
|
|
130
|
-
|
|
131
|
-
# Pyre type checker
|
|
132
|
-
.pyre/
|
|
133
|
-
|
|
134
|
-
# pytype static type analyzer
|
|
135
|
-
.pytype/
|
|
136
|
-
|
|
137
|
-
# Cython debug symbols
|
|
138
|
-
cython_debug/
|
batchfetch-1.0.4/run_tests.sh
DELETED
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
#
|
|
3
|
-
# Copyright (c) James Cherti
|
|
4
|
-
# URL: https://github.com/jamescherti/batchfetch
|
|
5
|
-
#
|
|
6
|
-
# This program is free software: you can redistribute it and/or modify it under
|
|
7
|
-
# the terms of the GNU General Public License as published by the Free Software
|
|
8
|
-
# Foundation, either version 3 of the License, or (at your option) any later
|
|
9
|
-
# version.
|
|
10
|
-
#
|
|
11
|
-
# This program is distributed in the hope that it will be useful, but WITHOUT
|
|
12
|
-
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
|
13
|
-
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
|
14
|
-
#
|
|
15
|
-
# You should have received a copy of the GNU General Public License along with
|
|
16
|
-
# this program. If not, see <https://www.gnu.org/licenses/>.
|
|
17
|
-
#
|
|
18
|
-
cd "$(dirname "${BASH_SOURCE[0]}")"
|
|
19
|
-
export PYTHONPATH="$(pwd)"
|
|
20
|
-
exec pytest -v -v --cov=batchfetch --cov=tests \
|
|
21
|
-
--cov-report=term \
|
|
22
|
-
--cov-report=html:htmlcov \
|
|
23
|
-
tests/test_*py
|
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env python
|
|
2
|
-
#
|
|
3
|
-
# Copyright (c) James Cherti
|
|
4
|
-
# URL: https://github.com/jamescherti/batchfetch
|
|
5
|
-
#
|
|
6
|
-
# This program is free software: you can redistribute it and/or modify it under
|
|
7
|
-
# the terms of the GNU General Public License as published by the Free Software
|
|
8
|
-
# Foundation, either version 3 of the License, or (at your option) any later
|
|
9
|
-
# version.
|
|
10
|
-
#
|
|
11
|
-
# This program is distributed in the hope that it will be useful, but WITHOUT
|
|
12
|
-
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
|
13
|
-
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
|
14
|
-
#
|
|
15
|
-
# You should have received a copy of the GNU General Public License along with
|
|
16
|
-
# this program. If not, see <https://www.gnu.org/licenses/>.
|
|
17
|
-
#
|
|
18
|
-
"""Unit tests."""
|
|
19
|
-
|
|
20
|
-
from pathlib import Path
|
|
21
|
-
|
|
22
|
-
from batchfetch import helpers
|
|
23
|
-
|
|
24
|
-
DATA_PATH = Path(".").joinpath("tests", "data").absolute()
|
|
25
|
-
SCRIPT_RUN_SIMPLE = DATA_PATH / "test-run_simple.sh"
|
|
26
|
-
TEST_MD5SUM_FILE = DATA_PATH / "test-md5sum.txt"
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
def test_md5sum():
|
|
30
|
-
md5sum = helpers.md5sum(TEST_MD5SUM_FILE)
|
|
31
|
-
assert md5sum == "f31e127edc87a6aa2eb01b7d94d2ec58"
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
def test_run_simple():
|
|
35
|
-
# Stdout
|
|
36
|
-
stdout_lines, stderr_lines = helpers.run_simple(str(SCRIPT_RUN_SIMPLE))
|
|
37
|
-
assert stdout_lines == ['Test 1.',
|
|
38
|
-
'Test 2.',
|
|
39
|
-
'Test 3.']
|
|
40
|
-
assert stderr_lines == ['Test 3.']
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
def test_indent_raw_output():
|
|
44
|
-
list_str = \
|
|
45
|
-
helpers.indent_raw_output(["Test 1.", "Test 2."])
|
|
46
|
-
|
|
47
|
-
assert list_str == [" Test 1.", " Test 2."]
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
def test_run_indent():
|
|
51
|
-
stdout_lines, stderr_lines = helpers.run_indent(str(SCRIPT_RUN_SIMPLE))
|
|
52
|
-
assert stdout_lines == [f' [RUN] {str(SCRIPT_RUN_SIMPLE)}',
|
|
53
|
-
' Test 1.',
|
|
54
|
-
' Test 2.',
|
|
55
|
-
' Test 3.']
|
|
56
|
-
assert stderr_lines == [' Test 3.']
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|