File size: 9,049 Bytes
05b45a5
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
#!/usr/bin/env python3
"""
Version Update Script

This script reads the version from the VERSION file and updates references
in pyproject.toml, the Helm chart, and README.md.
"""

import re
from pathlib import Path

import yaml

# Get the project root directory
ROOT_DIR = Path(__file__).parent.parent

# --- Configuration ---
VERSION_FILE = ROOT_DIR / "VERSION"
PYPROJECT_FILE = ROOT_DIR / "pyproject.toml"
HELM_CHART_FILE = ROOT_DIR / "charts" / "kokoro-fastapi" / "Chart.yaml"
README_FILE = ROOT_DIR / "README.md"
# --- End Configuration ---


def update_pyproject(version: str):
    """Updates the version in pyproject.toml"""
    if not PYPROJECT_FILE.exists():
        print(f"Skipping: {PYPROJECT_FILE} not found.")
        return

    try:
        content = PYPROJECT_FILE.read_text()
        # Regex to find and capture current version = "X.Y.Z" under [project]
        pattern = r'(^\[project\]\s*(?:.*\s)*?version\s*=\s*)"([^"]+)"'
        match = re.search(pattern, content, flags=re.MULTILINE)

        if not match:
            print(f"Warning: Version pattern not found in {PYPROJECT_FILE}")
            return

        current_version = match.group(2)
        if current_version == version:
            print(f"Already up-to-date: {PYPROJECT_FILE} (version {version})")
        else:
            # Perform replacement
            new_content = re.sub(
                pattern, rf'\1"{version}"', content, count=1, flags=re.MULTILINE
            )
            PYPROJECT_FILE.write_text(new_content)
            print(f"Updated {PYPROJECT_FILE} from {current_version} to {version}")

    except Exception as e:
        print(f"Error processing {PYPROJECT_FILE}: {e}")


def update_helm_chart(version: str):
    """Updates the version and appVersion in the Helm chart"""
    if not HELM_CHART_FILE.exists():
        print(f"Skipping: {HELM_CHART_FILE} not found.")
        return

    try:
        content = HELM_CHART_FILE.read_text()
        original_content = content
        updated_count = 0

        # Update 'version:' line (unquoted)
        # Looks for 'version:' followed by optional whitespace and the version number
        version_pattern = r"^(version:\s*)(\S+)"
        current_version_match = re.search(version_pattern, content, flags=re.MULTILINE)
        if current_version_match and current_version_match.group(2) != version:
            content = re.sub(
                version_pattern,
                rf"\g<1>{version}",
                content,
                count=1,
                flags=re.MULTILINE,
            )
            print(
                f"Updating 'version' in {HELM_CHART_FILE} from {current_version_match.group(2)} to {version}"
            )
            updated_count += 1
        elif current_version_match:
            print(f"Already up-to-date: 'version' in {HELM_CHART_FILE} is {version}")
        else:
            print(f"Warning: 'version:' pattern not found in {HELM_CHART_FILE}")

        # Update 'appVersion:' line (quoted or unquoted)
        # Looks for 'appVersion:' followed by optional whitespace, optional quote, the version, optional quote
        app_version_pattern = r"^(appVersion:\s*)(\"?)([^\"\s]+)(\"?)"
        current_app_version_match = re.search(
            app_version_pattern, content, flags=re.MULTILINE
        )

        if current_app_version_match:
            leading_whitespace = current_app_version_match.group(
                1
            )  # e.g., "appVersion: "
            opening_quote = current_app_version_match.group(2)  # e.g., '"' or ''
            current_app_ver = current_app_version_match.group(3)  # e.g., '0.2.0'
            closing_quote = current_app_version_match.group(4)  # e.g., '"' or ''

            # Check if quotes were consistent (both present or both absent)
            if opening_quote != closing_quote:
                print(
                    f"Warning: Inconsistent quotes found for appVersion in {HELM_CHART_FILE}. Skipping update for this line."
                )
            elif (
                current_app_ver == version and opening_quote == '"'
            ):  # Check if already correct *and* quoted
                print(
                    f"Already up-to-date: 'appVersion' in {HELM_CHART_FILE} is \"{version}\""
                )
            else:
                # Always replace with the quoted version
                replacement = f'{leading_whitespace}"{version}"'  # Ensure quotes
                original_display = f"{opening_quote}{current_app_ver}{closing_quote}"  # How it looked before
                target_display = f'"{version}"'  # How it should look

                # Only report update if the displayed value actually changes
                if original_display != target_display:
                    content = re.sub(
                        app_version_pattern,
                        replacement,
                        content,
                        count=1,
                        flags=re.MULTILINE,
                    )
                    print(
                        f"Updating 'appVersion' in {HELM_CHART_FILE} from {original_display} to {target_display}"
                    )
                    updated_count += 1
                else:
                    # It matches the target version but might need quoting fixed silently if we didn't update
                    # Or it was already correct. Check if content changed. If not, report up-to-date.
                    if not (
                        content != original_content and updated_count > 0
                    ):  # Avoid double message if version also changed
                        print(
                            f"Already up-to-date: 'appVersion' in {HELM_CHART_FILE} is {target_display}"
                        )

        else:
            print(f"Warning: 'appVersion:' pattern not found in {HELM_CHART_FILE}")

        # Write back only if changes were made
        if content != original_content:
            HELM_CHART_FILE.write_text(content)
            # Confirmation message printed above during the specific update
        elif updated_count == 0 and current_version_match and current_app_version_match:
            # If no updates were made but patterns were found, confirm it's up-to-date overall
            print(f"Already up-to-date: {HELM_CHART_FILE} (version {version})")

    except Exception as e:
        print(f"Error processing {HELM_CHART_FILE}: {e}")


