python-package-folder 4.1.3__py3-none-any.whl → 4.3.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -18,13 +18,29 @@ class CustomBuildHook(BuildHookInterface):
18
18
 
19
19
  def initialize(self, version: str, build_data: dict[str, Any]) -> None:
20
20
  """Initialize the build hook and add scripts directory files."""
21
- # Get the source directory for the package
22
- source_dir = Path(self.root) / "src" / "python_package_folder"
23
- scripts_dir = source_dir / "scripts"
24
-
25
21
  # Debug: Print to stderr so it shows in build output
26
22
  print(f"[DEBUG] Build hook called. Root: {self.root}", file=sys.stderr)
27
- print(f"[DEBUG] Scripts dir exists: {scripts_dir.exists()}", file=sys.stderr)
23
+
24
+ # Try multiple possible locations for the scripts directory
25
+ # 1. Source layout: src/python_package_folder/scripts
26
+ # 2. Sdist layout: python_package_folder/scripts (after extraction)
27
+ # 3. Alternative sdist layout: scripts/ (if extracted differently)
28
+ possible_scripts_dirs = [
29
+ Path(self.root) / "src" / "python_package_folder" / "scripts",
30
+ Path(self.root) / "python_package_folder" / "scripts",
31
+ Path(self.root) / "scripts",
32
+ ]
33
+
34
+ scripts_dir = None
35
+ for possible_dir in possible_scripts_dirs:
36
+ if possible_dir.exists() and possible_dir.is_dir():
37
+ scripts_dir = possible_dir
38
+ print(f"[DEBUG] Found scripts dir at: {scripts_dir}", file=sys.stderr)
39
+ break
40
+
41
+ if scripts_dir is None:
42
+ print(f"[DEBUG] Scripts directory not found. Tried: {[str(d) for d in possible_scripts_dirs]}", file=sys.stderr)
43
+ return
28
44
 
29
45
  # If scripts directory exists, include all files from it
30
46
  if scripts_dir.exists() and scripts_dir.is_dir():
@@ -32,9 +48,25 @@ class CustomBuildHook(BuildHookInterface):
32
48
  # This ensures they're included in the wheel at the correct location
33
49
  for script_file in scripts_dir.iterdir():
34
50
  if script_file.is_file():
35
- # Calculate relative paths
36
- source_path = script_file.relative_to(self.root)
37
- # Target path inside the wheel package
51
+ # Calculate relative paths from project root
52
+ try:
53
+ source_path = script_file.relative_to(self.root)
54
+ except ValueError:
55
+ # If relative_to fails, try to construct path manually
56
+ # This can happen with sdist layouts
57
+ if "python_package_folder" in str(script_file):
58
+ # Extract the part after python_package_folder
59
+ parts = script_file.parts
60
+ try:
61
+ idx = parts.index("python_package_folder")
62
+ source_path = Path(*parts[idx:])
63
+ except (ValueError, IndexError):
64
+ # Fallback: use the filename
65
+ source_path = Path("python_package_folder") / "scripts" / script_file.name
66
+ else:
67
+ source_path = Path("python_package_folder") / "scripts" / script_file.name
68
+
69
+ # Target path inside the wheel package (always the same)
38
70
  target_path = f"python_package_folder/scripts/{script_file.name}"
39
71
 
40
72
  print(f"[DEBUG] Adding {source_path} -> {target_path}", file=sys.stderr)
@@ -46,8 +78,6 @@ class CustomBuildHook(BuildHookInterface):
46
78
  build_data["force_include"][str(source_path)] = target_path
47
79
 
48
80
  print(f"[DEBUG] force_include now has {len(build_data.get('force_include', {}))} entries", file=sys.stderr)
49
- else:
50
- print(f"[DEBUG] Scripts directory not found at {scripts_dir}", file=sys.stderr)
51
81
 
52
82
 
53
83
  # Export the hook class (hatchling might need this)
