crackerjack 0.20.3__py3-none-any.whl → 0.20.7__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.
@@ -1017,51 +1017,33 @@ class Crackerjack:
1017
1017
  self.execute_command(["pdm", "bump", option])
1018
1018
  break
1019
1019
 
1020
- def _publish_project(self, options: OptionsProtocol) -> None:
1020
+ def _ensure_keyring_installed(self, options: OptionsProtocol) -> None:
1021
+ """Ensure keyring is installed for PDM on macOS."""
1021
1022
  from .errors import ErrorCode, PublishError, handle_error
1022
1023
 
1023
- if options.publish:
1024
- if platform.system() == "Darwin":
1025
- # First check if keyring is already installed in PDM
1026
- check_keyring = self.execute_command(
1027
- ["pdm", "self", "list"], capture_output=True, text=True
1028
- )
1029
- keyring_installed = "keyring" in check_keyring.stdout
1030
-
1031
- if not keyring_installed:
1032
- # Only attempt to install keyring if it's not already installed
1033
- self.console.print("Installing keyring for PDM...")
1034
- authorize = self.execute_command(
1035
- ["pdm", "self", "add", "keyring"],
1036
- capture_output=True,
1037
- text=True,
1038
- )
1039
- if authorize.returncode > 0:
1040
- error = PublishError(
1041
- message="Authentication setup failed",
1042
- error_code=ErrorCode.AUTHENTICATION_ERROR,
1043
- details=f"Failed to add keyring support to PDM.\nCommand output:\n{authorize.stderr}",
1044
- recovery="Please manually add your keyring credentials to PDM. Run `pdm self add keyring` and try again.",
1045
- exit_code=1,
1046
- )
1047
- handle_error(
1048
- error=error,
1049
- console=self.console,
1050
- verbose=options.verbose,
1051
- ai_agent=options.ai_agent,
1052
- )
1024
+ if platform.system() != "Darwin":
1025
+ return
1053
1026
 
