Skip to content
Open
Show file tree
Hide file tree
Changes from 2 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
8 changes: 7 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -61,4 +61,10 @@ docs/_build/
.ipynb_checkpoints

.idea/
.vscode/
.vscode/

# Local environment files (secrets, never commit)
.env
.env.*
!.env.example
!.env.sample
96 changes: 88 additions & 8 deletions generate_frontend_sdk.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,88 @@
import os
import re
import subprocess
import sys
import shutil
import tempfile
from pathlib import Path

# OpenAPI spec URL from Kinde Frontend API
OPENAPI_SPEC_URL = "https://api-spec.kinde.com/kinde-frontend-api-spec.yaml"
OUTPUT_DIR = "kinde_sdk/frontend"
GENERATOR_DIR = "generator"
CONFIG_FILE = f"{GENERATOR_DIR}/frontend_config.yaml"


def _read_sdk_version() -> str:
"""
Read the canonical SDK version from ``kinde_sdk/_version.py``.

The OpenAPI generator's ``packageVersion`` is set from this value so that
artifacts which embed the version literally (e.g. user-agent headers in
the generated ``configuration.py``) stay in lockstep with the SDK.

The generated ``kinde_sdk/frontend/__init__.py`` doesn't keep a literal
copy of the version - it re-exports ``kinde_sdk._version.__version__``,
and ``make_version_dynamic`` (called below after generation) rewrites the
OpenAPI-emitted ``__version__ = "..."`` line back to that import on every
regeneration.
"""
version_path = Path(__file__).resolve().parent / "kinde_sdk" / "_version.py"
text = version_path.read_text(encoding="utf-8")
match = re.search(r'^__version__\s*=\s*["\']([^"\']+)["\']', text, re.MULTILINE)
if not match:
raise RuntimeError(
f"Could not parse __version__ from {version_path}; "
"the generator can't derive packageVersion."
)
return match.group(1)


SDK_VERSION = _read_sdk_version()
Comment thread
KomanRudden marked this conversation as resolved.
Outdated


DYNAMIC_VERSION_IMPORT = (
"from kinde_sdk._version import __version__ "
"# single source of truth; see kinde_sdk/_version.py"
)


def make_version_dynamic(init_file):
"""
Replace the OpenAPI-emitted ``__version__ = "X"`` line in the generated
``__init__.py`` with an import from ``kinde_sdk._version``, so the
sub-package never holds a literal version that can drift.

Idempotent: a no-op if the file already imports from ``kinde_sdk._version``.
"""
init_path = Path(init_file)
if not init_path.exists():
print(f"Warning: cannot rewrite __version__: {init_path} does not exist")
return

text = init_path.read_text(encoding="utf-8")

if "from kinde_sdk._version import __version__" in text:
print(f"{init_path} already imports __version__ from kinde_sdk._version")
return

new_text, n = re.subn(
r'^__version__\s*=\s*["\'][^"\']+["\']\s*$',
DYNAMIC_VERSION_IMPORT,
text,
count=1,
flags=re.MULTILINE,
)
if n == 0:
print(
f"Warning: could not find literal __version__ in {init_path} to replace; "
"is the OpenAPI generator template still emitting one?"
)
return

init_path.write_text(new_text, encoding="utf-8")
print(f"Rewrote __version__ in {init_path} to import from kinde_sdk._version")

def fix_imports(directory):
"""Fix import paths in generated Python files."""
print(f"Fixing imports in {directory}")
Expand Down Expand Up @@ -162,20 +235,23 @@ def restore_custom_files(backup_dir):
os.makedirs(GENERATOR_DIR)
print(f"Created directory: {GENERATOR_DIR}")

