common-python-tasks 0.1.0__py3-none-any.whl → 0.2.0__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.
@@ -89,7 +89,39 @@ def get_github_token() -> str | None:
89
89
  Returns:
90
90
  The GitHub token from `GITHUB_TOKEN` or `GH_TOKEN`, or `None` if unset.
91
91
  """
92
- return (os.getenv("GITHUB_TOKEN") or os.getenv("GH_TOKEN") or "").strip() or None
92
+ token = (os.getenv("GITHUB_TOKEN") or os.getenv("GH_TOKEN") or "").strip()
93
+ if token:
94
+ return token
95
+
96
+ try:
97
+ result = utils.run_command(
98
+ ["gh", "auth", "token"],
99
+ capture_output=True,
100
+ acceptable_returncodes={0, 1},
101
+ )
102
+ except FileNotFoundError:
103
+ LOGGER.warning(
104
+ "GitHub CLI not installed; cannot retrieve token via `gh auth token`"
105
+ )
106
+ return None
107
+
108
+ if result is None:
109
+ return None
110
+
111
+ if result.returncode != 0:
112
+ stderr = (result.stderr or "").strip()
113
+ message = "Failed to retrieve GitHub token via `gh auth token`"
114
+ if stderr:
115
+ message += f": {stderr}"
116
+ LOGGER.warning(message)
117
+ return None
118
+
119
+ token = (result.stdout or "").strip()
120
+ if not token:
121
+ LOGGER.warning("`gh auth token` returned an empty token")
122
+ return None
123
+
124
+ return token
93
125
 
94
126
 
95
127
  def get_github_api_base_url() -> str:
@@ -112,6 +112,8 @@ def _script(
112
112
 
113
113
  tasks.script = _script
114
114
 
115
+ NO_PROMPT_ENV_VAR = "COMMON_PYTHON_TASKS_NO_PROMPT"
116
+
115
117
 
116
118
  def _should_update_release_changelog() -> bool:
117
119
  from common_python_tasks.env import env_truthy
@@ -119,6 +121,39 @@ def _should_update_release_changelog() -> bool:
119
121
  return env_truthy("RELEASE_UPDATE_CHANGELOG")
120
122
 
121
123
 
124
+ def _confirm_release_with_uncommitted_changes(dirty_files: list[Path]) -> bool:
125
+ preview = ", ".join(str(path) for path in dirty_files[:5])
126
+ if len(dirty_files) > 5:
127
+ preview = f"{preview}, ... and {len(dirty_files) - 5} more"
128
+
129
+ LOGGER.warning("Repository has uncommitted changes: %s", preview)
130
+ try:
131
+ response = input("Proceed with release anyway? [y/N]: ").strip().lower()
132
+ except (EOFError, OSError):
133
+ return False
134
+ return response in {"y", "yes"}
135
+
136
+
137
+ def _resolve_allow_dirty_release(dirty_files: list[Path]) -> bool:
138
+ from common_python_tasks.utils import fatal
139
+
140
+ if not dirty_files:
141
+ return False
142
+
143
+ if os.getenv(NO_PROMPT_ENV_VAR) == "1":
144
+ LOGGER.warning(
145
+ "Proceeding with release despite uncommitted changes because %s=1",
146
+ NO_PROMPT_ENV_VAR,
147
+ )
148
+ return True
149
+
150
+ if _confirm_release_with_uncommitted_changes(dirty_files):
151
+ return True
152
+
153
+ fatal("Aborted due to uncommitted changes.")
154
+ return False
155
+
156
+
122
157
  def _update_release_changelog(release_tag: str, dry_run: bool = False) -> None:
123
158
  from common_python_tasks.utils import prepend_changelog, run_command
124
159
 
@@ -884,6 +919,7 @@ def bump_version(
884
919
  *,
885
920
  stage: str | None = None,
886
921
  dry_run: bool = False,
922
+ allow_dirty: bool = False,
887
923
  ) -> str:
888
924
  """Bump the project version.
889
925
 