@@ -0,0 +1,553 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Get next version using semantic-release.
4
+ *
5
+ * This script runs semantic-release in dry-run mode to determine the next version
6
+ * for a package. It supports both subfolder builds (per-package tags) and main
7
+ * package builds (repo-level tags).
8
+ *
9
+ * Usage:
10
+ * node scripts/get-next-version.cjs <project_root> [subfolder_path] [package_name] [repository] [repository_url]
11
+ *
12
+ * Args:
13
+ * - project_root: Root directory of the project (absolute or relative path)
14
+ * - subfolder_path: Optional. Path to subfolder relative to project_root (for Workflow 1)
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)
18
+ *
19
+ * Output:
20
+ * - Version string (e.g., "1.2.3") if a release is determined
21
+ * - "none" if semantic-release determines no release is needed
22
+ * - Exits with non-zero code on error
23
+ */
24
+
25
+ const path = require('path');
26
+ const fs = require('fs');
27
+ const https = require('https');
28
+ const http = require('http');
29
+
30
+ // Parse command line arguments
31
+ const args = process.argv.slice(2);
32
+ if (args.length < 1) {
33
+ console.error('Error: project_root is required');
34
+ console.error('Usage: node get-next-version.cjs <project_root> [subfolder_path] [package_name] [repository] [repository_url]');
35
+ process.exit(1);
36
+ }
37
+
38
+ const projectRoot = path.resolve(args[0]);
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;
43
+
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]');
50
+ process.exit(1);
51
+ }
52
+ // Note: package_name can be provided without subfolder_path for main package registry queries
53
+
54
+ // Check if project root exists
55
+ if (!fs.existsSync(projectRoot)) {
56
+ console.error(`Error: Project root does not exist: ${projectRoot}`);
57
+ process.exit(1);
58
+ }
59
+
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
63
+ const isSubfolderBuild = subfolderPath !== null && packageName !== null;
64
+ const workingDir = isSubfolderBuild
65
+ ? path.resolve(projectRoot, subfolderPath)
66
+ : projectRoot;
67
+
68
+ // Check if working directory exists
69
+ if (!fs.existsSync(workingDir)) {
70
+ console.error(`Error: Working directory does not exist: ${workingDir}`);
71
+ process.exit(1);
72
+ }
73
+
74
+ // For subfolder builds, ensure package.json exists with correct name
75
+ let tempPackageJson = null;
76
+ let backupCreatedByScript = false;
77
+ let fileCreatedByScript = false;
78
+ let originalPackageJsonContent = null; // Track original content for restoration
79
+ if (isSubfolderBuild) {
80
+ const packageJsonPath = path.join(workingDir, 'package.json');
81
+ const hadPackageJson = fs.existsSync(packageJsonPath);
82
+
83
+ if (!hadPackageJson) {
84
+ // Create temporary package.json for semantic-release-commit-filter
85
+ const packageJsonContent = JSON.stringify({
86
+ name: packageName,
87
+ version: '0.0.0'
88
+ }, null, 2);
89
+ fs.writeFileSync(packageJsonPath, packageJsonContent, 'utf8');
90
+ tempPackageJson = packageJsonPath;
91
+ fileCreatedByScript = true;
92
+ } else {
93
+ // Read existing package.json and ensure name matches
94
+ try {
95
+ const existing = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
96
+ const backup = packageJsonPath + '.backup';
97
+ const backupExists = fs.existsSync(backup);
98
+
99
+ // Store original content before any modifications
100
+ originalPackageJsonContent = fs.readFileSync(packageJsonPath, 'utf8');
101
+
102
+ if (existing.name !== packageName) {
103
+ // Need to modify the name
104
+ // Check if backup is stale (from a previous crashed run)
105
+ // A backup is stale if it contains the same name we're trying to set
106
+ let isStaleBackup = false;
107
+ if (backupExists) {
108
+ try {
109
+ const backupContent = JSON.parse(fs.readFileSync(backup, 'utf8'));
110
+ // If backup has the name we're trying to set, it's stale from a previous run
111
+ if (backupContent.name === packageName) {
112
+ isStaleBackup = true;
113
+ }
114
+ } catch (e) {
115
+ // If we can't read the backup, treat it as potentially stale
116
+ isStaleBackup = true;
117
+ }
118
+ }
119
+
120
+ // If backup is stale, restore from it first, then create a fresh backup
121
+ if (isStaleBackup) {
122
+ try {
123
+ fs.copyFileSync(backup, packageJsonPath);
124
+ // Re-read after restoration and update original content
125
+ originalPackageJsonContent = fs.readFileSync(packageJsonPath, 'utf8');
126
+ const restored = JSON.parse(originalPackageJsonContent);
127
+ // Now create a fresh backup of the restored original
128
+ fs.copyFileSync(packageJsonPath, backup);
129
+ backupCreatedByScript = true;
130
+ // Update the restored content with the new name
131
+ restored.name = packageName;
132
+ fs.writeFileSync(packageJsonPath, JSON.stringify(restored, null, 2), 'utf8');
133
+ } catch (e) {
134
+ // If restoration fails, create a new backup of current state
135
+ fs.copyFileSync(packageJsonPath, backup);
136
+ backupCreatedByScript = true;
137
+ existing.name = packageName;
138
+ fs.writeFileSync(packageJsonPath, JSON.stringify(existing, null, 2), 'utf8');
139
+ }
140
+ } else {
141
+ // Backup doesn't exist or is valid (preserves user's original)
142
+ // If backup exists, it's user's backup - we'll restore from originalPackageJsonContent
143
+ // If backup doesn't exist, create one
144
+ if (!backupExists) {
145
+ fs.copyFileSync(packageJsonPath, backup);
146
+ backupCreatedByScript = true;
147
+ }
148
+ // Modify the file
149
+ existing.name = packageName;
150
+ fs.writeFileSync(packageJsonPath, JSON.stringify(existing, null, 2), 'utf8');
151
+ }
152
+ tempPackageJson = packageJsonPath;
153
+ } else if (backupExists) {
154
+ // Name already matches, but check if backup is stale
155
+ // If backup has the same name, it's from a previous crashed run
156
+ try {
157
+ const backupContent = JSON.parse(fs.readFileSync(backup, 'utf8'));
158
+ if (backupContent.name === packageName) {
159
+ // Stale backup from previous run - restore it
160
+ fs.copyFileSync(backup, packageJsonPath);
161
+ // Update original content after restoration
162
+ originalPackageJsonContent = fs.readFileSync(packageJsonPath, 'utf8');
163
+ // Remove stale backup since we've restored
164
+ fs.unlinkSync(backup);
165
+ // Re-check if we need to modify after restoration
166
+ const restored = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
167
+ if (restored.name !== packageName) {
168
+ // After restoration, name doesn't match - need to modify
169
+ fs.copyFileSync(packageJsonPath, backup);
170
+ backupCreatedByScript = true;
171
+ restored.name = packageName;
172
+ fs.writeFileSync(packageJsonPath, JSON.stringify(restored, null, 2), 'utf8');
173
+ tempPackageJson = packageJsonPath;
174
+ }
175
+ }
176
+ } catch (e) {
177
+ // If we can't read backup, leave it as-is (might be user's backup)
178
+ }
179
+ }
180
+ } catch (e) {
181
+ console.error(`Error reading package.json: ${e.message}`);
182
+ process.exit(1);
183
+ }
184
+ }
185
+ }
186
+
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;
319
+ try {
320
+ const semanticReleasePath = require.resolve('semantic-release', { paths: [projectRoot] });
321
+ semanticRelease = require(semanticReleasePath);
322
+ } catch (resolveError) {
323
+ try {
324
+ semanticRelease = require('semantic-release');
325
+ } catch (e) {
326
+ console.error('Error: semantic-release is not installed.');
327
+ console.error('Please install it with: npm install -g semantic-release');
328
+ console.error('Or install it as a devDependency: npm install --save-dev semantic-release');
329
+ if (isSubfolderBuild) {
330
+ console.error('For subfolder builds, also install: npm install -g semantic-release-commit-filter');
331
+ console.error('Or as devDependency: npm install --save-dev semantic-release-commit-filter');
332
+ }
333
+ process.exit(1);
334
+ }
335
+ }
336
+
337
+ // For subfolder builds, require semantic-release-commit-filter
338
+ // (required only to verify it's installed; the plugin is used via options.plugins)
339
+ // First try resolving from project root (for devDependencies), then fall back to global
340
+ if (isSubfolderBuild) {
341
+ try {
342
+ const commitFilterPath = require.resolve('semantic-release-commit-filter', { paths: [projectRoot] });
343
+ require(commitFilterPath);
344
+ } catch (resolveError) {
345
+ try {
346
+ require('semantic-release-commit-filter');
347
+ } catch (e) {
348
+ console.error('Error: semantic-release-commit-filter is not installed.');
349
+ console.error('Please install it with: npm install -g semantic-release-commit-filter');
350
+ console.error('Or install it as a devDependency: npm install --save-dev semantic-release-commit-filter');
351
+ process.exit(1);
352
+ }
353
+ }
354
+ }
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
+
372
+ // Configure semantic-release options
373
+ const options = {
374
+ dryRun: true,
375
+ ci: false,
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
+ }
391
+
392
+ // For subfolder builds, configure commit filter and per-package tags
393
+ if (isSubfolderBuild) {
394
+ // Get relative path from project root to subfolder for commit filtering
395
+ const relPath = path.relative(projectRoot, workingDir).replace(/\\/g, '/');
396
+
397
+ options.plugins = [
398
+ ['@semantic-release/commit-analyzer', {
399
+ preset: 'angular',
400
+ }],
401
+ ['semantic-release-commit-filter', {
402
+ cwd: workingDir,
403
+ path: relPath,
404
+ }],
405
+ ['@semantic-release/release-notes-generator', {
406
+ preset: 'angular',
407
+ }],
408
+ ];
409
+
410
+ // Use per-package tag format: {package-name}-v{version}
411
+ options.tagFormat = `${packageName}-v\${version}`;
412
+ } else {
413
+ // Main package: use default tag format v{version}
414
+ options.plugins = [
415
+ ['@semantic-release/commit-analyzer', {
416
+ preset: 'angular',
417
+ }],
418
+ ['@semantic-release/release-notes-generator', {
419
+ preset: 'angular',
420
+ }],
421
+ ];
422
+ }
423
+
424
+ // Run semantic-release (returns a promise)
425
+ semanticRelease(options, {
426
+ cwd: workingDir,
427
+ env: {
428
+ ...process.env,
429
+ // Ensure git commands run from project root for subfolder builds
430
+ GIT_DIR: path.join(projectRoot, '.git'),
431
+ GIT_WORK_TREE: projectRoot,
432
+ },
433
+ }).then((result) => {
434
+ // Clean up temporary package.json if we created or modified it
435
+ if (tempPackageJson && fs.existsSync(tempPackageJson)) {
436
+ const backup = tempPackageJson + '.backup';
437
+ if (backupCreatedByScript && fs.existsSync(backup)) {
438
+ // Restore original (only if we created the backup)
439
+ fs.copyFileSync(backup, tempPackageJson);
440
+ fs.unlinkSync(backup);
441
+ } else if (fileCreatedByScript) {
442
+ // Remove temporary file (only if we created it, not if it existed before)
443
+ fs.unlinkSync(tempPackageJson);
444
+ } else if (originalPackageJsonContent !== null) {
445
+ // We modified an existing file but didn't create a backup (user's backup exists)
446
+ // Restore from the original content we stored, but don't delete user's backup
447
+ fs.writeFileSync(tempPackageJson, originalPackageJsonContent, 'utf8');
448
+ }
449
+ }
450
+
451
+ // Output result
452
+ if (result && result.nextRelease && result.nextRelease.version) {
453
+ console.log(result.nextRelease.version);
454
+ process.exit(0);
455
+ } else {
456
+ console.log('none');
457
+ process.exit(0);
458
+ }
459
+ }).catch((error) => {
460
+ // Clean up temporary package.json on error
461
+ if (tempPackageJson && fs.existsSync(tempPackageJson)) {
462
+ const backup = tempPackageJson + '.backup';
463
+ if (backupCreatedByScript && fs.existsSync(backup)) {
464
+ try {
465
+ // Restore original (only if we created the backup)
466
+ fs.copyFileSync(backup, tempPackageJson);
467
+ fs.unlinkSync(backup);
468
+ } catch (e) {
469
+ // Ignore cleanup errors
470
+ }
471
+ } else if (fileCreatedByScript) {
472
+ try {
473
+ // Remove temporary file (only if we created it, not if it existed before)
474
+ fs.unlinkSync(tempPackageJson);
475
+ } catch (e) {
476
+ // Ignore cleanup errors
477
+ }
478
+ } else if (originalPackageJsonContent !== null) {
479
+ // We modified an existing file but didn't create a backup (user's backup exists)
480
+ // Restore from the original content we stored, but don't delete user's backup
481
+ try {
482
+ fs.writeFileSync(tempPackageJson, originalPackageJsonContent, 'utf8');
483
+ } catch (e) {
484
+ // Ignore cleanup errors
485
+ }
486
+ }
487
+ }
488
+
489
+ // Check if it's a "no release" case (common, not an error)
490
+ if (error.message && (
491
+ error.message.includes('no release') ||
492
+ error.message.includes('No release') ||
493
+ error.code === 'ENOCHANGE'
494
+ )) {
495
+ console.log('none');
496
+ process.exit(0);
497
+ }
498
+
499
+ // Other errors
500
+ console.error(`Error running semantic-release: ${error.message}`);
501
+ if (error.stack) {
502
+ console.error(error.stack);
503
+ }
504
+ process.exit(1);
505
+ });
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
+ }
533
+ }
534
+ }
535
+
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
+ }
545
+
546
+ // Other errors
547
+ console.error(`Error: ${error.message}`);
548
+ if (error.stack) {
549
+ console.error(error.stack);
550
+ }
551
+ process.exit(1);
552
+ }
553
+ })();
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-package-folder
3
- Version: 4.1.3
3
+ Version: 4.3.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>
@@ -1,6 +1,6 @@
1
1
  python_package_folder/__init__.py,sha256=DQt-uldOEKfh0MUqCvKdeNKOnpuOvpb7blYvXMyO9Wc,719
