python-package-folder 3.1.2__tar.gz → 4.0.0__tar.gz

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.
Files changed (52) hide show
  1. python_package_folder-4.0.0/.cursor/plans/optional_version_+_semantic-release_efed88a6.plan.md +123 -0
  2. {python_package_folder-3.1.2 → python_package_folder-4.0.0}/.github/workflows/ci.yml +4 -0
  3. {python_package_folder-3.1.2 → python_package_folder-4.0.0}/.github/workflows/publish.yml +39 -0
  4. {python_package_folder-3.1.2 → python_package_folder-4.0.0}/PKG-INFO +63 -9
  5. {python_package_folder-3.1.2 → python_package_folder-4.0.0}/README.md +62 -8
  6. {python_package_folder-3.1.2 → python_package_folder-4.0.0}/coverage.svg +2 -2
  7. {python_package_folder-3.1.2 → python_package_folder-4.0.0}/pyproject.toml +6 -16
  8. python_package_folder-4.0.0/python_package_folder/scripts/get-next-version.cjs +383 -0
  9. python_package_folder-4.0.0/scripts/get-next-version.cjs +383 -0
  10. python_package_folder-4.0.0/src/python_package_folder/_hatch_build.py +39 -0
  11. {python_package_folder-3.1.2 → python_package_folder-4.0.0}/src/python_package_folder/manager.py +131 -17
  12. {python_package_folder-3.1.2 → python_package_folder-4.0.0}/src/python_package_folder/python_package_folder.py +180 -11
  13. {python_package_folder-3.1.2 → python_package_folder-4.0.0}/tests/test_build_with_external_deps.py +30 -0
  14. {python_package_folder-3.1.2 → python_package_folder-4.0.0}/tests/test_linting.py +15 -4
  15. {python_package_folder-3.1.2 → python_package_folder-4.0.0}/.copier-answers.yml +0 -0
  16. {python_package_folder-3.1.2 → python_package_folder-4.0.0}/.cursor/rules/general.mdc +0 -0
  17. {python_package_folder-3.1.2 → python_package_folder-4.0.0}/.cursor/rules/python.mdc +0 -0
  18. {python_package_folder-3.1.2 → python_package_folder-4.0.0}/.gitignore +0 -0
  19. {python_package_folder-3.1.2 → python_package_folder-4.0.0}/.vscode/settings.json +0 -0
  20. {python_package_folder-3.1.2 → python_package_folder-4.0.0}/LICENSE +0 -0
  21. {python_package_folder-3.1.2 → python_package_folder-4.0.0}/Makefile +0 -0
  22. {python_package_folder-3.1.2 → python_package_folder-4.0.0}/development.md +0 -0
  23. {python_package_folder-3.1.2 → python_package_folder-4.0.0}/installation.md +0 -0
  24. {python_package_folder-3.1.2 → python_package_folder-4.0.0}/publishing.md +0 -0
  25. {python_package_folder-3.1.2 → python_package_folder-4.0.0}/src/python_package_folder/__init__.py +0 -0
  26. {python_package_folder-3.1.2 → python_package_folder-4.0.0}/src/python_package_folder/__main__.py +0 -0
  27. {python_package_folder-3.1.2 → python_package_folder-4.0.0}/src/python_package_folder/analyzer.py +0 -0
  28. {python_package_folder-3.1.2 → python_package_folder-4.0.0}/src/python_package_folder/finder.py +0 -0
  29. {python_package_folder-3.1.2 → python_package_folder-4.0.0}/src/python_package_folder/publisher.py +0 -0
  30. {python_package_folder-3.1.2 → python_package_folder-4.0.0}/src/python_package_folder/py.typed +0 -0
  31. {python_package_folder-3.1.2 → python_package_folder-4.0.0}/src/python_package_folder/subfolder_build.py +0 -0
  32. {python_package_folder-3.1.2 → python_package_folder-4.0.0}/src/python_package_folder/types.py +0 -0
  33. {python_package_folder-3.1.2 → python_package_folder-4.0.0}/src/python_package_folder/utils.py +0 -0
  34. {python_package_folder-3.1.2 → python_package_folder-4.0.0}/src/python_package_folder/version.py +0 -0
  35. {python_package_folder-3.1.2 → python_package_folder-4.0.0}/tests/conftest.py +0 -0
  36. {python_package_folder-3.1.2 → python_package_folder-4.0.0}/tests/folder_structure/some_globals.py +0 -0
  37. {python_package_folder-3.1.2 → python_package_folder-4.0.0}/tests/folder_structure/subfolder_to_build/README.md +0 -0
  38. {python_package_folder-3.1.2 → python_package_folder-4.0.0}/tests/folder_structure/subfolder_to_build/__init__.py +0 -0
  39. {python_package_folder-3.1.2 → python_package_folder-4.0.0}/tests/folder_structure/subfolder_to_build/some_function.py +0 -0
  40. {python_package_folder-3.1.2 → python_package_folder-4.0.0}/tests/folder_structure/subfolder_to_build/some_globals.py +0 -0
  41. {python_package_folder-3.1.2 → python_package_folder-4.0.0}/tests/folder_structure/utility_folder/_SS/some_superseded_file.py +0 -0
  42. {python_package_folder-3.1.2 → python_package_folder-4.0.0}/tests/folder_structure/utility_folder/some_utility.py +0 -0
  43. {python_package_folder-3.1.2 → python_package_folder-4.0.0}/tests/test_preserve_directory_structure.py +0 -0
  44. {python_package_folder-3.1.2 → python_package_folder-4.0.0}/tests/test_publisher.py +0 -0
  45. {python_package_folder-3.1.2 → python_package_folder-4.0.0}/tests/test_shared_subdirectory_imports.py +0 -0
  46. {python_package_folder-3.1.2 → python_package_folder-4.0.0}/tests/test_spreadsheet_creation_imports.py +0 -0
  47. {python_package_folder-3.1.2 → python_package_folder-4.0.0}/tests/test_subfolder_build.py +0 -0
  48. {python_package_folder-3.1.2 → python_package_folder-4.0.0}/tests/test_third_party_dependencies.py +0 -0
  49. {python_package_folder-3.1.2 → python_package_folder-4.0.0}/tests/test_utils.py +0 -0
  50. {python_package_folder-3.1.2 → python_package_folder-4.0.0}/tests/test_version_manager.py +0 -0
  51. {python_package_folder-3.1.2 → python_package_folder-4.0.0}/tests/tests.py +0 -0
  52. {python_package_folder-3.1.2 → python_package_folder-4.0.0}/uv.lock +0 -0
