mirror of
https://gh-proxy.org/https://github.com/tiajinsha/JKVideo
synced 2026-07-08 07:28:37 +08:00
feat:热更新
This commit is contained in:
67
.github/workflows/release.yml
vendored
Normal file
67
.github/workflows/release.yml
vendored
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
name: Release APK
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
release:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
if: "!contains(github.event.head_commit.message, '[skip ci]')"
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Setup Node 20
|
||||||
|
uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: '20'
|
||||||
|
cache: 'npm'
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: npm ci
|
||||||
|
|
||||||
|
- name: Bump version
|
||||||
|
id: bump
|
||||||
|
run: |
|
||||||
|
NEW_VERSION=$(node scripts/bump-version.js)
|
||||||
|
echo "version=$NEW_VERSION" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
- name: Commit version bump
|
||||||
|
run: |
|
||||||
|
git config user.name "github-actions[bot]"
|
||||||
|
git config user.email "github-actions[bot]@users.noreply.github.com"
|
||||||
|
git add app.json package.json android/app/build.gradle
|
||||||
|
git commit -m "chore: bump version to v${{ steps.bump.outputs.version }} [skip ci]"
|
||||||
|
git push
|
||||||
|
|
||||||
|
- name: Setup Java 17
|
||||||
|
uses: actions/setup-java@v4
|
||||||
|
with:
|
||||||
|
distribution: 'temurin'
|
||||||
|
java-version: '17'
|
||||||
|
|
||||||
|
- name: Setup Android SDK
|
||||||
|
uses: android-actions/setup-android@v3
|
||||||
|
|
||||||
|
- name: Grant Gradle execute permission
|
||||||
|
run: chmod +x android/gradlew
|
||||||
|
|
||||||
|
- name: Build Release APK
|
||||||
|
run: |
|
||||||
|
cd android
|
||||||
|
./gradlew assembleRelease --no-daemon
|
||||||
|
|
||||||
|
- name: Create GitHub Release
|
||||||
|
env:
|
||||||
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
run: |
|
||||||
|
gh release create "v${{ steps.bump.outputs.version }}" \
|
||||||
|
--title "JKVideo v${{ steps.bump.outputs.version }}" \
|
||||||
|
--generate-notes \
|
||||||
|
android/app/build/outputs/apk/release/app-release.apk
|
||||||
3
app.json
3
app.json
@@ -26,8 +26,7 @@
|
|||||||
"permissions": [
|
"permissions": [
|
||||||
"android.permission.RECORD_AUDIO",
|
"android.permission.RECORD_AUDIO",
|
||||||
"android.permission.MODIFY_AUDIO_SETTINGS",
|
"android.permission.MODIFY_AUDIO_SETTINGS",
|
||||||
"android.permission.RECORD_AUDIO",
|
"android.permission.REQUEST_INSTALL_PACKAGES"
|
||||||
"android.permission.MODIFY_AUDIO_SETTINGS"
|
|
||||||
],
|
],
|
||||||
"package": "com.anonymous.jkvideo"
|
"package": "com.anonymous.jkvideo"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,15 +1,17 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { View, Text, TouchableOpacity, StyleSheet } from 'react-native';
|
import { View, Text, TouchableOpacity, StyleSheet, ActivityIndicator } from 'react-native';
|
||||||
import { SafeAreaView } from 'react-native-safe-area-context';
|
import { SafeAreaView } from 'react-native-safe-area-context';
|
||||||
import { useRouter } from 'expo-router';
|
import { useRouter } from 'expo-router';
|
||||||
import { Ionicons } from '@expo/vector-icons';
|
import { Ionicons } from '@expo/vector-icons';
|
||||||
import { useAuthStore } from '../store/authStore';
|
import { useAuthStore } from '../store/authStore';
|
||||||
import { useSettingsStore } from '../store/settingsStore';
|
import { useSettingsStore } from '../store/settingsStore';
|
||||||
|
import { useCheckUpdate } from '../hooks/useCheckUpdate';
|
||||||
|
|
||||||
export default function SettingsScreen() {
|
export default function SettingsScreen() {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { isLoggedIn, logout } = useAuthStore();
|
const { isLoggedIn, logout } = useAuthStore();
|
||||||
const { coverQuality, setCoverQuality } = useSettingsStore();
|
const { coverQuality, setCoverQuality } = useSettingsStore();
|
||||||
|
const { currentVersion, isChecking, downloadProgress, checkUpdate } = useCheckUpdate();
|
||||||
|
|
||||||
const handleLogout = async () => {
|
const handleLogout = async () => {
|
||||||
await logout();
|
await logout();
|
||||||
@@ -26,6 +28,35 @@ export default function SettingsScreen() {
|
|||||||
<View style={styles.spacer} />
|
<View style={styles.spacer} />
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
|
<View style={styles.section}>
|
||||||
|
<Text style={styles.sectionLabel}>版本信息</Text>
|
||||||
|
<View style={styles.versionRow}>
|
||||||
|
<Text style={styles.versionLabel}>当前版本</Text>
|
||||||
|
<Text style={styles.versionValue}>v{currentVersion}</Text>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
<View style={styles.section}>
|
||||||
|
<Text style={styles.sectionLabel}>更新</Text>
|
||||||
|
<TouchableOpacity
|
||||||
|
style={styles.updateBtn}
|
||||||
|
onPress={checkUpdate}
|
||||||
|
activeOpacity={0.7}
|
||||||
|
disabled={isChecking || downloadProgress !== null}
|
||||||
|
>
|
||||||
|
{isChecking ? (
|
||||||
|
<>
|
||||||
|
<ActivityIndicator size="small" color="#00AEEC" style={{ marginRight: 8 }} />
|
||||||
|
<Text style={styles.updateBtnText}>检查中...</Text>
|
||||||
|
</>
|
||||||
|
) : downloadProgress !== null ? (
|
||||||
|
<Text style={styles.updateBtnText}>下载中 {downloadProgress}%</Text>
|
||||||
|
) : (
|
||||||
|
<Text style={styles.updateBtnText}>检查更新</Text>
|
||||||
|
)}
|
||||||
|
</TouchableOpacity>
|
||||||
|
</View>
|
||||||
|
|
||||||
<View style={styles.section}>
|
<View style={styles.section}>
|
||||||
<Text style={styles.sectionLabel}>封面图清晰度</Text>
|
<Text style={styles.sectionLabel}>封面图清晰度</Text>
|
||||||
<View style={styles.optionRow}>
|
<View style={styles.optionRow}>
|
||||||
@@ -102,6 +133,19 @@ const styles = StyleSheet.create({
|
|||||||
optionActive: { borderColor: '#00AEEC', backgroundColor: '#e8f7fd' },
|
optionActive: { borderColor: '#00AEEC', backgroundColor: '#e8f7fd' },
|
||||||
optionText: { fontSize: 14, color: '#666' },
|
optionText: { fontSize: 14, color: '#666' },
|
||||||
optionTextActive: { color: '#00AEEC', fontWeight: '600' },
|
optionTextActive: { color: '#00AEEC', fontWeight: '600' },
|
||||||
|
versionRow: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
justifyContent: 'space-between',
|
||||||
|
alignItems: 'center',
|
||||||
|
},
|
||||||
|
versionLabel: { fontSize: 14, color: '#212121' },
|
||||||
|
versionValue: { fontSize: 14, color: '#999' },
|
||||||
|
updateBtn: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
alignItems: 'center',
|
||||||
|
paddingVertical: 6,
|
||||||
|
},
|
||||||
|
updateBtnText: { fontSize: 14, color: '#00AEEC', fontWeight: '600' },
|
||||||
logoutBtn: {
|
logoutBtn: {
|
||||||
margin: 24,
|
margin: 24,
|
||||||
paddingVertical: 12,
|
paddingVertical: 12,
|
||||||
|
|||||||
@@ -460,7 +460,7 @@ const styles = StyleSheet.create({
|
|||||||
paddingBottom: 0,
|
paddingBottom: 0,
|
||||||
paddingTop: 12,
|
paddingTop: 12,
|
||||||
},
|
},
|
||||||
avatar: { width: 48, height: 48, borderRadius: 19, marginRight: 10 },
|
avatar: { width: 48, height: 48, borderRadius:30, marginRight: 10 },
|
||||||
upName: { flex: 1, fontSize: 14, color: "#212121", fontWeight: "500" },
|
upName: { flex: 1, fontSize: 14, color: "#212121", fontWeight: "500" },
|
||||||
followBtn: {
|
followBtn: {
|
||||||
backgroundColor: "#00AEEC",
|
backgroundColor: "#00AEEC",
|
||||||
|
|||||||
99
hooks/useCheckUpdate.ts
Normal file
99
hooks/useCheckUpdate.ts
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
import { useState } from 'react';
|
||||||
|
import { Alert, Platform } from 'react-native';
|
||||||
|
import * as FileSystem from 'expo-file-system';
|
||||||
|
import * as IntentLauncher from 'expo-intent-launcher';
|
||||||
|
import Constants from 'expo-constants';
|
||||||
|
|
||||||
|
const GITHUB_API = 'https://api.github.com/repos/tiajinsha/JKVideo/releases/latest';
|
||||||
|
|
||||||
|
function compareVersions(a: string, b: string): number {
|
||||||
|
const pa = a.replace(/^v/, '').split('.').map(Number);
|
||||||
|
const pb = b.replace(/^v/, '').split('.').map(Number);
|
||||||
|
for (let i = 0; i < 3; i++) {
|
||||||
|
if ((pa[i] ?? 0) > (pb[i] ?? 0)) return 1;
|
||||||
|
if ((pa[i] ?? 0) < (pb[i] ?? 0)) return -1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useCheckUpdate() {
|
||||||
|
const currentVersion = Constants.expoConfig?.version ?? '0.0.0';
|
||||||
|
const [isChecking, setIsChecking] = useState(false);
|
||||||
|
const [downloadProgress, setDownloadProgress] = useState<number | null>(null);
|
||||||
|
|
||||||
|
const checkUpdate = async () => {
|
||||||
|
setIsChecking(true);
|
||||||
|
try {
|
||||||
|
const res = await fetch(GITHUB_API, {
|
||||||
|
headers: { Accept: 'application/vnd.github+json' },
|
||||||
|
});
|
||||||
|
if (!res.ok) throw new Error(`GitHub API ${res.status}`);
|
||||||
|
const data = await res.json();
|
||||||
|
|
||||||
|
const latestVersion: string = data.tag_name ?? '';
|
||||||
|
const apkAsset = (data.assets as any[]).find((a) =>
|
||||||
|
(a.name as string).endsWith('.apk')
|
||||||
|
);
|
||||||
|
const downloadUrl: string = apkAsset?.browser_download_url ?? '';
|
||||||
|
const releaseNotes: string = data.body ?? '';
|
||||||
|
|
||||||
|
if (compareVersions(latestVersion, currentVersion) <= 0) {
|
||||||
|
Alert.alert('已是最新版本', `当前版本 v${currentVersion} 已是最新`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Alert.alert(
|
||||||
|
`发现新版本 ${latestVersion}`,
|
||||||
|
releaseNotes || '有新版本可用,是否立即下载?',
|
||||||
|
[
|
||||||
|
{ text: '取消', style: 'cancel' },
|
||||||
|
{
|
||||||
|
text: '下载安装',
|
||||||
|
onPress: () => downloadAndInstall(downloadUrl, latestVersion),
|
||||||
|
},
|
||||||
|
]
|
||||||
|
);
|
||||||
|
} catch (e: any) {
|
||||||
|
Alert.alert('检查失败', e?.message ?? '网络错误,请稍后重试');
|
||||||
|
} finally {
|
||||||
|
setIsChecking(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const downloadAndInstall = async (url: string, version: string) => {
|
||||||
|
if (Platform.OS !== 'android') {
|
||||||
|
Alert.alert('提示', '自动安装仅支持 Android 设备');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const localUri = FileSystem.cacheDirectory + `JKVideo-${version}.apk`;
|
||||||
|
try {
|
||||||
|
setDownloadProgress(0);
|
||||||
|
const downloadResumable = FileSystem.createDownloadResumable(
|
||||||
|
url,
|
||||||
|
localUri,
|
||||||
|
{},
|
||||||
|
({ totalBytesWritten, totalBytesExpectedToWrite }) => {
|
||||||
|
if (totalBytesExpectedToWrite > 0) {
|
||||||
|
setDownloadProgress(
|
||||||
|
Math.round((totalBytesWritten / totalBytesExpectedToWrite) * 100)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
await downloadResumable.downloadAsync();
|
||||||
|
setDownloadProgress(null);
|
||||||
|
|
||||||
|
const contentUri = await FileSystem.getContentUriAsync(localUri);
|
||||||
|
await IntentLauncher.startActivityAsync('android.intent.action.VIEW', {
|
||||||
|
data: contentUri,
|
||||||
|
flags: 1,
|
||||||
|
type: 'application/vnd.android.package-archive',
|
||||||
|
});
|
||||||
|
} catch (e: any) {
|
||||||
|
setDownloadProgress(null);
|
||||||
|
Alert.alert('下载失败', e?.message ?? '请稍后重试');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return { currentVersion, isChecking, downloadProgress, checkUpdate };
|
||||||
|
}
|
||||||
45
package-lock.json
generated
45
package-lock.json
generated
@@ -1,11 +1,11 @@
|
|||||||
{
|
{
|
||||||
"name": "reactbilibiliapp",
|
"name": "jkvideo",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "reactbilibiliapp",
|
"name": "jkvideo",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@dr.pogodin/react-native-static-server": "^0.26.0",
|
"@dr.pogodin/react-native-static-server": "^0.26.0",
|
||||||
@@ -15,8 +15,10 @@
|
|||||||
"expo": "~55.0.5",
|
"expo": "~55.0.5",
|
||||||
"expo-av": "^16.0.8",
|
"expo-av": "^16.0.8",
|
||||||
"expo-clipboard": "~55.0.9",
|
"expo-clipboard": "~55.0.9",
|
||||||
|
"expo-constants": "~55.0.9",
|
||||||
"expo-dev-client": "~55.0.11",
|
"expo-dev-client": "~55.0.11",
|
||||||
"expo-file-system": "~55.0.10",
|
"expo-file-system": "~55.0.10",
|
||||||
|
"expo-intent-launcher": "~55.0.9",
|
||||||
"expo-linear-gradient": "~55.0.8",
|
"expo-linear-gradient": "~55.0.8",
|
||||||
"expo-media-library": "~55.0.10",
|
"expo-media-library": "~55.0.10",
|
||||||
"expo-network": "~55.0.9",
|
"expo-network": "~55.0.9",
|
||||||
@@ -1532,15 +1534,15 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@expo/config": {
|
"node_modules/@expo/config": {
|
||||||
"version": "55.0.8",
|
"version": "55.0.10",
|
||||||
"resolved": "https://registry.npmjs.org/@expo/config/-/config-55.0.8.tgz",
|
"resolved": "https://registry.npmjs.org/@expo/config/-/config-55.0.10.tgz",
|
||||||
"integrity": "sha512-D7RYYHfErCgEllGxNwdYdkgzLna7zkzUECBV3snbUpf7RvIpB5l1LpCgzuVoc5KVew5h7N1Tn4LnT/tBSUZsQg==",
|
"integrity": "sha512-qCHxo9H1ZoeW+y0QeMtVZ3JfGmumpGrgUFX60wLWMarraoQZSe47ZUm9kJSn3iyoPjUtUNanO3eXQg+K8k4rag==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@expo/config-plugins": "~55.0.6",
|
"@expo/config-plugins": "~55.0.7",
|
||||||
"@expo/config-types": "^55.0.5",
|
"@expo/config-types": "^55.0.5",
|
||||||
"@expo/json-file": "^10.0.12",
|
"@expo/json-file": "^10.0.12",
|
||||||
"@expo/require-utils": "^55.0.2",
|
"@expo/require-utils": "^55.0.3",
|
||||||
"deepmerge": "^4.3.1",
|
"deepmerge": "^4.3.1",
|
||||||
"getenv": "^2.0.0",
|
"getenv": "^2.0.0",
|
||||||
"glob": "^13.0.0",
|
"glob": "^13.0.0",
|
||||||
@@ -1551,9 +1553,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@expo/config-plugins": {
|
"node_modules/@expo/config-plugins": {
|
||||||
"version": "55.0.6",
|
"version": "55.0.7",
|
||||||
"resolved": "https://registry.npmjs.org/@expo/config-plugins/-/config-plugins-55.0.6.tgz",
|
"resolved": "https://registry.npmjs.org/@expo/config-plugins/-/config-plugins-55.0.7.tgz",
|
||||||
"integrity": "sha512-cIox6FjZlFaaX40rbQ3DvP9e87S5X85H9uw+BAxJE5timkMhuByy3GAlOsj1h96EyzSiol7Q6YIGgY1Jiz4M+A==",
|
"integrity": "sha512-XZUoDWrsHEkH3yasnDSJABM/UxP5a1ixzRwU/M+BToyn/f0nTrSJJe/Ay/FpxkI4JSNz2n0e06I23b2bleXKVA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@expo/config-types": "^55.0.5",
|
"@expo/config-types": "^55.0.5",
|
||||||
@@ -1798,9 +1800,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@expo/require-utils": {
|
"node_modules/@expo/require-utils": {
|
||||||
"version": "55.0.2",
|
"version": "55.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/@expo/require-utils/-/require-utils-55.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/@expo/require-utils/-/require-utils-55.0.3.tgz",
|
||||||
"integrity": "sha512-dV5oCShQ1umKBKagMMT4B/N+SREsQe3lU4Zgmko5AO0rxKV0tynZT6xXs+e2JxuqT4Rz997atg7pki0BnZb4uw==",
|
"integrity": "sha512-TS1m5tW45q4zoaTlt6DwmdYHxvFTIxoLrTHKOFrIirHIqIXnHCzpceg8wumiBi+ZXSaGY2gobTbfv+WVhJY6Fw==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/code-frame": "^7.20.0",
|
"@babel/code-frame": "^7.20.0",
|
||||||
@@ -4451,12 +4453,12 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/expo-constants": {
|
"node_modules/expo-constants": {
|
||||||
"version": "55.0.7",
|
"version": "55.0.9",
|
||||||
"resolved": "https://registry.npmjs.org/expo-constants/-/expo-constants-55.0.7.tgz",
|
"resolved": "https://registry.npmjs.org/expo-constants/-/expo-constants-55.0.9.tgz",
|
||||||
"integrity": "sha512-kdcO4TsQRRqt0USvjaY5vgQMO9H52K3kBZ/ejC7F6rz70mv08GoowrZ1CYOr5O4JpPDRlIpQfZJUucaS/c+KWQ==",
|
"integrity": "sha512-iBiXjZeuU5S/8docQeNzsVvtDy4w0zlmXBpFEi1ypwugceEpdQQab65TVRbusXAcwpNVxCPMpNlDssYp0Pli2g==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@expo/config": "~55.0.8",
|
"@expo/config": "~55.0.10",
|
||||||
"@expo/env": "~2.1.1"
|
"@expo/env": "~2.1.1"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
@@ -4570,6 +4572,15 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/expo-intent-launcher": {
|
||||||
|
"version": "55.0.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/expo-intent-launcher/-/expo-intent-launcher-55.0.9.tgz",
|
||||||
|
"integrity": "sha512-s8k8dF8PfgzN0c+xzNTd9ceHh+/6mt2ncnMuqFaIIry2BeDGITbsgVijHr39eB5vS+KopCcOYYBYr+O1epx4TA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peerDependencies": {
|
||||||
|
"expo": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/expo-json-utils": {
|
"node_modules/expo-json-utils": {
|
||||||
"version": "55.0.0",
|
"version": "55.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/expo-json-utils/-/expo-json-utils-55.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/expo-json-utils/-/expo-json-utils-55.0.0.tgz",
|
||||||
|
|||||||
@@ -17,8 +17,10 @@
|
|||||||
"expo": "~55.0.5",
|
"expo": "~55.0.5",
|
||||||
"expo-av": "^16.0.8",
|
"expo-av": "^16.0.8",
|
||||||
"expo-clipboard": "~55.0.9",
|
"expo-clipboard": "~55.0.9",
|
||||||
|
"expo-constants": "~55.0.9",
|
||||||
"expo-dev-client": "~55.0.11",
|
"expo-dev-client": "~55.0.11",
|
||||||
"expo-file-system": "~55.0.10",
|
"expo-file-system": "~55.0.10",
|
||||||
|
"expo-intent-launcher": "~55.0.9",
|
||||||
"expo-linear-gradient": "~55.0.8",
|
"expo-linear-gradient": "~55.0.8",
|
||||||
"expo-media-library": "~55.0.10",
|
"expo-media-library": "~55.0.10",
|
||||||
"expo-network": "~55.0.9",
|
"expo-network": "~55.0.9",
|
||||||
|
|||||||
35
scripts/bump-version.js
Normal file
35
scripts/bump-version.js
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
const fs = require('fs');
|
||||||
|
const path = require('path');
|
||||||
|
|
||||||
|
const root = path.resolve(__dirname, '..');
|
||||||
|
|
||||||
|
// Read app.json
|
||||||
|
const appJsonPath = path.join(root, 'app.json');
|
||||||
|
const appJson = JSON.parse(fs.readFileSync(appJsonPath, 'utf8'));
|
||||||
|
const currentVersion = appJson.expo.version;
|
||||||
|
|
||||||
|
// Parse semver
|
||||||
|
const parts = currentVersion.split('.').map(Number);
|
||||||
|
parts[2] += 1;
|
||||||
|
const newVersion = parts.join('.');
|
||||||
|
const newVersionCode = parts[0] * 10000 + parts[1] * 100 + parts[2];
|
||||||
|
|
||||||
|
// Update app.json
|
||||||
|
appJson.expo.version = newVersion;
|
||||||
|
fs.writeFileSync(appJsonPath, JSON.stringify(appJson, null, 2) + '\n');
|
||||||
|
|
||||||
|
// Update package.json
|
||||||
|
const pkgJsonPath = path.join(root, 'package.json');
|
||||||
|
const pkgJson = JSON.parse(fs.readFileSync(pkgJsonPath, 'utf8'));
|
||||||
|
pkgJson.version = newVersion;
|
||||||
|
fs.writeFileSync(pkgJsonPath, JSON.stringify(pkgJson, null, 2) + '\n');
|
||||||
|
|
||||||
|
// Update android/app/build.gradle
|
||||||
|
const gradlePath = path.join(root, 'android', 'app', 'build.gradle');
|
||||||
|
let gradle = fs.readFileSync(gradlePath, 'utf8');
|
||||||
|
gradle = gradle.replace(/versionCode\s+\d+/, `versionCode ${newVersionCode}`);
|
||||||
|
gradle = gradle.replace(/versionName\s+"[^"]+"/, `versionName "${newVersion}"`);
|
||||||
|
fs.writeFileSync(gradlePath, gradle);
|
||||||
|
|
||||||
|
console.log(newVersion);
|
||||||
Reference in New Issue
Block a user