def update_readme(version_with_v: str):
    """Updates Docker image tags in README.md"""
    if not README_FILE.exists():
        print(f"Skipping: {README_FILE} not found.")
        return

    try:
        content = README_FILE.read_text()
        # Regex to find and capture current ghcr.io/.../kokoro-fastapi-(cpu|gpu):vX.Y.Z
        pattern = r"(ghcr\.io/remsky/kokoro-fastapi-(?:cpu|gpu)):(v\d+\.\d+\.\d+)"
        matches = list(re.finditer(pattern, content))  # Find all occurrences

        if not matches:
            print(f"Warning: Docker image tag pattern not found in {README_FILE}")
        else:
            updated_needed = False
            for match in matches:
                current_tag = match.group(2)
                if current_tag != version_with_v:
                    updated_needed = True
                    break  # Only need one mismatch to trigger update

            if updated_needed:
                # Perform replacement on all occurrences
                new_content = re.sub(pattern, rf"\1:{version_with_v}", content)
                README_FILE.write_text(new_content)
                print(f"Updated Docker image tags in {README_FILE} to {version_with_v}")
            else:
                print(
                    f"Already up-to-date: Docker image tags in {README_FILE} (version {version_with_v})"
                )

        # Check for ':latest' tag usage remains the same
        if ":latest" in content:
            print(
                f"Warning: Found ':latest' tag in {README_FILE}. Consider updating manually if needed."
            )

    except Exception as e:
        print(f"Error processing {README_FILE}: {e}")


def main():
    # Read the version from the VERSION file
    if not VERSION_FILE.exists():
        print(f"Error: {VERSION_FILE} not found.")
        return

    try:
        version = VERSION_FILE.read_text().strip()
        if not re.match(r"^\d+\.\d+\.\d+$", version):
            print(
                f"Error: Invalid version format '{version}' in {VERSION_FILE}. Expected X.Y.Z"
            )
            return
    except Exception as e:
        print(f"Error reading {VERSION_FILE}: {e}")
        return

    print(f"Read version: {version} from {VERSION_FILE}")
    print("-" * 20)

    # Prepare versions (with and without 'v')
    version_plain = version
    version_with_v = f"v{version}"

    # Update files
    update_pyproject(version_plain)
    update_helm_chart(version_plain)
    update_readme(version_with_v)

    print("-" * 20)
    print("Version update script finished.")


if __name__ == "__main__":
    main()