fix(ci): make release gates portable and deterministic
All checks were successful
CI / linux-portable (push) Successful in 3m46s
CI / windows-gate (push) Successful in 6m21s
CI / release (push) Successful in 2m15s

This commit is contained in:
Erik 2026-08-25 19:38:10 +02:00
parent af9327a17b
commit 0c699240e0
4 changed files with 25 additions and 15 deletions

View file

@ -2083,12 +2083,12 @@ public static class CharacterStatController
long skillCost = skill.AdvancementClass >= CharacterSkillAdvancementClass.Trained
? skill.RaiseCost
: skill.TrainedCost;
return skillCost > 0 ? skillCost.ToString("N0") : "Infinity!";
return skillCost > 0 ? FormatXp(skillCost) : "Infinity!";
}
if (attrSel[0] < 0) return sheet.SkillCredits.ToString();
long cost = GetRaiseCost(sheet, attrSel[0]);
return cost > 0 ? cost.ToString("N0") : "Infinity!";
return cost > 0 ? FormatXp(cost) : "Infinity!";
});
// Line-2 elements: pass null → keep dat font.
@ -2114,7 +2114,7 @@ public static class CharacterStatController
if (skill is not null && skill.AdvancementClass < CharacterSkillAdvancementClass.Trained)
return sheet.SkillCredits.ToString();
}
return sheet.UnassignedXp.ToString("N0");
return FormatXp(sheet.UnassignedXp);
});
BindSelectedFooterState(stateB);
@ -2166,12 +2166,12 @@ public static class CharacterStatController
long skillCost = skill.AdvancementClass >= CharacterSkillAdvancementClass.Trained
? skill.RaiseCost
: skill.TrainedCost;
return skillCost > 0 ? skillCost.ToString("N0") : "Infinity!";
return skillCost > 0 ? FormatXp(skillCost) : "Infinity!";
}
if (attrSel[0] < 0) return sheet.SkillCredits.ToString();
long cost = GetRaiseCost(sheet, attrSel[0]);
return cost > 0 ? cost.ToString("N0") : "Infinity!";
return cost > 0 ? FormatXp(cost) : "Infinity!";
});
LabelProvider(TextById(state, FooterLine2Label), null, Body, () =>
@ -2194,7 +2194,7 @@ public static class CharacterStatController
if (skill is not null && skill.AdvancementClass < CharacterSkillAdvancementClass.Trained)
return sheet.SkillCredits.ToString();
}
return sheet.UnassignedXp.ToString("N0");
return FormatXp(sheet.UnassignedXp);
});
}
}

View file

