Skip to content

Commit 46560be

Browse files
author
boonhapus
committed
Merge branch 'dev'
2 parents cda9599 + 66bb650 commit 46560be

File tree

17 files changed

+150
-52
lines changed

17 files changed

+150
-52
lines changed

CONTRIBUTING.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,23 @@ requires some extra dependencies.
7979
uv pip install -e ".[docs]"
8080
```
8181

82+
Set your environment variables so that the generated documentation (`hooks/cli_reference_generator.py`) can be built
83+
against a valid ThoughtSpot cluster.
84+
85+
`Windows`
86+
```powershell
87+
$env:CS_TOOLS_THOUGHTSPOT__URL = "https://<YOUR-THOUGHTSPOT-CLUSTER>.thoughtspot.cloud"
88+
$env:CS_TOOLS_THOUGHTSPOT__USERNAME = "<YOUR-THOUGHTSPOT-USERNAME>"
89+
$env:CS_TOOLS_THOUGHTSPOT__PASSWORD = "<YOUR-THOUGHTSPOT-PASSWORD>"
90+
```
91+
92+
`POSIX (Mac, Linux)`
93+
```bash
94+
CS_TOOLS_THOUGHTSPOT__URL = "https://<YOUR-THOUGHTSPOT-CLUSTER>.thoughtspot.cloud"
95+
CS_TOOLS_THOUGHTSPOT__USERNAME = "<YOUR-THOUGHTSPOT-USERNAME>"
96+
CS_TOOLS_THOUGHTSPOT__PASSWORD = "<YOUR-THOUGHTSPOT-PASSWORD>"
97+
```
98+
8299
Note that the [__CS Tools__ website](https://thoughtspot.github.io/cs_tools/) is only updated when a new version is
83100
released so your contribution might not show up for a while.
84101

cs_tools/cli/_logging.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ def __init__(self):
5353
"secret_key": (r'secret_key[\'"]\s*:\s*[\'"][^\'\"]+[\'"]', r'secret_key": "****"'),
5454
"token": (r'token[\'"]\s*:\s*[\'"][^\'\"]+[\'"]', r'token": "****"'),
5555
"authorization": (r"authorization\s*:\s*bearer\s+\S+", r"authorization: bearer ****"),
56+
"jsessionid": (r"JSESSIONID=[^\'\"]+;", r"JESSIONID=****"),
5657
# ADD MORE PATTERNS AS NEEDED
5758
}
5859

cs_tools/cli/commands/log.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ def report(
1919
...,
2020
metavar="DIRECTORY",
2121
help="Where to export the logs.",
22-
click_type=custom_types.Directory(exists=True, make=True),
22+
click_type=custom_types.Directory(exists=False, make=True),
2323
),
2424
latest: int = typer.Option(1, help="Number of most recent logfiles to export.", min=1),
2525
) -> _types.ExitCode:

cs_tools/cli/commands/main.py

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
import logging
55
import random
66

7-
from cs_tools import __project__, __version__, _compat, datastructures, errors
7+
from cs_tools import __project__, __version__, _compat, _types, datastructures, errors
88
from cs_tools.cli._logging import _setup_logging
99
from cs_tools.cli.ux import RICH_CONSOLE, AsyncTyper
1010
from cs_tools.settings import _meta_config as meta
@@ -52,10 +52,8 @@ def main(version: bool = typer.Option(False, "--version", help="Show the version
5252
raise typer.Exit(0)
5353

5454

55-
def run() -> int:
56-
"""
57-
Entrypoint into cs_tools.
58-
"""
55+
def run() -> _types.ExitCode:
56+
"""Entrypoint into cs_tools."""
5957
from cs_tools.cli import _monkey # noqa: F401
6058
from cs_tools.cli.commands import (
6159
config as config_command,
@@ -85,7 +83,7 @@ def run() -> int:
8583

8684
except click.ClickException as e:
8785
return_code = 1
88-
log.error(e)
86+
log.error(f"{e.format_message()}")
8987
log.debug("More info..", exc_info=True)
9088

9189
except errors.CSToolsError as e:

cs_tools/cli/commands/self.py

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ def info(
3434
directory: pathlib.Path = typer.Option(
3535
None,
3636
help="Where to export the info to share with the CS Tools team.",
37-
click_type=custom_types.Directory(exists=True),
37+
click_type=custom_types.Directory(exists=False, make=True),
3838
),
3939
anonymous: bool = typer.Option(False, "--anonymous", help="remove personal references from the output"),
4040
) -> _types.ExitCode:
@@ -102,11 +102,13 @@ def sync() -> _types.ExitCode:
102102
@app.command(name="update")
103103
def update(
104104
beta: custom_types.Version = typer.Option(None, "--beta", help="The specific beta version to fetch from Github."),
105-
offline: custom_types.Directory = typer.Option(None, help="Install cs_tools from a local directory."),
105+
offline: pathlib.Path = typer.Option(
106+
None,
107+
help="Install cs_tools from a local directory.",
108+
click_type=custom_types.Directory(),
109+
),
106110
) -> _types.ExitCode:
107111
"""Upgrade CS Tools."""
108-
assert isinstance(offline, pathlib.Path), "offline directory must be a pathlib.Path"
109-
110112
if offline is not None:
111113
cs_tools_venv.offline_index = offline
112114
where = offline.as_posix()
@@ -122,7 +124,11 @@ def update(
122124
@app.command(name="export", hidden=True)
123125
@app.command(name="download")
124126
def _make_offline_distributable(
125-
directory: custom_types.Directory = typer.Option(help="Location to export the python distributable to."),
127+
directory: pathlib.Path = typer.Option(
128+
...,
129+
help="Location to export the python distributable to.",
130+
click_type=custom_types.Directory(exists=False, make=True),
131+
),
126132
platform: str = typer.Option(help="A tag describing the target environment architecture, see --help for details."),
127133
python_version: custom_types.Version = typer.Option(
128134
metavar="X.Y", help="The major and minor version of the target Python environment, see --help for details"
@@ -139,6 +145,9 @@ def _make_offline_distributable(
139145
140146
Q. How can I find my python version?
141147
>>> [fg-secondary]python -c "import sys; print(f'{sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro}')"[/]
148+
149+
Q. Do I need anything other than python?
150+
A. You also likely need a rust compiler, which can be installed via Rust ( https://www.rust-lang.org/tools/install ).
142151
"""
143152
assert isinstance(directory, pathlib.Path), "directory must be a pathlib.Path"
144153

cs_tools/cli/custom_types.py

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,12 @@
99

1010
from awesomeversion import AwesomeVersion, AwesomeVersionStrategy, AwesomeVersionStrategyException
1111
import click
12-
import pydantic
1312
import toml
1413

1514
from cs_tools import datastructures, utils
1615
from cs_tools.sync import base
1716

18-
log = logging.getLogger(__name__)
17+
_LOG = logging.getLogger(__name__)
1918

2019

2120
class CustomType(click.ParamType):
@@ -113,7 +112,7 @@ def convert(self, value: Any, param: Optional[click.Parameter], ctx: Optional[cl
113112
class Directory(CustomType):
114113
"""Convert STR to DIRECTORY PATH."""
115114

116-
def __init__(self, exists: bool = False, make: bool = False):
115+
def __init__(self, exists: bool = True, make: bool = False):
117116
self.exists = exists
118117
self.make = make
119118

@@ -124,14 +123,15 @@ def get_metavar(self, param: click.Parameter) -> str: # noqa: ARG002
124123
def convert(self, value: Any, param: Optional[click.Parameter], ctx: Optional[click.Context]) -> pathlib.Path:
125124
"""Coerce string into a pathlib.Path.is_dir()."""
126125
try:
127-
path = pydantic.TypeAdapter(pydantic.DirectoryPath).validate_python(value)
128-
except pydantic.ValidationError as e:
129-
self.fail(message="\n".join(_["msg"] for _ in e.errors()), param=param, ctx=ctx)
126+
path = pathlib.Path(value)
127+
except TypeError:
128+
self.fail(message="Not a valid path", param=param, ctx=ctx)
130129

131130
if self.exists and not path.exists():
132131
self.fail(message="Directory does not exist", param=param, ctx=ctx)
133132

134-
if self.make:
133+
if not path.exists() and self.make:
134+
_LOG.warning(f"The directory '{path}' does not yet exist, creating it..")
135135
path.mkdir(parents=True, exist_ok=True)
136136

137137
return path.resolve()
@@ -159,7 +159,7 @@ def _parse_syncer_configuration(
159159
self.fail(message=f"Syncer definition file does not exist at '{definition_spec}'.", param=param, ctx=ctx)
160160

161161
except toml.TomlDecodeError:
162-
log.debug(f"Syncer definition file '{definition_spec}' is invalid TOML.", exc_info=True)
162+
_LOG.debug(f"Syncer definition file '{definition_spec}' is invalid TOML.", exc_info=True)
163163
self.fail(message=f"Syncer definition file '{definition_spec}' is invalid TOML.", param=param, ctx=ctx)
164164

165165
return options
@@ -175,7 +175,7 @@ def convert(self, value: Any, param: Optional[click.Parameter], ctx: Optional[cl
175175
protocol, _, definition_spec = value.partition("://")
176176

177177
# fmt: off
178-
log.debug(f"Registering syncer: {protocol.lower()}")
178+
_LOG.debug(f"Registering syncer: {protocol.lower()}")
179179
syncer_base_dir = CS_TOOLS_PKG_DIR / "sync" / protocol
180180
syncer_manifest = base.SyncerManifest.model_validate_json(syncer_base_dir.joinpath("MANIFEST.json").read_text())
181181
syncer_options = self._parse_syncer_configuration(definition_spec, param=param, ctx=ctx)
@@ -186,7 +186,7 @@ def convert(self, value: Any, param: Optional[click.Parameter], ctx: Optional[cl
186186
if issubclass(SyncerClass, base.DatabaseSyncer) and self.models is not None:
187187
syncer_options["models"] = self.models
188188

189-
log.info(f"Initializing syncer: {SyncerClass}")
189+
_LOG.info(f"Initializing syncer: {SyncerClass}")
190190
syncer = SyncerClass(**syncer_options)
191191

192192
# CLEAN UP DATABASE RESOURCES.
@@ -236,7 +236,7 @@ def convert(self, value: Any, param: Optional[click.Parameter], ctx: Optional[cl
236236
)
237237

238238
except Exception:
239-
log.debug(f"Could not coerce all values to '{self.type_caster}', {values}", exc_info=True)
239+
_LOG.debug(f"Could not coerce all values to '{self.type_caster}', {values}", exc_info=True)
240240
self.fail(message=f"Could not coerce all values to '{self.type_caster}', {values}", param=param, ctx=ctx)
241241

242242
return values

cs_tools/cli/tools/bulk-deleter/app.py

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import collections
44
import datetime as dt
55
import logging
6+
import pathlib
67
import threading
78
import time
89

@@ -49,9 +50,10 @@ def downstream(
4950
help="protocol and path for options to pass to the syncer",
5051
rich_help_panel="Syncer Options",
5152
),
52-
directory: custom_types.Directory = typer.Option(
53+
directory: pathlib.Path = typer.Option(
5354
None,
54-
help="folder/directory to export TML objects to",
55+
help="Folder/directory to export TML objects to",
56+
click_type=custom_types.Directory(exists=False, make=True),
5557
rich_help_panel="TML Export Options",
5658
),
5759
export_only: bool = typer.Option(
@@ -60,7 +62,7 @@ def downstream(
6062
help="export all tagged content, but don't remove it from ThoughtSpot",
6163
rich_help_panel="TML Export Options",
6264
),
63-
org_override: str = typer.Option(None, "--org", help="The org to import TML to."),
65+
org_override: str = typer.Option(None, "--org", help="The Org to switch to before performing actions."),
6466
) -> _types.ExitCode:
6567
"""
6668
Delete all downstream dependencies of an object.
@@ -215,9 +217,10 @@ def from_tag(
215217
tag_name: str = typer.Option(..., "--tag", help="case sensitive name to tag stale objects with"),
216218
tag_only: bool = typer.Option(False, "--tag-only", help="delete only the tag itself, not the objects"),
217219
no_prompt: bool = typer.Option(False, "--no-prompt", help="disable the confirmation prompt"),
218-
directory: custom_types.Directory = typer.Option(
220+
directory: pathlib.Path = typer.Option(
219221
None,
220-
help="folder/directory to export TML objects to",
222+
help="Folder/directory to export TML objects to",
223+
click_type=custom_types.Directory(exists=False, make=True),
221224
rich_help_panel="TML Export Options",
222225
),
223226
export_only: bool = typer.Option(
@@ -226,13 +229,17 @@ def from_tag(
226229
help="export all tagged content, but don't remove it from ThoughtSpot",
227230
rich_help_panel="TML Export Options",
228231
),
232+
org_override: str = typer.Option(None, "--org", help="The Org to switch to before performing actions."),
229233
) -> _types.ExitCode:
230234
"""Delete content with the identified --tag."""
231235
if export_only and directory is None:
232236
raise typer.BadParameter("You must provide a directory to export to when using --export-only.")
233237

234238
ts = ctx.obj.thoughtspot
235239

240+
if ts.session_context.thoughtspot.is_orgs_enabled and org_override is not None:
241+
ts.switch_org(org_id=org_override)
242+
236243
try:
237244
c = workflows.metadata.fetch_one(tag_name, "TAG", http=ts.api)
238245
_ = utils.run_sync(c)
@@ -338,9 +345,10 @@ def from_tabular(
338345
),
339346
deletion: str = typer.Option(..., help="directive to find content to delete", rich_help_panel="Syncer Options"),
340347
no_prompt: bool = typer.Option(False, "--no-prompt", help="disable the confirmation prompt"),
341-
directory: custom_types.Directory = typer.Option(
348+
directory: pathlib.Path = typer.Option(
342349
None,
343-
help="folder/directory to export TML objects to",
350+
help="Folder/directory to export TML objects to",
351+
click_type=custom_types.Directory(exists=False, make=True),
344352
rich_help_panel="TML Export Options",
345353
),
346354
export_only: bool = typer.Option(

cs_tools/cli/tools/bulk_sharing/app.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,10 +74,14 @@ def from_tag(
7474
help="The level of access to give to all listed principals.",
7575
),
7676
no_prompt: bool = typer.Option(False, "--no-prompt", help="disable the confirmation prompt"),
77+
org_override: str = typer.Option(None, "--org", help="The Org to switch to before performing actions."),
7778
) -> _types.ExitCode:
7879
"""Share content with the identified --tag."""
7980
ts = ctx.obj.thoughtspot
8081

82+
if ts.session_context.thoughtspot.is_orgs_enabled and org_override is not None:
83+
ts.switch_org(org_id=org_override)
84+
8185
try:
8286
c = workflows.metadata.fetch_one(tag_name, "TAG", http=ts.api)
8387
_ = utils.run_sync(c)

cs_tools/cli/tools/git/branches.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ def branches_commit(
7272
False, "--delete-aware", help="Deletes content in the GitHub repository if it is not present in this commit."
7373
),
7474
log_errors: bool = typer.Option(False, "--log-errors", help="Log API errors to the console."),
75-
org_override: str = typer.Option(None, "--org", help="The org to commit objects from."),
75+
org_override: str = typer.Option(None, "--org", help="The Org to switch to before performing actions."),
7676
# === DEPRECATED ===
7777
branch_override: str = typer.Option(
7878
None,
@@ -177,7 +177,7 @@ def branches_validate(
177177
ctx: typer.Context,
178178
source: str = typer.Option(..., "--source-branch", help="The source branch to merge from."),
179179
target: str = typer.Option(..., "--target-branch", help="The target branch to merge into."),
180-
org_override: str = typer.Option(None, "--org", help="The source Org to use when comparing branches."),
180+
org_override: str = typer.Option(None, "--org", help="The Org to switch to before performing actions."),
181181
):
182182
"""Validates that your GitHub branches can be merged."""
183183
ts = ctx.obj.thoughtspot
@@ -231,7 +231,7 @@ def branches_deploy(
231231
help="Whether to accept any errors during the DEPLOY.",
232232
rich_help_panel="TML Deploy Options",
233233
),
234-
org_override: str = typer.Option(None, "--org", help="The org to deploy TML to."),
234+
org_override: str = typer.Option(None, "--org", help="The Org to switch to before performing actions."),
235235
log_errors: bool = typer.Option(False, "--log-errors", help="Log TML errors to the console."),
236236
):
237237
"""Pulls from a branch in a GitHub repository to ThoughtSpot."""
@@ -307,7 +307,7 @@ def commits_search(
307307
"--branch-name",
308308
help="The name of the branch to search.",
309309
),
310-
org_override: str = typer.Option(None, "--org", help="The org to search commit from."),
310+
org_override: str = typer.Option(None, "--org", help="The Org to switch to before performing actions."),
311311
):
312312
"""Searches for the commits for the given metadata ID."""
313313
ts = ctx.obj.thoughtspot
@@ -337,7 +337,7 @@ def commit_revert(
337337
"--branch-name",
338338
help="The name of the branch to search.",
339339
),
340-
org_override: str = typer.Option(None, "--org", help="The org to revert the commit from."),
340+
org_override: str = typer.Option(None, "--org", help="The Org to switch to before performing actions."),
341341
):
342342
"""Searches for the commits for the given metadata ID."""
343343
ts = ctx.obj.thoughtspot

cs_tools/cli/tools/git/config.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ def config_create(
2929
access_token: str = typer.Option(..., help="A persanal access token with access to the GitHub repository."),
3030
commit_branch: str = typer.Option(..., help="The name of the branch to save TML to."),
3131
config_branch: str = typer.Option(..., help="The name of the branch to use for GUID mapping."),
32-
org_override: str = typer.Option(None, "--org", help="The org to use."),
32+
org_override: str = typer.Option(None, "--org", help="The default Org to switch to when issuing commands."),
3333
) -> _types.ExitCode:
3434
"""Creates a GitHub configuration for an org."""
3535
ts = ctx.obj.thoughtspot
@@ -69,7 +69,7 @@ def config_create(
6969
@depends_on(thoughtspot=ThoughtSpot())
7070
def config_update(
7171
ctx: typer.Context,
72-
org_override: str = typer.Option(None, "--org", help="The org to use."),
72+
org_override: str = typer.Option(None, "--org", help="The default Org to switch to when issuing commands."),
7373
repository_url: str = typer.Option(None, help="The GitHub repository URL to use."),
7474
username: str = typer.Option(None, help="The username of a user with access to the GitHub repository."),
7575
access_token: str = typer.Option(None, help="A persanal access token with access to the GitHub repository."),
@@ -97,7 +97,10 @@ def config_update(
9797

9898
@app.command(name="search")
9999
@depends_on(thoughtspot=ThoughtSpot())
100-
def config_search(ctx: typer.Context, org_override: str = typer.Option(None, "--org", help="The org to use.")):
100+
def config_search(
101+
ctx: typer.Context,
102+
org_override: str = typer.Option(None, "--org", help="The default Org to switch to when issuing commands."),
103+
):
101104
"""Searches for configurations."""
102105
ts = ctx.obj.thoughtspot
103106

0 commit comments

Comments
 (0)