build(android): add fdroid distribution flavor

This commit is contained in:
Tommaso Casaburi
2026-04-30 18:58:50 +07:00
parent ea03d04cd9
commit fdbb650baa
25 changed files with 259 additions and 75 deletions
+2 -2
View File
@@ -175,8 +175,8 @@ jobs:
- name: Sync Capacitor Android project
run: npx cap sync android
- name: Run Android unit tests and assemble debug APK
run: cd android && ./gradlew testDebugUnitTest assembleDebug --stacktrace
- name: Run Android unit tests and assemble debug APKs
run: cd android && ./gradlew testGithubDebugUnitTest testFdroidDebugUnitTest assembleGithubDebug assembleFdroidDebug --stacktrace
package-linux:
name: Package Linux (Ubuntu)
+10 -8
View File
@@ -263,28 +263,30 @@ jobs:
run: CI='' NODE_ENV=production yarn build
- name: Set Android versionCode and versionName
run: |
sed -i "s/versionCode 1/versionCode $(git tag | wc -l)/" ./android/app/build.gradle
sed -i "s/versionName \"1.0\"/versionName \"$(node -e "console.log(require('./package.json').version)")\"/" ./android/app/build.gradle
cat ./android/app/build.gradle
VERSION=$(node -e "console.log(require('./package.json').version)")
VERSION_CODE=$(node -e "const [major = 0, minor = 0, patch = 0] = require('./package.json').version.replace(/^v/, '').split('-')[0].split('.').map(Number); console.log((major * 10000) + (minor * 100) + patch)")
echo "APP_VERSION_NAME=${VERSION}" >> "$GITHUB_ENV"
echo "APP_VERSION_CODE=${VERSION_CODE}" >> "$GITHUB_ENV"
echo "Android versionName=${VERSION} versionCode=${VERSION_CODE}"
- name: Sync Capacitor
run: npx cap sync android
- name: Build APK
run: |
for i in 1 2 3; do
echo "gradlew assembleRelease attempt $i"
(cd android && ./gradlew assembleRelease --stacktrace) && break
echo "gradlew assembleGithubRelease attempt $i"
(cd android && ./gradlew assembleGithubRelease -PAPP_VERSION_NAME="${APP_VERSION_NAME}" -PAPP_VERSION_CODE="${APP_VERSION_CODE}" --stacktrace) && break
sleep 10
[ "$i" = "3" ] && exit 1
done
- name: Optimize APK
run: cd android/app/build/outputs/apk/release && zipalign 4 app-release-unsigned.apk app-release-unsigned-zip.apk
run: cd android/app/build/outputs/apk/github/release && zipalign 4 app-github-release-unsigned.apk app-github-release-unsigned-zip.apk
- name: Sign APK
run: cd android/app/build/outputs/apk/release && apksigner sign --ks ../../../../../plebbit.keystore --ks-pass pass:${{ secrets.PLEBBIT_REACT_KEYSTORE_PASSWORD }} --ks-key-alias release --out app-release-signed.apk app-release-unsigned-zip.apk
run: cd android/app/build/outputs/apk/github/release && apksigner sign --ks ../../../../../../plebbit.keystore --ks-pass pass:${{ secrets.PLEBBIT_REACT_KEYSTORE_PASSWORD }} --ks-key-alias release --out app-github-release-signed.apk app-github-release-unsigned-zip.apk
- name: Stage release artifacts
run: |
mkdir -p release-assets
VERSION=$(node -e "console.log(require('./package.json').version)")
mv android/app/build/outputs/apk/release/app-release-signed.apk "release-assets/5chan-${VERSION}.apk"
mv android/app/build/outputs/apk/github/release/app-github-release-signed.apk "release-assets/5chan-${VERSION}.apk"
- name: List release assets
run: ls -la release-assets
- uses: actions/upload-artifact@v4
+36 -2
View File
@@ -1,5 +1,30 @@
apply plugin: 'com.android.application'
import groovy.json.JsonSlurper
def resolvePackageVersionName() {
def packageJsonFile = file("../../package.json")
if (!packageJsonFile.exists()) {
return "1.0"
}
return new JsonSlurper().parse(packageJsonFile).version.toString()
}
def versionNameToCode(String versionName) {
def stableVersion = versionName.replaceFirst(/^v/, "").split("-")[0]
def parts = stableVersion.tokenize(".").collect { part -> part.isInteger() ? part.toInteger() : 0 }
while (parts.size() < 3) {
parts.add(0)
}
return (parts[0] * 10000) + (parts[1] * 100) + parts[2]
}
def resolvedVersionName = "${project.findProperty("APP_VERSION_NAME") ?: resolvePackageVersionName()}"
def resolvedVersionCode = Integer.parseInt("${project.findProperty("APP_VERSION_CODE") ?: versionNameToCode(resolvedVersionName)}")
android {
namespace "fivechan.android"
compileSdkVersion rootProject.ext.compileSdkVersion
@@ -7,8 +32,8 @@ android {
applicationId "fivechan.android"
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode Integer.parseInt(project.findProperty("APP_VERSION_CODE") ?: "1")
versionName "${project.findProperty("APP_VERSION_NAME") ?: "1.0"}"
versionCode resolvedVersionCode
versionName resolvedVersionName
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
aaptOptions {
// Files and dirs to omit from the packaged assets dir, modified to accommodate modern web apps.
@@ -16,6 +41,15 @@ android {
ignoreAssetsPattern '!.svn:!.git:!.ds_store:!*.scc:.*:!CVS:!thumbs.db:!picasa.ini:!*~'
}
}
flavorDimensions "distribution"
productFlavors {
github {
dimension "distribution"
}
fdroid {
dimension "distribution"
}
}
buildTypes {
release {
minifyEnabled false
@@ -0,0 +1,15 @@
package fivechan.android;
import android.os.Bundle;
import com.capacitorjs.plugins.statusbar.StatusBarPlugin;
import com.getcapacitor.BridgeActivity;
public class MainActivity extends BridgeActivity {
@Override
public void onCreate(Bundle savedInstanceState) {
// Custom plugins must be registered before bridge initialization.
registerPlugin(FileUploaderPlugin.class);
registerPlugin(StatusBarPlugin.class);
super.onCreate(savedInstanceState);
}
}
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
</manifest>
@@ -1,8 +1,8 @@
package fivechan.android;
import android.os.Bundle;
import com.getcapacitor.BridgeActivity;
import com.capacitorjs.plugins.statusbar.StatusBarPlugin;
import com.getcapacitor.BridgeActivity;
public class MainActivity extends BridgeActivity {
@Override
-1
View File
@@ -2,7 +2,6 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
<application
android:allowBackup="true"
+46
View File
@@ -0,0 +1,46 @@
# Draft metadata for https://gitlab.com/fdroid/fdroiddata.
# Replace the commit field with the full v0.8.2 commit hash before opening the F-Droid merge request.
Categories:
- Internet
License: GPL-3.0-or-later
AuthorName: Bitsocial Forge
WebSite: https://5chan.app
SourceCode: https://github.com/bitsocialnet/5chan
IssueTracker: https://github.com/bitsocialnet/5chan/issues
Changelog: https://github.com/bitsocialnet/5chan/releases
AutoName: 5chan
RepoType: git
Repo: https://github.com/bitsocialnet/5chan
Builds:
- versionName: '0.8.2'
versionCode: 802
commit: TODO_REPLACE_WITH_V0_8_2_COMMIT_HASH
subdir: android
sudo:
- curl -Lo node.tar.xz https://nodejs.org/dist/v22.12.0/node-v22.12.0-linux-x64.tar.xz
- echo "22982235e1b71fa8850f82edd09cdae7e3f32df1764a9ec298c72d25ef2c164f node.tar.xz" | sha256sum -c -
- tar xf node.tar.xz --strip-components=1 -C /usr/local/
- corepack enable
rm:
- android/plebbit.keystore
build:
- cd .. && corepack yarn install --immutable && corepack yarn build:fdroid && corepack yarn cap sync android
scandelete:
- node_modules/
- build/
- android/app/build/
- android/capacitor-cordova-android-plugins/build/
gradle:
- fdroid
gradleprops:
- APP_VERSION_NAME=0.8.2
- APP_VERSION_CODE=802
output: app/build/outputs/apk/fdroid/release/app-fdroid-release-unsigned.apk
AutoUpdateMode: Version
UpdateCheckMode: Tags
CurrentVersion: '0.8.2'
CurrentVersionCode: 802
@@ -0,0 +1 @@
Adds an F-Droid build without in-app APK updating and fixes Android version metadata.
@@ -0,0 +1,5 @@
5chan is a decentralized imageboard client for the Bitsocial protocol.
It keeps the familiar imageboard layout while letting anyone create and operate their own peer-to-peer communities. Boards are owned and moderated by their operators, and users can open any community directly by address.
The Android app supports browsing boards, posting, replying, media upload workflows, account management, themes, and the same directory-based discovery used by the web client.
@@ -0,0 +1 @@
Decentralized imageboard client for Bitsocial communities
@@ -0,0 +1 @@
5chan
+2 -1
View File
@@ -59,6 +59,7 @@
"start": "node scripts/start-dev.js",
"start:android-usb": "node scripts/start-android-usb.mjs",
"build": "cross-env PUBLIC_URL=./ GENERATE_SOURCEMAP=false vite build",
"build:fdroid": "cross-env PUBLIC_URL=./ GENERATE_SOURCEMAP=false VITE_APP_DISTRIBUTION=fdroid vite build",
"build:preload": "vite build --config electron/vite.preload.config.js",
"build-vercel": "cross-env NODE_OPTIONS=\"--max_old_space_size=4096\" PUBLIC_URL=./ GENERATE_SOURCEMAP=true VITE_COMMIT_REF=$COMMIT_REF CI='' vite build",
"test": "vitest",
@@ -93,7 +94,7 @@
"type-check": "tsgo --noEmit",
"prettier": "oxfmt src/**/*.{js,ts,tsx} electron/**/*.{js,mjs}",
"changelog": "conventional-changelog -p angular -i CHANGELOG.md -s -r 0",
"android:build": "corepack yarn build && npx cap sync android && npx cap run android",
"android:build": "corepack yarn build && corepack yarn cap sync android && corepack yarn cap run android --flavor github",
"i18n:update": "node scripts/update-translations.js",
"i18n:update:dry": "node scripts/update-translations.js --dry",
"doctor": "react-doctor . -y",
@@ -62,6 +62,13 @@ const render = (children: React.ReactNode) => {
});
};
const settleLazyImports = async () => {
await act(async () => {
await Promise.resolve();
await Promise.resolve();
});
};
const findButtonByText = (text: string) => Array.from(container.querySelectorAll('button')).find((candidate) => candidate.textContent === text);
describe('InterfaceSettings', () => {
@@ -180,25 +187,27 @@ describe('InterfaceSettings', () => {
expect(localStorage.getItem(INTERFACE_LANGUAGE_STORAGE_KEY)).toBe('fr');
});
it('renders a check button when no app update is available', () => {
it('renders a check button when no app update is available', async () => {
render(createElement(InterfaceSettings));
await settleLazyImports();
expect(container.textContent).toContain('Update:');
expect(findButtonByText('Check')).toBeTruthy();
});
it('shows the checking status while an update check is in progress', () => {
it('shows the checking status while an update check is in progress', async () => {
useAppUpdateStore.setState({
isCheckingForUpdate: true,
});
render(createElement(InterfaceSettings));
await settleLazyImports();
expect(findButtonByText('Check')?.disabled).toBe(true);
expect(container.textContent).toContain('checking_for_updates');
});
it('renders a download button and release link when an app update is available', () => {
it('renders a download button and release link when an app update is available', async () => {
useAppUpdateStore.setState({
availableUpdate: {
runtime: 'web',
@@ -208,6 +217,7 @@ describe('InterfaceSettings', () => {
});
render(createElement(InterfaceSettings));
await settleLazyImports();
expect(findButtonByText('Download')).toBeTruthy();
const releaseLink = container.querySelector<HTMLAnchorElement>('a[href="https://github.com/bitsocialnet/5chan/releases/tag/v9.9.9"]');
@@ -217,6 +227,7 @@ describe('InterfaceSettings', () => {
it('checks for app updates when the check button is pressed', async () => {
render(createElement(InterfaceSettings));
await settleLazyImports();
const button = findButtonByText('Check');
expect(button).toBeTruthy();
@@ -242,6 +253,7 @@ describe('InterfaceSettings', () => {
});
render(createElement(InterfaceSettings));
await settleLazyImports();
const button = findButtonByText('Download');
expect(button).toBeTruthy();
@@ -255,7 +267,7 @@ describe('InterfaceSettings', () => {
expect(testState.alertMock).not.toHaveBeenCalled();
});
it('disables the update button while an app update is already being applied', () => {
it('disables the update button while an app update is already being applied', async () => {
useAppUpdateStore.setState({
availableUpdate: {
runtime: 'web',
@@ -266,6 +278,7 @@ describe('InterfaceSettings', () => {
});
render(createElement(InterfaceSettings));
await settleLazyImports();
expect(findButtonByText('Download')?.disabled).toBe(true);
});
@@ -283,6 +296,7 @@ describe('InterfaceSettings', () => {
});
render(createElement(InterfaceSettings));
await settleLazyImports();
const button = findButtonByText('Download');
expect(button).toBeTruthy();
@@ -0,0 +1,61 @@
import { useTranslation } from 'react-i18next';
import capitalize from 'lodash/capitalize';
import styles from './interface-settings.module.css';
import useAppUpdateStore from '../../../stores/use-app-update-store';
const UpdateButton = () => {
const { t } = useTranslation();
const availableUpdate = useAppUpdateStore((state) => state.availableUpdate);
const isApplyingUpdate = useAppUpdateStore((state) => state.isApplyingUpdate);
const isCheckingForUpdate = useAppUpdateStore((state) => state.isCheckingForUpdate);
const applyAppUpdate = useAppUpdateStore((state) => state.applyAppUpdate);
const refreshAvailableUpdate = useAppUpdateStore((state) => state.refreshAvailableUpdate);
const handleUpdateAction = async () => {
try {
if (availableUpdate) {
await applyAppUpdate();
return;
}
await refreshAvailableUpdate();
} catch (error) {
alert(String(error));
}
};
const buttonLabel = availableUpdate ? t('download') : t('check');
const isBusy = isApplyingUpdate || isCheckingForUpdate;
return (
<>
<button type='button' onClick={handleUpdateAction} disabled={isBusy}>
{capitalize(buttonLabel)}
</button>
{isCheckingForUpdate && (
<span className={styles.updateStatus} aria-live='polite'>
{t('checking_for_updates')}
</span>
)}
{!isCheckingForUpdate && availableUpdate && (
<span className={styles.updateStatus} aria-live='polite'>
{t('new_version_found')}:&nbsp;
<a href={availableUpdate.releaseUrl} target='_blank' rel='noopener noreferrer'>
v{availableUpdate.targetVersion}
</a>
</span>
)}
</>
);
};
const AppUpdateSetting = () => {
const { t } = useTranslation();
return (
<div className={styles.setting}>
{capitalize(t('update'))}: <UpdateButton />
</div>
);
};
export default AppUpdateSetting;
@@ -1,4 +1,4 @@
import { memo } from 'react';
import { lazy, memo, Suspense } from 'react';
import { useTranslation } from 'react-i18next';
import styles from './interface-settings.module.css';
import capitalize from 'lodash/capitalize';
@@ -7,52 +7,9 @@ import useFeedViewSettingsStore from '../../../stores/use-feed-view-settings-sto
import Version from '../../version';
import StyleSelector from '../../style-selector/style-selector';
import { INTERFACE_LANGUAGE_STORAGE_KEY, SUPPORTED_INTERFACE_LANGUAGES } from '../../../lib/constants';
import useAppUpdateStore from '../../../stores/use-app-update-store';
const UpdateButton = () => {
const { t } = useTranslation();
const availableUpdate = useAppUpdateStore((state) => state.availableUpdate);
const isApplyingUpdate = useAppUpdateStore((state) => state.isApplyingUpdate);
const isCheckingForUpdate = useAppUpdateStore((state) => state.isCheckingForUpdate);
const applyAppUpdate = useAppUpdateStore((state) => state.applyAppUpdate);
const refreshAvailableUpdate = useAppUpdateStore((state) => state.refreshAvailableUpdate);
const handleUpdateAction = async () => {
try {
if (availableUpdate) {
await applyAppUpdate();
return;
}
await refreshAvailableUpdate();
} catch (error) {
alert(String(error));
}
};
const buttonLabel = availableUpdate ? t('download') : t('check');
const isBusy = isApplyingUpdate || isCheckingForUpdate;
return (
<>
<button type='button' onClick={handleUpdateAction} disabled={isBusy}>
{capitalize(buttonLabel)}
</button>
{isCheckingForUpdate && (
<span className={styles.updateStatus} aria-live='polite'>
{t('checking_for_updates')}
</span>
)}
{!isCheckingForUpdate && availableUpdate && (
<span className={styles.updateStatus} aria-live='polite'>
{t('new_version_found')}:&nbsp;
<a href={availableUpdate.releaseUrl} target='_blank' rel='noopener noreferrer'>
v{availableUpdate.targetVersion}
</a>
</span>
)}
</>
);
};
const shouldRenderAppUpdateSetting = import.meta.env.VITE_APP_DISTRIBUTION !== 'fdroid';
const AppUpdateSetting = shouldRenderAppUpdateSetting ? lazy(() => import('./app-update-setting')) : null;
const InterfaceLanguage = () => {
const { i18n } = useTranslation();
@@ -87,9 +44,11 @@ const InterfaceSettings = () => {
<div className={styles.version}>
{capitalize(t('version'))}: <Version />
</div>
<div className={styles.setting}>
{capitalize(t('update'))}: <UpdateButton />
</div>
{AppUpdateSetting && (
<Suspense fallback={null}>
<AppUpdateSetting />
</Suspense>
)}
<div className={styles.setting}>
{capitalize(t('interface_language'))}: <InterfaceLanguage />
</div>
+2
View File
@@ -3,6 +3,8 @@
declare interface ImportMetaEnv {
readonly VITE_COMMIT_REF: string;
readonly VITE_APP_VERSION?: string;
readonly VITE_APP_DISTRIBUTION?: string;
}
declare interface ImportMeta {
+6 -2
View File
@@ -8,13 +8,13 @@ import './index.css';
import './themes.css';
import AppUpdateRegistration from './components/app-update-registration';
import { App as CapacitorApp } from '@capacitor/app';
import { Analytics } from '@vercel/analytics/react';
import { configureP2PBrowserPkcOptions } from './lib/p2p-browser-config';
// Only enable analytics on 5chan.app (Vercel deployment)
// Exclude Electron (file:// or localhost), Capacitor/APK (capacitor:// or localhost), and IPFS (ipfs:// or different domain)
const isVercelDeployment =
typeof window !== 'undefined' && (window.location.hostname === '5chan.app' || window.location.hostname === 'www.5chan.app') && !window.isElectron;
const shouldLoadAnalytics = import.meta.env.VITE_APP_DISTRIBUTION !== 'fdroid' && isVercelDeployment;
const e2eStartHash = import.meta.env.VITE_E2E_START_HASH?.trim();
const requestedE2EHarness = import.meta.env.DEV && typeof window !== 'undefined' ? new URLSearchParams(window.location.search).get('e2e') : null;
@@ -27,6 +27,7 @@ configureP2PBrowserPkcOptions();
const root = ReactDOM.createRoot(document.getElementById('root') as HTMLElement);
const renderRoot = async () => {
let e2eHarness: React.ComponentType | null = null;
let Analytics: React.ComponentType | null = null;
if (requestedE2EHarness === 'thread-auto-update') {
e2eHarness = (await import('./e2e/thread-auto-update-harness')).default;
@@ -40,13 +41,16 @@ const renderRoot = async () => {
}
const App = (await import('./app')).default;
if (shouldLoadAnalytics) {
Analytics = (await import('@vercel/analytics/react')).Analytics;
}
root.render(
<React.StrictMode>
<Router>
<AppUpdateRegistration />
<App />
{isVercelDeployment && <Analytics />}
{Analytics && <Analytics />}
</Router>
</React.StrictMode>,
);
+22
View File
@@ -43,6 +43,7 @@ describe('app-update', () => {
testState.electronDownloadAndInstallUpdateMock.mockReset();
testState.electronGetPlatformMock.mockReset();
testState.fetchMock.mockReset();
vi.stubEnv('VITE_APP_VERSION', '0.8.1');
vi.stubGlobal('fetch', testState.fetchMock);
window.electronApi = undefined;
Object.defineProperty(navigator, 'serviceWorker', {
@@ -91,6 +92,27 @@ describe('app-update', () => {
expect(result).toBeNull();
});
it('disables update checks for F-Droid builds', async () => {
vi.stubEnv('VITE_APP_DISTRIBUTION', 'fdroid');
testState.capacitorPlatform = 'android';
const { applyAvailableAppUpdate, isAppUpdateEnabled, resolveAvailableAppUpdate } = await loadModule();
await expect(resolveAvailableAppUpdate()).resolves.toBeNull();
await expect(
applyAvailableAppUpdate({
runtime: 'android',
targetVersion: '9.9.9',
assetName: '5chan-9.9.9.apk',
downloadUrl: 'https://github.com/bitsocialnet/5chan/releases/download/v9.9.9/5chan-9.9.9.apk',
releaseUrl: 'https://github.com/bitsocialnet/5chan/releases/tag/v9.9.9',
}),
).rejects.toThrow('App updates are disabled for this build');
expect(isAppUpdateEnabled).toBe(false);
expect(testState.fetchMock).not.toHaveBeenCalled();
expect(testState.androidDownloadAndInstallUpdateMock).not.toHaveBeenCalled();
});
it('selects the matching electron release asset for the current desktop platform', async () => {
window.electronApi = {
isElectron: true,
+4
View File
@@ -0,0 +1,4 @@
const appDistribution = import.meta.env.VITE_APP_DISTRIBUTION?.trim().toLowerCase();
const isAppUpdateEnabled = appDistribution !== 'fdroid';
export { isAppUpdateEnabled };
+11 -2
View File
@@ -1,5 +1,5 @@
import { Capacitor } from '@capacitor/core';
import AppUpdater from '../plugins/app-updater';
import { isAppUpdateEnabled } from './app-distribution';
import { currentAppVersion } from './app-version';
import { getDefaultReleaseUrl, getReleaseApiUrl, isAllowedDownloadUrl } from './app-update-config';
@@ -180,6 +180,10 @@ const fetchLatestReleaseUpdate = async (runtime: Extract<AppRuntime, 'electron'
};
const resolveAvailableAppUpdate = async (): Promise<AvailableAppUpdate | null> => {
if (!isAppUpdateEnabled) {
return null;
}
const runtime = getAppRuntime();
if (runtime === 'web') {
@@ -201,6 +205,10 @@ const resolveAvailableAppUpdate = async (): Promise<AvailableAppUpdate | null> =
};
const applyAvailableAppUpdate = async (update: AvailableAppUpdate): Promise<void> => {
if (!isAppUpdateEnabled) {
throw new Error('App updates are disabled for this build');
}
if (update.runtime === 'web') {
await refreshServiceWorkerRegistration().catch((error) => {
console.error('Failed to refresh service worker registration', error);
@@ -221,6 +229,7 @@ const applyAvailableAppUpdate = async (update: AvailableAppUpdate): Promise<void
return;
}
const { default: AppUpdater } = await import('../plugins/app-updater');
await AppUpdater.downloadAndInstallUpdate({
url: update.downloadUrl,
fileName: update.assetName,
@@ -228,4 +237,4 @@ const applyAvailableAppUpdate = async (update: AvailableAppUpdate): Promise<void
};
export type { AppRuntime, AvailableAppUpdate, NativeAppUpdateInfo, WebAppUpdateInfo };
export { applyAvailableAppUpdate, fetchLatestStableVersion, getAppRuntime, isElectron, refreshServiceWorkerRegistration, resolveAvailableAppUpdate };
export { applyAvailableAppUpdate, fetchLatestStableVersion, getAppRuntime, isAppUpdateEnabled, isElectron, refreshServiceWorkerRegistration, resolveAvailableAppUpdate };
+1 -3
View File
@@ -1,5 +1,3 @@
import packageJson from '../../package.json';
const resolveCurrentAppVersion = (): string => {
const configuredVersion = import.meta.env.VITE_APP_VERSION;
@@ -7,7 +5,7 @@ const resolveCurrentAppVersion = (): string => {
return configuredVersion.trim();
}
return packageJson.version;
return '0.0.0';
};
const currentAppVersion = resolveCurrentAppVersion();
+2
View File
@@ -7,6 +7,7 @@ import { VitePWA } from 'vite-plugin-pwa';
const { version: packageVersion } = JSON.parse(readFileSync(new URL('./package.json', import.meta.url), 'utf8'));
const appVersion = `${process.env.VITE_APP_VERSION || packageVersion}`.trim() || packageVersion;
process.env.VITE_APP_VERSION = appVersion;
const publicBase = process.env.PUBLIC_URL || '/';
const buildOutDir = 'build';
const basePathPrefix = (() => {
@@ -373,6 +374,7 @@ export default defineConfig({
include: ['ethers', 'assert', 'buffer', 'process', 'util', 'stream-browserify', 'isomorphic-fetch', 'workbox-core', 'workbox-precaching'],
},
define: {
'import.meta.env.VITE_APP_VERSION': JSON.stringify(appVersion),
'process.env.VITE_COMMIT_REF': JSON.stringify(process.env.COMMIT_REF),
'process.version': JSON.stringify(''),
global: 'globalThis',