Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .idea/gradle.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions cli/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ repositories {

dependencies {
implementation(project(":core"))
implementation(project(":git"))
implementation(kotlin("stdlib"))
testImplementation(libs.mockk)
testImplementation(libs.junit4)
Expand Down
15 changes: 10 additions & 5 deletions cli/src/main/kotlin/com/mitteloupe/cag/cli/AppArgumentProcessor.kt
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,8 @@ class AppArgumentProcessor(private val argumentParser: ArgumentParser = Argument
featureName = secondaries[SecondaryFlagConstants.NAME].orEmpty(),
packageName = secondaries[SecondaryFlagConstants.PACKAGE],
enableKtlint = secondaries.containsKey(SecondaryFlagConstants.KTLINT),
enableDetekt = secondaries.containsKey(SecondaryFlagConstants.DETEKT)
enableDetekt = secondaries.containsKey(SecondaryFlagConstants.DETEKT),
enableGit = secondaries.containsKey(SecondaryFlagConstants.GIT)
)
}

Expand All @@ -85,7 +86,8 @@ class AppArgumentProcessor(private val argumentParser: ArgumentParser = Argument
DataSourceRequest(
dataSourceName = name,
useKtor = libraries.contains("ktor"),
useRetrofit = libraries.contains("retrofit")
useRetrofit = libraries.contains("retrofit"),
enableGit = secondaries.containsKey(SecondaryFlagConstants.GIT)
)
}

Expand All @@ -109,7 +111,8 @@ class AppArgumentProcessor(private val argumentParser: ArgumentParser = Argument
) { secondaries ->
ViewModelRequest(
viewModelName = secondaries[SecondaryFlagConstants.NAME].orEmpty(),
targetPath = secondaries[SecondaryFlagConstants.PATH]
targetPath = secondaries[SecondaryFlagConstants.PATH],
enableGit = secondaries.containsKey(SecondaryFlagConstants.GIT)
)
}

Expand All @@ -121,7 +124,8 @@ class AppArgumentProcessor(private val argumentParser: ArgumentParser = Argument
ArchitectureRequest(
enableCompose = !secondaries.containsKey(SecondaryFlagConstants.NO_COMPOSE),
enableKtlint = secondaries.containsKey(SecondaryFlagConstants.KTLINT),
enableDetekt = secondaries.containsKey(SecondaryFlagConstants.DETEKT)
enableDetekt = secondaries.containsKey(SecondaryFlagConstants.DETEKT),
enableGit = secondaries.containsKey(SecondaryFlagConstants.GIT)
)
}

Expand All @@ -137,7 +141,8 @@ class AppArgumentProcessor(private val argumentParser: ArgumentParser = Argument
enableKtlint = secondaries.containsKey(SecondaryFlagConstants.KTLINT),
enableDetekt = secondaries.containsKey(SecondaryFlagConstants.DETEKT),
enableKtor = secondaries.containsKey(SecondaryFlagConstants.KTOR),
enableRetrofit = secondaries.containsKey(SecondaryFlagConstants.RETROFIT)
enableRetrofit = secondaries.containsKey(SecondaryFlagConstants.RETROFIT),
enableGit = secondaries.containsKey(SecondaryFlagConstants.GIT)
)
}

Expand Down
71 changes: 53 additions & 18 deletions cli/src/main/kotlin/com/mitteloupe/cag/cli/HelpContent.kt
Original file line number Diff line number Diff line change
@@ -1,13 +1,22 @@
package com.mitteloupe.cag.cli

