Skip to content

Commit aa2299b

Browse files
committed
Log why a new repository couldn't be created
get_repo_info mutes the socketdev logger around the repository lookup and only restored the level after a successful lookup. This module logs through the same logger, so when the lookup raised for a repository that isn't in Socket yet, the "attempting to create it" warning and the creation error were dropped, and the CLI exited 2 right after file discovery with nothing logged. A successful create also left logging muted for the rest of the run. Restore the level in a finally block, and include the repository, the HTTP status and, on a 403, a repo:create hint in the creation error. The SDK keeps only the API's "Insufficient permissions" message and drops the required scope. Bump to 2.11.9.
1 parent 9aff373 commit aa2299b

6 files changed

Lines changed: 61 additions & 7 deletions

File tree

‎CHANGELOG.md‎

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,16 @@
11
# Changelog
22

3+
## 2.11.9
4+
5+
### Fixed: report why a new repository couldn't be created
6+
7+
- When a repository isn't in Socket yet and the API token can't create it, the
8+
CLI now logs the failure, the HTTP status, and on a 403 a hint about the
9+
`repo:create` scope before exiting `2`. Previously it exited `2` right after
10+
file discovery with nothing logged.
11+
- After the CLI creates a missing repository, its logging is no longer silenced
12+
for the rest of the run.
13+
314
## 2.11.8
415

516
### Changed: bump pinned Coana CLI to 15.14.0

‎pyproject.toml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ build-backend = "hatchling.build"
66

77
[project]
88
name = "socketsecurity"
9-
version = "2.11.8"
9+
version = "2.11.9"
1010
requires-python = ">= 3.11"
1111
license = {"file" = "LICENSE"}
1212
dependencies = [

‎socketsecurity/__init__.py‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
__author__ = 'socket.dev'
2-
__version__ = '2.11.8'
2+
__version__ = '2.11.9'
33
USER_AGENT = f'SocketPythonCLI/{__version__}'

‎socketsecurity/core/__init__.py‎

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1505,8 +1505,11 @@ def get_repo_info(self, repo_slug: str, default_branch: str = "socket-default-br
15051505
sdk_logger = logging_std.getLogger("socketdev")
15061506
original_level = sdk_logger.level
15071507
sdk_logger.setLevel(logging_std.CRITICAL)
1508-
response = self.sdk.repos.repo(self.config.org_slug, repo_slug, use_types=True)
1509-
sdk_logger.setLevel(original_level)
1508+
try:
1509+
response = self.sdk.repos.repo(self.config.org_slug, repo_slug, use_types=True)
1510+
finally:
1511+
# This module logs through "socketdev" too, so a failed lookup must not leave it muted.
1512+
sdk_logger.setLevel(original_level)
15101513
if not response.success:
15111514
log.error(f"Failed to get repository: {response.status}")
15121515
# log.error(response.message)
@@ -1530,7 +1533,10 @@ def get_repo_info(self, repo_slug: str, default_branch: str = "socket-default-br
15301533
return response.data
15311534

15321535
except APIFailure as e:
1533-
log.error(f"API failure while creating repository: {e}")
1536+
status = f" (HTTP {e.status_code})" if e.status_code else ""
1537+
log.error(f"API failure while creating repository {repo_slug}{status}: {e}")
1538+
if e.status_code == 403:
1539+
log.error("A 403 here usually means the API token is missing the repo:create scope.")
15341540
sys.exit(2) # Exit here with code 2. Code 1 indicates a successfully-detected security issue.
15351541

15361542
return response.data

‎tests/core/test_sdk_methods.py‎

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
1+
import logging
2+
13
import pytest
2-
from socketdev.exceptions import APIFailure
4+
from socketdev.exceptions import APIAccessDenied, APIFailure, APIResourceNotFound
35
from socketdev.fullscans import FullScanParams, FullScanStreamResponse, ScanType
46

57
from socketsecurity.config import CliConfig
@@ -44,6 +46,41 @@ def test_get_repo_info(core, mock_sdk_with_responses):
4446
assert repo_info.id == "f639d6c9-acc3-4d8a-9fb5-2090ad651c7e"
4547
assert repo_info.head_full_scan_id == "head"
4648

49+
50+
def test_get_repo_info_logs_why_repo_creation_failed(core, mock_sdk_with_responses, caplog):
51+
"""A repo that isn't in Socket yet and can't be created exits 2 with the reason logged"""
52+
mock_sdk_with_responses.repos.repo.side_effect = APIResourceNotFound(status_code=404)
53+
mock_sdk_with_responses.repos.post.side_effect = APIAccessDenied(
54+
"Insufficient permissions", status_code=403
55+
)
56+
57+
with caplog.at_level(logging.INFO, logger="socketdev"), pytest.raises(SystemExit) as exit_info:
58+
core.get_repo_info("new-repo")
59+
60+
assert exit_info.value.code == 2
61+
messages = [record.getMessage() for record in caplog.records]
62+
assert "Failed to get repository new-repo, attempting to create it" in messages
63+
assert any("creating repository new-repo (HTTP 403)" in message for message in messages)
64+
assert any("repo:create" in message for message in messages)
65+
66+
67+
def test_get_repo_info_restores_log_level_after_creating_repo(
68+
core, mock_sdk_with_responses, repo_info_response, caplog
69+
):
70+
"""Creating a missing repo must not leave the socketdev logger muted for the rest of the run"""
71+
mock_sdk_with_responses.repos.repo.side_effect = [
72+
APIResourceNotFound(status_code=404),
73+
repo_info_response,
74+
]
75+
mock_sdk_with_responses.repos.post.return_value = {"id": "created"}
76+
77+
with caplog.at_level(logging.INFO, logger="socketdev"):
78+
repo_info = core.get_repo_info("new-repo")
79+
assert logging.getLogger("socketdev").level == logging.INFO
80+
81+
assert repo_info.id == "f639d6c9-acc3-4d8a-9fb5-2090ad651c7e"
82+
83+
4784
def test_get_head_scan_for_repo(core, mock_sdk_with_responses):
4885
"""Test getting head scan ID for a repository"""
4986
head_scan_id = core.get_head_scan_for_repo("test")

‎uv.lock‎

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)