diff --git a/app/config.ts b/app/config.ts index f03f64444356..f38515e1c35a 100644 --- a/app/config.ts +++ b/app/config.ts @@ -15,7 +15,7 @@ import { // In production mode, NODE_ENV cannot be customized by the user if (app.isPackaged) { - setEnvironment(Environment.Production, false); + setEnvironment(Environment.PackagedApp, false); } else { setEnvironment( parseEnvironment(process.env.NODE_ENV || 'development'), @@ -27,7 +27,7 @@ if (app.isPackaged) { process.env.NODE_ENV = getEnvironment(); process.env.NODE_CONFIG_DIR = join(__dirname, '..', 'config'); -if (getEnvironment() === Environment.Production) { +if (getEnvironment() === Environment.PackagedApp) { // harden production config against the local env process.env.NODE_CONFIG = ''; process.env.NODE_CONFIG_STRICT_MODE = ''; @@ -47,7 +47,7 @@ if (getEnvironment() === Environment.Production) { // eslint-disable-next-line @typescript-eslint/no-var-requires const config: IConfig = require('config'); -if (getEnvironment() !== Environment.Production) { +if (getEnvironment() !== Environment.PackagedApp) { config.util.getConfigSources().forEach(source => { console.log(`config: Using config source ${basename(source.name)}`); }); diff --git a/app/main.ts b/app/main.ts index 2d19c3210b6e..246dc34179f0 100644 --- a/app/main.ts +++ b/app/main.ts @@ -199,7 +199,7 @@ const cliOptions = cliParser.parse(process.argv); const defaultWebPrefs = { devTools: process.argv.some(arg => arg === '--enable-dev-tools') || - getEnvironment() !== Environment.Production || + getEnvironment() !== Environment.PackagedApp || !isProduction(app.getVersion()), spellcheck: false, // https://chromium.googlesource.com/chromium/src/+/main/third_party/blink/renderer/platform/runtime_enabled_features.json5 @@ -2713,7 +2713,7 @@ ipc.on('get-config', async event => { certificateAuthority: config.get('certificateAuthority'), environment: !isTestEnvironment(getEnvironment()) && ciMode - ? Environment.Production + ? Environment.PackagedApp : getEnvironment(), isMockTestEnvironment: Boolean(process.env.MOCK_TEST), ciMode, diff --git a/ts/Crypto.ts b/ts/Crypto.ts index 191422d8655a..1e1c1d129fa0 100644 --- a/ts/Crypto.ts +++ b/ts/Crypto.ts @@ -12,7 +12,7 @@ import { HashType, CipherType, UUID_BYTE_SIZE } from './types/Crypto'; import { ProfileDecryptError } from './types/errors'; import { getBytesSubarray } from './util/uuidToBytes'; import { logPadSize } from './util/logPadding'; -import { Environment } from './environment'; +import { Environment, getEnvironment } from './environment'; export { HashType, CipherType }; @@ -636,7 +636,7 @@ export function encryptAttachment({ throw new Error(`${logId}: invalid length attachment keys`); } - if (dangerousTestOnlyIv && window.getEnvironment() !== Environment.Test) { + if (dangerousTestOnlyIv && getEnvironment() !== Environment.Test) { throw new Error(`${logId}: Used dangerousTestOnlyIv outside tests!`); } diff --git a/ts/background.ts b/ts/background.ts index 94e3db11ea6c..82a17647d9f5 100644 --- a/ts/background.ts +++ b/ts/background.ts @@ -126,6 +126,7 @@ import { SendStatus } from './messages/MessageSendState'; import * as Stickers from './types/Stickers'; import * as Errors from './types/errors'; import { InstallScreenStep } from './types/InstallScreen'; +import { getEnvironment } from './environment'; import { SignalService as Proto } from './protobuf'; import { onRetryRequest, @@ -348,7 +349,7 @@ export async function startApp(): Promise { const { upgradeMessageSchema } = window.Signal.Migrations; log.info('background page reloaded'); - log.info('environment:', window.getEnvironment()); + log.info('environment:', getEnvironment()); let newVersion = false; let lastVersion: string | undefined; diff --git a/ts/components/ChatsTab.tsx b/ts/components/ChatsTab.tsx index 198007e53c66..18256d96cf98 100644 --- a/ts/components/ChatsTab.tsx +++ b/ts/components/ChatsTab.tsx @@ -2,7 +2,6 @@ // SPDX-License-Identifier: AGPL-3.0-only import React from 'react'; -import { Environment, getEnvironment } from '../environment'; import type { LocalizerType } from '../types/I18N'; import type { NavTabPanelProps } from './NavTabs'; import { WhatsNewLink } from './WhatsNewLink'; @@ -11,6 +10,7 @@ import type { UnreadStats } from '../util/countUnreadStats'; type ChatsTabProps = Readonly<{ otherTabsUnreadStats: UnreadStats; i18n: LocalizerType; + isStaging: boolean; hasPendingUpdate: boolean; hasFailedStorySends: boolean; navTabsCollapsed: boolean; @@ -25,6 +25,7 @@ type ChatsTabProps = Readonly<{ export function ChatsTab({ otherTabsUnreadStats, i18n, + isStaging, hasPendingUpdate, hasFailedStorySends, navTabsCollapsed, @@ -60,9 +61,9 @@ export function ChatsTab({ {renderMiniPlayer({ shouldFlow: false })}

- {getEnvironment() !== Environment.Staging - ? i18n('icu:welcomeToSignal') - : 'THIS IS A STAGING DESKTOP'} + {isStaging + ? 'THIS IS A STAGING DESKTOP' + : i18n('icu:welcomeToSignal')}

diff --git a/ts/components/QrCode.tsx b/ts/components/QrCode.tsx index 1d865ec99c8d..38b846f28514 100644 --- a/ts/components/QrCode.tsx +++ b/ts/components/QrCode.tsx @@ -34,7 +34,7 @@ export function QrCode(props: PropsType): ReactElement { // simulator primary, which has a debug-only option to paste the linking URL instead of // scanning it. (By the time you read this comment Android may have a similar feature.) const onDoubleClick = () => { - if (getEnvironment() === Environment.Production) { + if (getEnvironment() === Environment.PackagedApp) { return; } if (data instanceof Uint8Array) { diff --git a/ts/components/installScreen/InstallScreenChoosingDeviceNameStep.tsx b/ts/components/installScreen/InstallScreenChoosingDeviceNameStep.tsx index b1a216290e81..919f19d4b7a1 100644 --- a/ts/components/installScreen/InstallScreenChoosingDeviceNameStep.tsx +++ b/ts/components/installScreen/InstallScreenChoosingDeviceNameStep.tsx @@ -42,7 +42,7 @@ export function InstallScreenChoosingDeviceNameStep({ normalizedName.length <= MAX_DEVICE_NAME_LENGTH; let maybeBackupInput: JSX.Element | undefined; - if (getEnvironment() !== Environment.Production) { + if (getEnvironment() !== Environment.PackagedApp) { maybeBackupInput = (

diff --git a/ts/environment.ts b/ts/environment.ts index e3ee9f44df81..754988dae88f 100644 --- a/ts/environment.ts +++ b/ts/environment.ts @@ -7,7 +7,7 @@ import * as log from './logging/log'; // Many places rely on this enum being a string. export enum Environment { Development = 'development', - Production = 'production', + PackagedApp = 'production', Staging = 'staging', Test = 'test', } @@ -20,7 +20,7 @@ export function getEnvironment(): Environment { // This should never happen—we should always have initialized the environment by this // point. It'd be nice to log here but the logger depends on the environment and we // can't have circular dependencies. - return Environment.Production; + return Environment.PackagedApp; } return environment; } @@ -42,7 +42,7 @@ export function setEnvironment(env: Environment, isMockTestEnv: boolean): void { export const parseEnvironment = makeEnumParser( Environment, - Environment.Production + Environment.PackagedApp ); export const isTestEnvironment = (env: Environment): boolean => diff --git a/ts/logging/set_up_renderer_logging.ts b/ts/logging/set_up_renderer_logging.ts index b72676f1e9b3..a7d744b7ece0 100644 --- a/ts/logging/set_up_renderer_logging.ts +++ b/ts/logging/set_up_renderer_logging.ts @@ -85,7 +85,7 @@ export function initialize(): void { // A modern logging interface for the browser function logAtLevel(level: LogLevel, ...args: ReadonlyArray): void { - if (getEnvironment() !== Environment.Production) { + if (getEnvironment() !== Environment.PackagedApp) { const prefix = getLogLevelString(level) .toUpperCase() .padEnd(levelMaxLength, ' '); diff --git a/ts/state/createStore.ts b/ts/state/createStore.ts index 63608e9dac1b..b9cc9c0e3134 100644 --- a/ts/state/createStore.ts +++ b/ts/state/createStore.ts @@ -16,6 +16,7 @@ import { reducer } from './reducer'; import { dispatchItemsMiddleware } from '../shims/dispatchItemsMiddleware'; import { isOlderThan } from '../util/timestamp'; import { SECOND } from '../util/durations'; +import { getEnvironment } from '../environment'; declare global { // We want to extend `window`'s properties, so we need an interface. @@ -25,7 +26,7 @@ declare global { } } -const env = window.getEnvironment(); +const env = getEnvironment(); // So Redux logging doesn't go to disk, and so we can get colors/styles const directConsole = { diff --git a/ts/state/ducks/app.ts b/ts/state/ducks/app.ts index 33d0391b965a..7d9290e05d9c 100644 --- a/ts/state/ducks/app.ts +++ b/ts/state/ducks/app.ts @@ -7,6 +7,7 @@ import type { StateType as RootStateType } from '../reducer'; import type { BoundActionCreatorsMapObject } from '../../hooks/useBoundActions'; import { useBoundActions } from '../../hooks/useBoundActions'; import * as log from '../../logging/log'; +import { getEnvironment, Environment } from '../../environment'; import { START_INSTALLER, type StartInstallerActionType, @@ -90,7 +91,7 @@ function openStandalone(): ThunkAction< OpenStandaloneActionType > { return dispatch => { - if (window.getEnvironment() === 'production') { + if (getEnvironment() === Environment.PackagedApp) { return; } diff --git a/ts/state/selectors/expiration.ts b/ts/state/selectors/expiration.ts index 332c26525d7d..c877ea0ca8b9 100644 --- a/ts/state/selectors/expiration.ts +++ b/ts/state/selectors/expiration.ts @@ -61,7 +61,7 @@ export const hasExpired = createSelector( getAutoDownloadUpdate, (_: StateType, { now = Date.now() }: HasExpiredOptionsType = {}) => now, (buildExpiration: number, autoDownloadUpdate: boolean, now: number) => { - if (getEnvironment() !== Environment.Production && buildExpiration === 0) { + if (getEnvironment() !== Environment.PackagedApp && buildExpiration === 0) { return false; } diff --git a/ts/state/smart/ChatsTab.tsx b/ts/state/smart/ChatsTab.tsx index e5810e9837df..5584d9d4a59d 100644 --- a/ts/state/smart/ChatsTab.tsx +++ b/ts/state/smart/ChatsTab.tsx @@ -14,6 +14,7 @@ import { TargetedMessageSource } from '../ducks/conversationsEnums'; import { useConversationsActions } from '../ducks/conversations'; import { useToastActions } from '../ducks/toast'; import { strictAssert } from '../../util/assert'; +import { isStagingServer } from '../../util/isStagingServer'; import { ToastType } from '../../types/Toast'; import { getNavTabsCollapsed } from '../selectors/items'; import { useItemsActions } from '../ducks/items'; @@ -146,6 +147,7 @@ export const SmartChatsTab = memo(function SmartChatsTab() { { describe('parseEnvironment', () => { - it('returns Environment.Production for non-strings', () => { - assert.equal(parseEnvironment(undefined), Environment.Production); - assert.equal(parseEnvironment(0), Environment.Production); + it('returns Environment.PackagedApp for non-strings', () => { + assert.equal(parseEnvironment(undefined), Environment.PackagedApp); + assert.equal(parseEnvironment(0), Environment.PackagedApp); }); - it('returns Environment.Production for invalid strings', () => { - assert.equal(parseEnvironment(''), Environment.Production); - assert.equal(parseEnvironment(' development '), Environment.Production); - assert.equal(parseEnvironment('PRODUCTION'), Environment.Production); + it('returns Environment.PackagedApp for invalid strings', () => { + assert.equal(parseEnvironment(''), Environment.PackagedApp); + assert.equal(parseEnvironment(' development '), Environment.PackagedApp); + assert.equal(parseEnvironment('PRODUCTION'), Environment.PackagedApp); }); it('parses "development" as Environment.Development', () => { assert.equal(parseEnvironment('development'), Environment.Development); }); - it('parses "production" as Environment.Production', () => { - assert.equal(parseEnvironment('production'), Environment.Production); + it('parses "production" as Environment.PackagedApp', () => { + assert.equal(parseEnvironment('production'), Environment.PackagedApp); }); it('parses "staging" as Environment.Staging', () => { @@ -42,7 +42,7 @@ describe('environment utilities', () => { describe('isTestEnvironment', () => { it('returns false for non-test environments', () => { assert.isFalse(isTestEnvironment(Environment.Development)); - assert.isFalse(isTestEnvironment(Environment.Production)); + assert.isFalse(isTestEnvironment(Environment.PackagedApp)); assert.isFalse(isTestEnvironment(Environment.Staging)); }); diff --git a/ts/textsecure/WebAPI.ts b/ts/textsecure/WebAPI.ts index 0e99308cf024..f5f7ecb10835 100644 --- a/ts/textsecure/WebAPI.ts +++ b/ts/textsecure/WebAPI.ts @@ -70,8 +70,8 @@ import * as log from '../logging/log'; import { maybeParseUrl, urlPathFromComponents } from '../util/url'; import { SECOND } from '../util/durations'; import { safeParseNumber } from '../util/numbers'; +import { isStagingServer } from '../util/isStagingServer'; import type { IWebSocketResource } from './WebsocketResources'; -import { Environment, getEnvironment } from '../environment'; // Note: this will break some code that expects to be able to use err.response when a // web request fails, because it will force it to text. But it is very useful for @@ -81,26 +81,11 @@ const DEFAULT_TIMEOUT = 30 * SECOND; // Libsignal has internally configured values for domain names // (and other connectivity params) of the services. -function resolveLibsignalNetEnvironment( - appEnv: Environment, - url: string -): Net.Environment { - switch (appEnv) { - case Environment.Production: - return Net.Environment.Production; - case Environment.Development: - // In the case of the `Development` Desktop env, - // we should be checking the provided string value - // of `libsignalNetEnv` - if (/staging/i.test(url)) { - return Net.Environment.Staging; - } - return Net.Environment.Production; - case Environment.Test: - case Environment.Staging: - default: - return Net.Environment.Staging; +function resolveLibsignalNetEnvironment(url: string): Net.Environment { + if (isStagingServer(url)) { + return Net.Environment.Staging; } + return Net.Environment.Production; } function _createRedactor( @@ -1609,7 +1594,7 @@ export function initialize({ // for providing network layer API and related functionality. // It's important to have a single instance of this class as it holds // resources that are shared across all other use cases. - const env = resolveLibsignalNetEnvironment(getEnvironment(), url); + const env = resolveLibsignalNetEnvironment(url); log.info(`libsignal net environment resolved to [${Net.Environment[env]}]`); const libsignalNet = new Net.Net(env, getUserAgent(version)); libsignalNet.setIpv6Enabled(!disableIPv6); diff --git a/ts/util/assert.ts b/ts/util/assert.ts index 49a42eb20931..4827974085fb 100644 --- a/ts/util/assert.ts +++ b/ts/util/assert.ts @@ -29,7 +29,7 @@ export function assertDev( ): asserts condition { if (!condition) { const err = new Error(message); - if (getEnvironment() !== Environment.Production) { + if (getEnvironment() !== Environment.PackagedApp) { if (getEnvironment() === Environment.Development) { debugger; // eslint-disable-line no-debugger } diff --git a/ts/util/deprecated.ts b/ts/util/deprecated.ts index e40c6c457621..8f2ed59a357c 100644 --- a/ts/util/deprecated.ts +++ b/ts/util/deprecated.ts @@ -5,7 +5,7 @@ import { getEnvironment, Environment } from '../environment'; import * as log from '../logging/log'; export function deprecated(message?: string): void { - if (getEnvironment() === Environment.Development) { + if (getEnvironment() !== Environment.PackagedApp) { log.error(`This method is deprecated: ${message}`); } } diff --git a/ts/util/isBackupEnabled.ts b/ts/util/isBackupEnabled.ts index 83ce52948e0b..212e85f4ed51 100644 --- a/ts/util/isBackupEnabled.ts +++ b/ts/util/isBackupEnabled.ts @@ -2,14 +2,10 @@ // SPDX-License-Identifier: AGPL-3.0-only import * as RemoteConfig from '../RemoteConfig'; -import { Environment, getEnvironment } from '../environment'; -import { isStaging } from './version'; +import { isStagingServer } from './isStagingServer'; export function isBackupEnabled(): boolean { - if (getEnvironment() === Environment.Staging) { - return true; - } - if (isStaging(window.getVersion())) { + if (isStagingServer()) { return true; } return Boolean(RemoteConfig.isEnabled('desktop.backup.credentialFetch')); diff --git a/ts/util/isStagingServer.ts b/ts/util/isStagingServer.ts new file mode 100644 index 000000000000..51508365eefb --- /dev/null +++ b/ts/util/isStagingServer.ts @@ -0,0 +1,17 @@ +// Copyright 2024 Signal Messenger, LLC +// SPDX-License-Identifier: AGPL-3.0-only + +import { Environment, getEnvironment } from '../environment'; +import { isStaging } from './version'; + +export function isStagingServer( + serverUrl = window.SignalContext.config.serverUrl +): boolean { + if (getEnvironment() === Environment.Staging) { + return true; + } + if (isStaging(window.getVersion())) { + return true; + } + return /staging/i.test(serverUrl); +} diff --git a/ts/window.d.ts b/ts/window.d.ts index bc48fdeffdeb..aa158f37a53f 100644 --- a/ts/window.d.ts +++ b/ts/window.d.ts @@ -27,7 +27,6 @@ import type * as Crypto from './Crypto'; import type * as Curve from './Curve'; import type * as RemoteConfig from './RemoteConfig'; import type { OSType } from './util/os/shared'; -import type { getEnvironment } from './environment'; import type { LocalizerType, ThemeType } from './types/Util'; import type { Receipt } from './types/Receipt'; import type { ConversationController } from './ConversationController'; @@ -194,7 +193,6 @@ declare global { getConversations: () => ConversationModelCollectionType; getBuildCreation: () => number; getBuildExpiration: () => number; - getEnvironment: typeof getEnvironment; getHostName: () => string; getInteractionMode: () => 'mouse' | 'keyboard'; getServerPublicParams: () => string; diff --git a/ts/windows/main/phase1-ipc.ts b/ts/windows/main/phase1-ipc.ts index ff64a98933ab..53cddac6ab45 100644 --- a/ts/windows/main/phase1-ipc.ts +++ b/ts/windows/main/phase1-ipc.ts @@ -9,7 +9,7 @@ import type { IPCType } from '../../window.d'; import { parseIntWithFallback } from '../../util/parseIntWithFallback'; import { getSignalConnections } from '../../util/getSignalConnections'; import { ThemeType } from '../../types/Util'; -import { getEnvironment, Environment } from '../../environment'; +import { Environment } from '../../environment'; import { SignalContext } from '../context'; import * as log from '../../logging/log'; import { formatCountForLogging } from '../../logging/formatCountForLogging'; @@ -48,7 +48,6 @@ window.RETRY_DELAY = false; window.platform = process.platform; window.getTitle = () => title; -window.getEnvironment = getEnvironment; window.getAppInstance = () => config.appInstance; window.getVersion = () => config.version; window.getBuildCreation = () => parseIntWithFallback(config.buildCreation, 0); @@ -61,8 +60,8 @@ window.getBackupServerPublicParams = () => config.backupServerPublicParams; window.getSfuUrl = () => config.sfuUrl; let title = config.name; -if (getEnvironment() !== Environment.Production) { - title += ` - ${getEnvironment()}`; +if (config.environment !== Environment.PackagedApp) { + title += ` - ${config.environment}`; } if (config.appInstance) { title += ` - ${config.appInstance}`; @@ -169,7 +168,7 @@ window.logAuthenticatedConnect = () => { window.open = () => null; // Playwright uses `eval` for `.evaluate()` API -if (config.ciMode !== 'full' && config.environment !== 'test') { +if (config.ciMode !== 'full' && config.environment !== Environment.Test) { // eslint-disable-next-line no-eval, no-multi-assign window.eval = global.eval = () => null; }