Skip to content

Commit

Permalink
Handle the case where exclude-signatures is a list of strings
Browse files Browse the repository at this point in the history
Add error handling when tartufo config is not found

Only open the file once when writing the config

Add a couple more tests
  • Loading branch information
jhall1-godaddy committed Jul 21, 2022
1 parent 74e5702 commit 8baa197
Show file tree
Hide file tree
Showing 2 changed files with 72 additions and 16 deletions.
64 changes: 48 additions & 16 deletions tartufo/commands/update_signatures.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
Optional,
Sequence,
Tuple,
Union,
)

import click
Expand All @@ -22,6 +23,18 @@
DeprecationSetT = MutableSet[Sequence[str]]


def unwrap_signature(data: Union[str, MutableMapping[str, str]]) -> str:
"""Handles the case where a signature can be a string or a dict
:param data: The string, or dictionary to pull the signature from
:returns: The unwrapped signature
"""
if isinstance(data, str):
return data

return data["signature"]


def scan_local_repo(
options: types.GlobalOptions,
repo_path: str,
Expand Down Expand Up @@ -101,13 +114,20 @@ def replace_deprecated_signatures(
updated = 0

for old_sig, new_sig in deprecations:
targets = functools.partial(lambda o, s: o == s["signature"], old_sig)
targets = functools.partial(lambda o, s: o == unwrap_signature(s), old_sig)
# Iterate all the deprecations and update them everywhere
# they are found in the exclude-signatures section of config
for target_signature in filter(targets, config_data["exclude_signatures"]):
for i, target_signature in enumerate(config_data["exclude_signatures"]):
if not targets(target_signature):
continue

updated += 1
click.echo(f"{updated}) {old_sig!r} -> {new_sig!r}")
target_signature["signature"] = new_sig

if isinstance(target_signature, str):
config_data["exclude_signatures"][i] = new_sig
else:
target_signature["signature"] = new_sig

return updated

Expand All @@ -120,16 +140,18 @@ def write_updated_signatures(
:param config_path: The path to the tartufo config file
:param config_data: The updated config data
"""
with open(str(config_path), "r") as file:
result = tomlkit.loads(file.read())
with open(str(config_path), "r+") as file:
file_content = file.read()
result = tomlkit.loads(file_content)
file.seek(0)

# Assign the new signatures and write it to the config
result["tool"]["tartufo"]["exclude-signatures"] = config_data[ # type: ignore
"exclude_signatures"
]
# Assign the new signatures and write it to the config
result["tool"]["tartufo"]["exclude-signatures"] = config_data[ # type: ignore
"exclude_signatures"
]

with open(str(config_path), "w") as file:
file.write(tomlkit.dumps(result))
file.truncate()


def remove_duplicated_entries(config_data: MutableMapping[str, Any]) -> int:
Expand All @@ -138,17 +160,19 @@ def remove_duplicated_entries(config_data: MutableMapping[str, Any]) -> int:
:param config_data: The config data to check for duplicates
"""
seen = set()
seen: MutableSet[str] = set()
count = 0

for i, exclude in enumerate(config_data["exclude_signatures"].copy()):
if exclude["signature"] in seen:
for i, exclude in enumerate(config_data["exclude_signatures"]):
signature = unwrap_signature(exclude)

if signature in seen:
# Remove this duplicated signature
del config_data["exclude_signatures"][i]
config_data["exclude_signatures"].pop(i)
count += 1
else:
# Mark this signature as seen
seen.add(exclude["signature"])
seen.add(signature)

return count

Expand Down Expand Up @@ -202,7 +226,15 @@ def main(
remove_duplicates: bool,
) -> GitRepoScanner:
"""Update deprecated signatures for a local repository."""
config_path, config_data = load_config_from_path(pathlib.Path(repo_path))
try:
config_path, config_data = load_config_from_path(pathlib.Path(repo_path))
except FileNotFoundError:
util.fail(
util.style_warning("No tartufo config found, exiting..."),
ctx,
code=0,
)

if not config_data.get("exclude_signatures"):
util.fail(
util.style_warning("No signatures found in configuration, exiting..."),
Expand Down
24 changes: 24 additions & 0 deletions tests/test_update_signatures.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,17 @@ def test_with_no_signatures_in_config(
result.output, "No signatures found in configuration, exiting...\n"
)

@mock.patch("tartufo.commands.update_signatures.load_config_from_path")
def test_with_no_config(self, mock_load_config: mock.MagicMock) -> None:
mock_load_config.side_effect = FileNotFoundError()

runner = CliRunner()
with runner.isolated_filesystem():
result = runner.invoke(cli.main, ["update-signatures", "."])

mock_load_config.assert_called_once()
self.assertEqual(result.output, "No tartufo config found, exiting...\n")

@mock.patch("tartufo.commands.update_signatures.GitRepoScanner")
@mock.patch("tartufo.commands.update_signatures.load_config_from_path")
def test_with_no_deprecated_signatures(
Expand Down Expand Up @@ -280,6 +291,19 @@ def test_found_output_with_no_signatures(
mock_scan_local.assert_called_once()
self.assertEqual(result.output, "Found 0 deprecated signatures.\n")

def test_replace_deprecated_with_list_of_strings(self) -> None:
deprecations: Set[Sequence[str]] = set()
deprecations.update((("123", "abc"), ("456", "def")))
config_data = {"exclude_signatures": ["123", "456"]}
expected_result = {"exclude_signatures": ["abc", "def"]}

count = update_signatures.replace_deprecated_signatures(
deprecations, config_data
)

self.assertEqual(count, 2)
self.assertEqual(config_data, expected_result)

def test_remove_duplicated_entries(self) -> None:
initial_data = {
"exclude_signatures": [
Expand Down

0 comments on commit 8baa197

Please sign in to comment.