@ -363,6 +363,8 @@ public sealed class LauncherContentStateStore
{
if (string.IsNullOrWhiteSpace(path)
|| Path.IsPathFullyQualified(path)
|| path.Contains('/')
|| path.Contains('\\')
|| !string.Equals(path, Path.GetFileName(path), StringComparison.Ordinal)
|| path is "." or ".."
|| !path.EndsWith(".pak", StringComparison.OrdinalIgnoreCase))

View file

@ -1,5 +1,6 @@
using AcDream.App.UI;
using AcDream.App.UI.Layout;
using System.Globalization;
using System.Numerics;
namespace AcDream.App.Tests.UI.Layout;
@ -184,8 +185,8 @@ public class CharacterStatControllerTests
Assert.Equal("Non-Player Killer", visiblePk.LinesProvider()[0].Text);
Assert.Equal("126", visibleLevel.LinesProvider()[0].Text);
Assert.Equal("Total Experience (XP):", visibleTotalXpLabel.LinesProvider()[0].Text);
Assert.Equal((1_250_000_000L).ToString("N0"), visibleTotalXp.LinesProvider()[0].Text);
Assert.Equal((42_000_000L).ToString("N0"), visibleXpNext.LinesProvider()[0].Text);
Assert.Equal((1_250_000_000L).ToString("N0", CultureInfo.InvariantCulture), visibleTotalXp.LinesProvider()[0].Text);
Assert.Equal((42_000_000L).ToString("N0", CultureInfo.InvariantCulture), visibleXpNext.LinesProvider()[0].Text);
Assert.Empty(hiddenName.LinesProvider());
Assert.Empty(hiddenXpNext.LinesProvider());
Assert.Empty(hiddenPk.LinesProvider());
@ -682,7 +683,7 @@ public class CharacterStatControllerTests
CharacterStatController.Bind(layout, SampleData.SampleCharacter);
var expected = (87_757_321_741L).ToString("N0");
var expected = (87_757_321_741L).ToString("N0", CultureInfo.InvariantCulture);
Assert.Equal(expected, val.LinesProvider()[0].Text);
}
@ -737,7 +738,7 @@ public class CharacterStatControllerTests
Descendants(list).OfType<UiClickablePanel>().ToList()[4].OnClick!();
// Focus raise cost = 110 (SampleData fixture).
Assert.Equal((110L).ToString("N0"), val.LinesProvider()[0].Text);
Assert.Equal((110L).ToString("N0", CultureInfo.InvariantCulture), val.LinesProvider()[0].Text);
}
[Fact]
@ -768,7 +769,7 @@ public class CharacterStatControllerTests
Descendants(list).OfType<UiClickablePanel>().ToList()[4].OnClick!();
// UnassignedXp = 87_757_321_741L
var expected = (87_757_321_741L).ToString("N0");
var expected = (87_757_321_741L).ToString("N0", CultureInfo.InvariantCulture);
Assert.Equal(expected, val.LinesProvider()[0].Text);
}
@ -1443,9 +1444,9 @@ public class CharacterStatControllerTests
// which was previously computed but never surfaced in the title text.
Assert.Equal("War Magic: 285 (+5)", title.LinesProvider()[0].Text);
Assert.Equal("Experience To Raise:", l1Label.LinesProvider()[0].Text);
Assert.Equal((11_100_000L).ToString("N0"), l1Value.LinesProvider()[0].Text);
Assert.Equal((11_100_000L).ToString("N0", CultureInfo.InvariantCulture), l1Value.LinesProvider()[0].Text);
Assert.Equal("Unassigned Experience:", l2Label.LinesProvider()[0].Text);
Assert.Equal((87_757_321_741L).ToString("N0"), l2Value.LinesProvider()[0].Text);
Assert.Equal((87_757_321_741L).ToString("N0", CultureInfo.InvariantCulture), l2Value.LinesProvider()[0].Text);
// CT5: RowHighlightSprite corrected to 0x06000F93 (see the sealed
// verdict on RowClick_WithSpriteResolve_SelectedRowHasHighlightSprite
// above); UseSelectionBars was retired outright (retail's actual
@ -2113,7 +2114,7 @@ public class CharacterStatControllerTests
CharacterStatController.Bind(layout, SampleData.SampleCharacter);
Assert.Equal((1_250_000_000L).ToString("N0"), value.LinesProvider()[0].Text);
Assert.Equal((1_250_000_000L).ToString("N0", CultureInfo.InvariantCulture), value.LinesProvider()[0].Text);
Assert.False(value.Centered);
Assert.True(value.RightAligned);
}
@ -2257,7 +2258,7 @@ public class CharacterStatControllerTests
var lines = xpValue.LinesProvider();
Assert.Single(lines);
// XpToNextLevel from SampleData = 42_000_000L formatted as "42,000,000"
Assert.Equal((42_000_000L).ToString("N0"), lines[0].Text);
Assert.Equal((42_000_000L).ToString("N0", CultureInfo.InvariantCulture), lines[0].Text);
}
/// <summary>

View file

@ -238,11 +238,18 @@ public sealed class PreparedAssetVerificationCacheTests : IDisposable
// Crash shape: the verified package was moved aside and what sits in
// its place is wrong. The cache still describes the ORIGINAL bytes, so
// if recovery trusted it the launcher would accept a bad package.
DateTime verifiedWriteTime = File.GetLastWriteTimeUtc(store.PreparedAssetPath);
string backup = LauncherInstallRecordStore.GetBackupPath(store.PreparedAssetPath);
File.Move(store.PreparedAssetPath, backup);
await File.WriteAllTextAsync(
store.PreparedAssetPath,
new string('z', PackageContent.Length));
File.SetLastWriteTimeUtc(
store.PreparedAssetPath,
verifiedWriteTime.AddMinutes(1));
Assert.NotEqual(
verifiedWriteTime,
File.GetLastWriteTimeUtc(store.PreparedAssetPath));
InstallRecordVerification verification = await store.LoadAndVerifyAsync();