Prepare Windows release and signing process (#16)

This commit is contained in:
2026-03-08 19:51:30 -07:00
committed by GitHub
parent 34d2dce8dc
commit c799a8de3c
113 changed files with 5042 additions and 386 deletions

View File

@@ -7,16 +7,28 @@ import {
readSetupState,
writeSetupState,
type SetupPluginInstallStatus,
type SetupWindowsMpvShortcutInstallStatus,
type SetupState,
} from '../../shared/setup-state';
import type { CliArgs } from '../../cli/args';
export interface SetupWindowsMpvShortcutSnapshot {
supported: boolean;
startMenuEnabled: boolean;
desktopEnabled: boolean;
startMenuInstalled: boolean;
desktopInstalled: boolean;
status: 'installed' | 'optional' | 'skipped' | 'failed';
message: string | null;
}
export interface SetupStatusSnapshot {
configReady: boolean;
dictionaryCount: number;
canFinish: boolean;
pluginStatus: 'installed' | 'optional' | 'skipped' | 'failed';
pluginInstallPathSummary: string | null;
windowsMpvShortcuts: SetupWindowsMpvShortcutSnapshot;
message: string | null;
state: SetupState;
}
@@ -37,6 +49,10 @@ export interface FirstRunSetupService {
markSetupCompleted: () => Promise<SetupStatusSnapshot>;
skipPluginInstall: () => Promise<SetupStatusSnapshot>;
installMpvPlugin: () => Promise<SetupStatusSnapshot>;
configureWindowsMpvShortcuts: (preferences: {
startMenuEnabled: boolean;
desktopEnabled: boolean;
}) => Promise<SetupStatusSnapshot>;
isSetupCompleted: () => boolean;
}
@@ -44,6 +60,7 @@ function hasAnyStartupCommandBeyondSetup(args: CliArgs): boolean {
return Boolean(
args.toggle ||
args.toggleVisibleOverlay ||
args.launchMpv ||
args.settings ||
args.show ||
args.hide ||
@@ -95,15 +112,51 @@ function getPluginStatus(
return 'optional';
}
function getWindowsMpvShortcutStatus(
state: SetupState,
installed: { startMenuInstalled: boolean; desktopInstalled: boolean },
): SetupWindowsMpvShortcutSnapshot['status'] {
if (installed.startMenuInstalled || installed.desktopInstalled) return 'installed';
if (state.windowsMpvShortcutLastStatus === 'skipped') return 'skipped';
if (state.windowsMpvShortcutLastStatus === 'failed') return 'failed';
return 'optional';
}
function getEffectiveWindowsMpvShortcutPreferences(
state: SetupState,
installed: { startMenuInstalled: boolean; desktopInstalled: boolean },
): { startMenuEnabled: boolean; desktopEnabled: boolean } {
if (state.windowsMpvShortcutLastStatus === 'unknown') {
return {
startMenuEnabled: installed.startMenuInstalled,
desktopEnabled: installed.desktopInstalled,
};
}
return {
startMenuEnabled: state.windowsMpvShortcutPreferences.startMenuEnabled,
desktopEnabled: state.windowsMpvShortcutPreferences.desktopEnabled,
};
}
export function createFirstRunSetupService(deps: {
platform?: NodeJS.Platform;
configDir: string;
getYomitanDictionaryCount: () => Promise<number>;
detectPluginInstalled: () => boolean | Promise<boolean>;
installPlugin: () => Promise<PluginInstallResult>;
detectWindowsMpvShortcuts?: () =>
| { startMenuInstalled: boolean; desktopInstalled: boolean }
| Promise<{ startMenuInstalled: boolean; desktopInstalled: boolean }>;
applyWindowsMpvShortcuts?: (preferences: {
startMenuEnabled: boolean;
desktopEnabled: boolean;
}) => Promise<{ ok: boolean; status: SetupWindowsMpvShortcutInstallStatus; message: string }>;
onStateChanged?: (state: SetupState) => void;
}): FirstRunSetupService {
const setupStatePath = getSetupStatePath(deps.configDir);
const configFilePaths = getDefaultConfigFilePaths(deps.configDir);
const isWindows = (deps.platform ?? process.platform) === 'win32';
let completed = false;
const readState = (): SetupState => readSetupState(setupStatePath) ?? createDefaultSetupState();
@@ -117,6 +170,17 @@ export function createFirstRunSetupService(deps: {
const buildSnapshot = async (state: SetupState, message: string | null = null) => {
const dictionaryCount = await deps.getYomitanDictionaryCount();
const pluginInstalled = await deps.detectPluginInstalled();
const detectedWindowsMpvShortcuts = isWindows
? await deps.detectWindowsMpvShortcuts?.()
: undefined;
const installedWindowsMpvShortcuts = {
startMenuInstalled: detectedWindowsMpvShortcuts?.startMenuInstalled ?? false,
desktopInstalled: detectedWindowsMpvShortcuts?.desktopInstalled ?? false,
};
const effectiveWindowsMpvShortcutPreferences = getEffectiveWindowsMpvShortcutPreferences(
state,
installedWindowsMpvShortcuts,
);
const configReady =
fs.existsSync(configFilePaths.jsoncPath) || fs.existsSync(configFilePaths.jsonPath);
return {
@@ -125,6 +189,15 @@ export function createFirstRunSetupService(deps: {
canFinish: dictionaryCount >= 1,
pluginStatus: getPluginStatus(state, pluginInstalled),
pluginInstallPathSummary: state.pluginInstallPathSummary,
windowsMpvShortcuts: {
supported: isWindows,
startMenuEnabled: effectiveWindowsMpvShortcutPreferences.startMenuEnabled,
desktopEnabled: effectiveWindowsMpvShortcutPreferences.desktopEnabled,
startMenuInstalled: installedWindowsMpvShortcuts.startMenuInstalled,
desktopInstalled: installedWindowsMpvShortcuts.desktopInstalled,
status: getWindowsMpvShortcutStatus(state, installedWindowsMpvShortcuts),
message: null,
},
message,
state,
} satisfies SetupStatusSnapshot;
@@ -220,6 +293,33 @@ export function createFirstRunSetupService(deps: {
result.message,
);
},
configureWindowsMpvShortcuts: async (preferences) => {
if (!isWindows || !deps.applyWindowsMpvShortcuts) {
return refreshWithState(
writeState({
...readState(),
windowsMpvShortcutPreferences: {
startMenuEnabled: preferences.startMenuEnabled,
desktopEnabled: preferences.desktopEnabled,
},
}),
null,
);
}
const result = await deps.applyWindowsMpvShortcuts(preferences);
const latestState = readState();
return refreshWithState(
writeState({
...latestState,
windowsMpvShortcutPreferences: {
startMenuEnabled: preferences.startMenuEnabled,
desktopEnabled: preferences.desktopEnabled,
},
windowsMpvShortcutLastStatus: result.status,
}),
result.message,
);
},
isSetupCompleted: () => completed || isSetupCompleted(readState()),
};
}