Skip to content

Commit 4ba7cfa

Browse files
committed
More robust hotfix version handling in automation
1 parent 1c3a2e2 commit 4ba7cfa

2 files changed

Lines changed: 70 additions & 46 deletions

File tree

‎.github/workflows/minor_patch_diff.yml‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ jobs:
3737
if: steps.versions.outputs.is_new == 'true'
3838
uses: WorkingRobot/ffxiv-downloader@v8
3939
with:
40-
version: ${{ steps.versions.outputs.date_prev }}
40+
version: ${{ steps.versions.outputs.thaliak_version_prev }}
4141
output-path: previous
4242
regex: '^ffxiv_dx11\.exe$'
4343

@@ -78,8 +78,8 @@ jobs:
7878
uses: peter-evans/create-pull-request@v8
7979
with:
8080
token: ${{ secrets.GITHUB_TOKEN }}
81-
commit-message: "Update for ${{ steps.versions.outputs.retail_new }} (${{ steps.versions.outputs.date_new }})"
82-
title: "Update for ${{ steps.versions.outputs.retail_new }} (${{ steps.versions.outputs.date_new }})"
81+
commit-message: "Update for ${{ steps.versions.outputs.retail_new }} (${{ steps.versions.outputs.thaliak_version_new }})"
82+
title: "Update for ${{ steps.versions.outputs.retail_new }} (${{ steps.versions.outputs.thaliak_version_new }})"
8383
body: |
8484
This automatic update contains the following changes:
8585
- Updated `ffxiv_versions_global.json`

‎automation/ffxiv_info.py‎

Lines changed: 67 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -79,12 +79,6 @@ def load_versions() -> List[dict]:
7979
return []
8080

8181