object HelpContent {
private const val NEW_PROJECT_SYNTAX =
"[--new-project --name=ProjectName --package=PackageName " +
"[--no-compose] [--ktlint] [--detekt] [--ktor] [--retrofit] [--git]]"
private const val NEW_ARCHITECTURE_SYNTAX = "[--new-architecture [--no-compose] [--ktlint] [--detekt] [--git]]"
private const val NEW_FEATURE_SYNTAX = "[--new-feature --name=FeatureName [--package=PackageName] [--ktlint] [--detekt] [--git]]"
private const val NEW_DATASOURCE_SYNTAX = "[--new-datasource --name=DataSourceName [--with=ktor|retrofit|ktor,retrofit] [--git]]"
private const val NEW_USE_CASE_SYNTAX = "[--new-use-case --name=UseCaseName [--path=TargetPath] [--git]]"
private const val NEW_VIEW_MODEL_SYNTAX = "[--new-view-model --name=ViewModelName [--path=TargetPath] [--git]]"
const val USAGE_SYNTAX: String =
"usage: cag " +
"[--new-project --name=ProjectName --package=PackageName [--no-compose] [--ktlint] [--detekt] [--ktor] [--retrofit]]... " +
"[--new-architecture [--no-compose] [--ktlint] [--detekt]]... " +
"[--new-feature --name=FeatureName [--package=PackageName] [--ktlint] [--detekt]]... " +
"[--new-datasource --name=DataSourceName [--with=ktor|retrofit|ktor,retrofit]]... " +
"[--new-use-case --name=UseCaseName [--path=TargetPath]]... [--new-view-model --name=ViewModelName [--path=TargetPath]]..."
"$NEW_PROJECT_SYNTAX... " +
"$NEW_ARCHITECTURE_SYNTAX... " +
"$NEW_FEATURE_SYNTAX... " +
"$NEW_DATASOURCE_SYNTAX... " +
"$NEW_USE_CASE_SYNTAX... " +
"$NEW_VIEW_MODEL_SYNTAX..."

fun helpSections(): Map<String, String> =
buildMap {
Expand All @@ -22,20 +31,23 @@ object HelpContent {
--package=PackageName | --package PackageName | -p=PackageName | -p PackageName | -pPackageName
Specify the package name (required)
--no-compose | -nc
Disable Compose support for the project
Disable Compose support for the project
--ktlint | -kl
Enable ktlint for the project
Enable ktlint for the project
--detekt | -d
Enable detekt for the project
Enable detekt for the project
--ktor | -kt
Enable Ktor for data sources
Enable Ktor for data sources
--retrofit | -rt
Enable Retrofit for data sources
Enable Retrofit for data sources
--git | -g
Automatically initialize git repository and stage changes

Examples:
cag --new-project --name=MyApp --package=com.example.myapp
cag --new-project --name=MyApp --package=com.example.myapp --no-compose --ktlint --detekt
cag --new-project --name=MyApp --package=com.example.myapp --ktor --retrofit
cag --new-project --name=MyApp --package=com.example.myapp --git
""".trimIndent()
)
put(
Expand All @@ -45,16 +57,19 @@ object HelpContent {
--new-architecture | -na
Generate a new Clean Architecture package with domain, presentation, and UI layers
--no-compose | -nc
Disable Compose support for the preceding architecture package
Disable Compose support for the preceding architecture package
--ktlint | -kl
Enable ktlint for the preceding architecture package
Enable ktlint for the preceding architecture package
--detekt | -d
Enable detekt for the preceding architecture package
Enable detekt for the preceding architecture package
--git | -g
Automatically stage changes to git repository

Examples:
cag --new-architecture
cag --new-architecture --no-compose
cag --new-architecture --ktlint --detekt
cag --new-architecture --git
""".trimIndent()
)
put(
Expand All @@ -68,9 +83,11 @@ object HelpContent {
--package=PackageName | --package PackageName | -p=PackageName | -p PackageName | -pPackageName
Override the feature package for the preceding feature
--ktlint | -kl
Enable ktlint for the preceding feature (adds plugin and .editorconfig if missing)
Enable ktlint for the preceding feature (adds plugin and .editorconfig if missing)
--detekt | -d
Enable detekt for the preceding feature (adds plugin and detekt.yml if missing)
Enable detekt for the preceding feature (adds plugin and detekt.yml if missing)
--git | -g
Automatically stage changes to git repository

Examples:
cag --new-feature --name=Profile
Expand All @@ -88,6 +105,8 @@ object HelpContent {
Specify the data source name (required, DataSource suffix will be added automatically)
--with=ktor|retrofit|ktor,retrofit | -w=ktor|retrofit|ktor,retrofit
Attach dependencies to the preceding new data source
--git | -g
Automatically stage changes to git repository

Examples:
cag --new-datasource --name=User
Expand Down Expand Up @@ -121,6 +140,8 @@ object HelpContent {
Specify the ViewModel name (required)
--path=TargetPath | --path TargetPath | -p=TargetPath | -p TargetPath | -pTargetPath
Specify the target directory for the preceding ViewModel
--git | -g
Automatically stage changes to git repository

Examples:
cag --new-view-model --name=Profile
Expand Down Expand Up @@ -155,10 +176,17 @@ object HelpContent {
- Values in the project .cagrc override values in ~/.cagrc.

Sections:
- [new.versions] — applied when generating new projects (e.g., --new-project)
- [existing.versions] — applied when generating into an existing project (e.g., new architecture, feature, data source, use case, or view model)
- [new.versions] - applied when generating new projects (e.g., --new-project)
- [existing.versions] - applied when generating into an existing project (e.g., new architecture, feature, data source, use case, or view model)
- [git] - configuration for git integration

Keys correspond to version keys used by the generator, for example: kotlin, androidGradlePlugin, composeBom, composeNavigation, retrofit, ktor, okhttp3, etc.
Version Keys:
- Keys in [new.versions] and [existing.versions] correspond to version keys used by the generator,
for example: kotlin, androidGradlePlugin, composeBom, composeNavigation, retrofit, ktor, okhttp3, etc.

Git Configuration:
- autoInitialize=true|false - Whether to automatically initialize a git repository for new projects (default: false)
- autoStage=true|false - Whether to automatically stage changes after generation (default: true)

Example ~/.cagrc:
[new.versions]
Expand All @@ -169,12 +197,19 @@ object HelpContent {
retrofit=2.11.0
ktor=3.0.3

[git]
autoInitialize=true
autoStage=true

Example ./.cagrc (project overrides):
[new.versions]
composeBom=2025.09.01

[existing.versions]
okhttp3=4.12.0

[git]
autoInitialize=false
""".trimIndent()
)
}
Expand Down
31 changes: 31 additions & 0 deletions cli/src/main/kotlin/com/mitteloupe/cag/cli/Main.kt
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import com.mitteloupe.cag.core.request.GenerateFeatureRequestBuilder
import com.mitteloupe.cag.core.request.GenerateProjectTemplateRequest
import com.mitteloupe.cag.core.request.GenerateUseCaseRequest
import com.mitteloupe.cag.core.request.GenerateViewModelRequest
import com.mitteloupe.cag.git.Git
import java.io.File
import java.nio.file.Paths
import kotlin.system.exitProcess
Expand Down Expand Up @@ -83,6 +84,8 @@ fun main(arguments: Array<String>) {
val destinationRootDirectory = projectModel.selectedModuleRootDir() ?: projectRoot
val projectNamespace = basePackage ?: "com.unknown.app."

val git = Git()

projectTemplateRequests.forEach { request ->
val projectTemplateDestinationDirectory =
if (projectModel.selectedModuleRootDir() != null) {
Expand All @@ -105,6 +108,14 @@ fun main(arguments: Array<String>) {
setVersionProvider(configuration.newProjectVersions)
generator.generateProjectTemplate(projectTemplateRequest)
}

val shouldInitGit = request.enableGit || configuration.git.autoInitialize == true
if (shouldInitGit) {
val didInit = git.initializeRepository(projectTemplateDestinationDirectory)
if (!didInit || configuration.git.autoStage == true) {
runCatching { git.stageAll(projectTemplateDestinationDirectory) }
}
}
}

architectureRequests.forEach { request ->
Expand All @@ -121,6 +132,11 @@ fun main(arguments: Array<String>) {
setVersionProvider(configuration.existingProjectVersions)
generator.generateArchitecture(architectureRequest)
}

if (request.enableGit || configuration.git.autoStage == true) {
val gitRoot = projectModel.selectedModuleRootDir() ?: projectRoot
runCatching { git.stageAll(gitRoot) }
}
}

featureRequests.forEach { requestFeature ->
Expand All @@ -140,6 +156,11 @@ fun main(arguments: Array<String>) {
setVersionProvider(configuration.existingProjectVersions)
generator.generateFeature(request)
}

if (requestFeature.enableGit || configuration.git.autoStage == true) {
val gitRoot = projectModel.selectedModuleRootDir() ?: projectRoot
runCatching { git.stageAll(gitRoot) }
}
}

dataSourceRequests.forEach { request ->
Expand All @@ -153,6 +174,11 @@ fun main(arguments: Array<String>) {
useRetrofit = request.useRetrofit
)
}

if (request.enableGit || configuration.git.autoStage == true) {
val gitRoot = projectModel.selectedModuleRootDir() ?: projectRoot
runCatching { git.stageAll(gitRoot) }
}
}

useCaseRequests.forEach { request ->
Expand Down Expand Up @@ -191,6 +217,11 @@ fun main(arguments: Array<String>) {
setVersionProvider(configuration.existingProjectVersions)
generator.generateViewModel(viewModelRequest)
}

if (request.enableGit || configuration.git.autoStage == true) {
val gitRoot = projectModel.selectedModuleRootDir() ?: projectRoot
runCatching { git.stageAll(gitRoot) }
}
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
package com.mitteloupe.cag.cli.configuration

data class GitConfiguration(
val autoInitialize: Boolean? = null,
val autoStage: Boolean? = null
)

data class ClientConfiguration(
val newProjectVersions: Map<String, String> = emptyMap(),
val existingProjectVersions: Map<String, String> = emptyMap()
val existingProjectVersions: Map<String, String> = emptyMap(),
val git: GitConfiguration = GitConfiguration()
) {
companion object {
val EMPTY = ClientConfiguration()
Expand Down
Loading