@@ -892,6 +928,7 @@ def bump_version(
892
928
  `auto` to infer the bump from git history using git-cliff.
893
929
  stage: Optional pre-release stage to apply: `alpha`, `beta`, or `rc`.
894
930
  dry_run: If `True`, print what would happen without making changes.
931
+ allow_dirty: If `True`, allow version bumping with uncommitted changes.
895
932
 
896
933
  Returns:
897
934
  The new version string that was/would be set.
@@ -909,7 +946,7 @@ def bump_version(
909
946
 
910
947
  ensure_on_default_branch()
911
948
 
912
- if get_dirty_files():
949
+ if get_dirty_files() and not allow_dirty:
913
950
  fatal(
914
951
  "Repository has uncommitted changes. "
915
952
  "Please commit or stash changes before bumping version."
@@ -960,6 +997,7 @@ def _run_release_flow(
960
997
  from common_python_tasks.git import (
961
998
  build_release_hook_environment,
962
999
  ensure_on_default_branch,
1000
+ get_dirty_files,
963
1001
  )
964
1002
  from common_python_tasks.utils import (
965
1003
  log_dry_run,
@@ -971,7 +1009,10 @@ def _run_release_flow(
971
1009
  post_script = post_script or os.getenv("RELEASE_POST_SCRIPT")
972
1010
 
973
1011
  ensure_on_default_branch()
1012
+ allow_dirty = _resolve_allow_dirty_release(get_dirty_files())
974
1013
  hook_env = build_release_hook_environment(component, normalized_stage, dry_run)
1014
+ if allow_dirty:
1015
+ hook_env[NO_PROMPT_ENV_VAR] = "1"
975
1016
  if pre_script:
976
1017
  run_command(["sh", "-lc", pre_script], env=hook_env, dry_run=False)
977
1018
 
@@ -987,6 +1028,7 @@ def _run_release_flow(
987
1028
  component=hook_env["RELEASE_COMPONENT"],
988
1029
  stage=normalized_stage,
989
1030
  dry_run=dry_run,
1031
+ allow_dirty=allow_dirty,
990
1032
  )
991
1033
  if dry_run:
992
1034
  log_dry_run("Would push tags to origin")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: common-python-tasks
3
- Version: 0.1.0
3
+ Version: 0.2.0
4
4
  Summary: Opinionated Poe the Poet tasks for Python package development.
5
5
  License-Expression: MIT
6
6
  License-File: LICENSE
@@ -23,7 +23,7 @@ Requires-Dist: git-cliff (>=2.12.0,<3.0.0)
23
23
  Requires-Dist: isort (>=8.0.1,<9.0.0)
24
24
  Requires-Dist: jinja2 (>=3.1.6,<4.0.0)
25
25
  Requires-Dist: poethepoet-tasks (>=0.3.0,<0.4.0)
26
- Requires-Dist: pytest (>=9.0.1,<10.0.0)
26
+ Requires-Dist: pytest (>=9.0.3,<10.0.0)
27
27
  Requires-Dist: pytest-cov (>=7.0.0,<8.0.0)
28
28
  Requires-Dist: tomlkit (>=0.13.3,<0.14.0)
29
29
  Project-URL: Homepage, http://github.com/ci-sourcerer/common-python-tasks
@@ -50,7 +50,7 @@ curl -sSL https://api.github.com/repos/ci-sourcerer/common-python-tasks/contents
50
50
  To install a specific release, set the environment variable `COMMON_PYTHON_TASKS_VERSION`.
51
51
 
52
52
  ```shell
53
- COMMON_PYTHON_TASKS_VERSION=0.1.0 \
53
+ COMMON_PYTHON_TASKS_VERSION=0.2.0 \
54
54
  sh -c "$(curl -sSL https://api.github.com/repos/ci-sourcerer/common-python-tasks/contents/scripts/add-common-python-tasks.sh | jq -r '.content' | base64 -d)"
55
55
  ```
56
56
 
@@ -71,9 +71,9 @@ There's no real reason to run the automated script; I just like automating every
71
71
  ```toml
72
72
  [project]
73
73
  name = "my-awesome-project"
74
- version = "0.1.0"
74
+ version = "0.2.0"
75
75
  dependencies = [
76
- "common-python-tasks==0.1.0", # Always pin to a specific version
76
+ "common-python-tasks==0.2.0", # Always pin to a specific version
77
77
  ]
78
78
 
79
79
  [tool.poe]
@@ -287,8 +287,8 @@ By default, `tasks()` exposes the common task set. You can still include or excl
287
287
  ```toml
288
288
  [project]
289
289
  name = "simple-cli-tool"
290
- version = "0.1.0"
291
- dependencies = ["common-python-tasks==0.1.0"]
290
+ version = "0.2.0"
291
+ dependencies = ["common-python-tasks==0.2.0"]
292
292
 
293
293
  [tool.poe]
294
294
  include_script = "common_python_tasks:tasks()"
@@ -301,8 +301,8 @@ Available tasks: common defaults such as `format`, `lint`, `test`, and `build`.
301
301
  ```toml
302
302
  [project]
303
303
  name = "containerized-app"
304
- version = "0.1.0"
305
- dependencies = ["common-python-tasks==0.1.0"]
304
+ version = "0.2.0"
305
+ dependencies = ["common-python-tasks==0.2.0"]
306
306
 
307
307
  [tool.poe]
308
308
  include_script = "common_python_tasks:tasks(include_tags=['common', 'containers'])"
@@ -319,7 +319,7 @@ Available tasks: All tasks including `build-image` and `push-image`.
319
319
  ```toml
320
320
  [project]
321
321
  name = "custom-test-setup"
322
- dependencies = ["common-python-tasks==0.1.0"]
322
+ dependencies = ["common-python-tasks==0.2.0"]
323
323
  dynamic = ["version"]
324
324
 
325
325
  [tool.poe]
@@ -17,11 +17,11 @@ common_python_tasks/data/generic/pytest.ini,sha256=RHak5QrLzKHvUBjFmaaiOYFrd_8YB
17
17
  common_python_tasks/docker.py,sha256=Pg_fySHIBdjIopme33DYdCYPwCesP9meG5T7xfKr8V8,15378
18
18
  common_python_tasks/env.py,sha256=hRGKcad32WjFRfo2d4yQlPrZ7fGpsneIUf-_SxDYt7c,13141
19
19
  common_python_tasks/git.py,sha256=XnirI2zNP21-pmxIKFsoG0HWEOmDJ8SzYN4eYWYN6N8,9813
20
- common_python_tasks/github.py,sha256=EEGem7SCNxyG9suYGEH6tf32yeMbbfA2WadWn2sWqcI,17671
20
+ common_python_tasks/github.py,sha256=IaK-VDN0PrxLIP_ob1n1dTAFdtQHzR_QA5THrrTSuSA,18502
21
21
  common_python_tasks/project.py,sha256=I78eQlHeRmVR5WFV_wxGyO_wxNWcYox_UYowUTE4xdU,8914
22
- common_python_tasks/tasks.py,sha256=LFF-fj_0ZpIgJ0QMsndxfu6rBSTd-NYND_mYbie_uC0,47412
22
+ common_python_tasks/tasks.py,sha256=nfcTyMAbZhfFM2zltWsh_J5AVUAEVT8pET_b685Y3ug,48798
23
23
  common_python_tasks/utils.py,sha256=3sNB84F66suu2nUN0mG-SOuJaUctx-p9uHmDAy-Ss0g,11198
24
- common_python_tasks-0.1.0.dist-info/METADATA,sha256=hELp8ZCIUE64aJxJk9UcprbhzE6eU8aSX73TrSGnXZc,21386
25
- common_python_tasks-0.1.0.dist-info/WHEEL,sha256=EGEvSphFYqXKs23-kQBeyNoJP1nrT8ZJKQoi5p5DYL8,88
26
- common_python_tasks-0.1.0.dist-info/licenses/LICENSE,sha256=RsSweM-DyzxFPx38fQOJqHrKyeT9Fiz8rO7yox-fOsw,1070
27
- common_python_tasks-0.1.0.dist-info/RECORD,,
24
+ common_python_tasks-0.2.0.dist-info/METADATA,sha256=ctLqOYE7z1r2oDBX07svRYxDE60ECwxeJcPnQc0I224,21386
25
+ common_python_tasks-0.2.0.dist-info/WHEEL,sha256=EGEvSphFYqXKs23-kQBeyNoJP1nrT8ZJKQoi5p5DYL8,88
26
+ common_python_tasks-0.2.0.dist-info/licenses/LICENSE,sha256=RsSweM-DyzxFPx38fQOJqHrKyeT9Fiz8rO7yox-fOsw,1070
27
+ common_python_tasks-0.2.0.dist-info/RECORD,,