82-
def get_latest_version_entry() -> Optional[dict]:
83-
"""Returns the most recent version entry from the version file."""
84-
data = load_versions()
85-
return data[-1] if data else None
86-
87-
8882
def fetch_latest_thaliak_patch() -> Optional[str]:
8983
"""
9084
Fetches the latest date-based version string from the Thaliak API.
@@ -131,23 +125,44 @@ def extract_patch_version(html):
131125
if not html or not (m := re.search(r"patch\s+(\d+\.\d+)", html, re.I)):
132126
return None
133127
v = m.group(1)
134-
v = v + "0" if len(v.split(".")[1]) == 1 else v
135-
return v + "h" if "HotFixes" in html else v
128+
if len(v.split(".")[1]) == 1:
129+
v += "0"
130+
return v
131+
132+
133+
def get_next_hotfix_version(base_v, existing_versions):
134+
matching = [
135+
v["retail_version"]
136+
for v in existing_versions
137+
if v["retail_version"].startswith(base_v)
138+
]
139+
hotfixes = [v for v in matching if "h" in v]
140+
if not hotfixes:
141+
return f"{base_v}h"
142+
143+
# Extract numeric suffix: h -> 1, h2 -> 2, etc.
144+
nums = [int(h.split("h")[1]) if h.split("h")[1].isdigit() else 1 for h in hotfixes]
145+
return f"{base_v}h{max(nums) + 1}"
146+
147+
148+
def determine_retail_version(base_v, existing_versions, thaliak_version_new):
149+
matching = [v for v in existing_versions if v["retail_version"].startswith(base_v)]
150+
latest = matching[-1] if matching else None
151+
152+
# If we have a record for this base version but the Thaliak version is new, it's a hotfix.
153+
if latest and thaliak_version_new and thaliak_version_new != latest["version_string"]:
154+
return get_next_hotfix_version(base_v, existing_versions)
155+
156+
return latest["retail_version"] if latest else base_v
157+
136158

137159
def is_maintenance_post(title):
138160
"""Returns True if the title represents a primary world maintenance post."""
139161
t = title.lower()
140162
return "all worlds" in t and "maintenance" in t and "follow-up" not in t
141163

142164

143-
def format_retail_version(v, count):
144-
"""Formats the version with hotfix suffixes: 7.41, 7.41h, 7.41h2, etc."""
145-
if count <= 1:
146-
return v
147-
return f"{v}h{count - 1 if count > 2 else ''}"
148-
149-
150-
def scrape_latest_maintenance():
165+
def scrape_latest_maintenance(thaliak_version_new=None):
151166
"""
152167
Scrapes the Lodestone to find the most recent 'All Worlds Maintenance' post.
153168
"""
@@ -164,6 +179,7 @@ def scrape_latest_maintenance():
164179
return None
165180

166181
news_items = parse_lodestone_news_list(html)
182+
existing_versions = load_versions()
167183

168184
for item in news_items:
169185
title = item["title"]
@@ -173,12 +189,18 @@ def scrape_latest_maintenance():
173189
continue
174190

175191
detail_html = fetch_url(link)
176-
version = extract_patch_version(detail_html)
192+
if not detail_html:
193+
continue
177194

178-
if version:
179-
# We assume the first (most recent) valid maintenance post
180-
# with a patch version is our target.
181-
return {"retail_version": version, "title": title, "url": link}
195+
base_version = extract_patch_version(detail_html)
196+
if not base_version:
197+
continue
198+
199+
retail_version = determine_retail_version(
200+
base_version, existing_versions, thaliak_version_new
201+
)
202+
203+
return {"retail_version": retail_version, "title": title, "url": link}
182204

183205
return None
184206

@@ -187,15 +209,15 @@ def get_version_info() -> Optional[dict]:
187209
"""
188210
Returns the latest version information by combining Thaliak and Lodestone data.
189211
"""
190-
date_new = fetch_latest_thaliak_patch()
191-
maintenance = scrape_latest_maintenance()
212+
thaliak_version_new = fetch_latest_thaliak_patch()
213+
maintenance = scrape_latest_maintenance(thaliak_version_new=thaliak_version_new)
192214

193-
if not maintenance and not date_new:
215+
if not maintenance and not thaliak_version_new:
194216
return None
195217

196218
return {
197219
"retail_version": maintenance["retail_version"] if maintenance else "Unknown",
198-
"version_string": date_new,
220+
"version_string": thaliak_version_new,
199221
"title": maintenance["title"] if maintenance else "Unknown",
200222
"url": maintenance["url"] if maintenance else "Unknown",
201223
}
@@ -204,27 +226,26 @@ def get_version_info() -> Optional[dict]:
204226
def get_patch_context():
205227
"""
206228
Returns a dictionary containing:
207-
- retail_prev, date_prev: The latest registered version.
208-
- retail_new, date_new: The latest versions found externally.
209-
- is_new: True if date_new is not in the registry.
229+
- retail_prev, thaliak_version_prev: The latest registered version.
230+
- retail_new, thaliak_version_new: The latest versions found externally.
231+
- is_new: True if thaliak_version_new is not in the registry.
210232
"""
211233
versions = load_versions()
212234
prev = versions[-1] if versions else None
213235

214-
date_new = fetch_latest_thaliak_patch()
215-
216-
maintenance = scrape_latest_maintenance()
236+
thaliak_version_new = fetch_latest_thaliak_patch()
237+
maintenance = scrape_latest_maintenance(thaliak_version_new=thaliak_version_new)
217238
retail_new = maintenance["retail_version"] if maintenance else None
218239

219-
is_new = False
220-
if date_new:
221-
is_new = not any(v["version_string"] == date_new for v in versions)
240+
is_new = thaliak_version_new is not None and not any(
241+
v["version_string"] == thaliak_version_new for v in versions
242+
)
222243

223244
return {
224245
"retail_prev": prev["retail_version"] if prev else None,
225-
"date_prev": prev["version_string"] if prev else None,
246+
"thaliak_version_prev": prev["version_string"] if prev else None,
226247
"retail_new": retail_new,
227-
"date_new": date_new,
248+
"thaliak_version_new": thaliak_version_new,
228249
"is_new": is_new,
229250
}
230251

@@ -236,23 +257,26 @@ def update_and_get_info():
236257
"""
237258
ctx = get_patch_context()
238259

239-
if ctx["date_prev"]:
240-
print(f"date_prev={ctx['date_prev']}")
260+
if ctx["thaliak_version_prev"]:
261+
print(f"thaliak_version_prev={ctx['thaliak_version_prev']}")
241262
if ctx["retail_prev"]:
242263
print(f"retail_prev={ctx['retail_prev']}")
243-
if ctx["date_new"]:
244-
print(f"date_new={ctx['date_new']}")
264+
if ctx["thaliak_version_new"]:
265+
print(f"thaliak_version_new={ctx['thaliak_version_new']}")
245266
if ctx["retail_new"]:
246267
print(f"retail_new={ctx['retail_new']}")
247268
print(f"is_new={'true' if ctx['is_new'] else 'false'}")
248269

249-
if ctx["is_new"] and ctx["date_new"] and ctx["retail_new"]:
270+
if ctx["is_new"] and ctx["thaliak_version_new"] and ctx["retail_new"]:
250271
data = load_versions()
251272
data.append(
252-
{"retail_version": ctx["retail_new"], "version_string": ctx["date_new"]}
273+
{
274+
"retail_version": ctx["retail_new"],
275+
"version_string": ctx["thaliak_version_new"],
276+
}
253277
)
254278
print(
255-
f"Added new version mapping: {ctx['retail_new']} -> {ctx['date_new']}",
279+
f"Added new version mapping: {ctx['retail_new']} -> {ctx['thaliak_version_new']}",
256280
file=sys.stderr,
257281
)
258282
with open(VERSIONS_FILE, "w") as f:

0 commit comments

Comments
 (0)