diff --git a/.cicdtemplate/.github/README.md b/.cicdtemplate/.github/README.md index 71d509d88..56f71406e 100644 --- a/.cicdtemplate/.github/README.md +++ b/.cicdtemplate/.github/README.md @@ -6,4 +6,4 @@ The `.github` contains all the required setup for Github Action to trigger. Simp Here are the current Secrets we need to add to the Project Settings - Secret: - `FIREBASE_APP_ID_STAGING` - your application id on Firebase. -- `FIREBASE_TOKEN` - your Firebase access/refresh token. Follow this [Guideline](https://firebase.google.com/docs/cli#cli-ci-systems) to get one for your project. \ No newline at end of file +- `FIREBASE_SERVICE_ACCOUNT_CREDENTIAL_FILE_CONTENT` - your Firebase service account credential file. Follow this [Guideline](https://github.com/wzieba/Firebase-Distribution-Github-Action/wiki/FIREBASE_TOKEN-migration#guide-2---the-same-but-with-screenshots) to get one for your project. \ No newline at end of file diff --git a/.cicdtemplate/.github/workflows/deploy_staging_and_production_to_firebase_app_distribution.yml b/.cicdtemplate/.github/workflows/deploy_staging_and_production_to_firebase_app_distribution.yml index b4a3c15ae..f684c2826 100644 --- a/.cicdtemplate/.github/workflows/deploy_staging_and_production_to_firebase_app_distribution.yml +++ b/.cicdtemplate/.github/workflows/deploy_staging_and_production_to_firebase_app_distribution.yml @@ -63,7 +63,7 @@ jobs: uses: wzieba/Firebase-Distribution-Github-Action@v1 with: appId: ${{secrets.FIREBASE_APP_ID_STAGING}} - token: ${{secrets.FIREBASE_TOKEN}} + serviceCredentialsFileContent: ${{ secrets.FIREBASE_SERVICE_ACCOUNT_CREDENTIAL_FILE_CONTENT }} groups: testers file: app/build/outputs/apk/staging/debug/app-staging-debug.apk @@ -74,6 +74,6 @@ jobs: uses: wzieba/Firebase-Distribution-Github-Action@v1 with: appId: ${{secrets.FIREBASE_APP_ID_PRODUCTION}} - token: ${{secrets.FIREBASE_TOKEN}} + serviceCredentialsFileContent: ${{ secrets.FIREBASE_SERVICE_ACCOUNT_CREDENTIAL_FILE_CONTENT }} groups: testers file: app/build/outputs/apk/production/debug/app-production-debug.apk diff --git a/README.md b/README.md index 821923ab0..40a882607 100644 --- a/README.md +++ b/README.md @@ -27,11 +27,14 @@ A collection of our Android templates: package-name= New package name (i.e., com.example.package) app-name= New app name (i.e., MyApp, "My App", "my-app") template= Template (i.e., xml, compose) + force= Force project creation even if the script fails (default: false) + destination= Set the output location where the project should be generated (i.e., /Users/johndoe/documents/projectfolder) ``` Examples: `kscript new_project.kts package-name=co.myxmlproject.example app-name="My XML Project" template=xml` `kscript scripts/new_project.kts package-name=co.myxmlproject.example app-name="My XML Project" template=xml` + `kscript new_project.kts package-name=co.myxmlproject.example app-name="My XML Project" template=xml destination=/Users/johndoe/documents/projectfolder` 4. Update `android_version_code` and `android_version_name` in `template/build.gradle` diff --git a/RxJavaTemplate[DEPRECATED]/fastlane/script/config.rb b/RxJavaTemplate[DEPRECATED]/fastlane/script/config.rb index 3f6281b69..b1141ec36 100644 --- a/RxJavaTemplate[DEPRECATED]/fastlane/script/config.rb +++ b/RxJavaTemplate[DEPRECATED]/fastlane/script/config.rb @@ -39,7 +39,7 @@ def sanity_check errors = [] errors << verify_slack_config unless verify_slack_config.nil? errors << verify_workspace_config unless verify_workspace_config.nil? - errors << verify_firebase_token unless verify_firebase_token.nil? + errors << verify_service_account unless verify_service_account.nil? throw errors unless errors.empty? end @@ -55,9 +55,9 @@ def verify_workspace_config 'Missing env.WORKSPACE, please set it accordingly and retry' end - def verify_firebase_token - return unless ENV['FIREBASE_TOKEN'].nil? + def verify_service_account + return unless ENV['FIREBASE_SERVICE_ACCOUNT_CREDENTIAL_FILE_CONTENT'].nil? - 'Missing env.FIREBASE_TOKEN for Firebase, please set it accordingly and retry' + 'Missing env.FIREBASE_SERVICE_ACCOUNT_CREDENTIAL_FILE_CONTENT for Firebase App Distribution, please set it accordingly and retry' end end diff --git a/sample-compose/app/src/androidTest/java/co/nimblehq/sample/compose/ui/screens/home/HomeScreenTest.kt b/sample-compose/app/src/androidTest/java/co/nimblehq/sample/compose/ui/screens/home/HomeScreenTest.kt index a5320cd92..a5ce5d6ea 100644 --- a/sample-compose/app/src/androidTest/java/co/nimblehq/sample/compose/ui/screens/home/HomeScreenTest.kt +++ b/sample-compose/app/src/androidTest/java/co/nimblehq/sample/compose/ui/screens/home/HomeScreenTest.kt @@ -46,10 +46,10 @@ class HomeScreenTest { every { mockIsFirstTimeLaunchPreferencesUseCase() } returns flowOf(false) viewModel = HomeViewModel( - TestDispatchersProvider, mockGetModelsUseCase, mockIsFirstTimeLaunchPreferencesUseCase, - mockUpdateFirstTimeLaunchPreferencesUseCase + mockUpdateFirstTimeLaunchPreferencesUseCase, + TestDispatchersProvider ) } diff --git a/sample-compose/app/src/main/java/co/nimblehq/sample/compose/ui/screens/AppBar.kt b/sample-compose/app/src/main/java/co/nimblehq/sample/compose/ui/screens/AppBar.kt index 880809bd0..ae9289870 100644 --- a/sample-compose/app/src/main/java/co/nimblehq/sample/compose/ui/screens/AppBar.kt +++ b/sample-compose/app/src/main/java/co/nimblehq/sample/compose/ui/screens/AppBar.kt @@ -4,6 +4,7 @@ import androidx.annotation.StringRes import androidx.compose.material.Text import androidx.compose.material.TopAppBar import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import co.nimblehq.sample.compose.R @@ -11,8 +12,12 @@ import co.nimblehq.sample.compose.ui.theme.AppTheme.colors import co.nimblehq.sample.compose.ui.theme.ComposeTheme @Composable -fun AppBar(@StringRes title: Int) { +fun AppBar( + @StringRes title: Int, + modifier: Modifier = Modifier, +) { TopAppBar( + modifier = modifier, title = { Text(text = stringResource(title)) }, backgroundColor = colors.topAppBarBackground ) diff --git a/sample-compose/app/src/main/java/co/nimblehq/sample/compose/ui/screens/home/HomeScreen.kt b/sample-compose/app/src/main/java/co/nimblehq/sample/compose/ui/screens/home/HomeScreen.kt index 2858d05b3..b2b521d73 100644 --- a/sample-compose/app/src/main/java/co/nimblehq/sample/compose/ui/screens/home/HomeScreen.kt +++ b/sample-compose/app/src/main/java/co/nimblehq/sample/compose/ui/screens/home/HomeScreen.kt @@ -38,7 +38,8 @@ fun HomeScreen( LaunchedEffect(isFirstTimeLaunch) { if (isFirstTimeLaunch) { - context.showToast("This is the first time launch") + context.showToast(context.getString(R.string.message_first_time_launch)) + viewModel.onFirstTimeLaunch() } } diff --git a/sample-compose/app/src/main/java/co/nimblehq/sample/compose/ui/screens/home/HomeViewModel.kt b/sample-compose/app/src/main/java/co/nimblehq/sample/compose/ui/screens/home/HomeViewModel.kt index 41174f1f2..889254ecc 100644 --- a/sample-compose/app/src/main/java/co/nimblehq/sample/compose/ui/screens/home/HomeViewModel.kt +++ b/sample-compose/app/src/main/java/co/nimblehq/sample/compose/ui/screens/home/HomeViewModel.kt @@ -15,10 +15,10 @@ import javax.inject.Inject @HiltViewModel class HomeViewModel @Inject constructor( - dispatchersProvider: DispatchersProvider, getModelsUseCase: GetModelsUseCase, isFirstTimeLaunchPreferencesUseCase: IsFirstTimeLaunchPreferencesUseCase, - updateFirstTimeLaunchPreferencesUseCase: UpdateFirstTimeLaunchPreferencesUseCase, + private val updateFirstTimeLaunchPreferencesUseCase: UpdateFirstTimeLaunchPreferencesUseCase, + private val dispatchersProvider: DispatchersProvider, ) : BaseViewModel() { private val _uiModels = MutableStateFlow>(emptyList()) @@ -38,15 +38,19 @@ class HomeViewModel @Inject constructor( .catch { e -> _error.emit(e) } .launchIn(viewModelScope) - launch(dispatchersProvider.io) { - val isFirstTimeLaunch = isFirstTimeLaunchPreferencesUseCase() - .catch { e -> _error.emit(e) } - .first() - - _isFirstTimeLaunch.emit(isFirstTimeLaunch) - if (isFirstTimeLaunch) { - updateFirstTimeLaunchPreferencesUseCase(false) + isFirstTimeLaunchPreferencesUseCase() + .onEach { isFirstTimeLaunch -> + _isFirstTimeLaunch.emit(isFirstTimeLaunch) } + .flowOn(dispatchersProvider.io) + .catch { e -> _error.emit(e) } + .launchIn(viewModelScope) + } + + fun onFirstTimeLaunch() { + launch(dispatchersProvider.io) { + updateFirstTimeLaunchPreferencesUseCase(false) + _isFirstTimeLaunch.emit(false) } } diff --git a/sample-compose/app/src/main/java/co/nimblehq/sample/compose/ui/screens/home/Item.kt b/sample-compose/app/src/main/java/co/nimblehq/sample/compose/ui/screens/home/Item.kt index 681169755..91fabd676 100644 --- a/sample-compose/app/src/main/java/co/nimblehq/sample/compose/ui/screens/home/Item.kt +++ b/sample-compose/app/src/main/java/co/nimblehq/sample/compose/ui/screens/home/Item.kt @@ -17,12 +17,13 @@ import co.nimblehq.sample.compose.ui.theme.AppTheme.dimensions fun Item( uiModel: UiModel, onClick: (UiModel) -> Unit, - onLongClick: (UiModel) -> Unit + onLongClick: (UiModel) -> Unit, + modifier: Modifier = Modifier, ) { var expanded by remember { mutableStateOf(false) } Box( - modifier = Modifier + modifier = modifier .fillMaxWidth() .combinedClickable( onClick = { onClick(uiModel) }, diff --git a/sample-compose/app/src/main/java/co/nimblehq/sample/compose/ui/screens/home/ItemList.kt b/sample-compose/app/src/main/java/co/nimblehq/sample/compose/ui/screens/home/ItemList.kt index 9ed8b0374..2323ecee9 100644 --- a/sample-compose/app/src/main/java/co/nimblehq/sample/compose/ui/screens/home/ItemList.kt +++ b/sample-compose/app/src/main/java/co/nimblehq/sample/compose/ui/screens/home/ItemList.kt @@ -4,6 +4,7 @@ import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items import androidx.compose.material.Divider import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier import androidx.compose.ui.tooling.preview.Preview import co.nimblehq.sample.compose.model.UiModel import co.nimblehq.sample.compose.ui.theme.ComposeTheme @@ -12,9 +13,10 @@ import co.nimblehq.sample.compose.ui.theme.ComposeTheme fun ItemList( uiModels: List, onItemClick: (UiModel) -> Unit, - onItemLongClick: (UiModel) -> Unit + onItemLongClick: (UiModel) -> Unit, + modifier: Modifier = Modifier, ) { - LazyColumn { + LazyColumn(modifier) { items(uiModels) { uiModel -> Item( uiModel = uiModel, diff --git a/sample-compose/app/src/main/res/values/strings.xml b/sample-compose/app/src/main/res/values/strings.xml index 1f735069e..06743934e 100644 --- a/sample-compose/app/src/main/res/values/strings.xml +++ b/sample-compose/app/src/main/res/values/strings.xml @@ -11,4 +11,6 @@ Third Data: %s Edit + + This is the first time launch diff --git a/sample-compose/app/src/test/java/co/nimblehq/sample/compose/ui/screens/home/HomeScreenTest.kt b/sample-compose/app/src/test/java/co/nimblehq/sample/compose/ui/screens/home/HomeScreenTest.kt index 2b508ba2b..cb67695aa 100644 --- a/sample-compose/app/src/test/java/co/nimblehq/sample/compose/ui/screens/home/HomeScreenTest.kt +++ b/sample-compose/app/src/test/java/co/nimblehq/sample/compose/ui/screens/home/HomeScreenTest.kt @@ -14,8 +14,7 @@ import co.nimblehq.sample.compose.ui.screens.BaseScreenTest import co.nimblehq.sample.compose.ui.screens.MainActivity import co.nimblehq.sample.compose.ui.theme.ComposeTheme import io.kotest.matchers.shouldBe -import io.mockk.every -import io.mockk.mockk +import io.mockk.* import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.test.* @@ -54,6 +53,29 @@ class HomeScreenTest : BaseScreenTest() { listOf(Model(1), Model(2), Model(3)) ) every { mockIsFirstTimeLaunchPreferencesUseCase() } returns flowOf(false) + coEvery { mockUpdateFirstTimeLaunchPreferencesUseCase(any()) } just Runs + } + + @Test + fun `When entering the Home screen for the first time, it shows a toast confirming that`() { + every { mockIsFirstTimeLaunchPreferencesUseCase() } returns flowOf(true) + + initComposable { + composeRule.waitForIdle() + advanceUntilIdle() + + ShadowToast.showedToast(activity.getString(R.string.message_first_time_launch)) shouldBe true + } + } + + @Test + fun `When entering the Home screen NOT for the first time, it doesn't show the toast confirming that`() { + initComposable { + composeRule.waitForIdle() + advanceUntilIdle() + + ShadowToast.showedToast(activity.getString(R.string.message_first_time_launch)) shouldBe false + } } @Test @@ -106,10 +128,10 @@ class HomeScreenTest : BaseScreenTest() { private fun initViewModel() { viewModel = HomeViewModel( - coroutinesRule.testDispatcherProvider, mockGetModelsUseCase, mockIsFirstTimeLaunchPreferencesUseCase, - mockUpdateFirstTimeLaunchPreferencesUseCase + mockUpdateFirstTimeLaunchPreferencesUseCase, + coroutinesRule.testDispatcherProvider ) } } diff --git a/sample-compose/app/src/test/java/co/nimblehq/sample/compose/ui/screens/home/HomeViewModelTest.kt b/sample-compose/app/src/test/java/co/nimblehq/sample/compose/ui/screens/home/HomeViewModelTest.kt index 117acc397..91cea39b6 100644 --- a/sample-compose/app/src/test/java/co/nimblehq/sample/compose/ui/screens/home/HomeViewModelTest.kt +++ b/sample-compose/app/src/test/java/co/nimblehq/sample/compose/ui/screens/home/HomeViewModelTest.kt @@ -8,11 +8,9 @@ import co.nimblehq.sample.compose.test.CoroutineTestRule import co.nimblehq.sample.compose.ui.AppDestination import co.nimblehq.sample.compose.util.DispatchersProvider import io.kotest.matchers.shouldBe -import io.mockk.every -import io.mockk.mockk +import io.mockk.* import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.flow.flow -import kotlinx.coroutines.flow.flowOf +import kotlinx.coroutines.flow.* import kotlinx.coroutines.test.* import org.junit.* @@ -29,11 +27,13 @@ class HomeViewModelTest { private lateinit var viewModel: HomeViewModel private val models = listOf(Model(1), Model(2), Model(3)) + private val isFirstTimeLaunch = false @Before fun setUp() { every { mockGetModelsUseCase() } returns flowOf(models) - every { mockIsFirstTimeLaunchPreferencesUseCase() } returns flowOf(false) + every { mockIsFirstTimeLaunchPreferencesUseCase() } returns flowOf(isFirstTimeLaunch) + coEvery { mockUpdateFirstTimeLaunchPreferencesUseCase(any()) } just Runs initViewModel() } @@ -78,12 +78,44 @@ class HomeViewModelTest { } } + @Test + fun `When initializing the ViewModel, it emits whether the app is launched for the first time accordingly`() = + runTest { + viewModel.isFirstTimeLaunch.first() shouldBe isFirstTimeLaunch + } + + @Test + fun `When initializing the ViewModel and isFirstTimeLaunchPreferencesUseCase returns error, it shows the corresponding error`() = + runTest { + val error = Exception() + every { mockIsFirstTimeLaunchPreferencesUseCase() } returns flow { throw error } + + initViewModel(dispatchers = CoroutineTestRule(StandardTestDispatcher()).testDispatcherProvider) + + viewModel.error.test { + advanceUntilIdle() + + expectMostRecentItem() shouldBe error + } + } + + @Test + fun `When launching the app for the first time, it executes the use case and emits value accordingly`() = + runTest { + viewModel.onFirstTimeLaunch() + + coVerify(exactly = 1) { + mockUpdateFirstTimeLaunchPreferencesUseCase(false) + } + viewModel.isFirstTimeLaunch.first() shouldBe false + } + private fun initViewModel(dispatchers: DispatchersProvider = coroutinesRule.testDispatcherProvider) { viewModel = HomeViewModel( - dispatchers, mockGetModelsUseCase, mockIsFirstTimeLaunchPreferencesUseCase, - mockUpdateFirstTimeLaunchPreferencesUseCase + mockUpdateFirstTimeLaunchPreferencesUseCase, + dispatchers ) } } diff --git a/sample-compose/build.gradle.kts b/sample-compose/build.gradle.kts index 97eea8d7a..267fe3af3 100644 --- a/sample-compose/build.gradle.kts +++ b/sample-compose/build.gradle.kts @@ -67,45 +67,28 @@ detekt { koverMerged { enable() - val generatedFiles = setOf( - "*.R.class", - "*.R\$*.class", - "*.*\$ViewBinder*.*", - "*.*\$InjectAdapter*.*", - "*.*Injector*.*", + val excludedFiles = listOf( "*.BuildConfig.*", "*.BuildConfig", - "*.Manifest*.*", - "*.*_ViewBinding*.*", - "*.*Adapter*.*", - "*.*Test*.*", // Enum "*.*\$Creator*", - // Nav Component - "*.*_Factory*", - "*.*FragmentArgs*", - "*.*FragmentDirections*", - "*.FragmentNavArgsLazy.kt", - "*.*Fragment*navArgs*", - "*.*ModuleDeps*.*", - "*.*NavGraphDirections*", + // DI + "*.di.*", // Hilt "*.*_ComponentTreeDeps*", "*.*_HiltComponents*", "*.*_HiltModules*", "*.*_MembersInjector*", - "*.Hilt_*" - ) - - val excludedPackages = setOf( - "com.bumptech.glide.*", + "*.*_Factory*", + "*.Hilt_*", "dagger.hilt.internal.*", "hilt_aggregated_deps.*", - "co.nimblehq.sample.compose.databinding.*", - "co.nimblehq.sample.compose.di.*" + // Jetpack Compose + "*.ComposableSingletons*", + "*.*\$*Preview\$*", + "*.ui.preview.*", ) - val excludedFiles = generatedFiles + excludedPackages filters { classes { excludes += excludedFiles diff --git a/sample-compose/buildSrc/src/main/java/Versions.kt b/sample-compose/buildSrc/src/main/java/Versions.kt index 7ef68e4d6..f8c2444c3 100644 --- a/sample-compose/buildSrc/src/main/java/Versions.kt +++ b/sample-compose/buildSrc/src/main/java/Versions.kt @@ -6,7 +6,7 @@ object Versions { const val ANDROID_TARGET_SDK_VERSION = 33 const val ANDROID_VERSION_CODE = 1 - const val ANDROID_VERSION_NAME = "3.20.0" + const val ANDROID_VERSION_NAME = "3.21.0" // Dependencies (Alphabet sorted) const val ACCOMPANIST_PERMISSIONS_VERSION = "0.30.1" diff --git a/sample-xml/buildSrc/src/main/java/Versions.kt b/sample-xml/buildSrc/src/main/java/Versions.kt index c13692e9d..8271e0940 100644 --- a/sample-xml/buildSrc/src/main/java/Versions.kt +++ b/sample-xml/buildSrc/src/main/java/Versions.kt @@ -6,7 +6,7 @@ object Versions { const val ANDROID_TARGET_SDK_VERSION = 33 const val ANDROID_VERSION_CODE = 1 - const val ANDROID_VERSION_NAME = "3.20.0" + const val ANDROID_VERSION_NAME = "3.21.0" // Dependencies (Alphabet sorted) const val ANDROID_COMMON_KTX_VERSION = "0.1.1" diff --git a/scripts/new_project.kts b/scripts/new_project.kts index 9ae3837c3..299496412 100644 --- a/scripts/new_project.kts +++ b/scripts/new_project.kts @@ -6,6 +6,8 @@ object NewProject { private const val DELIMITER_ARGUMENT = "=" private const val KEY_APP_NAME = "app-name" + private const val KEY_DESTINATION = "destination" + private const val KEY_FORCE = "force" private const val KEY_HELP = "--help" private const val KEY_PACKAGE_NAME = "package-name" private const val KEY_TEMPLATE = "template" @@ -36,10 +38,13 @@ object NewProject { $KEY_PACKAGE_NAME= New package name (i.e., com.example.package) $KEY_APP_NAME= New app name (i.e., MyApp, "My App", "my-app") $KEY_TEMPLATE= Template (i.e., $TEMPLATE_XML, $TEMPLATE_COMPOSE) + $KEY_FORCE= Force project creation even if the script fails (default: false) + $KEY_DESTINATION= Set the output location where the project should be generated (i.e., /Users/johndoe/documents/projectfolder) Examples: kscript new_project.kts $KEY_PACKAGE_NAME=co.myxmlproject.example $KEY_APP_NAME="My XML Project" $KEY_TEMPLATE=$TEMPLATE_XML - kscript scripts/new_project.kts $KEY_PACKAGE_NAME=co.myxmlproject.example $KEY_APP_NAME="My XML Project" $KEY_TEMPLATE=$TEMPLATE_XML + kscript scripts/new_project.kts $KEY_PACKAGE_NAME=co.myxmlproject.example $KEY_APP_NAME="My XML Project" $KEY_TEMPLATE=$TEMPLATE_XML $KEY_FORCE=true + kscript scripts/new_project.kts $KEY_PACKAGE_NAME=co.myxmlproject.example $KEY_APP_NAME="My XML Project" $KEY_TEMPLATE=$TEMPLATE_XML $KEY_FORCE=true $KEY_DESTINATION=/Users/johndoe/documents/projectfolder """.trimIndent() private val modules = listOf("app", "data", "domain") @@ -58,12 +63,16 @@ object NewProject { } } + private var forceProjectCreation = false + private var packageName = "" + private var destination = rootPath + private var projectFolderName: String = "" private val projectPath: String - get() = rootPath + projectFolderName + get() = destination + projectFolderName private val rootPath: String get() = System.getProperty("user.dir").let { userDir -> @@ -141,6 +150,14 @@ object NewProject { validateTemplate(value) hasTemplate = true } + arg.startsWith("$KEY_FORCE$DELIMITER_ARGUMENT") -> { + val (key, value) = arg.split(DELIMITER_ARGUMENT) + forceProjectCreation = value.toBoolean() + } + arg.startsWith("$KEY_DESTINATION$DELIMITER_ARGUMENT") -> { + val (key, value) = arg.split(DELIMITER_ARGUMENT) + validateDestination(value) + } else -> { showMessage( message = "ERROR: Invalid argument name: $arg \n$helpMessage", @@ -205,6 +222,18 @@ object NewProject { } } + private fun validateDestination(value: String) { + if (value.isNotBlank()) { + destination = "${value.trim()}$SEPARATOR_SLASH" + } else { + showMessage( + message = "Error: Invalid Destination: destination cannot be blank \n$helpMessage", + exitAfterMessage = true, + isError = true, + ) + } + } + private fun initializeNewProjectFolder() { showMessage("=> 🐢 Initializing new project...") copyFiles(fromPath = rootPath + templateFolderName, toPath = projectPath) @@ -375,7 +404,23 @@ object NewProject { isError: Boolean = false, ) { println("\n${if (isError) "❌ " else ""}${message}\n") - if (exitAfterMessage) System.exit(exitValue) + if (exitAfterMessage) { + if (isError) { + exitWithError(exitValue) + } else { + System.exit(exitValue) + } + } + } + + private fun exitWithError(exitValue: Int = 0) { + if (!forceProjectCreation && projectFolderName.isNotBlank()) { + val file = File(projectPath) + if (file.exists()) { + file.deleteRecursively() + } + } + System.exit(exitValue) } private fun String.uppercaseEveryFirstCharacter(): String { diff --git a/template-compose/build.gradle.kts b/template-compose/build.gradle.kts index c7c714ea4..c2d513a97 100644 --- a/template-compose/build.gradle.kts +++ b/template-compose/build.gradle.kts @@ -65,45 +65,28 @@ tasks.withType().configureEach { koverMerged { enable() - val generatedFiles = setOf( - "*.R.class", - "*.R\$*.class", - "*.*\$ViewBinder*.*", - "*.*\$InjectAdapter*.*", - "*.*Injector*.*", + val excludedFiles = listOf( "*.BuildConfig.*", "*.BuildConfig", - "*.Manifest*.*", - "*.*_ViewBinding*.*", - "*.*Adapter*.*", - "*.*Test*.*", // Enum "*.*\$Creator*", - // Nav Component - "*.*_Factory*", - "*.*FragmentArgs*", - "*.*FragmentDirections*", - "*.FragmentNavArgsLazy.kt", - "*.*Fragment*navArgs*", - "*.*ModuleDeps*.*", - "*.*NavGraphDirections*", + // DI + "*.di.*", // Hilt "*.*_ComponentTreeDeps*", "*.*_HiltComponents*", "*.*_HiltModules*", "*.*_MembersInjector*", - "*.Hilt_*" - ) - - val excludedPackages = setOf( - "com.bumptech.glide.*", + "*.*_Factory*", + "*.Hilt_*", "dagger.hilt.internal.*", "hilt_aggregated_deps.*", - "co.nimblehq.template.compose.databinding.*", - "co.nimblehq.template.compose.di.*" + // Jetpack Compose + "*.ComposableSingletons*", + "*.*\$*Preview\$*", + "*.ui.preview.*", ) - val excludedFiles = generatedFiles + excludedPackages filters { classes { excludes += excludedFiles diff --git a/version.properties b/version.properties index 230cb574e..052c20892 100644 --- a/version.properties +++ b/version.properties @@ -1,3 +1,3 @@ kotlinVersion=1.6.21 kscriptVersion=4.0.3 -templateScriptVersion=3.20.0 +templateScriptVersion=3.21.0