-
Notifications
You must be signed in to change notification settings - Fork 1
✨[FEAT] Bitrise CI/CD 환경 구축 #137
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: develop
Are you sure you want to change the base?
Changes from all commits
760055d
c4c25d8
7aa1ee4
3e98b0b
8d5ae68
9374703
8f57091
836ba60
bca79b0
e2b2f5a
fbe09ec
c712274
4d6507f
465484f
f934edf
c194f42
dd37659
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -3,18 +3,42 @@ package com.umc.edison | |
| import android.app.Application | ||
| import android.util.Log | ||
| import androidx.work.Configuration | ||
| import com.google.firebase.ktx.Firebase | ||
| import com.google.firebase.remoteconfig.ktx.remoteConfig | ||
| import com.umc.edison.common.logging.AppLogger | ||
| import com.umc.edison.data.di.EntryPointModule | ||
| import com.umc.edison.data.sync.SyncDataWorkerFactory | ||
| import com.umc.edison.presentation.sync.SyncTrigger | ||
| import com.umc.edison.remote.config.DomainProvider | ||
| import dagger.hilt.EntryPoints | ||
| import dagger.hilt.android.HiltAndroidApp | ||
| import io.branch.referral.Branch | ||
| import kotlinx.coroutines.CoroutineScope | ||
| import kotlinx.coroutines.Dispatchers | ||
| import kotlinx.coroutines.SupervisorJob | ||
| import kotlinx.coroutines.delay | ||
| import kotlinx.coroutines.launch | ||
| import kotlinx.coroutines.tasks.await | ||
| import javax.inject.Inject | ||
| import com.umc.edison.common.logging.UserContext | ||
|
|
||
| @HiltAndroidApp | ||
| class EdisonApplication : Application(), Configuration.Provider { | ||
| @Inject | ||
| lateinit var domainProvider: DomainProvider | ||
|
|
||
| @Inject | ||
| lateinit var userContext: UserContext | ||
|
|
||
| private val remoteConfigScope = CoroutineScope(SupervisorJob() + Dispatchers.IO) | ||
| private val remoteConfig by lazy { Firebase.remoteConfig } | ||
| private val maxAttempts = 4 | ||
| private val initialBackoffMs = 1_000L | ||
|
|
||
| override fun onCreate() { | ||
| super.onCreate() | ||
| initCrashlyticsContext() | ||
| initRemoteConfig() | ||
|
|
||
| // Branch SDK 초기화 | ||
| Branch.getAutoInstance(this) | ||
|
|
@@ -24,6 +48,51 @@ class EdisonApplication : Application(), Configuration.Provider { | |
| syncTrigger.setupSync() | ||
| } | ||
|
|
||
| private fun initCrashlyticsContext() { | ||
| remoteConfigScope.launch { | ||
| userContext.ensureInstallId() | ||
| userContext.setBuildInfo( | ||
| BuildConfig.BUILD_TYPE, | ||
| BuildConfig.APPLICATION_ID, | ||
| BuildConfig.VERSION_NAME | ||
| ) | ||
| } | ||
| } | ||
|
|
||
| private fun initRemoteConfig() { | ||
| remoteConfig.getString("base_url") | ||
| .takeIf { it.isNotBlank() } | ||
| ?.let(domainProvider::setDomain) | ||
|
|
||
| remoteConfigScope.launch { | ||
| var backoff = initialBackoffMs | ||
| repeat(maxAttempts) { attempt -> | ||
| val activated = try { | ||
| remoteConfig.fetchAndActivate().await() | ||
| } catch (e: Exception) { | ||
| AppLogger.w( | ||
| "EdisonApplication", | ||
| "Remote config fetch failed on attempt ${attempt + 1}", | ||
| e | ||
| ) | ||
| false | ||
| } | ||
|
|
||
| if (activated) { | ||
| remoteConfig.getString("base_url") | ||
| .takeIf { it.isNotBlank() } | ||
| ?.let(domainProvider::setDomain) | ||
| return@launch | ||
| } | ||
|
|
||
| if (attempt < maxAttempts - 1) { | ||
| delay(backoff) | ||
| backoff = (backoff * 2).coerceAtMost(8_000L) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
| override val workManagerConfiguration: Configuration | ||
| get() { | ||
| val syncDataWorkerFactory: SyncDataWorkerFactory = EntryPoints.get( | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,36 @@ | ||
| package com.umc.edison.common.logging | ||
|
|
||
| import android.util.Log | ||
| import com.google.firebase.crashlytics.ktx.crashlytics | ||
| import com.google.firebase.ktx.Firebase | ||
| import com.umc.edison.BuildConfig | ||
|
|
||
| object AppLogger { | ||
| private const val PREFIX_DEBUG = "D/" | ||
| private const val PREFIX_INFO = "I/" | ||
| private const val PREFIX_WARN = "W/" | ||
| private const val PREFIX_ERROR = "E/" | ||
| private val isDebug = BuildConfig.DEBUG | ||
|
|
||
| fun d(tag: String, message: String) { | ||
| if (isDebug) Log.d(tag, message) | ||
| Firebase.crashlytics.log("$PREFIX_DEBUG$tag: $message") | ||
| } | ||
|
Comment on lines
+15
to
+18
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The Specifically, |
||
|
|
||
| fun i(tag: String, message: String) { | ||
| if (isDebug) Log.i(tag, message) | ||
| Firebase.crashlytics.log("$PREFIX_INFO$tag: $message") | ||
| } | ||
|
|
||
| fun w(tag: String, message: String, throwable: Throwable? = null) { | ||
| if (isDebug) Log.w(tag, message, throwable) | ||
| Firebase.crashlytics.log("$PREFIX_WARN$tag: $message") | ||
| throwable?.let { Firebase.crashlytics.recordException(it) } | ||
| } | ||
|
|
||
| fun e(tag: String, message: String, throwable: Throwable? = null) { | ||
| if (isDebug) Log.e(tag, message, throwable) | ||
| Firebase.crashlytics.log("$PREFIX_ERROR$tag: $message") | ||
| throwable?.let { Firebase.crashlytics.recordException(it) } | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,39 @@ | ||
| package com.umc.edison.common.logging | ||
|
|
||
| import com.google.firebase.crashlytics.ktx.crashlytics | ||
| import com.google.firebase.ktx.Firebase | ||
| import com.umc.edison.data.datasources.PrefDataSource | ||
| import java.util.UUID | ||
| import javax.inject.Inject | ||
| import javax.inject.Singleton | ||
|
|
||
| @Singleton | ||
| class UserContext @Inject constructor( | ||
| private val prefDataSource: PrefDataSource | ||
| ) { | ||
| companion object { | ||
| private const val KEY_INSTALL_ID = "install_id" | ||
| } | ||
|
|
||
| suspend fun ensureInstallId(): String { | ||
| val existing: String = prefDataSource.get(KEY_INSTALL_ID, "") | ||
| if (existing.isNotBlank()) { | ||
| Firebase.crashlytics.setCustomKey(KEY_INSTALL_ID, existing) | ||
| return existing | ||
| } | ||
| val newId = UUID.randomUUID().toString() | ||
| prefDataSource.set(KEY_INSTALL_ID, newId) | ||
| Firebase.crashlytics.setCustomKey(KEY_INSTALL_ID, newId) | ||
| return newId | ||
| } | ||
|
|
||
| fun setAccountId(accountId: String) { | ||
| Firebase.crashlytics.setUserId(accountId) | ||
| } | ||
|
|
||
| fun setBuildInfo(buildType: String, applicationId: String, versionName: String) { | ||
| Firebase.crashlytics.setCustomKey("build_type", buildType) | ||
| Firebase.crashlytics.setCustomKey("application_id", applicationId) | ||
| Firebase.crashlytics.setCustomKey("version_name", versionName) | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,4 @@ | ||
| package com.umc.edison.data.token | ||
|
|
||
| class NoRefreshTokenException : IllegalStateException("No refresh token") | ||
| class RefreshFailedException(message: String) : IllegalStateException(message) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
qa빌드 유형에서buildConfigField를 다시 정의하고 있습니다.initWith(getByName("debug"))를 통해debug빌드 유형의 설정을 이미 상속받았기 때문에 이 부분은 중복됩니다. 코드의 간결성과 유지보수성을 위해 이 라인을 제거하는 것이 좋습니다.