python-package-folder 4.0.0__py3-none-any.whl → 4.1.1__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.
- python_package_folder/python_package_folder.py +172 -33
- {python_package_folder-4.0.0.dist-info → python_package_folder-4.1.1.dist-info}/METADATA +18 -1
- {python_package_folder-4.0.0.dist-info → python_package_folder-4.1.1.dist-info}/RECORD +6 -6
- {python_package_folder-4.0.0.dist-info → python_package_folder-4.1.1.dist-info}/WHEEL +0 -0
- {python_package_folder-4.0.0.dist-info → python_package_folder-4.1.1.dist-info}/entry_points.txt +0 -0
- {python_package_folder-4.0.0.dist-info → python_package_folder-4.1.1.dist-info}/licenses/LICENSE +0 -0
|
@@ -10,6 +10,8 @@ It can be invoked via:
|
|
|
10
10
|
|
|
11
11
|
from __future__ import annotations
|
|
12
12
|
|
|
13
|
+
import os
|
|
14
|
+
import shutil
|
|
13
15
|
import subprocess
|
|
14
16
|
import sys
|
|
15
17
|
from pathlib import Path
|
|
@@ -23,11 +25,23 @@ from .manager import BuildManager
|
|
|
23
25
|
from .utils import find_project_root, find_source_directory
|
|
24
26
|
|
|
25
27
|
|
|
28
|
+
def is_github_actions() -> bool:
|
|
29
|
+
"""Check if running in GitHub Actions."""
|
|
30
|
+
return os.getenv("GITHUB_ACTIONS") == "true"
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def check_node_available() -> bool:
|
|
34
|
+
"""Check if Node.js is available."""
|
|
35
|
+
return shutil.which("node") is not None
|
|
36
|
+
|
|
37
|
+
|
|
26
38
|
def resolve_version_via_semantic_release(
|
|
27
39
|
project_root: Path,
|
|
28
40
|
subfolder_path: Path | None = None,
|
|
29
41
|
package_name: str | None = None,
|
|
30
|
-
|
|
42
|
+
repository: str | None = None,
|
|
43
|
+
repository_url: str | None = None,
|
|
44
|
+
) -> tuple[str | None, str | None]:
|
|
31
45
|
"""
|
|
32
46
|
Resolve the next version using semantic-release via Node.js script.
|
|
33
47
|
|
|
@@ -35,10 +49,38 @@ def resolve_version_via_semantic_release(
|
|
|
35
49
|
project_root: Root directory of the project
|
|
36
50
|
subfolder_path: Optional path to subfolder (relative to project_root) for Workflow 1
|
|
37
51
|
package_name: Optional package name for subfolder builds
|
|
52
|
+
repository: Optional target repository ('pypi', 'testpypi', or 'azure')
|
|
53
|
+
repository_url: Optional repository URL (required for Azure Artifacts)
|
|
38
54
|
|
|
39
55
|
Returns:
|
|
40
|
-
|
|
56
|
+
Tuple of (version string if a release is determined, error message if any)
|
|
57
|
+
Returns (None, None) if no release or no error, (None, error_msg) on error
|
|
41
58
|
"""
|
|
59
|
+
# Note: Node.js availability should be checked before calling this function
|
|
60
|
+
# This check is a safety fallback
|
|
61
|
+
if not check_node_available():
|
|
62
|
+
if is_github_actions():
|
|
63
|
+
error_msg = """Node.js is not available in this GitHub Actions workflow.
|
|
64
|
+
|
|
65
|
+
To fix this, add the following steps BEFORE running python-package-folder:
|
|
66
|
+
|
|
67
|
+
- name: Setup Node.js
|
|
68
|
+
uses: actions/setup-node@v4
|
|
69
|
+
with:
|
|
70
|
+
node-version: '20'
|
|
71
|
+
|
|
72
|
+
- name: Install semantic-release
|
|
73
|
+
run: |
|
|
74
|
+
npm install -g semantic-release semantic-release-commit-filter
|
|
75
|
+
|
|
76
|
+
Alternatively, provide --version explicitly to skip automatic version resolution."""
|
|
77
|
+
print(f"Error: {error_msg}", file=sys.stderr)
|
|
78
|
+
return None, error_msg
|
|
79
|
+
else:
|
|
80
|
+
error_msg = "Node.js not found. Cannot resolve version via semantic-release."
|
|
81
|
+
print(f"Error: {error_msg}", file=sys.stderr)
|
|
82
|
+
return None, error_msg
|
|
83
|
+
|
|
42
84
|
# Try to find the script in multiple locations:
|
|
43
85
|
# 1. Project root / scripts (for development or when script is in repo)
|
|
44
86
|
# 2. Package installation directory / scripts (for installed package)
|
|
@@ -86,7 +128,9 @@ def resolve_version_via_semantic_release(
|
|
|
86
128
|
script_path = fallback_script
|
|
87
129
|
|
|
88
130
|
if not script_path:
|
|
89
|
-
|
|
131
|
+
error_msg = "Could not locate get-next-version.cjs script"
|
|
132
|
+
print(f"Error: {error_msg}", file=sys.stderr)
|
|
133
|
+
return None, error_msg
|
|
90
134
|
|
|
91
135
|
# Build command arguments
|
|
92
136
|
cmd = ["node", str(script_path), str(project_root)]
|
|
@@ -98,7 +142,17 @@ def resolve_version_via_semantic_release(
|
|
|
98
142
|
else subfolder_path
|
|
99
143
|
)
|
|
100
144
|
cmd.extend([str(rel_path), package_name])
|
|
101
|
-
|
|
145
|
+
elif package_name:
|
|
146
|
+
# Main package build with package_name (for registry queries)
|
|
147
|
+
# Pass null for subfolder_path, then package_name
|
|
148
|
+
cmd.extend(["", package_name])
|
|
149
|
+
# Workflow 2: main package without package_name (no additional args needed)
|
|
150
|
+
|
|
151
|
+
# Add repository information if provided
|
|
152
|
+
if repository:
|
|
153
|
+
cmd.append(repository)
|
|
154
|
+
if repository_url:
|
|
155
|
+
cmd.append(repository_url)
|
|
102
156
|
|
|
103
157
|
result = subprocess.run(
|
|
104
158
|
cmd,
|
|
@@ -109,38 +163,53 @@ def resolve_version_via_semantic_release(
|
|
|
109
163
|
)
|
|
110
164
|
|
|
111
165
|
if result.returncode != 0:
|
|
112
|
-
#
|
|
166
|
+
# Collect error details
|
|
167
|
+
error_details = []
|
|
113
168
|
if result.stderr:
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
return None
|
|
169
|
+
error_details.append(f"stderr: {result.stderr}")
|
|
170
|
+
if result.stdout:
|
|
171
|
+
error_details.append(f"stdout: {result.stdout}")
|
|
172
|
+
|
|
173
|
+
error_msg = "semantic-release version resolution failed"
|
|
174
|
+
if error_details:
|
|
175
|
+
error_msg += f": {'; '.join(error_details)}"
|
|
176
|
+
|
|
177
|
+
print(f"Error: {error_msg}", file=sys.stderr)
|
|
178
|
+
return None, error_msg
|
|
124
179
|
|
|
125
180
|
version = result.stdout.strip()
|
|
126
181
|
if version and version != "none":
|
|
127
|
-
return version
|
|
182
|
+
return version, None
|
|
128
183
|
|
|
129
|
-
return None
|
|
184
|
+
return None, None
|
|
130
185
|
except FileNotFoundError:
|
|
131
|
-
# Node.js not found
|
|
132
|
-
|
|
133
|
-
"
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
186
|
+
# Node.js not found (shouldn't happen if check_node_available() passed, but handle gracefully)
|
|
187
|
+
if is_github_actions():
|
|
188
|
+
error_msg = """Node.js is not available in this GitHub Actions workflow.
|
|
189
|
+
|
|
190
|
+
To fix this, add the following steps BEFORE running python-package-folder:
|
|
191
|
+
|
|
192
|
+
- name: Setup Node.js
|
|
193
|
+
uses: actions/setup-node@v4
|
|
194
|
+
with:
|
|
195
|
+
node-version: '20'
|
|
196
|
+
|
|
197
|
+
- name: Install semantic-release
|
|
198
|
+
run: |
|
|
199
|
+
npm install -g semantic-release semantic-release-commit-filter
|
|
200
|
+
|
|
201
|
+
Alternatively, provide --version explicitly to skip automatic version resolution."""
|
|
202
|
+
print(f"Error: {error_msg}", file=sys.stderr)
|
|
203
|
+
return None, error_msg
|
|
204
|
+
else:
|
|
205
|
+
error_msg = "Node.js not found. Cannot resolve version via semantic-release."
|
|
206
|
+
print(f"Error: {error_msg}", file=sys.stderr)
|
|
207
|
+
return None, error_msg
|
|
137
208
|
except Exception as e:
|
|
138
209
|
# Other errors (e.g., permission issues, script not found)
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
)
|
|
143
|
-
return None
|
|
210
|
+
error_msg = f"Error resolving version via semantic-release: {e}"
|
|
211
|
+
print(f"Error: {error_msg}", file=sys.stderr)
|
|
212
|
+
return None, error_msg
|
|
144
213
|
finally:
|
|
145
214
|
# Clean up temporary file if we extracted from zip/pex
|
|
146
215
|
# This must be at function level to ensure cleanup even on early return
|
|
@@ -297,6 +366,50 @@ def main() -> int:
|
|
|
297
366
|
# Version is needed for subfolder builds or when publishing main package
|
|
298
367
|
if is_subfolder or args.publish:
|
|
299
368
|
print("No --version provided, attempting to resolve via semantic-release...")
|
|
369
|
+
|
|
370
|
+
# Check Node.js availability upfront
|
|
371
|
+
if not check_node_available():
|
|
372
|
+
if is_github_actions():
|
|
373
|
+
error_msg = """Node.js is not available in this GitHub Actions workflow.
|
|
374
|
+
|
|
375
|
+
To fix this, add the following steps BEFORE running python-package-folder:
|
|
376
|
+
|
|
377
|
+
- name: Setup Node.js
|
|
378
|
+
uses: actions/setup-node@v4
|
|
379
|
+
with:
|
|
380
|
+
node-version: '20'
|
|
381
|
+
|
|
382
|
+
- name: Install semantic-release
|
|
383
|
+
run: |
|
|
384
|
+
npm install -g semantic-release semantic-release-commit-filter
|
|
385
|
+
|
|
386
|
+
Alternatively, provide --version explicitly to skip automatic version resolution."""
|
|
387
|
+
print(f"Error: {error_msg}", file=sys.stderr)
|
|
388
|
+
else:
|
|
389
|
+
print(
|
|
390
|
+
"Error: Node.js is not available. Cannot resolve version via semantic-release.",
|
|
391
|
+
file=sys.stderr,
|
|
392
|
+
)
|
|
393
|
+
print(
|
|
394
|
+
"Please install Node.js or provide --version explicitly.",
|
|
395
|
+
file=sys.stderr,
|
|
396
|
+
)
|
|
397
|
+
return 1
|
|
398
|
+
|
|
399
|
+
# Log that Node.js is available (for debugging)
|
|
400
|
+
node_version = subprocess.run(
|
|
401
|
+
["node", "--version"],
|
|
402
|
+
capture_output=True,
|
|
403
|
+
text=True,
|
|
404
|
+
check=False,
|
|
405
|
+
)
|
|
406
|
+
if node_version.returncode == 0:
|
|
407
|
+
print(f"Node.js detected: {node_version.stdout.strip()}")
|
|
408
|
+
|
|
409
|
+
# Get repository info if publishing
|
|
410
|
+
repository = args.publish if args.publish else None
|
|
411
|
+
repository_url = args.repository_url if args.publish else None
|
|
412
|
+
|
|
300
413
|
if is_subfolder:
|
|
301
414
|
# Workflow 1: subfolder build
|
|
302
415
|
# src_dir is guaranteed to be relative to project_root due to is_subfolder check
|
|
@@ -304,12 +417,35 @@ def main() -> int:
|
|
|
304
417
|
" ", "-"
|
|
305
418
|
).lower().strip("-")
|
|
306
419
|
subfolder_rel_path = src_dir.relative_to(project_root)
|
|
307
|
-
resolved_version = resolve_version_via_semantic_release(
|
|
308
|
-
project_root,
|
|
420
|
+
resolved_version, error_details = resolve_version_via_semantic_release(
|
|
421
|
+
project_root,
|
|
422
|
+
subfolder_rel_path,
|
|
423
|
+
package_name,
|
|
424
|
+
repository=repository,
|
|
425
|
+
repository_url=repository_url,
|
|
309
426
|
)
|
|
310
427
|
else:
|
|
311
428
|
# Workflow 2: main package
|
|
312
|
-
|
|
429
|
+
# For main package, we need package_name from pyproject.toml for registry queries
|
|
430
|
+
package_name_for_registry = None
|
|
431
|
+
if repository:
|
|
432
|
+
try:
|
|
433
|
+
import tomllib
|
|
434
|
+
pyproject_path = project_root / "pyproject.toml"
|
|
435
|
+
if pyproject_path.exists():
|
|
436
|
+
with open(pyproject_path, "rb") as f:
|
|
437
|
+
data = tomllib.load(f)
|
|
438
|
+
package_name_for_registry = data.get("project", {}).get("name")
|
|
439
|
+
except Exception:
|
|
440
|
+
pass
|
|
441
|
+
|
|
442
|
+
resolved_version, error_details = resolve_version_via_semantic_release(
|
|
443
|
+
project_root,
|
|
444
|
+
subfolder_path=None,
|
|
445
|
+
package_name=package_name_for_registry,
|
|
446
|
+
repository=repository,
|
|
447
|
+
repository_url=repository_url,
|
|
448
|
+
)
|
|
313
449
|
|
|
314
450
|
if resolved_version:
|
|
315
451
|
print(f"Resolved version via semantic-release: {resolved_version}")
|
|
@@ -319,8 +455,11 @@ def main() -> int:
|
|
|
319
455
|
"This could mean:\n"
|
|
320
456
|
" - No release is needed (no relevant commits)\n"
|
|
321
457
|
" - semantic-release is not installed or configured\n"
|
|
322
|
-
|
|
323
|
-
|
|
458
|
+
)
|
|
459
|
+
if error_details:
|
|
460
|
+
error_msg += f"\nDetails: {error_details}\n"
|
|
461
|
+
error_msg += (
|
|
462
|
+
"\nPlease either:\n"
|
|
324
463
|
" - Install semantic-release: npm install -g semantic-release"
|
|
325
464
|
)
|
|
326
465
|
if is_subfolder:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: python-package-folder
|
|
3
|
-
Version: 4.
|
|
3
|
+
Version: 4.1.1
|
|
4
4
|
Summary: Python package to automatically package and build a folder, fetching all relevant dependencies.
|
|
5
5
|
Project-URL: Repository, https://github.com/alelom/python-package-folder
|
|
6
6
|
Author-email: Alessio Lombardi <work@alelom.com>
|
|
@@ -475,15 +475,32 @@ The `--version` option:
|
|
|
475
475
|
|
|
476
476
|
When `--version` is not provided, the tool can automatically determine the next version using semantic-release. This requires Node.js, npm, and semantic-release to be installed.
|
|
477
477
|
|
|
478
|
+
**Version Detection:**
|
|
479
|
+
- **Baseline version**:
|
|
480
|
+
- **Registry Query (Preferred)**: When publishing to a repository (PyPI, TestPyPI, or Azure Artifacts), the tool queries the target registry for the latest published version and uses it as the baseline for version calculation. This ensures version calculations are based on what's actually published, not just git tags.
|
|
481
|
+
- **Git Tags (Fallback)**: If the package doesn't exist on the registry yet (first release) or if registry query fails, the tool falls back to using git tags to determine the starting version.
|
|
482
|
+
- **New version to publish**: After determining the baseline version, [`semantic-release`](https://semantic-release.gitbook.io/semantic-release/) analyzes commits since that version to calculate the next version bump (major, minor, or patch) based on [_conventional commit_](https://www.conventionalcommits.org/en/v1.0.0/) messages.
|
|
483
|
+
|
|
478
484
|
**For subfolder builds (Workflow 1):**
|
|
479
485
|
- Uses per-package tags: `{package-name}-v{version}` (e.g., `my-package-v1.2.3`)
|
|
486
|
+
- Queries the target registry for the latest published version of the subfolder package
|
|
480
487
|
- Filters commits to only those affecting the subfolder path
|
|
488
|
+
- **Commit filtering behavior**: Only commits that modify files within the subfolder path are considered for version calculation. Commits that only target files outside the subfolder are excluded. For example:
|
|
489
|
+
- `fix: update my_subfolder/foo.py` → **Included** (affects subfolder)
|
|
490
|
+
- `feat: add feature to other_package/bar.py` → **Excluded** (doesn't affect subfolder)
|
|
491
|
+
- `fix: update my_subfolder/baz.py and shared/utils.py` → **Included** (affects subfolder, even if it also touches files outside)
|
|
481
492
|
- Requires `semantic-release-commit-filter` plugin
|
|
482
493
|
|
|
483
494
|
**For main package builds (Workflow 2):**
|
|
484
495
|
- Uses repo-level tags: `v{version}` (e.g., `v1.2.3`)
|
|
496
|
+
- Queries the target registry for the latest published version when publishing
|
|
485
497
|
- Analyzes all commits in the repository
|
|
486
498
|
|
|
499
|
+
**Registry Support:**
|
|
500
|
+
- **PyPI**: Fully supported via JSON API (`https://pypi.org/pypi/{package-name}/json`)
|
|
501
|
+
- **TestPyPI**: Fully supported via JSON API (`https://test.pypi.org/pypi/{package-name}/json`)
|
|
502
|
+
- **Azure Artifacts**: Basic support with fallback to git tags. Azure Artifacts uses a different API format and may require authentication, so if the query fails, the tool automatically falls back to git tags.
|
|
503
|
+
|
|
487
504
|
**Setup:**
|
|
488
505
|
```bash
|
|
489
506
|
# Install semantic-release globally
|
|
@@ -6,13 +6,13 @@ python_package_folder/finder.py,sha256=RPidZ7LKCFuQ_KgCFIZdHWPXsZIDor3M4C0hKeYW7
|
|
|
6
6
|
python_package_folder/manager.py,sha256=Z9RPg0ZQ7jZhmEXfCzX9OrD_oiA5p2Pnm5Y9tgW3ObQ,55970
|
|
7
7
|
python_package_folder/publisher.py,sha256=TSjdOvxvnWLbJCnduTK_xZBRfvsrq9kpEH-sfebeWkU,13507
|
|
8
8
|
python_package_folder/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
9
|
-
python_package_folder/python_package_folder.py,sha256=
|
|
9
|
+
python_package_folder/python_package_folder.py,sha256=HOJQOgDiPhwVta4iudehuy1QLu4mrdjUNV6z0zaSTxs,21820
|
|
10
10
|
python_package_folder/subfolder_build.py,sha256=oH_KKLJIMByUZCl8y3AyohUO6Om0OvsIQ7Xg1fkd3jE,38782
|
|
11
11
|
python_package_folder/types.py,sha256=3yeSRR5p_3PDKEAaehW_RJ7NwJHexOIeA08bGaT1iSY,2368
|
|
12
12
|
python_package_folder/utils.py,sha256=lIkWsFKeAYAJ9TDUM99T4pUBHJVbUvCdUgkWQN-LUho,3111
|
|
13
13
|
python_package_folder/version.py,sha256=kIDP6S9trEfs9gj7lBYGxrWm4RPssRla24UtlO9Jkh4,9111
|
|
14
|
-
python_package_folder-4.
|
|
15
|
-
python_package_folder-4.
|
|
16
|
-
python_package_folder-4.
|
|
17
|
-
python_package_folder-4.
|
|
18
|
-
python_package_folder-4.
|
|
14
|
+
python_package_folder-4.1.1.dist-info/METADATA,sha256=tP7YhDnLYlnSzoDTKIjjpVcuCyIs4dGwtuedDFbajUc,37517
|
|
15
|
+
python_package_folder-4.1.1.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
|
|
16
|
+
python_package_folder-4.1.1.dist-info/entry_points.txt,sha256=ttu4wAhoYSHGhWQNercLz9IVTTpXxhVlRA9vSTvaLe0,91
|
|
17
|
+
python_package_folder-4.1.1.dist-info/licenses/LICENSE,sha256=vNgRJh8YiecqZoZld7TtwPI5I72HIymKD9g32fiJjCE,1073
|
|
18
|
+
python_package_folder-4.1.1.dist-info/RECORD,,
|
|
File without changes
|
{python_package_folder-4.0.0.dist-info → python_package_folder-4.1.1.dist-info}/entry_points.txt
RENAMED
|
File without changes
|
{python_package_folder-4.0.0.dist-info → python_package_folder-4.1.1.dist-info}/licenses/LICENSE
RENAMED
|
File without changes
|