# Create frontend config.yaml if it doesn't exist
if not os.path.isfile(CONFIG_FILE):
config_content = """# openapi-generator-cli config for python frontend API
# Always (re)write the frontend config so packageVersion stays in lockstep
# with the SDK version on every regeneration; otherwise a stale local file
# would silently override the SDK version.
config_content = f"""# openapi-generator-cli config for python frontend API
# NOTE: this file is regenerated by generate_frontend_sdk.py on every run.
# packageVersion is derived from kinde_sdk/__init__.py's __version__.
Comment thread
KomanRudden marked this conversation as resolved.
Outdated

packageName: kinde_sdk.frontend
projectName: kinde-python-sdk
packageVersion: 2.0.0
packageVersion: {SDK_VERSION}
# It is recommended to use a released version of the generator.
# For example:
# generatorVersion: "7.13.0"
"""
with open(CONFIG_FILE, 'w') as f:
f.write(config_content)
print(f"Created config file: {CONFIG_FILE}")
with open(CONFIG_FILE, 'w') as f:
f.write(config_content)
print(f"Wrote config file: {CONFIG_FILE} (packageVersion={SDK_VERSION})")

# Create temporary directory for generation
with tempfile.TemporaryDirectory() as temp_dir:
Expand Down Expand Up @@ -232,7 +308,11 @@ def restore_custom_files(backup_dir):

# Fix imports in the frontend directory
fix_imports(OUTPUT_DIR)


# Replace the OpenAPI-emitted literal __version__ with an import from
# kinde_sdk._version, so the sub-package never drifts from the SDK.
make_version_dynamic(os.path.join(OUTPUT_DIR, "__init__.py"))

# Preserve custom imports
preserve_custom_imports()

Expand Down
83 changes: 80 additions & 3 deletions generate_management_sdk.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,79 @@
logger = logging.getLogger(__name__)


def _read_sdk_version() -> str:
"""
Read the canonical SDK version from ``kinde_sdk/_version.py``.

The OpenAPI generator's ``packageVersion`` is set from this value so that
artifacts which embed the version literally (e.g. user-agent headers in
the generated ``configuration.py``) stay in lockstep with the SDK.

The sub-package ``__init__.py`` files don't keep a literal copy of the
version - they re-export ``kinde_sdk._version.__version__``, and a
post-generation step in this script rewrites the OpenAPI-emitted
``__version__ = "..."`` line back to that import on every regeneration.
"""
version_path = Path(__file__).resolve().parent / "kinde_sdk" / "_version.py"
text = version_path.read_text(encoding="utf-8")
match = re.search(r'^__version__\s*=\s*["\']([^"\']+)["\']', text, re.MULTILINE)
if not match:
raise RuntimeError(
f"Could not parse __version__ from {version_path}; "
"the generator can't derive packageVersion."
)
return match.group(1)


SDK_VERSION = _read_sdk_version()


# Sentinel line written into the generated sub-package ``__init__.py`` in
# place of ``__version__ = "..."``. Keeping it as a module-level constant so
# both generator scripts (and any future ones) stay in lockstep.
DYNAMIC_VERSION_IMPORT = (
"from kinde_sdk._version import __version__ "
"# single source of truth; see kinde_sdk/_version.py"
)


def make_version_dynamic(init_file: Path) -> None:
Comment thread
KomanRudden marked this conversation as resolved.
Outdated
"""
Replace the OpenAPI-emitted ``__version__ = "X"`` line in a generated
``__init__.py`` with an import from ``kinde_sdk._version``, so the
sub-package never holds a literal version that can drift.