@@ -0,0 +1,123 @@
1
+ # Optional --version with semantic-release (both workflows)
2
+
3
+ ## Scope: two workflows
4
+
5
+ 1. **Workflow 1 – Publishing a subfolder** (monorepo): Build and publish a subfolder of `src/` as its own package. Version today is required via `--version`.
6
+ 2. **Workflow 2 – Building packages with shared code**: Build the main package (e.g. `src/my_package/`) that imports from `shared/`. Version today comes from `pyproject.toml` (dynamic or static) or `--version` when publishing.
7
+
8
+ Both workflows should support **optional `--version**`: when omitted, resolve the next version via semantic-release and use it for the build/publish.
9
+
10
+ ## Current behavior
11
+
12
+ - **CLI** ([`python_package_folder.py`](src/python_package_folder/python_package_folder.py)): For subfolder builds, `--version` is required; the tool exits with an error if it is missing (lines 158–164). For main-package builds, `--version` is optional; version comes from `pyproject.toml` or user.
13
+ - **Manager** ([`manager.py`](src/python_package_folder/manager.py)): `prepare_build` defaults version to `"0.0.0"` with a warning when `version` is `None` for subfolders (lines 232–239). `build_and_publish` raises `ValueError` if `version` is missing for a subfolder build (lines 1254–1258). For main package, `version` can be None (no error); then publish uses whatever the build produced (dynamic versioning or static from pyproject).
14
+ - **Publisher** ([`publisher.py`](src/python_package_folder/publisher.py)): Filters dist files by `package_name` and `version`; both are required for reliable filtering.
15
+
16
+ ## Target behavior
17
+
18
+ - `**--version` optional for both workflows**: If `--version` is not provided and a version is needed (subfolder build, or main-package publish), compute the next version using semantic-release, then proceed with that version. If provided, keep current behavior (explicit version).
19
+ - **Workflow 1 (subfolder)**: Per-package tags `{package-name}-v{version}` and commits filtered to the subfolder path.
20
+ - **Workflow 2 (main package)**: Repo-level tags (e.g. `v{version}`), no path filter; run semantic-release from project root.
21
+
22
+ ## Architecture
23
+
24
+ ```mermaid
25
+ flowchart LR
26
+ subgraph Workflows
27
+ W1[Workflow 1: Subfolder build]
28
+ W2[Workflow 2: Main package with shared code]
29
+ end
30
+ subgraph CLI
31
+ A[Build or publish without --version]
32
+ B[Resolve version via semantic-release]
33
+ C[Build and publish with resolved version]
34
+ end
35
+ W1 --> A
36
+ W2 --> A
37
+ A --> B --> C
38
+ B --> Node[Node: get-next-version script]
39
+ Node --> SR[semantic-release dry-run]
40
+ SR --> NextVer[Next version]
41
+ NextVer --> C
42
+ ```
43
+
44
+ - **Version resolution**: When `--version` is missing and needed (subfolder build, or main-package publish), call a Node script that runs semantic-release in dry-run and prints the next version to stdout.
45
+ - **Workflow 1**: Script runs with subfolder path and package name → per-package tag format and path-filtered commits (semantic-release-commit-filter).
46
+ - **Workflow 2**: Script runs from project root, no path filter → default tag format `v{version}`; package name from `pyproject.toml` for Publisher filtering only.
47
+ - **Fallback**: If Node/semantic-release is unavailable or semantic-release decides there is no release, fail with a clear message and suggest installing semantic-release (and commit-filter for subfolders) or passing `--version` explicitly.
48
+
49
+ ## Implementation options for “get next version”
50
+
51
+ - **Option A (recommended): Small Node script using semantic-release API**
52
+
53
+ Add a script (e.g. `scripts/get-next-version.cjs` or under `.release/`) that:
54
+
55
+ - Takes args: project root, subfolder path (relative or absolute), package name.
56
+ - Ensures a minimal `package.json` exists in the subfolder (or in a temp location with correct `name`) so that semantic-release-commit-filter can use `package.name` for `tagFormat` and filter commits by cwd.
57
+ - Requires semantic-release and semantic-release-commit-filter, runs semantic-release programmatically with `dryRun: true`, and prints `nextRelease.version` (or “none”) to stdout.
58
+
59
+ This avoids parsing human-oriented dry-run output and gives a single, stable contract.
60
+
61
+ - **Option B: Parse `npx semantic-release --dry-run` output**
62
+
63
+ Run the CLI in dry-run and parse stdout. Possible but brittle (format can change, localization, etc.). Not recommended.
64
+
65
+ ## Key implementation details
66
+
67
+ 1. **Where to run semantic-release**
68
+
69
+ Run from the **subfolder** directory so that semantic-release-commit-filter’s “current directory” is the subfolder and commits are filtered to that path. Tag format will be `{package.name}-v${version}` from the `package.json` in that directory.
70
+
71
+ 2. **Temporary `package.json` in subfolder**
72
+
73
+ Python subfolders usually have no `package.json`. Create a temporary one for the version resolution only: `{"name": "<package_name>"}` (same name as used for the Python package). Run semantic-release from the subfolder, then remove the temp file (or overwrite only if we created it). Document that the script may create/remove `package.json` in the subfolder so users are not surprised.
74
+
75
+ 3. **Dependencies**
76
+
77
+ - No new Python dependencies.
78
+ - Document that **Node.js** and **npm** (or **npx**) must be available when using auto-versioning.
79
+ - Document (and optionally script) install of semantic-release and semantic-release-commit-filter, e.g. `npm install -g semantic-release semantic-release-commit-filter` or per-repo `package.json` with these as devDependencies.
80
+
81
+ 4. **CLI flow**
82
+
83
+ - If subfolder build and `args.version` is None:
84
+ - Call the version resolver (subprocess: `node scripts/get-next-version.cjs <project_root> <subfolder_path> <package_name>`).
85
+ - If resolver returns a version string: use it for the rest of the flow.
86
+ - If resolver returns “none” or fails (no release / semantic-release not found / Node error): exit with a clear error suggesting to pass `--version` or to install and configure semantic-release.
87
+ - Pass the resolved or explicit version into `build_and_publish` / `prepare_build` as today.
88
+
89
+ 5. **Manager / Publisher**
90
+
91
+ No change to the contract: they still receive a concrete `version` (either from CLI or from the resolver). Only the CLI and the new resolution step change.
92
+
93
+ 6. **Convention**
94
+
95
+ Rely on default Angular/conventional commit rules (e.g. `fix:` → patch, `feat:` → minor, `BREAKING CHANGE:` → major). Document that conventional commits are required for auto-versioning; no change to commit format inside this repo unless you add a config file for semantic-release.
96
+
97
+ ## Files to add or touch
98
+
99
+ | Item | Action |
100
+
101
+ | ---------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
102
+
103
+ | New script | Add `scripts/get-next-version.cjs` (or similar) that runs semantic-release in dry-run with commit-filter and prints next version. |
104
+
105
+ | CLI | In [`python_package_folder.py`](src/python_package_folder/python_package_folder.py): when `is_subfolder and not args.version`, call the resolver; on success set `args.version` (or a local variable) to the resolved version; on failure exit with error. Remove the “version required” error for this case. |
106
+
107
+ | Manager | In [`manager.py`](src/python_package_folder/manager.py): keep the `ValueError` when `version` is None for subfolder in `build_and_publish` (CLI will always pass a version after resolution). Optionally keep or adjust the “default 0.0.0” in `prepare_build` for programmatic callers who still omit version. |
108
+
109
+ | Docs | Update README (and any publishing doc) to describe: `--version` optional for subfolders when semantic-release is used, per-package tags, conventional commits, and Node/npm + semantic-release (and commit-filter) setup. |
110
+
111
+ | Tests | Add tests for: CLI with subfolder and no `--version` (mock or skip if Node/semantic-release missing), and for the resolver helper (or script) when given a fixture repo with tags and conventional commits. |
112
+
113
+ ## Open decisions
114
+
115
+ - **Script location**: Ship `get-next-version.cjs` inside this repo under `scripts/` (or `.release/`) so that `python-package-folder` can invoke it without requiring the user to add it. The script will `require('semantic-release')` and `require('semantic-release-commit-filter')`; users must have these installed (globally or in a local `package.json` at project root or subfolder).
116
+ - **First release / no tag**: If there is no tag for this package yet, semantic-release will use an initial version (e.g. 1.0.0). Confirm desired behavior (e.g. configurable first version or always 1.0.0).
117
+ - **No release (no relevant commits)**: If semantic-release determines there is no release, the script should output something like “none” and the CLI should exit with a clear message rather than defaulting to 0.0.0.
118
+
119
+ ## Summary
120
+
121
+ - Make `--version` optional for subfolder builds by resolving the next version via Node.js semantic-release with per-package tags and path-filtered commits.
122
+ - Add a small Node script that runs semantic-release in dry-run and prints the next version; wire it from the CLI when `--version` is omitted.
123
+ - Document Node/npm and semantic-release (and semantic-release-commit-filter) as requirements for this mode, and keep explicit `--version` as the fallback when auto-versioning is not available or not desired.
@@ -59,6 +59,10 @@ jobs:
59
59
  - name: Run linting
