refactor(client): unify web and desktop release workflow
Client Quality Gates / Shared client and Web (push) Has been cancelled
Client Quality Gates / macOS Desktop (push) Has been cancelled

This commit is contained in:
Cheng Zhou
2026-06-30 20:25:07 +08:00
parent d1a6c957f7
commit 7c721d4e5c
21 changed files with 1016 additions and 14 deletions
+3 -4
View File
@@ -1,15 +1,14 @@
import axios from "axios";
import type { AxiosResponse } from "axios";
import { resolveApiBaseUrl } from "../runtime/apiBaseUrl";
import { DESKTOP_SERVER_URL_CHANGED_EVENT } from "../runtime/desktopServerConfig";
import { clientRuntime, DESKTOP_SERVER_URL_CHANGED_EVENT } from "../runtime";
const authClient = axios.create({
baseURL: resolveApiBaseUrl(),
baseURL: clientRuntime.apiBaseUrl(),
timeout: 15000,
});
export const refreshAuthClientBaseUrl = (): void => {
authClient.defaults.baseURL = resolveApiBaseUrl();
authClient.defaults.baseURL = clientRuntime.apiBaseUrl();
};
if (typeof window !== "undefined") {
+3 -4
View File
@@ -3,16 +3,15 @@ import { ElMessage } from "element-plus";
import { getToken } from "../utils/auth";
import type { ApiError } from "../types/api";
import { TEXT } from "../locales";
import { resolveApiBaseUrl } from "../runtime/apiBaseUrl";
import { DESKTOP_SERVER_URL_CHANGED_EVENT } from "../runtime/desktopServerConfig";
import { clientRuntime, DESKTOP_SERVER_URL_CHANGED_EVENT } from "../runtime";
const instance: AxiosInstance = axios.create({
baseURL: resolveApiBaseUrl(),
baseURL: clientRuntime.apiBaseUrl(),
timeout: 15000,
});
export const refreshApiBaseUrl = (): void => {
instance.defaults.baseURL = resolveApiBaseUrl();
instance.defaults.baseURL = clientRuntime.apiBaseUrl();
};
if (typeof window !== "undefined") {
+9
View File
@@ -1,5 +1,14 @@
/// <reference types="vite/client" />
interface ImportMetaEnv {
readonly VITE_BUILD_CHANNEL?: "dev" | "main" | "release" | "local";
readonly VITE_BUILD_COMMIT?: string;
}
interface ImportMeta {
readonly env: ImportMetaEnv;
}
declare module "*.vue" {
import type { DefineComponent } from "vue";
const component: DefineComponent<{}, {}, any>;
+1 -1
View File
@@ -12,7 +12,7 @@ import App from "./App.vue";
import router from "./router";
import { getToken } from "./utils/auth";
import { useStudyStore } from "./store/study";
import { shouldRequireDesktopServerUrl } from "./runtime/desktopServerConfig";
import { shouldRequireDesktopServerUrl } from "./runtime";
const bootstrap = async () => {
const app = createApp(App);
+1 -2
View File
@@ -1,6 +1,5 @@
import type { RouteLocationNormalized } from "vue-router";
import { hasDesktopServerUrl, shouldRequireDesktopServerUrl } from "../runtime/desktopServerConfig";
import { isTauriRuntime } from "../runtime/platform";
import { hasDesktopServerUrl, isTauriRuntime, shouldRequireDesktopServerUrl } from "../runtime";
const DESKTOP_SETTINGS_PATH = "/desktop/server-settings";
+35
View File
@@ -0,0 +1,35 @@
import { afterEach, describe, expect, it } from "vitest";
import packageInfo from "../../package.json";
import { clientRuntime } from "./clientRuntime";
import { getAppMetadata } from "./appMetadata";
afterEach(() => {
Reflect.deleteProperty(window, "__TAURI_INTERNALS__");
});
describe("client runtime", () => {
it("reports a web runtime with desktop capabilities disabled", () => {
expect(getAppMetadata()).toMatchObject({
version: packageInfo.version,
commit: "local",
channel: "local",
clientType: "web",
platform: "web",
});
expect(clientRuntime.capabilities()).toEqual({
serverConfiguration: false,
nativeFiles: false,
systemNotifications: false,
secureSessionStorage: false,
automaticUpdates: false,
});
});
it("exposes only the implemented desktop capability", () => {
Object.defineProperty(window, "__TAURI_INTERNALS__", { value: {}, configurable: true });
expect(getAppMetadata().clientType).toBe("desktop");
expect(clientRuntime.capabilities().serverConfiguration).toBe(true);
expect(clientRuntime.capabilities().secureSessionStorage).toBe(false);
});
});
+26
View File
@@ -0,0 +1,26 @@
import packageInfo from "../../package.json";
import { getRuntimePlatform, isTauriRuntime, type RuntimePlatform } from "./platform";
export type ClientType = "web" | "desktop";
export type BuildChannel = "dev" | "main" | "release" | "local";
export interface AppMetadata {
version: string;
commit: string;
channel: BuildChannel;
clientType: ClientType;
platform: RuntimePlatform;
}
const BUILD_CHANNELS = new Set<BuildChannel>(["dev", "main", "release", "local"]);
const resolveBuildChannel = (value: string | undefined): BuildChannel =>
value && BUILD_CHANNELS.has(value as BuildChannel) ? (value as BuildChannel) : "local";
export const getAppMetadata = (): AppMetadata => ({
version: packageInfo.version,
commit: import.meta.env.VITE_BUILD_COMMIT || "local",
channel: resolveBuildChannel(import.meta.env.VITE_BUILD_CHANNEL),
clientType: isTauriRuntime() ? "desktop" : "web",
platform: getRuntimePlatform(),
});
+29
View File
@@ -0,0 +1,29 @@
import { resolveApiBaseUrl } from "./apiBaseUrl";
import { getAppMetadata } from "./appMetadata";
import { isTauriRuntime } from "./platform";
export interface RuntimeCapabilities {
serverConfiguration: boolean;
nativeFiles: boolean;
systemNotifications: boolean;
secureSessionStorage: boolean;
automaticUpdates: boolean;
}
export interface ClientRuntime {
apiBaseUrl(): string;
metadata: typeof getAppMetadata;
capabilities(): RuntimeCapabilities;
}
export const clientRuntime: ClientRuntime = {
apiBaseUrl: resolveApiBaseUrl,
metadata: getAppMetadata,
capabilities: () => ({
serverConfiguration: isTauriRuntime(),
nativeFiles: false,
systemNotifications: false,
secureSessionStorage: false,
automaticUpdates: false,
}),
};
+12
View File
@@ -0,0 +1,12 @@
export { clientRuntime, type ClientRuntime, type RuntimeCapabilities } from "./clientRuntime";
export {
clearDesktopServerUrl,
DESKTOP_SERVER_URL_CHANGED_EVENT,
getDesktopServerUrl,
hasDesktopServerUrl,
normalizeDesktopServerUrl,
setDesktopServerUrl,
shouldRequireDesktopServerUrl,
} from "./desktopServerConfig";
export { getAppMetadata, type AppMetadata, type BuildChannel, type ClientType } from "./appMetadata";
export { getRuntimePlatform, isTauriRuntime, type RuntimePlatform } from "./platform";
+1 -1
View File
@@ -37,7 +37,7 @@ import { useRouter } from "vue-router";
import { ElMessage } from "element-plus";
import { useAuthStore } from "../store/auth";
import { useStudyStore } from "../store/study";
import { getDesktopServerUrl, normalizeDesktopServerUrl, setDesktopServerUrl } from "../runtime/desktopServerConfig";
import { getDesktopServerUrl, normalizeDesktopServerUrl, setDesktopServerUrl } from "../runtime";
const router = useRouter();
const auth = useAuthStore();
+1 -1
View File
@@ -260,7 +260,7 @@ import { useAuthStore } from "../store/auth";
import { useStudyStore } from "../store/study";
import { fetchEmailDomains } from "../api/auth";
import { TEXT, requiredMessage } from "../locales";
import { isTauriRuntime } from "../runtime/platform";
import { isTauriRuntime } from "../runtime";
import {
consumeLogoutReason,
LOGOUT_REASON_AUTH_EXPIRED,