-
Notifications
You must be signed in to change notification settings - Fork 35
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #785 from WowRarity/cdn-version-checker
Implement a more reliable TOC version check (using Blizzard's CDN)
- Loading branch information
Showing
11 changed files
with
283 additions
and
22 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,19 +1,4 @@ | ||
local_version=$(cat Rarity.toc | grep -oP '## Interface: \K(\d{6},? ?)+') | ||
options_version=$(cat Modules/Options/Rarity_Options.toc | grep -oP '## Interface: \K(\d{6},? ?)+') | ||
set -eu | ||
|
||
# Since there's no "official" way to get the latest version, just use a popular/frequently updated addon ... | ||
remote_version=$(curl https://raw.githubusercontent.com/BigWigsMods/BigWigs/master/BigWigs.toc --silent | grep -oP '## Interface: \K(\d{6},? ?)+') | ||
|
||
if [ "$local_version" != "$remote_version" ]; then | ||
echo "✗ Local interface version ($local_version) does NOT match remote version ($remote_version)" | ||
exit 1 | ||
else | ||
echo "✓ Local interface version ($local_version) matches remote version ($remote_version)" | ||
fi | ||
|
||
if [ "$local_version" != "$options_version" ]; then | ||
echo "✗ Core interface version ($local_version) does NOT match options version ($options_version)" | ||
exit 1 | ||
else | ||
echo "✓ Core interface version ($local_version) matches options version ($options_version)" | ||
fi | ||
curl --silent --show-error https://us.version.battle.net/v2/products/wow/versions > cdn-response.txt | ||
evo Tests/TOC/check-cdn-version.lua cdn-response.txt |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
Region!STRING:0|BuildConfig!HEX:16|CDNConfig!HEX:16|KeyRing!HEX:16|BuildId!DEC:4|VersionsName!String:0|ProductConfig!HEX:16 | ||
## seqn = 2515340 | ||
us|afb222415432704dab1c5849cfd3e39f|bba400d95ca3cbf8a0912ec7c9d8899d|3ca57fe7319a297346440e4d2a03a0cd|57212|11.0.5.57212|53020d32e1a25648c8e1eafd5771935f | ||
eu|afb222415432704dab1c5849cfd3e39f|bba400d95ca3cbf8a0912ec7c9d8899d|3ca57fe7319a297346440e4d2a03a0cd|57212|11.0.5.57212|53020d32e1a25648c8e1eafd5771935f | ||
cn|afb222415432704dab1c5849cfd3e39f|bba400d95ca3cbf8a0912ec7c9d8899d|3ca57fe7319a297346440e4d2a03a0cd|57212|11.0.5.57212|53020d32e1a25648c8e1eafd5771935f | ||
kr|afb222415432704dab1c5849cfd3e39f|bba400d95ca3cbf8a0912ec7c9d8899d|3ca57fe7319a297346440e4d2a03a0cd|57212|11.0.5.57212|53020d32e1a25648c8e1eafd5771935f | ||
tw|afb222415432704dab1c5849cfd3e39f|bba400d95ca3cbf8a0912ec7c9d8899d|3ca57fe7319a297346440e4d2a03a0cd|57212|11.0.5.57212|53020d32e1a25648c8e1eafd5771935f | ||
sg|afb222415432704dab1c5849cfd3e39f|bba400d95ca3cbf8a0912ec7c9d8899d|3ca57fe7319a297346440e4d2a03a0cd|57212|11.0.5.57212|53020d32e1a25648c8e1eafd5771935f | ||
xx|afb222415432704dab1c5849cfd3e39f|bba400d95ca3cbf8a0912ec7c9d8899d|3ca57fe7319a297346440e4d2a03a0cd|57212|11.0.5.57212|53020d32e1a25648c8e1eafd5771935f |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
Region|BuildConfig!HEX:16|CDNConfig!HEX:16|KeyRing!HEX:16|BuildId!DEC:4|VersionsName!String:0|ProductConfig!HEX:16 | ||
## seqn = 2515340 | ||
us|afb222415432704dab1c5849cfd3e39f|bba400d95ca3cbf8a0912ec7c9d8899d|3ca57fe7319a297346440e4d2a03a0cd|57212|11.0.5.57212|53020d32e1a25648c8e1eafd5771935f | ||
eu|afb222415432704dab1c5849cfd3e39f|bba400d95ca3cbf8a0912ec7c9d8899d|3ca57fe7319a297346440e4d2a03a0cd|57212|11.0.5.57212|53020d32e1a25648c8e1eafd5771935f | ||
cn|afb222415432704dab1c5849cfd3e39f|bba400d95ca3cbf8a0912ec7c9d8899d|3ca57fe7319a297346440e4d2a03a0cd|57212|11.0.5.57212|53020d32e1a25648c8e1eafd5771935f | ||
kr|afb222415432704dab1c5849cfd3e39f|bba400d95ca3cbf8a0912ec7c9d8899d|3ca57fe7319a297346440e4d2a03a0cd|57212|11.0.5.57212|53020d32e1a25648c8e1eafd5771935f | ||
tw|afb222415432704dab1c5849cfd3e39f|bba400d95ca3cbf8a0912ec7c9d8899d|3ca57fe7319a297346440e4d2a03a0cd|57212|11.0.5.57212|53020d32e1a25648c8e1eafd5771935f | ||
sg|afb222415432704dab1c5849cfd3e39f|bba400d95ca3cbf8a0912ec7c9d8899d|3ca57fe7319a297346440e4d2a03a0cd|57212|11.0.5.57212|53020d32e1a25648c8e1eafd5771935f | ||
xx|afb222415432704dab1c5849cfd3e39f|bba400d95ca3cbf8a0912ec7c9d8899d|3ca57fe7319a297346440e4d2a03a0cd|57212|11.0.5.57212|53020d32e1a25648c8e1eafd5771935f |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
local BlizzardTOC = {} | ||
|
||
function BlizzardTOC:DecodeFileContents(fileContents) | ||
local toc = {} | ||
|
||
local lines = string.explode(fileContents, "\n") | ||
for _, line in ipairs(lines) do | ||
line = line:gsub("\r", "") -- Avoids crossplatform headaches (autoformat doesn't modify TOC files) | ||
toc["Title"] = toc["Title"] or line:match("^## Title: (.+)") | ||
toc["Author"] = toc["Author"] or line:match("^## Author: (.+)") | ||
toc["Interface"] = toc["Interface"] or tonumber(line:match("^## Interface: (%d+)")) | ||
toc["X-Min-Interface"] = toc["X-Min-Interface"] or tonumber(line:match("^## X%-Min%-Interface: (%d+)")) | ||
toc["X-Curse-Project-ID"] = toc["X-Curse-Project-ID"] | ||
or tonumber(line:match("^## X%-Curse%-Project%-ID: (%d+)")) | ||
toc["Dependencies"] = toc["Dependencies"] or line:match("^## Dependencies: (.+)") | ||
toc["X-Part-Of"] = toc["X-Part-Of"] or line:match("^## X%-%Part%-Of: (.+)") | ||
end | ||
|
||
return toc | ||
end | ||
|
||
return BlizzardTOC |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
local assert = assert | ||
local ipairs = ipairs | ||
local tonumber = tonumber | ||
|
||
local string_explode = string.explode | ||
local table_insert = table.insert | ||
|
||
local CDN = { | ||
SEQUENCE_NUMBER_PATTERN = "## seqn = ", | ||
TRIM_WHITESPACE_PATTERN = "^%s*(.-)%s*$", | ||
errorStrings = { | ||
INVALID_VERSION_FORMAT = "Invalid CDN version string format (should be be MAJOR.MINOR.PATCH)", | ||
MALFORMED_RESPONSE_HEADER = "Malformed CDN response header (should be !-separated key-value pairs)", | ||
}, | ||
} | ||
|
||
function CDN:VersionNameToInterfaceVersion(versionName) | ||
local tokens = string_explode(versionName, ".") | ||
|
||
if #tokens < 3 then | ||
return nil, CDN.errorStrings.INVALID_VERSION_FORMAT | ||
end | ||
|
||
local major = tokens[1] | ||
local minor = tokens[2] | ||
local patch = tokens[3] | ||
|
||
return tonumber(major) * 10000 + tonumber(minor) * 100 + tonumber(patch) | ||
end | ||
|
||
local function parseNextLine(response, line) | ||
line = line:match(CDN.TRIM_WHITESPACE_PATTERN) | ||
|
||
assert(line ~= nil) | ||
assert(line ~= "") | ||
|
||
-- Parse seqn (second line) | ||
if line:find(CDN.SEQUENCE_NUMBER_PATTERN) then | ||
local sequenceNumber = line:gsub(CDN.SEQUENCE_NUMBER_PATTERN, "") | ||
response.sequenceNumber = tonumber(sequenceNumber) | ||
return | ||
end | ||
|
||
local tokens = string_explode(line, "|") | ||
|
||
-- Parse header (first line) | ||
if #response.csvFieldNames == 0 then | ||
for _, csvFieldName in ipairs(tokens) do | ||
local tokens = string_explode(csvFieldName, "!") | ||
if #tokens ~= 2 then | ||
error(CDN.errorStrings.MALFORMED_RESPONSE_HEADER, 0) | ||
end | ||
|
||
table_insert(response.csvFieldNames, tokens[1]) | ||
end | ||
|
||
return | ||
end | ||
|
||
-- Parse the CSV data (subsequent lines) | ||
local regionKey = tokens[1] | ||
local csvEntry = {} | ||
|
||
for index, csvValue in ipairs(tokens) do | ||
local fieldName = response.csvFieldNames[index] | ||
assert(fieldName ~= nil) | ||
csvEntry[fieldName] = csvValue | ||
end | ||
|
||
assert(response.productInfoByRegion[regionKey] == nil) | ||
response.productInfoByRegion[regionKey] = csvEntry | ||
end | ||
|
||
function CDN:ParseResponseText(data) | ||
local response = { | ||
csvFieldNames = {}, | ||
productInfoByRegion = {}, | ||
} | ||
|
||
local lines = string_explode(data, "\n") | ||
for _, line in ipairs(lines) do | ||
-- Might be faster to use goto continue here, but it breaks the autoformatter (revisit later?) | ||
parseNextLine(response, line) | ||
end | ||
|
||
return response | ||
end | ||
|
||
return CDN |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
local BlizzardTOC = require("Tests.TOC.BlizzardTOC") | ||
local CDN = require("Tests.TOC.CDN") | ||
|
||
local transform = require("transform") | ||
local bold = transform.bold | ||
|
||
local cdnResponseText = C_FileSystem.ReadFile(arg[1]) | ||
C_FileSystem.Delete(arg[1]) | ||
local coreAddonVersion = arg[2] | ||
local optionsAddonVersion = arg[3] | ||
|
||
printf("Parsing CDN response:\n%s", transform.cyan(cdnResponseText)) | ||
local response = CDN:ParseResponseText(cdnResponseText) | ||
printf(transform.yellow("Sequence number: %d"), response.sequenceNumber) | ||
printf(transform.yellow("Region keys: %s"), dump(table.keys(response.productInfoByRegion), { silent = true })) | ||
|
||
print() | ||
|
||
for regionKey, productInfo in pairs(response.productInfoByRegion) do | ||
if regionKey == "us" then | ||
for key, value in pairs(productInfo) do | ||
printf(bold("%s") .. ": %s", key, value) | ||
end | ||
end | ||
end | ||
|
||
print() | ||
|
||
local tocFiles = { | ||
Core = "Rarity.toc", | ||
Options = "Modules/Options/Rarity_Options.toc", | ||
} | ||
|
||
for moduleName, tocFilePath in pairs(tocFiles) do | ||
local tocFileContents = C_FileSystem.ReadFile(tocFilePath) | ||
printf("Processing TOC file: %s -> %s", bold(moduleName), bold(tocFilePath)) | ||
local toc = BlizzardTOC:DecodeFileContents(tocFileContents) | ||
|
||
local tocInterfaceVersion = toc["Interface"] | ||
printf(bold("Detected interface version: %d"), tocInterfaceVersion) | ||
|
||
-- Assumes the US CDN is authoritative (should be the earliest to update?) | ||
local usVersionName = response.productInfoByRegion.us.VersionsName | ||
local latestInterfaceVersion = CDN:VersionNameToInterfaceVersion(usVersionName) | ||
|
||
if tocInterfaceVersion ~= latestInterfaceVersion then | ||
local errorMessage = format( | ||
"✗ Local TOC interface version %d does NOT match Blizzard CDN version %d", | ||
tocInterfaceVersion, | ||
latestInterfaceVersion | ||
) | ||
error(transform.red(errorMessage)) | ||
else | ||
printf( | ||
transform.green("✓ Local TOC interface version %d matches Blizzard CDN version %d"), | ||
tocInterfaceVersion, | ||
latestInterfaceVersion | ||
) | ||
end | ||
print() | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
local BlizzardTOC = require("Tests.TOC.BlizzardTOC") | ||
local CDN = require("Tests.TOC.CDN") | ||
|
||
local VALID_RESPONSE_FILE = path.join("Tests", "Fixtures", "cdn-response-example.txt") | ||
local VALID_RESPONSE_TEXT = C_FileSystem.ReadFile(VALID_RESPONSE_FILE) | ||
local INVALID_RESPONSE_FILE = path.join("Tests", "Fixtures", "cdn-response-malformed-header.txt") | ||
local INVALID_RESPONSE_TEXT = C_FileSystem.ReadFile(INVALID_RESPONSE_FILE) | ||
|
||
local RARITY_CORE_TOC = C_FileSystem.ReadFile("Rarity.toc") | ||
local RARITY_OPTIONS_TOC = C_FileSystem.ReadFile(path.join("Modules", "Options", "Rarity_Options.toc")) | ||
|
||
local EXAMPLE_PRODUCT_INFO = { | ||
BuildConfig = "afb222415432704dab1c5849cfd3e39f", | ||
BuildId = "57212", | ||
CDNConfig = "bba400d95ca3cbf8a0912ec7c9d8899d", | ||
KeyRing = "3ca57fe7319a297346440e4d2a03a0cd", | ||
ProductConfig = "53020d32e1a25648c8e1eafd5771935f", | ||
Region = "us", | ||
VersionsName = "11.0.5.57212", | ||
} | ||
|
||
describe("TOC", function() | ||
describe("BlizzardTOC", function() | ||
describe("DecodeFileContents", function() | ||
it("should be able to load valid TOC files", function() | ||
local RarityCoreTOC = BlizzardTOC:DecodeFileContents(RARITY_CORE_TOC) | ||
local RarityOptionsTOC = BlizzardTOC:DecodeFileContents(RARITY_OPTIONS_TOC) | ||
|
||
-- For now, only parse the header (other fields can be added as needed) | ||
assertEquals(RarityCoreTOC["Title"], "Rarity") | ||
assertEquals(RarityCoreTOC["Author"], "Allara") | ||
assertTrue(RarityCoreTOC["Interface"] > 0) | ||
assertEquals(type(RarityCoreTOC["X-Min-Interface"]), "number") | ||
assertEquals(RarityCoreTOC["X-Curse-Project-ID"], 30801) | ||
assertEquals(RarityCoreTOC["Interface"], RarityCoreTOC["X-Min-Interface"]) | ||
|
||
assertEquals(RarityOptionsTOC["Title"], "Rarity [|caaedc99fOptions|r]") | ||
assertEquals(RarityOptionsTOC["Dependencies"], "Rarity") | ||
assertEquals(RarityOptionsTOC["X-Part-Of"], "Rarity") | ||
|
||
assertEquals(RarityCoreTOC["Author"], RarityOptionsTOC["Author"]) | ||
assertEquals(RarityCoreTOC["Interface"], RarityOptionsTOC["Interface"]) | ||
assertEquals(RarityCoreTOC["X-Min-Interface"], RarityOptionsTOC["X-Min-Interface"]) | ||
end) | ||
end) | ||
end) | ||
|
||
describe("CDN", function() | ||
describe("VersionNameToInterfaceVersion", function() | ||
it("should return a number representing the TOC interface version", function() | ||
assertEquals(CDN:VersionNameToInterfaceVersion("11.0.5.57212"), 110005) | ||
end) | ||
|
||
it("should fail if the provided version name has an invalid format", function() | ||
assertFailure(function() | ||
return CDN:VersionNameToInterfaceVersion("11.0") | ||
end, CDN.errorStrings.INVALID_VERSION_FORMAT) | ||
end) | ||
end) | ||
|
||
describe("ParseResponseText", function() | ||
it("should throw if the header's key-value format is not as expected", function() | ||
assertThrows(function() | ||
CDN:ParseResponseText(INVALID_RESPONSE_TEXT) | ||
end, CDN.errorStrings.MALFORMED_RESPONSE_HEADER) | ||
end) | ||
|
||
it("should return a table representing the CDN response body", function() | ||
local expectedFieldNames = | ||
{ "Region", "BuildConfig", "CDNConfig", "KeyRing", "BuildId", "VersionsName", "ProductConfig" } | ||
|
||
local response = CDN:ParseResponseText(VALID_RESPONSE_TEXT) | ||
assertEquals(response.sequenceNumber, 2515340) | ||
assertEquals(#response.csvFieldNames, 7) | ||
assertEquals(response.csvFieldNames, expectedFieldNames) | ||
assertEquals(response.productInfoByRegion.us, EXAMPLE_PRODUCT_INFO) -- Don't care about the rest | ||
end) | ||
end) | ||
end) | ||
end) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters