baseshift 0.10.1__py3-none-win_amd64.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.
baseshift/__init__.py ADDED
@@ -0,0 +1,86 @@
1
+ #!/usr/bin/env python3
2
+ import argparse
3
+ import logging
4
+ import os
5
+ import json
6
+ import logging
7
+ import os
8
+ import re
9
+ from time import sleep
10
+ import requests
11
+ import shutil
12
+ import subprocess
13
+ import sys
14
+ import uuid
15
+ import warnings
16
+
17
+ warnings.filterwarnings(
18
+ "ignore", category=UserWarning, module="sentry_sdk.integrations.modules"
19
+ )
20
+
21
+ import psycopg
22
+ import sentry_sdk
23
+
24
+ from . import utils
25
+ from .commands.schema import PGDatabase, PGDatabaseEncoder
26
+
27
+ from .version import VERSION
28
+
29
+ BASESHIFT_ENV_VAR_NAME = "BASESHIFT_ENV"
30
+ DEFAULT_ENVIRONMENT = "production"
31
+ BASESHIFT_DSN_VAR_NAME = "BASESHIFT_SENTRY_DSN"
32
+ DEFAULT_DSN = (
33
+ "https://a74ab2ee702347d3b4461e63a7049dc0@o604958.ingest.sentry.io/6604591"
34
+ )
35
+
36
+
37
+ sentry_sdk.init(
38
+ dsn=os.environ.get(BASESHIFT_DSN_VAR_NAME, DEFAULT_DSN),
39
+ environment=os.environ.get(BASESHIFT_ENV_VAR_NAME, DEFAULT_ENVIRONMENT),
40
+ traces_sample_rate=0.0,
41
+ )
42
+
43
+
44
+ class ArgumentParser(argparse.ArgumentParser):
45
+ def error(self, message):
46
+ self.print_help(sys.stderr)
47
+ self.exit(2, "%s: error: %s\n" % (self.prog, message))
48
+
49
+
50
+ logger = logging.getLogger(__name__)
51
+
52
+ DEFAULT_HOST = "https://app.baseshift.io"
53
+ DEFAULT_HOST_ENV = "BASESHIFT_HOST"
54
+
55
+
56
+ def main(cli_args=None):
57
+ parser = argparse.ArgumentParser(description="Baseshift CLI")
58
+ parser.add_argument("--version", action="version", version=VERSION)
59
+ subparsers = parser.add_subparsers(dest="command", required=True)
60
+
61
+ import pkgutil
62
+ import inspect
63
+ from . import commands
64
+ from .commands.base import BaseCommand
65
+
66
+ for _, name, _ in pkgutil.iter_modules(commands.__path__):
67
+ if name == "base":
68
+ continue
69
+ module = __import__(f"baseshift.commands.{name}", fromlist=[""])
70
+ for _, obj in inspect.getmembers(module, inspect.isclass):
71
+ if issubclass(obj, BaseCommand) and obj is not BaseCommand:
72
+ command_name = name
73
+ command = obj()
74
+ subparser = subparsers.add_parser(command_name)
75
+ command.add_arguments(subparser)
76
+ subparser.set_defaults(func=command.run)
77
+
78
+ cli_args = cli_args or sys.argv[1:]
79
+ args = parser.parse_args(cli_args)
80
+ host = os.environ.get(DEFAULT_HOST_ENV, DEFAULT_HOST)
81
+
82
+ if hasattr(args, "func"):
83
+ args.func(args, host)
84
+ else:
85
+ logger.error("A command is required.")
86
+ parser.print_help()
@@ -0,0 +1,17 @@
1
+ # Build-time configuration for baseshift CLI
2
+ # This file is generated/modified during the build process
3
+
4
+ # Tailscale integration enabled/disabled at build time
5
+ # For baseshift, Tailscale is enabled by default
6
+ TAILSCALE_ENABLED = True
7
+
8
+ # Custom host prompting enabled/disabled at build time
9
+ CUSTOM_HOST_ENABLED = False
10
+
11
+ def is_tailscale_enabled():
12
+ """Check if Tailscale integration is enabled in this build."""
13
+ return TAILSCALE_ENABLED
14
+
15
+ def is_custom_host_enabled():
16
+ """Check if custom host prompting is enabled in this build."""
17
+ return CUSTOM_HOST_ENABLED
baseshift/client.py ADDED
@@ -0,0 +1,76 @@
1
+ import os
2
+ import httpx
3
+ from .version import VERSION
4
+
5
+
6
+ def get_ci_info():
7
+ """Gathers CI/CD environment variables from the environment."""
8
+ CI_PIPELINE_ID = os.environ.get("CI_PIPELINE_ID") or os.environ.get("GITHUB_RUN_ID")
9
+ CI_COMMIT_REF_NAME = os.environ.get("CI_COMMIT_REF_NAME") or os.environ.get(
10
+ "GITHUB_HEAD_REF"
11
+ )
12
+ CI_COMMIT_SHA = os.environ.get("CI_COMMIT_SHA") or os.environ.get("GITHUB_SHA")
13
+ CI_MERGE_REQUEST_IID = os.environ.get("CI_MERGE_REQUEST_IID", "github")
14
+ CI_DEFAULT_BRANCH = os.environ.get("CI_DEFAULT_BRANCH") or os.environ.get(
15
+ "GITHUB_BASE_REF"
16
+ )
17
+ CI_USER = os.environ.get("GITHUB_ACTOR")
18
+
19
+ return {
20
+ "CI_PIPELINE_ID": CI_PIPELINE_ID,
21
+ "CI_COMMIT_REF_NAME": CI_COMMIT_REF_NAME,
22
+ "CI_COMMIT_SHA": CI_COMMIT_SHA,
23
+ "CI_MERGE_REQUEST_IID": CI_MERGE_REQUEST_IID,
24
+ "CI_DEFAULT_BRANCH": CI_DEFAULT_BRANCH,
25
+ "CI_USER": CI_USER,
26
+ }
27
+
28
+
29
+ async def start_clone_api_async(
30
+ dub_uuid: str, org_token: str, server: str, grace: int = None, timeout: int = None, clone_config: dict = None
31
+ ):
32
+ """Asynchronously calls the clone start API with CI/CD info."""
33
+ json_msg = {
34
+ "dubUuid": dub_uuid,
35
+ "orgToken": org_token,
36
+ "VERSION": VERSION,
37
+ **get_ci_info(),
38
+ }
39
+ if grace:
40
+ json_msg["INACTIVITY_GRACE_SECS"] = grace
41
+ if timeout:
42
+ json_msg["INACTIVITY_TIMEOUT_SECS"] = timeout
43
+ if clone_config:
44
+ json_msg["cloneConfig"] = clone_config
45
+
46
+ async with httpx.AsyncClient() as client:
47
+ return await client.post(
48
+ f"{server}/api/clone/start",
49
+ json=json_msg,
50
+ timeout=300.0,
51
+ )
52
+
53
+
54
+ def start_clone_api_sync(
55
+ dub_uuid: str, org_token: str, server: str, grace: int = None, timeout: int = None, clone_config: dict = None
56
+ ):
57
+ """Synchronously calls the clone start API with CI/CD info."""
58
+ json_msg = {
59
+ "dubUuid": dub_uuid,
60
+ "orgToken": org_token,
61
+ "VERSION": VERSION,
62
+ **get_ci_info(),
63
+ }
64
+ if grace:
65
+ json_msg["INACTIVITY_GRACE_SECS"] = grace
66
+ if timeout:
67
+ json_msg["INACTIVITY_TIMEOUT_SECS"] = timeout
68
+ if clone_config:
69
+ json_msg["cloneConfig"] = clone_config
70
+
71
+ with httpx.Client() as client:
72
+ return client.post(
73
+ f"{server}/api/clone/start",
74
+ json=json_msg,
75
+ timeout=300.0,
76
+ )
File without changes
@@ -0,0 +1,6 @@
1
+ class BaseCommand:
2
+ def add_arguments(self, parser):
3
+ raise NotImplementedError
4
+
5
+ def run(self, args, dubhub):
6
+ raise NotImplementedError
@@ -0,0 +1,242 @@
1
+ import json
2
+ import logging
3
+ import os
4
+ import requests
5
+
6
+ import psycopg
7
+
8
+ from .. import utils
9
+ from ..version import VERSION
10
+ from .base import BaseCommand
11
+ from .schema import PGDatabase, PGDatabaseEncoder
12
+
13
+ logger = logging.getLogger(__name__)
14
+
15
+
16
+ class BaseguardCommand(BaseCommand):
17
+ def add_arguments(self, parser):
18
+ baseguard_subparsers = parser.add_subparsers(dest="action", required=True)
19
+
20
+ parser_create_run = baseguard_subparsers.add_parser(
21
+ "create_run", help="Create a baseguard run"
22
+ )
23
+ parser_create_run.add_argument(
24
+ "--projectUuid",
25
+ required=True,
26
+ type=utils.regex_type_uuid,
27
+ help="Uuid of Project",
28
+ )
29
+ parser_create_run.add_argument(
30
+ "--orgToken",
31
+ required=False,
32
+ default=None,
33
+ type=utils.regex_type_uuid,
34
+ help="Uuid of Org Token (CLI > BASESHIFT_ORG_TOKEN > config)",
35
+ )
36
+
37
+ parser_analyse = baseguard_subparsers.add_parser(
38
+ "analyse", help="Analyse a baseguard run"
39
+ )
40
+ parser_analyse.add_argument(
41
+ "--orgToken",
42
+ required=False,
43
+ default=None,
44
+ type=utils.regex_type_uuid,
45
+ help="Uuid of Org Token (CLI > BASESHIFT_ORG_TOKEN > config)",
46
+ )
47
+ parser_analyse.add_argument(
48
+ "--accessToken",
49
+ required=False,
50
+ default=None,
51
+ help="Gitlab/Github Access Token (CLI > BASESHIFT_ACCESS_TOKEN > config)",
52
+ )
53
+
54
+ parser_upload_schema = baseguard_subparsers.add_parser(
55
+ "upload_schema", help="Upload a schema to baseguard"
56
+ )
57
+ parser_upload_schema.add_argument(
58
+ "--orgToken",
59
+ required=False,
60
+ default=None,
61
+ type=utils.regex_type_uuid,
62
+ help="Uuid of Org Token (CLI > BASESHIFT_ORG_TOKEN > config)",
63
+ )
64
+
65
+ def run(self, args, server):
66
+ if args.action == "create_run":
67
+ self.create_run(args, server)
68
+ elif args.action == "analyse":
69
+ self.analyse(args, server)
70
+ elif args.action == "upload_schema":
71
+ self.upload_schema(args, server)
72
+
73
+ def create_run(self, args, server):
74
+ try:
75
+ org_token_val = utils.get_config_value(
76
+ args.orgToken, "BASESHIFT_ORG_TOKEN", "orgToken", required=True
77
+ )
78
+ if not utils.is_valid_uuid(org_token_val):
79
+ logger.error(f"Invalid Organization Token format: {org_token_val}")
80
+ print(
81
+ f"Error: Invalid Organization Token format: {org_token_val}. Please check CLI, BASESHIFT_ORG_TOKEN, or config file."
82
+ )
83
+ return
84
+ except ValueError as e:
85
+ logger.error(f"Configuration error in create_run: {e}")
86
+ print(f"Error: {e}")
87
+ return
88
+
89
+ try:
90
+ CI_COMMIT_SHA = os.environ.get("CI_COMMIT_SHA", None)
91
+ GITHUB_SHA = os.environ.get("GITHUB_SHA", None)
92
+ GITHUB_REF = os.environ.get("GITHUB_REF", None)
93
+ GITHUB_RUN_ID = os.environ.get("GITHUB_RUN_ID", None)
94
+ GITHUB_REPOSITORY = os.environ.get("GITHUB_REPOSITORY", None)
95
+ GITHUB_HEAD_REF = os.environ.get("GITHUB_HEAD_REF", None)
96
+ response = requests.post(
97
+ f"{server}/api/baseguard/create_run",
98
+ json={
99
+ "GITHUB_REF": GITHUB_REF,
100
+ "org_token": org_token_val, # Note: API expects 'org_token' with underscore
101
+ "project_uuid": args.projectUuid,
102
+ "GITHUB_SHA": GITHUB_SHA,
103
+ "CI_COMMIT_SHA": CI_COMMIT_SHA,
104
+ "GITHUB_RUN_ID": GITHUB_RUN_ID,
105
+ "GITHUB_REPOSITORY": GITHUB_REPOSITORY,
106
+ "GITHUB_HEAD_REF": GITHUB_HEAD_REF,
107
+ },
108
+ )
109
+ if response.status_code in [400, 500]:
110
+ logger.error(
111
+ "Failed API call with status code: %s", response.status_code
112
+ )
113
+ logger.error(response.json())
114
+ return
115
+ except Exception as e:
116
+ logger.exception("Error with sending post request:" + str(e))
117
+ return
118
+ try:
119
+ print(response.json())
120
+ except Exception as e:
121
+ logger.exception("Error converting response object to JSON file:" + str(e))
122
+
123
+ def analyse(self, args, server):
124
+ try:
125
+ org_token_val = utils.get_config_value(
126
+ args.orgToken, "BASESHIFT_ORG_TOKEN", "orgToken", required=True
127
+ )
128
+ if not utils.is_valid_uuid(org_token_val):
129
+ logger.error(f"Invalid Organization Token format: {org_token_val}")
130
+ print(
131
+ f"Error: Invalid Organization Token format: {org_token_val}. Please check CLI, BASESHIFT_ORG_TOKEN, or config file."
132
+ )
133
+ return
134
+
135
+ access_token_val = utils.get_config_value(
136
+ args.accessToken, "BASESHIFT_ACCESS_TOKEN", "accessToken", required=True
137
+ )
138
+ # No specific format validation for access_token, presence is key
139
+
140
+ except ValueError as e:
141
+ logger.error(f"Configuration error in analyse: {e}")
142
+ print(f"Error: {e}")
143
+ return
144
+
145
+ try:
146
+ CI_API_V4_URL = os.environ.get("CI_API_V4_URL", None)
147
+ CI_PROJECT_ID = os.environ.get("CI_PROJECT_ID", None)
148
+ CI_MERGE_REQUEST_IID = os.environ.get("CI_MERGE_REQUEST_IID", None)
149
+ CI_DEFAULT_BRANCH = os.environ.get("CI_DEFAULT_BRANCH", None)
150
+ CI_COMMIT_SHA = os.environ.get("CI_COMMIT_SHA", None)
151
+ if CI_DEFAULT_BRANCH is None:
152
+ CI_DEFAULT_BRANCH = os.environ.get("GITHUB_BASE_REF", None)
153
+ GITHUB_REPOSITORY = os.environ.get("GITHUB_REPOSITORY", None)
154
+ GITHUB_SHA = os.environ.get("GITHUB_SHA", None)
155
+ GITHUB_REF = os.environ.get("GITHUB_REF", None)
156
+ GITHUB_PR_REF = os.environ.get("PR_REF", None)
157
+ GITHUB_RUN_ID = os.environ.get("GITHUB_RUN_ID", None)
158
+ if CI_PROJECT_ID is None:
159
+ GITHUB_OR_GITLAB = "github"
160
+ else:
161
+ GITHUB_OR_GITLAB = "gitlab"
162
+ except Exception as e:
163
+ logger.exception("Error converting JSON file to JSON object:" + str(e))
164
+ return
165
+ try:
166
+ response = requests.post(
167
+ f"{server}/api/baseguard/analyse",
168
+ json={
169
+ "orgToken": org_token_val,
170
+ "ACCESS_TOKEN": access_token_val,
171
+ "CI_API_V4_URL": CI_API_V4_URL,
172
+ "CI_PROJECT_ID": CI_PROJECT_ID,
173
+ "CI_COMMIT_SHA": CI_COMMIT_SHA,
174
+ "CI_MERGE_REQUEST_IID": CI_MERGE_REQUEST_IID,
175
+ "CI_DEFAULT_BRANCH": CI_DEFAULT_BRANCH,
176
+ "GITHUB_OR_GITLAB": GITHUB_OR_GITLAB,
177
+ "GITHUB_REPOSITORY": GITHUB_REPOSITORY,
178
+ "GITHUB_SHA": GITHUB_SHA,
179
+ "GITHUB_REF": GITHUB_REF,
180
+ "VERSION": VERSION,
181
+ "GITHUB_PR_REF": GITHUB_PR_REF,
182
+ "GITHUB_RUN_ID": GITHUB_RUN_ID,
183
+ },
184
+ )
185
+ if response.status_code in [400, 500]:
186
+ logger.error(
187
+ "Failed API call with status code: %s", response.status_code
188
+ )
189
+ logger.error(response.json())
190
+ return
191
+ # print(f"See your results here: {response}")
192
+ except Exception as e:
193
+ logger.exception("Error with sending post request:" + str(e))
194
+
195
+ def upload_schema(self, args, server):
196
+ try:
197
+ org_token_val = utils.get_config_value(
198
+ args.orgToken, "BASESHIFT_ORG_TOKEN", "orgToken", required=True
199
+ )
200
+ if not utils.is_valid_uuid(org_token_val):
201
+ logger.error(f"Invalid Organization Token format: {org_token_val}")
202
+ print(
203
+ f"Error: Invalid Organization Token format: {org_token_val}. Please check CLI, BASESHIFT_ORG_TOKEN, or config file."
204
+ )
205
+ return
206
+ except ValueError as e:
207
+ logger.error(f"Configuration error in upload_schema: {e}")
208
+ print(f"Error: {e}")
209
+ return
210
+
211
+ try:
212
+ GITHUB_SHA = os.environ.get("GITHUB_SHA", None)
213
+ GITHUB_RUN_ID = os.environ.get("GITHUB_RUN_ID", None)
214
+ # redundant for now because the driver would looks for these anyway
215
+ conn_params = {
216
+ "dbname": os.getenv("PGDATABASE", default="postgres"),
217
+ "user": os.getenv("PGUSER", default="postgres"),
218
+ "password": os.getenv("PGPASSWORD", default=""),
219
+ "host": os.getenv("PGHOST", default="postgres"),
220
+ "port": os.getenv("PGPORT", default="5432"),
221
+ }
222
+ conn = psycopg.connect(**conn_params)
223
+ db = PGDatabase(conn)
224
+ db.load()
225
+ json_str = json.dumps(db, cls=PGDatabaseEncoder)
226
+ response = requests.post(
227
+ f"{server}/api/baseguard/upload_schema",
228
+ json={
229
+ "orgToken": org_token_val,
230
+ "pg_schema_dump": json.loads(json_str),
231
+ "GITHUB_SHA": GITHUB_SHA,
232
+ "GITHUB_RUN_ID": GITHUB_RUN_ID,
233
+ },
234
+ )
235
+ if response.status_code in [400, 500]:
236
+ logger.error(
237
+ "Failed API call with status code: %s", response.status_code
238
+ )
239
+ logger.error(response.json())
240
+ return
241
+ except Exception as e:
242
+ logger.exception("Error with sending post request:" + str(e))