Files
deepseek-harness/apps/desktop/scripts/desktop-auto-update-environment.mjs
T

170 lines
6.6 KiB
JavaScript

/** Resolve the Desktop auto-update channel and its Tencent COS destination. */
import { prerelease, valid } from 'semver'
/** Environment variable that selects the Desktop update deployment. */
export const DESKTOP_AUTO_UPDATE_ENV = 'DSH_DESKTOP_AUTO_UPDATE_ENV'
const UPDATE_ENVIRONMENTS = {
test: {
originEnvName: 'DOWNLOAD_TEST_ORIGIN',
fixedOrigin: undefined,
bucketEnvName: 'DOWNLOAD_TEST_COS_BUCKET',
secretIdEnvName: 'DOWNLOAD_TEST_COS_SECRET_ID',
secretKeyEnvName: 'DOWNLOAD_TEST_COS_SECRET_KEY',
},
production: {
originEnvName: undefined,
fixedOrigin: 'https://download.deepseek.com',
bucketEnvName: 'DOWNLOAD_PROD_COS_BUCKET',
secretIdEnvName: 'DOWNLOAD_PROD_COS_SECRET_ID',
secretKeyEnvName: 'DOWNLOAD_PROD_COS_SECRET_KEY',
},
}
const UPDATE_TARGETS = new Set(['mac-arm64', 'mac-x64', 'win-x64'])
/**
* Resolve the update deployment, defaulting local release work to test.
* @param {NodeJS.ProcessEnv} env - Packaging or upload environment.
* @returns {'test' | 'production'} Validated deployment name.
*/
export function resolveDesktopAutoUpdateEnvironment(env) {
const value = env[DESKTOP_AUTO_UPDATE_ENV]?.trim() || 'test'
if (value !== 'test' && value !== 'production') {
throw new Error(`desktop auto-update: ${DESKTOP_AUTO_UPDATE_ENV} must be "test" or "production"`)
}
return value
}
/**
* Resolve one supported platform and architecture to its update directory.
* @param {NodeJS.Platform} platform - Target Node.js platform.
* @param {string} arch - Target Node.js architecture.
* @returns {'mac-arm64' | 'mac-x64' | 'win-x64'} Update target directory.
*/
export function resolveDesktopAutoUpdateTarget(platform, arch) {
const os = platform === 'darwin' ? 'mac' : platform === 'win32' ? 'win' : platform
const target = `${os}-${arch}`
if (!UPDATE_TARGETS.has(target)) {
throw new Error(`desktop auto-update: unsupported target ${target}`)
}
return target
}
/**
* Return the local completion record filename for one packaged target.
* @param {'mac-arm64' | 'mac-x64' | 'win-x64'} target - Supported release target.
* @returns {string} Filename stored beside electron-builder artifacts.
*/
export function desktopBuildRecordFilename(target) {
if (!UPDATE_TARGETS.has(target)) {
throw new Error(`desktop auto-update: unsupported target ${target}`)
}
return `${target}-release.json`
}
/**
* Return the electron-builder channel metadata filename for an application version.
* @param {string} version - Desktop semantic version.
* @param {NodeJS.Platform} platform - Target platform.
* @returns {string} Channel metadata filename emitted for the target.
*/
export function desktopUpdateMetadataFilename(version, platform) {
if (valid(version) === null) {
throw new Error(`desktop auto-update: invalid Desktop version ${JSON.stringify(version)}`)
}
if (platform !== 'darwin' && platform !== 'win32') {
throw new Error(`desktop auto-update: unsupported metadata platform ${platform}`)
}
const release = prerelease(version)
const channel = release === null ? 'latest' : String(release[0])
return `${channel}${platform === 'darwin' ? '-mac' : ''}.yml`
}
/**
* Read one required release setting without accepting whitespace-only values.
* @param {NodeJS.ProcessEnv} env - Packaging or upload environment.
* @param {string} name - Environment variable to read.
* @returns {string} Trimmed setting.
*/
function requiredEnvironmentValue(env, name) {
const value = env[name]?.trim()
if (value === undefined || value === '') {
throw new Error(`desktop auto-update: ${name} must be set to a non-empty value`)
}
return value
}
/**
* Normalize an HTTPS origin and reject paths or credentials.
* @param {string} value - Candidate origin.
* @param {string} name - Environment variable used in diagnostics.
* @returns {string} Normalized HTTPS origin without a trailing slash.
*/
function httpsOrigin(value, name) {
let parsed
try {
parsed = new URL(value)
}
catch {
throw new Error(`desktop auto-update: ${name} must be an absolute HTTPS origin`)
}
if (parsed.protocol !== 'https:'
|| parsed.username !== ''
|| parsed.password !== ''
|| parsed.pathname !== '/'
|| parsed.search !== ''
|| parsed.hash !== '') {
throw new Error(`desktop auto-update: ${name} must be an absolute HTTPS origin without a path, credentials, query, or fragment`)
}
return parsed.origin
}
/**
* Resolve the public updater URL for one release target.
* @param {NodeJS.ProcessEnv} env - Packaging or upload environment.
* @param {NodeJS.Platform} platform - Target Node.js platform.
* @param {string} arch - Target Node.js architecture.
* @returns {{ environment: 'test' | 'production', target: 'mac-arm64' | 'mac-x64' | 'win-x64', origin: string, publicUrl: string, keyPrefix: string }} Resolved updater configuration.
* @throws {Error} When the test deployment lacks a valid HTTPS origin.
*/
export function resolveDesktopAutoUpdateConfig(env, platform, arch) {
const environment = resolveDesktopAutoUpdateEnvironment(env)
const target = resolveDesktopAutoUpdateTarget(platform, arch)
const deployment = UPDATE_ENVIRONMENTS[environment]
let origin = deployment.fixedOrigin
if (origin === undefined) {
const { originEnvName } = deployment
if (originEnvName === undefined) throw new Error('desktop auto-update: selected deployment has no origin')
origin = httpsOrigin(requiredEnvironmentValue(env, originEnvName), originEnvName)
}
const keyPrefix = `_/harness/desktop/stable/${target}`
return {
environment,
target,
origin,
keyPrefix,
publicUrl: `${origin}/${keyPrefix}/`,
}
}
/**
* Resolve the public updater URL and private COS destination for one upload target.
* @param {NodeJS.ProcessEnv} env - Upload environment.
* @param {NodeJS.Platform} platform - Target Node.js platform.
* @param {string} arch - Target Node.js architecture.
* @returns {{ environment: 'test' | 'production', target: 'mac-arm64' | 'mac-x64' | 'win-x64', origin: string, publicUrl: string, keyPrefix: string, bucket: string, secretIdEnvName: string, secretKeyEnvName: string }} Resolved upload configuration.
* @throws {Error} When the selected deployment lacks a required origin or bucket, or the test origin is not HTTPS.
*/
export function resolveDesktopUploadConfig(env, platform, arch) {
const update = resolveDesktopAutoUpdateConfig(env, platform, arch)
const deployment = UPDATE_ENVIRONMENTS[update.environment]
return {
...update,
bucket: requiredEnvironmentValue(env, deployment.bucketEnvName),
secretIdEnvName: deployment.secretIdEnvName,
secretKeyEnvName: deployment.secretKeyEnvName,
}
}