From c3a025cdf6e98d53b691bb542fda66b91be992e3 Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Sat, 14 Jun 2025 10:55:28 +0200 Subject: [PATCH 1/9] Move all release notes to CHANGELOG.md --- CHANGELOG.md | 72 ++++++++++++++++++++++++++++ docs/SUMMARY.md | 2 +- docs/release-notes/index.md | 7 --- docs/release-notes/v2.0.0.md | 93 ------------------------------------ docs/release-notes/v2.0.1.md | 12 ----- docs/release-notes/v2.1.0.md | 31 ------------ docs/release-notes/v2.1.1.md | 11 ----- 7 files changed, 73 insertions(+), 155 deletions(-) create mode 100644 CHANGELOG.md delete mode 100644 docs/release-notes/index.md delete mode 100644 docs/release-notes/v2.0.0.md delete mode 100644 docs/release-notes/v2.0.1.md delete mode 100644 docs/release-notes/v2.1.0.md delete mode 100644 docs/release-notes/v2.1.1.md diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 000000000..22acad1cd --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,72 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [Unreleased] + +## [2.1.1] - 2025-05-08 + +### Fixed +- Fixed bug in the `_ElementResults.constraints` not returning the constraints but rather the variables + +### Changed +- Improved docstring and tests + +## [2.1.0] - 2025-04-11 + +### Added +- Python 3.13 support added +- Logger warning if relative_minimum is used without on_off_parameters in Flow +- Greatly improved internal testing infrastructure by leveraging linopy's testing framework + +### Fixed +- Fixed the lower bound of `flow_rate` when using optional investments without OnOffParameters +- Fixed bug that prevented divest effects from working +- Added lower bounds of 0 to two unbounded vars (numerical improvement) + +### Changed +- **BREAKING**: Restructured the modeling of the On/Off state of Flows or Components + - Variable renaming: `...|consecutive_on_hours` → `...|ConsecutiveOn|hours` + - Variable renaming: `...|consecutive_off_hours` → `...|ConsecutiveOff|hours` + - Constraint renaming: `...|consecutive_on_hours_con1` → `...|ConsecutiveOn|con1` + - Similar pattern for all consecutive on/off constraints + +## [2.0.1] - 2025-04-10 + +### Added +- Logger warning if relative_minimum is used without on_off_parameters in Flow + +### Fixed +- Replace "|" with "__" in filenames when saving figures (Windows compatibility) +- Fixed bug that prevented the load factor from working without InvestmentParameters + +## [2.0.0] - 2025-03-29 + +### Changed +- **BREAKING**: Complete migration from Pyomo to Linopy optimization framework +- **BREAKING**: Redesigned data handling to rely on xarray.Dataset throughout the package +- **BREAKING**: Framework renamed from flixOpt to flixopt (`import flixopt as fx`) +- **BREAKING**: Results handling completely redesigned with new `CalculationResults` class + +### Added +- Full model serialization support - save and restore unsolved Models +- Enhanced model documentation with YAML export containing human-readable mathematical formulations +- Extend flixopt models with native linopy language support +- Full Model Export/Import capabilities via linopy.Model +- Unified solution exploration through `Calculation.results` attribute +- Compression support for result files +- `to_netcdf/from_netcdf` methods for FlowSystem and core components +- xarray integration for TimeSeries with improved datatypes support +- Google Style Docstrings throughout the codebase + +### Fixed +- Improved infeasible model detection and reporting +- Enhanced time series management and serialization +- Reduced file size through improved compression + +### Removed +- **BREAKING**: Pyomo dependency (replaced by linopy) +- Period concepts in time management (simplified to timesteps) \ No newline at end of file diff --git a/docs/SUMMARY.md b/docs/SUMMARY.md index 7afca119d..866c6018c 100644 --- a/docs/SUMMARY.md +++ b/docs/SUMMARY.md @@ -4,4 +4,4 @@ - [Examples](examples/) - [FAQ](faq/) - [API-Reference](api-reference/) -- [Release Notes](release-notes/) \ No newline at end of file +- [Changelog](changelog.md) \ No newline at end of file diff --git a/docs/release-notes/index.md b/docs/release-notes/index.md deleted file mode 100644 index fecb3d61b..000000000 --- a/docs/release-notes/index.md +++ /dev/null @@ -1,7 +0,0 @@ -# Release Notes - -This page provides links to release notes for all versions of flixopt. - -## Latest Release - -* [v2.0.0](v2.0.0.md) - 29.03.2025 - Migration from pyomo to linopy. Alround improvements in performance and usability diff --git a/docs/release-notes/v2.0.0.md b/docs/release-notes/v2.0.0.md deleted file mode 100644 index 008d1360d..000000000 --- a/docs/release-notes/v2.0.0.md +++ /dev/null @@ -1,93 +0,0 @@ -# Release v2.0.0 - -**Release Date:** March 29, 2025 - -## 🚀 Major Framework Changes - -- **Migration from Pyomo to Linopy**: Completely rebuilt the optimization foundation to use Linopy instead of Pyomo - - Significant performance improvements, especially for large models - - Internal useage of linopys mathematical modeling language -- **xarray-Based Data Architecture**: Redesigned data handling to rely on xarray.Dataset throughout the package for: - - Improved solution representation and analysis - - Enhanced time series management - - Consistent serialization across all elements - - Reduced file size and improved performance -- **Saving and restoring unsolved Models**: The FlowSystem is now fully serializable and can be saved to a file. - - Share your work with others by saving the FlowSystem to a file - - Load a FlowSystem from a file to extend or modify your work - -## 🔧 Key Improvements - -### Model Handling - -- **Extend every flixopt model with the linopy language**: Add any additional constraint or variable to your flixopt model by using the linopy language. -- **Full Model Export/Import**: As a result of the migration to Linopy, the linopy.Model can be exported before or after the solve. -- **Improved Infeasible Model Handling**: Added better detection and reporting for infeasible optimization models -- **Improved Model Documentation**: Every model can be documented in a .yaml file, containing human readable mathematical formulations of all variables and constraints. THis is used to document every Calculation. - -### Calculation Results and documentation: -The `CalculationResults` class has been completely redesigned to provide a more streamlined and intuitive interface. -Accessing the results of a Calculation is now as simple as: -```python -fx.FullCalculation('Sim1', flow_system) -calculation.solve(fx.solvers.HighsSolver()) -calculation.results # This object can be entirely saved and reloaded to file without any information loss -``` -This access doesn't change if you save and load the results to a file or use them in your script directly! - -- **Improved Documentation**: The FlowSystem as well as a model Documentation is created for every model run. -- **Results without saving to file**: The results of a Calculation can now be properly accessed without saving the results to a file first. -- **Unified Solution exploration**: Every `Calculation` has a `Calculation.results` attribute, which accesses the solution. This can be saved and reloaded without any information loss. -- **Improved Calculation Results**: The results of a Calculation are now more intuitive and easier to access. The `CalculationResults` class has been completely redesigned to provide a more streamlined and intuitive interface. - -### Data Management & I/O - -- **Unified Serialization**: Standardized serialization and deserialization across all elements -- **Compression Support**: Added data compression when saving results to reduce file size -- **to_netcdf/from_netcdf Methods**: Added for FlowSystem and other core components - -### Details -#### TimeSeries Enhancements - -- **xarray Integration**: Redesigned TimeSeries to depend on xr.DataArray -- **datatypes**: Added support for more datatypes, with methods for conversion to TimeSeries -- **Improved TimeSeriesCollection**: Enhanced indexing, representation, and dataset conversion -- **Simplified Time Management**: Removed period concepts and focused on timesteps for more intuitive time handling - -## 📚 Documentation - -- Improved documentation of the FlixOpt API and mathematical formulations -- **Google Style Docstrings**: Updated all docstrings to Google style format - -## 🔄 Dependencies - -- **Linopy**: Added as the core dependency replacing Pyomo -- **xarray**: Now a critical dependency for data handling and file I/O -- **netcdf4**: Dependency for fast and efficient file I/O - -### Dropped Dependencies -- **pyomo**: Replaced by linopy as the modeling language - -## 📋 Migration Notes - -This version represents a significant architecture change. If you're upgrading: - -- Code that directly accessed Pyomo models will need to be updated to work with Linopy -- Data handling now uses xarray.Dataset throughout, which may require changes in how you interact with results -- The way labels are constructed has changed throughout the system -- The results of calculations are now handled differently, and may require changes in how you access results -- The framework was renamed from flixOpt to flixopt. Use `import flixopt as fx`. - -For complete details, please refer to the full commit history. - -## Installation - -```bash -pip install flixopt==2.0.0 -``` - -## Upgrading - -```bash -pip install --upgrade flixopt -``` \ No newline at end of file diff --git a/docs/release-notes/v2.0.1.md b/docs/release-notes/v2.0.1.md deleted file mode 100644 index 9b6884e48..000000000 --- a/docs/release-notes/v2.0.1.md +++ /dev/null @@ -1,12 +0,0 @@ -# Release v2.0.1 - -**Release Date:** 2025-04-10 - -## Improvements - -* Add logger warning if relative_minimum is used without on_off_parameters in Flow, as this prevents the flow_rate from switching "OFF" - -## Bug Fixes - -* Replace "|" with "__" in filenames when saving figures, as "|" can lead to issues on windows -* Fixed a Bug that prevented the load factor from working without InvestmentParameters diff --git a/docs/release-notes/v2.1.0.md b/docs/release-notes/v2.1.0.md deleted file mode 100644 index 09972c5f7..000000000 --- a/docs/release-notes/v2.1.0.md +++ /dev/null @@ -1,31 +0,0 @@ -# Release v2.1.0 - -**Release Date:** 2025-04-11 - -## Improvements - -* Add logger warning if relative_minimum is used without on_off_parameters in Flow, as this prevents the flow_rate from switching "OFF" -* Python 3.13 support added -* Greatly improved internal testing infrastructure by leveraging linopy's testing framework - -## Bug Fixes - -* Bugfixing the lower bound of `flow_rate` when using optional investments without OnOffParameters. -* Fixes a Bug that prevented divest effects from working. -* added lower bounds of 0 to two unbounded vars (only numerical better) - -## Breaking Changes - -* We restructured the modeling of the On/Off state of FLows or Components. This leads to slightly renaming of variables and constraints. - -### Variable renaming -* "...|consecutive_on_hours" is now "...|ConsecutiveOn|hours" -* "...|consecutive_off_hours" is now "...|ConsecutiveOff|hours" - -### Constraint renaming -* "...|consecutive_on_hours_con1" is now "...|ConsecutiveOn|con1" -* "...|consecutive_on_hours_con2a" is now "...|ConsecutiveOn|con2a" -* "...|consecutive_on_hours_con2b" is now "...|ConsecutiveOn|con2b" -* "...|consecutive_on_hours_initial" is now "...|ConsecutiveOn|initial" -* "...|consecutive_on_hours_minimum_duration" is now "...|ConsecutiveOn|minimum" -The same goes for "...|consecutive_off..." --> "...|ConsecutiveOff|..." \ No newline at end of file diff --git a/docs/release-notes/v2.1.1.md b/docs/release-notes/v2.1.1.md deleted file mode 100644 index 44e635f87..000000000 --- a/docs/release-notes/v2.1.1.md +++ /dev/null @@ -1,11 +0,0 @@ -# Release v2.1.1 - -**Release Date:** 2025-05-08 - -## Improvements - -* Improving docstring and tests - -## Bug Fixes - -* Fixing bug in the `_ElementResults.constraints` not returning the constraints but rather the variables From 926eff3049bf76e0570651b5703c992ec9a55a9e Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Sat, 14 Jun 2025 10:55:38 +0200 Subject: [PATCH 2/9] delete not needed files --- docs/release-notes/_template.txt | 32 -------------------------------- site/release-notes/_template.txt | 32 -------------------------------- 2 files changed, 64 deletions(-) delete mode 100644 docs/release-notes/_template.txt delete mode 100644 site/release-notes/_template.txt diff --git a/docs/release-notes/_template.txt b/docs/release-notes/_template.txt deleted file mode 100644 index fe85a0554..000000000 --- a/docs/release-notes/_template.txt +++ /dev/null @@ -1,32 +0,0 @@ -# Release v{version} - -**Release Date:** YYYY-MM-DD - -## What's New - -* Feature 1 - Description -* Feature 2 - Description - -## Improvements - -* Improvement 1 - Description -* Improvement 2 - Description - -## Bug Fixes - -* Fixed issue with X -* Resolved problem with Y - -## Breaking Changes - -* Change 1 - Migration instructions -* Change 2 - Migration instructions - -## Deprecations - -* Feature X will be removed in v{next_version} - -## Dependencies - -* Added dependency X v1.2.3 -* Updated dependency Y to v2.0.0 \ No newline at end of file diff --git a/site/release-notes/_template.txt b/site/release-notes/_template.txt deleted file mode 100644 index fe85a0554..000000000 --- a/site/release-notes/_template.txt +++ /dev/null @@ -1,32 +0,0 @@ -# Release v{version} - -**Release Date:** YYYY-MM-DD - -## What's New - -* Feature 1 - Description -* Feature 2 - Description - -## Improvements - -* Improvement 1 - Description -* Improvement 2 - Description - -## Bug Fixes - -* Fixed issue with X -* Resolved problem with Y - -## Breaking Changes - -* Change 1 - Migration instructions -* Change 2 - Migration instructions - -## Deprecations - -* Feature X will be removed in v{next_version} - -## Dependencies - -* Added dependency X v1.2.3 -* Updated dependency Y to v2.0.0 \ No newline at end of file From 1a44f3a77280ff55dc706e9787d9e099fc81e59b Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Sat, 14 Jun 2025 10:55:57 +0200 Subject: [PATCH 3/9] Add options for mike --- mkdocs.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/mkdocs.yml b/mkdocs.yml index ebf4ac0d9..fb009b1fd 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -79,12 +79,13 @@ markdown_extensions: - pymdownx.snippets: base_path: .. - plugins: - search # Enables the search functionality in the documentation - table-reader # Allows including tables from external files - include-markdown - - mike + - mike: + version_selector: true + default_version: latest - gen-files: scripts: - scripts/gen_ref_pages.py From 1909e8ca7aab9f5307a7cb6dba208eca7c90be09 Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Sat, 14 Jun 2025 11:16:55 +0200 Subject: [PATCH 4/9] Automate release creation in workflow --- .github/workflows/python-app.yaml | 56 +++++++++-- scripts/extract_release_notes.py | 152 ++++++++++++++++++++++++++++++ 2 files changed, 202 insertions(+), 6 deletions(-) create mode 100644 scripts/extract_release_notes.py diff --git a/.github/workflows/python-app.yaml b/.github/workflows/python-app.yaml index abaef42e1..723dd8b37 100644 --- a/.github/workflows/python-app.yaml +++ b/.github/workflows/python-app.yaml @@ -56,12 +56,56 @@ jobs: - name: Run tests run: pytest -v -p no:warnings + create-release: + name: Create Release with Changelog + runs-on: ubuntu-22.04 + needs: [test] + if: startsWith(github.ref, 'refs/tags/v') + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Set up Python + uses: actions/setup-python@v4 + with: + python-version: "3.11" + + - name: Extract version from tag + id: version + run: | + VERSION=${GITHUB_REF#refs/tags/v} + echo "VERSION=$VERSION" >> $GITHUB_OUTPUT + echo "Processing version: $VERSION" + + - name: Validate release notes exist + run: | + python scripts/extract_release_notes.py ${{ steps.version.outputs.VERSION }} --validate-only + + - name: Extract release notes + run: | + python scripts/extract_release_notes.py ${{ steps.version.outputs.VERSION }} --output current_release_notes.md + echo "✅ Release notes extracted for version ${{ steps.version.outputs.VERSION }}" + + - name: Create GitHub Release + uses: softprops/action-gh-release@v1 + with: + name: "Release v${{ steps.version.outputs.VERSION }}" + body_path: current_release_notes.md + draft: false + prerelease: ${{ contains(steps.version.outputs.VERSION, 'alpha') || contains(steps.version.outputs.VERSION, 'beta') || contains(steps.version.outputs.VERSION, 'rc') }} + make_latest: true + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + publish-testpypi: name: Publish to TestPyPI runs-on: ubuntu-22.04 - needs: [test] # Only run after tests pass - if: github.event_name == 'release' && github.event.action == 'created' # Only on release creation - + needs: [test, create-release] # Run after tests and release creation + if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v') # Only on tag push + steps: - name: Checkout repository uses: actions/checkout@v4 @@ -86,7 +130,7 @@ jobs: env: TWINE_USERNAME: __token__ TWINE_PASSWORD: ${{ secrets.TEST_PYPI_API_TOKEN }} - + - name: Test install from TestPyPI run: | # Create a temporary environment to test installation @@ -104,8 +148,8 @@ jobs: publish-pypi: name: Publish to PyPI runs-on: ubuntu-22.04 - needs: [test] # Only run after TestPyPI publish succeeds - if: github.event_name == 'release' && github.event.action == 'created' # Only on release creation + needs: [publish-testpypi] # Only run after TestPyPI publish succeeds + if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v') # Only on tag push steps: - name: Checkout repository diff --git a/scripts/extract_release_notes.py b/scripts/extract_release_notes.py new file mode 100644 index 000000000..707dc79ec --- /dev/null +++ b/scripts/extract_release_notes.py @@ -0,0 +1,152 @@ +#!/usr/bin/env python3 +""" +Extract release notes from CHANGELOG.md for a specific version. +Usage: python extract_release_notes.py +""" + +import argparse +import re +import sys +from pathlib import Path +from typing import Optional + + +def extract_release_notes(version: str, changelog_path: Path = Path("CHANGELOG.md")) -> str: + """ + Extract release notes for a specific version from CHANGELOG.md + + Args: + version: Version string (e.g., "2.1.2") + changelog_path: Path to the changelog file + + Returns: + Release notes content as string + + Raises: + FileNotFoundError: If changelog file doesn't exist + ValueError: If version section not found + """ + if not changelog_path.exists(): + raise FileNotFoundError(f"Changelog file not found: {changelog_path}") + + content = changelog_path.read_text(encoding='utf-8') + + # Pattern to match version section in Keep a Changelog format + # Matches: ## [2.1.2] - 2025-06-14 + pattern = rf'## \[{re.escape(version)}\] - [^\n]+\n(.*?)(?=\n## \[|\n\[Unreleased\]|\Z)' + match = re.search(pattern, content, re.DOTALL) + + if not match: + # Try alternative format without brackets: ## 2.1.2 - 2025-06-14 + pattern_alt = rf'## {re.escape(version)} - [^\n]+\n(.*?)(?=\n## |\Z)' + match = re.search(pattern_alt, content, re.DOTALL) + + if not match: + available_versions = extract_available_versions(content) + raise ValueError( + f"No release notes found for version '{version}'. " + f"Available versions: {', '.join(available_versions)}" + ) + + release_notes = match.group(1).strip() + + # Clean up the content + release_notes = clean_release_notes(release_notes) + + return release_notes + +def extract_available_versions(content: str) -> list[str]: + """Extract all available version numbers from changelog.""" + # Match both [version] and version formats + patterns = [ + r'## \[([^\]]+)\] - ', # [2.1.2] format + r'## ([0-9]+\.[0-9]+\.[0-9]+) - ' # 2.1.2 format + ] + + versions = [] + for pattern in patterns: + versions.extend(re.findall(pattern, content)) + + # Remove "Unreleased" if present and deduplicate + versions = [v for v in set(versions) if v.lower() != 'unreleased'] + + # Sort versions (basic string sort, good enough for display) + return sorted(versions, reverse=True) + +def clean_release_notes(content: str) -> str: + """Clean up release notes content for GitHub release.""" + # Remove excessive whitespace + content = re.sub(r'\n\s*\n\s*\n', '\n\n', content) + + # Ensure proper spacing after headers + content = re.sub(r'(### [^\n]+)\n([^\n])', r'\1\n\n\2', content) + + return content.strip() + +def main(): + parser = argparse.ArgumentParser( + description="Extract release notes from CHANGELOG.md", + formatter_class=argparse.RawDescriptionHelpFormatter, + epilog=""" +Examples: + python extract_release_notes.py 2.1.2 + python extract_release_notes.py 2.1.2 --changelog docs/CHANGELOG.md + python extract_release_notes.py 2.1.2 --output release_notes.md + """ + ) + + parser.add_argument( + 'version', + help='Version to extract (e.g., "2.1.2")' + ) + + parser.add_argument( + '--changelog', + type=Path, + default=Path('CHANGELOG.md'), + help='Path to changelog file (default: CHANGELOG.md)' + ) + + parser.add_argument( + '--output', + type=Path, + help='Output file path (default: stdout)' + ) + + parser.add_argument( + '--validate-only', + action='store_true', + help='Only validate that the version exists, don\'t output content' + ) + + args = parser.parse_args() + + try: + release_notes = extract_release_notes(args.version, args.changelog) + + if args.validate_only: + print(f"✅ Release notes found for version {args.version}") + return 0 + + if args.output: + args.output.write_text(release_notes, encoding='utf-8') + print(f"✅ Release notes written to {args.output}") + else: + print(release_notes) + + return 0 + + except FileNotFoundError as e: + print(f"❌ Error: {e}", file=sys.stderr) + return 1 + + except ValueError as e: + print(f"❌ Error: {e}", file=sys.stderr) + return 1 + + except Exception as e: + print(f"❌ Unexpected error: {e}", file=sys.stderr) + return 1 + +if __name__ == "__main__": + sys.exit(main()) From 491613a30c01b95386b8f8d995bd846e153fc028 Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Sat, 14 Jun 2025 11:20:50 +0200 Subject: [PATCH 5/9] Simplify --- .github/workflows/python-app.yaml | 20 +---- scripts/extract_release_notes.py | 141 ++++-------------------------- 2 files changed, 21 insertions(+), 140 deletions(-) diff --git a/.github/workflows/python-app.yaml b/.github/workflows/python-app.yaml index 723dd8b37..90cf1681f 100644 --- a/.github/workflows/python-app.yaml +++ b/.github/workflows/python-app.yaml @@ -73,30 +73,18 @@ jobs: with: python-version: "3.11" - - name: Extract version from tag - id: version - run: | - VERSION=${GITHUB_REF#refs/tags/v} - echo "VERSION=$VERSION" >> $GITHUB_OUTPUT - echo "Processing version: $VERSION" - - - name: Validate release notes exist - run: | - python scripts/extract_release_notes.py ${{ steps.version.outputs.VERSION }} --validate-only - - name: Extract release notes run: | - python scripts/extract_release_notes.py ${{ steps.version.outputs.VERSION }} --output current_release_notes.md - echo "✅ Release notes extracted for version ${{ steps.version.outputs.VERSION }}" + VERSION=${GITHUB_REF#refs/tags/v} + echo "Extracting release notes for version: $VERSION" + python scripts/extract_release_notes.py $VERSION > current_release_notes.md - name: Create GitHub Release uses: softprops/action-gh-release@v1 with: - name: "Release v${{ steps.version.outputs.VERSION }}" body_path: current_release_notes.md draft: false - prerelease: ${{ contains(steps.version.outputs.VERSION, 'alpha') || contains(steps.version.outputs.VERSION, 'beta') || contains(steps.version.outputs.VERSION, 'rc') }} - make_latest: true + prerelease: false env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/scripts/extract_release_notes.py b/scripts/extract_release_notes.py index 707dc79ec..61ee16425 100644 --- a/scripts/extract_release_notes.py +++ b/scripts/extract_release_notes.py @@ -4,149 +4,42 @@ Usage: python extract_release_notes.py """ -import argparse import re import sys from pathlib import Path -from typing import Optional -def extract_release_notes(version: str, changelog_path: Path = Path("CHANGELOG.md")) -> str: - """ - Extract release notes for a specific version from CHANGELOG.md +def extract_release_notes(version: str) -> str: + """Extract release notes for a specific version from CHANGELOG.md""" + changelog_path = Path("CHANGELOG.md") - Args: - version: Version string (e.g., "2.1.2") - changelog_path: Path to the changelog file - - Returns: - Release notes content as string - - Raises: - FileNotFoundError: If changelog file doesn't exist - ValueError: If version section not found - """ if not changelog_path.exists(): - raise FileNotFoundError(f"Changelog file not found: {changelog_path}") + print("❌ Error: CHANGELOG.md not found", file=sys.stderr) + sys.exit(1) content = changelog_path.read_text(encoding='utf-8') - # Pattern to match version section in Keep a Changelog format - # Matches: ## [2.1.2] - 2025-06-14 + # Pattern to match version section: ## [2.1.2] - 2025-06-14 pattern = rf'## \[{re.escape(version)}\] - [^\n]+\n(.*?)(?=\n## \[|\n\[Unreleased\]|\Z)' match = re.search(pattern, content, re.DOTALL) if not match: - # Try alternative format without brackets: ## 2.1.2 - 2025-06-14 - pattern_alt = rf'## {re.escape(version)} - [^\n]+\n(.*?)(?=\n## |\Z)' - match = re.search(pattern_alt, content, re.DOTALL) - - if not match: - available_versions = extract_available_versions(content) - raise ValueError( - f"No release notes found for version '{version}'. " - f"Available versions: {', '.join(available_versions)}" - ) - - release_notes = match.group(1).strip() - - # Clean up the content - release_notes = clean_release_notes(release_notes) - - return release_notes - -def extract_available_versions(content: str) -> list[str]: - """Extract all available version numbers from changelog.""" - # Match both [version] and version formats - patterns = [ - r'## \[([^\]]+)\] - ', # [2.1.2] format - r'## ([0-9]+\.[0-9]+\.[0-9]+) - ' # 2.1.2 format - ] - - versions = [] - for pattern in patterns: - versions.extend(re.findall(pattern, content)) - - # Remove "Unreleased" if present and deduplicate - versions = [v for v in set(versions) if v.lower() != 'unreleased'] - - # Sort versions (basic string sort, good enough for display) - return sorted(versions, reverse=True) + print(f"❌ Error: No release notes found for version '{version}'", file=sys.stderr) + sys.exit(1) -def clean_release_notes(content: str) -> str: - """Clean up release notes content for GitHub release.""" - # Remove excessive whitespace - content = re.sub(r'\n\s*\n\s*\n', '\n\n', content) + return match.group(1).strip() - # Ensure proper spacing after headers - content = re.sub(r'(### [^\n]+)\n([^\n])', r'\1\n\n\2', content) - - return content.strip() def main(): - parser = argparse.ArgumentParser( - description="Extract release notes from CHANGELOG.md", - formatter_class=argparse.RawDescriptionHelpFormatter, - epilog=""" -Examples: - python extract_release_notes.py 2.1.2 - python extract_release_notes.py 2.1.2 --changelog docs/CHANGELOG.md - python extract_release_notes.py 2.1.2 --output release_notes.md - """ - ) - - parser.add_argument( - 'version', - help='Version to extract (e.g., "2.1.2")' - ) - - parser.add_argument( - '--changelog', - type=Path, - default=Path('CHANGELOG.md'), - help='Path to changelog file (default: CHANGELOG.md)' - ) - - parser.add_argument( - '--output', - type=Path, - help='Output file path (default: stdout)' - ) - - parser.add_argument( - '--validate-only', - action='store_true', - help='Only validate that the version exists, don\'t output content' - ) - - args = parser.parse_args() - - try: - release_notes = extract_release_notes(args.version, args.changelog) - - if args.validate_only: - print(f"✅ Release notes found for version {args.version}") - return 0 - - if args.output: - args.output.write_text(release_notes, encoding='utf-8') - print(f"✅ Release notes written to {args.output}") - else: - print(release_notes) - - return 0 - - except FileNotFoundError as e: - print(f"❌ Error: {e}", file=sys.stderr) - return 1 + if len(sys.argv) != 2: + print("Usage: python extract_release_notes.py ") + print("Example: python extract_release_notes.py 2.1.2") + sys.exit(1) - except ValueError as e: - print(f"❌ Error: {e}", file=sys.stderr) - return 1 + version = sys.argv[1] + release_notes = extract_release_notes(version) + print(release_notes) - except Exception as e: - print(f"❌ Unexpected error: {e}", file=sys.stderr) - return 1 if __name__ == "__main__": - sys.exit(main()) + main() From bb8dc8d8f74e3e2e8dee313e3ce81afbe7a36630 Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Sat, 14 Jun 2025 11:54:12 +0200 Subject: [PATCH 6/9] Copy Changelog to docs for releases --- .github/workflows/python-app.yaml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/python-app.yaml b/.github/workflows/python-app.yaml index 90cf1681f..ee72309be 100644 --- a/.github/workflows/python-app.yaml +++ b/.github/workflows/python-app.yaml @@ -73,6 +73,11 @@ jobs: with: python-version: "3.11" + - name: Sync changelog to docs + run: | + cp CHANGELOG.md docs/changelog.md + echo "✅ Synced changelog to docs" + - name: Extract release notes run: | VERSION=${GITHUB_REF#refs/tags/v} From d6dbd634243583612a2282ad9830766f90f3c7a3 Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Sat, 14 Jun 2025 12:01:12 +0200 Subject: [PATCH 7/9] Add prereleae detection --- .github/workflows/python-app.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/python-app.yaml b/.github/workflows/python-app.yaml index ee72309be..1896150f3 100644 --- a/.github/workflows/python-app.yaml +++ b/.github/workflows/python-app.yaml @@ -89,7 +89,7 @@ jobs: with: body_path: current_release_notes.md draft: false - prerelease: false + prerelease: ${{ contains(github.ref, 'alpha') || contains(github.ref, 'beta') || contains(github.ref, 'rc') }} env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} From d28f075e968e55f42d6cda945f25021d3678c98d Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Sat, 14 Jun 2025 12:01:53 +0200 Subject: [PATCH 8/9] Only create docs ro stable releases --- .github/workflows/deploy-docs.yaml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/deploy-docs.yaml b/.github/workflows/deploy-docs.yaml index 73a3f0b1f..37c15eb2f 100644 --- a/.github/workflows/deploy-docs.yaml +++ b/.github/workflows/deploy-docs.yaml @@ -8,6 +8,8 @@ on: jobs: deploy-docs: runs-on: ubuntu-latest + # Only deploy docs for stable releases (not pre-releases) + if: github.event.release.prerelease == false steps: - uses: actions/checkout@v4 with: @@ -30,4 +32,6 @@ jobs: - name: Deploy docs run: | VERSION=${GITHUB_REF#refs/tags/v} - mike deploy --push --update-aliases $VERSION latest \ No newline at end of file + echo "Deploying docs for stable release: $VERSION" + mike deploy --push --update-aliases $VERSION latest + mike set-default --push latest \ No newline at end of file From 68220604d8c2d6a9099ce20e15db690d58adca0b Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Sat, 14 Jun 2025 12:05:30 +0200 Subject: [PATCH 9/9] Change Header --- docs/SUMMARY.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/SUMMARY.md b/docs/SUMMARY.md index 866c6018c..fffb84610 100644 --- a/docs/SUMMARY.md +++ b/docs/SUMMARY.md @@ -4,4 +4,4 @@ - [Examples](examples/) - [FAQ](faq/) - [API-Reference](api-reference/) -- [Changelog](changelog.md) \ No newline at end of file +- [Release Notes](changelog.md) \ No newline at end of file