diff --git a/clients/aws-sdk-bedrock-runtime/.changes/0.0.1.json b/clients/aws-sdk-bedrock-runtime/.changes/0.0.1.json new file mode 100644 index 0000000..fd1a668 --- /dev/null +++ b/clients/aws-sdk-bedrock-runtime/.changes/0.0.1.json @@ -0,0 +1,12 @@ +{ + "changes": [ + { + "type": "feature", + "description": "Initial Client Release with support for current Amazon Bedrock Runtime operations." + }, + { + "type": "feature", + "description": "Added support for new InvokeModelWithBidirectionalStream API." + } + ] +} \ No newline at end of file diff --git a/clients/aws-sdk-bedrock-runtime/.changes/0.0.2.json b/clients/aws-sdk-bedrock-runtime/.changes/0.0.2.json new file mode 100644 index 0000000..cb83144 --- /dev/null +++ b/clients/aws-sdk-bedrock-runtime/.changes/0.0.2.json @@ -0,0 +1,8 @@ +{ + "changes": [ + { + "type": "dependency", + "description": "Updated support for all smithy dependencies in the 0.0.x minor version." + } + ] +} \ No newline at end of file diff --git a/clients/aws-sdk-bedrock-runtime/.changes/0.1.0.json b/clients/aws-sdk-bedrock-runtime/.changes/0.1.0.json new file mode 100644 index 0000000..22350f7 --- /dev/null +++ b/clients/aws-sdk-bedrock-runtime/.changes/0.1.0.json @@ -0,0 +1,36 @@ +{ + "changes": [ + { + "type": "api-change", + "description": "Fixed stop sequence limit for converse API." + }, + { + "type": "api-change", + "description": "Launch CountTokens API to allow token counting." + }, + { + "type": "api-change", + "description": "This release adds support for Automated Reasoning checks output models for the Amazon Bedrock Guardrails ApplyGuardrail API." + }, + { + "type": "api-change", + "description": "Document update to support on-demand custom model." + }, + { + "type": "api-change", + "description": "Add API Key and document citations support for Bedrock Runtime APIs." + }, + { + "type": "api-change", + "description": "This release adds native h2 support for the Bedrock Runtime API. Support is limited to SDKs that support h2 requests natively." + }, + { + "type": "api-change", + "description": "You can now reference images and documents stored in Amazon S3 when using InvokeModel and Converse APIs with Amazon Nova Lite and Nova Pro. This enables direct integration of S3-stored multimedia assets in your model requests without manual downloading or base64 encoding." + }, + { + "type": "dependency", + "description": "Updated support for all smithy dependencies in the 0.1.x minor version." + } + ] +} \ No newline at end of file diff --git a/clients/aws-sdk-bedrock-runtime/.changes/0.1.1.json b/clients/aws-sdk-bedrock-runtime/.changes/0.1.1.json new file mode 100644 index 0000000..39b5f1b --- /dev/null +++ b/clients/aws-sdk-bedrock-runtime/.changes/0.1.1.json @@ -0,0 +1,20 @@ +{ + "changes": [ + { + "type": "breaking", + "description": "Removed unused `serialize.py` and `deserialize.py` modules." + }, + { + "type": "api-change", + "description": "New stop reason for Converse and ConverseStream." + }, + { + "type": "enhancement", + "description": "Improvements to the underlying AWS CRT HTTP client result in a significant decrease in CPU usage. Addresses [aws-sdk-python#11](https://github.com/awslabs/aws-sdk-python/issues/11)." + }, + { + "type": "dependency", + "description": "**Updated**: `smithy_http[awscrt]` from `~=0.1.0` to `~=0.2.0`." + } + ] +} \ No newline at end of file diff --git a/clients/aws-sdk-bedrock-runtime/.changes/0.2.0.json b/clients/aws-sdk-bedrock-runtime/.changes/0.2.0.json new file mode 100644 index 0000000..660d3b0 --- /dev/null +++ b/clients/aws-sdk-bedrock-runtime/.changes/0.2.0.json @@ -0,0 +1,36 @@ +{ + "changes": [ + { + "type": "api-change", + "description": "Add support to automatically enforce safeguards across accounts within an AWS Organization." + }, + { + "type": "api-change", + "description": "This release includes support for Search Results." + }, + { + "type": "api-change", + "description": "Amazon Bedrock Runtime Service Tier Support Launch." + }, + { + "type": "api-change", + "description": "Add support for system tool and web citation response." + }, + { + "type": "enhancement", + "description": "Add Standard Retry Mode." + }, + { + "type": "dependency", + "description": "**Updated**: `smithy_aws_core[eventstream, json]` from `~=0.1.0` to `~=0.2.0`." + }, + { + "type": "dependency", + "description": "**Updated**: `smithy_core` from `~=0.1.0` to `~=0.2.0`." + }, + { + "type": "dependency", + "description": "**Updated**: `smithy_http[awscrt]~=0.3.0` from `~=0.2.0` to `~=0.3.0`." + } + ] +} \ No newline at end of file diff --git a/clients/aws-sdk-bedrock-runtime/.changes/0.3.0.json b/clients/aws-sdk-bedrock-runtime/.changes/0.3.0.json new file mode 100644 index 0000000..5da3648 --- /dev/null +++ b/clients/aws-sdk-bedrock-runtime/.changes/0.3.0.json @@ -0,0 +1,28 @@ +{ + "changes": [ + { + "type": "api-change", + "description": "Adds support for Audio Blocks and Streaming Image Output plus new Stop Reasons of malformed_model_output and malformed_tool_use." + }, + { + "type": "api-change", + "description": "Adds support for Bedrock Runtime Reserved Service." + }, + { + "type": "breaking", + "description": "Function signature for `resolve_retry_strategy` has been changed to prevent unnecessary code duplication in operation methods. This will affect all 0.3.0 clients." + }, + { + "type": "enhancement", + "description": "Add comprehensive integration tests for non-streaming, output streaming, and bidirectional streaming operations." + }, + { + "type": "dependency", + "description": "**Updated**: `smithy_aws_core[eventstream, json]` from `~=0.2.0` to `~=0.3.0`." + }, + { + "type": "dependency", + "description": "**Updated**: `smithy_core` from `~=0.2.0` to `~=0.3.0`." + } + ] +} \ No newline at end of file diff --git a/clients/aws-sdk-bedrock-runtime/CHANGELOG.md b/clients/aws-sdk-bedrock-runtime/CHANGELOG.md index 37e8b79..49bbfff 100644 --- a/clients/aws-sdk-bedrock-runtime/CHANGELOG.md +++ b/clients/aws-sdk-bedrock-runtime/CHANGELOG.md @@ -1,82 +1,72 @@ # Changelog -## Unreleased - -* None Yet. - ## v0.3.0 ### API Changes * Adds support for Audio Blocks and Streaming Image Output plus new Stop Reasons of malformed_model_output and malformed_tool_use. * Adds support for Bedrock Runtime Reserved Service. -### Breaking +### Breaking Changes * Function signature for `resolve_retry_strategy` has been changed to prevent unnecessary code duplication in operation methods. This will affect all 0.3.0 clients. -### Dependencies -* **Updated**: `smithy_aws_core[eventstream, json]` from `~=0.2.0` to `~=0.3.0`. -* **Updated**: `smithy_core` from `~=0.2.0` to `~=0.3.0`. - ### Enhancements * Add comprehensive integration tests for non-streaming, output streaming, and bidirectional streaming operations. +### Dependencies +* **Updated**: `smithy_aws_core[eventstream, json]` from `~=0.2.0` to `~=0.3.0`. +* **Updated**: `smithy_core` from `~=0.2.0` to `~=0.3.0`. ## v0.2.0 ### API Changes * Add support to automatically enforce safeguards across accounts within an AWS Organization. * This release includes support for Search Results. -* Amazon Bedrock Runtime Service Tier Support Launch +* Amazon Bedrock Runtime Service Tier Support Launch. * Add support for system tool and web citation response. ### Enhancements -* Add Standard Retry Mode +* Add Standard Retry Mode. ### Dependencies - * **Updated**: `smithy_aws_core[eventstream, json]` from `~=0.1.0` to `~=0.2.0`. * **Updated**: `smithy_core` from `~=0.1.0` to `~=0.2.0`. * **Updated**: `smithy_http[awscrt]~=0.3.0` from `~=0.2.0` to `~=0.3.0`. - ## v0.1.1 ### API Changes -* New stop reason for Converse and ConverseStream +* New stop reason for Converse and ConverseStream. + +### Breaking Changes +* Removed unused `serialize.py` and `deserialize.py` modules. ### Enhancements * Improvements to the underlying AWS CRT HTTP client result in a significant decrease in CPU usage. Addresses [aws-sdk-python#11](https://github.com/awslabs/aws-sdk-python/issues/11). ### Dependencies - * **Updated**: `smithy_http[awscrt]` from `~=0.1.0` to `~=0.2.0`. -### Breaking -- Removed unused `serialize.py` and `deserialize.py` modules. - ## v0.1.0 ### API Changes * Fixed stop sequence limit for converse API. -* Launch CountTokens API to allow token counting +* Launch CountTokens API to allow token counting. * This release adds support for Automated Reasoning checks output models for the Amazon Bedrock Guardrails ApplyGuardrail API. -* document update to support on demand custom model. -* Add API Key and document citations support for Bedrock Runtime APIs -* This release adds native h2 support for the bedrock runtime API, the support is only limited to SDKs that support h2 requests natively. +* Document update to support on-demand custom model. +* Add API Key and document citations support for Bedrock Runtime APIs. +* This release adds native h2 support for the Bedrock Runtime API. Support is limited to SDKs that support h2 requests natively. * You can now reference images and documents stored in Amazon S3 when using InvokeModel and Converse APIs with Amazon Nova Lite and Nova Pro. This enables direct integration of S3-stored multimedia assets in your model requests without manual downloading or base64 encoding. ### Dependencies - * Updated support for all smithy dependencies in the 0.1.x minor version. ## v0.0.2 ### Dependencies - * Updated support for all smithy dependencies in the 0.0.x minor version. ## v0.0.1 ### Features * Initial Client Release with support for current Amazon Bedrock Runtime operations. -* Added support for new InvokeModelWithBidirectionalStream API +* Added support for new InvokeModelWithBidirectionalStream API. diff --git a/clients/aws-sdk-sagemaker-runtime-http2/.changes/0.1.0.json b/clients/aws-sdk-sagemaker-runtime-http2/.changes/0.1.0.json new file mode 100644 index 0000000..ed21b64 --- /dev/null +++ b/clients/aws-sdk-sagemaker-runtime-http2/.changes/0.1.0.json @@ -0,0 +1,8 @@ +{ + "changes": [ + { + "type": "feature", + "description": "Initial client release with support for current Amazon SageMaker Runtime HTTP2 operations." + } + ] +} \ No newline at end of file diff --git a/clients/aws-sdk-sagemaker-runtime-http2/.changes/0.3.0.json b/clients/aws-sdk-sagemaker-runtime-http2/.changes/0.3.0.json new file mode 100644 index 0000000..d77b321 --- /dev/null +++ b/clients/aws-sdk-sagemaker-runtime-http2/.changes/0.3.0.json @@ -0,0 +1,17 @@ +{ + "changes": [ + { + "type": "breaking", + "description": "Function signature for `resolve_retry_strategy` has been changed to prevent unnecessary code duplication in operation methods. This will affect all 0.3.0 clients." + }, + { + "type": "dependency", + "description": "**Updated**: `smithy_aws_core[eventstream, json]` from `~=0.2.0` to `~=0.3.0`." + }, + { + "type": "dependency", + "description": "**Updated**: `smithy_core` from `~=0.2.0` to `~=0.3.0`." + } + ], + "summary": "This release follows 0.1.0. There is no 0.2.0 as the initial release was intended to be inline\nwith the `smithy-aws-core` version used in the client." +} \ No newline at end of file diff --git a/clients/aws-sdk-sagemaker-runtime-http2/CHANGELOG.md b/clients/aws-sdk-sagemaker-runtime-http2/CHANGELOG.md index 8bc9e07..f1fa0f1 100644 --- a/clients/aws-sdk-sagemaker-runtime-http2/CHANGELOG.md +++ b/clients/aws-sdk-sagemaker-runtime-http2/CHANGELOG.md @@ -1,15 +1,11 @@ # Changelog -## Unreleased - -* None yet. - ## v0.3.0 -This release proceeds 0.1.0. There is no 0.2.0 as the initial release was intended to be inline +This release follows 0.1.0. There is no 0.2.0 as the initial release was intended to be inline with the `smithy-aws-core` version used in the client. -### Breaking +### Breaking Changes * Function signature for `resolve_retry_strategy` has been changed to prevent unnecessary code duplication in operation methods. This will affect all 0.3.0 clients. ### Dependencies diff --git a/clients/aws-sdk-transcribe-streaming/.changes/0.1.0.json b/clients/aws-sdk-transcribe-streaming/.changes/0.1.0.json new file mode 100644 index 0000000..2fb1043 --- /dev/null +++ b/clients/aws-sdk-transcribe-streaming/.changes/0.1.0.json @@ -0,0 +1,8 @@ +{ + "changes": [ + { + "type": "feature", + "description": "Initial client release with support for current Amazon Transcribe Streaming operations." + } + ] +} \ No newline at end of file diff --git a/clients/aws-sdk-transcribe-streaming/.changes/0.2.0.json b/clients/aws-sdk-transcribe-streaming/.changes/0.2.0.json new file mode 100644 index 0000000..8e48cc7 --- /dev/null +++ b/clients/aws-sdk-transcribe-streaming/.changes/0.2.0.json @@ -0,0 +1,24 @@ +{ + "changes": [ + { + "type": "api-change", + "description": "This release adds support for additional locales in AWS transcribe streaming." + }, + { + "type": "enhancement", + "description": "Add Standard Retry Mode." + }, + { + "type": "dependency", + "description": "**Updated**: `smithy_aws_core[eventstream, json]` from `~=0.1.0` to `~=0.2.0`." + }, + { + "type": "dependency", + "description": "**Updated**: `smithy_core` from `~=0.1.0` to `~=0.2.0`." + }, + { + "type": "dependency", + "description": "**Updated**: `smithy_http[awscrt]~=0.3.0` from `~=0.2.0` to `~=0.3.0`." + } + ] +} \ No newline at end of file diff --git a/clients/aws-sdk-transcribe-streaming/.changes/0.3.0.json b/clients/aws-sdk-transcribe-streaming/.changes/0.3.0.json new file mode 100644 index 0000000..1f37b8d --- /dev/null +++ b/clients/aws-sdk-transcribe-streaming/.changes/0.3.0.json @@ -0,0 +1,16 @@ +{ + "changes": [ + { + "type": "breaking", + "description": "Function signature for `resolve_retry_strategy` has been changed to prevent unnecessary code duplication in operation methods. This will affect all 0.3.0 clients." + }, + { + "type": "dependency", + "description": "**Updated**: `smithy_aws_core[eventstream, json]` from `~=0.2.0` to `~=0.3.0`." + }, + { + "type": "dependency", + "description": "**Updated**: `smithy_core` from `~=0.2.0` to `~=0.3.0`." + } + ] +} \ No newline at end of file diff --git a/clients/aws-sdk-transcribe-streaming/CHANGELOG.md b/clients/aws-sdk-transcribe-streaming/CHANGELOG.md index 8be04cc..09417e4 100644 --- a/clients/aws-sdk-transcribe-streaming/CHANGELOG.md +++ b/clients/aws-sdk-transcribe-streaming/CHANGELOG.md @@ -1,12 +1,8 @@ # Changelog -## Unreleased - -* None yet. - ## v0.3.0 -### Breaking +### Breaking Changes * Function signature for `resolve_retry_strategy` has been changed to prevent unnecessary code duplication in operation methods. This will affect all 0.3.0 clients. ### Dependencies @@ -19,7 +15,7 @@ * This release adds support for additional locales in AWS transcribe streaming. ### Enhancements -* Add Standard Retry Mode +* Add Standard Retry Mode. ### Dependencies * **Updated**: `smithy_aws_core[eventstream, json]` from `~=0.1.0` to `~=0.2.0`. diff --git a/scripts/changelog/new-entry.py b/scripts/changelog/new-entry.py new file mode 100755 index 0000000..03c866d --- /dev/null +++ b/scripts/changelog/new-entry.py @@ -0,0 +1,127 @@ +#!/usr/bin/env python3 +# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +""" +Create new changelog entries for a specific package. +""" + +import argparse +import json +import time +import uuid +from pathlib import Path + +PROJECT_ROOT_DIR = Path(__file__).resolve().parent.parent.parent + + +def get_package_changes_dir(package_name: str) -> Path: + package_path = PROJECT_ROOT_DIR / "clients" / package_name + changes_dir = package_path / ".changes" + return changes_dir + + +def create_change_entry( + change_type: str, + description: str, + package_name: str, +) -> str: + # Get package .changes directory and ensure it exists + changes_dir = get_package_changes_dir(package_name) + changes_dir.mkdir(exist_ok=True) + + # Create next-release directory for pending changes + next_release_dir = changes_dir / "next-release" + next_release_dir.mkdir(exist_ok=True) + + # Generate unique filename + timestamp = int(time.time() * 1_000_000) + unique_id = uuid.uuid4().hex + filename = f"{package_name}-{change_type}-{timestamp}-{unique_id}.json" + + entry_data = { + "type": change_type, + "description": description, + } + + entry_file = next_release_dir / filename + with open(entry_file, "w") as f: + json.dump(entry_data, f, indent=2) + + print(f"Created changelog entry: {entry_file}") + return str(entry_file) + + +def create_summary_entry( + description: str, + package_name: str, +) -> str: + """Create or update the release summary for the next release.""" + # Get package .changes directory and ensure it exists + changes_dir = get_package_changes_dir(package_name) + changes_dir.mkdir(exist_ok=True) + + # Create next-release directory for pending changes + next_release_dir = changes_dir / "next-release" + next_release_dir.mkdir(exist_ok=True) + + # Summary is stored in a fixed file (only one summary per release) + summary_file = next_release_dir / "SUMMARY.json" + + if summary_file.exists(): + print(f"Warning: Overwriting existing summary in {summary_file}") + + entry_data = { + "summary": description, + } + + with open(summary_file, "w") as f: + json.dump(entry_data, f, indent=2) + + print(f"Created release summary: {summary_file}") + return str(summary_file) + + +def main(): + parser = argparse.ArgumentParser(description="Create a new changelog entry") + parser.add_argument( + "-t", + "--type", + # TODO: Remove the 'breaking' option once this project is stable. + choices=( + "feature", + "enhancement", + "bugfix", + "breaking", + "dependency", + "api-change", + "summary", + ), + required=True, + help="Type of change (use 'summary' for release overview)", + ) + parser.add_argument( + "-d", "--description", required=True, help="Description of the change" + ) + parser.add_argument( + "-p", + "--package", + required=True, + help="Package name", + ) + args = parser.parse_args() + + if args.type == "summary": + create_summary_entry( + description=args.description, + package_name=args.package, + ) + else: + create_change_entry( + change_type=args.type, + description=args.description, + package_name=args.package, + ) + + +if __name__ == "__main__": + main() diff --git a/scripts/changelog/new-release.py b/scripts/changelog/new-release.py new file mode 100755 index 0000000..94165d8 --- /dev/null +++ b/scripts/changelog/new-release.py @@ -0,0 +1,207 @@ +#!/usr/bin/env python3 +# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +""" +Create a new release by consolidating changelog entries from next-release directory +into a version JSON file (x.y.z.json). +""" + +import argparse +import json +import re +import sys +from pathlib import Path +from typing import Any + +PROJECT_ROOT_DIR = Path(__file__).resolve().parent.parent.parent +VERSION_PATTERN = r"^\d+\.\d+\.\d+$" +CHANGE_TYPES_ORDER = { + "breaking": 0, + "api-change": 1, + "feature": 2, + "enhancement": 3, + "bugfix": 4, + "dependency": 5, +} +CHANGE_TYPES = tuple(CHANGE_TYPES_ORDER.keys()) + + +def validate_change_entry(change_data: dict[str, Any], entry_file: Path) -> bool: + if "type" not in change_data or change_data["type"] not in CHANGE_TYPES: + print( + f"Error: Missing or invalid 'type' field in {entry_file}\n" + f"Type must be one of: {CHANGE_TYPES}" + ) + return False + + if "description" not in change_data or not change_data["description"]: + print(f"Error: Missing or empty 'description' field in {entry_file}") + return False + + return True + + +def collect_next_release_summary(next_release_dir: Path) -> str | None: + """Read the release summary if it exists.""" + summary_file = next_release_dir / "SUMMARY.json" + + if not summary_file.exists(): + return None + + try: + with open(summary_file) as f: + data = json.load(f) + return data.get("summary") + except (OSError, json.JSONDecodeError) as e: + print(f"Warning: Could not read summary file {summary_file}: {e}", file=sys.stderr) + return None + + +def collect_next_release_changes(next_release_dir: Path) -> list[dict[str, Any]]: + changes: list[dict[str, Any]] = [] + + if not next_release_dir.exists(): + return changes + + for entry_file in next_release_dir.iterdir(): + # Skip the SUMMARY.json file (handled separately) + if entry_file.name == "SUMMARY.json": + continue + + if entry_file.is_file() and entry_file.suffix == ".json": + try: + with open(entry_file) as f: + change_data = json.load(f) + + # Validate required fields + if not validate_change_entry(change_data, entry_file): + sys.exit(1) + + changes.append(change_data) + except (OSError, json.JSONDecodeError) as e: + print(f"Error: Could not process {entry_file}: {e}", file=sys.stderr) + sys.exit(1) + + # Sort changes by type for consistent ordering + changes.sort(key=lambda c: (CHANGE_TYPES_ORDER[c["type"]])) + + return changes + + +def create_version_file( + changes_dir: Path, + version: str, + changes: list[dict[str, Any]], + summary: str | None = None, +) -> Path: + version_file = changes_dir / f"{version}.json" + + if version_file.exists(): + print(f"Error: Version file {version_file} already exists!") + sys.exit(1) + + version_data: dict[str, Any] = {"changes": changes} + + if summary: + version_data["summary"] = summary + + with open(version_file, "w") as f: + json.dump(version_data, f, indent=2) + + return version_file + + +def cleanup_next_release_dir(next_release_dir: Path) -> int: + removed_count = 0 + + for entry_file in next_release_dir.iterdir(): + if entry_file.is_file() and entry_file.suffix == ".json": + try: + entry_file.unlink() + removed_count += 1 + except OSError as e: + print(f"Warning: Could not remove {entry_file}: {e}", file=sys.stderr) + + return removed_count + + +def create_new_release(package_name: str, version: str, dry_run: bool = False) -> int: + # Get package directories + changes_dir = PROJECT_ROOT_DIR / "clients" / package_name / ".changes" + next_release_dir = changes_dir / "next-release" + + if not changes_dir.exists(): + print(f"Error: No .changes directory found for package: {package_name}") + return 1 + + # Collect summary and changes from next-release + summary = collect_next_release_summary(next_release_dir) + changes = collect_next_release_changes(next_release_dir) + + if not changes: + print( + f"No changelog entries found in {next_release_dir}.\n" + "Use 'python scripts/changelog/new-entry.py' to create entries first" + ) + return 1 + + print(f"Found {len(changes)} changelog entries for {package_name} v{version}:") + + if summary: + print(f"\n SUMMARY: {summary}\n") + + for change in changes: + change_type = change.get("type", "unknown").upper() + description = change.get("description", "No description") + print(f" {change_type}: {description}") + + if dry_run: + print("\n[DRY RUN] Would create version file and remove next-release entries") + return 0 + + # Create version file + try: + version_file = create_version_file(changes_dir, version, changes, summary) + print(f"\nCreated version file: {version_file}") + except Exception as e: + print(f"Error creating version file: {e}") + return 1 + + # Clean up next-release directory + removed_count = cleanup_next_release_dir(next_release_dir) + print(f"Removed {removed_count} changelog entries from next-release") + print(f"Successfully created release {version} for {package_name}") + + return 0 + + +def main() -> int: + parser = argparse.ArgumentParser( + description="Create a new release by consolidating changelog entries" + ) + parser.add_argument("-p", "--package", required=True, help="Package name") + parser.add_argument( + "-v", "--version", required=True, help="Release version (e.g., 1.0.0)" + ) + parser.add_argument( + "--dry-run", + action="store_true", + help="Show what would be done without making changes", + ) + + args = parser.parse_args() + + # Basic version format validation + if not bool(re.match(VERSION_PATTERN, args.version)): + print("Error: Version must be in format x.y.z (e.g., 1.2.3)") + return 1 + + return create_new_release( + package_name=args.package, + version=args.version, + dry_run=args.dry_run, + ) + + +if __name__ == "__main__": + exit(main()) diff --git a/scripts/changelog/render.py b/scripts/changelog/render.py new file mode 100755 index 0000000..057118a --- /dev/null +++ b/scripts/changelog/render.py @@ -0,0 +1,156 @@ +#!/usr/bin/env python3 +# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +""" +Render changelog entries from version JSON files using Jinja templates. +""" + +import argparse +import json +import re +import sys +from pathlib import Path +from typing import IO, Any + +from jinja2 import Template + +PROJECT_ROOT_DIR = Path(__file__).resolve().parent.parent.parent + +TEMPLATES_DIR = PROJECT_ROOT_DIR / "scripts" / "changelog" / "templates" +DEFAULT_TEMPLATE_NAME = "PACKAGE" +VERSION_FILE_PATTERN = r"^\d+\.\d+\.\d+\.json$" + + +def get_sorted_versions(changes_dir: Path) -> list[str]: + """Get sorted list of version numbers from .changes directory.""" + version_pattern = re.compile(VERSION_FILE_PATTERN) + versions: list[str] = [] + + for file in changes_dir.iterdir(): + if file.is_file() and version_pattern.match(file.name): + versions.append(file.stem) + + # Sort by semantic version (oldest first) + versions.sort(key=lambda v: [int(x) for x in v.split(".")]) + return versions + + +def load_package_releases(changes_dir: Path) -> dict[str, dict[str, Any]]: + """Load all changelog entries from version JSON files.""" + releases: dict[str, dict[str, Any]] = {} + + for version_number in get_sorted_versions(changes_dir): + filename = changes_dir / f"{version_number}.json" + try: + with open(filename) as f: + data = json.load(f) + # Restructure data to match PACKAGE template expectations + changes = data.get("changes", []) + formatted_changes = [ + { + "type": change.get("type", "enhancement"), + "description": change.get("description", ""), + } + for change in changes + ] + + release_data: dict[str, Any] = { + "changes": formatted_changes, + } + + # Include summary if present + if "summary" in data: + release_data["summary"] = data["summary"] + + releases[version_number] = release_data + except (OSError, json.JSONDecodeError) as e: + print(f"Warning: Could not process {filename}: {e}", file=sys.stderr) + continue + + return releases + + +def render_changes( + changes: dict[str, dict[str, Any]], out: IO[str], template_contents: str +) -> None: + """Render changelog using Jinja template.""" + # Reverse order to show newest first + context: dict[str, Any] = {"releases": reversed(list(changes.items()))} + + template = Template(template_contents) + + result = template.render(**context) + out.write(result) + + +def render_package_changelog( + package_name: str, template_name: str | None = None, output_path: Path | None = None +) -> int: + # Determine changes directory from package name + package_dir = PROJECT_ROOT_DIR / "clients" / package_name + changes_dir = package_dir / ".changes" + + if not changes_dir.exists(): + print(f"No .changes directory found for package: {package_name}") + return 1 + + # Load changes from the directory + changes: dict[str, dict[str, Any]] = load_package_releases(changes_dir) + + if not changes: + print(f"No version JSON files found in {changes_dir}") + return 1 + + # Get template contents + template_path = TEMPLATES_DIR / (template_name or DEFAULT_TEMPLATE_NAME) + + if not template_path.exists(): + print(f"Template not found: {template_path}") + return 1 + + try: + with open(template_path) as f: + template_contents = f.read() + except OSError as e: + print(f"Error reading template {template_path}: {e}") + return 1 + + # Render to output + if output_path: + output_path.parent.mkdir(parents=True, exist_ok=True) + with open(output_path, "w") as f: + render_changes(changes, f, template_contents) + print(f"Changelog written to: {output_path}") + else: + render_changes(changes, sys.stdout, template_contents) + + return 0 + + +def main() -> int: + parser = argparse.ArgumentParser( + description="Render package changelog from changes files" + ) + parser.add_argument( + "-p", + "--package", + required=True, + help="Package name (looks in clients//.changes)", + ) + parser.add_argument( + "-t", + "--template", + help=f"Template name (looks in scripts/changelog/templates/, defaults to '{DEFAULT_TEMPLATE_NAME}')", + ) + parser.add_argument( + "-o", "--output", type=Path, help="Output file path (default: stdout)" + ) + + args = parser.parse_args() + return render_package_changelog( + package_name=args.package, template_name=args.template, output_path=args.output + ) + + +if __name__ == "__main__": + exit(main()) diff --git a/scripts/changelog/templates/PACKAGE b/scripts/changelog/templates/PACKAGE new file mode 100644 index 0000000..2fd7736 --- /dev/null +++ b/scripts/changelog/templates/PACKAGE @@ -0,0 +1,34 @@ +# Changelog +{% for release, changes in releases %} +## v{{ release }} +{%- if changes.summary %} + +{{ changes.summary }} +{%- endif %} +{%- for type_name in ['api-change', 'breaking', 'feature', 'enhancement', 'bugfix', 'dependency'] %} +{%- for change in changes.changes if change.type == type_name %} +{%- if loop.first %} +{%- if type_name == 'api-change' %} + +### API Changes +{%- elif type_name == 'breaking' %} + +### Breaking Changes +{%- elif type_name == 'feature' %} + +### Features +{%- elif type_name == 'enhancement' %} + +### Enhancements +{%- elif type_name == 'bugfix' %} + +### Bug fixes +{%- elif type_name == 'dependency' %} + +### Dependencies +{%- endif %} +{%- endif %} +* {{ change.description }} +{%- endfor %} +{%- endfor %} +{% endfor %}