python-package-folder 4.3.6__tar.gz → 5.0.1__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.
- python_package_folder-5.0.1/.cursor/plans/replace_node.js_semantic-release_with_custom_python_implementation_64e05e1a.plan.md +268 -0
- {python_package_folder-4.3.6 → python_package_folder-5.0.1}/.github/workflows/ci.yml +0 -3
- python_package_folder-5.0.1/PKG-INFO +192 -0
- python_package_folder-5.0.1/README.md +171 -0
- {python_package_folder-4.3.6 → python_package_folder-5.0.1}/coverage.svg +2 -2
- python_package_folder-5.0.1/docs/DEVELOPMENT.md +38 -0
- python_package_folder-5.0.1/docs/INSTALLATION.md +23 -0
- python_package_folder-5.0.1/docs/PUBLISHING.md +121 -0
- python_package_folder-5.0.1/docs/REFERENCE.md +177 -0
- python_package_folder-5.0.1/docs/USAGE.md +224 -0
- python_package_folder-5.0.1/docs/VERSION_RESOLUTION.md +191 -0
- {python_package_folder-4.3.6 → python_package_folder-5.0.1}/pyproject.toml +2 -8
- python_package_folder-5.0.1/src/python_package_folder/python_package_folder.py +318 -0
- python_package_folder-5.0.1/src/python_package_folder/version_calculator.py +597 -0
- python_package_folder-5.0.1/tests/folder_structure/utility_folder/_SS/some_superseded_file.py +0 -0
- python_package_folder-5.0.1/tests/test_version_calculator.py +494 -0
- {python_package_folder-4.3.6 → python_package_folder-5.0.1}/uv.lock +5 -17
- python_package_folder-4.3.6/MANIFEST.in +0 -2
- python_package_folder-4.3.6/PKG-INFO +0 -952
- python_package_folder-4.3.6/README.md +0 -932
- python_package_folder-4.3.6/python_package_folder/scripts/get-next-version.cjs +0 -668
- python_package_folder-4.3.6/scripts/get-next-version.cjs +0 -383
- python_package_folder-4.3.6/src/python_package_folder/python_package_folder.py +0 -579
- {python_package_folder-4.3.6 → python_package_folder-5.0.1}/.copier-answers.yml +0 -0
- {python_package_folder-4.3.6 → python_package_folder-5.0.1}/.cursor/plans/optional_version_+_semantic-release_efed88a6.plan.md +0 -0
- {python_package_folder-4.3.6 → python_package_folder-5.0.1}/.cursor/rules/general.mdc +0 -0
- {python_package_folder-4.3.6 → python_package_folder-5.0.1}/.cursor/rules/python.mdc +0 -0
- {python_package_folder-4.3.6 → python_package_folder-5.0.1}/.github/workflows/publish.yml +0 -0
- {python_package_folder-4.3.6 → python_package_folder-5.0.1}/.gitignore +0 -0
- {python_package_folder-4.3.6 → python_package_folder-5.0.1}/.vscode/settings.json +0 -0
- {python_package_folder-4.3.6 → python_package_folder-5.0.1}/LICENSE +0 -0
- /python_package_folder-4.3.6/src/python_package_folder/py.typed → /python_package_folder-5.0.1/MANIFEST.in +0 -0
- {python_package_folder-4.3.6 → python_package_folder-5.0.1}/Makefile +0 -0
- {python_package_folder-4.3.6 → python_package_folder-5.0.1}/development.md +0 -0
- {python_package_folder-4.3.6 → python_package_folder-5.0.1}/installation.md +0 -0
- {python_package_folder-4.3.6 → python_package_folder-5.0.1}/publishing.md +0 -0
- {python_package_folder-4.3.6 → python_package_folder-5.0.1}/src/python_package_folder/__init__.py +0 -0
- {python_package_folder-4.3.6 → python_package_folder-5.0.1}/src/python_package_folder/__main__.py +0 -0
- {python_package_folder-4.3.6 → python_package_folder-5.0.1}/src/python_package_folder/_hatch_build.py +0 -0
- {python_package_folder-4.3.6 → python_package_folder-5.0.1}/src/python_package_folder/analyzer.py +0 -0
- {python_package_folder-4.3.6 → python_package_folder-5.0.1}/src/python_package_folder/finder.py +0 -0
- {python_package_folder-4.3.6 → python_package_folder-5.0.1}/src/python_package_folder/manager.py +0 -0
- {python_package_folder-4.3.6 → python_package_folder-5.0.1}/src/python_package_folder/publisher.py +0 -0
- /python_package_folder-4.3.6/tests/folder_structure/utility_folder/_SS/some_superseded_file.py → /python_package_folder-5.0.1/src/python_package_folder/py.typed +0 -0
- {python_package_folder-4.3.6 → python_package_folder-5.0.1}/src/python_package_folder/subfolder_build.py +0 -0
- {python_package_folder-4.3.6 → python_package_folder-5.0.1}/src/python_package_folder/types.py +0 -0
- {python_package_folder-4.3.6 → python_package_folder-5.0.1}/src/python_package_folder/utils.py +0 -0
- {python_package_folder-4.3.6 → python_package_folder-5.0.1}/src/python_package_folder/version.py +0 -0
- {python_package_folder-4.3.6 → python_package_folder-5.0.1}/tests/conftest.py +0 -0
- {python_package_folder-4.3.6 → python_package_folder-5.0.1}/tests/folder_structure/some_globals.py +0 -0
- {python_package_folder-4.3.6 → python_package_folder-5.0.1}/tests/folder_structure/subfolder_to_build/README.md +0 -0
- {python_package_folder-4.3.6 → python_package_folder-5.0.1}/tests/folder_structure/subfolder_to_build/__init__.py +0 -0
- {python_package_folder-4.3.6 → python_package_folder-5.0.1}/tests/folder_structure/subfolder_to_build/some_function.py +0 -0
- {python_package_folder-4.3.6 → python_package_folder-5.0.1}/tests/folder_structure/subfolder_to_build/some_globals.py +0 -0
- {python_package_folder-4.3.6 → python_package_folder-5.0.1}/tests/folder_structure/utility_folder/some_utility.py +0 -0
- {python_package_folder-4.3.6 → python_package_folder-5.0.1}/tests/test_build_with_external_deps.py +0 -0
- {python_package_folder-4.3.6 → python_package_folder-5.0.1}/tests/test_linting.py +0 -0
- {python_package_folder-4.3.6 → python_package_folder-5.0.1}/tests/test_preserve_directory_structure.py +0 -0
- {python_package_folder-4.3.6 → python_package_folder-5.0.1}/tests/test_publisher.py +0 -0
- {python_package_folder-4.3.6 → python_package_folder-5.0.1}/tests/test_shared_subdirectory_imports.py +0 -0
- {python_package_folder-4.3.6 → python_package_folder-5.0.1}/tests/test_spreadsheet_creation_imports.py +0 -0
- {python_package_folder-4.3.6 → python_package_folder-5.0.1}/tests/test_subfolder_build.py +0 -0
- {python_package_folder-4.3.6 → python_package_folder-5.0.1}/tests/test_third_party_dependencies.py +0 -0
- {python_package_folder-4.3.6 → python_package_folder-5.0.1}/tests/test_utils.py +0 -0
- {python_package_folder-4.3.6 → python_package_folder-5.0.1}/tests/test_version_manager.py +0 -0
- {python_package_folder-4.3.6 → python_package_folder-5.0.1}/tests/tests.py +0 -0
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: Replace Node.js semantic-release with custom Python implementation
|
|
3
|
+
overview: Replace the Node.js semantic-release dependency with a pure Python implementation that calculates versions from conventional commits, queries registries for baseline versions, and filters commits by subfolder path. This eliminates the push permission requirement and Node.js dependency.
|
|
4
|
+
todos: []
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Replace Node.js semantic-release with Custom Python Implementation
|
|
8
|
+
|
|
9
|
+
## Overview
|
|
10
|
+
|
|
11
|
+
Replace the Node.js `semantic-release` implementation with a pure Python solution that:
|
|
12
|
+
|
|
13
|
+
- Calculates next version from conventional commits (no push permissions needed)
|
|
14
|
+
- Queries registries (PyPI/TestPyPI/Azure) for baseline versions
|
|
15
|
+
- Filters commits by subfolder path for monorepo support
|
|
16
|
+
- Eliminates Node.js dependency
|
|
17
|
+
|
|
18
|
+
## Architecture
|
|
19
|
+
|
|
20
|
+
```
|
|
21
|
+
┌─────────────────────────────────────────────────────────────┐
|
|
22
|
+
│ Version Resolution Flow │
|
|
23
|
+
└─────────────────────────────────────────────────────────────┘
|
|
24
|
+
│
|
|
25
|
+
▼
|
|
26
|
+
┌─────────────────────────────────────┐
|
|
27
|
+
│ Query Registry (PyPI/Azure/etc) │
|
|
28
|
+
│ for latest published version │
|
|
29
|
+
└─────────────────────────────────────┘
|
|
30
|
+
│
|
|
31
|
+
┌─────────────┴─────────────┐
|
|
32
|
+
│ │
|
|
33
|
+
▼ ▼
|
|
34
|
+
Success Failure
|
|
35
|
+
│ │
|
|
36
|
+
▼ ▼
|
|
37
|
+
Use registry Query Git Tags
|
|
38
|
+
version (v1.2.3 or
|
|
39
|
+
│ package-v1.2.3)
|
|
40
|
+
│ │
|
|
41
|
+
└─────────────┬─────────────┘
|
|
42
|
+
│
|
|
43
|
+
▼
|
|
44
|
+
┌─────────────────────────────┐
|
|
45
|
+
│ Get commits since baseline │
|
|
46
|
+
│ (filter by subfolder path) │
|
|
47
|
+
└─────────────────────────────┘
|
|
48
|
+
│
|
|
49
|
+
▼
|
|
50
|
+
┌─────────────────────────────┐
|
|
51
|
+
│ Parse conventional commits │
|
|
52
|
+
│ (feat/fix/BREAKING CHANGE) │
|
|
53
|
+
└─────────────────────────────┘
|
|
54
|
+
│
|
|
55
|
+
▼
|
|
56
|
+
┌─────────────────────────────┐
|
|
57
|
+
│ Determine bump type │
|
|
58
|
+
│ (major/minor/patch) │
|
|
59
|
+
└─────────────────────────────┘
|
|
60
|
+
│
|
|
61
|
+
▼
|
|
62
|
+
┌─────────────────────────────┐
|
|
63
|
+
│ Calculate next version │
|
|
64
|
+
│ (baseline + bump) │
|
|
65
|
+
└─────────────────────────────┘
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## Implementation Details
|
|
69
|
+
|
|
70
|
+
### 1. Create New Python Module: `src/python_package_folder/version_calculator.py`
|
|
71
|
+
|
|
72
|
+
**Functions to implement:**
|
|
73
|
+
|
|
74
|
+
- `query_registry_version(package_name: str, repository: str, repository_url: str | None = None) -> str | None`
|
|
75
|
+
- Query PyPI/TestPyPI JSON API (`https://pypi.org/pypi/{package}/json`)
|
|
76
|
+
- Query Azure Artifacts (basic support, fallback on failure)
|
|
77
|
+
- Return latest version string or None
|
|
78
|
+
|
|
79
|
+
- `get_latest_git_tag(project_root: Path, package_name: str | None = None, is_subfolder: bool = False) -> str | None`
|
|
80
|
+
- For main package: look for tags matching `v{version}` (e.g., `v1.2.3`)
|
|
81
|
+
- For subfolder: look for tags matching `{package-name}-v{version}` (e.g., `my-package-v1.2.3`)
|
|
82
|
+
- Use `git tag --list` and parse version from tag
|
|
83
|
+
- Return latest version or None
|
|
84
|
+
|
|
85
|
+
- `get_commits_since(project_root: Path, baseline_version: str, subfolder_path: Path | None = None) -> list[str]`
|
|
86
|
+
- Use `git log --oneline` to get commits since baseline
|
|
87
|
+
- If `subfolder_path` provided, use `git log -- <subfolder_path>` to filter
|
|
88
|
+
- Return list of commit messages
|
|
89
|
+
|
|
90
|
+
- `parse_commit_for_bump(commit_message: str) -> str | None`
|
|
91
|
+
- Parse conventional commit format following [Angular Commit Message Conventions](https://github.com/angular/angular/blob/main/contributing-docs/commit-message-guidelines.md) as used by semantic-release
|
|
92
|
+
- Return `"major"`, `"minor"`, `"patch"`, or `None`
|
|
93
|
+
- **Breaking Change Detection (Major bump):**
|
|
94
|
+
- `BREAKING CHANGE:` in commit footer (body)
|
|
95
|
+
- `!` after type/scope: `feat!:`, `feat(scope)!:`, `fix!:`, etc.
|
|
96
|
+
- **Version Bump Rules:**
|
|
97
|
+
- `feat:` → `minor` (new feature)
|
|
98
|
+
- `fix:` → `patch` (bug fix)
|
|
99
|
+
- `perf:` → `patch` (performance improvement)
|
|
100
|
+
- **No version bump** (ignored): `docs:`, `style:`, `refactor:`, `test:`, `build:`, `ci:`, `chore:`, `revert:`
|
|
101
|
+
- All commit types supported by semantic-release Angular preset are recognized, but only `feat`, `fix`, and `perf` affect version bumps
|
|
102
|
+
|
|
103
|
+
- `calculate_next_version(baseline_version: str, commits: list[str], subfolder_path: Path | None = None) -> str | None`
|
|
104
|
+
- Get commits since baseline (filtered by subfolder if needed)
|
|
105
|
+
- Parse each commit for bump type
|
|
106
|
+
- Determine highest bump (major > minor > patch)
|
|
107
|
+
- Increment version: `1.2.3` + `minor` → `1.3.0`
|
|
108
|
+
- Return next version or None if no changes
|
|
109
|
+
|
|
110
|
+
- `resolve_version(project_root: Path, package_name: str | None = None, subfolder_path: Path | None = None, repository: str | None = None, repository_url: str | None = None) -> tuple[str | None, str | None]`
|
|
111
|
+
- Main entry point (replaces `resolve_version_via_semantic_release`)
|
|
112
|
+
- Try registry query first, fallback to git tags
|
|
113
|
+
- Calculate next version from commits
|
|
114
|
+
- Return `(version, error_message)` tuple
|
|
115
|
+
|
|
116
|
+
### 2. Update `src/python_package_folder/python_package_folder.py`
|
|
117
|
+
|
|
118
|
+
**Changes:**
|
|
119
|
+
|
|
120
|
+
- Replace `resolve_version_via_semantic_release()` call with `resolve_version()` from `version_calculator`
|
|
121
|
+
- Remove `check_node_available()` function (no longer needed)
|
|
122
|
+
- Remove Node.js error messages
|
|
123
|
+
- Update imports: `from python_package_folder.version_calculator import resolve_version`
|
|
124
|
+
|
|
125
|
+
### 3. Remove Node.js Dependencies
|
|
126
|
+
|
|
127
|
+
**Files to delete:**
|
|
128
|
+
|
|
129
|
+
- `src/python_package_folder/scripts/get-next-version.cjs`
|
|
130
|
+
- `scripts/get-next-version.cjs` (if exists)
|
|
131
|
+
|
|
132
|
+
**Files to update:**
|
|
133
|
+
|
|
134
|
+
- `pyproject.toml`: Remove any references to Node.js scripts in build hooks
|
|
135
|
+
- `src/python_package_folder/_hatch_build.py`: Remove script inclusion logic (if present)
|
|
136
|
+
- `MANIFEST.in`: Remove script inclusion (if present)
|
|
137
|
+
|
|
138
|
+
### 4. Update Documentation: `README.md`
|
|
139
|
+
|
|
140
|
+
**Sections to update:**
|
|
141
|
+
|
|
142
|
+
- **"Automatic Version Resolution" section:**
|
|
143
|
+
- Remove Node.js/semantic-release setup instructions
|
|
144
|
+
- Update to describe Python-native implementation
|
|
145
|
+
- Keep registry query explanation
|
|
146
|
+
- Update commit filtering explanation (now native Python)
|
|
147
|
+
- **Add architecture diagram** (mermaid format) showing version resolution flow:
|
|
148
|
+
```mermaid
|
|
149
|
+
flowchart TD
|
|
150
|
+
Start[Version Resolution] --> QueryRegistry[Query Registry<br/>PyPI/Azure/etc]
|
|
151
|
+
QueryRegistry -->|Success| UseRegistry[Use Registry Version]
|
|
152
|
+
QueryRegistry -->|Failure| QueryGitTags[Query Git Tags<br/>v1.2.3 or package-v1.2.3]
|
|
153
|
+
UseRegistry --> GetCommits[Get Commits Since Baseline<br/>filter by subfolder path]
|
|
154
|
+
QueryGitTags --> GetCommits
|
|
155
|
+
GetCommits --> ParseCommits[Parse Conventional Commits<br/>feat/fix/perf/BREAKING CHANGE]
|
|
156
|
+
ParseCommits --> DetermineBump[Determine Bump Type<br/>major/minor/patch]
|
|
157
|
+
DetermineBump --> CalculateVersion[Calculate Next Version<br/>baseline + bump]
|
|
158
|
+
CalculateVersion --> End[Return Version]
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
- **Add "Supported Commit Types" subsection:**
|
|
162
|
+
- Document all Angular commit types supported (following [Angular Commit Message Conventions](https://github.com/angular/angular/blob/main/contributing-docs/commit-message-guidelines.md) as used by [semantic-release](https://semantic-release.gitbook.io/semantic-release/))
|
|
163
|
+
- **Version Bump Types:**
|
|
164
|
+
- `feat:` → **Minor** (new feature)
|
|
165
|
+
- `fix:` → **Patch** (bug fix)
|
|
166
|
+
- `perf:` → **Patch** (performance improvement)
|
|
167
|
+
- Breaking changes (any type with `!` or `BREAKING CHANGE:` footer) → **Major**
|
|
168
|
+
- **Ignored Types** (no version bump): `docs:`, `style:`, `refactor:`, `test:`, `build:`, `ci:`, `chore:`, `revert:`
|
|
169
|
+
- **Breaking Change Detection:**
|
|
170
|
+
- `BREAKING CHANGE:` in commit footer/body
|
|
171
|
+
- `!` after type: `feat!:`, `fix!:`, `feat(scope)!:`
|
|
172
|
+
- Include examples for each bump type with actual commit message examples
|
|
173
|
+
- Reference: "This implementation follows the same conventions as [semantic-release](https://semantic-release.gitbook.io/semantic-release/) using the Angular preset"
|
|
174
|
+
|
|
175
|
+
- **"Requirements" section:**
|
|
176
|
+
- Remove: "Install semantic-release globally"
|
|
177
|
+
- Remove: "Install semantic-release-commit-filter"
|
|
178
|
+
- Keep: "Conventional commits required"
|
|
179
|
+
- Add note: "Follows [Angular Commit Message Conventions](https://github.com/angular/angular/blob/main/contributing-docs/commit-message-guidelines.md) as used by [semantic-release](https://semantic-release.gitbook.io/semantic-release/)"
|
|
180
|
+
|
|
181
|
+
- **"Subfolder Versioning" section:**
|
|
182
|
+
- Update to reflect Python-native commit filtering
|
|
183
|
+
- Remove references to `semantic-release-commit-filter`
|
|
184
|
+
|
|
185
|
+
### 5. Add Tests: `tests/test_version_calculator.py`
|
|
186
|
+
|
|
187
|
+
**Test cases:**
|
|
188
|
+
|
|
189
|
+
- `test_query_pypi_version()` - Mock HTTP request to PyPI API
|
|
190
|
+
- `test_query_testpypi_version()` - Mock HTTP request to TestPyPI API
|
|
191
|
+
- `test_query_azure_artifacts_version()` - Mock Azure Artifacts request (basic)
|
|
192
|
+
- `test_get_latest_git_tag_main_package()` - Test `v1.2.3` tag parsing
|
|
193
|
+
- `test_get_latest_git_tag_subfolder()` - Test `package-v1.2.3` tag parsing
|
|
194
|
+
- `test_get_commits_since()` - Test git log filtering
|
|
195
|
+
- `test_get_commits_since_with_subfolder()` - Test subfolder path filtering
|
|
196
|
+
- `test_parse_commit_for_bump_major_breaking_footer()` - Test `BREAKING CHANGE:` in footer
|
|
197
|
+
- `test_parse_commit_for_bump_major_exclamation()` - Test `feat!:`, `fix!:` syntax
|
|
198
|
+
- `test_parse_commit_for_bump_major_exclamation_with_scope()` - Test `feat(scope)!:` syntax
|
|
199
|
+
- `test_parse_commit_for_bump_minor()` - Test `feat:` detection
|
|
200
|
+
- `test_parse_commit_for_bump_patch_fix()` - Test `fix:` detection
|
|
201
|
+
- `test_parse_commit_for_bump_patch_perf()` - Test `perf:` detection
|
|
202
|
+
- `test_parse_commit_for_bump_none()` - Test ignored commit types (docs, style, refactor, test, build, ci, chore, revert)
|
|
203
|
+
- `test_calculate_next_version_patch()` - Test version increment (1.2.3 → 1.2.4)
|
|
204
|
+
- `test_calculate_next_version_minor()` - Test version increment (1.2.3 → 1.3.0)
|
|
205
|
+
- `test_calculate_next_version_major()` - Test version increment (1.2.3 → 2.0.0)
|
|
206
|
+
- `test_calculate_next_version_no_changes()` - Test None when no relevant commits
|
|
207
|
+
- `test_resolve_version_registry_first()` - Integration test with registry query
|
|
208
|
+
- `test_resolve_version_git_fallback()` - Integration test with git tag fallback
|
|
209
|
+
- `test_resolve_version_subfolder()` - Integration test for subfolder builds
|
|
210
|
+
|
|
211
|
+
**Test utilities:**
|
|
212
|
+
|
|
213
|
+
- Use `pytest` fixtures for temporary git repositories
|
|
214
|
+
- Use `unittest.mock` to mock HTTP requests and git commands
|
|
215
|
+
- Use `subprocess` mocking for git commands
|
|
216
|
+
|
|
217
|
+
### 6. Update Integration Tests
|
|
218
|
+
|
|
219
|
+
**Files to check:**
|
|
220
|
+
|
|
221
|
+
- `tests/test_subfolder_build.py` - May need updates if it tests version resolution
|
|
222
|
+
- Any other tests that mock or test `resolve_version_via_semantic_release`
|
|
223
|
+
|
|
224
|
+
### 7. Dependencies
|
|
225
|
+
|
|
226
|
+
**Add to `pyproject.toml` (if not already present):**
|
|
227
|
+
|
|
228
|
+
- `requests>=2.0.0` (for registry queries - already in lock file)
|
|
229
|
+
|
|
230
|
+
**Remove:**
|
|
231
|
+
|
|
232
|
+
- Any Node.js/npm related dependencies or build hooks
|
|
233
|
+
|
|
234
|
+
## Testing Strategy
|
|
235
|
+
|
|
236
|
+
1. **Unit tests**: Test each function in isolation with mocks
|
|
237
|
+
2. **Integration tests**: Test full version resolution flow with temporary git repos
|
|
238
|
+
3. **Manual testing**: Test against this repository's actual git history
|
|
239
|
+
4. **CI testing**: Ensure tests pass in GitHub Actions (no Node.js needed)
|
|
240
|
+
|
|
241
|
+
## Migration Notes
|
|
242
|
+
|
|
243
|
+
- **Backward compatibility**: The function signature `resolve_version()` matches the old `resolve_version_via_semantic_release()` signature, so CLI code doesn't need changes
|
|
244
|
+
- **Error messages**: Update error messages to remove Node.js references
|
|
245
|
+
- **Documentation**: Update all docs to reflect Python-native approach
|
|
246
|
+
|
|
247
|
+
## Files to Create/Modify
|
|
248
|
+
|
|
249
|
+
**New files:**
|
|
250
|
+
|
|
251
|
+
- `src/python_package_folder/version_calculator.py` (~300-400 lines)
|
|
252
|
+
|
|
253
|
+
**Modified files:**
|
|
254
|
+
|
|
255
|
+
- `src/python_package_folder/python_package_folder.py` (replace function call, remove Node.js checks)
|
|
256
|
+
- `README.md` (update documentation)
|
|
257
|
+
- `tests/test_version_calculator.py` (new test file)
|
|
258
|
+
|
|
259
|
+
**Deleted files:**
|
|
260
|
+
|
|
261
|
+
- `src/python_package_folder/scripts/get-next-version.cjs`
|
|
262
|
+
- `scripts/get-next-version.cjs` (if exists)
|
|
263
|
+
|
|
264
|
+
**Potentially modified:**
|
|
265
|
+
|
|
266
|
+
- `pyproject.toml` (remove build hooks for scripts)
|
|
267
|
+
- `src/python_package_folder/_hatch_build.py` (remove script inclusion)
|
|
268
|
+
- `MANIFEST.in` (remove script inclusion)
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: python-package-folder
|
|
3
|
+
Version: 5.0.1
|
|
4
|
+
Summary: Python package to automatically package and build a folder, fetching all relevant dependencies.
|
|
5
|
+
Project-URL: Repository, https://github.com/alelom/python-package-folder
|
|
6
|
+
Author-email: Alessio Lombardi <work@alelom.com>
|
|
7
|
+
License-Expression: MIT
|
|
8
|
+
License-File: LICENSE
|
|
9
|
+
Classifier: Development Status :: 4 - Beta
|
|
10
|
+
Classifier: Intended Audience :: Developers
|
|
11
|
+
Classifier: Operating System :: OS Independent
|
|
12
|
+
Classifier: Programming Language :: Python
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
17
|
+
Classifier: Typing :: Typed
|
|
18
|
+
Requires-Python: <4.0,>=3.11
|
|
19
|
+
Requires-Dist: requests>=2.0.0
|
|
20
|
+
Description-Content-Type: text/markdown
|
|
21
|
+
|
|
22
|
+
# python-package-folder <!-- omit from toc -->
|
|
23
|
+
|
|
24
|
+
[](https://github.com/alelom/python-package-folder/actions/workflows/ci.yml)
|
|
25
|
+
[](https://github.com/alelom/python-package-folder)
|
|
26
|
+
|
|
27
|
+
Easily build and publish any target folder in a repository, including subfolders of a monorepo.
|
|
28
|
+
Together with [sysappend](https://pypi.org/project/sysappend/), this library makes relative imports, flexible import management, and package publishing a breeze.
|
|
29
|
+
|
|
30
|
+
## Documentation
|
|
31
|
+
|
|
32
|
+
- [Installation and Requirements](docs/INSTALLATION.md)
|
|
33
|
+
- [Usage Guide](docs/USAGE.md) - How it works, Python API, subfolder builds
|
|
34
|
+
- [Version Resolution](docs/VERSION_RESOLUTION.md) - Manual and automatic versioning with conventional commits
|
|
35
|
+
- [Publishing Packages](docs/PUBLISHING.md) - Publishing to PyPI, TestPyPI, and Azure Artifacts
|
|
36
|
+
- [API Reference](docs/REFERENCE.md) - Command-line options and Python API
|
|
37
|
+
- [Development](docs/DEVELOPMENT.md) - Contributing and project structure
|
|
38
|
+
|
|
39
|
+
## Use Cases
|
|
40
|
+
|
|
41
|
+
### 1) Publishing a Subfolder from src/ in a Monorepo
|
|
42
|
+
|
|
43
|
+
If you have a monorepo structure with multiple packages in `src/`:
|
|
44
|
+
|
|
45
|
+
```
|
|
46
|
+
project/
|
|
47
|
+
├── src/
|
|
48
|
+
│ ├── core_package/
|
|
49
|
+
│ │ ├── __init__.py
|
|
50
|
+
│ │ ├── core.py
|
|
51
|
+
│ │ └── README.md
|
|
52
|
+
│ ├── api_package/
|
|
53
|
+
│ │ ├── __init__.py
|
|
54
|
+
│ │ ├── api.py
|
|
55
|
+
│ │ └── README.md
|
|
56
|
+
│ └── utils_package/
|
|
57
|
+
│ ├── __init__.py
|
|
58
|
+
│ ├── utils.py
|
|
59
|
+
│ └── README.md
|
|
60
|
+
├── shared/
|
|
61
|
+
│ └── common.py
|
|
62
|
+
└── pyproject.toml
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
You can build and publish any subfolder from `src/` as a standalone package:
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
# Navigate to the subfolder you want to publish
|
|
69
|
+
cd src/api_package
|
|
70
|
+
|
|
71
|
+
# Build and publish to TestPyPI with version 1.2.0
|
|
72
|
+
python-package-folder --publish testpypi --version 1.2.0
|
|
73
|
+
|
|
74
|
+
# Or publish to PyPI with automatic version resolution via conventional commits
|
|
75
|
+
python-package-folder --publish pypi
|
|
76
|
+
|
|
77
|
+
# Or publish to PyPI with a custom package name
|
|
78
|
+
python-package-folder --publish pypi --version 1.2.0 --package-name "my-api-package"
|
|
79
|
+
|
|
80
|
+
# Include a specific dependency group from the parent pyproject.toml
|
|
81
|
+
python-package-folder --publish pypi --version 1.2.0 --dependency-group "dev"
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
The tool will automatically:
|
|
85
|
+
1. Detect the project root (where `pyproject.toml` is located)
|
|
86
|
+
2. Use `src/api_package` as the source directory
|
|
87
|
+
3. Copy any external dependencies (like `shared/common.py`) into the package before building
|
|
88
|
+
4. Use the subfolder's README if present, or create a minimal one
|
|
89
|
+
5. Create a temporary `pyproject.toml` with the subfolder's package name and version
|
|
90
|
+
6. Build and publish the package
|
|
91
|
+
7. Clean up all temporary files and restore the original `pyproject.toml`
|
|
92
|
+
|
|
93
|
+
This is especially useful for monorepos where you want to publish individual packages independently while sharing common code.
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
### 2) Building Packages with Shared Code
|
|
97
|
+
|
|
98
|
+
If your project structure looks like this:
|
|
99
|
+
|
|
100
|
+
```
|
|
101
|
+
project/
|
|
102
|
+
├── src/
|
|
103
|
+
│ └── my_package/
|
|
104
|
+
│ └── main.py
|
|
105
|
+
├── shared/
|
|
106
|
+
│ ├── utils.py
|
|
107
|
+
│ └── helpers.py
|
|
108
|
+
└── pyproject.toml
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
And `main.py` imports from `shared/`:
|
|
112
|
+
|
|
113
|
+
```python
|
|
114
|
+
from shared.utils import some_function
|
|
115
|
+
from shared.helpers import Helper
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
This package will automatically:
|
|
119
|
+
1. Detect that `shared/` is outside `src/`
|
|
120
|
+
2. Copy `shared/` into `src/` before building
|
|
121
|
+
3. Build your package with all dependencies included
|
|
122
|
+
4. Clean up the copied files after build
|
|
123
|
+
|
|
124
|
+
## Features
|
|
125
|
+
|
|
126
|
+
- **Subfolder Build Support**: Build subfolders as separate packages with automatic detection and configuration
|
|
127
|
+
- **Automatic subfolder detection**: Detects when building a subfolder (not the main `src/` directory)
|
|
128
|
+
- Creates any needed file for publishing automatically, cleaning up if not originally in the subfolder after the build/publish process. E.g. copies external dependencies into the source directory before build and cleans them up afterward; temporary `__init__.py` creation for non-package subfolders; uses subfolder README if present, otherwise creates minimal README
|
|
129
|
+
- Automatic package name derivation from subfolder name
|
|
130
|
+
- Automatic temporary `pyproject.toml` creation with correct package structure
|
|
131
|
+
- Dependency group selection: specify which dependency group from parent `pyproject.toml` to include.
|
|
132
|
+
|
|
133
|
+
- **Smart Import Classification and analysis**:
|
|
134
|
+
- Recursively parses all `.py` files to detect `import` and `from ... import ...` statements
|
|
135
|
+
- Handles external dependencies (modules and files that originate from outside the main package directory), and distinguishes standard library imports, 3rd-party packages (from site-packages), local/external/relative/ambiguous imports.
|
|
136
|
+
|
|
137
|
+
- **Idempotent Operations**: Safely handles repeated runs without duplicating files
|
|
138
|
+
- **Build Integration**: Seamlessly integrates with build tools like `uv build`, `pip build`, etc.
|
|
139
|
+
- **Version Management**:
|
|
140
|
+
- Set static versions for publishing (PEP 440 compliant)
|
|
141
|
+
- Temporarily override dynamic versioning during builds
|
|
142
|
+
- Automatic restoration of dynamic versioning after build
|
|
143
|
+
- **Automatic version resolution** via conventional commits (Python-native, no Node.js required)
|
|
144
|
+
- **Package Publishing**:
|
|
145
|
+
- Uses twine to publish the built folder/subfolder
|
|
146
|
+
- Handles publishing to to PyPI, TestPyPI, or Azure Artifacts, with interactive credential prompts, secure storage support
|
|
147
|
+
|
|
148
|
+
## Quick Start
|
|
149
|
+
|
|
150
|
+
The simplest way to use this package is via the command-line interface
|
|
151
|
+
|
|
152
|
+
**Build/publish a specific subfolder in a repository**
|
|
153
|
+
|
|
154
|
+
Useful for monorepos containing many subfolders that may need publishing as stand-alone packages for external usage.
|
|
155
|
+
|
|
156
|
+
```bash
|
|
157
|
+
# First cd to the specific subfolder
|
|
158
|
+
cd src/subfolder_to_build_and_publish
|
|
159
|
+
|
|
160
|
+
# Build and publish any subdirectory of your repo to TestPyPi (https://test.pypi.org/)
|
|
161
|
+
# Version can be provided explicitly or resolved automatically via conventional commits
|
|
162
|
+
python-package-folder --publish testpypi --version 0.0.2
|
|
163
|
+
|
|
164
|
+
# Or let the tool determine the next version automatically from conventional commits
|
|
165
|
+
python-package-folder --publish testpypi
|
|
166
|
+
|
|
167
|
+
# Only analyse (no building)
|
|
168
|
+
cd src/subfolder_to_build_and_publish
|
|
169
|
+
python-package-folder --analyze-only
|
|
170
|
+
|
|
171
|
+
# Only build
|
|
172
|
+
cd src/subfolder_to_build_and_publish
|
|
173
|
+
python-package-folder
|
|
174
|
+
|
|
175
|
+
# Build with automatic dependency management
|
|
176
|
+
python-package-folder --build-command "uv build"
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
You can also target a specific subfolder via commandline, rather than `cd`ing there:
|
|
180
|
+
|
|
181
|
+
```bash
|
|
182
|
+
# Specify custom project root and source directory
|
|
183
|
+
python-package-folder --project-root /path/to/project --src-dir /path/to/src --build-command "pip build"
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
## License <!-- omit from toc -->
|
|
187
|
+
|
|
188
|
+
MIT License - see LICENSE file for details
|
|
189
|
+
|
|
190
|
+
## Contributing <!-- omit from toc -->
|
|
191
|
+
|
|
192
|
+
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
# python-package-folder <!-- omit from toc -->
|
|
2
|
+
|
|
3
|
+
[](https://github.com/alelom/python-package-folder/actions/workflows/ci.yml)
|
|
4
|
+
[](https://github.com/alelom/python-package-folder)
|
|
5
|
+
|
|
6
|
+
Easily build and publish any target folder in a repository, including subfolders of a monorepo.
|
|
7
|
+
Together with [sysappend](https://pypi.org/project/sysappend/), this library makes relative imports, flexible import management, and package publishing a breeze.
|
|
8
|
+
|
|
9
|
+
## Documentation
|
|
10
|
+
|
|
11
|
+
- [Installation and Requirements](docs/INSTALLATION.md)
|
|
12
|
+
- [Usage Guide](docs/USAGE.md) - How it works, Python API, subfolder builds
|
|
13
|
+
- [Version Resolution](docs/VERSION_RESOLUTION.md) - Manual and automatic versioning with conventional commits
|
|
14
|
+
- [Publishing Packages](docs/PUBLISHING.md) - Publishing to PyPI, TestPyPI, and Azure Artifacts
|
|
15
|
+
- [API Reference](docs/REFERENCE.md) - Command-line options and Python API
|
|
16
|
+
- [Development](docs/DEVELOPMENT.md) - Contributing and project structure
|
|
17
|
+
|
|
18
|
+
## Use Cases
|
|
19
|
+
|
|
20
|
+
### 1) Publishing a Subfolder from src/ in a Monorepo
|
|
21
|
+
|
|
22
|
+
If you have a monorepo structure with multiple packages in `src/`:
|
|
23
|
+
|
|
24
|
+
```
|
|
25
|
+
project/
|
|
26
|
+
├── src/
|
|
27
|
+
│ ├── core_package/
|
|
28
|
+
│ │ ├── __init__.py
|
|
29
|
+
│ │ ├── core.py
|
|
30
|
+
│ │ └── README.md
|
|
31
|
+
│ ├── api_package/
|
|
32
|
+
│ │ ├── __init__.py
|
|
33
|
+
│ │ ├── api.py
|
|
34
|
+
│ │ └── README.md
|
|
35
|
+
│ └── utils_package/
|
|
36
|
+
│ ├── __init__.py
|
|
37
|
+
│ ├── utils.py
|
|
38
|
+
│ └── README.md
|
|
39
|
+
├── shared/
|
|
40
|
+
│ └── common.py
|
|
41
|
+
└── pyproject.toml
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
You can build and publish any subfolder from `src/` as a standalone package:
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
# Navigate to the subfolder you want to publish
|
|
48
|
+
cd src/api_package
|
|
49
|
+
|
|
50
|
+
# Build and publish to TestPyPI with version 1.2.0
|
|
51
|
+
python-package-folder --publish testpypi --version 1.2.0
|
|
52
|
+
|
|
53
|
+
# Or publish to PyPI with automatic version resolution via conventional commits
|
|
54
|
+
python-package-folder --publish pypi
|
|
55
|
+
|
|
56
|
+
# Or publish to PyPI with a custom package name
|
|
57
|
+
python-package-folder --publish pypi --version 1.2.0 --package-name "my-api-package"
|
|
58
|
+
|
|
59
|
+
# Include a specific dependency group from the parent pyproject.toml
|
|
60
|
+
python-package-folder --publish pypi --version 1.2.0 --dependency-group "dev"
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
The tool will automatically:
|
|
64
|
+
1. Detect the project root (where `pyproject.toml` is located)
|
|
65
|
+
2. Use `src/api_package` as the source directory
|
|
66
|
+
3. Copy any external dependencies (like `shared/common.py`) into the package before building
|
|
67
|
+
4. Use the subfolder's README if present, or create a minimal one
|
|
68
|
+
5. Create a temporary `pyproject.toml` with the subfolder's package name and version
|
|
69
|
+
6. Build and publish the package
|
|
70
|
+
7. Clean up all temporary files and restore the original `pyproject.toml`
|
|
71
|
+
|
|
72
|
+
This is especially useful for monorepos where you want to publish individual packages independently while sharing common code.
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
### 2) Building Packages with Shared Code
|
|
76
|
+
|
|
77
|
+
If your project structure looks like this:
|
|
78
|
+
|
|
79
|
+
```
|
|
80
|
+
project/
|
|
81
|
+
├── src/
|
|
82
|
+
│ └── my_package/
|
|
83
|
+
│ └── main.py
|
|
84
|
+
├── shared/
|
|
85
|
+
│ ├── utils.py
|
|
86
|
+
│ └── helpers.py
|
|
87
|
+
└── pyproject.toml
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
And `main.py` imports from `shared/`:
|
|
91
|
+
|
|
92
|
+
```python
|
|
93
|
+
from shared.utils import some_function
|
|
94
|
+
from shared.helpers import Helper
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
This package will automatically:
|
|
98
|
+
1. Detect that `shared/` is outside `src/`
|
|
99
|
+
2. Copy `shared/` into `src/` before building
|
|
100
|
+
3. Build your package with all dependencies included
|
|
101
|
+
4. Clean up the copied files after build
|
|
102
|
+
|
|
103
|
+
## Features
|
|
104
|
+
|
|
105
|
+
- **Subfolder Build Support**: Build subfolders as separate packages with automatic detection and configuration
|
|
106
|
+
- **Automatic subfolder detection**: Detects when building a subfolder (not the main `src/` directory)
|
|
107
|
+
- Creates any needed file for publishing automatically, cleaning up if not originally in the subfolder after the build/publish process. E.g. copies external dependencies into the source directory before build and cleans them up afterward; temporary `__init__.py` creation for non-package subfolders; uses subfolder README if present, otherwise creates minimal README
|
|
108
|
+
- Automatic package name derivation from subfolder name
|
|
109
|
+
- Automatic temporary `pyproject.toml` creation with correct package structure
|
|
110
|
+
- Dependency group selection: specify which dependency group from parent `pyproject.toml` to include.
|
|
111
|
+
|
|
112
|
+
- **Smart Import Classification and analysis**:
|
|
113
|
+
- Recursively parses all `.py` files to detect `import` and `from ... import ...` statements
|
|
114
|
+
- Handles external dependencies (modules and files that originate from outside the main package directory), and distinguishes standard library imports, 3rd-party packages (from site-packages), local/external/relative/ambiguous imports.
|
|
115
|
+
|
|
116
|
+
- **Idempotent Operations**: Safely handles repeated runs without duplicating files
|
|
117
|
+
- **Build Integration**: Seamlessly integrates with build tools like `uv build`, `pip build`, etc.
|
|
118
|
+
- **Version Management**:
|
|
119
|
+
- Set static versions for publishing (PEP 440 compliant)
|
|
120
|
+
- Temporarily override dynamic versioning during builds
|
|
121
|
+
- Automatic restoration of dynamic versioning after build
|
|
122
|
+
- **Automatic version resolution** via conventional commits (Python-native, no Node.js required)
|
|
123
|
+
- **Package Publishing**:
|
|
124
|
+
- Uses twine to publish the built folder/subfolder
|
|
125
|
+
- Handles publishing to to PyPI, TestPyPI, or Azure Artifacts, with interactive credential prompts, secure storage support
|
|
126
|
+
|
|
127
|
+
## Quick Start
|
|
128
|
+
|
|
129
|
+
The simplest way to use this package is via the command-line interface
|
|
130
|
+
|
|
131
|
+
**Build/publish a specific subfolder in a repository**
|
|
132
|
+
|
|
133
|
+
Useful for monorepos containing many subfolders that may need publishing as stand-alone packages for external usage.
|
|
134
|
+
|
|
135
|
+
```bash
|
|
136
|
+
# First cd to the specific subfolder
|
|
137
|
+
cd src/subfolder_to_build_and_publish
|
|
138
|
+
|
|
139
|
+
# Build and publish any subdirectory of your repo to TestPyPi (https://test.pypi.org/)
|
|
140
|
+
# Version can be provided explicitly or resolved automatically via conventional commits
|
|
141
|
+
python-package-folder --publish testpypi --version 0.0.2
|
|
142
|
+
|
|
143
|
+
# Or let the tool determine the next version automatically from conventional commits
|
|
144
|
+
python-package-folder --publish testpypi
|
|
145
|
+
|
|
146
|
+
# Only analyse (no building)
|
|
147
|
+
cd src/subfolder_to_build_and_publish
|
|
148
|
+
python-package-folder --analyze-only
|
|
149
|
+
|
|
150
|
+
# Only build
|
|
151
|
+
cd src/subfolder_to_build_and_publish
|
|
152
|
+
python-package-folder
|
|
153
|
+
|
|
154
|
+
# Build with automatic dependency management
|
|
155
|
+
python-package-folder --build-command "uv build"
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
You can also target a specific subfolder via commandline, rather than `cd`ing there:
|
|
159
|
+
|
|
160
|
+
```bash
|
|
161
|
+
# Specify custom project root and source directory
|
|
162
|
+
python-package-folder --project-root /path/to/project --src-dir /path/to/src --build-command "pip build"
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
## License <!-- omit from toc -->
|
|
166
|
+
|
|
167
|
+
MIT License - see LICENSE file for details
|
|
168
|
+
|
|
169
|
+
## Contributing <!-- omit from toc -->
|
|
170
|
+
|
|
171
|
+
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
@@ -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">
|
|
18
|
-
<text x="81" y="14">
|
|
17
|
+
<text x="81" y="15" fill="#010101" fill-opacity=".3">68%</text>
|
|
18
|
+
<text x="81" y="14">68%</text>
|
|
19
19
|
</g>
|
|
20
20
|
</svg>
|