60
60
  run: uv run ruff check .
61
61
 
62
+ - name: Check formatting
63
+ continue-on-error: true
64
+ run: uv run ruff format --check .
65
+
62
66
  - name: Run type checking
63
67
  run: uv run basedpyright src/ || true
64
68
 
@@ -4,6 +4,11 @@ on:
4
4
  release:
5
5
  types: [published]
6
6
  workflow_dispatch: # Enable manual trigger.
7
+ inputs:
8
+ version:
9
+ description: 'Package version to publish (e.g., 4.0.1). Required for manual triggers.'
10
+ required: true
11
+ type: string
7
12
 
8
13
  jobs:
9
14
  build-and-publish:
@@ -58,6 +63,40 @@ jobs:
58
63
  rm -rf dist/
59
64
  echo "✓ Cleaned dist/ directory"
60
65
 
66
+ - name: Extract version from release tag or input
67
+ id: version
68
+ run: |
69
+ if [ "${{ github.event_name }}" = "release" ]; then
70
+ # Extract from release tag
71
+ VERSION="${{ github.event.release.tag_name }}"
72
+ # Strip 'v' prefix if present
73
+ VERSION="${VERSION#v}"
74
+ echo "version=$VERSION" >> $GITHUB_OUTPUT
75
+ echo "Extracted version from release tag: $VERSION"
76
+ else
77
+ # For workflow_dispatch, use the provided input
78
+ VERSION="${{ github.event.inputs.version }}"
79
+ if [ -z "$VERSION" ]; then
80
+ echo "Error: Version is required for manual workflow_dispatch triggers"
81
+ exit 1
82
+ fi
83
+ # Strip 'v' prefix if present (user might include it)
84
+ VERSION="${VERSION#v}"
85
+ echo "version=$VERSION" >> $GITHUB_OUTPUT
86
+ echo "Using version from workflow input: $VERSION"
87
+ fi
88
+
89
+ - name: Set version for release
90
+ if: steps.version.outputs.version != ''
91
+ run: |
92
+ python -c "
93
+ from pathlib import Path
94
+ from python_package_folder.version import VersionManager
95
+ version_manager = VersionManager(project_root=Path('.'))
96
+ version_manager.set_version('${{ steps.version.outputs.version }}')
97
+ print(f'✓ Set version to ${{ steps.version.outputs.version }}')
98
+ "
99
+
61
100
  - name: Build package
