developers-chamber 0.1.39__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.
- developers_chamber/__init__.py +0 -0
- developers_chamber/bin/__init__.py +0 -0
- developers_chamber/bin/pydev.py +92 -0
- developers_chamber/bitbucket_utils.py +94 -0
- developers_chamber/click/__init__.py +0 -0
- developers_chamber/click/alias.py +130 -0
- developers_chamber/click/options.py +86 -0
- developers_chamber/docker_utils.py +15 -0
- developers_chamber/ecs_utils.py +838 -0
- developers_chamber/git_utils.py +242 -0
- developers_chamber/gitlab_utils.py +94 -0
- developers_chamber/jira_utils.py +113 -0
- developers_chamber/project_utils.py +355 -0
- developers_chamber/qa/__init__.py +0 -0
- developers_chamber/qa/base.py +142 -0
- developers_chamber/qa/checks.py +217 -0
- developers_chamber/scripts/__init__.py +49 -0
- developers_chamber/scripts/bitbucket.py +75 -0
- developers_chamber/scripts/docker.py +64 -0
- developers_chamber/scripts/ecs.py +593 -0
- developers_chamber/scripts/git.py +254 -0
- developers_chamber/scripts/gitlab.py +297 -0
- developers_chamber/scripts/init_aliasses.py +15 -0
- developers_chamber/scripts/jira.py +209 -0
- developers_chamber/scripts/project.py +846 -0
- developers_chamber/scripts/qa.py +93 -0
- developers_chamber/scripts/sh.py +15 -0
- developers_chamber/scripts/slack.py +49 -0
- developers_chamber/scripts/toggle.py +194 -0
- developers_chamber/scripts/version.py +120 -0
- developers_chamber/slack_utils.py +52 -0
- developers_chamber/toggle_utils.py +150 -0
- developers_chamber/types.py +64 -0
- developers_chamber/utils.py +153 -0
- developers_chamber/version_utils.py +194 -0
- developers_chamber-0.1.39.dist-info/METADATA +46 -0
- developers_chamber-0.1.39.dist-info/RECORD +41 -0
- developers_chamber-0.1.39.dist-info/WHEEL +5 -0
- developers_chamber-0.1.39.dist-info/entry_points.txt +2 -0
- developers_chamber-0.1.39.dist-info/licenses/LICENSE +21 -0
- developers_chamber-0.1.39.dist-info/top_level.txt +1 -0
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
import logging.config
|
|
3
|
+
import os
|
|
4
|
+
import sys
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
from importlib.machinery import SourceFileLoader
|
|
7
|
+
|
|
8
|
+
import click_completion
|
|
9
|
+
import coloredlogs
|
|
10
|
+
from dotenv import load_dotenv
|
|
11
|
+
from developers_chamber.utils import INSTALLED_MODULES
|
|
12
|
+
|
|
13
|
+
for config_path in (Path.home(), Path.cwd()):
|
|
14
|
+
if (config_path / ".pydev").exists() and (config_path / ".pydev").is_dir():
|
|
15
|
+
for file in (config_path / ".pydev").iterdir():
|
|
16
|
+
if (
|
|
17
|
+
file.is_file()
|
|
18
|
+
and file.suffix == ".conf"
|
|
19
|
+
and not file.name.startswith("~")
|
|
20
|
+
):
|
|
21
|
+
load_dotenv(dotenv_path=str(file), override=True)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
from developers_chamber.scripts import cli
|
|
25
|
+
|
|
26
|
+
if "bitbucket" in INSTALLED_MODULES:
|
|
27
|
+
from developers_chamber.scripts.bitbucket import *
|
|
28
|
+
from developers_chamber.scripts.docker import *
|
|
29
|
+
|
|
30
|
+
if "aws" in INSTALLED_MODULES:
|
|
31
|
+
from developers_chamber.scripts.ecs import *
|
|
32
|
+
|
|
33
|
+
if "git" in INSTALLED_MODULES:
|
|
34
|
+
from developers_chamber.scripts.git import *
|
|
35
|
+
|
|
36
|
+
from developers_chamber.scripts.gitlab import *
|
|
37
|
+
|
|
38
|
+
if "jira" in INSTALLED_MODULES:
|
|
39
|
+
from developers_chamber.scripts.jira import *
|
|
40
|
+
|
|
41
|
+
if "qa" in INSTALLED_MODULES:
|
|
42
|
+
from developers_chamber.scripts.qa import *
|
|
43
|
+
|
|
44
|
+
from developers_chamber.scripts.sh import *
|
|
45
|
+
|
|
46
|
+
if "slack" in INSTALLED_MODULES:
|
|
47
|
+
from developers_chamber.scripts.slack import *
|
|
48
|
+
|
|
49
|
+
if "toggle" in INSTALLED_MODULES:
|
|
50
|
+
from developers_chamber.scripts.toggle import *
|
|
51
|
+
|
|
52
|
+
from developers_chamber.scripts.version import *
|
|
53
|
+
from developers_chamber.scripts.project import *
|
|
54
|
+
|
|
55
|
+
click_completion.init()
|
|
56
|
+
|
|
57
|
+
# Import external scripts
|
|
58
|
+
for base_path in (Path.home(), Path.cwd()):
|
|
59
|
+
if (base_path / ".pydev" / "scripts" / "__init__.py").exists():
|
|
60
|
+
SourceFileLoader(
|
|
61
|
+
"*", str(base_path / ".pydev" / "scripts" / "__init__.py")
|
|
62
|
+
).load_module()
|
|
63
|
+
|
|
64
|
+
from developers_chamber.scripts.init_aliasses import *
|
|
65
|
+
|
|
66
|
+
coloredlogs.install(milliseconds=True)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
@cli.command()
|
|
70
|
+
@click.option(
|
|
71
|
+
"--append/--overwrite", help="Append the completion code to the file", default=None
|
|
72
|
+
)
|
|
73
|
+
@click.option(
|
|
74
|
+
"-i", "--case-insensitive/--no-case-insensitive", help="Case insensitive completion"
|
|
75
|
+
)
|
|
76
|
+
@click.argument(
|
|
77
|
+
"shell",
|
|
78
|
+
required=False,
|
|
79
|
+
type=click_completion.DocumentedChoice(click_completion.core.shells),
|
|
80
|
+
)
|
|
81
|
+
@click.argument("path", required=False)
|
|
82
|
+
def init_completion(append, case_insensitive, shell, path):
|
|
83
|
+
"""Install the pydev completion"""
|
|
84
|
+
extra_env = {"_PYDEV_CASE_INSENSITIVE_COMPLETE": "ON"} if case_insensitive else {}
|
|
85
|
+
shell, path = click_completion.core.install(
|
|
86
|
+
shell=shell, path=path, append=append, extra_env=extra_env
|
|
87
|
+
)
|
|
88
|
+
click.echo("{} completion installed in {}".format(shell, path))
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
if __name__ == "__main__":
|
|
92
|
+
cli()
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import requests
|
|
2
|
+
from click import UsageError
|
|
3
|
+
from requests.auth import HTTPBasicAuth
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def get_commit_builds(username, password, repository_name, commit):
|
|
7
|
+
response = requests.get(
|
|
8
|
+
"https://api.bitbucket.org/2.0/repositories/{repository}/commit/{commit}/statuses".format(
|
|
9
|
+
repository=repository_name, commit=commit
|
|
10
|
+
),
|
|
11
|
+
headers={"content-type": "application/json"},
|
|
12
|
+
auth=HTTPBasicAuth(username, password),
|
|
13
|
+
)
|
|
14
|
+
if response.status_code != 200:
|
|
15
|
+
raise UsageError("Bitbucket error: {}".format(response.content.decode("utf-8")))
|
|
16
|
+
return [
|
|
17
|
+
build_data
|
|
18
|
+
for build_data in response.json()["values"]
|
|
19
|
+
if build_data["type"] == "build"
|
|
20
|
+
]
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def get_current_user_uuid(username, password):
|
|
24
|
+
response = requests.get(
|
|
25
|
+
"https://api.bitbucket.org/2.0/user",
|
|
26
|
+
headers={"content-type": "application/json"},
|
|
27
|
+
auth=HTTPBasicAuth(username, password),
|
|
28
|
+
)
|
|
29
|
+
if response.status_code != 200:
|
|
30
|
+
raise UsageError("Bitbucket error: {}".format(response.content.decode("utf-8")))
|
|
31
|
+
return response.json()["uuid"]
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def get_default_reviewers(username, password, repository_name):
|
|
35
|
+
url = "https://api.bitbucket.org/2.0/repositories/{repository}/default-reviewers".format(
|
|
36
|
+
repository=repository_name
|
|
37
|
+
)
|
|
38
|
+
response = requests.get(
|
|
39
|
+
url,
|
|
40
|
+
headers={"content-type": "application/json"},
|
|
41
|
+
auth=HTTPBasicAuth(username, password),
|
|
42
|
+
)
|
|
43
|
+
if response.status_code != 200:
|
|
44
|
+
raise UsageError("Bitbucket error: {}".format(response.content.decode("utf-8")))
|
|
45
|
+
current_user_uuid = get_current_user_uuid(username, password)
|
|
46
|
+
return [
|
|
47
|
+
{"uuid": reviewer_data["uuid"]}
|
|
48
|
+
for reviewer_data in response.json()["values"]
|
|
49
|
+
if reviewer_data["uuid"] != current_user_uuid
|
|
50
|
+
]
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def create_pull_request(
|
|
54
|
+
username,
|
|
55
|
+
password,
|
|
56
|
+
title,
|
|
57
|
+
description,
|
|
58
|
+
source_branch_name,
|
|
59
|
+
destination_branch_name,
|
|
60
|
+
repository_name,
|
|
61
|
+
):
|
|
62
|
+
url = "https://api.bitbucket.org/2.0/repositories/{repository}/pullrequests".format(
|
|
63
|
+
repository=repository_name
|
|
64
|
+
)
|
|
65
|
+
json_data = {
|
|
66
|
+
"title": title,
|
|
67
|
+
"description": description,
|
|
68
|
+
"source": {"branch": {"name": source_branch_name}},
|
|
69
|
+
"destination": {"branch": {"name": destination_branch_name}},
|
|
70
|
+
"reviewers": get_default_reviewers(username, password, repository_name),
|
|
71
|
+
}
|
|
72
|
+
response = requests.post(
|
|
73
|
+
url,
|
|
74
|
+
headers={"content-type": "application/json"},
|
|
75
|
+
json=json_data,
|
|
76
|
+
auth=HTTPBasicAuth(username, password),
|
|
77
|
+
)
|
|
78
|
+
if response.status_code != 201:
|
|
79
|
+
raise UsageError("Bitbucket error: {}".format(response.content.decode("utf-8")))
|
|
80
|
+
return response.json()["links"]["html"]["href"]
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def create_merge_release_pull_request(
|
|
84
|
+
username, password, source_branch_name, destination_branch_name, repository_name
|
|
85
|
+
):
|
|
86
|
+
return create_pull_request(
|
|
87
|
+
username=username,
|
|
88
|
+
password=password,
|
|
89
|
+
title='Merge branch "{}"'.format(source_branch_name),
|
|
90
|
+
description="",
|
|
91
|
+
source_branch_name=source_branch_name,
|
|
92
|
+
destination_branch_name=destination_branch_name,
|
|
93
|
+
repository_name=repository_name,
|
|
94
|
+
)
|
|
File without changes
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import re
|
|
2
|
+
import shlex
|
|
3
|
+
from gettext import gettext as _
|
|
4
|
+
|
|
5
|
+
import click
|
|
6
|
+
from click.formatting import wrap_text
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def find_and_replace_command_variable(arg, command, index):
|
|
10
|
+
match = re.match(r"^--(?P<arg_name>[^=\ ]+)[\ =](?P<arg_value>.+)", arg)
|
|
11
|
+
if match:
|
|
12
|
+
arg_name, arg_value = match.groups()
|
|
13
|
+
if f"${arg_name}" in command:
|
|
14
|
+
return True, command.replace(f"${arg_name}", arg_value)
|
|
15
|
+
elif f'${arg_name.replace("-", "_")}' in command:
|
|
16
|
+
return True, command.replace(f'${arg_name.replace("-", "_")}', arg_value)
|
|
17
|
+
if f"${index}" in command:
|
|
18
|
+
return True, command.replace(f"${index}", arg)
|
|
19
|
+
return False, command
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def parse_alias(alias):
|
|
23
|
+
from developers_chamber.scripts import cli
|
|
24
|
+
|
|
25
|
+
command_parts = list(shlex.split(alias))
|
|
26
|
+
used_command_parts = []
|
|
27
|
+
|
|
28
|
+
click_command = cli
|
|
29
|
+
while command_parts:
|
|
30
|
+
command_part = command_parts[0]
|
|
31
|
+
current_click_command = click_command.get_command(None, command_part)
|
|
32
|
+
if current_click_command is None:
|
|
33
|
+
raise click.ClickException(
|
|
34
|
+
'Command to the alias "{}" cannot be found'.format(alias)
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
used_command_parts.append(command_parts.pop(0))
|
|
38
|
+
click_command = current_click_command
|
|
39
|
+
if not isinstance(current_click_command, click.Group):
|
|
40
|
+
break
|
|
41
|
+
return used_command_parts, command_parts, click_command, alias
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class AliasCommand(click.Command):
|
|
45
|
+
|
|
46
|
+
def __init__(self, commands, *args, **kwargs):
|
|
47
|
+
super().__init__(*args, **kwargs)
|
|
48
|
+
self._parse_alias(commands)
|
|
49
|
+
|
|
50
|
+
def _parse_alias(self, commands):
|
|
51
|
+
if isinstance(commands, str):
|
|
52
|
+
commands = [commands]
|
|
53
|
+
description = ""
|
|
54
|
+
elif isinstance(commands, list):
|
|
55
|
+
commands = commands
|
|
56
|
+
description = ""
|
|
57
|
+
elif isinstance(commands, dict):
|
|
58
|
+
description = commands["description"]
|
|
59
|
+
commands = commands["command"]
|
|
60
|
+
else:
|
|
61
|
+
raise click.ClickException("Invalid alias type")
|
|
62
|
+
|
|
63
|
+
commands = [commands] if isinstance(commands, str) else commands
|
|
64
|
+
self.aliases = [(parse_alias(alias)) for alias in commands]
|
|
65
|
+
self.short_help = description
|
|
66
|
+
|
|
67
|
+
def format_help_text(self, ctx, formatter) -> None:
|
|
68
|
+
formatter.write_paragraph()
|
|
69
|
+
with formatter.indentation():
|
|
70
|
+
if self.short_help:
|
|
71
|
+
formatter.write_text(self.short_help)
|
|
72
|
+
formatter.write_paragraph()
|
|
73
|
+
formatter.write_text("Alias to commands: ")
|
|
74
|
+
with formatter.indentation():
|
|
75
|
+
for (
|
|
76
|
+
used_command_parts,
|
|
77
|
+
remaining_command_parts,
|
|
78
|
+
click_command,
|
|
79
|
+
alias_str,
|
|
80
|
+
) in self.aliases:
|
|
81
|
+
formatter.write_text("* " + " ".join(used_command_parts))
|
|
82
|
+
if remaining_command_parts:
|
|
83
|
+
with formatter.indentation(), formatter.indentation():
|
|
84
|
+
formatter.write_text(" ".join(remaining_command_parts))
|
|
85
|
+
|
|
86
|
+
def format_epilog(self, ctx, formatter) -> None:
|
|
87
|
+
with formatter.section(_("Commands in alias help")):
|
|
88
|
+
for (
|
|
89
|
+
used_command_parts,
|
|
90
|
+
remaining_command_parts,
|
|
91
|
+
click_command,
|
|
92
|
+
alias_str,
|
|
93
|
+
) in self.aliases:
|
|
94
|
+
with formatter.indentation():
|
|
95
|
+
formatter.write_paragraph()
|
|
96
|
+
formatter.write_text("* " + " ".join(used_command_parts))
|
|
97
|
+
with formatter.indentation(), formatter.indentation():
|
|
98
|
+
click_command.format_help_text(ctx, formatter)
|
|
99
|
+
click_command.format_options(ctx, formatter)
|
|
100
|
+
formatter.write_paragraph()
|
|
101
|
+
|
|
102
|
+
def shell_complete(self, ctx, param):
|
|
103
|
+
from click.shell_completion import CompletionItem
|
|
104
|
+
|
|
105
|
+
return [CompletionItem(param, type="file")]
|
|
106
|
+
|
|
107
|
+
def invoke(self, ctx):
|
|
108
|
+
from developers_chamber.scripts import cli
|
|
109
|
+
|
|
110
|
+
for i, (
|
|
111
|
+
used_command_parts,
|
|
112
|
+
remaining_command_parts,
|
|
113
|
+
click_command,
|
|
114
|
+
alias_str,
|
|
115
|
+
) in enumerate(self.aliases):
|
|
116
|
+
if i == 0:
|
|
117
|
+
alias_args = []
|
|
118
|
+
|
|
119
|
+
for index, arg in enumerate(ctx.args, start=1):
|
|
120
|
+
replaced_arg, alias_str = find_and_replace_command_variable(
|
|
121
|
+
arg, alias_str, index
|
|
122
|
+
)
|
|
123
|
+
if not replaced_arg:
|
|
124
|
+
alias_args.append(arg)
|
|
125
|
+
|
|
126
|
+
if "$@" in alias_str:
|
|
127
|
+
alias_str = alias_str.replace("$@", " ".join(ctx.args))
|
|
128
|
+
alias_args = []
|
|
129
|
+
|
|
130
|
+
cli.main(args=shlex.split(alias_str) + alias_args, standalone_mode=False)
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import click
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class RequiredIfNotEmpty(click.Option):
|
|
5
|
+
|
|
6
|
+
def __init__(self, *args, **kwargs):
|
|
7
|
+
self.required_if_not_empty = kwargs.pop("required_if_empty")
|
|
8
|
+
if not self.required_if_not_empty:
|
|
9
|
+
raise ValueError(
|
|
10
|
+
'"required_if_not_empty" argument is required for "RequiredIfNotEmpty" option'
|
|
11
|
+
)
|
|
12
|
+
kwargs["help"] += " NOTE: This option is required with {}".format(
|
|
13
|
+
self.required_if_not_empty
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
super().__init__(*args, **kwargs)
|
|
17
|
+
|
|
18
|
+
def handle_parse_result(self, ctx, opts, args):
|
|
19
|
+
if self.required_if_not_empty in opts:
|
|
20
|
+
if self.name not in opts:
|
|
21
|
+
raise click.UsageError(
|
|
22
|
+
"Illegal usage: {} is required with {}".format(
|
|
23
|
+
self.name, self.required_if_not_empty
|
|
24
|
+
)
|
|
25
|
+
)
|
|
26
|
+
else:
|
|
27
|
+
self.prompt = None
|
|
28
|
+
|
|
29
|
+
return super().handle_parse_result(ctx, opts, args)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class ContainerDirToCopyType(click.ParamType):
|
|
33
|
+
|
|
34
|
+
name = "container_dir_to_copy"
|
|
35
|
+
|
|
36
|
+
def convert(self, value, param, ctx):
|
|
37
|
+
try:
|
|
38
|
+
container_name, container_dir, host_dir = value.split(":")
|
|
39
|
+
return container_name, container_dir, host_dir
|
|
40
|
+
except ValueError:
|
|
41
|
+
self.fail(
|
|
42
|
+
'Invalid value "{}" format must be "DOCKER_CONTAINER_NAME:CONTAINER_DIRECTORY:HOST_DIRECTORY"'.format(
|
|
43
|
+
value
|
|
44
|
+
),
|
|
45
|
+
param,
|
|
46
|
+
ctx,
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
class ContainerCommandType(click.ParamType):
|
|
51
|
+
|
|
52
|
+
name = "container_command_type"
|
|
53
|
+
|
|
54
|
+
def convert(self, value, param, ctx):
|
|
55
|
+
if value:
|
|
56
|
+
try:
|
|
57
|
+
container_name, command = value.split(":")
|
|
58
|
+
return container_name, command
|
|
59
|
+
except ValueError:
|
|
60
|
+
self.fail(
|
|
61
|
+
'Invalid value "{}" format must be "DOCKER_CONTAINER_NAME:COMMAND"'.format(
|
|
62
|
+
value
|
|
63
|
+
),
|
|
64
|
+
param,
|
|
65
|
+
ctx,
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
class ContainerEnvironment(click.ParamType):
|
|
70
|
+
|
|
71
|
+
name = "container_environment"
|
|
72
|
+
|
|
73
|
+
def convert(self, value, param, ctx):
|
|
74
|
+
try:
|
|
75
|
+
return {
|
|
76
|
+
variable.split("=")[0]: variable.split("=")[1]
|
|
77
|
+
for variable in value.split(" ")
|
|
78
|
+
}
|
|
79
|
+
except ValueError:
|
|
80
|
+
self.fail(
|
|
81
|
+
'Invalid value "{}" format must be "NAME=VALUE [NAME2=VALUE2]"'.format(
|
|
82
|
+
value
|
|
83
|
+
),
|
|
84
|
+
param,
|
|
85
|
+
ctx,
|
|
86
|
+
)
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
from developers_chamber.utils import call_command
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def login_client(username, password, registry):
|
|
5
|
+
call_command(
|
|
6
|
+
["docker", "login", "--username", username, "--password", password, registry]
|
|
7
|
+
)
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def tag(source_image, target_image):
|
|
11
|
+
call_command(["docker", "tag", source_image, target_image])
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def push_image(repository, tag):
|
|
15
|
+
call_command(["docker", "push", "{}:{}".format(repository, tag)])
|