python-package-folder 4.0.0__tar.gz → 4.1.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.
- {python_package_folder-4.0.0 → python_package_folder-4.1.0}/PKG-INFO +18 -1
- {python_package_folder-4.0.0 → python_package_folder-4.1.0}/README.md +17 -0
- {python_package_folder-4.0.0 → python_package_folder-4.1.0}/coverage.svg +2 -2
- {python_package_folder-4.0.0 → python_package_folder-4.1.0}/pyproject.toml +1 -1
- {python_package_folder-4.0.0 → python_package_folder-4.1.0/python_package_folder}/scripts/get-next-version.cjs +224 -54
- {python_package_folder-4.0.0 → python_package_folder-4.1.0}/src/python_package_folder/python_package_folder.py +44 -3
- {python_package_folder-4.0.0 → python_package_folder-4.1.0}/.copier-answers.yml +0 -0
- {python_package_folder-4.0.0 → python_package_folder-4.1.0}/.cursor/plans/optional_version_+_semantic-release_efed88a6.plan.md +0 -0
- {python_package_folder-4.0.0 → python_package_folder-4.1.0}/.cursor/rules/general.mdc +0 -0
- {python_package_folder-4.0.0 → python_package_folder-4.1.0}/.cursor/rules/python.mdc +0 -0
- {python_package_folder-4.0.0 → python_package_folder-4.1.0}/.github/workflows/ci.yml +0 -0
- {python_package_folder-4.0.0 → python_package_folder-4.1.0}/.github/workflows/publish.yml +0 -0
- {python_package_folder-4.0.0 → python_package_folder-4.1.0}/.gitignore +0 -0
- {python_package_folder-4.0.0 → python_package_folder-4.1.0}/.vscode/settings.json +0 -0
- {python_package_folder-4.0.0 → python_package_folder-4.1.0}/LICENSE +0 -0
- {python_package_folder-4.0.0 → python_package_folder-4.1.0}/Makefile +0 -0
- {python_package_folder-4.0.0 → python_package_folder-4.1.0}/development.md +0 -0
- {python_package_folder-4.0.0 → python_package_folder-4.1.0}/installation.md +0 -0
- {python_package_folder-4.0.0 → python_package_folder-4.1.0}/publishing.md +0 -0
- {python_package_folder-4.0.0/python_package_folder → python_package_folder-4.1.0}/scripts/get-next-version.cjs +0 -0
- {python_package_folder-4.0.0 → python_package_folder-4.1.0}/src/python_package_folder/__init__.py +0 -0
- {python_package_folder-4.0.0 → python_package_folder-4.1.0}/src/python_package_folder/__main__.py +0 -0
- {python_package_folder-4.0.0 → python_package_folder-4.1.0}/src/python_package_folder/_hatch_build.py +0 -0
- {python_package_folder-4.0.0 → python_package_folder-4.1.0}/src/python_package_folder/analyzer.py +0 -0
- {python_package_folder-4.0.0 → python_package_folder-4.1.0}/src/python_package_folder/finder.py +0 -0
- {python_package_folder-4.0.0 → python_package_folder-4.1.0}/src/python_package_folder/manager.py +0 -0
- {python_package_folder-4.0.0 → python_package_folder-4.1.0}/src/python_package_folder/publisher.py +0 -0
- {python_package_folder-4.0.0 → python_package_folder-4.1.0}/src/python_package_folder/py.typed +0 -0
- {python_package_folder-4.0.0 → python_package_folder-4.1.0}/src/python_package_folder/subfolder_build.py +0 -0
- {python_package_folder-4.0.0 → python_package_folder-4.1.0}/src/python_package_folder/types.py +0 -0
- {python_package_folder-4.0.0 → python_package_folder-4.1.0}/src/python_package_folder/utils.py +0 -0
- {python_package_folder-4.0.0 → python_package_folder-4.1.0}/src/python_package_folder/version.py +0 -0
- {python_package_folder-4.0.0 → python_package_folder-4.1.0}/tests/conftest.py +0 -0
- {python_package_folder-4.0.0 → python_package_folder-4.1.0}/tests/folder_structure/some_globals.py +0 -0
- {python_package_folder-4.0.0 → python_package_folder-4.1.0}/tests/folder_structure/subfolder_to_build/README.md +0 -0
- {python_package_folder-4.0.0 → python_package_folder-4.1.0}/tests/folder_structure/subfolder_to_build/__init__.py +0 -0
- {python_package_folder-4.0.0 → python_package_folder-4.1.0}/tests/folder_structure/subfolder_to_build/some_function.py +0 -0
- {python_package_folder-4.0.0 → python_package_folder-4.1.0}/tests/folder_structure/subfolder_to_build/some_globals.py +0 -0
- {python_package_folder-4.0.0 → python_package_folder-4.1.0}/tests/folder_structure/utility_folder/_SS/some_superseded_file.py +0 -0
- {python_package_folder-4.0.0 → python_package_folder-4.1.0}/tests/folder_structure/utility_folder/some_utility.py +0 -0
- {python_package_folder-4.0.0 → python_package_folder-4.1.0}/tests/test_build_with_external_deps.py +0 -0
- {python_package_folder-4.0.0 → python_package_folder-4.1.0}/tests/test_linting.py +0 -0
- {python_package_folder-4.0.0 → python_package_folder-4.1.0}/tests/test_preserve_directory_structure.py +0 -0
- {python_package_folder-4.0.0 → python_package_folder-4.1.0}/tests/test_publisher.py +0 -0
- {python_package_folder-4.0.0 → python_package_folder-4.1.0}/tests/test_shared_subdirectory_imports.py +0 -0
- {python_package_folder-4.0.0 → python_package_folder-4.1.0}/tests/test_spreadsheet_creation_imports.py +0 -0
- {python_package_folder-4.0.0 → python_package_folder-4.1.0}/tests/test_subfolder_build.py +0 -0
- {python_package_folder-4.0.0 → python_package_folder-4.1.0}/tests/test_third_party_dependencies.py +0 -0
- {python_package_folder-4.0.0 → python_package_folder-4.1.0}/tests/test_utils.py +0 -0
- {python_package_folder-4.0.0 → python_package_folder-4.1.0}/tests/test_version_manager.py +0 -0
- {python_package_folder-4.0.0 → python_package_folder-4.1.0}/tests/tests.py +0 -0
- {python_package_folder-4.0.0 → python_package_folder-4.1.0}/uv.lock +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: python-package-folder
|
|
3
|
-
Version: 4.
|
|
3
|
+
Version: 4.1.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>
|
|
@@ -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
|
|
@@ -455,15 +455,32 @@ The `--version` option:
|
|
|
455
455
|
|
|
456
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
457
|
|
|
458
|
+
**Version Detection:**
|
|
459
|
+
- **Baseline version**:
|
|
460
|
+
- **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.
|
|
461
|
+
- **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.
|
|
462
|
+
- **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.
|
|
463
|
+
|
|
458
464
|
**For subfolder builds (Workflow 1):**
|
|
459
465
|
- Uses per-package tags: `{package-name}-v{version}` (e.g., `my-package-v1.2.3`)
|
|
466
|
+
- Queries the target registry for the latest published version of the subfolder package
|
|
460
467
|
- Filters commits to only those affecting the subfolder path
|
|
468
|
+
- **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:
|
|
469
|
+
- `fix: update my_subfolder/foo.py` → **Included** (affects subfolder)
|
|
470
|
+
- `feat: add feature to other_package/bar.py` → **Excluded** (doesn't affect subfolder)
|
|
471
|
+
- `fix: update my_subfolder/baz.py and shared/utils.py` → **Included** (affects subfolder, even if it also touches files outside)
|
|
461
472
|
- Requires `semantic-release-commit-filter` plugin
|
|
462
473
|
|
|
463
474
|
**For main package builds (Workflow 2):**
|
|
464
475
|
- Uses repo-level tags: `v{version}` (e.g., `v1.2.3`)
|
|
476
|
+
- Queries the target registry for the latest published version when publishing
|
|
465
477
|
- Analyzes all commits in the repository
|
|
466
478
|
|
|
479
|
+
**Registry Support:**
|
|
480
|
+
- **PyPI**: Fully supported via JSON API (`https://pypi.org/pypi/{package-name}/json`)
|
|
481
|
+
- **TestPyPI**: Fully supported via JSON API (`https://test.pypi.org/pypi/{package-name}/json`)
|
|
482
|
+
- **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.
|
|
483
|
+
|
|
467
484
|
**Setup:**
|
|
468
485
|
```bash
|
|
469
486
|
# Install semantic-release globally
|
|
@@ -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">65%</text>
|
|
18
|
+
<text x="81" y="14">65%</text>
|
|
19
19
|
</g>
|
|
20
20
|
</svg>
|
|
@@ -7,12 +7,14 @@
|
|
|
7
7
|
* package builds (repo-level tags).
|
|
8
8
|
*
|
|
9
9
|
* Usage:
|
|
10
|
-
* node scripts/get-next-version.cjs <project_root> [subfolder_path] [package_name]
|
|
10
|
+
* node scripts/get-next-version.cjs <project_root> [subfolder_path] [package_name] [repository] [repository_url]
|
|
11
11
|
*
|
|
12
12
|
* Args:
|
|
13
13
|
* - project_root: Root directory of the project (absolute or relative path)
|
|
14
14
|
* - subfolder_path: Optional. Path to subfolder relative to project_root (for Workflow 1)
|
|
15
15
|
* - package_name: Optional. Package name for subfolder builds (for per-package tags)
|
|
16
|
+
* - repository: Optional. Target repository ('pypi', 'testpypi', or 'azure')
|
|
17
|
+
* - repository_url: Optional. Repository URL (required for Azure Artifacts)
|
|
16
18
|
*
|
|
17
19
|
* Output:
|
|
18
20
|
* - Version string (e.g., "1.2.3") if a release is determined
|
|
@@ -22,25 +24,32 @@
|
|
|
22
24
|
|
|
23
25
|
const path = require('path');
|
|
24
26
|
const fs = require('fs');
|
|
27
|
+
const https = require('https');
|
|
28
|
+
const http = require('http');
|
|
25
29
|
|
|
26
30
|
// Parse command line arguments
|
|
27
31
|
const args = process.argv.slice(2);
|
|
28
32
|
if (args.length < 1) {
|
|
29
33
|
console.error('Error: project_root is required');
|
|
30
|
-
console.error('Usage: node get-next-version.cjs <project_root> [subfolder_path] [package_name]');
|
|
34
|
+
console.error('Usage: node get-next-version.cjs <project_root> [subfolder_path] [package_name] [repository] [repository_url]');
|
|
31
35
|
process.exit(1);
|
|
32
36
|
}
|
|
33
37
|
|
|
34
38
|
const projectRoot = path.resolve(args[0]);
|
|
35
|
-
const subfolderPath = args[1]
|
|
36
|
-
const packageName = args[2]
|
|
39
|
+
const subfolderPath = args[1] && args[1] !== 'null' && args[1] !== '' ? args[1] : null;
|
|
40
|
+
const packageName = args[2] && args[2] !== 'null' && args[2] !== '' ? args[2] : null;
|
|
41
|
+
const repository = args[3] && args[3] !== 'null' && args[3] !== '' ? args[3] : null;
|
|
42
|
+
const repositoryUrl = args[4] && args[4] !== 'null' && args[4] !== '' ? args[4] : null;
|
|
37
43
|
|
|
38
|
-
// Validate argument combination
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
44
|
+
// Validate argument combination
|
|
45
|
+
// - For subfolder builds: both subfolder_path and package_name are required
|
|
46
|
+
// - For main package builds: package_name can be provided alone (for registry queries)
|
|
47
|
+
if (subfolderPath !== null && packageName === null) {
|
|
48
|
+
console.error('Error: package_name is required when subfolder_path is provided.');
|
|
49
|
+
console.error('Usage: node get-next-version.cjs <project_root> [subfolder_path] [package_name] [repository] [repository_url]');
|
|
42
50
|
process.exit(1);
|
|
43
51
|
}
|
|
52
|
+
// Note: package_name can be provided without subfolder_path for main package registry queries
|
|
44
53
|
|
|
45
54
|
// Check if project root exists
|
|
46
55
|
if (!fs.existsSync(projectRoot)) {
|
|
@@ -49,6 +58,8 @@ if (!fs.existsSync(projectRoot)) {
|
|
|
49
58
|
}
|
|
50
59
|
|
|
51
60
|
// Determine if this is a subfolder build
|
|
61
|
+
// A subfolder build requires both subfolder_path and package_name
|
|
62
|
+
// package_name alone (without subfolder_path) indicates a main package build with registry query
|
|
52
63
|
const isSubfolderBuild = subfolderPath !== null && packageName !== null;
|
|
53
64
|
const workingDir = isSubfolderBuild
|
|
54
65
|
? path.resolve(projectRoot, subfolderPath)
|
|
@@ -173,10 +184,138 @@ if (isSubfolderBuild) {
|
|
|
173
184
|
}
|
|
174
185
|
}
|
|
175
186
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
187
|
+
/**
|
|
188
|
+
* Query PyPI or TestPyPI JSON API for the latest version of a package.
|
|
189
|
+
* @param {string} packageName - Package name to query
|
|
190
|
+
* @param {string} registry - 'pypi' or 'testpypi'
|
|
191
|
+
* @returns {Promise<string|null>} Latest version string or null if not found
|
|
192
|
+
*/
|
|
193
|
+
async function queryPyPIVersion(packageName, registry) {
|
|
194
|
+
const baseUrl = registry === 'testpypi'
|
|
195
|
+
? 'https://test.pypi.org'
|
|
196
|
+
: 'https://pypi.org';
|
|
197
|
+
const url = `${baseUrl}/pypi/${packageName}/json`;
|
|
198
|
+
|
|
199
|
+
return new Promise((resolve, reject) => {
|
|
200
|
+
https.get(url, (res) => {
|
|
201
|
+
let data = '';
|
|
202
|
+
|
|
203
|
+
res.on('data', (chunk) => {
|
|
204
|
+
data += chunk;
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
res.on('end', () => {
|
|
208
|
+
if (res.statusCode === 404) {
|
|
209
|
+
// Package doesn't exist yet (first release)
|
|
210
|
+
resolve(null);
|
|
211
|
+
} else if (res.statusCode === 200) {
|
|
212
|
+
try {
|
|
213
|
+
const json = JSON.parse(data);
|
|
214
|
+
// Get latest version from info.version or releases
|
|
215
|
+
const version = json.info?.version || Object.keys(json.releases || {}).pop() || null;
|
|
216
|
+
resolve(version);
|
|
217
|
+
} catch (e) {
|
|
218
|
+
reject(new Error(`Failed to parse PyPI response: ${e.message}`));
|
|
219
|
+
}
|
|
220
|
+
} else {
|
|
221
|
+
reject(new Error(`PyPI API returned status ${res.statusCode}`));
|
|
222
|
+
}
|
|
223
|
+
});
|
|
224
|
+
}).on('error', (err) => {
|
|
225
|
+
reject(err);
|
|
226
|
+
});
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Query Azure Artifacts for the latest version of a package.
|
|
232
|
+
* Azure Artifacts uses a simple index format (HTML) which is more complex to parse.
|
|
233
|
+
* For now, we'll attempt to query but fall back gracefully if it fails.
|
|
234
|
+
* @param {string} packageName - Package name to query
|
|
235
|
+
* @param {string} repositoryUrl - Azure Artifacts repository URL
|
|
236
|
+
* @returns {Promise<string|null>} Latest version string or null if not found/unsupported
|
|
237
|
+
*/
|
|
238
|
+
async function queryAzureArtifactsVersion(packageName, repositoryUrl) {
|
|
239
|
+
// Convert upload URL to simple index URL
|
|
240
|
+
// Upload: https://pkgs.dev.azure.com/ORG/PROJECT/_packaging/FEED/pypi/upload
|
|
241
|
+
// Simple: https://pkgs.dev.azure.com/ORG/PROJECT/_packaging/FEED/pypi/simple/{package}/
|
|
242
|
+
let simpleIndexUrl;
|
|
243
|
+
try {
|
|
244
|
+
const url = new URL(repositoryUrl);
|
|
245
|
+
if (url.pathname.endsWith('/upload')) {
|
|
246
|
+
simpleIndexUrl = repositoryUrl.replace('/upload', `/simple/${packageName}/`);
|
|
247
|
+
} else {
|
|
248
|
+
// Try to construct from common patterns
|
|
249
|
+
simpleIndexUrl = repositoryUrl.replace(/\/upload$/, `/simple/${packageName}/`);
|
|
250
|
+
}
|
|
251
|
+
} catch (e) {
|
|
252
|
+
// Invalid URL format, return null to fall back to git tags
|
|
253
|
+
return null;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
return new Promise((resolve) => {
|
|
257
|
+
// Azure Artifacts may require authentication and returns HTML
|
|
258
|
+
// For now, we'll attempt the request but gracefully fall back if it fails
|
|
259
|
+
// This is a limitation - Azure Artifacts API is more complex than PyPI
|
|
260
|
+
const url = new URL(simpleIndexUrl);
|
|
261
|
+
const client = url.protocol === 'https:' ? https : http;
|
|
262
|
+
|
|
263
|
+
const req = client.get(simpleIndexUrl, (res) => {
|
|
264
|
+
// Azure Artifacts simple index returns HTML, not JSON
|
|
265
|
+
// Parsing HTML is complex and may require authentication
|
|
266
|
+
// For now, we'll return null to fall back to git tags
|
|
267
|
+
// This can be enhanced later with proper HTML parsing or API endpoint discovery
|
|
268
|
+
resolve(null);
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
req.on('error', () => {
|
|
272
|
+
// Network error or authentication required - fall back to git tags
|
|
273
|
+
resolve(null);
|
|
274
|
+
});
|
|
275
|
+
|
|
276
|
+
req.setTimeout(5000, () => {
|
|
277
|
+
req.destroy();
|
|
278
|
+
resolve(null);
|
|
279
|
+
});
|
|
280
|
+
});
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
/**
|
|
284
|
+
* Query the package registry for the latest published version.
|
|
285
|
+
* @param {string} packageName - Package name to query
|
|
286
|
+
* @param {string|null} repository - Repository type ('pypi', 'testpypi', 'azure', or null)
|
|
287
|
+
* @param {string|null} repositoryUrl - Repository URL (for Azure)
|
|
288
|
+
* @returns {Promise<string|null>} Latest version or null if not found/unsupported
|
|
289
|
+
*/
|
|
290
|
+
async function queryRegistryVersion(packageName, repository, repositoryUrl) {
|
|
291
|
+
if (!repository || !packageName) {
|
|
292
|
+
return null;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
try {
|
|
296
|
+
if (repository === 'pypi' || repository === 'testpypi') {
|
|
297
|
+
return await queryPyPIVersion(packageName, repository);
|
|
298
|
+
} else if (repository === 'azure') {
|
|
299
|
+
if (!repositoryUrl) {
|
|
300
|
+
return null;
|
|
301
|
+
}
|
|
302
|
+
return await queryAzureArtifactsVersion(packageName, repositoryUrl);
|
|
303
|
+
}
|
|
304
|
+
} catch (error) {
|
|
305
|
+
// Log error but don't fail - fall back to git tags
|
|
306
|
+
console.error(`Warning: Failed to query ${repository} for latest version: ${error.message}`);
|
|
307
|
+
console.error('Falling back to git tags for version detection.');
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
return null;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
// Main execution
|
|
314
|
+
(async () => {
|
|
315
|
+
try {
|
|
316
|
+
// Try to require semantic-release
|
|
317
|
+
// First try resolving from project root (for devDependencies), then fall back to global
|
|
318
|
+
let semanticRelease;
|
|
180
319
|
try {
|
|
181
320
|
const semanticReleasePath = require.resolve('semantic-release', { paths: [projectRoot] });
|
|
182
321
|
semanticRelease = require(semanticReleasePath);
|
|
@@ -214,11 +353,41 @@ try {
|
|
|
214
353
|
}
|
|
215
354
|
}
|
|
216
355
|
|
|
356
|
+
// Query registry for latest version if repository info is provided
|
|
357
|
+
let registryVersion = null;
|
|
358
|
+
if (repository && packageName) {
|
|
359
|
+
try {
|
|
360
|
+
registryVersion = await queryRegistryVersion(packageName, repository, repositoryUrl);
|
|
361
|
+
if (registryVersion) {
|
|
362
|
+
console.error(`Found latest version on ${repository}: ${registryVersion}`);
|
|
363
|
+
} else {
|
|
364
|
+
console.error(`Package not found on ${repository} or query failed, using git tags as fallback`);
|
|
365
|
+
}
|
|
366
|
+
} catch (error) {
|
|
367
|
+
console.error(`Warning: Registry query failed: ${error.message}`);
|
|
368
|
+
console.error('Falling back to git tags for version detection.');
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
|
|
217
372
|
// Configure semantic-release options
|
|
218
373
|
const options = {
|
|
219
374
|
dryRun: true,
|
|
220
375
|
ci: false,
|
|
221
376
|
};
|
|
377
|
+
|
|
378
|
+
// If we have a registry version, we can use it to set lastRelease in semantic-release context
|
|
379
|
+
// This ensures semantic-release uses the registry version as baseline instead of git tags
|
|
380
|
+
if (registryVersion) {
|
|
381
|
+
// Set lastRelease in options to use registry version as baseline
|
|
382
|
+
// This tells semantic-release to analyze commits since this version
|
|
383
|
+
options.lastRelease = {
|
|
384
|
+
version: registryVersion,
|
|
385
|
+
gitTag: isSubfolderBuild
|
|
386
|
+
? `${packageName}-v${registryVersion}`
|
|
387
|
+
: `v${registryVersion}`,
|
|
388
|
+
gitHead: null, // We don't know the commit, but semantic-release will find it
|
|
389
|
+
};
|
|
390
|
+
}
|
|
222
391
|
|
|
223
392
|
// For subfolder builds, configure commit filter and per-package tags
|
|
224
393
|
if (isSubfolderBuild) {
|
|
@@ -334,50 +503,51 @@ try {
|
|
|
334
503
|
}
|
|
335
504
|
process.exit(1);
|
|
336
505
|
});
|
|
337
|
-
} catch (error) {
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
506
|
+
} catch (error) {
|
|
507
|
+
// Clean up temporary package.json on error
|
|
508
|
+
if (tempPackageJson && fs.existsSync(tempPackageJson)) {
|
|
509
|
+
const backup = tempPackageJson + '.backup';
|
|
510
|
+
if (backupCreatedByScript && fs.existsSync(backup)) {
|
|
511
|
+
try {
|
|
512
|
+
// Restore original (only if we created the backup)
|
|
513
|
+
fs.copyFileSync(backup, tempPackageJson);
|
|
514
|
+
fs.unlinkSync(backup);
|
|
515
|
+
} catch (e) {
|
|
516
|
+
// Ignore cleanup errors
|
|
517
|
+
}
|
|
518
|
+
} else if (fileCreatedByScript) {
|
|
519
|
+
try {
|
|
520
|
+
// Remove temporary file (only if we created it, not if it existed before)
|
|
521
|
+
fs.unlinkSync(tempPackageJson);
|
|
522
|
+
} catch (e) {
|
|
523
|
+
// Ignore cleanup errors
|
|
524
|
+
}
|
|
525
|
+
} else if (originalPackageJsonContent !== null) {
|
|
526
|
+
// We modified an existing file but didn't create a backup (user's backup exists)
|
|
527
|
+
// Restore from the original content we stored, but don't delete user's backup
|
|
528
|
+
try {
|
|
529
|
+
fs.writeFileSync(tempPackageJson, originalPackageJsonContent, 'utf8');
|
|
530
|
+
} catch (e) {
|
|
531
|
+
// Ignore cleanup errors
|
|
532
|
+
}
|
|
363
533
|
}
|
|
364
534
|
}
|
|
365
|
-
}
|
|
366
535
|
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
536
|
+
// Check if it's a "no release" case (common, not an error)
|
|
537
|
+
if (error.message && (
|
|
538
|
+
error.message.includes('no release') ||
|
|
539
|
+
error.message.includes('No release') ||
|
|
540
|
+
error.code === 'ENOCHANGE'
|
|
541
|
+
)) {
|
|
542
|
+
console.log('none');
|
|
543
|
+
process.exit(0);
|
|
544
|
+
}
|
|
376
545
|
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
546
|
+
// Other errors
|
|
547
|
+
console.error(`Error: ${error.message}`);
|
|
548
|
+
if (error.stack) {
|
|
549
|
+
console.error(error.stack);
|
|
550
|
+
}
|
|
551
|
+
process.exit(1);
|
|
381
552
|
}
|
|
382
|
-
|
|
383
|
-
}
|
|
553
|
+
})();
|
|
@@ -27,6 +27,8 @@ def resolve_version_via_semantic_release(
|
|
|
27
27
|
project_root: Path,
|
|
28
28
|
subfolder_path: Path | None = None,
|
|
29
29
|
package_name: str | None = None,
|
|
30
|
+
repository: str | None = None,
|
|
31
|
+
repository_url: str | None = None,
|
|
30
32
|
) -> str | None:
|
|
31
33
|
"""
|
|
32
34
|
Resolve the next version using semantic-release via Node.js script.
|
|
@@ -35,6 +37,8 @@ def resolve_version_via_semantic_release(
|
|
|
35
37
|
project_root: Root directory of the project
|
|
36
38
|
subfolder_path: Optional path to subfolder (relative to project_root) for Workflow 1
|
|
37
39
|
package_name: Optional package name for subfolder builds
|
|
40
|
+
repository: Optional target repository ('pypi', 'testpypi', or 'azure')
|
|
41
|
+
repository_url: Optional repository URL (required for Azure Artifacts)
|
|
38
42
|
|
|
39
43
|
Returns:
|
|
40
44
|
Version string if a release is determined, None if no release or error
|
|
@@ -98,7 +102,17 @@ def resolve_version_via_semantic_release(
|
|
|
98
102
|
else subfolder_path
|
|
99
103
|
)
|
|
100
104
|
cmd.extend([str(rel_path), package_name])
|
|
101
|
-
|
|
105
|
+
elif package_name:
|
|
106
|
+
# Main package build with package_name (for registry queries)
|
|
107
|
+
# Pass null for subfolder_path, then package_name
|
|
108
|
+
cmd.extend(["", package_name])
|
|
109
|
+
# Workflow 2: main package without package_name (no additional args needed)
|
|
110
|
+
|
|
111
|
+
# Add repository information if provided
|
|
112
|
+
if repository:
|
|
113
|
+
cmd.append(repository)
|
|
114
|
+
if repository_url:
|
|
115
|
+
cmd.append(repository_url)
|
|
102
116
|
|
|
103
117
|
result = subprocess.run(
|
|
104
118
|
cmd,
|
|
@@ -297,6 +311,10 @@ def main() -> int:
|
|
|
297
311
|
# Version is needed for subfolder builds or when publishing main package
|
|
298
312
|
if is_subfolder or args.publish:
|
|
299
313
|
print("No --version provided, attempting to resolve via semantic-release...")
|
|
314
|
+
# Get repository info if publishing
|
|
315
|
+
repository = args.publish if args.publish else None
|
|
316
|
+
repository_url = args.repository_url if args.publish else None
|
|
317
|
+
|
|
300
318
|
if is_subfolder:
|
|
301
319
|
# Workflow 1: subfolder build
|
|
302
320
|
# src_dir is guaranteed to be relative to project_root due to is_subfolder check
|
|
@@ -305,11 +323,34 @@ def main() -> int:
|
|
|
305
323
|
).lower().strip("-")
|
|
306
324
|
subfolder_rel_path = src_dir.relative_to(project_root)
|
|
307
325
|
resolved_version = resolve_version_via_semantic_release(
|
|
308
|
-
project_root,
|
|
326
|
+
project_root,
|
|
327
|
+
subfolder_rel_path,
|
|
328
|
+
package_name,
|
|
329
|
+
repository=repository,
|
|
330
|
+
repository_url=repository_url,
|
|
309
331
|
)
|
|
310
332
|
else:
|
|
311
333
|
# Workflow 2: main package
|
|
312
|
-
|
|
334
|
+
# For main package, we need package_name from pyproject.toml for registry queries
|
|
335
|
+
package_name_for_registry = None
|
|
336
|
+
if repository:
|
|
337
|
+
try:
|
|
338
|
+
import tomllib
|
|
339
|
+
pyproject_path = project_root / "pyproject.toml"
|
|
340
|
+
if pyproject_path.exists():
|
|
341
|
+
with open(pyproject_path, "rb") as f:
|
|
342
|
+
data = tomllib.load(f)
|
|
343
|
+
package_name_for_registry = data.get("project", {}).get("name")
|
|
344
|
+
except Exception:
|
|
345
|
+
pass
|
|
346
|
+
|
|
347
|
+
resolved_version = resolve_version_via_semantic_release(
|
|
348
|
+
project_root,
|
|
349
|
+
subfolder_path=None,
|
|
350
|
+
package_name=package_name_for_registry,
|
|
351
|
+
repository=repository,
|
|
352
|
+
repository_url=repository_url,
|
|
353
|
+
)
|
|
313
354
|
|
|
314
355
|
if resolved_version:
|
|
315
356
|
print(f"Resolved version via semantic-release: {resolved_version}")
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_package_folder-4.0.0 → python_package_folder-4.1.0}/src/python_package_folder/__init__.py
RENAMED
|
File without changes
|
{python_package_folder-4.0.0 → python_package_folder-4.1.0}/src/python_package_folder/__main__.py
RENAMED
|
File without changes
|
|
File without changes
|
{python_package_folder-4.0.0 → python_package_folder-4.1.0}/src/python_package_folder/analyzer.py
RENAMED
|
File without changes
|
{python_package_folder-4.0.0 → python_package_folder-4.1.0}/src/python_package_folder/finder.py
RENAMED
|
File without changes
|
{python_package_folder-4.0.0 → python_package_folder-4.1.0}/src/python_package_folder/manager.py
RENAMED
|
File without changes
|
{python_package_folder-4.0.0 → python_package_folder-4.1.0}/src/python_package_folder/publisher.py
RENAMED
|
File without changes
|
{python_package_folder-4.0.0 → python_package_folder-4.1.0}/src/python_package_folder/py.typed
RENAMED
|
File without changes
|
|
File without changes
|
{python_package_folder-4.0.0 → python_package_folder-4.1.0}/src/python_package_folder/types.py
RENAMED
|
File without changes
|
{python_package_folder-4.0.0 → python_package_folder-4.1.0}/src/python_package_folder/utils.py
RENAMED
|
File without changes
|
{python_package_folder-4.0.0 → python_package_folder-4.1.0}/src/python_package_folder/version.py
RENAMED
|
File without changes
|
|
File without changes
|
{python_package_folder-4.0.0 → python_package_folder-4.1.0}/tests/folder_structure/some_globals.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_package_folder-4.0.0 → python_package_folder-4.1.0}/tests/test_build_with_external_deps.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_package_folder-4.0.0 → python_package_folder-4.1.0}/tests/test_third_party_dependencies.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|