62
101
  run: uv build
63
102
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-package-folder
3
- Version: 3.1.2
3
+ Version: 4.0.0
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>
@@ -75,6 +75,9 @@ cd src/api_package
75
75
  # Build and publish to TestPyPI with version 1.2.0
76
76
  python-package-folder --publish testpypi --version 1.2.0
77
77
 
78
+ # Or publish to PyPI with automatic version resolution via semantic-release
79
+ python-package-folder --publish pypi
80
+
78
81
  # Or publish to PyPI with a custom package name
79
82
  python-package-folder --publish pypi --version 1.2.0 --package-name "my-api-package"
80
83
 
@@ -169,6 +172,13 @@ uv add twine
169
172
 
170
173
  **For secure credential storage**: `keyring` is optional but recommended (install with `pip install keyring`)
171
174
 
175
+ **For automatic version resolution**: When using `--version` optional mode (automatic version resolution via semantic-release), you'll need:
176
+ - Node.js and npm (or npx)
177
+ - semantic-release: `npm install -g semantic-release`
178
+ - For subfolder builds: semantic-release-commit-filter: `npm install -g semantic-release-commit-filter`
179
+
180
+ Alternatively, install these as devDependencies in your project's `package.json`.
181
+
172
182
 
173
183
  ## Quick Start