2
2
  python_package_folder/__main__.py,sha256=a-__-VLhYw-J7S7CsHdhtEvQr3RiAZxiYDvKhKTgMX4,291
3
- python_package_folder/_hatch_build.py,sha256=03UCz4RSGaLFPpksgZultQ2lskfgMCI-t9gg6rXKj_A,2500
3
+ python_package_folder/_hatch_build.py,sha256=Y50LAM-Jn6f_2M48UZRu-ZIWMY0_nsV8XQyqlRUKoqY,4146
4
4
  python_package_folder/analyzer.py,sha256=cmTNUDCWBIh3XZ_mShlQVG1P9NN_oe3FUBTirVtYfTQ,16709
5
5
  python_package_folder/finder.py,sha256=RPidZ7LKCFuQ_KgCFIZdHWPXsZIDor3M4C0hKeYW7EI,11799
6
6
  python_package_folder/manager.py,sha256=Z9RPg0ZQ7jZhmEXfCzX9OrD_oiA5p2Pnm5Y9tgW3ObQ,55970
@@ -11,8 +11,9 @@ python_package_folder/subfolder_build.py,sha256=oH_KKLJIMByUZCl8y3AyohUO6Om0OvsI
11
11
  python_package_folder/types.py,sha256=3yeSRR5p_3PDKEAaehW_RJ7NwJHexOIeA08bGaT1iSY,2368
