From 9bec59b70ac1c92de19ef9037c63daaaa6976732 Mon Sep 17 00:00:00 2001 From: trevor-signal <131492920+trevor-signal@users.noreply.github.com> Date: Thu, 16 Jan 2025 11:44:23 -0500 Subject: [PATCH] Ensure adhoc builds expire correctly --- ts/scripts/get-expire-time.ts | 9 ++++++--- ts/state/selectors/expiration.ts | 8 +++++--- ts/state/selectors/items.ts | 10 ++++++++-- ts/types/Settings.ts | 13 ++++++++++--- ts/updater/common.ts | 8 ++++---- ts/util/version.ts | 2 ++ ts/windows/settings/preload.ts | 5 ++++- 7 files changed, 39 insertions(+), 16 deletions(-) diff --git a/ts/scripts/get-expire-time.ts b/ts/scripts/get-expire-time.ts index 7120b3d99..4d33e266f 100644 --- a/ts/scripts/get-expire-time.ts +++ b/ts/scripts/get-expire-time.ts @@ -7,7 +7,7 @@ import { writeFileSync } from 'fs'; import { DAY } from '../util/durations'; import { version } from '../../package.json'; -import { isAdhoc } from '../util/version'; +import { isNotUpdatable } from '../util/version'; const unixTimestamp = parseInt( process.env.SOURCE_DATE_EPOCH || @@ -16,7 +16,10 @@ const unixTimestamp = parseInt( ); const buildCreation = unixTimestamp * 1000; -const buildExpiration = buildCreation + DAY * 90; +// NB: Build expirations are also determined via users' auto-update settings; see +// getExpirationTimestamp +const validDuration = isNotUpdatable(version) ? DAY * 30 : DAY * 90; +const buildExpiration = buildCreation + validDuration; const localProductionPath = join( __dirname, @@ -26,7 +29,7 @@ const localProductionPath = join( const localProductionConfig = { buildCreation, buildExpiration, - ...(isAdhoc(version) ? { updatesEnabled: false } : {}), + ...(isNotUpdatable(version) ? { updatesEnabled: false } : {}), }; writeFileSync( diff --git a/ts/state/selectors/expiration.ts b/ts/state/selectors/expiration.ts index c877ea0ca..3b5772979 100644 --- a/ts/state/selectors/expiration.ts +++ b/ts/state/selectors/expiration.ts @@ -10,6 +10,7 @@ import * as log from '../../logging/log'; import type { StateType } from '../reducer'; import type { ExpirationStateType } from '../ducks/expiration'; import { getRemoteBuildExpiration, getAutoDownloadUpdate } from './items'; +import { isNotUpdatable } from '../../util/version'; const NINETY_ONE_DAYS = 91 * DAY; const THIRTY_ONE_DAYS = 31 * DAY; @@ -32,9 +33,10 @@ export const getExpirationTimestamp = createSelector( remoteBuildExpiration: number | undefined, autoDownloadUpdate: boolean ): number => { - const localBuildExpiration = autoDownloadUpdate - ? buildExpiration - : buildExpiration - SIXTY_DAYS; + const localBuildExpiration = + isNotUpdatable(window.getVersion()) || autoDownloadUpdate + ? buildExpiration + : buildExpiration - SIXTY_DAYS; // Log the expiration date in this selector because it invalidates only // if one of the arguments changes. diff --git a/ts/state/selectors/items.ts b/ts/state/selectors/items.ts index 2f5f97ae2..31a57b7d3 100644 --- a/ts/state/selectors/items.ts +++ b/ts/state/selectors/items.ts @@ -19,6 +19,7 @@ import { getPreferredReactionEmoji as getPreferredReactionEmojiFromStoredValue } import { DurationInSeconds } from '../../util/durations'; import * as Bytes from '../../Bytes'; import { contactByEncryptedUsernameRoute } from '../../util/signalRoutes'; +import { isNotUpdatable } from '../../util/version'; const DEFAULT_PREFERRED_LEFT_PANE_WIDTH = 320; @@ -215,8 +216,13 @@ export const getRemoteBuildExpiration = createSelector( export const getAutoDownloadUpdate = createSelector( getItems, - (state: ItemsStateType): boolean => - Boolean(state['auto-download-update'] ?? true) + (state: ItemsStateType): boolean => { + if (isNotUpdatable(window.getVersion())) { + return false; + } + + return Boolean(state['auto-download-update'] ?? true); + } ); export const getTextFormattingEnabled = createSelector( diff --git a/ts/types/Settings.ts b/ts/types/Settings.ts index 7a045ee25..9e2436f95 100644 --- a/ts/types/Settings.ts +++ b/ts/types/Settings.ts @@ -5,7 +5,7 @@ import semver from 'semver'; import type { OSType } from '../util/os/shared'; import { SystemTraySetting } from './SystemTraySetting'; -import { isProduction } from '../util/version'; +import { isNotUpdatable, isProduction } from '../util/version'; const MIN_WINDOWS_VERSION = '8.0.0'; @@ -56,8 +56,15 @@ export const isMinimizeToAndStartInSystemTraySupported = ( OS: OSType ): boolean => !OS.isWindows() && isSystemTraySupported(OS); -export const isAutoDownloadUpdatesSupported = (OS: OSType): boolean => - OS.isWindows() || OS.isMacOS(); +export const isAutoDownloadUpdatesSupported = ( + OS: OSType, + appVersion: string +): boolean => { + if (isNotUpdatable(appVersion)) { + return false; + } + return OS.isWindows() || OS.isMacOS(); +}; export const shouldHideExpiringMessageBody = ( OS: OSType, diff --git a/ts/updater/common.ts b/ts/updater/common.ts index b9f734d50..e85c7976a 100644 --- a/ts/updater/common.ts +++ b/ts/updater/common.ts @@ -28,10 +28,10 @@ import { strictAssert } from '../util/assert'; import { drop } from '../util/drop'; import * as durations from '../util/durations'; import { - isAdhoc, isAlpha, isAxolotl, isBeta, + isNotUpdatable, isStaging, } from '../util/version'; @@ -527,9 +527,9 @@ export abstract class Updater { async #checkForUpdates( checkType: CheckType ): Promise { - if (isAdhoc(packageJson.version)) { + if (isNotUpdatable(packageJson.version)) { this.logger.info( - 'checkForUpdates: not checking for updates, this is an adhoc build' + 'checkForUpdates: not checking for updates, this is not an updatable build' ); return; } @@ -950,7 +950,7 @@ export function getUpdatesFileName(): string { function getChannel(): string { const { version } = packageJson; - if (isAdhoc(version)) { + if (isNotUpdatable(version)) { // we don't want ad hoc versions to update return version; } diff --git a/ts/util/version.ts b/ts/util/version.ts index 3d7100468..1e1f23301 100644 --- a/ts/util/version.ts +++ b/ts/util/version.ts @@ -28,6 +28,8 @@ export const isAxolotl = (version: string): boolean => export const isAdhoc = (version: string): boolean => semver.parse(version)?.prerelease[0] === 'adhoc'; +export const isNotUpdatable = (version: string): boolean => isAdhoc(version); + export const isStaging = (version: string): boolean => semver.parse(version)?.prerelease[0] === 'staging'; diff --git a/ts/windows/settings/preload.ts b/ts/windows/settings/preload.ts index 2d922765d..e192a501a 100644 --- a/ts/windows/settings/preload.ts +++ b/ts/windows/settings/preload.ts @@ -330,7 +330,10 @@ async function renderPreferences() { setGlobalDefaultConversationColor: ipcSetGlobalDefaultConversationColor, // Limited support features - isAutoDownloadUpdatesSupported: Settings.isAutoDownloadUpdatesSupported(OS), + isAutoDownloadUpdatesSupported: Settings.isAutoDownloadUpdatesSupported( + OS, + MinimalSignalContext.getVersion() + ), isAutoLaunchSupported: Settings.isAutoLaunchSupported(OS), isHideMenuBarSupported: Settings.isHideMenuBarSupported(OS), isNotificationAttentionSupported: Settings.isDrawAttentionSupported(OS),