174
184
 
@@ -182,9 +192,13 @@ Useful for monorepos containing many subfolders that may need publishing as stan
182
192
  # First cd to the specific subfolder
183
193
  cd src/subfolder_to_build_and_publish
184
194
 
185
- # Build and publish any subdirectory of your repo to TestPyPi (https://test.pypi.org/)
195
+ # Build and publish any subdirectory of your repo to TestPyPi (https://test.pypi.org/)
196
+ # Version can be provided explicitly or resolved automatically via semantic-release
186
197
  python-package-folder --publish testpypi --version 0.0.2
187
198
 
199
+ # Or let semantic-release determine the next version automatically (requires semantic-release setup)
200
+ python-package-folder --publish testpypi
201
+
188
202
  # Only analyse (no building)
189
203
  cd src/subfolder_to_build_and_publish
190
204
  python-package-folder --analyze-only
@@ -457,33 +471,72 @@ The `--version` option:
457
471
  **Version Format**: Versions must follow PEP 440 (e.g., `1.2.3`, `1.2.3a1`, `1.2.3.post1`, `1.2.3.dev1`)
458
472
 
459
473
 
474
+ ### Automatic Version Resolution (semantic-release)
475
+
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
+
478
+ **For subfolder builds (Workflow 1):**
479
+ - Uses per-package tags: `{package-name}-v{version}` (e.g., `my-package-v1.2.3`)
480
+ - Filters commits to only those affecting the subfolder path
481
+ - Requires `semantic-release-commit-filter` plugin
482
+
483
+ **For main package builds (Workflow 2):**
484
+ - Uses repo-level tags: `v{version}` (e.g., `v1.2.3`)
485
+ - Analyzes all commits in the repository
486
+
487
+ **Setup:**
488
+ ```bash
489
+ # Install semantic-release globally
490
+ npm install -g semantic-release
491
+
492
+ # For subfolder builds, also install semantic-release-commit-filter
493
+ npm install -g semantic-release-commit-filter
494
+ ```
495
+
496
+ **Usage:**
497
+ ```bash
498
+ # Subfolder build - version resolved automatically
499
+ cd src/my_subfolder
500
+ python-package-folder --publish pypi
501
+
502
+ # Main package - version resolved automatically
503
+ python-package-folder --publish pypi
504
+ ```
505
+
506
+ **Requirements:**
507
+ - Conventional commits (e.g., `fix:`, `feat:`, `BREAKING CHANGE:`) are required for semantic-release to determine version bumps
508
+ - The tool will fall back to requiring `--version` explicitly if semantic-release is not available or determines no release is needed
509
+
460
510
  ### Subfolder Versioning