1054
- build = self.execute_command(
1055
- ["pdm", "build"], capture_output=True, text=True
1027
+ # Check if keyring is already installed in PDM
1028
+ check_keyring = self.execute_command(
1029
+ ["pdm", "self", "list"], capture_output=True, text=True
1030
+ )
1031
+ keyring_installed = "keyring" in check_keyring.stdout
1032
+
1033
+ if not keyring_installed:
1034
+ # Only attempt to install keyring if it's not already installed
1035
+ self.console.print("Installing keyring for PDM...")
1036
+ authorize = self.execute_command(
1037
+ ["pdm", "self", "add", "keyring"],
1038
+ capture_output=True,
1039
+ text=True,
1056
1040
  )
1057
- self.console.print(build.stdout)
1058
-
1059
- if build.returncode > 0:
1041
+ if authorize.returncode > 0:
1060
1042
  error = PublishError(
1061
- message="Package build failed",
1062
- error_code=ErrorCode.BUILD_ERROR,
1063
- details=f"Command output:\n{build.stderr}",
1064
- recovery="Review the error message above for details. Common issues include missing dependencies, invalid project structure, or incorrect metadata in pyproject.toml.",
1043
+ message="Authentication setup failed",
1044
+ error_code=ErrorCode.AUTHENTICATION_ERROR,
1045
+ details=f"Failed to add keyring support to PDM.\nCommand output:\n{authorize.stderr}",
1046
+ recovery="Please manually add your keyring credentials to PDM. Run `pdm self add keyring` and try again.",
1065
1047
  exit_code=1,
1066
1048
  )
1067
1049
  handle_error(
@@ -1071,26 +1053,233 @@ class Crackerjack:
1071
1053
  ai_agent=options.ai_agent,
1072
1054
  )
1073
1055
 
1074
- publish_result = self.execute_command(
1075
- ["pdm", "publish", "--no-build"], capture_output=True, text=True
1056
+ def _build_package(self, options: OptionsProtocol) -> None:
1057
+ """Build the package using PDM."""
1058
+ from .errors import ErrorCode, PublishError, handle_error
1059
+
1060
+ build = self.execute_command(["pdm", "build"], capture_output=True, text=True)
1061
+ self.console.print(build.stdout)
1062
+
1063
+ if build.returncode > 0:
1064
+ error = PublishError(
1065
+ message="Package build failed",
1066
+ error_code=ErrorCode.BUILD_ERROR,
1067
+ details=f"Command output:\n{build.stderr}",
1068
+ recovery="Review the error message above for details. Common issues include missing dependencies, invalid project structure, or incorrect metadata in pyproject.toml.",
1069
+ exit_code=1,
1070
+ )
1071
+ handle_error(
1072
+ error=error,
1073
+ console=self.console,
1074
+ verbose=options.verbose,
1075
+ ai_agent=options.ai_agent,
1076
1076
  )
1077
1077
 
1078
- if publish_result.returncode > 0:
1079
- error = PublishError(
1080
- message="Package publication failed",
1081
- error_code=ErrorCode.PUBLISH_ERROR,
1082
- details=f"Command output:\n{publish_result.stderr}",
1083
- recovery="Ensure you have the correct PyPI credentials configured. Check your internet connection and that the package name is available on PyPI.",
1084
- exit_code=1,
1078
+ def _prepare_publish_command(self) -> list[str]:
1079
+ """Prepare the PDM publish command with appropriate flags."""
1080
+ # Prepare the publish command
1081
+ publish_cmd = ["pdm", "publish", "--no-build"]
1082
+
1083
+ # Check if we're running in a CI environment
1084
+ is_ci = any(env in os.environ for env in ("CI", "GITHUB_ACTIONS", "GITLAB_CI"))
1085
+
1086
+ # If in CI environment, check if OIDC is likely to be the issue
1087
+ if is_ci:
1088
+ self.console.print("[yellow]Detected CI environment[/yellow]")
1089
+
1090
+ # Check for required OIDC-related environment variables
1091
+ if "GITHUB_ACTIONS" not in os.environ and "GITLAB_CI" not in os.environ:
1092
+ self.console.print(
1093
+ "[yellow]Non-GitHub/GitLab CI environment detected[/yellow]"
1085
1094
  )
1086
- handle_error(
1087
- error=error,
1088
- console=self.console,
1089
- verbose=options.verbose,
1090
- ai_agent=options.ai_agent,
1095
+ # Don't add --no-oidc flag as it may not be supported by all PDM versions
1096
+ elif "ACTIONS_ID_TOKEN_REQUEST_URL" not in os.environ:
1097
+ self.console.print("[yellow]OIDC token request URL not found[/yellow]")
1098
+ # Don't add --no-oidc flag as it may not be supported by all PDM versions
1099
+
1100
+ # Check for API token in environment regardless of OIDC status
1101
+ self._check_for_token_env_vars()
1102
+
1103
+ return publish_cmd
1104
+
1105
+ def _check_for_token_env_vars(self) -> None:
1106
+ """Check for token environment variables."""
1107
+ for token_var in ("PYPI_TOKEN", "PYPI_API_TOKEN", "TWINE_PASSWORD"):
1108
+ if token_var in os.environ:
1109
+ self.console.print(
1110
+ f"[yellow]Found {token_var} environment variable, will use for authentication[/yellow]"
1091
1111
  )
1092
- else:
1093
- self.console.print("[green]✅ Package published successfully![/green]")
1112
+ # If we have a token, PDM will use it automatically
1113
+ break
1114
+ else:
1115
+ self.console.print(
1116
+ "[yellow]No PyPI token found in environment variables[/yellow]"
1117
+ )
1118
+
1119
+ def _check_keyring_credentials(self) -> None:
1120
+ """Check for PyPI credentials in keyring."""
1121
+ # First try to verify if keyring has PyPI credentials, wrapped in double try/except
1122
+ # to handle both ImportError and keyring backend errors
1123
+ try:
1124
+ try:
1125
+ import keyring
1126
+
1127
+ username = keyring.get_password("pdm-publish", "username")
1128
+ token = (
1129
+ keyring.get_password("pdm-publish", username) if username else None
1130
+ )
1131
+
1132
+ if username and token:
1133
+ self.console.print(
1134
+ f"[green]Found PyPI credentials in keyring for user: {username}[/green]"
1135
+ )
1136
+ else:
1137
+ self.console.print(
1138
+ "[yellow]Warning: Could not find PyPI credentials in keyring.[/yellow]"
1139
+ )
1140
+ self.console.print(
1141
+ "[yellow]You might be prompted for username and password.[/yellow]"
1142
+ )
1143
+ except ImportError:
1144
+ self.console.print(
1145
+ "[yellow]Warning: Could not import keyring module to check credentials.[/yellow]"
1146
+ )
1147
+ except Exception as e:
1148
+ # Catch any keyring-related exceptions, including NoKeyringError
1149
+ self.console.print(f"[yellow]Warning: Keyring error: {e}[/yellow]")
1150
+ self.console.print(
1151
+ "[yellow]Will use PDM's built-in credential handling.[/yellow]"
1152
+ )
1153
+
1154
+ def _execute_publish_command(
1155
+ self, publish_cmd: list[str], options: OptionsProtocol
1156
+ ) -> None:
1157
+ """Execute the publish command and handle OIDC errors."""
1158
+ from .errors import (
1159
+ ErrorCode,
1160
+ ExecutionError,
1161
+ PublishError,
1162
+ check_command_result,
1163
+ handle_error,
1164
+ )
1165
+
1166
+ # Log the exact command we're running
1167
+ self.console.print(f"[yellow]Running command: {' '.join(publish_cmd)}[/yellow]")
1168
+
1169
+ # Execute the publish command with detailed output
1170
+ publish_result = self.execute_command(
1171
+ publish_cmd, capture_output=True, text=True
1172
+ )
1173
+
1174
+ # Print the command's stdout and stderr for debugging
1175
+ self.console.print(
1176
+ f"[yellow]Debug: publish stdout:[/yellow]\n{publish_result.stdout}"
1177
+ )
1178
+ self.console.print(
1179
+ f"[yellow]Debug: publish stderr:[/yellow]\n{publish_result.stderr}"
1180
+ )
1181
+ self.console.print(
1182
+ f"[yellow]Debug: publish return code: {publish_result.returncode}[/yellow]"
1183
+ )
1184
+
1185
+ # If first attempt failed with OIDC error, try an alternative approach
1186
+ if publish_result.returncode > 0 and "OIDC" in publish_result.stderr:
1187
+ self.console.print(
1188
+ "[yellow]Detected OIDC error, OIDC may not be supported on this platform[/yellow]"
1189
+ )
1190
+
1191
+ # Provide a more helpful error message
1192
+ error = PublishError(
1193
+ message="Package publication failed - OIDC authentication issue",
1194
+ error_code=ErrorCode.AUTHENTICATION_ERROR,
1195
+ details=f"OIDC authentication is not supported on this platform or configuration.\n\nCommand output:\n{publish_result.stderr}",
1196
+ recovery="Consider manually setting PyPI credentials using the keyring or environment variables. Run 'pdm config pypi.username YOUR_USERNAME' and 'pdm config pypi.password YOUR_PASSWORD' to configure credentials.",
1197
+ exit_code=1,
1198
+ )
1199
+ handle_error(
1200
+ error=error,
1201
+ console=self.console,
1202
+ verbose=options.verbose,
1203
+ ai_agent=options.ai_agent,
1204
+ )
1205
+ return
1206
+
1207
+ # Check the command result and raise an error if it failed
1208
+ try:
1209
+ cmd_str = " ".join(publish_cmd)
1210
+ check_command_result(
1211
+ publish_result,
1212
+ cmd_str,
1213
+ "Package publication failed",
1214
+ ErrorCode.PUBLISH_ERROR,
1215
+ "Ensure you have the correct PyPI credentials configured. Check your internet connection and that the package name is available on PyPI.",
1216
+ )
1217
+ except ExecutionError as e:
1218
+ # Convert to PublishError for consistent error handling
1219
+ error = PublishError(
1220
+ message=e.message,
1221
+ error_code=ErrorCode.PUBLISH_ERROR,
1222
+ details=e.details,
1223
+ recovery=e.recovery,
1224
+ exit_code=1,
1225
+ )
1226
+ handle_error(
1227
+ error=error,
1228
+ console=self.console,
1229
+ verbose=options.verbose,
1230
+ ai_agent=options.ai_agent,
1231
+ )
1232
+
1233
+ def _publish_project(self, options: OptionsProtocol) -> None:
1234
+ """Publish the package to PyPI."""
1235
+ from .errors import ErrorCode, PublishError, handle_error
1236
+
1237
+ if not options.publish:
1238
+ return
1239
+
1240
+ try:
1241
+ # Step 1: Ensure keyring is installed (on macOS)
1242
+ self._ensure_keyring_installed(options)
1243
+
1244
+ # Step 2: Build the package
1245
+ self._build_package(options)
1246
+
1247
+ # Print debug info before executing publish command
1248
+ self.console.print(
1249
+ "[yellow]Debug: About to execute PDM publish command[/yellow]"
1250
+ )
1251
+
1252
+ # Step 3: Setup environment variables for authentication
1253
+ os.environ["PDM_USE_KEYRING"] = "1"
1254
+
1255
+ # Step 4: Prepare publish command with appropriate flags
1256
+ publish_cmd = self._prepare_publish_command()
1257
+
1258
+ # Step 5: Check keyring credentials
1259
+ self._check_keyring_credentials()
1260
+
1261
+ # Step 6: Execute publish command and handle OIDC errors
1262
+ self._execute_publish_command(publish_cmd, options)
1263
+
1264
+ # Success message
1265
+ self.console.print("[green]✅ Package published successfully![/green]")
1266
+
1267
+ except Exception as e:
1268
+ # Catch any other unexpected errors
1269
+ self.console.print(f"[red]Debug: Unexpected error: {e}[/red]")
1270
+ error = PublishError(
1271
+ message="Package publication failed",
1272
+ error_code=ErrorCode.PUBLISH_ERROR,
1273
+ details=f"Unexpected error: {e}",
1274
+ recovery="This is an unexpected error. Please report this issue.",
1275
+ exit_code=1,
1276
+ )
1277
+ handle_error(
1278
+ error=error,
1279
+ console=self.console,
1280
+ verbose=options.verbose,
1281
+ ai_agent=options.ai_agent,
1282
+ )
1094
1283
 
1095
1284
  def _commit_and_push(self, options: OptionsProtocol) -> None:
1096
1285
  if options.commit:
@@ -4,7 +4,7 @@ requires = [ "pdm-backend" ]
4
4
 
5
5
  [project]
6
6
  name = "crackerjack"
7
- version = "0.20.2"
7
+ version = "0.20.6"
8
8
  description = "Crackerjack: code quality toolkit"
9
9
  readme = "README.md"
10
10
  keywords = [
@@ -42,6 +42,7 @@ classifiers = [
42
42
  ]
43
43
  dependencies = [
44
44
  "autotyping>=24.9",
45
+ "keyring>=25.6",
45
46
  "pdm>=2.24.2",
46
47
  "pdm-bump>=0.9.12",
47
48
  "pre-commit>=4.2",
@@ -216,6 +217,7 @@ exclude-deps = [
216
217
  "tomli-w",
217
218
  "google-crc32c",
218
219
  "pytest-timeout",
220
+ "keyring",
219
221
  ]
220
222
 
221
223
  [tool.refurb]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: crackerjack
3
- Version: 0.20.3
3
+ Version: 0.20.7
4
4
  Summary: Crackerjack: code quality toolkit
5
5
  Keywords: bandit,black,creosote,mypy,pyright,pytest,refurb,ruff
6
6
  Author-Email: lesleslie <les@wedgwoodwebworks.com>
@@ -23,6 +23,7 @@ Project-URL: homepage, https://github.com/lesleslie/crackerjack
23
23
  Project-URL: repository, https://github.com/lesleslie/crackerjack
24
24
  Requires-Python: >=3.13
25
25
  Requires-Dist: autotyping>=24.9
26
+ Requires-Dist: keyring>=25.6
26
27
  Requires-Dist: pdm>=2.24.2
27
28
  Requires-Dist: pdm-bump>=0.9.12
28
29
  Requires-Dist: pre-commit>=4.2
@@ -1,7 +1,7 @@
1
- crackerjack-0.20.3.dist-info/METADATA,sha256=XNTChcz8YHD5P5n57GnqTS7wy7KymNgaqfm0wexV_D0,26354
2
- crackerjack-0.20.3.dist-info/WHEEL,sha256=tSfRZzRHthuv7vxpI4aehrdN9scLjk-dCJkPLzkHxGg,90
3
- crackerjack-0.20.3.dist-info/entry_points.txt,sha256=6OYgBcLyFCUgeqLgnvMyOJxPCWzgy7se4rLPKtNonMs,34
4
- crackerjack-0.20.3.dist-info/licenses/LICENSE,sha256=fDt371P6_6sCu7RyqiZH_AhT1LdN3sN1zjBtqEhDYCk,1531
1
+ crackerjack-0.20.7.dist-info/METADATA,sha256=AF97uTrJMtTby4zDowoXOZXUS4uUx_BlCV2Ao13qELs,26383
2
+ crackerjack-0.20.7.dist-info/WHEEL,sha256=tSfRZzRHthuv7vxpI4aehrdN9scLjk-dCJkPLzkHxGg,90
3
+ crackerjack-0.20.7.dist-info/entry_points.txt,sha256=6OYgBcLyFCUgeqLgnvMyOJxPCWzgy7se4rLPKtNonMs,34
4
+ crackerjack-0.20.7.dist-info/licenses/LICENSE,sha256=fDt371P6_6sCu7RyqiZH_AhT1LdN3sN1zjBtqEhDYCk,1531
5
5
  crackerjack/.gitignore,sha256=4DYG7ZoVEHR5Tv1gQliRWmsNku5Fw2_k756cG_t12Cg,185
6
6
  crackerjack/.libcst.codemod.yaml,sha256=a8DlErRAIPV1nE6QlyXPAzTOgkB24_spl2E9hphuf5s,772
7
7
  crackerjack/.pdm.toml,sha256=dZe44HRcuxxCFESGG8SZIjmc-cGzSoyK3Hs6t4NYA8w,23
@@ -23,7 +23,8 @@ crackerjack/.ruff_cache/0.11.12/16869036553936192448,sha256=pYYUCDrYh7fPq8xkFLxv
23
23
  crackerjack/.ruff_cache/0.11.12/1867267426380906393,sha256=2w4M0Lrjd9flwuq6uJxehTbm7FVUcK5sL2sz1gS2Yvo,256
24
24
  crackerjack/.ruff_cache/0.11.12/4240757255861806333,sha256=uph5uIRG-XnF7ywAEcCxqqgIkWALPCvJFcwCgnNfTI4,77
25
25
  crackerjack/.ruff_cache/0.11.12/4441409093023629623,sha256=eHrESew3XCFJ2WqmKvtGLO1r4mY5Q_mv7yGlDmM1sSc,153
26
- crackerjack/.ruff_cache/0.11.13/1867267426380906393,sha256=FQ8Z7PFzLBASpKw20TqKwD02FLSG_mj4C3cEt8nU2YY,256
26
+ crackerjack/.ruff_cache/0.11.13/1867267426380906393,sha256=Zt526cTInLmZB1vp5V7r_JDhNHzHw6nyguTIvqlF5wY,256
27
+ crackerjack/.ruff_cache/0.11.13/4240757255861806333,sha256=l35TwAYyTusgJgyePvfP4_CCllPs1sWapEiLFZw8chQ,83
27
28
  crackerjack/.ruff_cache/0.11.2/4070660268492669020,sha256=FTRTUmvj6nZw_QQBp_WHI-h3_iqRejzL39api-9wTvs,224
28
29
  crackerjack/.ruff_cache/0.11.3/9818742842212983150,sha256=U-4mT__a-OljovvAJvv5M6X7TCMa3dReLXx3kTNGgwU,224
29
30
  crackerjack/.ruff_cache/0.11.4/9818742842212983150,sha256=QF9j6-3MH_d0pDNotdbF2hlqCL66SxN8OLVKR3PZyZU,224
@@ -60,9 +61,9 @@ crackerjack/.ruff_cache/0.9.9/8843823720003377982,sha256=e4ymkXfQsUg5e_mtO34xTsa
60
61
  crackerjack/.ruff_cache/CACHEDIR.TAG,sha256=WVMVbX4MVkpCclExbq8m-IcOZIOuIZf5FrYw5Pk-Ma4,43
61
62
  crackerjack/__init__.py,sha256=w5jukdION0D0fyeKYl-7hfCPzI0isXbEjzdjw8RecKA,840
62
63
  crackerjack/__main__.py,sha256=jg-eO0Z1VZkx5F-97dsd1rxOQ0uwHWabuKma8thUJVw,6779
63
- crackerjack/crackerjack.py,sha256=DpbodHdqeoyA0_QC6ADGtNX0A3zMIPpnWqk2UsIdyQw,53203
64
+ crackerjack/crackerjack.py,sha256=fYOjOZ77GWlLg2PrtIqvRfi1ed3dll6mQV-FXsd49DY,60763
64
65
  crackerjack/errors.py,sha256=OtbmtA912kzDOWVo6JASuFbaMU-VhmQD_fUNsvnWCZc,4311
65
66
  crackerjack/interactive.py,sha256=Ay7_s3pc4ntc_3F_bRKBsWxmjor6nkN9v6tqqJe1iRw,15904
66
67
  crackerjack/py313.py,sha256=VgthlcpLL6nNDcg3evvEmGNYWCdMuojnMhow58ISEdY,6184
67
- crackerjack/pyproject.toml,sha256=HLvG6faHtQDxAtTYK7ujGjPX93n_OLCIgdOaPYtTago,4843
68
- crackerjack-0.20.3.dist-info/RECORD,,
68
+ crackerjack/pyproject.toml,sha256=ylIWuetL7038ko_YQOVVYAZDUULIWeFuUuAKZtYEy5Q,4879
69
+ crackerjack-0.20.7.dist-info/RECORD,,