diff --git a/.github/actions/setup-xcode/action.yml b/.github/actions/setup-xcode/action.yml index 905ab1ba..51e80c93 100644 --- a/.github/actions/setup-xcode/action.yml +++ b/.github/actions/setup-xcode/action.yml @@ -4,7 +4,7 @@ inputs: xcode-version: description: 'The xcode version to use' required: false - default: '16.3.0' + default: '16.4.0' runs: using: "composite" steps: diff --git a/.github/workflows/build-ios.yml b/.github/workflows/build-ios.yml index 31a49f14..3728a4c4 100644 --- a/.github/workflows/build-ios.yml +++ b/.github/workflows/build-ios.yml @@ -31,14 +31,23 @@ jobs: - name: 🚀 Run fastlane build - run: yarn fastlane:ios:build - env: - # FASTLANE_PASSWORD: ${{ secrets.FASTLANE_PASSWORD }} - APPSTORE_CONNECT_API_KEY_JSON: ${{ secrets.APPSTORE_CONNECT_API_KEY_JSON }} - FASTLANE_APPLE_APPLICATION_SPECIFIC_PASSWORD: ${{ secrets.FASTLANE_APPLE_APPLICATION_SPECIFIC_PASSWORD }} - MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }} - MATCH_REPO_PAT: "anultravioletaurora:${{ secrets.SIGNING_REPO_PAT }}" - + run: | + cd ios + set -o pipefail + xcodebuild \ + CC=clang CPLUSPLUS=clang++ LD=clang LDPLUSPLUS=clang++ \ + -derivedDataPath build -UseModernBuildSystem=YES \ + -workspace Jellify.xcworkspace \ + -scheme Jellify \ + -sdk iphonesimulator \ + -configuration Release \ + -destination 'generic/platform=iOS Simulator' \ + build \ + CODE_SIGNING_ALLOWED=NO + - name: Package .app for Simulator + run: | + cd ios/build/Build/Products/Release-iphonesimulator + zip -r Jellify-Release-Simulator.zip Jellify.app - name: 📦 Upload IPA for testing uses: actions/upload-artifact@v4 @@ -48,5 +57,8 @@ jobs: path: | ios/build/*.ipa ios/*.ipa + Jellify.app + *.zip + ios/build/Build/Products/Release-iphonesimulator/Jellify-Release-Simulator.zip retention-days: 7 if-no-files-found: warn \ No newline at end of file diff --git a/android/app/src/main/java/com/jellify/MainApplication.kt b/android/app/src/main/java/com/jellify/MainApplication.kt index 000a2d19..4d12b84b 100644 --- a/android/app/src/main/java/com/jellify/MainApplication.kt +++ b/android/app/src/main/java/com/jellify/MainApplication.kt @@ -11,7 +11,7 @@ import com.facebook.react.defaults.DefaultReactHost.getDefaultReactHost import com.facebook.react.defaults.DefaultReactNativeHost import com.facebook.react.soloader.OpenSourceMergedSoMapping import com.facebook.soloader.SoLoader -import com.otahotupdate.OtaHotUpdate +import com.margelo.nitro.nitroota.core.getStoredBundlePath import com.facebook.react.ReactNativeApplicationEntryPoint.loadReactNative @@ -27,7 +27,7 @@ class MainApplication : Application(), ReactApplication { // Packages that cannot be autolinked yet can be added manually here, for example: // add(MyReactNativePackage()) }, - jsBundleFilePath = OtaHotUpdate.bundleJS(this@MainApplication) + jsBundleFilePath = getStoredBundlePath(applicationContext) ) } diff --git a/ios/AppDelegate.swift b/ios/AppDelegate.swift index a4a1b62d..6aa0bb43 100644 --- a/ios/AppDelegate.swift +++ b/ios/AppDelegate.swift @@ -6,6 +6,8 @@ import React_RCTAppDelegate import ReactAppDependencyProvider import react_native_ota_hot_update import GoogleCast +import NitroOtaBundleManager + @@ -73,7 +75,12 @@ class ReactNativeDelegate: RCTDefaultReactNativeFactoryDelegate { #if DEBUG return RCTBundleURLProvider.sharedSettings().jsBundleURL(forBundleRoot: "index"); #else - return OtaHotUpdate.getBundle() // -> Add this line + // Check for OTA bundle first + if let bundleURL = NitroOtaBundleManager.shared.getStoredBundleURL() { + return bundleURL + } + // Fallback to main bundle + return Bundle.main.url(forResource: "main", withExtension: "jsbundle") #endif } diff --git a/ios/Gemfile.lock b/ios/Gemfile.lock index 75697078..03c39de9 100644 --- a/ios/Gemfile.lock +++ b/ios/Gemfile.lock @@ -187,15 +187,15 @@ GEM fastlane-sirp (1.0.0) sysrandom (~> 1.0) ffi (1.17.2) - ffi (1.17.2-aarch64-linux-gnu) + ffi (1.17.2-aarch64-linux) ffi (1.17.2-aarch64-linux-musl) - ffi (1.17.2-arm-linux-gnu) + ffi (1.17.2-arm-linux) ffi (1.17.2-arm-linux-musl) ffi (1.17.2-arm64-darwin) - ffi (1.17.2-x86-linux-gnu) + ffi (1.17.2-x86-linux) ffi (1.17.2-x86-linux-musl) ffi (1.17.2-x86_64-darwin) - ffi (1.17.2-x86_64-linux-gnu) + ffi (1.17.2-x86_64-linux) ffi (1.17.2-x86_64-linux-musl) fourflusher (2.3.1) fuzzy_match (2.0.4) @@ -334,16 +334,16 @@ GEM xcpretty (~> 0.2, >= 0.0.7) PLATFORMS - aarch64-linux-gnu + aarch64-linux aarch64-linux-musl - arm-linux-gnu + arm-linux arm-linux-musl arm64-darwin ruby - x86-linux-gnu + x86-linux x86-linux-musl x86_64-darwin - x86_64-linux-gnu + x86_64-linux x86_64-linux-musl DEPENDENCIES diff --git a/ios/Jellify.xcodeproj/xcshareddata/xcschemes/Jellify.xcscheme b/ios/Jellify.xcodeproj/xcshareddata/xcschemes/Jellify.xcscheme index d48489d2..4a780d3b 100644 --- a/ios/Jellify.xcodeproj/xcshareddata/xcschemes/Jellify.xcscheme +++ b/ios/Jellify.xcodeproj/xcshareddata/xcschemes/Jellify.xcscheme @@ -41,7 +41,7 @@ true + pod 'NitroOtaBundleManager', :path => '../node_modules/react-native-nitro-ota' use_react_native!( :path => config[:reactNativePath], diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 59b84f69..0e9f6205 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -42,7 +42,7 @@ PODS: - ReactCommon/turbomodule/core - SocketRocket - Yoga - - NitroModules (0.29.4): + - NitroModules (0.30.2): - boost - DoubleConversion - fast_float @@ -71,6 +71,65 @@ PODS: - ReactCommon/turbomodule/core - SocketRocket - Yoga + - NitroOta (0.2.3): + - boost + - DoubleConversion + - fast_float + - fmt + - glog + - hermes-engine + - NitroModules + - RCT-Folly + - RCT-Folly/Fabric + - RCTRequired + - RCTTypeSafety + - React-callinvoker + - React-Core + - React-debug + - React-Fabric + - React-featureflags + - React-graphics + - React-ImageManager + - React-jsi + - React-NativeModulesApple + - React-RCTFabric + - React-renderercss + - React-rendererdebug + - React-utils + - ReactCodegen + - ReactCommon/turbomodule/bridging + - ReactCommon/turbomodule/core + - SocketRocket + - SSZipArchive + - Yoga + - NitroOtaBundleManager (0.2.3): + - boost + - DoubleConversion + - fast_float + - fmt + - glog + - hermes-engine + - RCT-Folly + - RCT-Folly/Fabric + - RCTRequired + - RCTTypeSafety + - React-Core + - React-debug + - React-Fabric + - React-featureflags + - React-graphics + - React-ImageManager + - React-jsi + - React-NativeModulesApple + - React-RCTFabric + - React-renderercss + - React-rendererdebug + - React-utils + - ReactCodegen + - ReactCommon/turbomodule/bridging + - ReactCommon/turbomodule/core + - SocketRocket + - Yoga - NitroWebImage (0.6.1): - boost - DoubleConversion @@ -3050,9 +3109,9 @@ PODS: - ReactCommon/turbomodule/core - SocketRocket - Yoga - - SDWebImage (5.11.1): - - SDWebImage/Core (= 5.11.1) - - SDWebImage/Core (5.11.1) + - SDWebImage (5.21.2): + - SDWebImage/Core (= 5.21.2) + - SDWebImage/Core (5.21.2) - Sentry/HybridSDK (8.56.0) - SocketRocket (0.7.1) - SSZipArchive (2.4.3) @@ -3070,6 +3129,8 @@ DEPENDENCIES: - hermes-engine (from `../node_modules/react-native/sdks/hermes-engine/hermes-engine.podspec`) - NitroImage (from `../node_modules/react-native-nitro-image`) - NitroModules (from `../node_modules/react-native-nitro-modules`) + - NitroOta (from `../node_modules/react-native-nitro-ota`) + - NitroOtaBundleManager (from `../node_modules/react-native-nitro-ota`) - NitroWebImage (from `../node_modules/react-native-nitro-web-image`) - RCT-Folly (from `../node_modules/react-native/third-party-podspecs/RCT-Folly.podspec`) - RCTDeprecation (from `../node_modules/react-native/ReactApple/Libraries/RCTFoundation/RCTDeprecation`) @@ -3197,6 +3258,10 @@ EXTERNAL SOURCES: :path: "../node_modules/react-native-nitro-image" NitroModules: :path: "../node_modules/react-native-nitro-modules" + NitroOta: + :path: "../node_modules/react-native-nitro-ota" + NitroOtaBundleManager: + :path: "../node_modules/react-native-nitro-ota" NitroWebImage: :path: "../node_modules/react-native-nitro-web-image" RCT-Folly: @@ -3389,7 +3454,9 @@ SPEC CHECKSUMS: google-cast-sdk: 1fb6724e94cc5ff23b359176e0cf6360586bb97a hermes-engine: 273e30e7fb618279934b0b95ffab60ecedb7acf5 NitroImage: f2d14e6531629630904d8ceb4037459d6057440a - NitroModules: 8d96528777600e967d371fd62b7eb183e9204530 + NitroModules: 72acdf761541be4f8bbb3f0cdca41ae23ce13c50 + NitroOta: b2adec40232e3da0cc9eeaa3f7e1ec67313b1f17 + NitroOtaBundleManager: 09eeec5c1d7e33b868b2374ce64613f23ad348cd NitroWebImage: 04de36dd513d350fe3d4d3a9279a95817c1a39f1 PromisesObjC: f5707f49cb48b9636751c5b2e7d227e43fba9f47 Protobuf: 164aea2ae380c3951abdc3e195220c01d17400e0 @@ -3481,13 +3548,13 @@ SPEC CHECKSUMS: RNScreens: 833237c48c756d40764540246a501b47dadb2cac RNSentry: 60919c9cdac7e4b35e9f5dd0149f551ec12f35cb RNWorklets: ab618bf7d1c7fd2cb793b9f0f39c3e29274b3ebf - SDWebImage: a7f831e1a65eb5e285e3fb046a23fcfbf08e696d + SDWebImage: 9f177d83116802728e122410fb25ad88f5c7608a Sentry: 3d82977434c80381cae856c40b99c39e4be6bc11 SocketRocket: d4aabe649be1e368d1318fdf28a022d714d65748 SSZipArchive: fe6a26b2a54d5a0890f2567b5cc6de5caa600aef SwiftAudioEx: f6aa653770f3a0d3851edaf8d834a30aee4a7646 Yoga: 689c8e04277f3ad631e60fe2a08e41d411daf8eb -PODFILE CHECKSUM: 320ff45ce6a038db4058773abf4993a674de141e +PODFILE CHECKSUM: 05d07b9cff134e4c27345bc2b588e090e4d3431c COCOAPODS: 1.16.2 diff --git a/jest.config.js b/jest.config.js index 77ab5795..1f019f5e 100644 --- a/jest.config.js +++ b/jest.config.js @@ -13,6 +13,7 @@ module.exports = { './jest/setup/rntp.ts', './jest/setup/sentry.ts', './jest/setup/nitro-image.ts', + './jest/setup/nitro-ota.ts', './tamagui.config.ts', './jest/setup/native-modules.ts', ], diff --git a/jest/setup/nitro-ota.ts b/jest/setup/nitro-ota.ts new file mode 100644 index 00000000..2244e15c --- /dev/null +++ b/jest/setup/nitro-ota.ts @@ -0,0 +1,21 @@ +// Mock for react-native-nitro-ota +jest.mock('react-native-nitro-ota', () => ({ + githubOTA: jest.fn(() => ({ + downloadUrl: 'mock://download.url', + versionUrl: 'mock://version.url', + })), + OTAUpdateManager: jest.fn().mockImplementation(() => ({ + checkForUpdates: jest.fn().mockResolvedValue(null), + downloadUpdate: jest.fn().mockResolvedValue(undefined), + })), +})) + +// Update the existing nitro-modules mock to include createHybridObject +jest.mock('react-native-nitro-modules', () => ({ + NitroModules: { + createModule: jest.fn(), + install: jest.fn(), + createHybridObject: jest.fn(() => ({})), + }, + createNitroModule: jest.fn(), +})) diff --git a/package.json b/package.json index c9510218..3a3e1add 100644 --- a/package.json +++ b/package.json @@ -77,7 +77,8 @@ "react-native-linear-gradient": "^2.8.3", "react-native-mmkv": "3.3.3", "react-native-nitro-image": "^0.6.1", - "react-native-nitro-modules": "^0.29.4", + "react-native-nitro-modules": "^0.30.2", + "react-native-nitro-ota": "^0.2.3", "react-native-nitro-web-image": "^0.6.1", "react-native-ota-hot-update": "2.3.1", "react-native-pager-view": "^6.9.1", @@ -143,4 +144,4 @@ "node": ">=18" }, "packageManager": "yarn@1.22.22" -} \ No newline at end of file +} diff --git a/scripts/getRandomVersion.sh b/scripts/getRandomVersion.sh new file mode 100644 index 00000000..74b3afea --- /dev/null +++ b/scripts/getRandomVersion.sh @@ -0,0 +1,40 @@ +#!/usr/bin/env bash +set -euo pipefail + +FILE="ota.version" + +# Array of sentences +sentences=( + "Git Blame violet" + "Thank you pikachu" + "Margelo folks are Awesome" + "Pikachu Should have coded this" + "meta sue violet" +) + +# Read previous value if file exists +prev="" +if [[ -f "$FILE" ]]; then + prev=$(<"$FILE") +fi + +# Function to get a random new sentence (not same as prev) +get_random_sentence() { + local choice + while true; do + choice="${sentences[RANDOM % ${#sentences[@]}]}" + if [[ "$choice" != "$prev" ]]; then + echo "$choice" + return + fi + done +} + +new_sentence=$(get_random_sentence) + +# Write atomically +tmp="${FILE}.tmp.$$" +echo "$new_sentence" > "$tmp" +mv "$tmp" "$FILE" + +echo "✅ Updated $FILE with: \"$new_sentence\"" \ No newline at end of file diff --git a/scripts/ota-android.sh b/scripts/ota-android.sh index 4ccb193e..48355aab 100644 --- a/scripts/ota-android.sh +++ b/scripts/ota-android.sh @@ -1,5 +1,5 @@ version=$(jq -r '.version' "$(dirname "$0")/../package.json") -target_branch="${version}/android" +target_branch="nitro_${version}_android" cd android git clone https://github.com/Jellify-Music/App-Bundles.git cd App-Bundles @@ -13,6 +13,7 @@ fi cd ../.. yarn createBundle:android cd android/App-Bundles +bash ../../scripts/getRandomVersion.sh git add . git commit -m "OTA-Update - $(date +'%b %d %H:%M')" git push https://x-access-token:$SIGNING_REPO_PAT@github.com/Jellify-Music/App-Bundles.git "$target_branch" diff --git a/scripts/ota-iOS.sh b/scripts/ota-iOS.sh index ff702f55..1c38c26b 100644 --- a/scripts/ota-iOS.sh +++ b/scripts/ota-iOS.sh @@ -1,6 +1,6 @@ version=$(jq -r '.version' "$(dirname "$0")/../package.json") -target_branch="${version}/ios" +target_branch="nitro_${version}_ios" cd ios rm -rf App-Bundles git clone https://github.com/Jellify-Music/App-Bundles.git @@ -16,6 +16,7 @@ rm -rf Readme.md cd ../.. yarn createBundle:ios cd ios/App-Bundles +bash ../../scripts/getRandomVersion.sh git add . git commit -m "OTA-Update - $(date +'%b %d %H:%M')" git push https://x-access-token:$SIGNING_REPO_PAT@github.com/Jellify-Music/App-Bundles.git "$target_branch" diff --git a/src/components/OtaUpdates/index.tsx b/src/components/OtaUpdates/index.tsx index 9589c282..83930ad3 100644 --- a/src/components/OtaUpdates/index.tsx +++ b/src/components/OtaUpdates/index.tsx @@ -13,10 +13,19 @@ import Animated, { useSharedValue, useAnimatedStyle, withTiming } from 'react-na import hotUpdate from 'react-native-ota-hot-update' import DeviceInfo from 'react-native-device-info' import { OTA_UPDATE_ENABLED } from '../../configs/config' +import { githubOTA, OTAUpdateManager } from 'react-native-nitro-ota' const version = DeviceInfo.getVersion() -const gitBranch = `${version}/${Platform.OS}` +const gitBranch = `nitro_${version}_${Platform.OS}` + +const { downloadUrl, versionUrl } = githubOTA({ + githubUrl: 'https://github.com/Jellify-Music/App-Bundles', + otaVersionPath: 'ota.version', // optional, defaults to 'ota.version' + ref: gitBranch, // optional, defaults to 'main' +}) + +const otaManager = new OTAUpdateManager(downloadUrl, versionUrl) const GitUpdateModal = () => { const progress = useSharedValue(0) @@ -35,41 +44,33 @@ const GitUpdateModal = () => { console.log(isVisible, 'isVisible') const onCheckGitVersion = () => { setLoading(true) - - hotUpdate.git.checkForGitUpdate({ - branch: gitBranch, - bundlePath: Platform.OS === 'ios' ? 'main.jsbundle' : 'index.android.bundle', - url: 'https://github.com/Jellify-Music/App-Bundles', - - onCloneFailed(msg: string) { + otaManager + .checkForUpdates() + .then((update) => { + if (update) { + otaManager + .downloadUpdate() + .then(() => { + Alert.alert( + 'Jellify has been updated!', + 'Restart to apply the changes', + [ + { text: 'OK', onPress: () => hotUpdate.resetApp() }, + { text: 'Cancel', style: 'cancel' }, + ], + ) + }) + .catch((error) => { + console.error('Error downloading update:', error) + }) + } + }) + .catch((error) => { + console.error('Error checking for updates:', error) + }) + .finally(() => { setLoading(false) - // Alert.alert('Clone project faile .d!', msg) - }, - onCloneSuccess() { - Alert.alert('Jellify has been updated!', 'Restart to apply the changes', [ - { text: 'OK', onPress: () => hotUpdate.resetApp() }, - { text: 'Cancel', style: 'cancel' }, - ]) - }, - onPullFailed(msg: string) { - setLoading(false) - // Alert.alert('Pull project failed!', msg) - }, - onPullSuccess() { - Alert.alert('Jellify has been updated!', 'Restart to apply the changes', [ - { text: 'OK', onPress: () => hotUpdate.resetApp() }, - { text: 'Cancel', style: 'cancel' }, - ]) - }, - onProgress(received: number, total: number) { - const percent = (+received / +total) * 100 - LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut) - progress.value = withTiming(percent, { duration: 300 }) - }, - onFinishProgress() { - setLoading(false) - }, - }) + }) } useEffect(() => { diff --git a/yarn.lock b/yarn.lock index 15a1412b..ee2b5abd 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8498,10 +8498,15 @@ react-native-nitro-image@^0.6.1: resolved "https://registry.yarnpkg.com/react-native-nitro-image/-/react-native-nitro-image-0.6.1.tgz#bf4d37ed52d435f751a27017fade4a6d7b7fb9a6" integrity sha512-LKttegj2fZ6NdmqZG531LB2NteW5TbGy/vbyHFtTX39g73it3R5jss+IMYmuHJHjhbFse/wu2yP+zh3MXAa6Jg== -react-native-nitro-modules@^0.29.4: - version "0.29.4" - resolved "https://registry.yarnpkg.com/react-native-nitro-modules/-/react-native-nitro-modules-0.29.4.tgz#e82a243996f4a85b9194a2e2a89970487638e1c2" - integrity sha512-AfUMcwFtj9FuEDwDLN5eIVo0lBYTQqDaV7meiFzuoZyRmc8ywykFTKfyZwRN2t8Z/WtTlfCj9Y9yaET33IImsg== +react-native-nitro-modules@^0.30.2: + version "0.30.2" + resolved "https://registry.yarnpkg.com/react-native-nitro-modules/-/react-native-nitro-modules-0.30.2.tgz#0a13acbb0bc3032cfa0198059944ddd832bc2dc3" + integrity sha512-+/uVS7FQwOiKYZQERMIvBRv5/X3CVHrFG6Nr/kIhVfVxGeUimHnBz7cgA97lJKIn7AKDRWL+UjLedW8pGOt0dg== + +react-native-nitro-ota@^0.2.3: + version "0.2.3" + resolved "https://registry.yarnpkg.com/react-native-nitro-ota/-/react-native-nitro-ota-0.2.3.tgz#555f4922d7dd951a86706016ebdf04dd69ad7b16" + integrity sha512-AB2eycbbRoSPA7UvDCyaHgiSh5QhiI8kq9gpPoDSTbjLEEdFGVb9r+YPTm78bzYC5XqZIP+mPJ7TGWnuRr2c3Q== react-native-nitro-web-image@^0.6.1: version "0.6.1"