461
511
 
462
512
  When building from a subdirectory (not the main `src/` directory), the tool automatically detects the subfolder and sets up the build configuration:
463
513
 
464
514
  ```bash
465
- # Build a subfolder as a separate package (version recommended but not required)
515
+ # Build a subfolder as a separate package with explicit version
466
516
  cd my_project/subfolder_to_build
467
517
  python-package-folder --version "1.0.0" --publish pypi
468
518
 
519
+ # Or let semantic-release determine the version automatically
520
+ python-package-folder --publish pypi
521
+
469
522
  # With custom package name
470
523
  python-package-folder --version "1.0.0" --package-name "my-custom-name" --publish pypi
471
-
472
- # Version defaults to "0.0.0" if not specified (with a warning)
473
- python-package-folder --publish pypi
474
524
  ```
475
525
 
476
526
  For subfolder builds:
477
527
  - **Automatic detection**: The tool automatically detects subfolder builds
528
+ - **Version resolution**:
529
+ - If `--version` is provided: Uses the explicit version
530
+ - If `--version` is omitted: Attempts to resolve via semantic-release (requires setup)
531
+ - If semantic-release is unavailable or determines no release: Requires `--version` explicitly
478
532
  - **pyproject.toml handling**:
479
533
  - If `pyproject.toml` exists in subfolder: Uses that file (copied to project root temporarily)
480
534
  - If no `pyproject.toml` in subfolder: Creates temporary one with correct package structure
481
- - **Version**: Recommended but not required when creating temporary pyproject.toml. If not provided, defaults to `0.0.0` with a warning. Ignored if subfolder has its own `pyproject.toml`.
482
535
  - **Package name**: Automatically derived from the subfolder name (e.g., `subfolder_to_build` → `subfolder-to-build`). Only used when creating temporary pyproject.toml.
483
536
  - **Restoration**: Original `pyproject.toml` is restored after build
484
537
  - **Temporary configuration**: Creates a temporary `pyproject.toml` with:
485
538
  - Custom package name (from `--package-name` or derived)
486
- - Specified version
539
+ - Specified or resolved version
487
540
  - Correct package path for hatchling
488
541
  - Dependency group from parent (if `--dependency-group` is specified)
489
542
  - **Package initialization**: Automatically creates `__init__.py` if the subfolder doesn't have one (required for hatchling)
@@ -686,7 +739,8 @@ options:
686
739
  --password PASSWORD Password/token for publishing (will prompt if not provided)
687
740
  --skip-existing Skip files that already exist on the repository
688
741
  --version VERSION Set a specific version before building (PEP 440 format).
689
- Required for subfolder builds.
742
+ Optional: if omitted, version will be resolved via
743
+ semantic-release (requires Node.js and semantic-release setup).
690
744
  --package-name PACKAGE_NAME
691
745
  Package name for subfolder builds (default: derived from
692
746
  source directory name)
@@ -55,6 +55,9 @@ cd src/api_package
55
55
  # Build and publish to TestPyPI with version 1.2.0
56
56
  python-package-folder --publish testpypi --version 1.2.0
57
57
 
58
+ # Or publish to PyPI with automatic version resolution via semantic-release
59
+ python-package-folder --publish pypi
60
+
58
61
  # Or publish to PyPI with a custom package name
59
62
  python-package-folder --publish pypi --version 1.2.0 --package-name "my-api-package"
60
63
 
@@ -149,6 +152,13 @@ uv add twine
149
152
 
150
153
  **For secure credential storage**: `keyring` is optional but recommended (install with `pip install keyring`)
151
154
 
