From 34cc87e8a860d2c3fe19d27f09d0490e58f84546 Mon Sep 17 00:00:00 2001 From: Fedor Indutny <79877362+indutny-signal@users.noreply.github.com> Date: Wed, 20 Nov 2024 19:14:21 -0800 Subject: [PATCH] Add new release line for testing --- package.json | 4 +- reproducible-builds/docker-entrypoint.sh | 3 + scripts/prepare_axolotl_build.js | 77 +++++++++++++++++++ ...a_version.js => prepare_tagged_version.js} | 14 +++- ts/components/Inbox.stories.tsx | 4 +- ts/components/Inbox.tsx | 6 +- ts/services/backups/import.ts | 4 +- ts/services/backups/index.ts | 4 +- ts/state/selectors/user.ts | 4 +- ts/state/smart/Inbox.tsx | 4 +- ts/test-both/state/selectors/user_test.ts | 10 +-- ts/test-both/util/version_test.ts | 76 +++++++++++++++--- ts/textsecure/SocketManager.ts | 4 +- ts/updater/common.ts | 5 +- ts/util/groupSendEndorsements.ts | 4 +- ts/util/isLinkAndSyncEnabled.ts | 4 +- ts/util/version.ts | 15 +++- 17 files changed, 199 insertions(+), 43 deletions(-) create mode 100644 scripts/prepare_axolotl_build.js rename scripts/{prepare_alpha_version.js => prepare_tagged_version.js} (52%) diff --git a/package.json b/package.json index 6e23733e8..02e0ee731 100644 --- a/package.json +++ b/package.json @@ -35,7 +35,9 @@ "clean-protobuf": "npm run clean-module-protobuf", "prepare-beta-build": "node scripts/prepare_beta_build.js", "prepare-alpha-build": "node scripts/prepare_alpha_build.js", - "prepare-alpha-version": "node scripts/prepare_alpha_version.js", + "prepare-alpha-version": "node scripts/prepare_tagged_version.js alpha", + "prepare-axolotl-build": "node scripts/prepare_axolotl_build.js", + "prepare-axolotl-version": "node scripts/prepare_tagged_version.js axolotl", "prepare-staging-build": "node scripts/prepare_staging_build.js", "prepare-windows-cert": "node scripts/prepare_windows_cert.js", "test": "run-s test-node test-electron test-lint-intl test-eslint", diff --git a/reproducible-builds/docker-entrypoint.sh b/reproducible-builds/docker-entrypoint.sh index da65ebc85..d8210a70f 100644 --- a/reproducible-builds/docker-entrypoint.sh +++ b/reproducible-builds/docker-entrypoint.sh @@ -44,6 +44,9 @@ if [ "${BUILD_TYPE}" = "public" ]; then elif [ "${BUILD_TYPE}" = "alpha" ]; then npm run prepare-alpha-version npm run prepare-alpha-build +elif [ "${BUILD_TYPE}" = "axolotl" ]; then + npm run prepare-axolotl-version + npm run prepare-axolotl-build elif [ "${BUILD_TYPE}" = "staging" ]; then npm run prepare-alpha-version npm run prepare-staging-build diff --git a/scripts/prepare_axolotl_build.js b/scripts/prepare_axolotl_build.js new file mode 100644 index 000000000..6c29072c9 --- /dev/null +++ b/scripts/prepare_axolotl_build.js @@ -0,0 +1,77 @@ +// Copyright 2021 Signal Messenger, LLC +// SPDX-License-Identifier: AGPL-3.0-only + +const fs = require('fs'); +const _ = require('lodash'); + +const packageJson = require('../package.json'); +const { isAxolotl } = require('../ts/util/version'); + +const { version } = packageJson; + +// You might be wondering why this file is necessary. It comes down to our desire to allow +// side-by-side installation of production and alpha builds. Electron-Builder uses +// top-level data from package.json for many things, like the executable name, the +// debian package name, the install directory under /opt on linux, etc. We tried +// adding the ${channel} macro to these values, but Electron-Builder didn't like that. + +if (!isAxolotl(version)) { + console.error(`Version '${version}' is not an backup version!`); + process.exit(1); +} + +console.log('prepare_backup_build: updating package.json'); + +// ------- + +const NAME_PATH = 'name'; +const PRODUCTION_NAME = 'signal-desktop'; +const AXOLOTL_NAME = 'signal-desktop-axolotl'; + +const PRODUCT_NAME_PATH = 'productName'; +const PRODUCTION_PRODUCT_NAME = 'Signal'; +const AXOLOTL_PRODUCT_NAME = 'Signal Axolotl'; + +const APP_ID_PATH = 'build.appId'; +const PRODUCTION_APP_ID = 'org.whispersystems.signal-desktop'; +const AXOLOTL_APP_ID = 'org.whispersystems.signal-desktop-axolotl'; + +const STARTUP_WM_CLASS_PATH = 'build.linux.desktop.StartupWMClass'; +const PRODUCTION_STARTUP_WM_CLASS = 'Signal'; +const AXOLOTL_STARTUP_WM_CLASS = 'Signal Axolotl'; + +const DESKTOP_NAME_PATH = 'desktopName'; + +// Note: we're avoiding dashes in our .desktop name due to xdg-settings behavior +// https://github.com/signalapp/Signal-Desktop/issues/3602 +const PRODUCTION_DESKTOP_NAME = 'signal.desktop'; +const AXOLOTL_DESKTOP_NAME = 'signalaxolotl.desktop'; + +// ------- + +function checkValue(object, objectPath, expected) { + const actual = _.get(object, objectPath); + if (actual !== expected) { + throw new Error(`${objectPath} was ${actual}; expected ${expected}`); + } +} + +// ------ + +checkValue(packageJson, NAME_PATH, PRODUCTION_NAME); +checkValue(packageJson, PRODUCT_NAME_PATH, PRODUCTION_PRODUCT_NAME); +checkValue(packageJson, APP_ID_PATH, PRODUCTION_APP_ID); +checkValue(packageJson, STARTUP_WM_CLASS_PATH, PRODUCTION_STARTUP_WM_CLASS); +checkValue(packageJson, DESKTOP_NAME_PATH, PRODUCTION_DESKTOP_NAME); + +// ------- + +_.set(packageJson, NAME_PATH, AXOLOTL_NAME); +_.set(packageJson, PRODUCT_NAME_PATH, AXOLOTL_PRODUCT_NAME); +_.set(packageJson, APP_ID_PATH, AXOLOTL_APP_ID); +_.set(packageJson, STARTUP_WM_CLASS_PATH, AXOLOTL_STARTUP_WM_CLASS); +_.set(packageJson, DESKTOP_NAME_PATH, AXOLOTL_DESKTOP_NAME); + +// ------- + +fs.writeFileSync('./package.json', JSON.stringify(packageJson, null, ' ')); diff --git a/scripts/prepare_alpha_version.js b/scripts/prepare_tagged_version.js similarity index 52% rename from scripts/prepare_alpha_version.js rename to scripts/prepare_tagged_version.js index bdd90e47c..99f7903ab 100644 --- a/scripts/prepare_alpha_version.js +++ b/scripts/prepare_tagged_version.js @@ -6,7 +6,13 @@ const { execSync } = require('child_process'); const _ = require('lodash'); -const { generateAlphaVersion } = require('../ts/util/version'); +const release = process.argv[2]; +if (release !== 'alpha' && release !== 'axolotl') { + console.error(`Invalid release line: ${release}`); + process.exit(1); +} + +const { generateTaggedVersion } = require('../ts/util/version'); const packageJson = require('../package.json'); @@ -16,15 +22,15 @@ const shortSha = execSync('git rev-parse --short HEAD') .toString('utf8') .replace(/[\n\r]/g, ''); -const alphaVersion = generateAlphaVersion({ currentVersion, shortSha }); +const newVersion = generateTaggedVersion({ release, currentVersion, shortSha }); console.log( - `prepare_alpha_version: updating package.json.\n Previous: ${currentVersion}\n New: ${alphaVersion}` + `prepare_tagged_version: updating package.json.\n Previous: ${currentVersion}\n New: ${newVersion}` ); // ------- -_.set(packageJson, 'version', alphaVersion); +_.set(packageJson, 'version', newVersion); // ------- diff --git a/ts/components/Inbox.stories.tsx b/ts/components/Inbox.stories.tsx index 083d97ad8..e6b1b5506 100644 --- a/ts/components/Inbox.stories.tsx +++ b/ts/components/Inbox.stories.tsx @@ -19,12 +19,12 @@ export default { args: { i18n, hasInitialLoadCompleted: false, - isAlpha: false, + isNightly: false, isCustomizingPreferredReactions: false, }, argTypes: { daysAgo: { control: { type: 'number' } }, - isAlpha: { control: { type: 'boolean' } }, + isNightly: { control: { type: 'boolean' } }, }, } satisfies Meta; diff --git a/ts/components/Inbox.tsx b/ts/components/Inbox.tsx index 946b16dba..faf16deb3 100644 --- a/ts/components/Inbox.tsx +++ b/ts/components/Inbox.tsx @@ -14,7 +14,7 @@ export type PropsType = { envelopeTimestamp: number | undefined; hasInitialLoadCompleted: boolean; i18n: LocalizerType; - isAlpha: boolean; + isNightly: boolean; isCustomizingPreferredReactions: boolean; navTabsCollapsed: boolean; onToggleNavTabsCollapse: (navTabsCollapsed: boolean) => unknown; @@ -32,7 +32,7 @@ export function Inbox({ envelopeTimestamp, hasInitialLoadCompleted, i18n, - isAlpha, + isNightly, isCustomizingPreferredReactions, navTabsCollapsed, onToggleNavTabsCollapse, @@ -128,7 +128,7 @@ export function Inbox({ } let logo: JSX.Element; - if (isAlpha) { + if (isNightly) { const parts = new Array(); parts.push( diff --git a/ts/services/backups/import.ts b/ts/services/backups/import.ts index 01b62e2e7..9388e803f 100644 --- a/ts/services/backups/import.ts +++ b/ts/services/backups/import.ts @@ -116,7 +116,7 @@ import { } from '../../util/backupMediaDownload'; import { getEnvironment, isTestEnvironment } from '../../environment'; import { hasAttachmentDownloads } from '../../util/hasAttachmentDownloads'; -import { isAlpha } from '../../util/version'; +import { isNightly } from '../../util/version'; import { ToastType } from '../../types/Toast'; const MAX_CONCURRENCY = 10; @@ -369,7 +369,7 @@ export class BackupImportStream extends Writable { log.error( `${this.logId}: errored while processing ${this.frameErrorCount} frames.` ); - if (isAlpha(window.getVersion())) { + if (isNightly(window.getVersion())) { window.reduxActions.toast.showToast({ toastType: ToastType.FailedToImportBackup, }); diff --git a/ts/services/backups/index.ts b/ts/services/backups/index.ts index 0a01d2c59..259bb46be 100644 --- a/ts/services/backups/index.ts +++ b/ts/services/backups/index.ts @@ -51,7 +51,7 @@ import { validateBackup } from './validator'; import { BackupType } from './types'; import { UnsupportedBackupVersion } from './errors'; import { ToastType } from '../../types/Toast'; -import { isAlpha } from '../../util/version'; +import { isNightly } from '../../util/version'; export { BackupType }; @@ -388,7 +388,7 @@ export class BackupsService { } catch (error) { log.info(`importBackup: failed, error: ${Errors.toLogFormat(error)}`); - if (isAlpha(window.getVersion())) { + if (isNightly(window.getVersion())) { window.reduxActions.toast.showToast({ toastType: ToastType.FailedToImportBackup, }); diff --git a/ts/state/selectors/user.ts b/ts/state/selectors/user.ts index 1a4ff2583..3093c9338 100644 --- a/ts/state/selectors/user.ts +++ b/ts/state/selectors/user.ts @@ -12,7 +12,7 @@ import type { StateType } from '../reducer'; import type { CallingStateType } from '../ducks/calling'; import type { UserStateType } from '../ducks/user'; -import { isAlpha, isBeta } from '../../util/version'; +import { isNightly, isBeta } from '../../util/version'; export const getUser = (state: StateType): UserStateType => state.user; @@ -108,7 +108,7 @@ const getVersion = createSelector( (state: UserStateType) => state.version ); -export const getIsAlpha = createSelector(getVersion, isAlpha); +export const getIsNightly = createSelector(getVersion, isNightly); export const getIsBeta = createSelector(getVersion, isBeta); diff --git a/ts/state/smart/Inbox.tsx b/ts/state/smart/Inbox.tsx index 40fc4aed6..27491d845 100644 --- a/ts/state/smart/Inbox.tsx +++ b/ts/state/smart/Inbox.tsx @@ -4,7 +4,7 @@ import React, { memo } from 'react'; import { useSelector } from 'react-redux'; import { Inbox } from '../../components/Inbox'; -import { isAlpha } from '../../util/version'; +import { isNightly } from '../../util/version'; import { getIntl } from '../selectors/user'; import { SmartCustomizingPreferredReactionsModal } from './CustomizingPreferredReactionsModal'; import { getIsCustomizingPreferredReactions } from '../selectors/preferredReactions'; @@ -59,7 +59,7 @@ export const SmartInbox = memo(function SmartInbox(): JSX.Element { firstEnvelopeTimestamp={firstEnvelopeTimestamp} hasInitialLoadCompleted={hasInitialLoadCompleted} i18n={i18n} - isAlpha={isAlpha(window.getVersion())} + isNightly={isNightly(window.getVersion())} isCustomizingPreferredReactions={isCustomizingPreferredReactions} navTabsCollapsed={navTabsCollapsed} onToggleNavTabsCollapse={toggleNavTabsCollapse} diff --git a/ts/test-both/state/selectors/user_test.ts b/ts/test-both/state/selectors/user_test.ts index 31ad542ac..f8f7eac5c 100644 --- a/ts/test-both/state/selectors/user_test.ts +++ b/ts/test-both/state/selectors/user_test.ts @@ -7,7 +7,7 @@ import type { StateType } from '../../../state/reducer'; import type { UserStateType } from '../../../state/ducks/user'; import { getEmptyState } from '../../../state/ducks/user'; -import { getIsAlpha, getIsBeta } from '../../../state/selectors/user'; +import { getIsNightly, getIsBeta } from '../../../state/selectors/user'; describe('both/state/selectors/user', () => { function getRootState( @@ -22,20 +22,20 @@ describe('both/state/selectors/user', () => { } as any; } - describe('#getIsAlpha', () => { + describe('#getIsNightly', () => { it('returns false for beta', () => { const state = getRootState({ version: '1.23.4-beta.5' }); - assert.isFalse(getIsAlpha(state)); + assert.isFalse(getIsNightly(state)); }); it('returns false for production', () => { const state = getRootState({ version: '1.23.4' }); - assert.isFalse(getIsAlpha(state)); + assert.isFalse(getIsNightly(state)); }); it('returns true for alpha', () => { const state = getRootState({ version: '1.23.4-alpha.987' }); - assert.isTrue(getIsAlpha(state)); + assert.isTrue(getIsNightly(state)); }); }); diff --git a/ts/test-both/util/version_test.ts b/ts/test-both/util/version_test.ts index fa7c115c8..ca3ae4dfc 100644 --- a/ts/test-both/util/version_test.ts +++ b/ts/test-both/util/version_test.ts @@ -6,8 +6,10 @@ import { useFakeTimers } from 'sinon'; import * as semver from 'semver'; import { - generateAlphaVersion, + generateTaggedVersion, isAlpha, + isAxolotl, + isNightly, isBeta, isProduction, isStaging, @@ -57,6 +59,34 @@ describe('version utilities', () => { }); }); + describe('isAxolotl', () => { + it('returns false for non-axolotl version strings', () => { + assert.isFalse(isAxolotl('1.2.3')); + assert.isFalse(isAxolotl('1.2.3-staging.1')); + assert.isFalse(isAxolotl('1.2.3-beta')); + assert.isFalse(isAxolotl('1.2.3-beta.1')); + assert.isFalse(isAxolotl('1.2.3-rc.1')); + }); + + it('returns true for Axolotl version strings', () => { + assert.isTrue(isAxolotl('1.2.3-axolotl')); + assert.isTrue(isAxolotl('1.2.3-axolotl.1')); + }); + }); + + describe('isNightly', () => { + it('returns false for non-nightly version strings', () => { + assert.isFalse(isNightly('1.2.3')); + assert.isFalse(isNightly('1.2.3-beta.1')); + assert.isFalse(isNightly('1.2.3-staging.1')); + }); + + it('returns true for nightly version strings', () => { + assert.isTrue(isNightly('1.2.3-alpha.1')); + assert.isTrue(isNightly('1.2.3-axolotl.1')); + }); + }); + describe('isStaging', () => { it('returns false for non-staging version strings', () => { assert.isFalse(isStaging('1.2.3')); @@ -73,7 +103,7 @@ describe('version utilities', () => { }); }); - describe('generateAlphaVersion', () => { + describe('generateTaggedVersion', () => { beforeEach(function (this: Mocha.Context) { // This isn't a hook. this.clock = useFakeTimers(); @@ -90,7 +120,11 @@ describe('version utilities', () => { const shortSha = '07f0efc45'; const expected = '5.12.0-alpha.20210723.01-07f0efc45'; - const actual = generateAlphaVersion({ currentVersion, shortSha }); + const actual = generateTaggedVersion({ + release: 'alpha', + currentVersion, + shortSha, + }); assert.strictEqual(expected, actual); }); @@ -100,7 +134,11 @@ describe('version utilities', () => { const shortSha = '07f0efc45'; this.clock.setSystemTime(new Date('2021-07-23T01:22:55.692Z').getTime()); - const actual = generateAlphaVersion({ currentVersion, shortSha }); + const actual = generateTaggedVersion({ + release: 'alpha', + currentVersion, + shortSha, + }); assert.isTrue(semver.gt('5.12.0', actual)); }); @@ -110,7 +148,11 @@ describe('version utilities', () => { const shortSha = '07f0efc45'; this.clock.setSystemTime(new Date('2021-07-23T01:22:55.692Z').getTime()); - const actual = generateAlphaVersion({ currentVersion, shortSha }); + const actual = generateTaggedVersion({ + release: 'alpha', + currentVersion, + shortSha, + }); assert.isTrue(semver.gt(currentVersion, actual)); }); @@ -120,10 +162,18 @@ describe('version utilities', () => { const shortSha = '07f0efc45'; this.clock.setSystemTime(new Date('2021-07-23T00:22:55.692Z').getTime()); - const actualEarlier = generateAlphaVersion({ currentVersion, shortSha }); + const actualEarlier = generateTaggedVersion({ + release: 'alpha', + currentVersion, + shortSha, + }); this.clock.setSystemTime(new Date('2021-07-23T01:22:55.692Z').getTime()); - const actualLater = generateAlphaVersion({ currentVersion, shortSha }); + const actualLater = generateTaggedVersion({ + release: 'alpha', + currentVersion, + shortSha, + }); assert.isTrue(semver.lt(actualEarlier, actualLater)); }); @@ -133,10 +183,18 @@ describe('version utilities', () => { const shortSha = '07f0efc45'; this.clock.setSystemTime(new Date('2021-07-22T01:22:55.692Z').getTime()); - const actualEarlier = generateAlphaVersion({ currentVersion, shortSha }); + const actualEarlier = generateTaggedVersion({ + release: 'alpha', + currentVersion, + shortSha, + }); this.clock.setSystemTime(new Date('2021-07-23T01:22:55.692Z').getTime()); - const actualLater = generateAlphaVersion({ currentVersion, shortSha }); + const actualLater = generateTaggedVersion({ + release: 'alpha', + currentVersion, + shortSha, + }); assert.isTrue(semver.lt(actualEarlier, actualLater)); }); diff --git a/ts/textsecure/SocketManager.ts b/ts/textsecure/SocketManager.ts index b5a482b60..dcaf69135 100644 --- a/ts/textsecure/SocketManager.ts +++ b/ts/textsecure/SocketManager.ts @@ -46,7 +46,7 @@ import WebSocketResource, { import { ConnectTimeoutError, HTTPError } from './Errors'; import type { IRequestHandler, WebAPICredentials } from './Types.d'; import { connect as connectWebSocket } from './WebSocket'; -import { isAlpha, isBeta, isStaging } from '../util/version'; +import { isNightly, isBeta, isStaging } from '../util/version'; import { getBasicAuth } from '../util/getBasicAuth'; const FIVE_MINUTES = 5 * durations.MINUTE; @@ -601,7 +601,7 @@ export class SocketManager extends EventListener { // in alpha, switch to using libsignal transport, unless user opts out, // in which case switching to shadowing - if (isAlpha(this.options.version)) { + if (isNightly(this.options.version)) { const configValue = window.Signal.RemoteConfig.isEnabled( 'desktop.experimentalTransportEnabled.alpha' ); diff --git a/ts/updater/common.ts b/ts/updater/common.ts index e5e3217d7..b14e11530 100644 --- a/ts/updater/common.ts +++ b/ts/updater/common.ts @@ -27,7 +27,7 @@ import * as Errors from '../types/errors'; import { strictAssert } from '../util/assert'; import { drop } from '../util/drop'; import * as durations from '../util/durations'; -import { isAlpha, isBeta, isStaging } from '../util/version'; +import { isAlpha, isAxolotl, isBeta, isStaging } from '../util/version'; import * as packageJson from '../../package.json'; import type { SettingsChannel } from '../main/settingsChannel'; @@ -912,6 +912,9 @@ function getChannel(): string { if (isAlpha(version)) { return 'alpha'; } + if (isAxolotl(version)) { + return 'axolotl'; + } if (isBeta(version)) { return 'beta'; } diff --git a/ts/util/groupSendEndorsements.ts b/ts/util/groupSendEndorsements.ts index 2752a8a4b..57323380a 100644 --- a/ts/util/groupSendEndorsements.ts +++ b/ts/util/groupSendEndorsements.ts @@ -27,7 +27,7 @@ import { DurationInSeconds, MINUTE } from './durations'; import { ToastType } from '../types/Toast'; import * as Errors from '../types/errors'; import { isTestOrMockEnvironment } from '../environment'; -import { isAlpha } from './version'; +import { isNightly } from './version'; import { parseStrict } from './schemas'; import { DataReader } from '../sql/Client'; import { maybeUpdateGroup } from '../groups'; @@ -331,7 +331,7 @@ const showFailedToSendWithEndorsementsToast = throttle( export function onFailedToSendWithEndorsements(error: Error): void { log.error('onFailedToSendWithEndorsements', Errors.toLogFormat(error)); - if (isTestOrMockEnvironment() || isAlpha(window.getVersion())) { + if (isTestOrMockEnvironment() || isNightly(window.getVersion())) { showFailedToSendWithEndorsementsToast(); } if (window.SignalCI) { diff --git a/ts/util/isLinkAndSyncEnabled.ts b/ts/util/isLinkAndSyncEnabled.ts index a7fbe6472..09271dccb 100644 --- a/ts/util/isLinkAndSyncEnabled.ts +++ b/ts/util/isLinkAndSyncEnabled.ts @@ -3,7 +3,7 @@ import { isTestOrMockEnvironment } from '../environment'; import { isStagingServer } from './isStagingServer'; -import { isAlpha } from './version'; +import { isNightly } from './version'; import { everDone as wasRegistrationEverDone } from './registration'; export function isLinkAndSyncEnabled(version: string): boolean { @@ -12,5 +12,5 @@ export function isLinkAndSyncEnabled(version: string): boolean { return false; } - return isStagingServer() || isTestOrMockEnvironment() || isAlpha(version); + return isStagingServer() || isTestOrMockEnvironment() || isNightly(version); } diff --git a/ts/util/version.ts b/ts/util/version.ts index 74c3d40ab..22b7f4715 100644 --- a/ts/util/version.ts +++ b/ts/util/version.ts @@ -16,21 +16,28 @@ export const isProduction = (version: string): boolean => { export const isBeta = (version: string): boolean => semver.parse(version)?.prerelease[0] === 'beta'; +export const isNightly = (version: string): boolean => + isAlpha(version) || isAxolotl(version); + export const isAlpha = (version: string): boolean => semver.parse(version)?.prerelease[0] === 'alpha'; +export const isAxolotl = (version: string): boolean => + semver.parse(version)?.prerelease[0] === 'axolotl'; + export const isStaging = (version: string): boolean => semver.parse(version)?.prerelease[0] === 'staging'; -export const generateAlphaVersion = (options: { +export const generateTaggedVersion = (options: { + release: string; currentVersion: string; shortSha: string; }): string => { - const { currentVersion, shortSha } = options; + const { release, currentVersion, shortSha } = options; const parsed = semver.parse(currentVersion); if (!parsed) { - throw new Error(`generateAlphaVersion: Invalid version ${currentVersion}`); + throw new Error(`generateTaggedVersion: Invalid version ${currentVersion}`); } const dateTimeParts = new Intl.DateTimeFormat('en', { @@ -51,5 +58,5 @@ export const generateAlphaVersion = (options: { const formattedVersion = `${parsed.major}.${parsed.minor}.${parsed.patch}`; - return `${formattedVersion}-alpha.${formattedDate}-${shortSha}`; + return `${formattedVersion}-${release}.${formattedDate}-${shortSha}`; };