Introduce isStagingServer util method

This commit is contained in:
Fedor Indutny 2024-09-04 11:12:45 -07:00 committed by GitHub
parent 4cdb6fab08
commit cd44a7a033
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
24 changed files with 80 additions and 71 deletions

View file

@ -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)}`);
});

View file

@ -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<string>('certificateAuthority'),
environment:
!isTestEnvironment(getEnvironment()) && ciMode
? Environment.Production
? Environment.PackagedApp
: getEnvironment(),
isMockTestEnvironment: Boolean(process.env.MOCK_TEST),
ciMode,

View file

@ -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!`);
}

View file

@ -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<void> {
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;

View file

@ -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 })}
<div className="module-splash-screen__logo module-img--80 module-logo-blue" />
<h3 className="Inbox__welcome">
{getEnvironment() !== Environment.Staging
? i18n('icu:welcomeToSignal')
: 'THIS IS A STAGING DESKTOP'}
{isStaging
? 'THIS IS A STAGING DESKTOP'
: i18n('icu:welcomeToSignal')}
</h3>
<p className="Inbox__whatsnew">
<WhatsNewLink i18n={i18n} showWhatsNewModal={showWhatsNewModal} />

View file

@ -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) {

View file

@ -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 = (
<label className="module-InstallScreenChoosingDeviceNameStep__input">
{/* Since this is only for testing - we don't require translation */}

View file

@ -58,6 +58,7 @@ function Simulation({
return (
<InstallScreenQrCodeNotScannedStep
i18n={i18n}
isStaging={false}
provisioningUrl={provisioningUrl}
updates={DEFAULT_UPDATES}
OS="macOS"
@ -72,6 +73,7 @@ export function QrCodeLoading(): JSX.Element {
return (
<InstallScreenQrCodeNotScannedStep
i18n={i18n}
isStaging={false}
provisioningUrl={{
loadingState: LoadingState.Loading,
}}
@ -88,6 +90,7 @@ export function QrCodeFailedToLoad(): JSX.Element {
return (
<InstallScreenQrCodeNotScannedStep
i18n={i18n}
isStaging={false}
provisioningUrl={{
loadingState: LoadingState.LoadFailed,
error: InstallScreenQRCodeError.Unknown,
@ -105,6 +108,7 @@ export function QrCodeLoaded(): JSX.Element {
return (
<InstallScreenQrCodeNotScannedStep
i18n={i18n}
isStaging={false}
provisioningUrl={LOADED_URL}
updates={DEFAULT_UPDATES}
OS="macOS"
@ -164,6 +168,7 @@ export const WithUpdateKnobs: StoryFn<PropsType & { dialogType: DialogType }> =
return (
<InstallScreenQrCodeNotScannedStep
i18n={i18n}
isStaging={false}
provisioningUrl={LOADED_URL}
hasExpired
updates={{

View file

@ -19,7 +19,6 @@ import { InstallScreenSignalLogo } from './InstallScreenSignalLogo';
import { InstallScreenUpdateDialog } from './InstallScreenUpdateDialog';
import { getClassNamesFor } from '../../util/getClassNamesFor';
import type { UpdatesStateType } from '../../state/ducks/updates';
import { Environment, getEnvironment } from '../../environment';
// We can't always use destructuring assignment because of the complexity of this props
// type.
@ -31,6 +30,7 @@ export type PropsType = Readonly<{
updates: UpdatesStateType;
currentVersion: string;
OS: string;
isStaging: boolean;
retryGetQrCode: () => void;
startUpdate: () => void;
}>;
@ -46,6 +46,7 @@ export function InstallScreenQrCodeNotScannedStep({
currentVersion,
hasExpired,
i18n,
isStaging,
OS,
provisioningUrl,
retryGetQrCode,
@ -102,12 +103,12 @@ export function InstallScreenQrCodeNotScannedStep({
/>
</li>
</ol>
{getEnvironment() !== Environment.Staging ? (
{isStaging ? (
'THIS IS A STAGING DESKTOP'
) : (
<a target="_blank" rel="noreferrer" href={SUPPORT_PAGE}>
{i18n('icu:Install__support-link')}
</a>
) : (
'THIS IS A STAGING DESKTOP'
)}
</div>
</div>

View file

@ -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 =>

View file

@ -85,7 +85,7 @@ export function initialize(): void {
// A modern logging interface for the browser
function logAtLevel(level: LogLevel, ...args: ReadonlyArray<unknown>): void {
if (getEnvironment() !== Environment.Production) {
if (getEnvironment() !== Environment.PackagedApp) {
const prefix = getLogLevelString(level)
.toUpperCase()
.padEnd(levelMaxLength, ' ');

View file

@ -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 = {

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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() {
<ChatsTab
otherTabsUnreadStats={otherTabsUnreadStats}
i18n={i18n}
isStaging={isStagingServer()}
hasFailedStorySends={hasFailedStorySends}
hasPendingUpdate={hasPendingUpdate}
navTabsCollapsed={navTabsCollapsed}

View file

@ -17,6 +17,7 @@ import { WidthBreakpoint } from '../../components/_util';
import { InstallScreenStep } from '../../types/InstallScreen';
import OS from '../../util/os/osMain';
import { fileToBytes } from '../../util/fileToBytes';
import { isStagingServer } from '../../util/isStagingServer';
import * as log from '../../logging/log';
import { SmartToastManager } from './ToastManager';
@ -70,6 +71,7 @@ export const SmartInstallScreen = memo(function SmartInstallScreen() {
startUpdate,
retryGetQrCode: startInstaller,
OS: OS.getName(),
isStaging: isStagingServer(),
},
};
break;

View file

@ -11,23 +11,23 @@ import {
describe('environment utilities', () => {
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));
});

View file

@ -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);

View file

@ -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
}

View file

@ -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}`);
}
}

View file

@ -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'));

View file

@ -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);
}

2
ts/window.d.ts vendored
View file

@ -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;

View file

@ -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;
}