155
+ **For automatic version resolution**: When using `--version` optional mode (automatic version resolution via semantic-release), you'll need:
156
+ - Node.js and npm (or npx)
157
+ - semantic-release: `npm install -g semantic-release`
158
+ - For subfolder builds: semantic-release-commit-filter: `npm install -g semantic-release-commit-filter`
159
+
160
+ Alternatively, install these as devDependencies in your project's `package.json`.
161
+
152
162
 
153
163
  ## Quick Start
154
164
 
@@ -162,9 +172,13 @@ Useful for monorepos containing many subfolders that may need publishing as stan
162
172
  # First cd to the specific subfolder
163
173
  cd src/subfolder_to_build_and_publish
164
174
 
165
- # Build and publish any subdirectory of your repo to TestPyPi (https://test.pypi.org/)
175
+ # Build and publish any subdirectory of your repo to TestPyPi (https://test.pypi.org/)
176
+ # Version can be provided explicitly or resolved automatically via semantic-release
166
177
  python-package-folder --publish testpypi --version 0.0.2
167
178
 
179
+ # Or let semantic-release determine the next version automatically (requires semantic-release setup)
180
+ python-package-folder --publish testpypi
181
+
168
182
  # Only analyse (no building)
169
183
  cd src/subfolder_to_build_and_publish
170
184
  python-package-folder --analyze-only
@@ -437,33 +451,72 @@ The `--version` option:
437
451
  **Version Format**: Versions must follow PEP 440 (e.g., `1.2.3`, `1.2.3a1`, `1.2.3.post1`, `1.2.3.dev1`)
438
452
 
439
453
 
454
+ ### Automatic Version Resolution (semantic-release)
455
+
456
+ 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.
457
+
458
+ **For subfolder builds (Workflow 1):**
459
+ - Uses per-package tags: `{package-name}-v{version}` (e.g., `my-package-v1.2.3`)
460
+ - Filters commits to only those affecting the subfolder path
461
+ - Requires `semantic-release-commit-filter` plugin
462
+
463
+ **For main package builds (Workflow 2):**
464
+ - Uses repo-level tags: `v{version}` (e.g., `v1.2.3`)
465
+ - Analyzes all commits in the repository
466
+
467
+ **Setup:**
468
+ ```bash
469
+ # Install semantic-release globally
470
+ npm install -g semantic-release
471
+
472
+ # For subfolder builds, also install semantic-release-commit-filter
473
+ npm install -g semantic-release-commit-filter
474
+ ```
475
+
476
+ **Usage:**
477
+ ```bash
478
+ # Subfolder build - version resolved automatically
479
+ cd src/my_subfolder
480
+ python-package-folder --publish pypi
481
+
482
+ # Main package - version resolved automatically
483
+ python-package-folder --publish pypi
484
+ ```
485
+
486
+ **Requirements:**
487
+ - Conventional commits (e.g., `fix:`, `feat:`, `BREAKING CHANGE:`) are required for semantic-release to determine version bumps
488
+ - The tool will fall back to requiring `--version` explicitly if semantic-release is not available or determines no release is needed
489
+
440
490
  ### Subfolder Versioning
441
491
 
442
492
  When building from a subdirectory (not the main `src/` directory), the tool automatically detects the subfolder and sets up the build configuration:
443
493
 
444
494
  ```bash
445
- # Build a subfolder as a separate package (version recommended but not required)
495
+ # Build a subfolder as a separate package with explicit version
446
496
  cd my_project/subfolder_to_build
447
497
  python-package-folder --version "1.0.0" --publish pypi
448
498
 
499
+ # Or let semantic-release determine the version automatically
500
+ python-package-folder --publish pypi
501
+
449
502
  # With custom package name
450
503
  python-package-folder --version "1.0.0" --package-name "my-custom-name" --publish pypi
451
-
452
- # Version defaults to "0.0.0" if not specified (with a warning)
453
- python-package-folder --publish pypi
454
504
  ```
455
505
 
456
506
  For subfolder builds:
457
507
  - **Automatic detection**: The tool automatically detects subfolder builds
508
+ - **Version resolution**:
509
+ - If `--version` is provided: Uses the explicit version
510
+ - If `--version` is omitted: Attempts to resolve via semantic-release (requires setup)
511
+ - If semantic-release is unavailable or determines no release: Requires `--version` explicitly
458
512
  - **pyproject.toml handling**:
459
513
  - If `pyproject.toml` exists in subfolder: Uses that file (copied to project root temporarily)
