Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,6 @@ dist/

# scratch workspace
/workspace

# Codegen sync changelog text, read by `make clients`
/CODEGEN_SYNC_CHANGELOG.md
7 changes: 6 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ SMITHY_PYTHON ?= ../smithy-python
# Clients per gradle build in `make clients`; lower it if the build runs out of heap.
BATCH_SIZE ?= 25

.PHONY: docs docs-serve docs-clean docs-install docs-lock venv gen clients publish-codegen aio-check static-analysis test-unit test-functional test-integration
.PHONY: docs docs-serve docs-clean docs-install docs-lock venv gen clients fragments publish-codegen aio-check static-analysis test-unit test-functional test-integration

# Each client and package runs in its own .venv. Dependencies outside this repo,
# including smithy-python, resolve from PyPI at the versions each one declares.
Expand Down Expand Up @@ -60,6 +60,11 @@ test-integration:
clients:
SMITHY_PYTHON=$(abspath $(SMITHY_PYTHON)) uv run python codegen/gen_client.py --all --pinned --batch-size $(BATCH_SIZE)

# Only for a smithy-python pointer move: writes a changelog fragment from
# CODEGEN_SYNC_CHANGELOG.md for every generated client.
fragments:
uv run --no-project --python $(PYTHON_VERSION) python codegen/gen_fragments.py
Comment on lines +65 to +66

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could fragments depend on clients? That keeps normal make clients unchanged, but makes sure fragments only get written after generation succeeds

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, this is a manual invocation so the developer could have run make clients and paused.

A small typo or error in fragments should not require rerunning client generation, which is time-consuming.


########################## Granular targets below ##########################

# Publishes the smithy-python codegen jars to the local Maven cache so `gen`
Expand Down
87 changes: 87 additions & 0 deletions codegen/gen_fragments.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
#!/usr/bin/env python3
"""Write a codegen sync changelog fragment for every generated client.

Usage: python codegen/gen_fragments.py
Only for a smithy-python pointer move; the text comes from CODEGEN_SYNC_CHANGELOG.md
in the repo root.
"""

import json
import re
import sys
import time
import tomllib
from pathlib import Path

CODEGEN_DIR = Path(__file__).resolve().parent
REPO = CODEGEN_DIR.parent
CLIENTS = REPO / "clients"
MODELS = CODEGEN_DIR / "aws-models"
POINTER = CODEGEN_DIR / "smithy-python.toml"
SYNC_CHANGELOG = REPO / "CODEGEN_SYNC_CHANGELOG.md"
SMITHY_PYTHON_COMMITS = "https://github.com/smithy-lang/smithy-python/commits"
# An older file is assumed to be left over from a previous sync.
SYNC_CHANGELOG_MAX_AGE_SECONDS = 8 * 60 * 60


def main() -> None:
previous, commit = _pointer_commits()
description = _description(previous, commit)
for name in _generated_clients():
_write_fragment(name, commit, description)


def _pointer_commits() -> tuple[str, str]:
with POINTER.open("rb") as f:
pointer = tomllib.load(f)
commits = []
for key in ("previous_commit", "commit"):
value = pointer.get(key)
if not isinstance(value, str) or not re.fullmatch(r"[0-9a-f]{40}", value):
sys.exit(
f"{POINTER}: {key} must be a full 40-character hash, got {value!r}"
)
commits.append(value)
previous, commit = commits
if previous == commit:
sys.exit(f"{POINTER}: previous_commit and commit must differ")
return previous, commit


def _description(previous: str, commit: str) -> str:
changes = f"{SMITHY_PYTHON_COMMITS}/{previous}..{commit}"
reminder = f"write the changelog text for this codegen sync of {changes}"
if not SYNC_CHANGELOG.exists():
SYNC_CHANGELOG.touch()
sys.exit(f"created empty {SYNC_CHANGELOG}; {reminder}")
age = time.time() - SYNC_CHANGELOG.stat().st_mtime
if age > SYNC_CHANGELOG_MAX_AGE_SECONDS:
sys.exit(f"{SYNC_CHANGELOG} is {age / 3600:.1f} hours old; {reminder}")
text = SYNC_CHANGELOG.read_text().strip()
if not text:
sys.exit(f"{SYNC_CHANGELOG} is empty; {reminder}")
link = f"[smithy-python {previous[:7]}..{commit[:7]}]({changes})"
return f"Codegen sync for {link}: {text}"


def _generated_clients() -> list[str]:
return [f"aws-sdk-{model.stem}" for model in sorted(MODELS.glob("*.json"))]


def _write_fragment(name: str, commit: str, description: str) -> None:
# Keyed by target commit: a rerun for the same pin overwrites, a later sync adds.
fragment = (
CLIENTS
/ name
/ ".changes"
/ "next-release"
/ f"{name}-enhancement-codegen-sync-{commit}.json"
)
fragment.parent.mkdir(parents=True, exist_ok=True)
entry = {"type": "enhancement", "description": description}
fragment.write_text(json.dumps(entry, indent=2) + "\n")
print(f"wrote {fragment}")


if __name__ == "__main__":
main()
3 changes: 3 additions & 0 deletions codegen/smithy-python.toml
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
# The smithy-python commit `make clients` builds codegen from. May be any commit
# reachable from the colocated smithy-python checkout, including a local one.
commit = "b8eceadb0573bd0c757def9a2b26fe7bf938b239"
# The commit this pointer moved from; with `commit` it bounds the range linked
# in codegen sync changelog entries.
previous_commit = "505312562bf78d45265145ae3ae9f874c00464d1"
Loading