Compare commits

...
40 changed files with 208 additions and 117 deletions
@@ -308,11 +308,14 @@ export class AuthSessionManager {
}
this.send({type: 'initialize.start'});
try {
await this.loadStoredAccounts();
const storedToken = parseStoredSessionValue(this.deps.appStorage.getItem(AuthSessionStorageKey.Token));
const storedUserId = parseStoredSessionValue(this.deps.appStorage.getItem(AuthSessionStorageKey.UserId));
await this.loadStoredAccounts();
logger.debug(`Loaded from storage: token=${storedToken ? 'present' : 'null'}, userId=${storedUserId ?? 'null'}`);
const storedAccount = storedUserId ? this._snapshot.context.accounts.get(storedUserId) : undefined;
let storedAccount = storedUserId ? this._snapshot.context.accounts.get(storedUserId) : undefined;
if (storedToken && storedUserId && storedAccount?.token !== storedToken) {
storedAccount = this.recoverStoredActiveAccount(storedToken, storedUserId, storedAccount);
}
if (storedToken && storedUserId && storedAccount?.token === storedToken) {
this.send({type: 'initialize.tokenLoaded', token: storedToken, userId: storedUserId});
this.deps.restoreLocalPresenceIntent(storedAccount.presenceIntent ?? null);
@@ -328,6 +331,25 @@ export class AuthSessionManager {
}
}
private recoverStoredActiveAccount(token: string, userId: string, existing?: Account): Account {
const instance = this.deps.getRuntimeSnapshot();
const account: Account = {
userId,
token,
userData: existing?.userData,
presenceIntent: existing?.presenceIntent ?? null,
lastActive: this.deps.now(),
instance,
isValid: true,
};
this.send({type: 'account.upsert', account});
void this.deps.accountStorage
.stashAccountData(userId, token, account.userData, instance, account.presenceIntent)
.catch((error) => logger.warn('Failed to persist recovered active account', error));
logger.info('Recovered stored active account after local account data was unavailable');
return account;
}
private async loadStoredAccounts(): Promise<void> {
try {
const stored = await this.deps.accountStorage.getAllAccounts();
+1 -1
View File
@@ -25,7 +25,7 @@
"devDependencies": {
"@types/node": "24.10.9",
"@typescript/native-preview": "7.0.0-dev.20260130.1",
"electron": "43.4.0",
"electron": "42.10.0",
"electron-builder": "26.9.0",
"esbuild": "0.27.2",
"typescript": "5.9.3"
+2 -2
View File
@@ -4,8 +4,8 @@
msgid ""
msgstr ""
"Project-Id-Version: fluxer-marketing\n"
"POT-Creation-Date: 2026-08-24 00:00+0000\n"
"PO-Revision-Date: 2026-08-24 00:00+0000\n"
"POT-Creation-Date: 2026-08-25 00:00+0000\n"
"PO-Revision-Date: 2026-08-25 00:00+0000\n"
"Language: ar\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
+2 -2
View File
@@ -4,8 +4,8 @@
msgid ""
msgstr ""
"Project-Id-Version: fluxer-marketing\n"
"POT-Creation-Date: 2026-08-24 00:00+0000\n"
"PO-Revision-Date: 2026-08-24 00:00+0000\n"
"POT-Creation-Date: 2026-08-25 00:00+0000\n"
"PO-Revision-Date: 2026-08-25 00:00+0000\n"
"Language: bg\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
+2 -2
View File
@@ -4,8 +4,8 @@
msgid ""
msgstr ""
"Project-Id-Version: fluxer-marketing\n"
"POT-Creation-Date: 2026-08-24 00:00+0000\n"
"PO-Revision-Date: 2026-08-24 00:00+0000\n"
"POT-Creation-Date: 2026-08-25 00:00+0000\n"
"PO-Revision-Date: 2026-08-25 00:00+0000\n"
"Language: cs\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
+2 -2
View File
@@ -4,8 +4,8 @@
msgid ""
msgstr ""
"Project-Id-Version: fluxer-marketing\n"
"POT-Creation-Date: 2026-08-24 00:00+0000\n"
"PO-Revision-Date: 2026-08-24 00:00+0000\n"
"POT-Creation-Date: 2026-08-25 00:00+0000\n"
"PO-Revision-Date: 2026-08-25 00:00+0000\n"
"Language: da\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
+2 -2
View File
@@ -4,8 +4,8 @@
msgid ""
msgstr ""
"Project-Id-Version: fluxer-marketing\n"
"POT-Creation-Date: 2026-08-24 00:00+0000\n"
"PO-Revision-Date: 2026-08-24 00:00+0000\n"
"POT-Creation-Date: 2026-08-25 00:00+0000\n"
"PO-Revision-Date: 2026-08-25 00:00+0000\n"
"Language: de\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
+2 -2
View File
@@ -4,8 +4,8 @@
msgid ""
msgstr ""
"Project-Id-Version: fluxer-marketing\n"
"POT-Creation-Date: 2026-08-24 00:00+0000\n"
"PO-Revision-Date: 2026-08-24 00:00+0000\n"
"POT-Creation-Date: 2026-08-25 00:00+0000\n"
"PO-Revision-Date: 2026-08-25 00:00+0000\n"
"Language: el\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
+2 -2
View File
@@ -4,8 +4,8 @@
msgid ""
msgstr ""
"Project-Id-Version: fluxer-marketing\n"
"POT-Creation-Date: 2026-08-24 00:00+0000\n"
"PO-Revision-Date: 2026-08-24 00:00+0000\n"
"POT-Creation-Date: 2026-08-25 00:00+0000\n"
"PO-Revision-Date: 2026-08-25 00:00+0000\n"
"Language: en-GB\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
+2 -2
View File
@@ -4,8 +4,8 @@
msgid ""
msgstr ""
"Project-Id-Version: fluxer-marketing\n"
"POT-Creation-Date: 2026-08-24 00:00+0000\n"
"PO-Revision-Date: 2026-08-24 00:00+0000\n"
"POT-Creation-Date: 2026-08-25 00:00+0000\n"
"PO-Revision-Date: 2026-08-25 00:00+0000\n"
"Language: en-US\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
+2 -2
View File
@@ -4,8 +4,8 @@
msgid ""
msgstr ""
"Project-Id-Version: fluxer-marketing\n"
"POT-Creation-Date: 2026-08-24 00:00+0000\n"
"PO-Revision-Date: 2026-08-24 00:00+0000\n"
"POT-Creation-Date: 2026-08-25 00:00+0000\n"
"PO-Revision-Date: 2026-08-25 00:00+0000\n"
"Language: es-419\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
+2 -2
View File
@@ -4,8 +4,8 @@
msgid ""
msgstr ""
"Project-Id-Version: fluxer-marketing\n"
"POT-Creation-Date: 2026-08-24 00:00+0000\n"
"PO-Revision-Date: 2026-08-24 00:00+0000\n"
"POT-Creation-Date: 2026-08-25 00:00+0000\n"
"PO-Revision-Date: 2026-08-25 00:00+0000\n"
"Language: es-ES\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
+2 -2
View File
@@ -4,8 +4,8 @@
msgid ""
msgstr ""
"Project-Id-Version: fluxer-marketing\n"
"POT-Creation-Date: 2026-08-24 00:00+0000\n"
"PO-Revision-Date: 2026-08-24 00:00+0000\n"
"POT-Creation-Date: 2026-08-25 00:00+0000\n"
"PO-Revision-Date: 2026-08-25 00:00+0000\n"
"Language: fi\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
+2 -2
View File
@@ -4,8 +4,8 @@
msgid ""
msgstr ""
"Project-Id-Version: fluxer-marketing\n"
"POT-Creation-Date: 2026-08-24 00:00+0000\n"
"PO-Revision-Date: 2026-08-24 00:00+0000\n"
"POT-Creation-Date: 2026-08-25 00:00+0000\n"
"PO-Revision-Date: 2026-08-25 00:00+0000\n"
"Language: fr\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
+2 -2
View File
@@ -4,8 +4,8 @@
msgid ""
msgstr ""
"Project-Id-Version: fluxer-marketing\n"
"POT-Creation-Date: 2026-08-24 00:00+0000\n"
"PO-Revision-Date: 2026-08-24 00:00+0000\n"
"POT-Creation-Date: 2026-08-25 00:00+0000\n"
"PO-Revision-Date: 2026-08-25 00:00+0000\n"
"Language: he\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
+2 -2
View File
@@ -4,8 +4,8 @@
msgid ""
msgstr ""
"Project-Id-Version: fluxer-marketing\n"
"POT-Creation-Date: 2026-08-24 00:00+0000\n"
"PO-Revision-Date: 2026-08-24 00:00+0000\n"
"POT-Creation-Date: 2026-08-25 00:00+0000\n"
"PO-Revision-Date: 2026-08-25 00:00+0000\n"
"Language: hi\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
+2 -2
View File
@@ -4,8 +4,8 @@
msgid ""
msgstr ""
"Project-Id-Version: fluxer-marketing\n"
"POT-Creation-Date: 2026-08-24 00:00+0000\n"
"PO-Revision-Date: 2026-08-24 00:00+0000\n"
"POT-Creation-Date: 2026-08-25 00:00+0000\n"
"PO-Revision-Date: 2026-08-25 00:00+0000\n"
"Language: hr\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
+2 -2
View File
@@ -4,8 +4,8 @@
msgid ""
msgstr ""
"Project-Id-Version: fluxer-marketing\n"
"POT-Creation-Date: 2026-08-24 00:00+0000\n"
"PO-Revision-Date: 2026-08-24 00:00+0000\n"
"POT-Creation-Date: 2026-08-25 00:00+0000\n"
"PO-Revision-Date: 2026-08-25 00:00+0000\n"
"Language: hu\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
+2 -2
View File
@@ -4,8 +4,8 @@
msgid ""
msgstr ""
"Project-Id-Version: fluxer-marketing\n"
"POT-Creation-Date: 2026-08-24 00:00+0000\n"
"PO-Revision-Date: 2026-08-24 00:00+0000\n"
"POT-Creation-Date: 2026-08-25 00:00+0000\n"
"PO-Revision-Date: 2026-08-25 00:00+0000\n"
"Language: id\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
+2 -2
View File
@@ -4,8 +4,8 @@
msgid ""
msgstr ""
"Project-Id-Version: fluxer-marketing\n"
"POT-Creation-Date: 2026-08-24 00:00+0000\n"
"PO-Revision-Date: 2026-08-24 00:00+0000\n"
"POT-Creation-Date: 2026-08-25 00:00+0000\n"
"PO-Revision-Date: 2026-08-25 00:00+0000\n"
"Language: it\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
+2 -2
View File
@@ -4,8 +4,8 @@
msgid ""
msgstr ""
"Project-Id-Version: fluxer-marketing\n"
"POT-Creation-Date: 2026-08-24 00:00+0000\n"
"PO-Revision-Date: 2026-08-24 00:00+0000\n"
"POT-Creation-Date: 2026-08-25 00:00+0000\n"
"PO-Revision-Date: 2026-08-25 00:00+0000\n"
"Language: ja\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
+2 -2
View File
@@ -4,8 +4,8 @@
msgid ""
msgstr ""
"Project-Id-Version: fluxer-marketing\n"
"POT-Creation-Date: 2026-08-24 00:00+0000\n"
"PO-Revision-Date: 2026-08-24 00:00+0000\n"
"POT-Creation-Date: 2026-08-25 00:00+0000\n"
"PO-Revision-Date: 2026-08-25 00:00+0000\n"
"Language: ko\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
+2 -2
View File
@@ -4,8 +4,8 @@
msgid ""
msgstr ""
"Project-Id-Version: fluxer-marketing\n"
"POT-Creation-Date: 2026-08-24 00:00+0000\n"
"PO-Revision-Date: 2026-08-24 00:00+0000\n"
"POT-Creation-Date: 2026-08-25 00:00+0000\n"
"PO-Revision-Date: 2026-08-25 00:00+0000\n"
"Language: lt\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
+2 -2
View File
@@ -4,8 +4,8 @@
msgid ""
msgstr ""
"Project-Id-Version: fluxer-marketing\n"
"POT-Creation-Date: 2026-08-24 00:00+0000\n"
"PO-Revision-Date: 2026-08-24 00:00+0000\n"
"POT-Creation-Date: 2026-08-25 00:00+0000\n"
"PO-Revision-Date: 2026-08-25 00:00+0000\n"
"Language: nl\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
+2 -2
View File
@@ -4,8 +4,8 @@
msgid ""
msgstr ""
"Project-Id-Version: fluxer-marketing\n"
"POT-Creation-Date: 2026-08-24 00:00+0000\n"
"PO-Revision-Date: 2026-08-24 00:00+0000\n"
"POT-Creation-Date: 2026-08-25 00:00+0000\n"
"PO-Revision-Date: 2026-08-25 00:00+0000\n"
"Language: no\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
+2 -2
View File
@@ -4,8 +4,8 @@
msgid ""
msgstr ""
"Project-Id-Version: fluxer-marketing\n"
"POT-Creation-Date: 2026-08-24 00:00+0000\n"
"PO-Revision-Date: 2026-08-24 00:00+0000\n"
"POT-Creation-Date: 2026-08-25 00:00+0000\n"
"PO-Revision-Date: 2026-08-25 00:00+0000\n"
"Language: pl\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
+2 -2
View File
@@ -4,8 +4,8 @@
msgid ""
msgstr ""
"Project-Id-Version: fluxer-marketing\n"
"POT-Creation-Date: 2026-08-24 00:00+0000\n"
"PO-Revision-Date: 2026-08-24 00:00+0000\n"
"POT-Creation-Date: 2026-08-25 00:00+0000\n"
"PO-Revision-Date: 2026-08-25 00:00+0000\n"
"Language: pt-BR\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
+2 -2
View File
@@ -4,8 +4,8 @@
msgid ""
msgstr ""
"Project-Id-Version: fluxer-marketing\n"
"POT-Creation-Date: 2026-08-24 00:00+0000\n"
"PO-Revision-Date: 2026-08-24 00:00+0000\n"
"POT-Creation-Date: 2026-08-25 00:00+0000\n"
"PO-Revision-Date: 2026-08-25 00:00+0000\n"
"Language: ro\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
+2 -2
View File
@@ -4,8 +4,8 @@
msgid ""
msgstr ""
"Project-Id-Version: fluxer-marketing\n"
"POT-Creation-Date: 2026-08-24 00:00+0000\n"
"PO-Revision-Date: 2026-08-24 00:00+0000\n"
"POT-Creation-Date: 2026-08-25 00:00+0000\n"
"PO-Revision-Date: 2026-08-25 00:00+0000\n"
"Language: ru\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
+2 -2
View File
@@ -4,8 +4,8 @@
msgid ""
msgstr ""
"Project-Id-Version: fluxer-marketing\n"
"POT-Creation-Date: 2026-08-24 00:00+0000\n"
"PO-Revision-Date: 2026-08-24 00:00+0000\n"
"POT-Creation-Date: 2026-08-25 00:00+0000\n"
"PO-Revision-Date: 2026-08-25 00:00+0000\n"
"Language: sv-SE\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
+2 -2
View File
@@ -4,8 +4,8 @@
msgid ""
msgstr ""
"Project-Id-Version: fluxer-marketing\n"
"POT-Creation-Date: 2026-08-24 00:00+0000\n"
"PO-Revision-Date: 2026-08-24 00:00+0000\n"
"POT-Creation-Date: 2026-08-25 00:00+0000\n"
"PO-Revision-Date: 2026-08-25 00:00+0000\n"
"Language: th\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
+2 -2
View File
@@ -4,8 +4,8 @@
msgid ""
msgstr ""
"Project-Id-Version: fluxer-marketing\n"
"POT-Creation-Date: 2026-08-24 00:00+0000\n"
"PO-Revision-Date: 2026-08-24 00:00+0000\n"
"POT-Creation-Date: 2026-08-25 00:00+0000\n"
"PO-Revision-Date: 2026-08-25 00:00+0000\n"
"Language: tr\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
+2 -2
View File
@@ -4,8 +4,8 @@
msgid ""
msgstr ""
"Project-Id-Version: fluxer-marketing\n"
"POT-Creation-Date: 2026-08-24 00:00+0000\n"
"PO-Revision-Date: 2026-08-24 00:00+0000\n"
"POT-Creation-Date: 2026-08-25 00:00+0000\n"
"PO-Revision-Date: 2026-08-25 00:00+0000\n"
"Language: uk\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
+2 -2
View File
@@ -4,8 +4,8 @@
msgid ""
msgstr ""
"Project-Id-Version: fluxer-marketing\n"
"POT-Creation-Date: 2026-08-24 00:00+0000\n"
"PO-Revision-Date: 2026-08-24 00:00+0000\n"
"POT-Creation-Date: 2026-08-25 00:00+0000\n"
"PO-Revision-Date: 2026-08-25 00:00+0000\n"
"Language: vi\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
+2 -2
View File
@@ -4,8 +4,8 @@
msgid ""
msgstr ""
"Project-Id-Version: fluxer-marketing\n"
"POT-Creation-Date: 2026-08-24 00:00+0000\n"
"PO-Revision-Date: 2026-08-24 00:00+0000\n"
"POT-Creation-Date: 2026-08-25 00:00+0000\n"
"PO-Revision-Date: 2026-08-25 00:00+0000\n"
"Language: zh-CN\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
+2 -2
View File
@@ -4,8 +4,8 @@
msgid ""
msgstr ""
"Project-Id-Version: fluxer-marketing\n"
"POT-Creation-Date: 2026-08-24 00:00+0000\n"
"PO-Revision-Date: 2026-08-24 00:00+0000\n"
"POT-Creation-Date: 2026-08-25 00:00+0000\n"
"PO-Revision-Date: 2026-08-25 00:00+0000\n"
"Language: zh-TW\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
+3 -1
View File
@@ -116,7 +116,9 @@ fn parse_inline_with_context(
position += 1 + result.advance;
continue;
}
if let Some(emoji) = parser.emoji_context().standard_at(base_offset + position + 1)
if let Some(emoji) = parser
.emoji_context()
.standard_at(base_offset + position + 1)
&& text[position + 1..].starts_with(&emoji.raw)
{
accumulated.extend_from_slice(emoji.raw.as_bytes());
+5 -5
View File
@@ -1680,8 +1680,8 @@ importers:
specifier: 7.0.0-dev.20260130.1
version: 7.0.0-dev.20260130.1
electron:
specifier: 43.4.0
version: 43.4.0
specifier: 42.10.0
version: 42.10.0
electron-builder:
specifier: 26.9.0
version: 26.9.0([email protected])
@@ -8222,8 +8222,8 @@ packages:
resolution: {integrity: sha512-bO3y10YikuUwUuDUQRM4KfwNkKhnpVO7IPdbsrejwN9/AABJzzTQ4GeHwyzNSrVO+tEH3/Np255a3sVZpZDjvg==}
engines: {node: '>=8.0.0'}
electron@43.4.0:
resolution: {integrity: sha512-3qxGF0CeQbiox5oWV1JlbWGQ1VerbmDhTFqW4sJ8h7uqTHniFYPObXJcDna0DMh32et0fFyKzz0YY8lJv3t5jg==}
electron@42.10.0:
resolution: {integrity: sha512-yBWnZO8tKId0ADvGa4+imPmm3+8Kfs8MtkUPhQQJz1AjTnUUDwCTWkQEU0CrArVVL97Jo4Q/0mVcLHciicBDGQ==}
engines: {node: '>= 22.12.0'}
hasBin: true
@@ -18314,7 +18314,7 @@ snapshots:
transitivePeerDependencies:
- supports-color
electron@43.4.0:
electron@42.10.0:
dependencies:
'@electron-internal/extract-zip': 1.0.5
'@electron/get': 5.1.0
+106 -39
View File
@@ -897,10 +897,32 @@ fn apt_get(args: &[&str]) -> Result<()> {
}
fn install_msvc_arm64_tools_step() -> Result<()> {
let installer =
Path::new(r"C:\Program Files (x86)\Microsoft Visual Studio\Installer\setup.exe");
let install_path = Path::new(r"C:\Program Files (x86)\Microsoft Visual Studio\2022\BuildTools");
run_command(CommandSpec::new(installer).args([
let program_files_x86 = env::var_os("ProgramFiles(x86)")
.map(PathBuf::from)
.ok_or_else(|| anyhow!("ProgramFiles(x86) is not set on the Windows runner"))?;
let installer_dir = program_files_x86
.join("Microsoft Visual Studio")
.join("Installer");
let installer = installer_dir.join("setup.exe");
let vswhere = installer_dir.join("vswhere.exe");
ensure!(
vswhere.is_file(),
"Visual Studio locator not found: {}",
vswhere.display()
);
let install_path = resolve_visual_studio_install_path(&vswhere)?;
if let Some(linker) = find_msvc_arm64_linker(&install_path)? {
println!("ARM64 cross link.exe: {}", linker.display());
return Ok(());
}
ensure!(
installer.is_file(),
"Visual Studio installer not found: {}",
installer.display()
);
run_command(CommandSpec::new(&installer).args([
"modify",
"--installPath",
install_path.to_string_lossy().as_ref(),
@@ -924,30 +946,66 @@ fn install_msvc_arm64_tools_step() -> Result<()> {
"VS installer did not finish within the timeout."
);
let mut found = false;
let linker = find_msvc_arm64_linker(&install_path)?.ok_or_else(|| {
anyhow!(
"ARM64 cross-build tools were not installed under {}\\VC\\Tools\\MSVC\\*\\bin\\HostX64\\arm64",
install_path.display()
)
})?;
println!("ARM64 cross link.exe: {}", linker.display());
Ok(())
}
fn resolve_visual_studio_install_path(vswhere: &Path) -> Result<PathBuf> {
let output = output_text(CommandSpec::new(vswhere).args([
"-products",
"*",
"-latest",
"-prerelease",
"-property",
"installationPath",
]))?;
let paths = output
.lines()
.map(str::trim)
.filter(|line| !line.is_empty())
.collect::<Vec<_>>();
ensure!(
paths.len() == 1,
"vswhere returned {} Visual Studio installation paths; expected exactly one",
paths.len()
);
let install_path = PathBuf::from(paths[0]);
ensure!(
install_path.is_dir(),
"vswhere returned a missing Visual Studio installation: {}",
install_path.display()
);
println!("Visual Studio installation: {}", install_path.display());
Ok(install_path)
}
fn find_msvc_arm64_linker(install_path: &Path) -> Result<Option<PathBuf>> {
let msvc_root = install_path.join("VC").join("Tools").join("MSVC");
if msvc_root.exists() {
for entry in fs::read_dir(&msvc_root)
.with_context(|| format!("Failed to read {}", msvc_root.display()))?
{
let candidate = entry?
.path()
.join("bin")
.join("HostX64")
.join("arm64")
.join("link.exe");
if candidate.exists() {
println!("ARM64 cross link.exe: {}", candidate.display());
found = true;
}
if !msvc_root.is_dir() {
return Ok(None);
}
let mut linkers = Vec::new();
for entry in fs::read_dir(&msvc_root)
.with_context(|| format!("Failed to read {}", msvc_root.display()))?
{
let candidate = entry?
.path()
.join("bin")
.join("HostX64")
.join("arm64")
.join("link.exe");
if candidate.is_file() {
linkers.push(candidate);
}
}
ensure!(
found,
"ARM64 cross-build tools were not installed under {}\\*\\bin\\HostX64\\arm64.",
msvc_root.display()
);
Ok(())
linkers.sort();
Ok(linkers.pop())
}
fn windows_installer_process_running() -> Result<bool> {
@@ -1823,7 +1881,16 @@ fn create_portable_zip_windows_step() -> Result<()> {
}
const FLUXER_WINDOWS_SIGNER_COMMON_NAME: &str = "Fluxer Platform AB";
const THIRD_PARTY_PUBLISHER_ALLOWLIST: &[&str] = &[];
const THIRD_PARTY_WINDOWS_SIGNATURE_ALLOWLIST: &[(&str, &str)] = &[
(
"d3dcompiler_47.dll",
"CN=Microsoft Windows, O=Microsoft Corporation, L=Redmond, S=Washington, C=US",
),
(
"dxil.dll",
"CN=Microsoft Windows, O=Microsoft Corporation, L=Redmond, S=Washington, C=US",
),
];
const KNOWN_OPTIONAL_WINDOWS_PE_INVENTORY: &[&str] = &["fluxer-vulkan-layer.win32-ia32-msvc.dll"];
const WINDOWS_NATIVE_ADDON_STEMS: &[&str] = &[
"hardware-encoder",
@@ -2144,7 +2211,7 @@ fn assert_fluxer_signed(row: &SignatureRow) -> Result<()> {
Ok(())
}
fn assert_third_party_signed(row: &SignatureRow) -> Result<()> {
fn assert_third_party_signed(row: &SignatureRow, relative: &str) -> Result<()> {
ensure!(
row.status == "Valid",
"Authenticode status is {} (expected Valid)",
@@ -2154,11 +2221,13 @@ fn assert_third_party_signed(row: &SignatureRow) -> Result<()> {
.subject
.as_deref()
.ok_or_else(|| anyhow!("Authenticode signature has no signer certificate subject"))?;
let common_name = certificate_common_name(subject)
.ok_or_else(|| anyhow!("Signer subject has no CN= component: {subject}"))?;
ensure!(
THIRD_PARTY_PUBLISHER_ALLOWLIST.contains(&common_name),
"Signer CN '{common_name}' is not an allowlisted third-party publisher"
THIRD_PARTY_WINDOWS_SIGNATURE_ALLOWLIST
.iter()
.any(|(allowed_path, allowed_subject)| {
relative.eq_ignore_ascii_case(allowed_path) && subject == *allowed_subject
}),
"Signer subject '{subject}' is not allowlisted for {relative}"
);
ensure!(
row.ts_subject.is_some(),
@@ -2167,10 +2236,10 @@ fn assert_third_party_signed(row: &SignatureRow) -> Result<()> {
Ok(())
}
fn assert_signed_by_known_publisher(row: &SignatureRow) -> Result<()> {
fn assert_signed_by_known_publisher(row: &SignatureRow, relative: &str) -> Result<()> {
match assert_fluxer_signed(row) {
Ok(()) => Ok(()),
Err(fluxer_error) => assert_third_party_signed(row)
Err(fluxer_error) => assert_third_party_signed(row, relative)
.map_err(|third_party_error| anyhow!("{fluxer_error}; {third_party_error}")),
}
}
@@ -2333,22 +2402,20 @@ fn verify_windows_pe_signatures(
));
continue;
};
if let Err(error) = assert_signed_by_known_publisher(row) {
if let Err(error) = assert_signed_by_known_publisher(row, &relative) {
failures.push(format!("{relative}: {error}"));
}
}
ensure!(
failures.is_empty(),
"{label}: {} of {} Windows binaries are not signed by '{}':\n{}",
"{label}: {} of {} Windows binaries do not have an approved signature:\n{}",
failures.len(),
files.len(),
FLUXER_WINDOWS_SIGNER_COMMON_NAME,
failures.join("\n")
);
println!(
"{label}: verified {} Windows binaries signed by '{}'.",
files.len(),
FLUXER_WINDOWS_SIGNER_COMMON_NAME
"{label}: verified {} Windows binary signatures.",
files.len()
);
Ok(())
}