460
514
  - If no `pyproject.toml` in subfolder: Creates temporary one with correct package structure
461
- - **Version**: Recommended but not required when creating temporary pyproject.toml. If not provided, defaults to `0.0.0` with a warning. Ignored if subfolder has its own `pyproject.toml`.
462
515
  - **Package name**: Automatically derived from the subfolder name (e.g., `subfolder_to_build` → `subfolder-to-build`). Only used when creating temporary pyproject.toml.
463
516
  - **Restoration**: Original `pyproject.toml` is restored after build
464
517
  - **Temporary configuration**: Creates a temporary `pyproject.toml` with:
465
518
  - Custom package name (from `--package-name` or derived)
466
- - Specified version
519
+ - Specified or resolved version
467
520
  - Correct package path for hatchling
468
521
  - Dependency group from parent (if `--dependency-group` is specified)
469
522
  - **Package initialization**: Automatically creates `__init__.py` if the subfolder doesn't have one (required for hatchling)
@@ -666,7 +719,8 @@ options:
666
719
  --password PASSWORD Password/token for publishing (will prompt if not provided)
667
720
  --skip-existing Skip files that already exist on the repository
668
721
  --version VERSION Set a specific version before building (PEP 440 format).
669
- Required for subfolder builds.
722
+ Optional: if omitted, version will be resolved via
723
+ semantic-release (requires Node.js and semantic-release setup).
670
724
  --package-name PACKAGE_NAME
671
725
  Package name for subfolder builds (default: derived from
672
726
  source directory name)
@@ -14,7 +14,7 @@
14
14
  <g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11">
15
15
  <text x="31.5" y="15" fill="#010101" fill-opacity=".3">coverage</text>
16
16
  <text x="31.5" y="14">coverage</text>
17
- <text x="81" y="15" fill="#010101" fill-opacity=".3">68%</text>
18
- <text x="81" y="14">68%</text>
17
+ <text x="81" y="15" fill="#010101" fill-opacity=".3">66%</text>
18
+ <text x="81" y="14">66%</text>
19
19
  </g>
20
20
  </svg>
@@ -14,7 +14,6 @@ authors = [
14
14
  readme = "README.md"
15
15
  license = "MIT"
16
16
  requires-python = ">=3.11,<4.0"
17
- dynamic = ["version"]
18
17
 
19
18
  # https://pypi.org/classifiers/
20
19
  # Adjust as needed:
@@ -43,6 +42,7 @@ dependencies = [
43
42
 
44
43
  # ---- Dev dependencies ----
45
44
 
45
+ version = "4.0.0"
46
46
  [dependency-groups]
47
47
  dev = [
48
48
  "pytest>=8.3.5",
@@ -71,20 +71,8 @@ python-package-folder = "python_package_folder.python_package_folder:main"
71
71
  requires = ["hatchling", "uv-dynamic-versioning"]
72
72
  build-backend = "hatchling.build"
73
73
 
74
- [tool.hatch.version]
75
- source = "uv-dynamic-versioning"
76
- # Note JSON schemas don't seem to be right for tool.hatch.version.source so
77
- # this may cause false warnings in IDEs.
78
- # https://github.com/ninoseki/uv-dynamic-versioning/issues/21
79
-
80
- [tool.uv-dynamic-versioning]
81
- vcs = "git"
82
- style = "pep440"
83
- bump = true
84
-
85
- [tool.hatch.build.targets.wheel]
86
- # The source location for the package.
87
- packages = ["src/python_package_folder"]
74
+ [tool.hatch.build.hooks.custom]
75
+ path = "src/python_package_folder/_hatch_build.py"
88
76
 
89
77
 
90
78
  # ---- Settings ----
@@ -164,12 +152,14 @@ reportUnreachable = false
164
152
  # skip = "foo.py,bar.py"
165
153
 
166
154
  [tool.pytest.ini_options]
167
- python_files = ["*.py"]
155
+ python_files = ["test_*.py", "*_test.py"]
168
156
  python_classes = ["Test*"]
169
157
  python_functions = ["test_*"]
170
158
  testpaths = [
171
159
  "src",
172
160
  "tests",
173
161
  ]
162
+ # Ignore build hooks - they're not test files and require hatchling which isn't in test deps
163
+ ignore = ["_hatch_build.py"]
174
164
  norecursedirs = []
175
165
  filterwarnings = []