Compare commits
5
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ceabd258b2 | ||
|
|
964e2e909d | ||
|
|
8bfa266661 | ||
|
|
4ba7cfaea1 | ||
|
|
1c3a2e2ffe |
@@ -15,13 +15,6 @@ jobs:
|
|||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v6
|
uses: actions/checkout@v6
|
||||||
|
|
||||||
- name: Download Latest ffxiv_dx11.exe
|
|
||||||
id: downloader-latest
|
|
||||||
uses: WorkingRobot/ffxiv-downloader@v8
|
|
||||||
with:
|
|
||||||
output-path: latest
|
|
||||||
regex: '^ffxiv_dx11\.exe$'
|
|
||||||
|
|
||||||
- name: Set up Python
|
- name: Set up Python
|
||||||
uses: actions/setup-python@v6
|
uses: actions/setup-python@v6
|
||||||
with:
|
with:
|
||||||
@@ -30,14 +23,20 @@ jobs:
|
|||||||
|
|
||||||
- name: Fetch Patch Info and Update Versions
|
- name: Fetch Patch Info and Update Versions
|
||||||
id: versions
|
id: versions
|
||||||
if: steps.downloader-latest.outputs.updated == 'true' || github.event_name == 'workflow_dispatch'
|
|
||||||
run: python automation/ffxiv_info.py >> $GITHUB_OUTPUT
|
run: python automation/ffxiv_info.py >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
- name: Download Latest ffxiv_dx11.exe
|
||||||
|
if: steps.versions.outputs.is_new == 'true'
|
||||||
|
uses: WorkingRobot/ffxiv-downloader@v8
|
||||||
|
with:
|
||||||
|
output-path: latest
|
||||||
|
regex: '^ffxiv_dx11\.exe$'
|
||||||
|
|
||||||
- name: Download Previous ffxiv_dx11.exe
|
- name: Download Previous ffxiv_dx11.exe
|
||||||
if: steps.versions.outputs.is_new == 'true'
|
if: steps.versions.outputs.is_new == 'true'
|
||||||
uses: WorkingRobot/ffxiv-downloader@v8
|
uses: WorkingRobot/ffxiv-downloader@v8
|
||||||
with:
|
with:
|
||||||
version: ${{ steps.versions.outputs.date_prev }}
|
version: ${{ steps.versions.outputs.thaliak_version_prev }}
|
||||||
output-path: previous
|
output-path: previous
|
||||||
regex: '^ffxiv_dx11\.exe$'
|
regex: '^ffxiv_dx11\.exe$'
|
||||||
|
|
||||||
@@ -78,8 +77,8 @@ jobs:
|
|||||||
uses: peter-evans/create-pull-request@v8
|
uses: peter-evans/create-pull-request@v8
|
||||||
with:
|
with:
|
||||||
token: ${{ secrets.GITHUB_TOKEN }}
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
commit-message: "Update for ${{ steps.versions.outputs.retail_new }} (${{ steps.versions.outputs.date_new }})"
|
commit-message: "Update for ${{ steps.versions.outputs.retail_new }} (${{ steps.versions.outputs.thaliak_version_new }})"
|
||||||
title: "Update for ${{ steps.versions.outputs.retail_new }} (${{ steps.versions.outputs.date_new }})"
|
title: "Update for ${{ steps.versions.outputs.retail_new }} (${{ steps.versions.outputs.thaliak_version_new }})"
|
||||||
body: |
|
body: |
|
||||||
This automatic update contains the following changes:
|
This automatic update contains the following changes:
|
||||||
- Updated `ffxiv_versions_global.json`
|
- Updated `ffxiv_versions_global.json`
|
||||||
|
|||||||
+67
-43
@@ -79,12 +79,6 @@ def load_versions() -> List[dict]:
|
|||||||
return []
|
return []
|
||||||
|
|
||||||
|
|
||||||
def get_latest_version_entry() -> Optional[dict]:
|
|
||||||
"""Returns the most recent version entry from the version file."""
|
|
||||||
data = load_versions()
|
|
||||||
return data[-1] if data else None
|
|
||||||
|
|
||||||
|
|
||||||
def fetch_latest_thaliak_patch() -> Optional[str]:
|
def fetch_latest_thaliak_patch() -> Optional[str]:
|
||||||
"""
|
"""
|
||||||
Fetches the latest date-based version string from the Thaliak API.
|
Fetches the latest date-based version string from the Thaliak API.
|
||||||
@@ -131,8 +125,36 @@ def extract_patch_version(html):
|
|||||||
if not html or not (m := re.search(r"patch\s+(\d+\.\d+)", html, re.I)):
|
if not html or not (m := re.search(r"patch\s+(\d+\.\d+)", html, re.I)):
|
||||||
return None
|
return None
|
||||||
v = m.group(1)
|
v = m.group(1)
|
||||||
v = v + "0" if len(v.split(".")[1]) == 1 else v
|
if len(v.split(".")[1]) == 1:
|
||||||
return v + "h" if "HotFixes" in html else v
|
v += "0"
|
||||||
|
return v
|
||||||
|
|
||||||
|
|
||||||
|
def get_next_hotfix_version(base_v, existing_versions):
|
||||||
|
matching = [
|
||||||
|
v["retail_version"]
|
||||||
|
for v in existing_versions
|
||||||
|
if v["retail_version"].startswith(base_v)
|
||||||
|
]
|
||||||
|
hotfixes = [v for v in matching if "h" in v]
|
||||||
|
if not hotfixes:
|
||||||
|
return f"{base_v}h"
|
||||||
|
|
||||||
|
# Extract numeric suffix: h -> 1, h2 -> 2, etc.
|
||||||
|
nums = [int(h.split("h")[1]) if h.split("h")[1].isdigit() else 1 for h in hotfixes]
|
||||||
|
return f"{base_v}h{max(nums) + 1}"
|
||||||
|
|
||||||
|
|
||||||
|
def determine_retail_version(base_v, existing_versions, thaliak_version_new):
|
||||||
|
matching = [v for v in existing_versions if v["retail_version"].startswith(base_v)]
|
||||||
|
latest = matching[-1] if matching else None
|
||||||
|
|
||||||
|
# If we have a record for this base version but the Thaliak version is new, it's a hotfix.
|
||||||
|
if latest and thaliak_version_new and thaliak_version_new != latest["version_string"]:
|
||||||
|
return get_next_hotfix_version(base_v, existing_versions)
|
||||||
|
|
||||||
|
return latest["retail_version"] if latest else base_v
|
||||||
|
|
||||||
|
|
||||||
def is_maintenance_post(title):
|
def is_maintenance_post(title):
|
||||||
"""Returns True if the title represents a primary world maintenance post."""
|
"""Returns True if the title represents a primary world maintenance post."""
|
||||||
@@ -140,14 +162,7 @@ def is_maintenance_post(title):
|
|||||||
return "all worlds" in t and "maintenance" in t and "follow-up" not in t
|
return "all worlds" in t and "maintenance" in t and "follow-up" not in t
|
||||||
|
|
||||||
|
|
||||||
def format_retail_version(v, count):
|
def scrape_latest_maintenance(thaliak_version_new=None):
|
||||||
"""Formats the version with hotfix suffixes: 7.41, 7.41h, 7.41h2, etc."""
|
|
||||||
if count <= 1:
|
|
||||||
return v
|
|
||||||
return f"{v}h{count - 1 if count > 2 else ''}"
|
|
||||||
|
|
||||||
|
|
||||||
def scrape_latest_maintenance():
|
|
||||||
"""
|
"""
|
||||||
Scrapes the Lodestone to find the most recent 'All Worlds Maintenance' post.
|
Scrapes the Lodestone to find the most recent 'All Worlds Maintenance' post.
|
||||||
"""
|
"""
|
||||||
@@ -164,6 +179,7 @@ def scrape_latest_maintenance():
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
news_items = parse_lodestone_news_list(html)
|
news_items = parse_lodestone_news_list(html)
|
||||||
|
existing_versions = load_versions()
|
||||||
|
|
||||||
for item in news_items:
|
for item in news_items:
|
||||||
title = item["title"]
|
title = item["title"]
|
||||||
@@ -173,12 +189,18 @@ def scrape_latest_maintenance():
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
detail_html = fetch_url(link)
|
detail_html = fetch_url(link)
|
||||||
version = extract_patch_version(detail_html)
|
if not detail_html:
|
||||||
|
continue
|
||||||
|
|
||||||
if version:
|
base_version = extract_patch_version(detail_html)
|
||||||
# We assume the first (most recent) valid maintenance post
|
if not base_version:
|
||||||
# with a patch version is our target.
|
continue
|
||||||
return {"retail_version": version, "title": title, "url": link}
|
|
||||||
|
retail_version = determine_retail_version(
|
||||||
|
base_version, existing_versions, thaliak_version_new
|
||||||
|
)
|
||||||
|
|
||||||
|
return {"retail_version": retail_version, "title": title, "url": link}
|
||||||
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
@@ -187,15 +209,15 @@ def get_version_info() -> Optional[dict]:
|
|||||||
"""
|
"""
|
||||||
Returns the latest version information by combining Thaliak and Lodestone data.
|
Returns the latest version information by combining Thaliak and Lodestone data.
|
||||||
"""
|
"""
|
||||||
date_new = fetch_latest_thaliak_patch()
|
thaliak_version_new = fetch_latest_thaliak_patch()
|
||||||
maintenance = scrape_latest_maintenance()
|
maintenance = scrape_latest_maintenance(thaliak_version_new=thaliak_version_new)
|
||||||
|
|
||||||
if not maintenance and not date_new:
|
if not maintenance and not thaliak_version_new:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"retail_version": maintenance["retail_version"] if maintenance else "Unknown",
|
"retail_version": maintenance["retail_version"] if maintenance else "Unknown",
|
||||||
"version_string": date_new,
|
"version_string": thaliak_version_new,
|
||||||
"title": maintenance["title"] if maintenance else "Unknown",
|
"title": maintenance["title"] if maintenance else "Unknown",
|
||||||
"url": maintenance["url"] if maintenance else "Unknown",
|
"url": maintenance["url"] if maintenance else "Unknown",
|
||||||
}
|
}
|
||||||
@@ -204,27 +226,26 @@ def get_version_info() -> Optional[dict]:
|
|||||||
def get_patch_context():
|
def get_patch_context():
|
||||||
"""
|
"""
|
||||||
Returns a dictionary containing:
|
Returns a dictionary containing:
|
||||||
- retail_prev, date_prev: The latest registered version.
|
- retail_prev, thaliak_version_prev: The latest registered version.
|
||||||
- retail_new, date_new: The latest versions found externally.
|
- retail_new, thaliak_version_new: The latest versions found externally.
|
||||||
- is_new: True if date_new is not in the registry.
|
- is_new: True if thaliak_version_new is not in the registry.
|
||||||
"""
|
"""
|
||||||
versions = load_versions()
|
versions = load_versions()
|
||||||
prev = versions[-1] if versions else None
|
prev = versions[-1] if versions else None
|
||||||
|
|
||||||
date_new = fetch_latest_thaliak_patch()
|
thaliak_version_new = fetch_latest_thaliak_patch()
|
||||||
|
maintenance = scrape_latest_maintenance(thaliak_version_new=thaliak_version_new)
|
||||||
maintenance = scrape_latest_maintenance()
|
|
||||||
retail_new = maintenance["retail_version"] if maintenance else None
|
retail_new = maintenance["retail_version"] if maintenance else None
|
||||||
|
|
||||||
is_new = False
|
is_new = thaliak_version_new is not None and not any(
|
||||||
if date_new:
|
v["version_string"] == thaliak_version_new for v in versions
|
||||||
is_new = not any(v["version_string"] == date_new for v in versions)
|
)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"retail_prev": prev["retail_version"] if prev else None,
|
"retail_prev": prev["retail_version"] if prev else None,
|
||||||
"date_prev": prev["version_string"] if prev else None,
|
"thaliak_version_prev": prev["version_string"] if prev else None,
|
||||||
"retail_new": retail_new,
|
"retail_new": retail_new,
|
||||||
"date_new": date_new,
|
"thaliak_version_new": thaliak_version_new,
|
||||||
"is_new": is_new,
|
"is_new": is_new,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -236,23 +257,26 @@ def update_and_get_info():
|
|||||||
"""
|
"""
|
||||||
ctx = get_patch_context()
|
ctx = get_patch_context()
|
||||||
|
|
||||||
if ctx["date_prev"]:
|
if ctx["thaliak_version_prev"]:
|
||||||
print(f"date_prev={ctx['date_prev']}")
|
print(f"thaliak_version_prev={ctx['thaliak_version_prev']}")
|
||||||
if ctx["retail_prev"]:
|
if ctx["retail_prev"]:
|
||||||
print(f"retail_prev={ctx['retail_prev']}")
|
print(f"retail_prev={ctx['retail_prev']}")
|
||||||
if ctx["date_new"]:
|
if ctx["thaliak_version_new"]:
|
||||||
print(f"date_new={ctx['date_new']}")
|
print(f"thaliak_version_new={ctx['thaliak_version_new']}")
|
||||||
if ctx["retail_new"]:
|
if ctx["retail_new"]:
|
||||||
print(f"retail_new={ctx['retail_new']}")
|
print(f"retail_new={ctx['retail_new']}")
|
||||||
print(f"is_new={'true' if ctx['is_new'] else 'false'}")
|
print(f"is_new={'true' if ctx['is_new'] else 'false'}")
|
||||||
|
|
||||||
if ctx["is_new"] and ctx["date_new"] and ctx["retail_new"]:
|
if ctx["is_new"] and ctx["thaliak_version_new"] and ctx["retail_new"]:
|
||||||
data = load_versions()
|
data = load_versions()
|
||||||
data.append(
|
data.append(
|
||||||
{"retail_version": ctx["retail_new"], "version_string": ctx["date_new"]}
|
{
|
||||||
|
"retail_version": ctx["retail_new"],
|
||||||
|
"version_string": ctx["thaliak_version_new"],
|
||||||
|
}
|
||||||
)
|
)
|
||||||
print(
|
print(
|
||||||
f"Added new version mapping: {ctx['retail_new']} -> {ctx['date_new']}",
|
f"Added new version mapping: {ctx['retail_new']} -> {ctx['thaliak_version_new']}",
|
||||||
file=sys.stderr,
|
file=sys.stderr,
|
||||||
)
|
)
|
||||||
with open(VERSIONS_FILE, "w") as f:
|
with open(VERSIONS_FILE, "w") as f:
|
||||||
|
|||||||
@@ -134,5 +134,9 @@
|
|||||||
{
|
{
|
||||||
"retail_version": "7.45h",
|
"retail_version": "7.45h",
|
||||||
"version_string": "2026.03.07.0000.0000"
|
"version_string": "2026.03.07.0000.0000"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"retail_version": "7.45h2",
|
||||||
|
"version_string": "2026.03.17.0000.0000"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user