Idempotent: if the file already imports ``__version__`` from
``kinde_sdk._version``, this is a no-op.
"""
if not init_file.exists():
print(f"⚠️ Cannot rewrite __version__: {init_file} does not exist")
return

text = init_file.read_text(encoding="utf-8")

if "from kinde_sdk._version import __version__" in text:
print(f"✓ {init_file} already imports __version__ from kinde_sdk._version")
return

new_text, n = re.subn(
r'^__version__\s*=\s*["\'][^"\']+["\']\s*$',
DYNAMIC_VERSION_IMPORT,
text,
count=1,
flags=re.MULTILINE,
)
if n == 0:
print(
f"⚠️ Could not find a literal __version__ in {init_file} to replace; "
"is the OpenAPI generator template still emitting one?"
)
return

init_file.write_text(new_text, encoding="utf-8")
print(f"✓ Rewrote __version__ in {init_file} to import from kinde_sdk._version")


# =============================================================================
# SDK CONFIGURATION
# =============================================================================
Expand Down Expand Up @@ -316,7 +389,7 @@ def ensure_openapitools_config(config: Dict[str, Any]):
"additionalProperties": {
"packageName": config["package_name"],
"projectName": "kinde-python-sdk",
"packageVersion": "2.0.0",
"packageVersion": SDK_VERSION,
"library": "urllib3",
"generateSourceCodeOnly": True # CRITICAL: Only generate source code, not project templates
},
Expand Down Expand Up @@ -388,7 +461,7 @@ def generate_sdk(config: Dict[str, Any]) -> bool:
additional_props = ",".join([
f"packageName={config['package_name']}",
"projectName=kinde-python-sdk",
"packageVersion=2.0.0",
f"packageVersion={SDK_VERSION}",
"library=urllib3",
"generateSourceCodeOnly=true"
])
Expand Down Expand Up @@ -731,7 +804,11 @@ def generate_single_sdk(skip_tests: bool, no_diff: bool) -> bool:

# Step 2: Fix imports
fix_imports(config)


# Step 2b: Replace the OpenAPI-emitted literal __version__ with an import
# from kinde_sdk._version, so this sub-package never drifts from the SDK.
make_version_dynamic(Path(config["output_dir"]) / "__init__.py")

# Step 3: Add custom imports
add_custom_imports(config)

Expand Down
2 changes: 1 addition & 1 deletion kinde_sdk/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
from kinde_sdk.core.framework.null_framework import NullFramework
from kinde_sdk.core.session_management import KindeSessionManagement

__version__ = "2.2.0"
from kinde_sdk._version import __version__ # noqa: F401 (re-exported)

__all__ = [
"OAuth",
Expand Down
16 changes: 16 additions & 0 deletions kinde_sdk/_version.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
"""Single source of truth for the Kinde Python SDK version.

This module is intentionally minimal: it must not import anything else,
so it can be safely imported from any sub-package's ``__init__`` without
risking a circular import with ``kinde_sdk/__init__.py``.

The OpenAPI-generated ``kinde_sdk/management/__init__.py`` and
``kinde_sdk/frontend/__init__.py`` re-export this attribute, so all three
namespaces report the same version. The generator scripts
(``generate_management_sdk.py`` and ``generate_frontend_sdk.py``) read
this value to set the OpenAPI Generator's ``packageVersion``.

Bump this string on every release; nothing else needs touching.
"""

__version__ = "2.3.0"
2 changes: 1 addition & 1 deletion kinde_sdk/frontend/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
""" # noqa: E501


__version__ = "2.0.0"
from kinde_sdk._version import __version__ # single source of truth; see kinde_sdk/_version.py

# import apis into sdk package
from kinde_sdk.frontend.api.billing_api import BillingApi
Expand Down
2 changes: 1 addition & 1 deletion kinde_sdk/management/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
""" # noqa: E501


__version__ = "2.0.0"
from kinde_sdk._version import __version__ # single source of truth; see kinde_sdk/_version.py

# Define package exports
__all__ = [
Expand Down
2 changes: 1 addition & 1 deletion openapitools.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
"additionalProperties": {
"packageName": "kinde_sdk.management",
"projectName": "kinde-python-sdk",
"packageVersion": "2.0.0",
"packageVersion": "2.3.0",
Comment thread
KomanRudden marked this conversation as resolved.
Outdated
"library": "urllib3",
"generateSourceCodeOnly": true
},
Expand Down
Loading