travis2docker 7.0.0__py3-none-any.whl
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.
- travis2docker/__init__.py +1 -0
- travis2docker/__main__.py +14 -0
- travis2docker/cli.py +281 -0
- travis2docker/docker_helper/build.py +88 -0
- travis2docker/exceptions.py +5 -0
- travis2docker/git_run.py +114 -0
- travis2docker/templates/.coveragerc +39 -0
- travis2docker/templates/.vscode/extensions.json +14 -0
- travis2docker/templates/.vscode/launch.json +30 -0
- travis2docker/templates/.vscode/settings.json +11 -0
- travis2docker/templates/10-build.sh +4 -0
- travis2docker/templates/20-run.sh +17 -0
- travis2docker/templates/Dockerfile_deployv +54 -0
- travis2docker/templates/build.sh +364 -0
- travis2docker/templates/entrypoint_deployv.sh +125 -0
- travis2docker/travis2docker.py +162 -0
- travis2docker-7.0.0.dist-info/METADATA +268 -0
- travis2docker-7.0.0.dist-info/RECORD +23 -0
- travis2docker-7.0.0.dist-info/WHEEL +5 -0
- travis2docker-7.0.0.dist-info/entry_points.txt +2 -0
- travis2docker-7.0.0.dist-info/licenses/AUTHORS.rst +5 -0
- travis2docker-7.0.0.dist-info/licenses/LICENSE +19 -0
- travis2docker-7.0.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "7.0.0"
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"""Entrypoint module, in case you use `python -mtravis2docker`.
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
Why does this file exist, and why __main__? For more info, read:
|
|
5
|
+
|
|
6
|
+
- https://www.python.org/dev/peps/pep-0338/
|
|
7
|
+
- https://docs.python.org/2/using/cmdline.html#cmdoption-m
|
|
8
|
+
- https://docs.python.org/3/using/cmdline.html#cmdoption-m
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from .cli import main
|
|
12
|
+
|
|
13
|
+
if __name__ == "__main__":
|
|
14
|
+
main()
|
travis2docker/cli.py
ADDED
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
"""Module that contains the command line app.
|
|
2
|
+
|
|
3
|
+
Why does this file exist, and why not put this in __main__?
|
|
4
|
+
|
|
5
|
+
You might be tempted to import things from __main__ later, but that will cause
|
|
6
|
+
problems: the code will get executed twice:
|
|
7
|
+
|
|
8
|
+
- When you run `python -mtravis2docker` python will execute
|
|
9
|
+
``__main__.py`` as a script. That means there won't be any
|
|
10
|
+
``travis2docker.__main__`` in ``sys.modules``.
|
|
11
|
+
- When you import __main__ it will get executed again (as a module) because
|
|
12
|
+
there's no ``travis2docker.__main__`` in ``sys.modules``.
|
|
13
|
+
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
import argparse
|
|
17
|
+
import os
|
|
18
|
+
import pathlib
|
|
19
|
+
from sys import stdout
|
|
20
|
+
|
|
21
|
+
from . import __version__
|
|
22
|
+
from .exceptions import InvalidRepoBranchError
|
|
23
|
+
from .git_run import GitRun
|
|
24
|
+
from .travis2docker import Travis2Docker
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def variables_sh_read(variables_sh_path):
|
|
28
|
+
variables_sh_path = pathlib.Path(variables_sh_path).expanduser()
|
|
29
|
+
if variables_sh_path.is_dir():
|
|
30
|
+
variables_sh_path /= "variables.sh"
|
|
31
|
+
if not variables_sh_path.is_file():
|
|
32
|
+
return None
|
|
33
|
+
return variables_sh_path.read_text()
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def get_git_data(project, path, revision):
|
|
37
|
+
git_obj = GitRun(project, path, path_prefix_repo=True)
|
|
38
|
+
git_obj.update()
|
|
39
|
+
data = {
|
|
40
|
+
"sha": git_obj.get_sha(revision),
|
|
41
|
+
"variables_sh": git_obj.show_file("variables.sh", revision),
|
|
42
|
+
"repo_owner": git_obj.owner,
|
|
43
|
+
"repo_project": git_obj.repo,
|
|
44
|
+
"git_email": git_obj.get_config_data("user.email"),
|
|
45
|
+
"git_user": git_obj.get_config_data("user.name"),
|
|
46
|
+
"revision": revision,
|
|
47
|
+
"project": project,
|
|
48
|
+
}
|
|
49
|
+
return data
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def main(return_result=False):
|
|
53
|
+
parser = argparse.ArgumentParser()
|
|
54
|
+
parser.add_argument(
|
|
55
|
+
"git_repo_url",
|
|
56
|
+
help="Specify repository git of work."
|
|
57
|
+
"\nThis is used to clone it "
|
|
58
|
+
"and get the variables.sh file of the deployv image"
|
|
59
|
+
"\nIf your repository is private, "
|
|
60
|
+
"don't use https url, "
|
|
61
|
+
"use ssh url",
|
|
62
|
+
)
|
|
63
|
+
parser.add_argument(
|
|
64
|
+
"git_revision",
|
|
65
|
+
help="Revision git of work."
|
|
66
|
+
"\nYou can use "
|
|
67
|
+
"branch name e.g. master or 8.0 "
|
|
68
|
+
"or pull number with 'pull/#' e.g. pull/1 "
|
|
69
|
+
"NOTE: A sha e.g. b48228 NOT IMPLEMENTED YET",
|
|
70
|
+
)
|
|
71
|
+
parser.add_argument(
|
|
72
|
+
"--docker-user",
|
|
73
|
+
dest="docker_user",
|
|
74
|
+
help="User of work into Dockerfile.\nBased on your docker image.\nDefault: odoo",
|
|
75
|
+
)
|
|
76
|
+
parser.add_argument(
|
|
77
|
+
"--docker-image",
|
|
78
|
+
dest="default_docker_image",
|
|
79
|
+
help="Docker image to use by default in Dockerfile."
|
|
80
|
+
"\nDefault: built from variables.sh as "
|
|
81
|
+
"'DOCKER_IMAGE_REPO:MAIN_APP-VERSION-SHA_SHORT'",
|
|
82
|
+
)
|
|
83
|
+
default_root_path = os.environ.get("TRAVIS2DOCKER_ROOT_PATH")
|
|
84
|
+
if not default_root_path:
|
|
85
|
+
default_root_path = pathlib.Path("~").expanduser()
|
|
86
|
+
default_root_path = str(pathlib.Path(default_root_path) / ".t2d")
|
|
87
|
+
parser.add_argument(
|
|
88
|
+
"--root-path",
|
|
89
|
+
dest="root_path",
|
|
90
|
+
help=f"Root path to save scripts generated.\nDefault: {default_root_path}",
|
|
91
|
+
default=default_root_path,
|
|
92
|
+
)
|
|
93
|
+
parser.add_argument(
|
|
94
|
+
"--add-remote",
|
|
95
|
+
dest="remotes",
|
|
96
|
+
help="Add git remote to git of build path, separated by a comma.\nUse remote name. E.g. 'Vauxoo,moylop260'",
|
|
97
|
+
)
|
|
98
|
+
parser.add_argument(
|
|
99
|
+
"--exclude-after-success",
|
|
100
|
+
dest="exclude_after_success",
|
|
101
|
+
action="store_true",
|
|
102
|
+
default=False,
|
|
103
|
+
help="Deprecated. Ignored: the travis_after_success section does not exist anymore",
|
|
104
|
+
)
|
|
105
|
+
parser.add_argument(
|
|
106
|
+
"--run-extra-args",
|
|
107
|
+
dest="run_extra_args",
|
|
108
|
+
help="Extra arguments to `docker run RUN_EXTRA_ARGS` command",
|
|
109
|
+
default="-itP -e LANG=C.UTF-8",
|
|
110
|
+
)
|
|
111
|
+
parser.add_argument(
|
|
112
|
+
"--run-extra-cmds",
|
|
113
|
+
dest="run_extra_cmds",
|
|
114
|
+
nargs="*",
|
|
115
|
+
default="",
|
|
116
|
+
help='Extra commands to run after "run" script. '
|
|
117
|
+
"Note: You can use \\$IMAGE escaped environment variable."
|
|
118
|
+
'E.g. "docker rmi -f \\$IMAGE"',
|
|
119
|
+
)
|
|
120
|
+
parser.add_argument(
|
|
121
|
+
"--build-extra-args",
|
|
122
|
+
dest="build_extra_args",
|
|
123
|
+
help="Extra arguments to `docker build BUILD_EXTRA_ARGS` command",
|
|
124
|
+
default="--rm",
|
|
125
|
+
)
|
|
126
|
+
parser.add_argument(
|
|
127
|
+
"--build-extra-cmds",
|
|
128
|
+
dest="build_extra_cmds",
|
|
129
|
+
nargs="*",
|
|
130
|
+
default="",
|
|
131
|
+
help='Extra commands to run after "build" script. Note: You can use \\$IMAGE escaped environment variable.',
|
|
132
|
+
)
|
|
133
|
+
parser.add_argument(
|
|
134
|
+
"--travis-yml-path",
|
|
135
|
+
dest="travis_yml_path",
|
|
136
|
+
default=None,
|
|
137
|
+
help="Deprecated. Ignored: the .travis.yml file is not used anymore",
|
|
138
|
+
)
|
|
139
|
+
parser.add_argument(
|
|
140
|
+
"--variables-sh-path",
|
|
141
|
+
dest="variables_sh_path",
|
|
142
|
+
default=None,
|
|
143
|
+
help="Optional path of the variables.sh file (or the directory containing it) to use.\n"
|
|
144
|
+
"Default: Extracted from git repo and git revision.",
|
|
145
|
+
)
|
|
146
|
+
parser.add_argument(
|
|
147
|
+
"--no-clone",
|
|
148
|
+
dest="no_clone",
|
|
149
|
+
action="store_true",
|
|
150
|
+
default=False,
|
|
151
|
+
help="Avoid cloning the repository. It requires --variables-sh-path",
|
|
152
|
+
)
|
|
153
|
+
parser.add_argument(
|
|
154
|
+
"--add-rcfile",
|
|
155
|
+
dest="add_rcfile",
|
|
156
|
+
default="",
|
|
157
|
+
help="Optional paths of configuration files to "
|
|
158
|
+
"copy for user's HOME path into container, separated by a comma.",
|
|
159
|
+
)
|
|
160
|
+
parser.add_argument("-v", "--version", action="version", version="%(prog)s " + __version__)
|
|
161
|
+
parser.add_argument(
|
|
162
|
+
"--runs-at-the-end-script",
|
|
163
|
+
dest="runs_at_the_end_script",
|
|
164
|
+
nargs="*",
|
|
165
|
+
default="",
|
|
166
|
+
help="Deprecated. Ignored: the script section of .travis.yml does not exist anymore",
|
|
167
|
+
)
|
|
168
|
+
parser.add_argument(
|
|
169
|
+
"--build-env-args",
|
|
170
|
+
dest="build_env_args",
|
|
171
|
+
nargs="*",
|
|
172
|
+
action="append",
|
|
173
|
+
default=[],
|
|
174
|
+
help="Args used as environment variables "
|
|
175
|
+
"More info about: https://vsupalov.com/docker-build-time-env-values\n"
|
|
176
|
+
"E.g. --build-env-args ENVAR1\n"
|
|
177
|
+
"It generates the following line for Dockerfile:\n"
|
|
178
|
+
"ARG ENVVAR1\nENV ENVVAR1=$ENVVAR1",
|
|
179
|
+
)
|
|
180
|
+
parser.add_argument(
|
|
181
|
+
"--deployv",
|
|
182
|
+
dest="deployv",
|
|
183
|
+
action="store_true",
|
|
184
|
+
default=True,
|
|
185
|
+
help="Deprecated. The deployv image is now the only supported mode",
|
|
186
|
+
)
|
|
187
|
+
parser.add_argument(
|
|
188
|
+
"--build-extra-steps",
|
|
189
|
+
nargs="*",
|
|
190
|
+
default="",
|
|
191
|
+
dest="build_extra_steps",
|
|
192
|
+
help="Append these extra steps at the end of the Dockerfile",
|
|
193
|
+
)
|
|
194
|
+
|
|
195
|
+
args = parser.parse_args()
|
|
196
|
+
deprecated_args = {
|
|
197
|
+
"--exclude-after-success": args.exclude_after_success,
|
|
198
|
+
"--travis-yml-path": args.travis_yml_path,
|
|
199
|
+
"--runs-at-the-end-script": args.runs_at_the_end_script,
|
|
200
|
+
}
|
|
201
|
+
for deprecated_arg, value in deprecated_args.items():
|
|
202
|
+
if value:
|
|
203
|
+
stdout.write("WARNING: %s is deprecated and its value will be ignored\n" % deprecated_arg)
|
|
204
|
+
revision = args.git_revision
|
|
205
|
+
git_repo = args.git_repo_url
|
|
206
|
+
git_base = GitRun.get_data_url(git_repo, False)[0]
|
|
207
|
+
docker_user = args.docker_user
|
|
208
|
+
root_path = args.root_path
|
|
209
|
+
default_docker_image = args.default_docker_image
|
|
210
|
+
remotes = args.remotes and args.remotes.split(",")
|
|
211
|
+
run_extra_args = args.run_extra_args
|
|
212
|
+
build_extra_args = args.build_extra_args
|
|
213
|
+
build_extra_cmds = "\n".join(args.build_extra_cmds)
|
|
214
|
+
run_extra_cmds = "\n".join(args.run_extra_cmds)
|
|
215
|
+
rcfiles_args = args.add_rcfile and args.add_rcfile.split(",")
|
|
216
|
+
build_env_args = [build_env_args[0] for build_env_args in args.build_env_args]
|
|
217
|
+
rcfiles = [
|
|
218
|
+
(pathlib.Path(rc_file).expanduser(), "$HOME/%s" % pathlib.Path(rc_file).name) for rc_file in rcfiles_args
|
|
219
|
+
]
|
|
220
|
+
if args.no_clone:
|
|
221
|
+
os_kwargs = {
|
|
222
|
+
"repo_owner": "local_file",
|
|
223
|
+
"repo_project": "local_file",
|
|
224
|
+
"revision": revision,
|
|
225
|
+
"sha": "local_file",
|
|
226
|
+
"project": git_repo,
|
|
227
|
+
}
|
|
228
|
+
else:
|
|
229
|
+
os_kwargs = get_git_data(git_repo, pathlib.Path(root_path) / "repo", revision)
|
|
230
|
+
|
|
231
|
+
if args.variables_sh_path:
|
|
232
|
+
os_kwargs["variables_sh"] = variables_sh_read(args.variables_sh_path)
|
|
233
|
+
|
|
234
|
+
if not os_kwargs.get("variables_sh"):
|
|
235
|
+
msg = (
|
|
236
|
+
"The file %s is empty or does not exist." % args.variables_sh_path
|
|
237
|
+
if args.variables_sh_path
|
|
238
|
+
else "The repo or the branch is incorrect value, because "
|
|
239
|
+
+ "It can not got the variables.sh content from %s %s. " % (git_repo, revision)
|
|
240
|
+
+ "\nPlease, verify access repository,"
|
|
241
|
+
+ "\nverify exists url and revision, "
|
|
242
|
+
+ "\nverify exists variables.sh"
|
|
243
|
+
)
|
|
244
|
+
raise InvalidRepoBranchError(msg)
|
|
245
|
+
os_kwargs.update({"remotes": remotes, "git_base": git_base})
|
|
246
|
+
if docker_user:
|
|
247
|
+
os_kwargs.update({"user": docker_user})
|
|
248
|
+
t2d = Travis2Docker(
|
|
249
|
+
work_path=pathlib.Path(root_path) / "script" / GitRun.url2dirname(git_repo) / revision,
|
|
250
|
+
image=default_docker_image,
|
|
251
|
+
os_kwargs=os_kwargs,
|
|
252
|
+
copy_paths=[(pathlib.Path("~/.ssh").expanduser(), "$HOME/.ssh")] + rcfiles,
|
|
253
|
+
build_env_args=build_env_args,
|
|
254
|
+
build_extra_steps=args.build_extra_steps,
|
|
255
|
+
)
|
|
256
|
+
t2d.build_extra_params = {
|
|
257
|
+
"extra_params": build_extra_args,
|
|
258
|
+
"extra_cmds": build_extra_cmds,
|
|
259
|
+
}
|
|
260
|
+
t2d.run_extra_params = {
|
|
261
|
+
"extra_params": run_extra_args,
|
|
262
|
+
"extra_cmds": run_extra_cmds,
|
|
263
|
+
}
|
|
264
|
+
fname_scripts = t2d.compute_dockerfile()
|
|
265
|
+
if fname_scripts:
|
|
266
|
+
fname_list = "- " + "\n- ".join(fname_scripts)
|
|
267
|
+
stdout.write("\nGenerated scripts:\n%s\n" % fname_list)
|
|
268
|
+
if not default_docker_image:
|
|
269
|
+
stdout.write("=" * 80)
|
|
270
|
+
# TODO: Add the URL to open the pipelines
|
|
271
|
+
stdout.write(
|
|
272
|
+
'\nTIP: Use the parameter "--docker-image=quay.io/vauxoo/PROJECT:TAG" '
|
|
273
|
+
'get the PROJECT:TAG info in your "build_docker" pipeline similar to '
|
|
274
|
+
'\n"... INFO - deployv.deployv_addon_gitlab_tools.common.common.push_image - '
|
|
275
|
+
'Pushing image ... to quay.io/vauxoo/PROJECT:TAG"\n'
|
|
276
|
+
)
|
|
277
|
+
stdout.write("=" * 80)
|
|
278
|
+
else:
|
|
279
|
+
stdout.write("\nNo scripts were generated.")
|
|
280
|
+
if return_result:
|
|
281
|
+
return fname_scripts
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
# No plan to use logging here
|
|
2
|
+
# pylint: disable=print-used
|
|
3
|
+
|
|
4
|
+
import glob
|
|
5
|
+
import pathlib
|
|
6
|
+
import re
|
|
7
|
+
import subprocess
|
|
8
|
+
import sys
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def ssh_keyscan2known_hosts(url, known_hosts_path=None):
|
|
12
|
+
# python3 -c "import build;build.ssh_keyscan2known_hosts('url')"
|
|
13
|
+
if known_hosts_path is None:
|
|
14
|
+
known_hosts_path = pathlib.Path("~/.ssh/known_hosts").expanduser()
|
|
15
|
+
|
|
16
|
+
# Clear current known hosts
|
|
17
|
+
cmd = ["ssh-keygen", "-R", url]
|
|
18
|
+
print(" ".join(cmd))
|
|
19
|
+
subprocess.call(cmd)
|
|
20
|
+
|
|
21
|
+
# Scan new key of host and store
|
|
22
|
+
cmd = ["ssh-keyscan", "-p", "22", url]
|
|
23
|
+
print(" ".join(cmd))
|
|
24
|
+
keys_scanned = subprocess.check_output(cmd).decode(sys.stdout.encoding).strip()
|
|
25
|
+
with pathlib.Path(known_hosts_path).open("r+") as known_hosts_f:
|
|
26
|
+
known_hosts_f.write("\n" + keys_scanned)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def git_set_remote(path=None):
|
|
30
|
+
# python3 -c "import build;build.git_set_remote('')"
|
|
31
|
+
if path is None:
|
|
32
|
+
# /home/odoo/instance/odoo spends a lot of time
|
|
33
|
+
path = "/home/odoo/instance/extra_addons"
|
|
34
|
+
git_re = re.compile(r"([^/|@]+)/([^/]+)/([^/.]+(.git)?)")
|
|
35
|
+
# TODO: Support ssh url
|
|
36
|
+
path = str(pathlib.Path(path) / "*" / "**" / ".git")
|
|
37
|
+
hosts_scanned = set()
|
|
38
|
+
for git_dir in glob.glob(path, recursive=True):
|
|
39
|
+
git_cmd = [
|
|
40
|
+
"git",
|
|
41
|
+
"--work-tree=%s" % pathlib.Path(git_dir).parent,
|
|
42
|
+
"--git-dir=%s" % git_dir,
|
|
43
|
+
]
|
|
44
|
+
cmd = git_cmd + ["remote", "get-url", "--push", "origin"]
|
|
45
|
+
remote = subprocess.check_output(cmd).decode(sys.stdout.encoding).strip().splitlines()[0]
|
|
46
|
+
git_re_match = git_re.search(remote)
|
|
47
|
+
|
|
48
|
+
# Configure to fetch all branches (not only the single-branch)
|
|
49
|
+
cmd = git_cmd + [
|
|
50
|
+
"config",
|
|
51
|
+
"remote.origin.fetch",
|
|
52
|
+
"+refs/heads/*:refs/remotes/origin/*",
|
|
53
|
+
]
|
|
54
|
+
subprocess.call(cmd)
|
|
55
|
+
|
|
56
|
+
if not git_re_match:
|
|
57
|
+
print("Remote not matched %s" % remote)
|
|
58
|
+
continue
|
|
59
|
+
|
|
60
|
+
git_re_groups = git_re_match.groups()
|
|
61
|
+
host = git_re_groups[0]
|
|
62
|
+
org = git_re_groups[1]
|
|
63
|
+
repo = git_re_groups[2]
|
|
64
|
+
|
|
65
|
+
# Transform https url to ssh format
|
|
66
|
+
ssh_url_stb = "git@%s:%s/%s" % (host, org, repo)
|
|
67
|
+
cmd = git_cmd + ["remote", "set-url", "origin", ssh_url_stb]
|
|
68
|
+
print(" ".join(cmd))
|
|
69
|
+
subprocess.call(cmd)
|
|
70
|
+
|
|
71
|
+
# Unshallow repository
|
|
72
|
+
cmd = git_cmd + ["rev-parse", "--is-shallow-repository"]
|
|
73
|
+
is_shallow = subprocess.check_output(cmd).decode(sys.stdout.encoding).strip().splitlines()[0]
|
|
74
|
+
if is_shallow == "true":
|
|
75
|
+
if host not in hosts_scanned:
|
|
76
|
+
ssh_keyscan2known_hosts(host)
|
|
77
|
+
hosts_scanned.add(host)
|
|
78
|
+
cmd = git_cmd + ["fetch", "--unshallow"]
|
|
79
|
+
print(" ".join(cmd))
|
|
80
|
+
subprocess.call(cmd)
|
|
81
|
+
|
|
82
|
+
# Add extra remote if "stb" so add "dev" if "dev" so add "stb"
|
|
83
|
+
new_org = org.replace("-dev", "") if "-dev" in org else "%s-dev" % org
|
|
84
|
+
new_remote = "dev" if "dev" in new_org else "stb"
|
|
85
|
+
ssh_url_dev = "git@%s:%s/%s" % (host, new_org, repo)
|
|
86
|
+
cmd = git_cmd + ["remote", "add", new_remote, ssh_url_dev]
|
|
87
|
+
print(" ".join(cmd))
|
|
88
|
+
subprocess.call(cmd)
|
travis2docker/git_run.py
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
# pylint: disable=useless-object-inheritance,print-used,except-pass
|
|
2
|
+
|
|
3
|
+
import contextlib
|
|
4
|
+
import pathlib
|
|
5
|
+
import re
|
|
6
|
+
import subprocess
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def decode_utf(field):
|
|
10
|
+
try:
|
|
11
|
+
return field.decode("utf-8")
|
|
12
|
+
except UnicodeDecodeError:
|
|
13
|
+
return ""
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class GitRun:
|
|
17
|
+
def __init__(self, repo_git, path, path_prefix_repo=False):
|
|
18
|
+
self.repo_git = repo_git
|
|
19
|
+
path = pathlib.Path(path)
|
|
20
|
+
if path_prefix_repo:
|
|
21
|
+
path /= self.url2dirname(repo_git)
|
|
22
|
+
self.path = path
|
|
23
|
+
self.host, self.owner, self.repo = self.get_data_url(repo_git)
|
|
24
|
+
|
|
25
|
+
@staticmethod
|
|
26
|
+
def get_data_url(repo_git, no_user=True):
|
|
27
|
+
host, owner, repo = False, False, False
|
|
28
|
+
repo_git_sub = repo_git.replace(":", "/")
|
|
29
|
+
if no_user:
|
|
30
|
+
repo_git_sub = re.sub(r".+@", "", repo_git_sub)
|
|
31
|
+
repo_git_sub = re.sub(r".git$", "", repo_git_sub)
|
|
32
|
+
match_object = re.search(r"(?P<host>[^/]+)/(?P<owner>[^/]+)/(?P<repo>[^/]+)", repo_git_sub)
|
|
33
|
+
if match_object:
|
|
34
|
+
host = match_object.group("host")
|
|
35
|
+
owner = match_object.group("owner")
|
|
36
|
+
repo = match_object.group("repo")
|
|
37
|
+
elif pathlib.Path(repo_git).is_dir():
|
|
38
|
+
host = "local"
|
|
39
|
+
owner = pathlib.Path(repo_git).name
|
|
40
|
+
repo = pathlib.Path(repo_git).parent.name
|
|
41
|
+
return host, owner, repo
|
|
42
|
+
|
|
43
|
+
@staticmethod
|
|
44
|
+
def url2dirname(url):
|
|
45
|
+
for invalid_char in "@:/#\\":
|
|
46
|
+
url = url.replace(invalid_char, "_")
|
|
47
|
+
return url
|
|
48
|
+
|
|
49
|
+
def checkout_bare(self, branch):
|
|
50
|
+
return self.run(["symbolic-ref", "HEAD", branch])
|
|
51
|
+
|
|
52
|
+
def get_config_data(self, field=None):
|
|
53
|
+
if field is None:
|
|
54
|
+
field = "-l"
|
|
55
|
+
res = self.run(["config", field])
|
|
56
|
+
if res:
|
|
57
|
+
res = res.strip("\n ")
|
|
58
|
+
return res
|
|
59
|
+
|
|
60
|
+
def run(self, cmd):
|
|
61
|
+
"""Execute git command in bash"""
|
|
62
|
+
cmd = ["git", "--git-dir=%s" % self.path] + cmd
|
|
63
|
+
print("cmd list", cmd)
|
|
64
|
+
print("cmd", " ".join(cmd))
|
|
65
|
+
res = None
|
|
66
|
+
with contextlib.suppress(BaseException):
|
|
67
|
+
res = subprocess.check_output(cmd)
|
|
68
|
+
if res:
|
|
69
|
+
try:
|
|
70
|
+
res = res.decode()
|
|
71
|
+
except UnicodeDecodeError:
|
|
72
|
+
res = res.decode("utf-8")
|
|
73
|
+
return res
|
|
74
|
+
|
|
75
|
+
def get_ref_data(self, refs=None, fields=None):
|
|
76
|
+
if refs is None:
|
|
77
|
+
refs = ["refs/heads"]
|
|
78
|
+
if fields is None:
|
|
79
|
+
fields = []
|
|
80
|
+
if "refname" not in fields:
|
|
81
|
+
fields.append("refname")
|
|
82
|
+
# fields = ['refname', 'objectname', 'committerdate:iso8601',
|
|
83
|
+
# 'authorname', 'authoremail','subject','committername',
|
|
84
|
+
# 'committeremail']
|
|
85
|
+
fmt = "%00".join(["%(" + field + ")" for field in fields])
|
|
86
|
+
git_refs = self.run(["for-each-ref", "--format", fmt, "--sort=refname"] + refs)
|
|
87
|
+
git_refs = git_refs.strip()
|
|
88
|
+
refs = [[decode_utf(field) for field in line.split("\x00")] for line in git_refs.split("\n")]
|
|
89
|
+
res = {}
|
|
90
|
+
for data_field in refs:
|
|
91
|
+
subres = dict(zip(fields, data_field))
|
|
92
|
+
res[subres.pop("refname")] = subres
|
|
93
|
+
return res
|
|
94
|
+
|
|
95
|
+
def update(self):
|
|
96
|
+
"""Get a repository git or update it"""
|
|
97
|
+
if not self.path.is_dir():
|
|
98
|
+
self.path.mkdir(parents=True)
|
|
99
|
+
if not (self.path / "refs").is_dir():
|
|
100
|
+
subprocess.check_output(["git", "clone", "--bare", self.repo_git, self.path])
|
|
101
|
+
self.run(["gc", "--auto", "--prune=all"])
|
|
102
|
+
self.run(["fetch", "-p", "origin", "+refs/heads/*:refs/heads/*"])
|
|
103
|
+
# github support
|
|
104
|
+
self.run(["fetch", "origin", "+refs/pull/*/head:refs/pull/*"])
|
|
105
|
+
# gitlab support
|
|
106
|
+
self.run(["fetch", "origin", "+refs/merge-requests/*/head:refs/pull/*"])
|
|
107
|
+
|
|
108
|
+
def show_file(self, git_file, sha):
|
|
109
|
+
result = self.run(["show", "%s:%s" % (sha, git_file)])
|
|
110
|
+
return result
|
|
111
|
+
|
|
112
|
+
def get_sha(self, revision):
|
|
113
|
+
result = self.run(["rev-parse", revision])
|
|
114
|
+
return result if isinstance(result, list) else (result or "").strip(" \n")
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
[run]
|
|
2
|
+
branch = True
|
|
3
|
+
command_line =
|
|
4
|
+
${HOME}/instance/odoo/odoo-bin -c ${ODOO_CONFIG_FILE} -d odoocov -i ${MODULES2INSTALL}
|
|
5
|
+
-u ${MODULES2INSTALL} --test-tags ${MODULES2TEST} --workers 0 --stop-after-init
|
|
6
|
+
relative_files = True
|
|
7
|
+
source = ${COVERAGE_HOME}
|
|
8
|
+
data_file = ${COVERAGE_HOME}/.coverage
|
|
9
|
+
dynamic_context = ${COVERAGE_DYNAMIC_CONTEXT}
|
|
10
|
+
omit =
|
|
11
|
+
*/scenario/*
|
|
12
|
+
*/scenarios/*
|
|
13
|
+
*/test/*
|
|
14
|
+
*/tests/*
|
|
15
|
+
*/migrations/*
|
|
16
|
+
*migration*
|
|
17
|
+
*_example/*
|
|
18
|
+
*__init__.py
|
|
19
|
+
*__openerp__.py
|
|
20
|
+
*__manifest__.py
|
|
21
|
+
${EXCLUDE_COVERAGE}
|
|
22
|
+
|
|
23
|
+
[report]
|
|
24
|
+
# Regexes for lines to exclude from consideration
|
|
25
|
+
exclude_lines =
|
|
26
|
+
# Have to re-enable the standard pragma
|
|
27
|
+
pragma: no cover
|
|
28
|
+
# Don't complain about null context checking
|
|
29
|
+
if context is None:
|
|
30
|
+
# Don't check obviously not implemented
|
|
31
|
+
raise NotImplementedError
|
|
32
|
+
# We don't really care what happens if fail
|
|
33
|
+
except ImportError:
|
|
34
|
+
|
|
35
|
+
fail_under = ${COVERAGE_MIN-0}
|
|
36
|
+
precision = ${COVERAGE_PRECISION-0}
|
|
37
|
+
|
|
38
|
+
[html]
|
|
39
|
+
show_contexts=True
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"recommendations": [
|
|
3
|
+
"eamodio.gitlens",
|
|
4
|
+
"ms-python.pylint",
|
|
5
|
+
"dotjoshjohnson.xml",
|
|
6
|
+
"ms-python.python",
|
|
7
|
+
"ms-python.vscode-pylance",
|
|
8
|
+
"ms-python.flake8",
|
|
9
|
+
"dbaeumer.vscode-eslint",
|
|
10
|
+
"redhat.vscode-xml",
|
|
11
|
+
"mstuttgart.odoo-snippets",
|
|
12
|
+
"shardulm94.trailing-spaces"
|
|
13
|
+
]
|
|
14
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": "0.2.0",
|
|
3
|
+
"configurations": [
|
|
4
|
+
{
|
|
5
|
+
"name": "Odoo",
|
|
6
|
+
"type": "python",
|
|
7
|
+
"request": "launch",
|
|
8
|
+
"program": "${env:HOME}/instance/odoo/odoo-bin",
|
|
9
|
+
"console": "integratedTerminal",
|
|
10
|
+
"justMyCode": false,
|
|
11
|
+
"gevent": true,
|
|
12
|
+
"args": [
|
|
13
|
+
"-c", "/home/odoo/.openerp_serverrc"
|
|
14
|
+
]
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
"name": "Odoo Tests",
|
|
18
|
+
"type": "python",
|
|
19
|
+
"request": "launch",
|
|
20
|
+
"program": "${env:HOME}/instance/odoo/odoo-bin",
|
|
21
|
+
"console": "integratedTerminal",
|
|
22
|
+
"justMyCode": false,
|
|
23
|
+
"gevent": true,
|
|
24
|
+
"args": [
|
|
25
|
+
"-c", "/home/odoo/.openerp_serverrc",
|
|
26
|
+
"--test-tags", "standard"
|
|
27
|
+
]
|
|
28
|
+
}
|
|
29
|
+
]
|
|
30
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
{
|
|
2
|
+
"editor.rulers": [120],
|
|
3
|
+
"files.autoSave": "afterDelay",
|
|
4
|
+
"files.autoSaveDelay": 500,
|
|
5
|
+
"python.analysis.extraPaths": ["${env:HOME}/instance/odoo"],
|
|
6
|
+
"python.linting.enabled": true,
|
|
7
|
+
"pylint.args": ["--rcfile=${env:HOME}/.pylintrc"],
|
|
8
|
+
"flake8.args": ["--config", "${env:HOME}/.flake8"],
|
|
9
|
+
"isort.args": ["--settings-path", "${env:HOME}/.isort.cfg"],
|
|
10
|
+
"eslint.options": { "overrideConfigFile": "${env:HOME}/.eslintrc.json" }
|
|
11
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
export IMAGE={{ image }}
|
|
3
|
+
CONTAINER=$(docker run {{ extra_params }} $1 -ditP $IMAGE $2)
|
|
4
|
+
|
|
5
|
+
# Some files under /home/odoo end up owned by root or other users (declared VOLUMEs and
|
|
6
|
+
# files shared into the container); fix their ownership in the background so we can attach ASAP
|
|
7
|
+
docker exec --user=root "$CONTAINER" find /home/odoo -maxdepth 2 -not -user odoo -exec chown -R odoo:odoo {} + &
|
|
8
|
+
|
|
9
|
+
# If asked to detach, don't attach but print the ID, to mimic docker run behavior
|
|
10
|
+
detach_re='[[:space:]](-[a-zA-Z]*d[a-zA-Z]*|--detach)[[:space:]]'
|
|
11
|
+
if [[ " {{ extra_params }} $1 " =~ $detach_re ]]; then
|
|
12
|
+
wait
|
|
13
|
+
echo "$CONTAINER"
|
|
14
|
+
else
|
|
15
|
+
docker attach "$CONTAINER"
|
|
16
|
+
fi
|
|
17
|
+
{{ extra_cmds }}
|