pyASDReader 1.2.2__tar.gz → 1.2.4__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.
- {pyasdreader-1.2.2 → pyasdreader-1.2.4}/.codecov.yml +0 -6
- pyasdreader-1.2.4/.pre-commit-hooks/check_changelog_format.py +148 -0
- pyasdreader-1.2.4/.pre-commit-hooks/check_commit_message.py +127 -0
- pyasdreader-1.2.4/.pre-commit-hooks/check_version_consistency.py +120 -0
- {pyasdreader-1.2.2 → pyasdreader-1.2.4}/CHANGELOG.md +47 -1
- pyasdreader-1.2.4/CONTRIBUTING.md +332 -0
- {pyasdreader-1.2.2 → pyasdreader-1.2.4}/PKG-INFO +117 -213
- {pyasdreader-1.2.2 → pyasdreader-1.2.4}/README.md +84 -183
- pyasdreader-1.2.4/docs/.readthedocs.yaml +28 -0
- pyasdreader-1.2.4/docs/conf.py +33 -0
- pyasdreader-1.2.4/docs/developers/architecture/class_design/Core_Class_Design.vpp +0 -0
- pyasdreader-1.2.4/docs/developers/architecture/class_diagram/Core_Class_Design.pdf +0 -0
- pyasdreader-1.2.4/docs/developers/architecture/class_diagram/Core_Class_Design.svg +1 -0
- pyasdreader-1.2.4/docs/developers/readme.md +155 -0
- pyasdreader-1.2.4/docs/index.md +184 -0
- pyasdreader-1.2.4/docs/maintainers/CI_CD_WORKFLOWS.md +430 -0
- pyasdreader-1.2.4/docs/maintainers/FALLBACK_API_TOKEN_SETUP.md +290 -0
- pyasdreader-1.2.4/docs/maintainers/GITHUB_ENVIRONMENT_SETUP.md +216 -0
- pyasdreader-1.2.4/docs/maintainers/IMPROVEMENTS_2025-10-07.md +248 -0
- pyasdreader-1.2.4/docs/maintainers/RELEASE_EXAMPLES.md +532 -0
- pyasdreader-1.2.4/docs/maintainers/TROUBLESHOOTING_TRUSTED_PUBLISHING.md +497 -0
- pyasdreader-1.2.4/docs/maintainers/TRUSTED_PUBLISHING_IMPLEMENTATION_SUMMARY.md +426 -0
- pyasdreader-1.2.4/docs/maintainers/TRUSTED_PUBLISHING_SETUP.md +551 -0
- pyasdreader-1.2.4/docs/maintainers/VERSION_MANAGEMENT.md +709 -0
- pyasdreader-1.2.4/docs/maintainers/VERSION_ROLLBACK.md +294 -0
- pyasdreader-1.2.4/docs/maintainers/readme.md +34 -0
- {pyasdreader-1.2.2 → pyasdreader-1.2.4/docs/users}/examples/README.md +5 -5
- {pyasdreader-1.2.2 → pyasdreader-1.2.4/docs/users}/examples/basic_usage.py +9 -6
- pyasdreader-1.2.4/docs/users/readme.md +144 -0
- pyasdreader-1.2.4/pyASDReader.egg-info/SOURCES.txt +64 -0
- {pyasdreader-1.2.2 → pyasdreader-1.2.4}/pyproject.toml +150 -144
- pyasdreader-1.2.4/scripts/diagnose_trusted_publishing.sh +212 -0
- {pyasdreader-1.2.2 → pyasdreader-1.2.4}/scripts/publish.sh +74 -7
- pyasdreader-1.2.4/scripts/release.sh +615 -0
- pyasdreader-1.2.4/scripts/test_environment_setup.sh +166 -0
- pyasdreader-1.2.4/scripts/test_release.sh +303 -0
- pyasdreader-1.2.4/scripts/utils/__init__.py +1 -0
- pyasdreader-1.2.4/scripts/utils/changelog_manager.py +276 -0
- pyasdreader-1.2.4/scripts/validate_release.py +584 -0
- pyasdreader-1.2.4/src/_version.py +15 -0
- {pyasdreader-1.2.2 → pyasdreader-1.2.4}/src/asd_file_reader.py +1 -1
- pyasdreader-1.2.2/.pre-commit-config.yaml +0 -43
- pyasdreader-1.2.2/VERSION_MANAGEMENT.md +0 -303
- pyasdreader-1.2.2/pyASDReader.egg-info/SOURCES.txt +0 -36
- pyasdreader-1.2.2/src/_version.py +0 -12
- {pyasdreader-1.2.2 → pyasdreader-1.2.4}/LICENSE +0 -0
- {pyasdreader-1.2.2 → pyasdreader-1.2.4}/MANIFEST.in +0 -0
- {pyasdreader-1.2.2 → pyasdreader-1.2.4}/coverage.json +0 -0
- {pyasdreader-1.2.2 → pyasdreader-1.2.4}/setup.cfg +0 -0
- {pyasdreader-1.2.2 → pyasdreader-1.2.4}/src/__init__.py +0 -0
- {pyasdreader-1.2.2 → pyasdreader-1.2.4}/src/constant.py +0 -0
- {pyasdreader-1.2.2 → pyasdreader-1.2.4}/src/file_attributes.py +0 -0
- {pyasdreader-1.2.2 → pyasdreader-1.2.4}/src/logger_setup.py +0 -0
- {pyasdreader-1.2.2 → pyasdreader-1.2.4}/tests/__init__.py +0 -0
- {pyasdreader-1.2.2 → pyasdreader-1.2.4}/tests/sample_data/v6sample/v6sample00000.asd +0 -0
- {pyasdreader-1.2.2 → pyasdreader-1.2.4}/tests/sample_data/v6sample/v6sample00001.asd +0 -0
- {pyasdreader-1.2.2 → pyasdreader-1.2.4}/tests/sample_data/v6sample/v6sample00002.asd +0 -0
- {pyasdreader-1.2.2 → pyasdreader-1.2.4}/tests/sample_data/v7sample/v7sample00000.asd +0 -0
- {pyasdreader-1.2.2 → pyasdreader-1.2.4}/tests/sample_data/v7sample/v7sample00001.asd +0 -0
- {pyasdreader-1.2.2 → pyasdreader-1.2.4}/tests/sample_data/v7sample/v7sample00002.asd +0 -0
- {pyasdreader-1.2.2 → pyasdreader-1.2.4}/tests/sample_data/v7sample/v7sample00003.asd +0 -0
- {pyasdreader-1.2.2 → pyasdreader-1.2.4}/tests/sample_data/v7sample/v7sample00004.asd +0 -0
- {pyasdreader-1.2.2 → pyasdreader-1.2.4}/tests/sample_data/v7sample/v7sample00005.asd +0 -0
- {pyasdreader-1.2.2 → pyasdreader-1.2.4}/tests/sample_data/v7sample_field_spectroscopy/44231B009-1-FW300000.asd +0 -0
- {pyasdreader-1.2.2 → pyasdreader-1.2.4}/tests/sample_data/v7sample_field_spectroscopy/44231B009-1-FW3R00000.asd +0 -0
- {pyasdreader-1.2.2 → pyasdreader-1.2.4}/tests/sample_data/v7sample_field_spectroscopy/44231B174-1-FF300000.asd +0 -0
- {pyasdreader-1.2.2 → pyasdreader-1.2.4}/tests/sample_data/v8sample/v8sample00001.asd +0 -0
- {pyasdreader-1.2.2 → pyasdreader-1.2.4}/tests/sample_data/v8sample/v8sample00002.asd +0 -0
- {pyasdreader-1.2.2 → pyasdreader-1.2.4}/tests/test_asd_file_reader.py +0 -0
- {pyasdreader-1.2.2 → pyasdreader-1.2.4}/tests/test_data.py +0 -0
- {pyasdreader-1.2.2 → pyasdreader-1.2.4}/tests/test_file_attributes.py +0 -0
|
@@ -28,8 +28,6 @@ comment:
|
|
|
28
28
|
require_head: true
|
|
29
29
|
|
|
30
30
|
flags:
|
|
31
|
-
ubuntu-latest-py3.8:
|
|
32
|
-
carryforward: false
|
|
33
31
|
ubuntu-latest-py3.9:
|
|
34
32
|
carryforward: false
|
|
35
33
|
ubuntu-latest-py3.10:
|
|
@@ -38,8 +36,6 @@ flags:
|
|
|
38
36
|
carryforward: false
|
|
39
37
|
ubuntu-latest-py3.12:
|
|
40
38
|
carryforward: false
|
|
41
|
-
windows-latest-py3.8:
|
|
42
|
-
carryforward: false
|
|
43
39
|
windows-latest-py3.9:
|
|
44
40
|
carryforward: false
|
|
45
41
|
windows-latest-py3.10:
|
|
@@ -48,8 +44,6 @@ flags:
|
|
|
48
44
|
carryforward: false
|
|
49
45
|
windows-latest-py3.12:
|
|
50
46
|
carryforward: false
|
|
51
|
-
macos-latest-py3.8:
|
|
52
|
-
carryforward: false
|
|
53
47
|
macos-latest-py3.9:
|
|
54
48
|
carryforward: false
|
|
55
49
|
macos-latest-py3.10:
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Pre-commit hook to validate CHANGELOG.md format.
|
|
4
|
+
|
|
5
|
+
Validates that CHANGELOG.md follows the Keep a Changelog format:
|
|
6
|
+
- Has [Unreleased] section
|
|
7
|
+
- Version entries follow format: ## [X.Y.Z] - YYYY-MM-DD
|
|
8
|
+
- Versions are in descending order
|
|
9
|
+
- Uses standard sections (Added, Changed, Fixed, etc.)
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
import re
|
|
13
|
+
import sys
|
|
14
|
+
from datetime import datetime
|
|
15
|
+
from pathlib import Path
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def check_unreleased_section(content):
|
|
19
|
+
"""Check if [Unreleased] section exists."""
|
|
20
|
+
if not re.search(r'##\s+\[Unreleased\]', content):
|
|
21
|
+
return False, "[Unreleased] section is missing"
|
|
22
|
+
return True, None
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def extract_versions(content):
|
|
26
|
+
"""Extract all version entries from CHANGELOG."""
|
|
27
|
+
# Match pattern: ## [X.Y.Z] - YYYY-MM-DD
|
|
28
|
+
pattern = r'##\s+\[(\d+\.\d+\.\d+)\]\s+-\s+(\d{4}-\d{2}-\d{2})'
|
|
29
|
+
matches = re.findall(pattern, content)
|
|
30
|
+
return matches
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def validate_version_format(version):
|
|
34
|
+
"""Validate semantic version format."""
|
|
35
|
+
pattern = r'^\d+\.\d+\.\d+$'
|
|
36
|
+
return re.match(pattern, version) is not None
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def validate_date_format(date_str):
|
|
40
|
+
"""Validate date format (YYYY-MM-DD)."""
|
|
41
|
+
try:
|
|
42
|
+
datetime.strptime(date_str, '%Y-%m-%d')
|
|
43
|
+
return True
|
|
44
|
+
except ValueError:
|
|
45
|
+
return False
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def check_version_order(versions):
|
|
49
|
+
"""Check if versions are in descending order."""
|
|
50
|
+
version_numbers = [tuple(map(int, v[0].split('.'))) for v in versions]
|
|
51
|
+
|
|
52
|
+
for i in range(len(version_numbers) - 1):
|
|
53
|
+
if version_numbers[i] < version_numbers[i + 1]:
|
|
54
|
+
return False, f"Version {versions[i][0]} should come before {versions[i+1][0]}"
|
|
55
|
+
|
|
56
|
+
return True, None
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def check_standard_sections(content):
|
|
60
|
+
"""Check if content uses standard Keep a Changelog sections."""
|
|
61
|
+
standard_sections = [
|
|
62
|
+
'Added', 'Changed', 'Deprecated', 'Removed',
|
|
63
|
+
'Fixed', 'Security', 'Technical Improvements'
|
|
64
|
+
]
|
|
65
|
+
|
|
66
|
+
# Find all section headers (### Section)
|
|
67
|
+
section_pattern = r'###\s+([A-Za-z\s]+)'
|
|
68
|
+
found_sections = re.findall(section_pattern, content)
|
|
69
|
+
|
|
70
|
+
# Check if non-standard sections are used
|
|
71
|
+
warnings = []
|
|
72
|
+
for section in found_sections:
|
|
73
|
+
section = section.strip()
|
|
74
|
+
if section not in standard_sections and section != '':
|
|
75
|
+
warnings.append(f"Non-standard section: '{section}'")
|
|
76
|
+
|
|
77
|
+
return warnings
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def main():
|
|
81
|
+
"""Validate CHANGELOG.md format."""
|
|
82
|
+
project_root = Path(__file__).parent.parent
|
|
83
|
+
changelog_path = project_root / 'CHANGELOG.md'
|
|
84
|
+
|
|
85
|
+
if not changelog_path.exists():
|
|
86
|
+
print("CHANGELOG.md not found")
|
|
87
|
+
return 1
|
|
88
|
+
|
|
89
|
+
with open(changelog_path, 'r', encoding='utf-8') as f:
|
|
90
|
+
content = f.read()
|
|
91
|
+
|
|
92
|
+
errors = []
|
|
93
|
+
warnings = []
|
|
94
|
+
|
|
95
|
+
# Check 1: Unreleased section
|
|
96
|
+
has_unreleased, error = check_unreleased_section(content)
|
|
97
|
+
if not has_unreleased:
|
|
98
|
+
errors.append(error)
|
|
99
|
+
|
|
100
|
+
# Check 2: Extract and validate version entries
|
|
101
|
+
versions = extract_versions(content)
|
|
102
|
+
|
|
103
|
+
if not versions:
|
|
104
|
+
errors.append("No version entries found in CHANGELOG.md")
|
|
105
|
+
else:
|
|
106
|
+
# Check version format
|
|
107
|
+
for version, date in versions:
|
|
108
|
+
if not validate_version_format(version):
|
|
109
|
+
errors.append(f"Invalid version format: {version}")
|
|
110
|
+
if not validate_date_format(date):
|
|
111
|
+
errors.append(f"Invalid date format for version {version}: {date}")
|
|
112
|
+
|
|
113
|
+
# Check version order
|
|
114
|
+
ordered, error = check_version_order(versions)
|
|
115
|
+
if not ordered:
|
|
116
|
+
errors.append(error)
|
|
117
|
+
|
|
118
|
+
# Check 3: Standard sections (warnings only)
|
|
119
|
+
section_warnings = check_standard_sections(content)
|
|
120
|
+
warnings.extend(section_warnings)
|
|
121
|
+
|
|
122
|
+
# Print results
|
|
123
|
+
if errors:
|
|
124
|
+
print("CHANGELOG.md validation failed")
|
|
125
|
+
print("\nErrors:")
|
|
126
|
+
for error in errors:
|
|
127
|
+
print(f" - {error}")
|
|
128
|
+
|
|
129
|
+
if warnings:
|
|
130
|
+
print("\nWarnings:")
|
|
131
|
+
for warning in warnings:
|
|
132
|
+
print(f" - {warning}")
|
|
133
|
+
|
|
134
|
+
print("\nPlease fix the errors and ensure CHANGELOG.md follows Keep a Changelog format.")
|
|
135
|
+
print("See: https://keepachangelog.com/en/1.0.0/")
|
|
136
|
+
return 1
|
|
137
|
+
else:
|
|
138
|
+
if warnings:
|
|
139
|
+
print("CHANGELOG.md validation passed with warnings:")
|
|
140
|
+
for warning in warnings:
|
|
141
|
+
print(f" - {warning}")
|
|
142
|
+
else:
|
|
143
|
+
print("CHANGELOG.md validation passed")
|
|
144
|
+
return 0
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
if __name__ == '__main__':
|
|
148
|
+
sys.exit(main())
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Pre-commit hook to validate commit message format.
|
|
4
|
+
|
|
5
|
+
Validates that commit messages follow Conventional Commits format:
|
|
6
|
+
- type(scope): description
|
|
7
|
+
- Valid types: feat, fix, docs, style, refactor, test, chore, perf, ci, build, revert
|
|
8
|
+
|
|
9
|
+
Reference: https://www.conventionalcommits.org/
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
import re
|
|
13
|
+
import sys
|
|
14
|
+
from pathlib import Path
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
VALID_TYPES = [
|
|
18
|
+
'feat', # New feature
|
|
19
|
+
'fix', # Bug fix
|
|
20
|
+
'docs', # Documentation changes
|
|
21
|
+
'style', # Code style changes (formatting, etc.)
|
|
22
|
+
'refactor', # Code refactoring
|
|
23
|
+
'test', # Adding or updating tests
|
|
24
|
+
'chore', # Maintenance tasks
|
|
25
|
+
'perf', # Performance improvements
|
|
26
|
+
'ci', # CI/CD changes
|
|
27
|
+
'build', # Build system changes
|
|
28
|
+
'revert', # Revert previous commit
|
|
29
|
+
]
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def validate_commit_message(message):
|
|
33
|
+
"""
|
|
34
|
+
Validate commit message format.
|
|
35
|
+
|
|
36
|
+
Expected format:
|
|
37
|
+
- type: description
|
|
38
|
+
- type(scope): description
|
|
39
|
+
|
|
40
|
+
Returns (is_valid, error_message)
|
|
41
|
+
"""
|
|
42
|
+
lines = message.strip().split('\n')
|
|
43
|
+
if not lines:
|
|
44
|
+
return False, "Commit message is empty"
|
|
45
|
+
|
|
46
|
+
first_line = lines[0].strip()
|
|
47
|
+
|
|
48
|
+
# Check for merge commits (allowed)
|
|
49
|
+
if first_line.startswith('Merge '):
|
|
50
|
+
return True, None
|
|
51
|
+
|
|
52
|
+
# Check for revert commits (allowed)
|
|
53
|
+
if first_line.startswith('Revert '):
|
|
54
|
+
return True, None
|
|
55
|
+
|
|
56
|
+
# Pattern 1: type: description
|
|
57
|
+
# Pattern 2: type(scope): description
|
|
58
|
+
pattern = r'^(\w+)(\([a-zA-Z0-9_\-/]+\))?: .{3,}'
|
|
59
|
+
|
|
60
|
+
match = re.match(pattern, first_line)
|
|
61
|
+
if not match:
|
|
62
|
+
return False, (
|
|
63
|
+
f"Invalid commit message format: '{first_line}'\n"
|
|
64
|
+
"Expected format: 'type: description' or 'type(scope): description'\n"
|
|
65
|
+
f"Valid types: {', '.join(VALID_TYPES)}"
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
commit_type = match.group(1)
|
|
69
|
+
if commit_type not in VALID_TYPES:
|
|
70
|
+
return False, (
|
|
71
|
+
f"Invalid commit type: '{commit_type}'\n"
|
|
72
|
+
f"Valid types: {', '.join(VALID_TYPES)}"
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
# Check description length (at least 3 characters)
|
|
76
|
+
description_start = first_line.find(':') + 1
|
|
77
|
+
description = first_line[description_start:].strip()
|
|
78
|
+
if len(description) < 3:
|
|
79
|
+
return False, "Commit description too short (minimum 3 characters)"
|
|
80
|
+
|
|
81
|
+
# Check first line length (recommended: 72 chars, max: 100)
|
|
82
|
+
if len(first_line) > 100:
|
|
83
|
+
return False, f"First line too long ({len(first_line)} chars, max 100)"
|
|
84
|
+
|
|
85
|
+
# Check if description starts with capital letter
|
|
86
|
+
if description and not description[0].isupper():
|
|
87
|
+
return False, "Commit description should start with a capital letter"
|
|
88
|
+
|
|
89
|
+
return True, None
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def main():
|
|
93
|
+
"""Main function to validate commit message."""
|
|
94
|
+
# Get commit message file path from arguments
|
|
95
|
+
if len(sys.argv) < 2:
|
|
96
|
+
print("Usage: check_commit_message.py <commit-msg-file>")
|
|
97
|
+
return 0
|
|
98
|
+
|
|
99
|
+
commit_msg_file = Path(sys.argv[1])
|
|
100
|
+
|
|
101
|
+
if not commit_msg_file.exists():
|
|
102
|
+
print(f"Error: Commit message file not found: {commit_msg_file}")
|
|
103
|
+
return 1
|
|
104
|
+
|
|
105
|
+
# Read commit message
|
|
106
|
+
with open(commit_msg_file, 'r', encoding='utf-8') as f:
|
|
107
|
+
commit_message = f.read()
|
|
108
|
+
|
|
109
|
+
# Validate
|
|
110
|
+
is_valid, error_message = validate_commit_message(commit_message)
|
|
111
|
+
|
|
112
|
+
if not is_valid:
|
|
113
|
+
print("\nCommit message validation failed")
|
|
114
|
+
print(error_message)
|
|
115
|
+
print("\nConventional Commits format:")
|
|
116
|
+
print(" feat: Add new feature")
|
|
117
|
+
print(" fix: Fix bug in parser")
|
|
118
|
+
print(" docs: Update README")
|
|
119
|
+
print(" chore(deps): Update dependencies")
|
|
120
|
+
print("\nSee: https://www.conventionalcommits.org/")
|
|
121
|
+
return 1
|
|
122
|
+
|
|
123
|
+
return 0
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
if __name__ == '__main__':
|
|
127
|
+
sys.exit(main())
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Pre-commit hook to check version consistency across multiple files.
|
|
4
|
+
|
|
5
|
+
Ensures that version numbers in CHANGELOG.md, README.md, pyproject.toml,
|
|
6
|
+
and src/_version.py are consistent.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import re
|
|
10
|
+
import sys
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def extract_changelog_version(changelog_path):
|
|
15
|
+
"""Extract the latest version from CHANGELOG.md."""
|
|
16
|
+
if not changelog_path.exists():
|
|
17
|
+
return None
|
|
18
|
+
|
|
19
|
+
with open(changelog_path, 'r', encoding='utf-8') as f:
|
|
20
|
+
content = f.read()
|
|
21
|
+
|
|
22
|
+
# Match pattern: ## [X.Y.Z] - YYYY-MM-DD
|
|
23
|
+
match = re.search(r'##\s+\[(\d+\.\d+\.\d+)\]\s+-\s+\d{4}-\d{2}-\d{2}', content)
|
|
24
|
+
if match:
|
|
25
|
+
return match.group(1)
|
|
26
|
+
return None
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def extract_readme_version(readme_path):
|
|
30
|
+
"""Extract version from README.md citation section."""
|
|
31
|
+
if not readme_path.exists():
|
|
32
|
+
return None
|
|
33
|
+
|
|
34
|
+
with open(readme_path, 'r', encoding='utf-8') as f:
|
|
35
|
+
content = f.read()
|
|
36
|
+
|
|
37
|
+
# Match pattern: version = {X.Y.Z}
|
|
38
|
+
match = re.search(r'version\s*=\s*\{(\d+\.\d+\.\d+)\}', content)
|
|
39
|
+
if match:
|
|
40
|
+
return match.group(1)
|
|
41
|
+
return None
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def extract_pyproject_version(pyproject_path):
|
|
45
|
+
"""Extract fallback_version from pyproject.toml."""
|
|
46
|
+
if not pyproject_path.exists():
|
|
47
|
+
return None
|
|
48
|
+
|
|
49
|
+
with open(pyproject_path, 'r', encoding='utf-8') as f:
|
|
50
|
+
content = f.read()
|
|
51
|
+
|
|
52
|
+
# Match pattern: fallback_version = "X.Y.Z"
|
|
53
|
+
match = re.search(r'fallback_version\s*=\s*["\'](\d+\.\d+\.\d+)["\']', content)
|
|
54
|
+
if match:
|
|
55
|
+
return match.group(1)
|
|
56
|
+
return None
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def extract_version_py_version(version_py_path):
|
|
60
|
+
"""Extract __version__ from src/_version.py."""
|
|
61
|
+
if not version_py_path.exists():
|
|
62
|
+
return None
|
|
63
|
+
|
|
64
|
+
with open(version_py_path, 'r', encoding='utf-8') as f:
|
|
65
|
+
content = f.read()
|
|
66
|
+
|
|
67
|
+
# Match pattern: __version__ = "X.Y.Z"
|
|
68
|
+
match = re.search(r'__version__\s*=\s*["\'](\d+\.\d+\.\d+)["\']', content)
|
|
69
|
+
if match:
|
|
70
|
+
return match.group(1)
|
|
71
|
+
return None
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def main():
|
|
75
|
+
"""Check version consistency across all files."""
|
|
76
|
+
project_root = Path(__file__).parent.parent
|
|
77
|
+
|
|
78
|
+
files = {
|
|
79
|
+
'CHANGELOG.md': project_root / 'CHANGELOG.md',
|
|
80
|
+
'README.md': project_root / 'README.md',
|
|
81
|
+
'pyproject.toml': project_root / 'pyproject.toml',
|
|
82
|
+
'src/_version.py': project_root / 'src' / '_version.py',
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
# Extract versions
|
|
86
|
+
versions = {
|
|
87
|
+
'CHANGELOG.md': extract_changelog_version(files['CHANGELOG.md']),
|
|
88
|
+
'README.md': extract_readme_version(files['README.md']),
|
|
89
|
+
'pyproject.toml': extract_pyproject_version(files['pyproject.toml']),
|
|
90
|
+
'src/_version.py': extract_version_py_version(files['src/_version.py']),
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
# Filter out None values
|
|
94
|
+
valid_versions = {k: v for k, v in versions.items() if v is not None}
|
|
95
|
+
|
|
96
|
+
if not valid_versions:
|
|
97
|
+
print("Warning: Could not extract version from any file")
|
|
98
|
+
return 0
|
|
99
|
+
|
|
100
|
+
# Check consistency
|
|
101
|
+
unique_versions = set(valid_versions.values())
|
|
102
|
+
|
|
103
|
+
if len(unique_versions) == 1:
|
|
104
|
+
print(f"✓ Version consistency check passed: {unique_versions.pop()}")
|
|
105
|
+
return 0
|
|
106
|
+
else:
|
|
107
|
+
print("✗ Version inconsistency detected!")
|
|
108
|
+
print("\nVersions found:")
|
|
109
|
+
for file, version in sorted(valid_versions.items()):
|
|
110
|
+
print(f" {file:20} -> {version}")
|
|
111
|
+
|
|
112
|
+
print("\nPlease ensure all version numbers are consistent before committing.")
|
|
113
|
+
print("Expected version (from CHANGELOG.md): {}".format(
|
|
114
|
+
versions.get('CHANGELOG.md', 'NOT FOUND')
|
|
115
|
+
))
|
|
116
|
+
return 1
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
if __name__ == '__main__':
|
|
120
|
+
sys.exit(main())
|
|
@@ -13,6 +13,53 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
|
|
|
13
13
|
|
|
14
14
|
## [Unreleased]
|
|
15
15
|
|
|
16
|
+
## [1.2.4] - 2026-09-18
|
|
17
|
+
|
|
18
|
+
### Added
|
|
19
|
+
- 📚 **Documentation Improvements** - Expanded user and maintainer documentation, including Sphinx/Read the Docs configuration and corrected documentation links.
|
|
20
|
+
|
|
21
|
+
### Changed
|
|
22
|
+
- 🐍 **Python Version Support** - Updated the minimum supported Python version to 3.9 and synchronized the requirement across documentation and CI workflows.
|
|
23
|
+
|
|
24
|
+
### Fixed
|
|
25
|
+
- 🚀 **Release Workflow** - Corrected PyPI and TestPyPI publishing workflow configuration and standardized installation verification messages.
|
|
26
|
+
|
|
27
|
+
## [1.2.3] - 2025-10-07
|
|
28
|
+
|
|
29
|
+
### Added
|
|
30
|
+
- 📚 **Documentation Section** - Added comprehensive documentation links in README.md
|
|
31
|
+
- Link to CHANGELOG.md for version history and feature updates
|
|
32
|
+
- Link to docs/maintainers/VERSION_MANAGEMENT.md for release workflow and branch strategy
|
|
33
|
+
- Links to GitHub Issues and Discussions for community engagement
|
|
34
|
+
|
|
35
|
+
### Changed
|
|
36
|
+
- 🏗️ **Branch Strategy Documentation** - Major update to VERSION_MANAGEMENT.md
|
|
37
|
+
- Added detailed branch strategy section explaining dev/main workflow
|
|
38
|
+
- Updated release workflow with 4-step process including branch merge requirement
|
|
39
|
+
- Enhanced publishing scenarios (normal release, hotfix, quick release)
|
|
40
|
+
- Expanded best practices from 8 to 10 comprehensive guidelines
|
|
41
|
+
- Updated quick reference with branch-specific commands
|
|
42
|
+
|
|
43
|
+
- 🔄 **Enhanced CI/CD Workflows** - Intelligent optimization for GitHub Actions
|
|
44
|
+
- Added TestPyPI test reuse mechanism in PyPI workflow (saves approximately 45 minutes per release)
|
|
45
|
+
- Implemented 7-day test validity window for TestPyPI results
|
|
46
|
+
- Added verification summary job with detailed test status reporting
|
|
47
|
+
- Optimized GitHub Actions minutes usage (approximately 40% savings)
|
|
48
|
+
- Enhanced workflow comments and documentation
|
|
49
|
+
|
|
50
|
+
### Fixed
|
|
51
|
+
- 🐛 **Python 3.8 Compatibility** - Resolved f-string syntax issues in CI workflows
|
|
52
|
+
- Fixed package metadata verification step in publish-to-pypi.yml
|
|
53
|
+
- Fixed package metadata verification step in publish-to-testpypi.yml
|
|
54
|
+
- Resolved f-string backslash syntax error (not supported in Python 3.8)
|
|
55
|
+
- Ensured compatibility across all supported Python versions (3.8-3.12)
|
|
56
|
+
|
|
57
|
+
### Technical Improvements
|
|
58
|
+
- Better CI/CD resource optimization with intelligent test reuse
|
|
59
|
+
- Clearer release workflow documentation for contributors
|
|
60
|
+
- Improved branch management guidelines
|
|
61
|
+
- Enhanced Python version compatibility in automated workflows
|
|
62
|
+
|
|
16
63
|
## [1.2.2] - 2025-10-05
|
|
17
64
|
|
|
18
65
|
### Added
|
|
@@ -192,7 +239,6 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
|
|
|
192
239
|
- ✨ Automatic version management using setuptools-scm
|
|
193
240
|
- 🤖 Dual GitHub Actions workflows: dev branch → TestPyPI, tag → PyPI
|
|
194
241
|
- 📚 Comprehensive documentation for version management and publishing
|
|
195
|
-
- 📝 CLAUDE.md - Guide for Claude Code integration
|
|
196
242
|
- 📝 VERSION_MANAGEMENT.md - Complete guide for version management with Git tags and setuptools_scm
|
|
197
243
|
- 📝 SECURITY.md - Security policy and vulnerability reporting guidelines
|
|
198
244
|
- 📝 PROJECT_IMPROVEMENT_ANALYSIS.md - Detailed analysis of 26 issues
|