12
12
  python_package_folder/utils.py,sha256=lIkWsFKeAYAJ9TDUM99T4pUBHJVbUvCdUgkWQN-LUho,3111
13
13
  python_package_folder/version.py,sha256=kIDP6S9trEfs9gj7lBYGxrWm4RPssRla24UtlO9Jkh4,9111
14
- python_package_folder-4.1.3.dist-info/METADATA,sha256=5B6fZHMnTazvMxVnV6O10Mr6IU_6MY7nSrqJmqj5yCs,37517
15
- python_package_folder-4.1.3.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
16
- python_package_folder-4.1.3.dist-info/entry_points.txt,sha256=ttu4wAhoYSHGhWQNercLz9IVTTpXxhVlRA9vSTvaLe0,91
17
- python_package_folder-4.1.3.dist-info/licenses/LICENSE,sha256=vNgRJh8YiecqZoZld7TtwPI5I72HIymKD9g32fiJjCE,1073
18
- python_package_folder-4.1.3.dist-info/RECORD,,
14
+ python_package_folder/scripts/get-next-version.cjs,sha256=5gImk8Yyg0_TZ9ZtL6hXx8lSRT4Dn_sdkDl6mynlqTs,21535
15
+ python_package_folder-4.3.0.dist-info/METADATA,sha256=5SxWRMB_PR-_s3P9JaoIQmwE9Eg5srFAEwoesDziop0,37517
16
+ python_package_folder-4.3.0.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
17
+ python_package_folder-4.3.0.dist-info/entry_points.txt,sha256=ttu4wAhoYSHGhWQNercLz9IVTTpXxhVlRA9vSTvaLe0,91
18
+ python_package_folder-4.3.0.dist-info/licenses/LICENSE,sha256=vNgRJh8YiecqZoZld7TtwPI5I72HIymKD9g32fiJjCE,1073
19
+ python_package_folder-4.3.0.dist-info/RECORD,,