├── gradle.properties
├── settings.gradle
├── icon.png
├── example.gif
├── META-INF
└── MANIFEST.MF
├── extras
└── logo
│ ├── gpmxcf.xcf
│ ├── gpm_original_logo.png
│ └── npm_original_logo.png
├── jdeploy-bundle
├── icon.png
└── jdeploy.js
├── src
├── main
│ └── kotlin
│ │ └── com
│ │ └── theapache64
│ │ └── gpm
│ │ ├── utils
│ │ ├── Logger.kt
│ │ ├── GpmConfig.kt
│ │ ├── StringExtensions.kt
│ │ ├── GradleUtils.kt
│ │ ├── InputUtils.kt
│ │ └── StringUtils.kt
│ │ ├── commands
│ │ ├── base
│ │ │ ├── BaseViewModel.kt
│ │ │ ├── BaseCommand.kt
│ │ │ └── BaseInstallUninstallViewModel.kt
│ │ ├── gpm
│ │ │ ├── GpmComponent.kt
│ │ │ ├── GpmViewModel.kt
│ │ │ └── Gpm.kt
│ │ └── subcommands
│ │ │ ├── docs
│ │ │ ├── DocsComponent.kt
│ │ │ ├── Docs.kt
│ │ │ └── DocsViewModel.kt
│ │ │ ├── uninstall
│ │ │ ├── UninstallComponent.kt
│ │ │ ├── UninstallViewModel.kt
│ │ │ └── Uninstall.kt
│ │ │ └── install
│ │ │ ├── InstallComponent.kt
│ │ │ ├── Install.kt
│ │ │ └── InstallViewModel.kt
│ │ ├── data
│ │ ├── remote
│ │ │ ├── maven
│ │ │ │ ├── models
│ │ │ │ │ ├── ArtifactInfo.kt
│ │ │ │ │ └── SearchResult.kt
│ │ │ │ └── MavenApiInterface.kt
│ │ │ └── gpm
│ │ │ │ ├── GpmApiInterface.kt
│ │ │ │ └── models
│ │ │ │ └── GpmDep.kt
│ │ └── repos
│ │ │ ├── GpmRepo.kt
│ │ │ └── MavenRepo.kt
│ │ ├── di
│ │ ├── Qualifiers.kt
│ │ └── modules
│ │ │ ├── CommandModule.kt
│ │ │ ├── MoshiModule.kt
│ │ │ ├── TransactionModule.kt
│ │ │ ├── NetworkModule.kt
│ │ │ └── GradleModule.kt
│ │ ├── Main.kt
│ │ ├── core
│ │ ├── gm
│ │ │ ├── GradleDep.kt
│ │ │ └── GradleManager.kt
│ │ └── TransactionManager.kt
│ │ └── models
│ │ └── GpmFileData.kt
└── test
│ ├── kotlin
│ └── com
│ │ └── theapache64
│ │ └── gpm
│ │ ├── core
│ │ └── gm
│ │ │ └── GradleDependencyTest.kt
│ │ ├── Utils.kt
│ │ ├── rules
│ │ ├── MyDaggerMockRule.kt
│ │ └── MainCoroutineRule.kt
│ │ ├── data
│ │ └── repos
│ │ │ ├── GpmRepoTest.kt
│ │ │ └── MavenRepoTest.kt
│ │ ├── utils
│ │ ├── StringUtilsTest.kt
│ │ ├── GradleManagerTest.kt
│ │ └── GradleUtilsTest.kt
│ │ └── commands
│ │ └── subcommands
│ │ ├── docs
│ │ └── DocsTest.kt
│ │ ├── InstallTest.kt
│ │ └── uninstall
│ │ └── UninstallTest.kt
│ └── resources
│ └── sample.build.gradle
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── .idea
├── codeStyles
│ ├── codeStyleConfig.xml
│ └── Project.xml
├── compiler.xml
├── kotlinc.xml
├── vcs.xml
├── .gitignore
├── runConfigurations.xml
├── misc.xml
├── jarRepositories.xml
├── libraries-with-intellij-classes.xml
├── markdown-navigator-enh.xml
└── artifacts
│ └── gpm_main_jar.xml
├── registry
├── okhttp.json
└── navigation-material.json
├── install.sh
├── .github
└── workflows
│ └── gradle.yml
├── package.json
├── gradlew.bat
├── .gitignore
├── README.md
├── gradlew
├── gpm_completion
└── LICENSE
/gradle.properties:
--------------------------------------------------------------------------------
1 | kotlin.code.style=official
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | rootProject.name = 'gpm'
2 |
3 |
--------------------------------------------------------------------------------
/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/theapache64/gpm/HEAD/icon.png
--------------------------------------------------------------------------------
/example.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/theapache64/gpm/HEAD/example.gif
--------------------------------------------------------------------------------
/META-INF/MANIFEST.MF:
--------------------------------------------------------------------------------
1 | Manifest-Version: 1.0
2 | Main-Class: com.theapache64.gpm.MainKt
3 |
4 |
--------------------------------------------------------------------------------
/extras/logo/gpmxcf.xcf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/theapache64/gpm/HEAD/extras/logo/gpmxcf.xcf
--------------------------------------------------------------------------------
/jdeploy-bundle/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/theapache64/gpm/HEAD/jdeploy-bundle/icon.png
--------------------------------------------------------------------------------
/src/main/kotlin/com/theapache64/gpm/utils/Logger.kt:
--------------------------------------------------------------------------------
1 | package com.theapache64.gpm.utils
2 |
3 |
--------------------------------------------------------------------------------
/extras/logo/gpm_original_logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/theapache64/gpm/HEAD/extras/logo/gpm_original_logo.png
--------------------------------------------------------------------------------
/extras/logo/npm_original_logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/theapache64/gpm/HEAD/extras/logo/npm_original_logo.png
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/theapache64/gpm/HEAD/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/src/test/kotlin/com/theapache64/gpm/core/gm/GradleDependencyTest.kt:
--------------------------------------------------------------------------------
1 | package com.theapache64.gpm.core.gm
2 |
3 | class GradleDependencyTest {
4 |
5 |
6 | }
--------------------------------------------------------------------------------
/src/main/kotlin/com/theapache64/gpm/utils/GpmConfig.kt:
--------------------------------------------------------------------------------
1 | package com.theapache64.gpm.utils
2 |
3 | object GpmConfig {
4 | // const val IS_DEBUG_MODE = true
5 | }
--------------------------------------------------------------------------------
/.idea/codeStyles/codeStyleConfig.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/.idea/compiler.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/src/main/kotlin/com/theapache64/gpm/commands/base/BaseViewModel.kt:
--------------------------------------------------------------------------------
1 | package com.theapache64.gpm.commands.base
2 |
3 | abstract class BaseViewModel {
4 | abstract suspend fun call(command: T): Int
5 | }
--------------------------------------------------------------------------------
/.idea/kotlinc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/.gitignore:
--------------------------------------------------------------------------------
1 | # Default ignored files
2 | /shelf/
3 | /workspace.xml
4 | # Datasource local storage ignored files
5 | /dataSources/
6 | /dataSources.local.xml
7 | # Editor-based HTTP Client requests
8 | /httpRequests/
9 |
--------------------------------------------------------------------------------
/src/main/kotlin/com/theapache64/gpm/data/remote/maven/models/ArtifactInfo.kt:
--------------------------------------------------------------------------------
1 | package com.theapache64.gpm.data.remote.maven.models
2 |
3 | class ArtifactInfo(
4 | val version: String,
5 | val repoName: String,
6 | val repoUrl: String
7 | )
--------------------------------------------------------------------------------
/src/main/kotlin/com/theapache64/gpm/commands/gpm/GpmComponent.kt:
--------------------------------------------------------------------------------
1 | package com.theapache64.gpm.commands.gpm
2 |
3 | import dagger.Component
4 | import javax.inject.Singleton
5 |
6 | @Singleton
7 | @Component
8 | interface GpmComponent {
9 | fun inject(gpm: Gpm)
10 | }
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Tue Apr 28 07:14:11 IST 2020
2 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-all.zip
3 | distributionBase=GRADLE_USER_HOME
4 | distributionPath=wrapper/dists
5 | zipStorePath=wrapper/dists
6 | zipStoreBase=GRADLE_USER_HOME
7 |
--------------------------------------------------------------------------------
/src/test/kotlin/com/theapache64/gpm/Utils.kt:
--------------------------------------------------------------------------------
1 | package com.theapache64.gpm
2 |
3 | import kotlinx.coroutines.CoroutineScope
4 | import kotlinx.coroutines.runBlocking
5 |
6 | fun runBlockingUnitTest(block: suspend (scope: CoroutineScope) -> Unit) = runBlocking {
7 | block(this)
8 | Unit
9 | }
--------------------------------------------------------------------------------
/registry/okhttp.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "OkHttp",
3 | "github": "square/okhttp",
4 | "docs": "https://square.github.io/okhttp/",
5 | "group_id": "com.squareup.okhttp3",
6 | "artifact_id": "okhttp",
7 | "get_from": "maven",
8 | "default_type": "implementation",
9 | "description": "For networking"
10 | }
--------------------------------------------------------------------------------
/src/main/kotlin/com/theapache64/gpm/di/Qualifiers.kt:
--------------------------------------------------------------------------------
1 | package com.theapache64.gpm.di
2 |
3 | import javax.inject.Qualifier
4 |
5 | @Qualifier
6 | @MustBeDocumented
7 | @Retention(AnnotationRetention.RUNTIME)
8 | annotation class GradleFile
9 |
10 | @Qualifier
11 | @MustBeDocumented
12 | @Retention(AnnotationRetention.RUNTIME)
13 | annotation class GpmJsonFile
--------------------------------------------------------------------------------
/src/main/kotlin/com/theapache64/gpm/commands/gpm/GpmViewModel.kt:
--------------------------------------------------------------------------------
1 | package com.theapache64.gpm.commands.gpm
2 |
3 | import com.theapache64.gpm.commands.base.BaseViewModel
4 | import javax.inject.Inject
5 |
6 | class GpmViewModel @Inject constructor() : BaseViewModel() {
7 | override suspend fun call(command: Gpm): Int {
8 | return 0
9 | }
10 | }
--------------------------------------------------------------------------------
/.idea/runConfigurations.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
9 |
10 |
--------------------------------------------------------------------------------
/src/main/kotlin/com/theapache64/gpm/data/remote/gpm/GpmApiInterface.kt:
--------------------------------------------------------------------------------
1 | package com.theapache64.gpm.data.remote.gpm
2 |
3 | import com.theapache64.gpm.data.remote.gpm.models.GpmDep
4 | import retrofit2.http.GET
5 | import retrofit2.http.Path
6 |
7 | interface GpmApiInterface {
8 |
9 | @GET("registry/{name}.json")
10 | suspend fun getDependency(
11 | @Path("name") repoName: String
12 | ): GpmDep?
13 |
14 | }
--------------------------------------------------------------------------------
/registry/navigation-material.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Navigation Material",
3 | "github": "google/accompanist",
4 | "docs": "https://google.github.io/accompanist/navigation-material/",
5 | "group_id": "com.google.accompanist",
6 | "artifact_id": "accompanist-navigation-material",
7 | "get_from": "maven",
8 | "default_type": "implementation",
9 | "description": "Compose Material support for Jetpack Navigation Compose"
10 | }
--------------------------------------------------------------------------------
/.idea/codeStyles/Project.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/src/main/kotlin/com/theapache64/gpm/di/modules/CommandModule.kt:
--------------------------------------------------------------------------------
1 | package com.theapache64.gpm.di.modules
2 |
3 | import com.theapache64.gpm.commands.gpm.Gpm
4 | import dagger.Module
5 | import dagger.Provides
6 | import javax.inject.Singleton
7 |
8 | @Module
9 | class CommandModule(private val isFromTest: Boolean) {
10 |
11 | @Singleton
12 | @Provides
13 | fun provideGpm(): Gpm {
14 | return Gpm(isFromTest)
15 | }
16 | }
--------------------------------------------------------------------------------
/src/main/kotlin/com/theapache64/gpm/Main.kt:
--------------------------------------------------------------------------------
1 | package com.theapache64.gpm
2 |
3 | import com.theapache64.gpm.commands.gpm.Gpm
4 | import picocli.CommandLine
5 | import kotlin.system.exitProcess
6 |
7 | fun main(args: Array) {
8 |
9 | val cmd = CommandLine(Gpm(false))
10 | if (args.isEmpty()) {
11 | cmd.usage(System.out)
12 | } else {
13 | val exitCode = cmd.execute(*args)
14 | exitProcess(exitCode)
15 | }
16 | }
--------------------------------------------------------------------------------
/src/main/kotlin/com/theapache64/gpm/core/gm/GradleDep.kt:
--------------------------------------------------------------------------------
1 | package com.theapache64.gpm.core.gm
2 |
3 | data class GradleDep(
4 | val type: Type,
5 | val groupId: String,
6 | val artifactId: String,
7 | val version: String
8 | ) {
9 | enum class Type(val key: String) {
10 | IMP("implementation"),
11 | TEST_IMP("testImplementation"),
12 | AND_TEST_IMP("androidTestImplementation"),
13 | KAPT("kapt")
14 | }
15 | }
--------------------------------------------------------------------------------
/src/main/kotlin/com/theapache64/gpm/utils/StringExtensions.kt:
--------------------------------------------------------------------------------
1 | package com.theapache64.gpm.utils
2 |
3 | fun String.removeNewLinesAndMultipleSpaces(): String {
4 | return this.replace("\n", "")
5 | .replace("\r", "")
6 | .replace("\\s{2,}".toRegex(), " ")
7 | }
8 |
9 | fun String.insertAt(position: Int, text: String): String {
10 | return this.substring(0, position) + text + this.substring(
11 | position,
12 | this.length
13 | )
14 | }
--------------------------------------------------------------------------------
/src/main/kotlin/com/theapache64/gpm/data/remote/maven/models/SearchResult.kt:
--------------------------------------------------------------------------------
1 | package com.theapache64.gpm.data.remote.maven.models
2 |
3 | import java.util.*
4 |
5 | data class SearchResult(
6 | val id: Int,
7 | val name: String,
8 | val groupId: String,
9 | val artifactId: String,
10 | val description: String,
11 | val usage: Int?,
12 | val lastRelease: Date
13 | ) {
14 | val url = "https://mvnrepository.com/artifact/$groupId/$artifactId"
15 | }
16 |
17 |
--------------------------------------------------------------------------------
/src/main/kotlin/com/theapache64/gpm/models/GpmFileData.kt:
--------------------------------------------------------------------------------
1 | package com.theapache64.gpm.models
2 |
3 | import com.squareup.moshi.Json
4 | import com.theapache64.gpm.data.remote.gpm.models.GpmDep
5 |
6 | class GpmFileData(
7 | @Json(name = "added")
8 | val deps: MutableList
9 | ) {
10 | class AddedDep(
11 | @Json(name = "id")
12 | val id: Int,
13 | @Json(name = "type")
14 | val type: String,
15 | @Json(name = "installed_name")
16 | val installedName: String,
17 | @Json(name = "gpm_dep")
18 | val gpmDep: GpmDep
19 | )
20 | }
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/src/main/kotlin/com/theapache64/gpm/commands/subcommands/docs/DocsComponent.kt:
--------------------------------------------------------------------------------
1 | package com.theapache64.gpm.commands.subcommands.docs
2 |
3 | import com.theapache64.gpm.core.TransactionManager
4 | import com.theapache64.gpm.di.GpmJsonFile
5 | import com.theapache64.gpm.di.modules.NetworkModule
6 | import com.theapache64.gpm.di.modules.TransactionModule
7 | import dagger.Component
8 | import java.io.File
9 | import javax.inject.Singleton
10 |
11 | @Singleton
12 | @Component(modules = [TransactionModule::class, NetworkModule::class])
13 | interface DocsComponent {
14 |
15 | fun transactionManager(): TransactionManager
16 | fun inject(docs: Docs)
17 |
18 | @GpmJsonFile
19 | fun gpmJsonFile(): File
20 | }
--------------------------------------------------------------------------------
/install.sh:
--------------------------------------------------------------------------------
1 | echo "Downloading main JAR..." &&
2 | wget -q "https://github.com/theapache64/gpm/releases/latest/download/gpm.main.jar" -O "gpm.main.jar" --show-progress &&
3 | # cp /home/theapache64/Documents/projects/gpm/gpm.main.jar gpm.main.jar &&
4 |
5 | echo "Downloading autocompletion script..." &&
6 | wget -q "https://github.com/theapache64/gpm/releases/latest/download/gpm_completion" -O "gpm_completion" --show-progress &&
7 |
8 | echo "Moving files to ~/.gmp" &&
9 |
10 | mkdir -p ~/.gpm &&
11 | mv gpm.main.jar ~/.gpm/gpm.main.jar &&
12 | mv gpm_completion ~/.gpm/gpm_completion &&
13 |
14 | echo "Installing..." &&
15 | echo "alias gpm='java -jar ~/.gpm/gpm.main.jar'" >> ~/.bashrc &&
16 | echo ". ~/.gpm/gpm_completion" >> ~/.bashrc &&
17 |
18 | echo "Done"
19 |
--------------------------------------------------------------------------------
/src/main/kotlin/com/theapache64/gpm/di/modules/MoshiModule.kt:
--------------------------------------------------------------------------------
1 | package com.theapache64.gpm.di.modules
2 |
3 | import com.squareup.moshi.Moshi
4 | import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory
5 | import dagger.Module
6 | import dagger.Provides
7 | import javax.inject.Singleton
8 |
9 | @Module
10 | class MoshiModule {
11 |
12 | @Singleton
13 | @Provides
14 | fun provideKotlinJsonAdapterFactory(): KotlinJsonAdapterFactory {
15 | return KotlinJsonAdapterFactory()
16 | }
17 |
18 |
19 | @Singleton
20 | @Provides
21 | fun provideMoshi(kotlinJsonAdapter: KotlinJsonAdapterFactory): Moshi {
22 | return Moshi.Builder()
23 | .add(kotlinJsonAdapter)
24 | .build()
25 | }
26 |
27 |
28 | }
--------------------------------------------------------------------------------
/src/main/kotlin/com/theapache64/gpm/utils/GradleUtils.kt:
--------------------------------------------------------------------------------
1 | package com.theapache64.gpm.utils
2 |
3 | object GradleUtils {
4 | fun getFullSignature(
5 | typeKeyword: String,
6 | groupId: String,
7 | artifactId: String,
8 | version: String,
9 | isGradleKts: Boolean
10 | ): String {
11 |
12 | val quote = if (isGradleKts || version.startsWith("$")) {
13 | "\""
14 | } else {
15 | "'"
16 | }
17 |
18 | return if (isGradleKts) {
19 | // kotlin script
20 | "$typeKeyword($quote$groupId:$artifactId:$version$quote)"
21 | } else {
22 | // normal gradle
23 | "$typeKeyword $quote$groupId:$artifactId:$version$quote"
24 | }
25 | }
26 | }
--------------------------------------------------------------------------------
/src/main/kotlin/com/theapache64/gpm/commands/base/BaseCommand.kt:
--------------------------------------------------------------------------------
1 | package com.theapache64.gpm.commands.base
2 |
3 | import com.theapache64.gpm.utils.InputUtils
4 | import kotlinx.coroutines.delay
5 | import java.util.concurrent.Callable
6 |
7 | abstract class BaseCommand(
8 | val isFromTest: Boolean
9 | ) : Callable {
10 |
11 | suspend fun chooseIndex(items: List): Int {
12 |
13 | println("Choose: ")
14 |
15 | items.forEachIndexed() { index: Int, string: String ->
16 | println("${index + 1}) $string")
17 | }
18 |
19 | @Suppress("ConstantConditionIf")
20 | return if (isFromTest) {
21 | delay(1000)
22 | 0
23 | } else {
24 | InputUtils.getInt("Choose #", 1, items.size) - 1
25 | }
26 | }
27 | }
--------------------------------------------------------------------------------
/.github/workflows/gradle.yml:
--------------------------------------------------------------------------------
1 | # This workflow will build a Java project with Gradle
2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-gradle
3 |
4 | name: Java CI with Gradle
5 |
6 | on:
7 | push:
8 | branches: [ master ]
9 | pull_request:
10 | branches: [ master ]
11 |
12 | jobs:
13 | build:
14 |
15 | runs-on: ubuntu-latest
16 |
17 | steps:
18 | - uses: actions/checkout@v2
19 | - name: Set up JDK 1.8
20 | uses: actions/setup-java@v1
21 | with:
22 | java-version: 1.8
23 | - name: Grant execute permission for gradlew
24 | run: chmod +x gradlew
25 | - name: Clean Gradle
26 | run: ./gradlew clean
27 | - name: Build with Gradle
28 | run: ./gradlew build
29 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "bin": {
3 | "gpm": "jdeploy-bundle/jdeploy.js"
4 | },
5 | "author": "theapache64",
6 | "description": "GPM is a package manager for Gradle projects",
7 | "main": "index.js",
8 | "preferGlobal": "true",
9 | "repository": "https://github.com/theapache64/gpm",
10 | "version": "1.0.7",
11 | "jdeploy": {
12 | "jdk": "false",
13 | "javaVersion": "17",
14 | "jar": "gpm.main.jar",
15 | "javafx": "false",
16 | "title": "GPM"
17 | },
18 | "dependencies": {
19 | "command-exists-promise": "2.0.2",
20 | "node-fetch": "2.6.7",
21 | "tar": "4.4.8",
22 | "yauzl": "2.10.0",
23 | "shelljs": "0.8.4"
24 | },
25 | "license": "ISC",
26 | "name": "gpm-cli",
27 | "files": [
28 | "jdeploy-bundle"
29 | ],
30 | "scripts": {
31 | "test": "echo \"Error: no test specified\" && exit 1"
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/src/main/kotlin/com/theapache64/gpm/data/remote/gpm/models/GpmDep.kt:
--------------------------------------------------------------------------------
1 | package com.theapache64.gpm.data.remote.gpm.models
2 |
3 | import com.squareup.moshi.Json
4 |
5 | data class GpmDep(
6 | @Json(name = "artifact_id")
7 | val artifactId: String, // okhttp
8 | @Json(name = "default_type")
9 | val defaultType: String?, // implementation
10 | @Json(name = "docs")
11 | val docs: String, // https://square.github.io/okhttp/
12 | @Json(name = "get_from")
13 | val getFrom: String, // maven
14 | @Json(name = "github")
15 | val github: String?, // square/okhttp
16 | @Json(name = "group_id")
17 | val groupId: String, // com.squareup.okhttp3
18 | @Json(name = "name")
19 | val name: String,
20 | @Json(name = "description")
21 | val description: String,
22 | @Transient
23 | var version: String? = null // OkHttp
24 | ) {
25 |
26 | }
--------------------------------------------------------------------------------
/src/main/kotlin/com/theapache64/gpm/di/modules/TransactionModule.kt:
--------------------------------------------------------------------------------
1 | package com.theapache64.gpm.di.modules
2 |
3 | import com.squareup.moshi.Moshi
4 | import com.theapache64.gpm.core.TransactionManager
5 | import com.theapache64.gpm.di.GpmJsonFile
6 | import dagger.Module
7 | import dagger.Provides
8 | import java.io.File
9 |
10 | @Module(includes = [MoshiModule::class])
11 | class TransactionModule(private val isFromTest: Boolean) {
12 |
13 | @Provides
14 | @GpmJsonFile
15 | fun provideGpmJsonFile(): File {
16 | return if (isFromTest) {
17 | File("src/test/resources/temp.gpm.json")
18 | } else {
19 | File("gpm.json")
20 | }
21 | }
22 |
23 | @Provides
24 | fun provideTransactionManager(@GpmJsonFile gpmJsonFile: File, moshi: Moshi): TransactionManager {
25 | return TransactionManager(gpmJsonFile, moshi)
26 | }
27 | }
--------------------------------------------------------------------------------
/src/test/kotlin/com/theapache64/gpm/rules/MyDaggerMockRule.kt:
--------------------------------------------------------------------------------
1 | package com.theapache64.gpm.rules
2 |
3 | import com.theapache64.gpm.commands.subcommands.install.DaggerInstallComponent
4 | import com.theapache64.gpm.commands.subcommands.install.InstallComponent
5 | import com.theapache64.gpm.di.modules.CommandModule
6 |
7 | import com.theapache64.gpm.di.modules.GradleModule
8 | import com.theapache64.gpm.di.modules.NetworkModule
9 | import com.theapache64.gpm.di.modules.TransactionModule
10 | import it.cosenonjaviste.daggermock.DaggerMockRule
11 |
12 | class MyDaggerMockRule : DaggerMockRule(
13 | InstallComponent::class.java,
14 | NetworkModule(),
15 | GradleModule(isFromTest = true, null)
16 | ) {
17 | init {
18 | customizeBuilder {
19 | it.transactionModule(TransactionModule(true))
20 | .commandModule(CommandModule(true))
21 | }
22 | }
23 | }
--------------------------------------------------------------------------------
/src/main/kotlin/com/theapache64/gpm/commands/subcommands/uninstall/UninstallComponent.kt:
--------------------------------------------------------------------------------
1 | package com.theapache64.gpm.commands.subcommands.uninstall
2 |
3 | import com.theapache64.gpm.core.TransactionManager
4 | import com.theapache64.gpm.core.gm.GradleManager
5 | import com.theapache64.gpm.di.GpmJsonFile
6 | import com.theapache64.gpm.di.GradleFile
7 | import com.theapache64.gpm.di.modules.GradleModule
8 | import com.theapache64.gpm.di.modules.TransactionModule
9 | import dagger.Component
10 | import java.io.File
11 | import javax.inject.Singleton
12 |
13 | @Singleton
14 | @Component(
15 | modules = [GradleModule::class, TransactionModule::class]
16 | )
17 | interface UninstallComponent {
18 | fun inject(uninstall: Uninstall)
19 |
20 | @GradleFile
21 | fun gradleFile(): File
22 |
23 | @GpmJsonFile
24 | fun gpmJsonFile(): File
25 | fun transactionManager(): TransactionManager
26 | fun gradleManager(): GradleManager
27 |
28 | }
--------------------------------------------------------------------------------
/src/main/kotlin/com/theapache64/gpm/data/remote/maven/MavenApiInterface.kt:
--------------------------------------------------------------------------------
1 | package com.theapache64.gpm.data.remote.maven
2 |
3 | import retrofit2.http.GET
4 | import retrofit2.http.Header
5 | import retrofit2.http.Path
6 | import retrofit2.http.Query
7 |
8 | interface MavenApiInterface {
9 |
10 | @GET("search")
11 | suspend fun search(
12 | @Query("q") query: String,
13 | @Header("User-Agent") userAgent : String = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36"
14 | ): String
15 |
16 | @GET("artifact/{groupId}/{artifactId}")
17 | suspend fun getArtifact(
18 | @Path("groupId") groupId: String,
19 | @Path("artifactId") artifactId: String,
20 | @Header("User-Agent") userAgent : String = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36"
21 | ): String
22 |
23 | }
--------------------------------------------------------------------------------
/src/test/kotlin/com/theapache64/gpm/rules/MainCoroutineRule.kt:
--------------------------------------------------------------------------------
1 | package com.theapache64.gpm.rules
2 |
3 | import kotlinx.coroutines.Dispatchers
4 | import kotlinx.coroutines.ExperimentalCoroutinesApi
5 | import kotlinx.coroutines.test.TestCoroutineDispatcher
6 | import kotlinx.coroutines.test.TestCoroutineScope
7 | import kotlinx.coroutines.test.resetMain
8 | import kotlinx.coroutines.test.setMain
9 | import org.junit.rules.TestWatcher
10 | import org.junit.runner.Description
11 |
12 | @Suppress("unused")
13 | @ExperimentalCoroutinesApi
14 | class MainCoroutineRule(
15 | @Suppress("MemberVisibilityCanBePrivate")
16 | val dispatcher: TestCoroutineDispatcher = TestCoroutineDispatcher()
17 | ) :
18 | TestWatcher(),
19 | TestCoroutineScope by TestCoroutineScope(dispatcher) {
20 |
21 | override fun starting(description: Description?) {
22 | super.starting(description)
23 | Dispatchers.setMain(dispatcher)
24 | }
25 |
26 | override fun finished(description: Description?) {
27 | super.finished(description)
28 | Dispatchers.resetMain()
29 | }
30 |
31 | }
--------------------------------------------------------------------------------
/src/main/kotlin/com/theapache64/gpm/data/repos/GpmRepo.kt:
--------------------------------------------------------------------------------
1 | package com.theapache64.gpm.data.repos
2 |
3 | import com.theapache64.gpm.data.remote.gpm.GpmApiInterface
4 | import com.theapache64.gpm.data.remote.gpm.models.GpmDep
5 | import javax.inject.Inject
6 | import javax.inject.Singleton
7 |
8 | @Singleton
9 | class GpmRepo @Inject constructor(
10 | private val gpmApiInterface: GpmApiInterface,
11 | private val mavenRepo: MavenRepo
12 | ) {
13 |
14 | /**
15 | * To get dependency from GPM github registry
16 | */
17 | suspend fun getDep(depName: String): GpmDep? {
18 | return try {
19 | val dep = gpmApiInterface.getDependency(depName)
20 | if (dep == null) {
21 | null
22 | } else {
23 | val versionInfo = mavenRepo.getLatestVersion(dep.groupId, dep.artifactId)
24 | require(versionInfo != null) { "Couldn't get version info for '$depName'" }
25 | dep.version = versionInfo.version
26 | dep
27 | }
28 | } catch (e: Exception) {
29 | null
30 | }
31 | }
32 |
33 | }
--------------------------------------------------------------------------------
/src/main/kotlin/com/theapache64/gpm/commands/subcommands/docs/Docs.kt:
--------------------------------------------------------------------------------
1 | package com.theapache64.gpm.commands.subcommands.docs
2 |
3 | import com.theapache64.gpm.commands.base.BaseCommand
4 | import com.theapache64.gpm.di.modules.TransactionModule
5 | import kotlinx.coroutines.runBlocking
6 | import picocli.CommandLine
7 | import javax.inject.Inject
8 |
9 | @CommandLine.Command(
10 | name = "docs",
11 | aliases = ["d"],
12 | description = ["To open library docs"]
13 | )
14 | class Docs(
15 | isFromTest: Boolean = false
16 | ) : BaseCommand(isFromTest) {
17 | @CommandLine.Parameters(index = "0", description = ["Dependency name"])
18 | lateinit var depName: String
19 |
20 | @Inject
21 | lateinit var docsViewModel: DocsViewModel
22 |
23 | init {
24 | DaggerDocsComponent.builder()
25 | .transactionModule(TransactionModule(isFromTest))
26 | .build()
27 | .inject(this)
28 | }
29 |
30 | override fun call(): Int = runBlocking {
31 | docsViewModel.call(this@Docs)
32 | }
33 |
34 | fun onDocUrl(docsUrl: String) {
35 | println("Docs -> $docsUrl")
36 | }
37 | }
--------------------------------------------------------------------------------
/src/main/kotlin/com/theapache64/gpm/commands/gpm/Gpm.kt:
--------------------------------------------------------------------------------
1 | package com.theapache64.gpm.commands.gpm
2 |
3 | import com.theapache64.gpm.commands.base.BaseCommand
4 | import com.theapache64.gpm.commands.subcommands.docs.Docs
5 | import com.theapache64.gpm.commands.subcommands.install.Install
6 | import com.theapache64.gpm.commands.subcommands.uninstall.Uninstall
7 | import kotlinx.coroutines.runBlocking
8 | import picocli.CommandLine
9 | import javax.inject.Inject
10 | import javax.inject.Singleton
11 |
12 | @CommandLine.Command(
13 | name = "gpm",
14 | version = ["v1.0.7"],
15 | mixinStandardHelpOptions = true,
16 | subcommands = [
17 | Install::class,
18 | Uninstall::class,
19 | Docs::class
20 | ]
21 | )
22 | @Singleton
23 | class Gpm constructor(isFromTest: Boolean = false) : BaseCommand(isFromTest) {
24 |
25 |
26 | init {
27 | DaggerGpmComponent.create().inject(this)
28 | }
29 |
30 | @CommandLine.Parameters(
31 | index = "0",
32 | description = ["Module path or directory, separated and started by ':'. eg: :app , :feature:login"],
33 | defaultValue = ""
34 | )
35 | var modulePath: String? = null
36 |
37 | @Inject
38 | lateinit var viewModel: GpmViewModel
39 |
40 | override fun call(): Int = runBlocking {
41 | viewModel.call(this@Gpm)
42 | }
43 | }
--------------------------------------------------------------------------------
/.idea/jarRepositories.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/src/main/kotlin/com/theapache64/gpm/commands/base/BaseInstallUninstallViewModel.kt:
--------------------------------------------------------------------------------
1 | package com.theapache64.gpm.commands.base
2 |
3 | import com.theapache64.gpm.core.gm.GradleDep
4 |
5 | abstract class BaseInstallUninstallViewModel : BaseViewModel() {
6 |
7 | fun getDepTypes(
8 | isSave: Boolean,
9 | isSaveDev: Boolean,
10 | isSaveDevAndroid: Boolean,
11 | isKapt: Boolean,
12 | defaultType: String?
13 | ): List = mutableListOf().apply {
14 |
15 | if (isSave) {
16 | add(GradleDep.Type.IMP)
17 | }
18 |
19 | if (isSaveDev) {
20 | add(GradleDep.Type.TEST_IMP)
21 | }
22 |
23 | if (isSaveDevAndroid) {
24 | add(GradleDep.Type.AND_TEST_IMP)
25 | }
26 |
27 | if (isKapt) {
28 | add(GradleDep.Type.KAPT)
29 | }
30 |
31 | // Still empty
32 | if (isEmpty() && !defaultType.isNullOrBlank()) {
33 | // setting default dependency
34 | val depType = GradleDep.Type.values().find { it.key == defaultType.trim() }
35 | ?: throw IllegalArgumentException("Invalid default type '${defaultType}'")
36 |
37 | add(depType)
38 | }
39 |
40 | // Still Empty?
41 | if (isEmpty()) {
42 | // adding default
43 | add(GradleDep.Type.IMP)
44 | }
45 | }
46 |
47 | }
--------------------------------------------------------------------------------
/src/main/kotlin/com/theapache64/gpm/utils/InputUtils.kt:
--------------------------------------------------------------------------------
1 | package com.theapache64.gpm.utils
2 |
3 | import java.util.*
4 |
5 | /**
6 | * Operations related to reading text input from console
7 | */
8 | object InputUtils {
9 |
10 | private val scanner by lazy { Scanner(System.`in`) }
11 |
12 | /**
13 | * Get a String with given prompt as prompt
14 | */
15 | fun getString(prompt: String, isRequired: Boolean): String {
16 | print("$prompt: ")
17 | val value = scanner.nextLine()
18 | while (value.trim().isEmpty() && isRequired) {
19 | println("Invalid ${prompt.toLowerCase()} `$value`")
20 | return getString(prompt, isRequired)
21 | }
22 | return value
23 | }
24 |
25 | fun getInt(prompt: String, lowerBound: Int, upperBound: Int, but: Array = arrayOf()): Int {
26 | print("$prompt :")
27 |
28 | val sVal = scanner.nextLine()
29 | try {
30 | val value = sVal.toInt()
31 | if (!but.contains(value) && (value < lowerBound || value > upperBound)) {
32 | // error
33 | println("Input must between $lowerBound and $upperBound")
34 | return getInt(prompt, lowerBound, upperBound)
35 | }
36 | return value
37 | } catch (e: NumberFormatException) {
38 | println("Invalid input `$sVal`")
39 | return getInt(prompt, lowerBound, upperBound)
40 | }
41 | }
42 |
43 | }
--------------------------------------------------------------------------------
/src/main/kotlin/com/theapache64/gpm/commands/subcommands/install/InstallComponent.kt:
--------------------------------------------------------------------------------
1 | package com.theapache64.gpm.commands.subcommands.install
2 |
3 | import com.squareup.moshi.Moshi
4 | import com.theapache64.gpm.commands.gpm.Gpm
5 | import com.theapache64.gpm.core.TransactionManager
6 | import com.theapache64.gpm.data.remote.gpm.GpmApiInterface
7 | import com.theapache64.gpm.data.remote.maven.MavenApiInterface
8 | import com.theapache64.gpm.data.repos.MavenRepo
9 | import com.theapache64.gpm.di.GpmJsonFile
10 | import com.theapache64.gpm.di.GradleFile
11 | import com.theapache64.gpm.di.modules.CommandModule
12 | import com.theapache64.gpm.di.modules.GradleModule
13 | import com.theapache64.gpm.di.modules.MoshiModule
14 | import com.theapache64.gpm.di.modules.NetworkModule
15 | import dagger.Component
16 | import java.io.File
17 | import javax.inject.Singleton
18 |
19 | @Singleton
20 | @Component(
21 | modules = [
22 | NetworkModule::class,
23 | MoshiModule::class,
24 | GradleModule::class,
25 | CommandModule::class
26 | ]
27 | )
28 | interface InstallComponent {
29 | /*
30 | * For testing
31 | */
32 | fun getGpm(): Gpm
33 | fun gpmApiInterface(): GpmApiInterface
34 | fun mavenApiInterface(): MavenApiInterface
35 |
36 | @GradleFile
37 | fun gradleFile(): File
38 |
39 | @GpmJsonFile
40 | fun gpmJsonFile(): File
41 |
42 | fun mavenRepo(): MavenRepo
43 | fun gpmFileManager(): TransactionManager
44 |
45 | fun inject(install: Install)
46 | fun moshi(): Moshi
47 | }
--------------------------------------------------------------------------------
/src/main/kotlin/com/theapache64/gpm/utils/StringUtils.kt:
--------------------------------------------------------------------------------
1 | package com.theapache64.gpm.utils
2 |
3 |
4 | object StringUtils {
5 |
6 | fun getClosingIndexOf(text: String, openingChar: Char, openingIndex: Int, closingChar: Char): Int {
7 | var closePos: Int = openingIndex
8 | var counter = 1
9 | while (counter > 0) {
10 | val c: Char = text[++closePos]
11 | if (c == openingChar) {
12 | counter++
13 | } else if (c == closingChar) {
14 | counter--
15 | }
16 | }
17 | return closePos
18 | }
19 |
20 | fun breakOnAndComment(charLimit: Int, input: String): String {
21 | if (input.length < charLimit) {
22 | return input
23 | }
24 |
25 | return getChunked(input, charLimit).joinToString("\n// ")
26 | }
27 |
28 | private fun getChunked(input: String, charLimit: Int): List {
29 | val chunks = mutableListOf()
30 | val words = input.split(" ")
31 | val builder = StringBuilder()
32 | for (word in words) {
33 | builder.append(word).append(" ")
34 | if (builder.length >= charLimit) {
35 | // move to chunk
36 | val newLine = builder.toString().trim()
37 | chunks.add(newLine)
38 | builder.clear()
39 | }
40 | }
41 | if (builder.isNotEmpty()) {
42 | chunks.add(builder.toString().trim())
43 | }
44 | return chunks
45 | }
46 |
47 | }
--------------------------------------------------------------------------------
/src/main/kotlin/com/theapache64/gpm/commands/subcommands/docs/DocsViewModel.kt:
--------------------------------------------------------------------------------
1 | package com.theapache64.gpm.commands.subcommands.docs
2 |
3 | import com.theapache64.gpm.commands.base.BaseViewModel
4 | import com.theapache64.gpm.core.TransactionManager
5 | import com.theapache64.gpm.data.repos.GpmRepo
6 | import javax.inject.Inject
7 |
8 | class DocsViewModel @Inject constructor(
9 | private val tm: TransactionManager,
10 | private val gpmRepo: GpmRepo
11 | ) : BaseViewModel() {
12 |
13 | companion object {
14 | const val RESULT_DOC_FOUND = 123
15 | const val RESULT_NOT_FOUND = 404
16 | }
17 |
18 | override suspend fun call(command: Docs): Int {
19 |
20 | val depName = command.depName.trim().toLowerCase()
21 | val transaction = tm.getInstalled(null, depName)
22 |
23 | if (transaction.isEmpty()) {
24 | // check if remote registry got info about the repo in question
25 | val remoteDep = gpmRepo.getDep(depName) ?: return RESULT_NOT_FOUND
26 |
27 | command.onDocUrl(remoteDep.docs)
28 | return RESULT_DOC_FOUND
29 |
30 | } else {
31 |
32 | val index = if (transaction.size > 1) {
33 | command.chooseIndex(transaction.map {
34 | "${it.type} ${it.gpmDep.groupId}:${it.gpmDep.artifactId}"
35 | })
36 | } else {
37 | 0
38 | }
39 |
40 | val selDep = transaction[index]
41 | command.onDocUrl(selDep.gpmDep.docs)
42 |
43 | return RESULT_DOC_FOUND
44 | }
45 | }
46 |
47 | }
--------------------------------------------------------------------------------
/src/test/kotlin/com/theapache64/gpm/data/repos/GpmRepoTest.kt:
--------------------------------------------------------------------------------
1 | package com.theapache64.gpm.data.repos
2 |
3 | import com.theapache64.gpm.data.remote.gpm.GpmApiInterface
4 | import com.theapache64.gpm.rules.MyDaggerMockRule
5 | import com.theapache64.gpm.runBlockingUnitTest
6 | import com.winterbe.expekt.should
7 | import it.cosenonjaviste.daggermock.InjectFromComponent
8 | import kotlinx.coroutines.ExperimentalCoroutinesApi
9 | import org.junit.Before
10 | import org.junit.Rule
11 | import org.junit.Test
12 |
13 | @ExperimentalCoroutinesApi
14 | class GpmRepoTest {
15 |
16 | @get:Rule
17 | val daggerMockRule = MyDaggerMockRule()
18 |
19 | @InjectFromComponent
20 | private lateinit var gpmApiInterface: GpmApiInterface
21 |
22 | @InjectFromComponent
23 | private lateinit var mavenRepo: MavenRepo
24 |
25 | private lateinit var gmpRepo: GpmRepo
26 |
27 | @Before
28 | fun setUp() {
29 | gmpRepo = GpmRepo(gpmApiInterface, mavenRepo)
30 | }
31 |
32 |
33 | @Test
34 | fun `Valid search`() = runBlockingUnitTest {
35 | val dep = gmpRepo.getDep("okhttp")
36 | dep.should.not.`null`
37 | dep!!.name.should.equal("OkHttp")
38 | dep.github.should.equal("square/okhttp")
39 | dep.docs.should.equal("https://square.github.io/okhttp/")
40 | dep.groupId.should.equal("com.squareup.okhttp3")
41 | dep.artifactId.should.equal("okhttp")
42 | dep.getFrom.should.equal("maven")
43 | dep.defaultType.should.equal("implementation")
44 | }
45 |
46 | @Test
47 | fun `Invalid search`() = runBlockingUnitTest {
48 | gmpRepo.getDep("fghdfghfgh").should.`null`
49 | }
50 |
51 |
52 | }
--------------------------------------------------------------------------------
/src/test/kotlin/com/theapache64/gpm/utils/StringUtilsTest.kt:
--------------------------------------------------------------------------------
1 | package com.theapache64.gpm.utils
2 |
3 | import com.winterbe.expekt.should
4 | import org.junit.Assert.assertEquals
5 | import org.junit.Test
6 |
7 | class StringUtilsTest {
8 |
9 | @Test
10 | fun `Test nested braces`() {
11 | val input = """
12 | {
13 | x
14 | {
15 | y
16 | }
17 | }
18 | """.trimIndent()
19 |
20 | // open = 13, close = 29
21 | val endingIndex = StringUtils.getClosingIndexOf(input, '{', 13, '}')
22 | endingIndex.should.equal(29)
23 | }
24 |
25 |
26 | @Test
27 | fun `Comment break - single-line`() {
28 | val input = """
29 | This library allows you to use your PC webcam.
30 | """.trimIndent()
31 | val expectedOutput = """
32 | This library allows you to use your PC webcam.
33 | """.trimIndent()
34 | val actualOutput = StringUtils.breakOnAndComment(80, input)
35 | assertEquals(actualOutput, expectedOutput)
36 | }
37 |
38 | @Test
39 | fun `Comment break - multiline`() {
40 | val input = """
41 | This library allows you to use your PC webcam, IP or network cameras directly from Java. It's compatible with most operating systems (Windows, Linux, MacOS).
42 | """.trimIndent()
43 | val expectedOutput = """
44 | This library allows you to use your PC webcam, IP or network cameras directly from
45 | // Java. It's compatible with most operating systems (Windows, Linux, MacOS).
46 | """.trimIndent()
47 | val actualOutput = StringUtils.breakOnAndComment(80, input)
48 | assertEquals(actualOutput, expectedOutput)
49 | }
50 | }
--------------------------------------------------------------------------------
/src/main/kotlin/com/theapache64/gpm/di/modules/NetworkModule.kt:
--------------------------------------------------------------------------------
1 | package com.theapache64.gpm.di.modules
2 |
3 | import com.moczul.ok2curl.CurlInterceptor
4 | import com.moczul.ok2curl.logger.Logger
5 | import com.squareup.moshi.Moshi
6 | import com.theapache64.gpm.data.remote.gpm.GpmApiInterface
7 | import com.theapache64.gpm.data.remote.maven.MavenApiInterface
8 | import dagger.Module
9 | import dagger.Provides
10 | import okhttp3.OkHttpClient
11 | import retrofit2.Retrofit
12 | import retrofit2.converter.moshi.MoshiConverterFactory
13 | import retrofit2.converter.scalars.ScalarsConverterFactory
14 | import javax.inject.Singleton
15 |
16 | @Module(includes = [MoshiModule::class])
17 | class NetworkModule {
18 |
19 | @Provides
20 | fun provideRetrofit(): Retrofit.Builder {
21 | val okHttp = OkHttpClient.Builder()
22 | /*.addInterceptor(
23 | CurlInterceptor(object : Logger {
24 | override fun log(message: String) {
25 | println("QuickTag: NetworkModule:log: `$message`")
26 | }
27 | })
28 | )*/
29 | .build()
30 |
31 | return Retrofit.Builder()
32 | .client(okHttp)
33 | }
34 |
35 | @Singleton
36 | @Provides
37 | fun provideGpmApiInterface(retrofitBuilder: Retrofit.Builder, moshi: Moshi): GpmApiInterface {
38 | return retrofitBuilder
39 | .baseUrl("https://raw.githubusercontent.com/theapache64/gpm/master/")
40 | .addConverterFactory(MoshiConverterFactory.create(moshi))
41 | .build()
42 | .create(GpmApiInterface::class.java)
43 | }
44 |
45 | @Singleton
46 | @Provides
47 | fun provideMavenApiInterface(retrofitBuilder: Retrofit.Builder): MavenApiInterface {
48 | return retrofitBuilder
49 | .baseUrl("https://mvnrepository.com/")
50 | .addConverterFactory(ScalarsConverterFactory.create())
51 | .build()
52 | .create(MavenApiInterface::class.java)
53 | }
54 | }
--------------------------------------------------------------------------------
/src/main/kotlin/com/theapache64/gpm/commands/subcommands/uninstall/UninstallViewModel.kt:
--------------------------------------------------------------------------------
1 | package com.theapache64.gpm.commands.subcommands.uninstall
2 |
3 | import com.theapache64.gpm.commands.base.BaseInstallUninstallViewModel
4 | import com.theapache64.gpm.core.TransactionManager
5 | import com.theapache64.gpm.core.gm.GradleDep
6 | import com.theapache64.gpm.core.gm.GradleManager
7 | import javax.inject.Inject
8 |
9 | class UninstallViewModel @Inject constructor(
10 | private val tm: TransactionManager,
11 | private val gradleManager: GradleManager
12 | ) : BaseInstallUninstallViewModel() {
13 |
14 | companion object {
15 | const val RESULT_DEP_UNINSTALLED = 123
16 | const val RESULT_NO_DEP_INSTALLED = 134
17 | }
18 |
19 | override suspend fun call(command: Uninstall): Int {
20 |
21 | val depName = command.depName.trim().toLowerCase()
22 |
23 | val depTypes = getDepTypes(
24 | command.isSave,
25 | command.isSaveDev,
26 | command.isSaveDevAndroid,
27 | command.isKapt,
28 | GradleDep.Type.IMP.key
29 | )
30 |
31 | for (depType in depTypes) {
32 |
33 |
34 | val installedDeps = tm.getInstalled(depType.key, depName)
35 |
36 | if (installedDeps.isEmpty()) {
37 | command.onNoDepInstalled(depType, depName)
38 | return RESULT_NO_DEP_INSTALLED
39 | }
40 |
41 | val depToRemove = if (installedDeps.size > 1) {
42 | // multiple, choose one
43 | val selDepIndex = command.chooseIndex(installedDeps.map {
44 | "${it.type} ${it.gpmDep.groupId}:${it.gpmDep.artifactId}"
45 | })
46 |
47 | installedDeps[selDepIndex]
48 |
49 | } else {
50 | installedDeps.first()
51 | }
52 |
53 | gradleManager.removeDep(depToRemove)
54 | command.onAfterDepRemove(depType, depName)
55 | }
56 |
57 | return RESULT_DEP_UNINSTALLED
58 | }
59 |
60 | }
--------------------------------------------------------------------------------
/src/test/kotlin/com/theapache64/gpm/data/repos/MavenRepoTest.kt:
--------------------------------------------------------------------------------
1 | package com.theapache64.gpm.data.repos
2 |
3 | import com.theapache64.gpm.data.remote.maven.MavenApiInterface
4 | import com.theapache64.gpm.rules.MyDaggerMockRule
5 | import com.theapache64.gpm.runBlockingUnitTest
6 | import com.winterbe.expekt.should
7 | import it.cosenonjaviste.daggermock.InjectFromComponent
8 | import org.junit.Before
9 | import org.junit.Rule
10 | import org.junit.Test
11 | import retrofit2.HttpException
12 |
13 | class MavenRepoTest {
14 |
15 | @get:Rule
16 | val daggerMockRule = MyDaggerMockRule()
17 |
18 | @InjectFromComponent
19 | private lateinit var mavenApiInterface: MavenApiInterface
20 |
21 | private lateinit var mavenRepo: MavenRepo
22 |
23 | @Before
24 | fun setUp() {
25 | mavenRepo = MavenRepo(mavenApiInterface)
26 | }
27 |
28 | @Test
29 | fun `Valid search`() = runBlockingUnitTest {
30 | val dep = mavenRepo.search("okhttp")
31 | dep.size.should.above(1)
32 | }
33 |
34 | @Test
35 | fun `Invalid search`() = runBlockingUnitTest {
36 | val dep = mavenRepo.search("dsfgdfgdsf")
37 | dep.size.should.equal(0)
38 | }
39 |
40 | @Test
41 | fun `Getting valid artifact information`() = runBlockingUnitTest {
42 | val info = mavenRepo.getLatestVersion("org.junit.jupiter", "junit-jupiter-api")
43 | info.should.not.`null`
44 | info!!.version.should.not.empty
45 | info.repoName.should.equal("Central")
46 | info.repoUrl.should.equal("/repos/central")
47 | }
48 |
49 | @Test
50 | fun `Getting valid artifact information 2`() = runBlockingUnitTest {
51 | val info = mavenRepo.getLatestVersion("me.tongfei", "progressbar")
52 | info.should.not.`null`
53 | info!!.version.should.not.empty
54 | }
55 |
56 |
57 | @Test(expected = HttpException::class)
58 | fun `Getting invalid artifact information`() = runBlockingUnitTest {
59 | val info = mavenRepo.getLatestVersion("gfh", "jhj")
60 | info.should.`null`
61 | }
62 |
63 | }
--------------------------------------------------------------------------------
/src/test/kotlin/com/theapache64/gpm/utils/GradleManagerTest.kt:
--------------------------------------------------------------------------------
1 | package com.theapache64.gpm.utils
2 |
3 | import com.theapache64.gpm.commands.subcommands.install.InstallComponent
4 | import com.theapache64.gpm.core.TransactionManager
5 | import com.theapache64.gpm.core.gm.GradleDep
6 | import com.theapache64.gpm.core.gm.GradleManager
7 | import com.theapache64.gpm.data.remote.gpm.models.GpmDep
8 | import com.theapache64.gpm.di.modules.CommandModule
9 | import com.theapache64.gpm.di.modules.GradleModule
10 | import com.theapache64.gpm.di.modules.TransactionModule
11 | import com.winterbe.expekt.should
12 | import it.cosenonjaviste.daggermock.DaggerMock
13 | import it.cosenonjaviste.daggermock.InjectFromComponent
14 | import org.junit.Before
15 | import org.junit.Rule
16 | import org.junit.Test
17 | import java.io.File
18 |
19 | class GradleManagerTest {
20 |
21 | private lateinit var gradleManager: GradleManager
22 | private lateinit var gradleFile: File
23 |
24 |
25 | @get:Rule
26 | val daggerRule = DaggerMock.rule(
27 | GradleModule(isFromTest = true, modulePath = null),
28 | TransactionModule(isFromTest = true),
29 | CommandModule(isFromTest = true)
30 | ) {
31 | set {
32 | gradleFile = it.gradleFile()
33 | }
34 | }
35 |
36 | @InjectFromComponent
37 | lateinit var tm: TransactionManager
38 |
39 | @Before
40 | fun setUp() {
41 | this.gradleManager = GradleManager(tm, gradleFile)
42 | }
43 |
44 | @Test
45 | fun whenGetDependenciesSize_then10() {
46 | gradleManager.parseDeps().size.should.equal(37)
47 | gradleManager.addDep(
48 | "my-artifact",
49 | GradleDep.Type.IMP,
50 | GpmDep(
51 | "myArtifact",
52 | GradleDep.Type.IMP.key,
53 | "https://mylib.docs",
54 | "jcenter",
55 | "https://github.com/userx/myArtifact",
56 | "myGroup",
57 | "My Lib",
58 | "Some description",
59 | "1.0.0"
60 | )
61 | )
62 | gradleManager.parseDeps().size.should.equal(38)
63 | }
64 | }
--------------------------------------------------------------------------------
/src/main/kotlin/com/theapache64/gpm/commands/subcommands/uninstall/Uninstall.kt:
--------------------------------------------------------------------------------
1 | package com.theapache64.gpm.commands.subcommands.uninstall
2 |
3 | import com.theapache64.gpm.commands.base.BaseCommand
4 | import com.theapache64.gpm.core.gm.GradleDep
5 | import com.theapache64.gpm.di.modules.GradleModule
6 | import com.theapache64.gpm.di.modules.TransactionModule
7 | import kotlinx.coroutines.runBlocking
8 | import picocli.CommandLine
9 | import javax.inject.Inject
10 |
11 | @CommandLine.Command(
12 | name = "uninstall",
13 | aliases = ["u"],
14 | description = ["To uninstall a dependency"]
15 | )
16 | class Uninstall(isFromTest: Boolean = false) : BaseCommand(isFromTest) {
17 |
18 | @CommandLine.Option(
19 | names = ["-S", "--save"],
20 | description = ["To uninstall the dependency defined as 'implementation'"]
21 | )
22 | var isSave: Boolean = false
23 |
24 | @CommandLine.Option(
25 | names = ["-D", "--save-dev"],
26 | description = ["To uninstall the dependency defined as 'testImplementation'"]
27 | )
28 | var isSaveDev: Boolean = false
29 |
30 | @CommandLine.Option(
31 | names = ["-DA", "--save-dev-android"],
32 | description = ["To uninstall the dependency defined as 'androidTestImplementation'"]
33 | )
34 | var isSaveDevAndroid: Boolean = false
35 |
36 | @CommandLine.Option(
37 | names = ["-K", "--kapt"],
38 | description = ["To uninstall the dependency defined as 'kapt'"]
39 | )
40 | var isKapt: Boolean = false
41 |
42 | @CommandLine.Parameters(index = "0", description = ["Dependency name"])
43 | lateinit var depName: String
44 |
45 | @Inject
46 | lateinit var uninstallViewModel: UninstallViewModel
47 |
48 | init {
49 | DaggerUninstallComponent
50 | .builder()
51 | .gradleModule(GradleModule(isFromTest = false, modulePath = null))
52 | .transactionModule(TransactionModule(isFromTest = false))
53 | .build()
54 | .inject(this)
55 | }
56 |
57 | override fun call(): Int = runBlocking {
58 | uninstallViewModel.call(this@Uninstall)
59 | }
60 |
61 | fun onNoDepInstalled(depType: GradleDep.Type, depName: String) {
62 | println("⚠️ No dependency named '$depName' installed as '${depType.key}' using gpm. You might have installed it manually.")
63 | }
64 |
65 | fun onAfterDepRemove(depType: GradleDep.Type, depName: String) {
66 | println("🗑️ Removed '${depType.key}' of '$depName'...")
67 | }
68 | }
--------------------------------------------------------------------------------
/src/test/kotlin/com/theapache64/gpm/utils/GradleUtilsTest.kt:
--------------------------------------------------------------------------------
1 | package com.theapache64.gpm.utils
2 |
3 | import com.theapache64.gpm.core.gm.GradleDep
4 | import com.winterbe.expekt.should
5 | import org.junit.Test
6 |
7 | class GradleUtilsTest {
8 |
9 |
10 | @Test
11 | fun `Full signature for implementation`() {
12 |
13 | val gradleDependency = GradleUtils.getFullSignature(
14 | GradleDep.Type.IMP.key,
15 | "org.mockito",
16 | "mockito",
17 | "3.3.3",
18 | false
19 | )
20 |
21 | gradleDependency.should.equal(
22 | "implementation 'org.mockito:mockito:3.3.3'"
23 | )
24 | }
25 |
26 | @Test
27 | fun `Full signature for testImplementation`() {
28 |
29 | val gradleDependency = GradleUtils.getFullSignature(
30 | GradleDep.Type.TEST_IMP.key,
31 | "org.mockito",
32 | "mockito",
33 | "3.3.3",
34 | false
35 | )
36 |
37 | gradleDependency.should.equal(
38 | "testImplementation 'org.mockito:mockito:3.3.3'"
39 | )
40 | }
41 |
42 | @Test
43 | fun `Full signature for androidTestImplementation`() {
44 |
45 | val gradleDependency = GradleUtils.getFullSignature(
46 | GradleDep.Type.AND_TEST_IMP.key,
47 | "org.mockito",
48 | "mockito",
49 | "3.3.3",
50 | false
51 | )
52 |
53 | gradleDependency.should.equal(
54 | "androidTestImplementation 'org.mockito:mockito:3.3.3'"
55 | )
56 | }
57 |
58 |
59 | @Test
60 | fun `Full signature for implementation - kts`() {
61 |
62 | val gradleDependency = GradleUtils.getFullSignature(
63 | GradleDep.Type.IMP.key,
64 | "org.mockito",
65 | "mockito",
66 | "3.3.3",
67 | true
68 | )
69 |
70 | gradleDependency.should.equal(
71 | "implementation(\"org.mockito:mockito:3.3.3\")"
72 | )
73 | }
74 |
75 | @Test
76 | fun `Full signature for testImplementation - kts`() {
77 |
78 | val gradleDependency = GradleUtils.getFullSignature(
79 | GradleDep.Type.TEST_IMP.key,
80 | "org.mockito",
81 | "mockito",
82 | "3.3.3",
83 | true
84 | )
85 |
86 | gradleDependency.should.equal(
87 | "testImplementation(\"org.mockito:mockito:3.3.3\")"
88 | )
89 | }
90 |
91 | @Test
92 | fun `Full signature for androidTestImplementation - kts`() {
93 |
94 | val gradleDependency = GradleUtils.getFullSignature(
95 | GradleDep.Type.AND_TEST_IMP.key,
96 | "org.mockito",
97 | "mockito",
98 | "3.3.3",
99 | true
100 | )
101 |
102 | gradleDependency.should.equal(
103 | "androidTestImplementation(\"org.mockito:mockito:3.3.3\")"
104 | )
105 | }
106 | }
--------------------------------------------------------------------------------
/.idea/libraries-with-intellij-classes.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @rem
2 | @rem Copyright 2015 the original author or authors.
3 | @rem
4 | @rem Licensed under the Apache License, Version 2.0 (the "License");
5 | @rem you may not use this file except in compliance with the License.
6 | @rem You may obtain a copy of the License at
7 | @rem
8 | @rem https://www.apache.org/licenses/LICENSE-2.0
9 | @rem
10 | @rem Unless required by applicable law or agreed to in writing, software
11 | @rem distributed under the License is distributed on an "AS IS" BASIS,
12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | @rem See the License for the specific language governing permissions and
14 | @rem limitations under the License.
15 | @rem
16 |
17 | @if "%DEBUG%" == "" @echo off
18 | @rem ##########################################################################
19 | @rem
20 | @rem Gradle startup script for Windows
21 | @rem
22 | @rem ##########################################################################
23 |
24 | @rem Set local scope for the variables with windows NT shell
25 | if "%OS%"=="Windows_NT" setlocal
26 |
27 | set DIRNAME=%~dp0
28 | if "%DIRNAME%" == "" set DIRNAME=.
29 | set APP_BASE_NAME=%~n0
30 | set APP_HOME=%DIRNAME%
31 |
32 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
33 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
34 |
35 | @rem Find java.exe
36 | if defined JAVA_HOME goto findJavaFromJavaHome
37 |
38 | set JAVA_EXE=java.exe
39 | %JAVA_EXE% -version >NUL 2>&1
40 | if "%ERRORLEVEL%" == "0" goto init
41 |
42 | echo.
43 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
44 | echo.
45 | echo Please set the JAVA_HOME variable in your environment to match the
46 | echo location of your Java installation.
47 |
48 | goto fail
49 |
50 | :findJavaFromJavaHome
51 | set JAVA_HOME=%JAVA_HOME:"=%
52 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
53 |
54 | if exist "%JAVA_EXE%" goto init
55 |
56 | echo.
57 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
58 | echo.
59 | echo Please set the JAVA_HOME variable in your environment to match the
60 | echo location of your Java installation.
61 |
62 | goto fail
63 |
64 | :init
65 | @rem Get command-line arguments, handling Windows variants
66 |
67 | if not "%OS%" == "Windows_NT" goto win9xME_args
68 |
69 | :win9xME_args
70 | @rem Slurp the command line arguments.
71 | set CMD_LINE_ARGS=
72 | set _SKIP=2
73 |
74 | :win9xME_args_slurp
75 | if "x%~1" == "x" goto execute
76 |
77 | set CMD_LINE_ARGS=%*
78 |
79 | :execute
80 | @rem Setup the command line
81 |
82 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
83 |
84 | @rem Execute Gradle
85 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
86 |
87 | :end
88 | @rem End local scope for the variables with windows NT shell
89 | if "%ERRORLEVEL%"=="0" goto mainEnd
90 |
91 | :fail
92 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
93 | rem the _cmd.exe /c_ return code!
94 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
95 | exit /b 1
96 |
97 | :mainEnd
98 | if "%OS%"=="Windows_NT" endlocal
99 |
100 | :omega
101 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 |
2 | # Created by https://www.gitignore.io/api/kotlin,intellij,gradle
3 | # Edit at https://www.gitignore.io/?templates=kotlin,intellij,gradle
4 |
5 | ### Intellij ###
6 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm
7 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
8 |
9 | # User-specific stuff
10 | .idea/**/workspace.xml
11 | .idea/**/tasks.xml
12 | .idea/**/usage.statistics.xml
13 | .idea/**/dictionaries
14 | .idea/**/shelf
15 |
16 | # Generated files
17 | .idea/**/contentModel.xml
18 |
19 | # Sensitive or high-churn files
20 | .idea/**/dataSources/
21 | .idea/**/dataSources.ids
22 | .idea/**/dataSources.local.xml
23 | .idea/**/sqlDataSources.xml
24 | .idea/**/dynamic.xml
25 | .idea/**/uiDesigner.xml
26 | .idea/**/dbnavigator.xml
27 |
28 | # Gradle
29 | .idea/**/gradle.xml
30 | .idea/**/libraries
31 |
32 | # Gradle and Maven with auto-import
33 | # When using Gradle or Maven with auto-import, you should exclude module files,
34 | # since they will be recreated, and may cause churn. Uncomment if using
35 | # auto-import.
36 | # .idea/modules.xml
37 | # .idea/*.iml
38 | # .idea/modules
39 | # *.iml
40 | # *.ipr
41 |
42 | # CMake
43 | cmake-build-*/
44 |
45 | # Mongo Explorer plugin
46 | .idea/**/mongoSettings.xml
47 |
48 | # File-based project format
49 | *.iws
50 |
51 | # IntelliJ
52 | out/
53 |
54 | # mpeltonen/sbt-idea plugin
55 | .idea_modules/
56 |
57 | # JIRA plugin
58 | atlassian-ide-plugin.xml
59 |
60 | # Cursive Clojure plugin
61 | .idea/replstate.xml
62 |
63 | # Crashlytics plugin (for Android Studio and IntelliJ)
64 | com_crashlytics_export_strings.xml
65 | crashlytics.properties
66 | crashlytics-build.properties
67 | fabric.properties
68 |
69 | # Editor-based Rest Client
70 | .idea/httpRequests
71 |
72 | # Android studio 3.1+ serialized cache file
73 | .idea/caches/build_file_checksums.ser
74 |
75 | ### Intellij Patch ###
76 | # Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721
77 |
78 | # *.iml
79 | # modules.xml
80 | # .idea/misc.xml
81 | # *.ipr
82 |
83 | # Sonarlint plugin
84 | .idea/**/sonarlint/
85 |
86 | # SonarQube Plugin
87 | .idea/**/sonarIssues.xml
88 |
89 | # Markdown Navigator plugin
90 | .idea/**/markdown-navigator.xml
91 | .idea/**/markdown-navigator/
92 |
93 | ### Kotlin ###
94 | # Compiled class file
95 | *.class
96 |
97 | # Log file
98 | *.log
99 |
100 | # BlueJ files
101 | *.ctxt
102 |
103 | # Mobile Tools for Java (J2ME)
104 | .mtj.tmp/
105 |
106 | # Package Files #
107 | *.jar
108 | *.war
109 | *.nar
110 | *.ear
111 | *.zip
112 | *.tar.gz
113 | *.rar
114 |
115 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
116 | hs_err_pid*
117 |
118 | ### Gradle ###
119 | .gradle
120 | build/
121 |
122 | # Ignore Gradle GUI config
123 | gradle-app.setting
124 |
125 | # Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored)
126 | !gradle-wrapper.jar
127 |
128 | # Cache of project
129 | .gradletasknamecache
130 |
131 | # # Work around https://youtrack.jetbrains.com/issue/IDEA-116898
132 | # gradle/wrapper/gradle-wrapper.properties
133 |
134 | ### Gradle Patch ###
135 | **/build/
136 |
137 | # End of https://www.gitignore.io/api/kotlin,intellij,gradle
138 | SecretConstants.kt
139 | src/test/resources/temp.build.gradle
140 | # test generated file
141 | feature/auth/src/test/resources/temp.build.gradle
142 | x.html
143 | src/test/resources/temp.gpm.json
144 | node_modules
145 | package-lock.json
146 | gpm.json
--------------------------------------------------------------------------------
/src/test/kotlin/com/theapache64/gpm/commands/subcommands/docs/DocsTest.kt:
--------------------------------------------------------------------------------
1 | package com.theapache64.gpm.commands.subcommands.docs
2 |
3 | import com.theapache64.gpm.core.TransactionManager
4 | import com.theapache64.gpm.core.gm.GradleDep
5 | import com.theapache64.gpm.data.remote.gpm.models.GpmDep
6 | import com.theapache64.gpm.di.modules.TransactionModule
7 | import com.winterbe.expekt.should
8 | import it.cosenonjaviste.daggermock.DaggerMock
9 | import it.cosenonjaviste.daggermock.InjectFromComponent
10 | import org.junit.After
11 | import org.junit.Before
12 | import org.junit.Rule
13 | import org.junit.Test
14 | import picocli.CommandLine
15 | import java.io.File
16 |
17 | class DocsTest {
18 |
19 | private lateinit var gpmJsonFile: File
20 | private lateinit var docsCmd: CommandLine
21 | private val docs = Docs(true)
22 |
23 | @InjectFromComponent
24 | private lateinit var tm: TransactionManager
25 |
26 | @get:Rule
27 | val daggerRule = DaggerMock.rule(TransactionModule(true)) {
28 | set {
29 | tm = it.transactionManager()
30 | gpmJsonFile = it.gpmJsonFile()
31 | }
32 | }
33 |
34 | @Before
35 | fun setUp() {
36 | this.docsCmd = CommandLine(docs)
37 | }
38 |
39 | @After
40 | fun tearDown() {
41 | gpmJsonFile.delete()
42 | }
43 |
44 | @Test
45 | fun `Opening docs for installed dependency`() {
46 | // Adding fake transaction
47 | tm.add(
48 | "okhttp",
49 | GradleDep.Type.IMP,
50 | GpmDep(
51 | "okhttp",
52 | GradleDep.Type.IMP.key,
53 | "https://square.github.io/okhttp/",
54 | "jcenter",
55 | "https://github.com/square/okhttp/",
56 | "com.square.okhttp3",
57 | "Material Colors",
58 | "A networking library",
59 | "1.0.0"
60 | )
61 | )
62 |
63 | val exitCode = docsCmd.execute("okhttp")
64 | exitCode.should.equal(DocsViewModel.RESULT_DOC_FOUND)
65 |
66 | }
67 |
68 | @Test
69 | fun `Opening docs for not installed dependency`() {
70 | val exitCode = docsCmd.execute("not-installed-dep")
71 | exitCode.should.equal(DocsViewModel.RESULT_NOT_FOUND)
72 | }
73 |
74 | @Test
75 | fun `Opening docs for dependency installed with similar name`() {
76 | tm.add(
77 | "okhttp",
78 | GradleDep.Type.IMP,
79 | GpmDep(
80 | "okhttp",
81 | GradleDep.Type.IMP.key,
82 | "https://square.github.io/okhttp/",
83 | "jcenter",
84 | "https://github.com/square/okhttp/",
85 | "com.square.okhttp3",
86 | "Material Colors",
87 | "A networking library",
88 | "1.0.0"
89 | )
90 | )
91 |
92 | tm.add(
93 | "okhttp",
94 | GradleDep.Type.IMP,
95 | GpmDep(
96 | "some-other-lib",
97 | GradleDep.Type.IMP.key,
98 | "https://square.github.io/okhttp/",
99 | "jcenter",
100 | "https://github.com/square/okhttp/",
101 | "some-other-lib",
102 | "some-other-lib",
103 | "Some lib",
104 | "1.0.0"
105 | )
106 | )
107 |
108 | val exitCode = docsCmd.execute("okhttp")
109 | exitCode.should.equal(DocsViewModel.RESULT_DOC_FOUND)
110 | }
111 |
112 | }
--------------------------------------------------------------------------------
/src/main/kotlin/com/theapache64/gpm/data/repos/MavenRepo.kt:
--------------------------------------------------------------------------------
1 | package com.theapache64.gpm.data.repos
2 |
3 | import com.theapache64.gpm.data.remote.maven.MavenApiInterface
4 | import com.theapache64.gpm.data.remote.maven.models.ArtifactInfo
5 | import com.theapache64.gpm.data.remote.maven.models.SearchResult
6 | import com.theapache64.gpm.utils.removeNewLinesAndMultipleSpaces
7 | import org.apache.commons.text.StringEscapeUtils
8 | import java.text.SimpleDateFormat
9 | import javax.inject.Inject
10 | import javax.inject.Singleton
11 |
12 | @Singleton
13 | class MavenRepo @Inject constructor(
14 | private val mavenApiInterface: MavenApiInterface
15 | ) {
16 | companion object {
17 | private val SEARCH_RESULT_REGEX by lazy {
18 | ".+?(?
\\d+?)\\. <\\/span>.+?)\\/(?.+?)\">(?.+?)<\\/a>.+?(?:(?.+?)<\\/b> usages<\\/a>.+?)?im-description\">(?.+?).+?)<\\/div".toRegex()
19 | }
20 |
21 | private val ARTIFACT_VERSION_REGEX by lazy {
22 | ".+?vbtn (?:release candidate|release|alpha|beta)\">(?.+?)<\\/a>.+?.+?)\">(?.+?)<\\/a>".toRegex()
23 | }
24 |
25 | //Apr 29, 2020
26 | private val SEARCH_RESULT_DATE_FORMAT = SimpleDateFormat("MMM dd, yyyy")
27 | }
28 |
29 | suspend fun search(depName: String): List {
30 | val searchHtml = mavenApiInterface.search(depName)
31 | val htmlResp = searchHtml.removeNewLinesAndMultipleSpaces()
32 | val matches = SEARCH_RESULT_REGEX.findAll(htmlResp).toMutableList().let { matches ->
33 |
34 | // Minimizing results to top5
35 | if (matches.size > 5) {
36 | // only first five needed
37 | matches.subList(0, 5)
38 | } else {
39 | // no change
40 | matches
41 | }
42 | }
43 | val searchResults = mutableListOf()
44 | for (match in matches) {
45 |
46 | val (
47 | number,
48 | groupId,
49 | artifactId,
50 | name,
51 | usages,
52 | description,
53 | lastRelease
54 | ) = match.destructured
55 |
56 | val usage = usages.trim().replace(",", "").let {
57 | if (it.isEmpty()) {
58 | null
59 | } else {
60 | it.toInt()
61 | }
62 | }
63 | searchResults.add(
64 | SearchResult(
65 | number.trim().toInt(),
66 | name.trim(),
67 | groupId.trim(),
68 | artifactId.trim(),
69 | StringEscapeUtils.unescapeHtml4(description.trim()),
70 | usage,
71 | SEARCH_RESULT_DATE_FORMAT.parse(lastRelease.trim())
72 | )
73 | )
74 | }
75 | return searchResults
76 | }
77 |
78 | /**
79 | * To get latest version of the given artifactId from the given group
80 | */
81 | suspend fun getLatestVersion(groupId: String, artifactId: String): ArtifactInfo? {
82 | val htmlResp = mavenApiInterface.getArtifact(groupId, artifactId).removeNewLinesAndMultipleSpaces()
83 | val results = ARTIFACT_VERSION_REGEX.find(htmlResp)
84 | return if (results != null) {
85 | val (version, repoUrl, repoName) = results.destructured
86 | ArtifactInfo(version, repoName, repoUrl)
87 | } else {
88 | null
89 | }
90 | }
91 |
92 |
93 | }
--------------------------------------------------------------------------------
/src/main/kotlin/com/theapache64/gpm/core/TransactionManager.kt:
--------------------------------------------------------------------------------
1 | package com.theapache64.gpm.core
2 |
3 | import com.squareup.moshi.JsonAdapter
4 | import com.squareup.moshi.Moshi
5 | import com.theapache64.gpm.core.gm.GradleDep
6 | import com.theapache64.gpm.data.remote.gpm.models.GpmDep
7 | import com.theapache64.gpm.models.GpmFileData
8 | import java.io.File
9 | import java.io.FileNotFoundException
10 | import java.io.IOException
11 | import javax.inject.Inject
12 |
13 | class TransactionManager @Inject constructor(
14 | private val gpmJsonFile: File,
15 | private val moshi: Moshi
16 | ) {
17 |
18 | private val adapter: JsonAdapter by lazy {
19 | moshi.adapter(GpmFileData::class.java).indent(" ")
20 | }
21 |
22 |
23 | fun add(installedName: String, type: GradleDep.Type, newGpmDep: GpmDep) {
24 |
25 | // Need to login
26 | val newDepId = getLastDepAdded()?.id?.plus(1) ?: 1
27 |
28 | if (newDepId != 1) {
29 | // got prev transaction, so check if this is a duplicate one
30 | if (isDuplicate(installedName, type, newGpmDep)) {
31 | return
32 | }
33 | }
34 |
35 | val depToStore = GpmFileData.AddedDep(
36 | newDepId,
37 | type.key,
38 | installedName,
39 | newGpmDep
40 | )
41 | val newFileData = if (!gpmJsonFile.exists()) {
42 | GpmFileData(mutableListOf(depToStore))
43 | } else {
44 | val gpmFileData = adapter.fromJson(gpmJsonFile.readText())
45 | gpmFileData!!.deps.add(depToStore)
46 | gpmFileData
47 | }
48 |
49 | setData(newFileData)
50 | }
51 |
52 | private fun isDuplicate(installedName: String, type: GradleDep.Type, newGpmDep: GpmDep): Boolean {
53 | return getData()?.deps?.find {
54 | it.installedName == installedName &&
55 | it.type == type.key &&
56 | it.gpmDep.artifactId == newGpmDep.artifactId &&
57 | it.gpmDep.groupId == newGpmDep.groupId &&
58 | it.gpmDep.name == newGpmDep.name
59 | } != null
60 | }
61 |
62 | private fun getLastDepAdded(): GpmFileData.AddedDep? {
63 | return try {
64 | getData()?.deps?.lastOrNull()
65 | } catch (e: FileNotFoundException) {
66 | null
67 | }
68 | }
69 |
70 | private fun setData(newFileData: GpmFileData) {
71 | val gpmFileDataJson = adapter.toJson(newFileData)
72 | gpmJsonFile.writeText(gpmFileDataJson)
73 | }
74 |
75 | fun getInstalled(type: String?, depName: String): List {
76 | return getData()?.deps?.filter {
77 | it.installedName == depName && (type == null || it.type == type)
78 | } ?: listOf()
79 | }
80 |
81 | fun remove(depToRemove: GpmFileData.AddedDep) {
82 | val data = getData()
83 | if (data != null) {
84 | val isRemoved = data.deps.removeIf { it.id == depToRemove.id }
85 | if (isRemoved) {
86 | setData(data)
87 | } else {
88 | throw IOException("Failed to remove dependency. Couldn't find dependency with id '${depToRemove.id}'")
89 | }
90 | } else {
91 | throw IOException(
92 | """
93 | Failed to remove dependency. Are you sure you have installed it through gpm?
94 | """.trimIndent()
95 | )
96 | }
97 | }
98 |
99 | private fun getData() = try {
100 | adapter.fromJson(gpmJsonFile.readText())
101 | } catch (e: FileNotFoundException) {
102 | null
103 | }
104 | }
--------------------------------------------------------------------------------
/src/main/kotlin/com/theapache64/gpm/commands/subcommands/install/Install.kt:
--------------------------------------------------------------------------------
1 | package com.theapache64.gpm.commands.subcommands.install
2 |
3 | import com.theapache64.gpm.commands.base.BaseCommand
4 | import com.theapache64.gpm.commands.gpm.Gpm
5 | import com.theapache64.gpm.core.gm.GradleDep
6 | import com.theapache64.gpm.di.modules.CommandModule
7 | import com.theapache64.gpm.di.modules.GradleModule
8 | import com.theapache64.gpm.di.modules.TransactionModule
9 | import kotlinx.coroutines.runBlocking
10 | import picocli.CommandLine
11 | import javax.inject.Inject
12 |
13 | @CommandLine.Command(
14 | name = "install",
15 | aliases = ["i"],
16 | description = ["To install the dependency"]
17 | )
18 | class Install(isFromTest: Boolean = false) : BaseCommand(isFromTest) {
19 |
20 |
21 | @CommandLine.ParentCommand
22 | var parent: Gpm? = null
23 |
24 | @CommandLine.Option(
25 | names = ["-S", "-s", "--save"],
26 | description = ["To install the dependency as 'implementation'"]
27 | )
28 | var isSave: Boolean = false
29 |
30 | @CommandLine.Option(
31 | names = ["-FS", "-fs", "--force-search"],
32 | description = ["To skip gpm registry search check and quick search with other repos"]
33 | )
34 | var isForceSearch: Boolean = true
35 |
36 | @CommandLine.Option(
37 | names = ["-po"],
38 | description = ["To print only. No files will be modified"]
39 | )
40 | var isPrintOnly: Boolean = false
41 |
42 | @CommandLine.Option(
43 | names = ["-D", "-d", "--save-dev"],
44 | description = ["To install the dependency as 'testImplementation'"]
45 | )
46 | var isSaveDev: Boolean = false
47 |
48 | @CommandLine.Option(
49 | names = ["-DA", "-da", "--save-dev-android"],
50 | description = ["To install the dependency as 'androidTestImplementation'"]
51 | )
52 | var isSaveDevAndroid: Boolean = false
53 |
54 | @CommandLine.Option(
55 | names = ["-K", "-k", "--kapt"],
56 | description = ["To install the dependency as 'kapt'"]
57 | )
58 | var isKapt: Boolean = false
59 |
60 | @CommandLine.Parameters(index = "0", description = ["Dependency name"])
61 | lateinit var depName: String
62 |
63 | @Inject
64 | lateinit var installViewModel: InstallViewModel
65 | private var modulePath: String? = null
66 |
67 | override fun call(): Int = runBlocking {
68 | modulePath = parent?.modulePath?.ifBlank { null }
69 | if (!isFromTest) {
70 | DaggerInstallComponent
71 | .builder()
72 | .commandModule(CommandModule(isFromTest = isFromTest))
73 | .gradleModule(GradleModule(isFromTest = isFromTest, modulePath))
74 | .transactionModule(TransactionModule(isFromTest))
75 | .build()
76 | .inject(this@Install)
77 | }
78 |
79 | installViewModel.call(this@Install)
80 | }
81 |
82 | fun onBeforeGetDep() {
83 | if (modulePath != null) {
84 | println("➡️ Module: $modulePath")
85 | }
86 |
87 | println("🔍 Searching for '$depName'")
88 | }
89 |
90 | fun onDepGot() {
91 | println("✔️ Found dependency")
92 | }
93 |
94 | fun onBeforeSearchingInGpmRegistry() {
95 | println("🔍 Searching in gpm registry for '$depName'...")
96 | }
97 |
98 | fun onBeforeSearchingInMavenCentral() {
99 | println("🔍 Searching in maven for '$depName'")
100 | }
101 |
102 | fun onDepNotFoundAnywhere() {
103 | println("❌ Couldn't find dependency with name '$depName'")
104 | }
105 |
106 | fun onBeforeAddDependency(depType: GradleDep.Type) {
107 | println("⌨️ Adding ${depType.key} to build.gradle...")
108 | }
109 |
110 | fun onAfterDependenciesAdded(newlyAddedLines: String) {
111 | val coloredLines = CommandLine.Help.Ansi.AUTO.string("@|bold,green $newlyAddedLines|@")
112 | println("✅ Added $coloredLines to build.gradle!")
113 | }
114 | }
115 |
--------------------------------------------------------------------------------
/src/main/kotlin/com/theapache64/gpm/di/modules/GradleModule.kt:
--------------------------------------------------------------------------------
1 | package com.theapache64.gpm.di.modules
2 |
3 | import com.theapache64.gpm.core.TransactionManager
4 | import com.theapache64.gpm.core.gm.GradleManager
5 | import com.theapache64.gpm.di.GradleFile
6 | import dagger.Module
7 | import dagger.Provides
8 | import java.io.File
9 | import kotlin.system.exitProcess
10 |
11 | @Module(includes = [TransactionModule::class])
12 | class GradleModule(
13 | private val isFromTest: Boolean,
14 | private val modulePath: String?
15 | ) {
16 |
17 | @Provides
18 | @GradleFile
19 | fun gradleFile(): File {
20 |
21 | @Suppress("ConstantConditionIf")
22 | return if (isFromTest) {
23 | val modPath = if (!modulePath.isNullOrBlank()) {
24 | "$modulePath/"
25 | } else {
26 | ""
27 | }
28 | val tempGradleFile = File("${modPath}src/test/resources/temp.build.gradle")
29 | val sampleFile = File("src/test/resources/sample.build.gradle")
30 | tempGradleFile.delete()
31 | sampleFile.copyTo(tempGradleFile)
32 | tempGradleFile
33 |
34 | } else {
35 | if (modulePath != null) {
36 | val androidGradleFile = File("$modulePath/build.gradle")
37 | val androidKtsGradleFile = File("$modulePath/build.gradle.kts")
38 | when {
39 | androidGradleFile.exists() -> {
40 | // android project
41 | androidGradleFile
42 | }
43 |
44 | androidKtsGradleFile.exists() -> {
45 | androidKtsGradleFile
46 | }
47 |
48 | else -> {
49 | val currentDir = File(System.getProperty("user.dir"))
50 |
51 | println(
52 | """
53 | Couldn't find `$modulePath` inside '${currentDir.absolutePath}'.
54 | Are you sure that you're executing the command from project root?
55 | """.trimIndent()
56 | )
57 |
58 | exitProcess(0)
59 | }
60 |
61 | }
62 | } else {
63 | findGradleFile()
64 | }
65 | }
66 | }
67 |
68 | private fun findGradleFile(): File {
69 | val androidGradleFile = File("app/build.gradle")
70 | val androidKtsGradleFile = File("app/build.gradle.kts")
71 | val jvmGradleFile = File("build.gradle")
72 | val jvmKtsGradleFile = File("build.gradle.kts")
73 |
74 | return when {
75 | androidGradleFile.exists() -> {
76 | // android project
77 | androidGradleFile
78 | }
79 |
80 | androidKtsGradleFile.exists() -> {
81 | androidKtsGradleFile
82 | }
83 |
84 | jvmGradleFile.exists() -> {
85 | jvmGradleFile
86 | }
87 |
88 | jvmKtsGradleFile.exists() -> {
89 | jvmKtsGradleFile
90 | }
91 |
92 | else -> {
93 |
94 | /**
95 | * SMART END
96 | */
97 |
98 | val currentDir = File(System.getProperty("user.dir"))
99 |
100 | println(
101 | """
102 | Invalid directory '${currentDir.absolutePath}'.
103 | Are you sure that you're executing the command from project root?
104 | """.trimIndent()
105 | )
106 |
107 | exitProcess(0)
108 | }
109 | }
110 | }
111 |
112 |
113 | @Provides
114 | fun provideGradleManager(
115 | @GradleFile gradleFile: File,
116 | transactionManager: TransactionManager
117 | ): GradleManager {
118 | return GradleManager(transactionManager, gradleFile)
119 | }
120 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [](https://www.npmjs.com/package/gpm-cli)
2 |
3 |
4 |
5 |
6 |
7 | # gpm 📦
8 |
9 | 
10 |
11 | GPM is a package manager for Gradle projects. It is inspired by the famous npm package manager. GPM is not a replacement
12 | for the Gradle build system, but a helper to install the dependencies seamlessly to reduce latency. It consists of a
13 | command-line client, also called **gpm**, and an online database of a package directory called the GPM registry. It uses
14 | similar syntax as npm.
15 |
16 | ## Install ⚙️
17 |
18 | ```shell script
19 | sudo npm install -g gpm-cli
20 | ```
21 |
22 | ## Usage ⌨️
23 |
24 | ```shell script
25 | Usage: gpm [-hV] [COMMAND]
26 | -h, --help Show this help message and exit.
27 | -V, --version Print version information and exit.
28 | Commands:
29 | install, i To install the dependency
30 | uninstall, u To uninstall a dependency
31 | docs, d To open library docs
32 | ```
33 |
34 | **Example**
35 |
36 | To install a library
37 |
38 | ```shell script
39 | $ gpm i
40 | ```
41 |
42 | *example*
43 |
44 | ```shell script
45 | $ gpm i okhttp
46 | ```
47 |
48 | 
49 |
50 | Other supported commands given below
51 |
52 | ## Features ⚡
53 |
54 | | Command | Description | Implemented | Status | Milestone |
55 | |------------------------------|--------------------------------------------------------------|-------------|---------|----------------|
56 | | `install` | To install the dependency as `implementation` | ✔️ | Done | v1.0.0-alpha01 |
57 | | `install --save-dev` | To install the dependency as `testImplementation` | ✔️ | Done | v1.0.0-alpha01 |
58 | | `install --save-dev-android` | To install the dependency as `androidTestImplementation` | ✔️ | Done | v1.0.0-alpha01 |
59 | | `uninstall` | To uninstall the dependency from `implementation` | ✔️ | Done | v1.0.0-alpha01 |
60 | | `uninstall dev` | To uninstall the dependency from `testImplementation` | ✔️ | Done | v1.0.0-alpha01 |
61 | | `uninstall dev-android` | To uninstall the dependency from `androidTestImplementation` | ✔️ | Done | v1.0.0-alpha01 |
62 | | `docs` | To open the documentation in default browser | ✔️ | Done | v1.0.0-alpha01 |
63 | | `update` | To update the dependency version to latest | ❌ | Pending | - |
64 | | `list` | To list all the dependencies | ❌ | Pending | - |
65 |
66 | ### How can I add my repo to the registry? 🤗
67 |
68 | [Create an issue](https://github.com/theapache64/gpm/issues/new) with below given JSON model as the comment body.
69 |
70 | ```
71 | {
72 | "name": "YOUR REPO NAME", // Required : Depenedency Name
73 | "github": "GITHUB REPO", // Optional: In format, user/repo
74 | "docs": "DOCUMENTATION-URL", // Optional : Can be full URL or file name. For eg. "README.md",
75 | "group_id": "LIBRARY GROUP ID", // Required : Eg. "com.squareup.okhttp3"
76 | "artifact_id": "ARTIFACT ID", // Required: Eg. okhttp
77 | "get_from" : "WHERES YOUR REPO HOSTED", // Required : Possible value are jcenter, mavenCentral, jitpack
78 | "default_type": "implementation" // Required: Possible values are implementation, testImplementation, androidTestImplementation
79 | }
80 | ```
81 |
82 | **Example**
83 |
84 | ```json
85 | {
86 | "name": "OkHttp",
87 | "github": "square/okhttp",
88 | "docs": "https://square.github.io/okhttp/",
89 | "groupId": "com.squareup.okhttp3",
90 | "artifactId": "okhttp",
91 | "get_from": "jcenter",
92 | "default_type": "implementation"
93 | }
94 | ```
95 |
96 | ## Project Status 👷
97 |
98 | This project is under active development. Tap the `👁️ Watch` button to get updates.
99 |
100 | ## Author ✍️
101 |
102 | - theapache64
103 |
104 |
--------------------------------------------------------------------------------
/.idea/markdown-navigator-enh.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/.idea/artifacts/gpm_main_jar.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | $PROJECT_DIR$
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/src/main/kotlin/com/theapache64/gpm/commands/subcommands/install/InstallViewModel.kt:
--------------------------------------------------------------------------------
1 | package com.theapache64.gpm.commands.subcommands.install
2 |
3 | import com.theapache64.gpm.commands.base.BaseInstallUninstallViewModel
4 | import com.theapache64.gpm.core.gm.GradleDep
5 | import com.theapache64.gpm.core.gm.GradleManager
6 | import com.theapache64.gpm.data.remote.gpm.models.GpmDep
7 | import com.theapache64.gpm.data.repos.GpmRepo
8 | import com.theapache64.gpm.data.repos.MavenRepo
9 | import com.theapache64.gpm.utils.GradleUtils
10 | import picocli.CommandLine
11 | import javax.inject.Inject
12 |
13 | const val RESET_COLOR = "\u001b[0m" // Text Reset
14 | const val GREEN_BOLD = "\u001b[1;32m" // GREEN
15 |
16 | class InstallViewModel @Inject constructor(
17 | private val gpmRepo: GpmRepo,
18 | private val mavenRepo: MavenRepo,
19 | private val gradleManager: GradleManager
20 | ) : BaseInstallUninstallViewModel() {
21 |
22 |
23 | companion object {
24 | const val RESULT_DEP_INSTALLED = 200
25 | const val RESULT_REPO_NOT_FOUND = 404
26 | }
27 |
28 | override suspend fun call(command: Install): Int {
29 |
30 | val depName = command.depName.trim().toLowerCase()
31 |
32 | // first get from
33 | command.onBeforeGetDep()
34 | val gpmDep = getDep(command, depName)
35 | ?: return RESULT_REPO_NOT_FOUND
36 |
37 | command.onDepGot()
38 | val depTypes = getDepTypes(
39 | command.isSave,
40 | command.isSaveDev,
41 | command.isSaveDevAndroid,
42 | command.isKapt,
43 | gpmDep.defaultType
44 | )
45 |
46 | require(depTypes.isNotEmpty()) { "Dependency type can't be empty" }
47 |
48 | // Adding each dependency
49 | for (depType in depTypes) {
50 | if(command.isPrintOnly){
51 | // Only print. No files need to be modified
52 | val depSign = GradleUtils.getFullSignature(
53 | depType.key,
54 | gpmDep.groupId,
55 | gpmDep.artifactId,
56 | gpmDep.version!!,
57 | isGradleKts = true
58 | )
59 | val coloredDepsSign = CommandLine.Help.Ansi.AUTO.string("@|bold,green $depSign|@")
60 | println(coloredDepsSign)
61 | }else{
62 | // Modify files
63 | command.onBeforeAddDependency(depType)
64 | val newlyAddedLines = gradleManager.addDep(
65 | depName,
66 | depType,
67 | gpmDep
68 | )
69 | command.onAfterDependenciesAdded(newlyAddedLines)
70 | }
71 | }
72 |
73 |
74 | return RESULT_DEP_INSTALLED
75 | }
76 |
77 | private suspend fun getDep(
78 | install: Install,
79 | depName: String
80 | ): GpmDep? {
81 | var gpmDep = if (install.isForceSearch) {
82 | // dont look at gpm repo
83 | null
84 | } else {
85 | install.onBeforeSearchingInGpmRegistry()
86 | gpmRepo.getDep(depName)
87 | }
88 |
89 | if (gpmDep == null) {
90 | // Searching for maven
91 | install.onBeforeSearchingInMavenCentral()
92 | gpmDep = getFromMaven(install, depName)
93 | }
94 |
95 | if (gpmDep == null) {
96 | install.onDepNotFoundAnywhere()
97 | }
98 |
99 | return gpmDep
100 | }
101 |
102 | private suspend fun getFromMaven(install: Install, depName: String): GpmDep? {
103 | val mavenDeps = mavenRepo.search(depName)
104 |
105 | if (mavenDeps.isNotEmpty()) {
106 |
107 | val mostUsed = mavenDeps.maxByOrNull { it.usage ?: 0 }!!
108 | val selDepIndex = if (mavenDeps.size > 1) {
109 |
110 | val choosables = mavenDeps.map {
111 | val text = "${it.groupId}:${it.artifactId}"
112 | if (it == mostUsed) {
113 | // color text
114 | "$GREEN_BOLD$text${RESET_COLOR}"
115 | } else {
116 | //normal text
117 | text
118 | }
119 | }
120 | install.chooseIndex(choosables)
121 | } else {
122 | 0
123 | }
124 |
125 | val selMavenDep = mavenDeps[selDepIndex]
126 |
127 | // Getting latest version
128 | val artifactInfo = mavenRepo.getLatestVersion(
129 | selMavenDep.groupId,
130 | selMavenDep.artifactId
131 | )
132 |
133 | require(artifactInfo != null) { "Failed to artifact information for $selMavenDep" }
134 |
135 | return GpmDep(
136 | selMavenDep.artifactId,
137 | GradleDep.Type.IMP.key,
138 | selMavenDep.url,
139 | artifactInfo.repoName,
140 | null,
141 | selMavenDep.groupId,
142 | selMavenDep.name,
143 | selMavenDep.description,
144 | artifactInfo.version
145 | )
146 | } else {
147 | return null
148 | }
149 | }
150 |
151 | }
--------------------------------------------------------------------------------
/src/test/kotlin/com/theapache64/gpm/commands/subcommands/InstallTest.kt:
--------------------------------------------------------------------------------
1 | package com.theapache64.gpm.commands.subcommands
2 |
3 | import com.theapache64.gpm.commands.subcommands.install.DaggerInstallComponent
4 | import com.theapache64.gpm.commands.subcommands.install.Install
5 | import com.theapache64.gpm.commands.subcommands.install.InstallComponent
6 | import com.theapache64.gpm.commands.subcommands.install.InstallViewModel
7 | import com.theapache64.gpm.di.modules.CommandModule
8 | import com.theapache64.gpm.di.modules.GradleModule
9 | import com.theapache64.gpm.di.modules.NetworkModule
10 | import com.theapache64.gpm.di.modules.TransactionModule
11 | import com.theapache64.gpm.runBlockingUnitTest
12 | import com.winterbe.expekt.should
13 | import it.cosenonjaviste.daggermock.DaggerMock
14 | import org.junit.After
15 | import org.junit.Before
16 | import org.junit.Rule
17 | import org.junit.Test
18 | import picocli.CommandLine
19 | import java.io.File
20 | import java.io.PrintWriter
21 | import java.io.StringWriter
22 |
23 | class InstallTest {
24 |
25 |
26 | private lateinit var installCmd: CommandLine
27 | private val install = Install(true)
28 |
29 |
30 | private lateinit var tempBuildGradle: File
31 | private lateinit var tempGpmJson: File
32 |
33 | companion object {
34 | private const val DUMMY_MODULE = "feature/auth"
35 | }
36 |
37 | @get:Rule
38 | val daggerRule = DaggerMock.rule(NetworkModule()) {
39 | customizeBuilder {
40 | it.gradleModule(GradleModule(isFromTest = true, DUMMY_MODULE)) // sample module
41 | .transactionModule(TransactionModule(true))
42 | .commandModule(CommandModule(true))
43 | }
44 | set {
45 | tempBuildGradle = it.gradleFile()
46 | tempGpmJson = it.gpmJsonFile()
47 | it.inject(install)
48 | }
49 | }
50 |
51 |
52 | @Before
53 | fun setUp() = runBlockingUnitTest {
54 | this.installCmd = CommandLine(install)
55 | installCmd.out = PrintWriter(StringWriter())
56 | }
57 |
58 | @Test
59 | fun `Install default`() {
60 | val exitCode = installCmd.execute("okhttp")
61 | exitCode.should.equal(InstallViewModel.RESULT_DEP_INSTALLED)
62 | tempBuildGradle.readText().should.contain("implementation 'com.squareup.okhttp3:okhttp:")
63 | }
64 |
65 | @Test
66 | fun `Install default another`() {
67 | val exitCode = installCmd.execute("progressbar")
68 | exitCode.should.equal(InstallViewModel.RESULT_DEP_INSTALLED)
69 | /*
70 | tempBuildGradle.readText().should.contain("implementation 'com.squareup.okhttp3:okhttp:")*/
71 | }
72 |
73 | @Test
74 | fun `Install non existing registry`() {
75 | val exitCode = installCmd.execute("retrofit")
76 | exitCode.should.equal(InstallViewModel.RESULT_DEP_INSTALLED)
77 | tempBuildGradle.readText().should.contain("implementation 'com.squareup.retrofit2:retrofit")
78 | }
79 |
80 | @Test
81 | fun `Install --save`() {
82 | val exitCode = installCmd.execute("--save", "okhttp")
83 | exitCode.should.equal(InstallViewModel.RESULT_DEP_INSTALLED)
84 | tempBuildGradle.readText().should.contain("implementation 'com.squareup.okhttp3:okhttp:")
85 | }
86 |
87 | @Test
88 | fun `Install --save-dev`() {
89 | val exitCode = installCmd.execute("--save-dev", "okhttp")
90 | exitCode.should.equal(InstallViewModel.RESULT_DEP_INSTALLED)
91 | tempBuildGradle.readText().should.contain("testImplementation 'com.squareup.okhttp3:okhttp:")
92 | }
93 |
94 | @Test
95 | fun `Install --save-dev-android`() {
96 | val exitCode = installCmd.execute("--save-dev-android", "okhttp")
97 | exitCode.should.equal(InstallViewModel.RESULT_DEP_INSTALLED)
98 | tempBuildGradle.readText().should.contain("androidTestImplementation 'com.squareup.okhttp3:okhttp:")
99 | }
100 |
101 | @Test
102 | fun `Install --kapt`() {
103 | val exitCode = installCmd.execute("--kapt", "dagger-compiler")
104 | exitCode.should.equal(InstallViewModel.RESULT_DEP_INSTALLED)
105 | tempBuildGradle.readText().should.contain("kapt 'com.google.dagger:dagger-compiler:")
106 | }
107 |
108 |
109 | @Test
110 | fun `Install not existing library`() {
111 | val exitCode = installCmd.execute("gdfhdfghdfghfdg")
112 | exitCode.should.equal(InstallViewModel.RESULT_REPO_NOT_FOUND)
113 | }
114 |
115 | @Test
116 | fun `Install --save into modules`() {
117 | val exitCode = installCmd.execute("--save", "okhttp")
118 | exitCode.should.equal(InstallViewModel.RESULT_DEP_INSTALLED)
119 | tempBuildGradle.readText().should.contain("implementation 'com.squareup.okhttp3:okhttp:")
120 | tempBuildGradle.absolutePath.should.contain(DUMMY_MODULE)
121 | }
122 |
123 | @Test
124 | fun `Install lengthy description library`() {
125 | val exitCode = installCmd.execute("webcam")
126 | exitCode.should.equal(InstallViewModel.RESULT_DEP_INSTALLED)
127 | tempBuildGradle.readText().should.contain("implementation 'com.github.sarxos:webcam-capture")
128 | }
129 |
130 | @After
131 | fun tearDown() {
132 | // tempBuildGradle.delete()
133 | tempGpmJson.delete()
134 | }
135 |
136 | }
--------------------------------------------------------------------------------
/src/main/kotlin/com/theapache64/gpm/core/gm/GradleManager.kt:
--------------------------------------------------------------------------------
1 | package com.theapache64.gpm.core.gm
2 |
3 | import com.theapache64.gpm.core.TransactionManager
4 | import com.theapache64.gpm.data.remote.gpm.models.GpmDep
5 | import com.theapache64.gpm.models.GpmFileData
6 | import com.theapache64.gpm.utils.GradleUtils
7 | import com.theapache64.gpm.utils.StringUtils
8 | import com.theapache64.gpm.utils.insertAt
9 | import java.io.File
10 | import java.io.IOException
11 |
12 | /**
13 | * Currently supports 'implementation' and 'testImplementation' only.
14 | */
15 | class GradleManager constructor(
16 | private val transactionManager: TransactionManager,
17 | private val gradleFile: File
18 | ) {
19 |
20 | companion object {
21 | private val DEP_REGEX by lazy {
22 | "(?androidTestImplementation|testImplementation|implementation)\\s*\\(?[\"'](?.+?):(?.+?):(?.+?)[\"']\\)?".toRegex()
23 | }
24 |
25 | private const val KEY_DEP = "dependencies"
26 | }
27 |
28 | fun parseDeps(): List {
29 | val deps = mutableListOf()
30 | val fileContent = gradleFile.readText()
31 | val matchResults = DEP_REGEX.findAll(fileContent)
32 |
33 | for (result in matchResults) {
34 |
35 | val type = result.groups["type"]!!.value
36 | val groupId = result.groups["groupId"]!!.value
37 | val artifactId = result.groups["artifactId"]!!.value
38 | val version = result.groups["version"]!!.value
39 |
40 | val gdType = GradleDep.Type.values().find { it.key == type }
41 | ?: throw IllegalArgumentException("Couldn't find dependency type for '$type.'")
42 |
43 | deps.add(
44 | GradleDep(
45 | gdType,
46 | groupId,
47 | artifactId,
48 | version
49 | )
50 | )
51 | }
52 |
53 | return deps
54 | }
55 |
56 | /**
57 | * To add dependency
58 | */
59 | @Throws(IndexOutOfBoundsException::class)
60 | fun addDep(
61 | installedName: String,
62 | type: GradleDep.Type,
63 | newGpmDep: GpmDep
64 | ): String {
65 |
66 | val fileContent = gradleFile.readText()
67 | val name = newGpmDep.name
68 | val brokenDescription = StringUtils.breakOnAndComment(80, newGpmDep.description)
69 | .replace("\n", "\n\t")
70 |
71 | val isGradleKts = gradleFile.extension == "kts"
72 | val fullSignature = GradleUtils.getFullSignature(
73 | type.key,
74 | newGpmDep.groupId,
75 | newGpmDep.artifactId,
76 | newGpmDep.version!!,
77 | isGradleKts
78 | )
79 |
80 | val addedLines: String
81 |
82 | if (fileContent.contains(KEY_DEP)) {
83 |
84 | val newDepSign = "\n // $name : $brokenDescription\n $fullSignature\n"
85 |
86 | // Appending dependency
87 | val depIndex = fileContent.indexOf(KEY_DEP)
88 | val openIndex = fileContent.indexOf('{', depIndex)
89 | val closingIndex = StringUtils.getClosingIndexOf(fileContent, '{', openIndex, '}')
90 | val newContent = fileContent.insertAt(closingIndex, newDepSign)
91 | gradleFile.writeText(newContent)
92 |
93 | addedLines = newDepSign
94 |
95 | } else {
96 |
97 | // Adding first dependency
98 | val newDepSign = """// $name : $brokenDescription
99 | $fullSignature"""
100 |
101 | val firstDep = """
102 |
103 | // Project Dependencies
104 | dependencies {
105 |
106 | $newDepSign
107 | }
108 |
109 | """.trimIndent()
110 | gradleFile.appendText(firstDep)
111 |
112 | addedLines = newDepSign
113 | }
114 |
115 | transactionManager.add(installedName, type, newGpmDep)
116 |
117 | return addedLines
118 | }
119 |
120 | /**
121 | * To remove dependency
122 | */
123 | fun removeDep(depToRemove: GpmFileData.AddedDep) {
124 |
125 | val name = depToRemove.gpmDep.name
126 | val description = depToRemove.gpmDep.description
127 | val groupId = depToRemove.gpmDep.groupId
128 | val artifactId = depToRemove.gpmDep.artifactId
129 |
130 | val depRegEx = ("(?:\\s*\\/\\/\\s$name:$description)?\n" +
131 | "\\s*${depToRemove.type}\\s*\\(?['\"]$groupId:$artifactId:.+?['\"]\\)?").toRegex()
132 |
133 | val fileContent = gradleFile.readText()
134 | val newFileContent = fileContent.replace(depRegEx, "")
135 |
136 | if (fileContent.length != newFileContent.length) {
137 | // dep removed, update gradle file
138 | gradleFile.writeText(newFileContent)
139 |
140 | // remove the transaction also
141 | transactionManager.remove(depToRemove)
142 | } else {
143 | throw IOException(
144 | """
145 | Failed to remove dependency.
146 | Couldn't find `${depToRemove.type} '${groupId}:$artifactId:...'` in ${gradleFile.name}
147 | Signature might have changed.
148 | """.trimIndent()
149 | )
150 | }
151 | }
152 |
153 | }
--------------------------------------------------------------------------------
/src/test/resources/sample.build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | id 'com.android.application'
3 | id 'kotlin-android'
4 | id 'kotlin-kapt'
5 | id 'kotlin-android-extensions'
6 | id "org.jetbrains.kotlin.plugin.allopen" version "$kotlin_version"
7 | }
8 |
9 | allOpen {
10 | annotation("com.thinkpalm.test.movieapp.utils.test.OpenForTesting")
11 | }
12 |
13 | android {
14 | compileSdkVersion compile_sdk_version
15 | defaultConfig {
16 | applicationId "com.thinkpalm.test.movieapp"
17 | minSdkVersion min_sdk_version
18 | targetSdkVersion target_sdk_version
19 | versionCode 10001
20 | versionName "1.0.0-alpha01"
21 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
22 | multiDexEnabled true
23 | }
24 | buildTypes {
25 | release {
26 | minifyEnabled false
27 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
28 | }
29 | }
30 |
31 | buildFeatures {
32 | dataBinding true
33 | }
34 |
35 | compileOptions {
36 | sourceCompatibility JavaVersion.VERSION_1_8
37 | targetCompatibility JavaVersion.VERSION_1_8
38 | }
39 |
40 | kotlinOptions {
41 | jvmTarget = JavaVersion.VERSION_1_8.toString()
42 | }
43 |
44 | // To make `sharedTest` accessible from both unit test and instrumentation test
45 | sourceSets {
46 | String sharedTestDir = 'src/sharedTest/java'
47 | test {
48 | java.srcDir sharedTestDir
49 | }
50 | androidTest {
51 | java.srcDir sharedTestDir
52 | }
53 | }
54 |
55 | testOptions {
56 | unitTests {
57 | includeAndroidResources true
58 | returnDefaultValues true
59 | }
60 | }
61 | }
62 |
63 | dependencies {
64 |
65 | //Core
66 | implementation fileTree(dir: 'libs', include: ['*.jar'])
67 | implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
68 | implementation "androidx.appcompat:appcompat:$appcompat_version"
69 | implementation "androidx.core:core-ktx:$ktx_version"
70 | implementation "androidx.constraintlayout:constraintlayout:$constraint_version"
71 | implementation "com.google.android.material:material:$material_version"
72 |
73 | // Coroutines
74 | def coroutines_version = '1.3.5'
75 | implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutines_version"
76 | testImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-test:$coroutines_version"
77 | androidTestImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-test:$coroutines_version"
78 |
79 | // Lifecycle extension
80 | implementation "androidx.lifecycle:lifecycle-extensions:$lifecycle_version"
81 | implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version"
82 | implementation "androidx.lifecycle:lifecycle-livedata-ktx:$lifecycle_version"
83 |
84 | // Dagger 2
85 | def dagger_version = '2.27'
86 | implementation "com.google.dagger:dagger:$dagger_version"
87 | implementation "com.google.dagger:dagger-android-support:$dagger_version"
88 |
89 |
90 | // MaterialColors
91 | implementation 'com.theah64.materialcolors:materialcolors:1.0.0'
92 |
93 | // TwinKill
94 | def twinkill_version = '1.1.0-alpha05'
95 | implementation "com.theapache64.twinkill:core:$twinkill_version"
96 | testImplementation "com.theapache64.twinkill:test:$twinkill_version"
97 | androidTestImplementation "com.theapache64.twinkill:test:$twinkill_version"
98 | implementation "com.theapache64.twinkill:logger:$twinkill_version"
99 |
100 | // Moshi
101 | implementation "com.squareup.moshi:moshi-kotlin:$moshi_version"
102 |
103 | // Test
104 | testImplementation "junit:junit:$junit_version"
105 | testImplementation "org.mockito:mockito-core:$mockito_version"
106 | testImplementation "org.mockito:mockito-inline:$mockito_version"
107 | androidTestImplementation "org.mockito:mockito-android:$mockito_version"
108 | testImplementation('com.winterbe:expekt:0.5.0') {
109 | exclude group: "org.jetbrains.kotlin"
110 | }
111 |
112 | // Robolectric
113 | testImplementation "org.robolectric:robolectric:4.3.1"
114 |
115 | // Mocktio Kotlin
116 | testImplementation "com.nhaarman.mockitokotlin2:mockito-kotlin:2.2.0"
117 | androidTestImplementation "com.nhaarman.mockitokotlin2:mockito-kotlin:2.2.0"
118 |
119 | // Arch testing
120 | def core_testing_version = '2.1.0'
121 | testImplementation "androidx.arch.core:core-testing:$core_testing_version"
122 | androidTestImplementation "androidx.arch.core:core-testing:$core_testing_version"
123 | testImplementation 'androidx.test.ext:junit:1.1.1'
124 | androidTestImplementation 'androidx.test.ext:junit:1.1.1'
125 |
126 | // Espresso
127 | androidTestImplementation "androidx.test.espresso:espresso-core:$espresso_version"
128 | androidTestImplementation('com.schibsted.spain:barista:3.4.0') {
129 | exclude group: 'org.jetbrains.kotlin'
130 | }
131 |
132 | // DaggerMock
133 | testImplementation 'com.github.fabioCollini.daggermock:daggermock:0.8.5'
134 | androidTestImplementation 'com.github.fabioCollini.daggermock:daggermock:0.8.5'
135 | testImplementation 'com.github.fabioCollini.daggermock:daggermock-kotlin:0.8.5'
136 | androidTestImplementation 'com.github.fabioCollini.daggermock:daggermock-kotlin:0.8.5'
137 | }
138 |
--------------------------------------------------------------------------------
/src/test/kotlin/com/theapache64/gpm/commands/subcommands/uninstall/UninstallTest.kt:
--------------------------------------------------------------------------------
1 | package com.theapache64.gpm.commands.subcommands.uninstall
2 |
3 | import com.theapache64.gpm.core.gm.GradleDep
4 | import com.theapache64.gpm.core.gm.GradleManager
5 | import com.theapache64.gpm.data.remote.gpm.models.GpmDep
6 | import com.theapache64.gpm.di.modules.GradleModule
7 | import com.theapache64.gpm.di.modules.TransactionModule
8 | import com.winterbe.expekt.should
9 | import it.cosenonjaviste.daggermock.DaggerMock
10 | import org.junit.After
11 | import org.junit.Before
12 | import org.junit.Rule
13 | import org.junit.Test
14 | import picocli.CommandLine
15 | import java.io.File
16 | import java.io.PrintWriter
17 | import java.io.StringWriter
18 |
19 | class UninstallTest {
20 |
21 | private lateinit var gm: GradleManager
22 | private lateinit var gpmJsonFile: File
23 | private lateinit var tempBuildGradle: File
24 | private lateinit var uninstallCmd: CommandLine
25 | private val uninstall = Uninstall(true)
26 |
27 | @get:Rule
28 | val daggerMock = DaggerMock.rule() {
29 | customizeBuilder {
30 | it.gradleModule(GradleModule(true, modulePath = null))
31 | .transactionModule(TransactionModule(true))
32 | }
33 | set {
34 | tempBuildGradle = it.gradleFile()
35 | gpmJsonFile = it.gpmJsonFile()
36 | gm = it.gradleManager()
37 | it.inject(uninstall)
38 | }
39 | }
40 |
41 |
42 | @Before
43 | fun setUp() {
44 | this.uninstallCmd = CommandLine(uninstall).apply {
45 | out = PrintWriter(StringWriter())
46 | }
47 | }
48 |
49 | @After
50 | fun tearDown() {
51 | tempBuildGradle.delete()
52 | gpmJsonFile.delete()
53 | }
54 |
55 | @Test
56 | fun `Uninstall installed dependency`() {
57 |
58 | // Adding manual dependency
59 | gm.addDep(
60 | "materialcolors",
61 | GradleDep.Type.IMP,
62 | GpmDep(
63 | "materialcolors",
64 | GradleDep.Type.IMP.key,
65 | "https://materialcolors.github.io/materialcolors/",
66 | "jcenter",
67 | "https://github.com/square/materialcolors/",
68 | "com.theah64.materialcolors",
69 | "Material Colors",
70 | "A material color library",
71 | "1.0.0"
72 | )
73 | )
74 |
75 |
76 | // Uninstall dep
77 | val uninstallExitCode = uninstallCmd.execute("materialcolors")
78 | uninstallExitCode.should.equal(UninstallViewModel.RESULT_DEP_UNINSTALLED)
79 | }
80 |
81 | @Test
82 | fun `Uninstall dependency which is manually added`() {
83 | // Uninstall dep
84 | val uninstallExitCode = uninstallCmd.execute("robolectric")
85 | uninstallExitCode.should.equal(UninstallViewModel.RESULT_NO_DEP_INSTALLED)
86 | }
87 |
88 | @Test
89 | fun `Uninstall not installed dependency`() {
90 | val uninstallExitCode = uninstallCmd.execute("invalid-library")
91 | uninstallExitCode.should.equal(UninstallViewModel.RESULT_NO_DEP_INSTALLED)
92 | }
93 |
94 | @Test
95 | fun `Uninstall dependency which installed through same dependency name`() {
96 |
97 | gm.addDep(
98 | "same-name",
99 | GradleDep.Type.IMP,
100 | GpmDep(
101 | "same-name-1",
102 | GradleDep.Type.IMP.key,
103 | "",
104 | "",
105 | "",
106 | "com.theah64.same-name",
107 | "Same Name 1",
108 | "Same Name 1",
109 | "1.0.0"
110 | )
111 | )
112 |
113 | gm.addDep(
114 | "same-name",
115 | GradleDep.Type.IMP,
116 | GpmDep(
117 | "same-name-2",
118 | GradleDep.Type.IMP.key,
119 | "",
120 | "",
121 | "",
122 | "com.theah64.same-name",
123 | "Same Name 2",
124 | "Same Name 2",
125 | "1.0.0"
126 | )
127 | )
128 |
129 |
130 | val uninstallExitCode = uninstallCmd.execute("same-name")
131 | uninstallExitCode.should.equal(UninstallViewModel.RESULT_DEP_UNINSTALLED)
132 | }
133 |
134 | @Test
135 | fun `Uninstall testImplementation`() {
136 |
137 | val libName = "some-testing-library"
138 | gm.addDep(
139 | libName,
140 | GradleDep.Type.TEST_IMP,
141 | GpmDep(
142 | libName,
143 | GradleDep.Type.TEST_IMP.key,
144 | "",
145 | "",
146 | "",
147 | "com.theah64.some-testing-library",
148 | "Some Testing Library",
149 | "Some testing lib for JVM",
150 | "1.0.0"
151 | )
152 | )
153 |
154 |
155 | val uninstallExitCode = uninstallCmd.execute("--save-dev", libName)
156 | uninstallExitCode.should.equal(UninstallViewModel.RESULT_DEP_UNINSTALLED)
157 | }
158 |
159 | @Test
160 | fun `Uninstall androidTestImplementation`() {
161 | val libName = "some-testing-library"
162 | gm.addDep(
163 | libName,
164 | GradleDep.Type.AND_TEST_IMP,
165 | GpmDep(
166 | libName,
167 | GradleDep.Type.AND_TEST_IMP.key,
168 | "",
169 | "",
170 | "",
171 | "com.theah64.some-testing-library",
172 | "Some Testing Library",
173 | "Some testing lib for Android",
174 | "1.0.0"
175 | )
176 | )
177 |
178 |
179 | val uninstallExitCode = uninstallCmd.execute("--save-dev-android", libName)
180 | uninstallExitCode.should.equal(UninstallViewModel.RESULT_DEP_UNINSTALLED)
181 | }
182 | }
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | #
4 | # Copyright 2015 the original author or authors.
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # https://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 | #
18 |
19 | ##############################################################################
20 | ##
21 | ## Gradle start up script for UN*X
22 | ##
23 | ##############################################################################
24 |
25 | # Attempt to set APP_HOME
26 | # Resolve links: $0 may be a link
27 | PRG="$0"
28 | # Need this for relative symlinks.
29 | while [ -h "$PRG" ] ; do
30 | ls=`ls -ld "$PRG"`
31 | link=`expr "$ls" : '.*-> \(.*\)$'`
32 | if expr "$link" : '/.*' > /dev/null; then
33 | PRG="$link"
34 | else
35 | PRG=`dirname "$PRG"`"/$link"
36 | fi
37 | done
38 | SAVED="`pwd`"
39 | cd "`dirname \"$PRG\"`/" >/dev/null
40 | APP_HOME="`pwd -P`"
41 | cd "$SAVED" >/dev/null
42 |
43 | APP_NAME="Gradle"
44 | APP_BASE_NAME=`basename "$0"`
45 |
46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
48 |
49 | # Use the maximum available, or set MAX_FD != -1 to use that value.
50 | MAX_FD="maximum"
51 |
52 | warn () {
53 | echo "$*"
54 | }
55 |
56 | die () {
57 | echo
58 | echo "$*"
59 | echo
60 | exit 1
61 | }
62 |
63 | # OS specific support (must be 'true' or 'false').
64 | cygwin=false
65 | msys=false
66 | darwin=false
67 | nonstop=false
68 | case "`uname`" in
69 | CYGWIN* )
70 | cygwin=true
71 | ;;
72 | Darwin* )
73 | darwin=true
74 | ;;
75 | MINGW* )
76 | msys=true
77 | ;;
78 | NONSTOP* )
79 | nonstop=true
80 | ;;
81 | esac
82 |
83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
84 |
85 | # Determine the Java command to use to start the JVM.
86 | if [ -n "$JAVA_HOME" ] ; then
87 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
88 | # IBM's JDK on AIX uses strange locations for the executables
89 | JAVACMD="$JAVA_HOME/jre/sh/java"
90 | else
91 | JAVACMD="$JAVA_HOME/bin/java"
92 | fi
93 | if [ ! -x "$JAVACMD" ] ; then
94 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
95 |
96 | Please set the JAVA_HOME variable in your environment to match the
97 | location of your Java installation."
98 | fi
99 | else
100 | JAVACMD="java"
101 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
102 |
103 | Please set the JAVA_HOME variable in your environment to match the
104 | location of your Java installation."
105 | fi
106 |
107 | # Increase the maximum file descriptors if we can.
108 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
109 | MAX_FD_LIMIT=`ulimit -H -n`
110 | if [ $? -eq 0 ] ; then
111 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
112 | MAX_FD="$MAX_FD_LIMIT"
113 | fi
114 | ulimit -n $MAX_FD
115 | if [ $? -ne 0 ] ; then
116 | warn "Could not set maximum file descriptor limit: $MAX_FD"
117 | fi
118 | else
119 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
120 | fi
121 | fi
122 |
123 | # For Darwin, add options to specify how the application appears in the dock
124 | if $darwin; then
125 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
126 | fi
127 |
128 | # For Cygwin or MSYS, switch paths to Windows format before running java
129 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
130 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
131 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
132 | JAVACMD=`cygpath --unix "$JAVACMD"`
133 |
134 | # We build the pattern for arguments to be converted via cygpath
135 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
136 | SEP=""
137 | for dir in $ROOTDIRSRAW ; do
138 | ROOTDIRS="$ROOTDIRS$SEP$dir"
139 | SEP="|"
140 | done
141 | OURCYGPATTERN="(^($ROOTDIRS))"
142 | # Add a user-defined pattern to the cygpath arguments
143 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
144 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
145 | fi
146 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
147 | i=0
148 | for arg in "$@" ; do
149 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
150 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
151 |
152 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
153 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
154 | else
155 | eval `echo args$i`="\"$arg\""
156 | fi
157 | i=`expr $i + 1`
158 | done
159 | case $i in
160 | 0) set -- ;;
161 | 1) set -- "$args0" ;;
162 | 2) set -- "$args0" "$args1" ;;
163 | 3) set -- "$args0" "$args1" "$args2" ;;
164 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;;
165 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
166 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
167 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
168 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
169 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
170 | esac
171 | fi
172 |
173 | # Escape application args
174 | save () {
175 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
176 | echo " "
177 | }
178 | APP_ARGS=`save "$@"`
179 |
180 | # Collect all arguments for the java command, following the shell quoting and substitution rules
181 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
182 |
183 | exec "$JAVACMD" "$@"
184 |
--------------------------------------------------------------------------------
/gpm_completion:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | #
3 | # gpm Bash Completion
4 | # =======================
5 | #
6 | # Bash completion support for the `gpm` command,
7 | # generated by [picocli](http://picocli.info/) version 4.2.0.
8 | #
9 | # Installation
10 | # ------------
11 | #
12 | # 1. Source all completion scripts in your .bash_profile
13 | #
14 | # cd $YOUR_APP_HOME/bin
15 | # for f in $(find . -name "*_completion"); do line=". $(pwd)/$f"; grep "$line" ~/.bash_profile || echo "$line" >> ~/.bash_profile; done
16 | #
17 | # 2. Open a new bash console, and type `gpm [TAB][TAB]`
18 | #
19 | # 1a. Alternatively, if you have [bash-completion](https://github.com/scop/bash-completion) installed:
20 | # Place this file in a `bash-completion.d` folder:
21 | #
22 | # * /etc/bash-completion.d
23 | # * /usr/local/etc/bash-completion.d
24 | # * ~/bash-completion.d
25 | #
26 | # Documentation
27 | # -------------
28 | # The script is called by bash whenever [TAB] or [TAB][TAB] is pressed after
29 | # 'gpm (..)'. By reading entered command line parameters,
30 | # it determines possible bash completions and writes them to the COMPREPLY variable.
31 | # Bash then completes the user input if only one entry is listed in the variable or
32 | # shows the options if more than one is listed in COMPREPLY.
33 | #
34 | # References
35 | # ----------
36 | # [1] http://stackoverflow.com/a/12495480/1440785
37 | # [2] http://tiswww.case.edu/php/chet/bash/FAQ
38 | # [3] https://www.gnu.org/software/bash/manual/html_node/The-Shopt-Builtin.html
39 | # [4] http://zsh.sourceforge.net/Doc/Release/Options.html#index-COMPLETE_005fALIASES
40 | # [5] https://stackoverflow.com/questions/17042057/bash-check-element-in-array-for-elements-in-another-array/17042655#17042655
41 | # [6] https://www.gnu.org/software/bash/manual/html_node/Programmable-Completion.html#Programmable-Completion
42 | # [7] https://stackoverflow.com/questions/3249432/can-a-bash-tab-completion-script-be-used-in-zsh/27853970#27853970
43 | #
44 |
45 | if [ -n "$BASH_VERSION" ]; then
46 | # Enable programmable completion facilities when using bash (see [3])
47 | shopt -s progcomp
48 | elif [ -n "$ZSH_VERSION" ]; then
49 | # Make alias a distinct command for completion purposes when using zsh (see [4])
50 | setopt COMPLETE_ALIASES
51 | alias compopt=complete
52 |
53 | # Enable bash completion in zsh (see [7])
54 | autoload -U +X compinit && compinit
55 | autoload -U +X bashcompinit && bashcompinit
56 | fi
57 |
58 | # CompWordsContainsArray takes an array and then checks
59 | # if all elements of this array are in the global COMP_WORDS array.
60 | #
61 | # Returns zero (no error) if all elements of the array are in the COMP_WORDS array,
62 | # otherwise returns 1 (error).
63 | function CompWordsContainsArray() {
64 | declare -a localArray
65 | localArray=("$@")
66 | local findme
67 | for findme in "${localArray[@]}"; do
68 | if ElementNotInCompWords "$findme"; then return 1; fi
69 | done
70 | return 0
71 | }
72 | function ElementNotInCompWords() {
73 | local findme="$1"
74 | local element
75 | for element in "${COMP_WORDS[@]}"; do
76 | if [[ "$findme" = "$element" ]]; then return 1; fi
77 | done
78 | return 0
79 | }
80 |
81 | # The `currentPositionalIndex` function calculates the index of the current positional parameter.
82 | #
83 | # currentPositionalIndex takes three parameters:
84 | # the command name,
85 | # a space-separated string with the names of options that take a parameter, and
86 | # a space-separated string with the names of boolean options (that don't take any params).
87 | # When done, this function echos the current positional index to std_out.
88 | #
89 | # Example usage:
90 | # local currIndex=$(currentPositionalIndex "mysubcommand" "$ARG_OPTS" "$FLAG_OPTS")
91 | function currentPositionalIndex() {
92 | local commandName="$1"
93 | local optionsWithArgs="$2"
94 | local booleanOptions="$3"
95 | local previousWord
96 | local result=0
97 |
98 | for i in $(seq $((COMP_CWORD - 1)) -1 0); do
99 | previousWord=${COMP_WORDS[i]}
100 | if [ "${previousWord}" = "$commandName" ]; then
101 | break
102 | fi
103 | if [[ "${optionsWithArgs}" =~ ${previousWord} ]]; then
104 | ((result-=2)) # Arg option and its value not counted as positional param
105 | elif [[ "${booleanOptions}" =~ ${previousWord} ]]; then
106 | ((result-=1)) # Flag option itself not counted as positional param
107 | fi
108 | ((result++))
109 | done
110 | echo "$result"
111 | }
112 |
113 | # Bash completion entry point function.
114 | # _complete_gpm finds which commands and subcommands have been specified
115 | # on the command line and delegates to the appropriate function
116 | # to generate possible options and subcommands for the last specified subcommand.
117 | function _complete_gpm() {
118 | local cmds0=(install)
119 | local cmds1=(i)
120 | local cmds2=(uninstall)
121 | local cmds3=(u)
122 | local cmds4=(docs)
123 | local cmds5=(d)
124 |
125 | if CompWordsContainsArray "${cmds5[@]}"; then _picocli_gpm_d; return $?; fi
126 | if CompWordsContainsArray "${cmds4[@]}"; then _picocli_gpm_docs; return $?; fi
127 | if CompWordsContainsArray "${cmds3[@]}"; then _picocli_gpm_u; return $?; fi
128 | if CompWordsContainsArray "${cmds2[@]}"; then _picocli_gpm_uninstall; return $?; fi
129 | if CompWordsContainsArray "${cmds1[@]}"; then _picocli_gpm_i; return $?; fi
130 | if CompWordsContainsArray "${cmds0[@]}"; then _picocli_gpm_install; return $?; fi
131 |
132 | # No subcommands were specified; generate completions for the top-level command.
133 | _picocli_gpm; return $?;
134 | }
135 |
136 | # Generates completions for the options and subcommands of the `gpm` command.
137 | function _picocli_gpm() {
138 | # Get completion data
139 | local curr_word=${COMP_WORDS[COMP_CWORD]}
140 |
141 | local commands="install i uninstall u docs d"
142 | local flag_opts="-h --help -V --version"
143 | local arg_opts=""
144 |
145 | if [[ "${curr_word}" == -* ]]; then
146 | COMPREPLY=( $(compgen -W "${flag_opts} ${arg_opts}" -- "${curr_word}") )
147 | else
148 | local positionals=""
149 | COMPREPLY=( $(compgen -W "${commands} ${positionals}" -- "${curr_word}") )
150 | fi
151 | }
152 |
153 | # Generates completions for the options and subcommands of the `install` subcommand.
154 | function _picocli_gpm_install() {
155 | # Get completion data
156 | local curr_word=${COMP_WORDS[COMP_CWORD]}
157 |
158 | local commands=""
159 | local flag_opts="-S --save -D --save-dev -DA --save-dev-android -K --kapt"
160 | local arg_opts=""
161 |
162 | if [[ "${curr_word}" == -* ]]; then
163 | COMPREPLY=( $(compgen -W "${flag_opts} ${arg_opts}" -- "${curr_word}") )
164 | else
165 | local positionals=""
166 | COMPREPLY=( $(compgen -W "${commands} ${positionals}" -- "${curr_word}") )
167 | fi
168 | }
169 |
170 | # Generates completions for the options and subcommands of the `i` subcommand.
171 | function _picocli_gpm_i() {
172 | # Get completion data
173 | local curr_word=${COMP_WORDS[COMP_CWORD]}
174 |
175 | local commands=""
176 | local flag_opts="-S --save -D --save-dev -DA --save-dev-android -K --kapt"
177 | local arg_opts=""
178 |
179 | if [[ "${curr_word}" == -* ]]; then
180 | COMPREPLY=( $(compgen -W "${flag_opts} ${arg_opts}" -- "${curr_word}") )
181 | else
182 | local positionals=""
183 | COMPREPLY=( $(compgen -W "${commands} ${positionals}" -- "${curr_word}") )
184 | fi
185 | }
186 |
187 | # Generates completions for the options and subcommands of the `uninstall` subcommand.
188 | function _picocli_gpm_uninstall() {
189 | # Get completion data
190 | local curr_word=${COMP_WORDS[COMP_CWORD]}
191 |
192 | local commands=""
193 | local flag_opts="-S --save -D --save-dev -DA --save-dev-android -K --kapt"
194 | local arg_opts=""
195 |
196 | if [[ "${curr_word}" == -* ]]; then
197 | COMPREPLY=( $(compgen -W "${flag_opts} ${arg_opts}" -- "${curr_word}") )
198 | else
199 | local positionals=""
200 | COMPREPLY=( $(compgen -W "${commands} ${positionals}" -- "${curr_word}") )
201 | fi
202 | }
203 |
204 | # Generates completions for the options and subcommands of the `u` subcommand.
205 | function _picocli_gpm_u() {
206 | # Get completion data
207 | local curr_word=${COMP_WORDS[COMP_CWORD]}
208 |
209 | local commands=""
210 | local flag_opts="-S --save -D --save-dev -DA --save-dev-android -K --kapt"
211 | local arg_opts=""
212 |
213 | if [[ "${curr_word}" == -* ]]; then
214 | COMPREPLY=( $(compgen -W "${flag_opts} ${arg_opts}" -- "${curr_word}") )
215 | else
216 | local positionals=""
217 | COMPREPLY=( $(compgen -W "${commands} ${positionals}" -- "${curr_word}") )
218 | fi
219 | }
220 |
221 | # Generates completions for the options and subcommands of the `docs` subcommand.
222 | function _picocli_gpm_docs() {
223 | # Get completion data
224 | local curr_word=${COMP_WORDS[COMP_CWORD]}
225 |
226 | local commands=""
227 | local flag_opts=""
228 | local arg_opts=""
229 |
230 | if [[ "${curr_word}" == -* ]]; then
231 | COMPREPLY=( $(compgen -W "${flag_opts} ${arg_opts}" -- "${curr_word}") )
232 | else
233 | local positionals=""
234 | COMPREPLY=( $(compgen -W "${commands} ${positionals}" -- "${curr_word}") )
235 | fi
236 | }
237 |
238 | # Generates completions for the options and subcommands of the `d` subcommand.
239 | function _picocli_gpm_d() {
240 | # Get completion data
241 | local curr_word=${COMP_WORDS[COMP_CWORD]}
242 |
243 | local commands=""
244 | local flag_opts=""
245 | local arg_opts=""
246 |
247 | if [[ "${curr_word}" == -* ]]; then
248 | COMPREPLY=( $(compgen -W "${flag_opts} ${arg_opts}" -- "${curr_word}") )
249 | else
250 | local positionals=""
251 | COMPREPLY=( $(compgen -W "${commands} ${positionals}" -- "${curr_word}") )
252 | fi
253 | }
254 |
255 | # Define a completion specification (a compspec) for the
256 | # `gpm`, `gpm.sh`, and `gpm.bash` commands.
257 | # Uses the bash `complete` builtin (see [6]) to specify that shell function
258 | # `_complete_gpm` is responsible for generating possible completions for the
259 | # current word on the command line.
260 | # The `-o default` option means that if the function generated no matches, the
261 | # default Bash completions and the Readline default filename completions are performed.
262 | complete -F _complete_gpm -o default gpm gpm.sh gpm.bash
263 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "[]"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright [yyyy] [name of copyright owner]
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------
/jdeploy-bundle/jdeploy.js:
--------------------------------------------------------------------------------
1 | #! /usr/bin/env node
2 |
3 | var jarName = "gpm.main.jar";
4 | var mainClass = "{{MAIN_CLASS}}";
5 | var classPath = "{{CLASSPATH}}";
6 | var port = "0";
7 | var warPath = "";
8 | var javaVersionString = "17";
9 | var tryJavaHomeFirst = false;
10 | var javafx = false;
11 | var bundleType = 'jre';
12 | if ('{{JAVAFX}}' === 'true') {
13 | javafx = true;
14 | }
15 | if ('{{JDK}}' === 'true') {
16 | bundleType = 'jdk';
17 | }
18 |
19 | var jdk = (bundleType === 'jdk');
20 | var jdkProvider = 'zulu';
21 |
22 |
23 | function njreWrap() {
24 | 'use strict'
25 |
26 | const path = require('path')
27 | const fs = require('fs')
28 | const os = require('os')
29 | const crypto = require('crypto')
30 | const fetch = require('node-fetch')
31 | const yauzl = require('yauzl')
32 | const tar = require('tar')
33 |
34 | function createDir (dir) {
35 | return new Promise((resolve, reject) => {
36 | fs.access(dir, err => {
37 | if (err && err.code === 'ENOENT') {
38 | fs.mkdir(dir, err => {
39 | if (err) reject(err)
40 | resolve()
41 | })
42 | } else if (!err) resolve()
43 | else reject(err)
44 | })
45 | })
46 | }
47 |
48 | function download (dir, url) {
49 | if (url.indexOf("?") > 0 || jdkProvider === 'zulu') {
50 | var ext = ".zip";
51 | switch (process.platform) {
52 | case 'linux':
53 | ext = ".tar.gz";
54 | break;
55 | }
56 | var destName = bundleType + ext;
57 | } else {
58 | destName = path.basename(url);
59 | }
60 |
61 | return new Promise((resolve, reject) => {
62 | createDir(dir)
63 | .then(() => fetch(url))
64 | .then(response => {
65 | const destFile = path.join(dir, destName)
66 | const destStream = fs.createWriteStream(destFile)
67 | response.body.pipe(destStream).on('finish', () => resolve(destFile))
68 | })
69 | .catch(err => reject(err))
70 | })
71 | }
72 |
73 | function downloadAll (dir, url) {
74 | return download(dir, url + '.sha256.txt').then(() => download(dir, url))
75 | }
76 |
77 | function genChecksum (file) {
78 | return new Promise((resolve, reject) => {
79 | fs.readFile(file, (err, data) => {
80 | if (err) reject(err)
81 |
82 | resolve(
83 | crypto
84 | .createHash('sha256')
85 | .update(data)
86 | .digest('hex')
87 | )
88 | })
89 | })
90 | }
91 |
92 | function verify (file) {
93 | return new Promise((resolve, reject) => {
94 | fs.readFile(file + '.sha256.txt', 'utf-8', (err, data) => {
95 | if (err) reject(err)
96 |
97 | genChecksum(file).then(checksum => {
98 | checksum === data.split(' ')[0]
99 | ? resolve(file)
100 | : reject(new Error('File and checksum don\'t match'))
101 | })
102 | })
103 | })
104 | }
105 |
106 | function move (file) {
107 | return new Promise((resolve, reject) => {
108 | const jdeployDir = path.join(os.homedir(), '.jdeploy');
109 | if (!fs.existsSync(jdeployDir)) {
110 | fs.mkdirSync(jdeployDir);
111 | }
112 |
113 | var jreDir = path.join(jdeployDir, bundleType);
114 | if (!fs.existsSync(jreDir)) {
115 | fs.mkdirSync(jreDir);
116 | }
117 | var vs = javaVersionString;
118 | if (javafx) {
119 | vs += 'fx';
120 | }
121 | jreDir = path.join(jreDir, vs);
122 | if (!fs.existsSync(jreDir)) {
123 | fs.mkdirSync(jreDir);
124 | }
125 | const newFile = path.join(jreDir, file.split(path.sep).slice(-1)[0])
126 | //console.log("Copying file "+file+" to "+newFile);
127 | fs.copyFile(file, newFile, err => {
128 | if (err) reject(err)
129 |
130 | fs.unlink(file, err => {
131 | if (err) reject(err)
132 | resolve(newFile)
133 | })
134 | })
135 | })
136 | }
137 |
138 | function extractZip (file, dir) {
139 | //console.log("Extracting "+file+" to "+dir);
140 | return new Promise((resolve, reject) => {
141 | yauzl.open(file, { lazyEntries: true }, (err, zipFile) => {
142 | if (err) reject(err)
143 |
144 | zipFile.readEntry()
145 | zipFile.on('entry', entry => {
146 | const entryPath = path.join(dir, entry.fileName)
147 |
148 | if (/\/$/.test(entry.fileName)) {
149 | fs.mkdir(entryPath, { recursive: true }, err => {
150 | if (err && err.code !== 'EEXIST') reject(err)
151 |
152 | zipFile.readEntry()
153 | })
154 | } else {
155 | zipFile.openReadStream(entry, (err, readStream) => {
156 | if (err) reject(err)
157 |
158 | readStream.on('end', () => {
159 | zipFile.readEntry()
160 | })
161 | readStream.pipe(fs.createWriteStream(entryPath))
162 | })
163 | }
164 | })
165 | zipFile.once('close', () => {
166 | fs.unlink(file, err => {
167 | if (err) reject(err)
168 | resolve(dir)
169 | })
170 | })
171 | })
172 | })
173 | }
174 |
175 | function extractTarGz (file, dir) {
176 | return tar.x({ file: file, cwd: dir }).then(() => {
177 | return new Promise((resolve, reject) => {
178 | fs.unlink(file, err => {
179 | if (err) reject(err)
180 | resolve(dir)
181 | })
182 | })
183 | })
184 | }
185 |
186 | function extract (file) {
187 | var dirString = jdk? 'jdk' : 'jre';
188 |
189 | const dir = path.join(path.dirname(file), dirString)
190 | //console.log("About to extract "+file+" to "+dir);
191 | return createDir(dir).then(() => {
192 | return path.extname(file) === '.zip'
193 | ? extractZip(file, dir)
194 | : extractTarGz(file, dir)
195 | })
196 | }
197 |
198 | /**
199 | * Installs a JRE copy for the app
200 | * @param {number} [version = 8] - Java Version (`8`/`9`/`10`/`11`/`12`)
201 | * @param {object} [options] - Installation Options
202 | * @param {string} [options.os] - Operating System (defaults to current) (`windows`/`mac`/`linux`/`solaris`/`aix`)
203 | * @param {string} [options.arch] - Architecture (defaults to current) (`x64`/`x32`/`ppc64`/`s390x`/`ppc64le`/`aarch64`/`sparcv9`)
204 | * @param {string} [options.openjdk_impl = hotspot] - OpenJDK Implementation (`hotspot`/`openj9`)
205 | * @param {string} [options.release = latest] - Release
206 | * @param {string} [options.type = jre] - Binary Type (`jre`/`jdk`)
207 | * @param {string} [options.heap_size] - Heap Size (`normal`/`large`)
208 | * @return Promise - Resolves to the installation directory or rejects an error
209 | * @example
210 | * const njre = require('njre')
211 | *
212 | * // Use default options
213 | * njre.install()
214 | * .then(dir => {
215 | * // Do stuff
216 | * })
217 | * .catch(err => {
218 | * // Handle the error
219 | * })
220 | *
221 | * // or custom ones
222 | * njre.install(11, { os: 'aix', arch: 'ppc64', openjdk_impl: 'openj9' })
223 | * .then(dir => {
224 | * // Do stuff
225 | * })
226 | * .catch(err => {
227 | * // Handle the error
228 | * })
229 | */
230 | function install (version = 11, options = {}) {
231 | const { openjdk_impl = 'hotspot', release = 'latest', type = 'jre', javafx = false, provider = 'zulu' } = options
232 | options = { ...options, openjdk_impl, release, type }
233 |
234 | if (provider === 'zulu') {
235 | return installZulu(version, options);
236 | }
237 |
238 | let url = 'https://api.adoptopenjdk.net/v2/info/releases/openjdk' + version + '?'
239 |
240 | if (!options.os) {
241 | switch (process.platform) {
242 | case 'aix':
243 | options.os = 'aix'
244 | break
245 | case 'darwin':
246 | options.os = 'mac'
247 | break
248 | case 'linux':
249 | options.os = 'linux'
250 | break
251 | case 'sunos':
252 | options.os = 'solaris'
253 | break
254 | case 'win32':
255 | options.os = 'windows'
256 | break
257 | default:
258 | return Promise.reject(new Error('Unsupported operating system'))
259 | }
260 | }
261 | if (!options.arch) {
262 | if (options.os == 'mac') {
263 | // For now, for compatibility reasons use x64 always
264 | options.arch = 'x64';
265 | } else if (/^ppc64|s390x|x32|x64$/g.test(process.arch)) options.arch = process.arch
266 | else if (process.arch === 'ia32') options.arch = 'x32'
267 | else return Promise.reject(new Error('Unsupported architecture'))
268 | }
269 |
270 | Object.keys(options).forEach(key => { url += key + '=' + options[key] + '&' })
271 |
272 | const tmpdir = path.join(os.tmpdir(), 'njre')
273 |
274 | return fetch(url)
275 | .then(response => response.json())
276 | .then(json => downloadAll(tmpdir, json.binaries[0]['binary_link']))
277 | .then(verify)
278 | .then(move)
279 | .then(extract)
280 | }
281 |
282 | function installZulu(version = 11, options = {}) {
283 | const { type = 'jre', javafx = false } = options
284 | var q = {
285 |
286 | java_version: version,
287 | ext: 'zip',
288 | bundle_type: type,
289 | javafx: ''+javafx,
290 | arch: 'x86',
291 | hw_bitness: '64',
292 |
293 | };
294 |
295 |
296 | var zuluBaseURL = "https://api.azul.com/zulu/download/community/v1.0/bundles/latest/binary?"
297 | if (!options.os) {
298 | switch (process.platform) {
299 |
300 | case 'darwin':
301 | q.os = 'macos'
302 | break
303 | case 'linux':
304 | q.os = 'linux'
305 | q.ext = 'tar.gz'
306 | break
307 |
308 | case 'win32':
309 | case 'win64':
310 | q.os = 'windows'
311 | break
312 | default:
313 | return Promise.reject(new Error('Unsupported operating system'))
314 | }
315 | }
316 |
317 |
318 | var url = zuluBaseURL;
319 | Object.keys(q).forEach(key => { url += key + '=' + q[key] + '&' })
320 | const tmpdir = path.join(os.tmpdir(), 'njre')
321 | //console.log("Downloading "+url);
322 | return download(tmpdir, url)
323 | .then(move)
324 | .then(extract)
325 |
326 | }
327 |
328 | return {install:install};
329 |
330 |
331 |
332 | }
333 |
334 |
335 | var fs = require('fs');
336 | var os = require('os');
337 | var path = require('path');
338 | const njre = njreWrap();
339 | const targetJavaVersion = parseInt(javaVersionString);
340 | var shell = require("shelljs/global");
341 | function getJdeploySupportDir() {
342 | return os.homedir() + path.sep + ".jdeploy";
343 | }
344 |
345 | function getJavaVersion(binPath) {
346 |
347 | var oldPath = env['PATH'];
348 | if (binPath) {
349 | env['PATH'] = binPath + path.delimiter + env['PATH'];
350 | }
351 |
352 | try {
353 | var javaVersionProc = exec('java -version', {silent:true});
354 | if (javaVersionProc.code !== 0) {
355 | return false;
356 | }
357 | var stdout = javaVersionProc.stderr;
358 | var regexp = /version "(.*?)"/;
359 | var match = regexp.exec(stdout);
360 | var parts = match[1].split('.');
361 | var join = '.';
362 | var versionStr = '';
363 | parts.forEach(function(v) {
364 | versionStr += v;
365 | if (join !== null) {
366 | versionStr += join;
367 | join = null;
368 | }
369 | });
370 | versionStr = versionStr.replace('_', '');
371 | return parseFloat(versionStr);
372 | } catch (e) {
373 | return false;
374 | } finally {
375 | env['PATH'] = oldPath;
376 | }
377 | }
378 | var getDirectories = dirPath => fs.readdirSync(dirPath).filter(
379 | file => fs.statSync(path.join(dirPath, file)).isDirectory()
380 | );
381 |
382 | function getJavaHomeInPath(basepath) {
383 |
384 | var dirs = null;
385 | try {
386 | dirs = getDirectories(basepath);
387 | } catch (e) {
388 | return null;
389 | }
390 | if (dirs && dirs.length > 0) {
391 | basepath = path.join(basepath, dirs[0]);
392 | if (os.platform() != 'darwin') {
393 | return basepath;
394 | }
395 | if (fs.existsSync(path.join(basepath, 'Contents', 'Home'))) {
396 | return path.join(basepath, 'Contents', 'Home');
397 | }
398 |
399 | var adapterDirectories = getDirectories(basepath).filter(subdir => {
400 | return subdir.match(/^zulu/) && fs.existsSync(path.join(basepath, subdir, 'Contents', 'Home'));
401 | });
402 |
403 | if (adapterDirectories && adapterDirectories.length > 0) {
404 | return path.join(basepath, adapterDirectories[0], 'Contents', 'Home');
405 | }
406 | }
407 | return null;
408 | }
409 |
410 | function findSupportedRuntime(javaVersion, jdk, javafx) {
411 | var jdeployDir = path.join(os.homedir(), ".jdeploy");
412 | var JAVA_HOME_OVERRIDE = env['JDEPLOY_JAVA_HOME_OVERRIDE'];
413 |
414 | if (JAVA_HOME_OVERRIDE && fs.existsSync(JAVA_HOME_OVERRIDE)) {
415 | return JAVA_HOME_OVERRIDE;
416 | }
417 |
418 | // First check for the full-meal deal
419 | var _javaHomePath = getJavaHomeInPath(path.join(jdeployDir, 'jdk', javaVersion+'fx', 'jdk'));
420 | if (_javaHomePath && fs.existsSync(_javaHomePath)) {
421 | return _javaHomePath;
422 | }
423 | if (!javafx) {
424 | var _javaHomePath = getJavaHomeInPath(path.join(jdeployDir, 'jdk', javaVersion, 'jdk'));
425 | if (_javaHomePath && fs.existsSync(_javaHomePath)) {
426 | return _javaHomePath;
427 | }
428 | }
429 |
430 | if (!jdk) {
431 | var _javaHomePath = getJavaHomeInPath(path.join(jdeployDir, 'jre', javaVersion+'fx', 'jre'));
432 | if (_javaHomePath && fs.existsSync(_javaHomePath)) {
433 | return _javaHomePath;
434 | }
435 | }
436 |
437 | if (!jdk && !javafx) {
438 | var _javaHomePath = getJavaHomeInPath(path.join(jdeployDir, 'jre', javaVersion, 'jre'));
439 | if (_javaHomePath && fs.existsSync(_javaHomePath)) {
440 | return _javaHomePath;
441 | }
442 | }
443 | return null;
444 |
445 | }
446 |
447 | function getEmbeddedJavaHome() {
448 | var _platform = os.platform();
449 | var _driver = '';
450 | switch (_platform) {
451 | case 'darwin': _platform = 'macosx'; _driver = 'Contents' + path.sep + 'Home'; break;
452 | case 'win32': _platform = 'windows'; _driver = ''; break;
453 | case 'linux': _driver = ''; break;
454 | default:
455 | fail('unsupported platform: ' + _platform);
456 | }
457 | var vs = javaVersionString;
458 | if (javafx) {
459 | vs += 'fx';
460 | }
461 | var typeDir = jdk ? 'jdk' : 'jre';
462 |
463 | var jreDir = path.join(os.homedir(), '.jdeploy', 'jre', vs, 'jre');
464 | try {
465 | var out = jreDir + path.sep + getDirectories(jreDir)[0] + (_driver ? (path.sep + _driver) : '');
466 | return out;
467 | } catch (e) {
468 | return null;
469 | }
470 | }
471 |
472 | function javaVersionMatch(v1, v2) {
473 | if (v1 === 8) v1 = 1.8;
474 | if (v2 === 8) v2 = 1.8;
475 | if (Math.floor(v1) !== Math.floor(v2)) {
476 |
477 | return false;
478 | }
479 | if (v1 < 2) {
480 | // Up to 1.8, the version would be like 1.7, 1.8, etc..
481 | // So we need to check the minor version for equivalency
482 | return (Math.floor(v1*10) === Math.floor(v2*10));
483 | } else {
484 | // Starting with Java 9, the version is like 9, 10, 11, etc..
485 | // so we just compare major version.
486 | return (Math.floor(v1) === Math.floor(v2));
487 | }
488 |
489 | }
490 |
491 | var done = false;
492 | if (tryJavaHomeFirst) {
493 | if (env['JAVA_HOME']) {
494 | var javaHomeVersion = getJavaVersion(path.join(env['JAVA_HOME'], 'bin'));
495 | if (javaVersionMatch(javaHomeVersion, targetJavaVersion)) {
496 | done = true;
497 | env['PATH'] = path.join(env['JAVA_HOME'], 'bin') + path.delimiter + env['PATH'];
498 | run(env['JAVA_HOME']);
499 |
500 | }
501 | }
502 |
503 | if (!done) {
504 | var javaVersion = getJavaVersion();
505 | if (javaVersionMatch(javaVersion, targetJavaVersion)) {
506 | done = true;
507 | run();
508 | }
509 | }
510 | }
511 |
512 |
513 | if (!done) {
514 |
515 | var _javaHome = findSupportedRuntime(javaVersionString, bundleType === 'jdk', javafx);
516 | if (_javaHome && fs.existsSync(_javaHome)) {
517 | var javaVersion = getJavaVersion(path.join(_javaHome, 'bin'));
518 | if (javaVersionMatch(javaVersion, targetJavaVersion)) {
519 | env['PATH'] = path.join(_javaHome, 'bin') + path.delimiter + env['PATH'];
520 | env['JAVA_HOME'] = _javaHome;
521 | done = true;
522 | run(_javaHome);
523 | }
524 | }
525 |
526 | }
527 |
528 | if (!done) {
529 | console.log("Downloading java runtime environment for version "+targetJavaVersion);
530 | njre.install(targetJavaVersion, {type: bundleType, javafx: javafx}).then(function(dir) {
531 | var _javaHome = getJavaHomeInPath(dir);
532 | if (_javaHome == null)
533 |
534 | if (!_javaHome || !fs.existsSync(_javaHome)) {
535 | throw new Error("After install, could not find java home at "+_javaHome);
536 | }
537 | env['JAVA_HOME'] = _javaHome;
538 |
539 | var javaBinary = path.join(_javaHome, 'bin', 'java');
540 | if (!fs.existsSync(javaBinary)) {
541 | javaBinary += '.exe';
542 |
543 | }
544 | fs.chmodSync(javaBinary, 0o755);
545 |
546 | env['PATH'] = path.join(env['JAVA_HOME'], 'bin') + path.delimiter + env['PATH'];
547 |
548 | run(env['JAVA_HOME']);
549 | }).catch(function(err) {
550 | console.log("Failed to install JRE", err);
551 | });
552 | }
553 |
554 |
555 |
556 |
557 | function run(_javaHome) {
558 | var fail = reason => {
559 | console.error(reason);
560 | process.exit(1);
561 | };
562 |
563 |
564 | classPath = classPath.split(':');
565 | var classPathStr = '';
566 | var first = true;
567 | classPath.forEach(function(part) {
568 | if (!first) classPathStr += path.delimiter;
569 | first = false;
570 | classPathStr += __dirname + '/' + part;
571 | });
572 | classPath = classPathStr;
573 |
574 | var userArgs = process.argv.slice(2);
575 | var javaArgs = [];
576 | javaArgs.push('-Djdeploy.base='+__dirname);
577 | javaArgs.push('-Djdeploy.port='+port);
578 | javaArgs.push('-Djdeploy.war.path='+warPath);
579 | var programArgs = [];
580 | userArgs.forEach(function(arg) {
581 | if (arg.startsWith('-D') || arg.startsWith('-X')) {
582 | javaArgs.push(arg);
583 | } else {
584 | programArgs.push(arg);
585 | }
586 | });
587 | var cmd = 'java';
588 |
589 | if (!_javaHome) {
590 | env['PATH'] = path.join(getEmbeddedJavaHome(), 'bin') + path.delimiter + env['PATH'];
591 | if (env['JAVA_HOME']) {
592 | env['PATH'] = env['JAVA_HOME'] + path.sep + 'bin' + path.delimiter + env['PATH'];
593 | }
594 |
595 | } else {
596 | env['JAVA_HOME'] = _javaHome;
597 | cmd = _javaHome + path.sep + 'bin' + path.sep + 'java';
598 | }
599 |
600 | javaArgs.forEach(function(arg) {
601 | cmd += ' "'+arg+'"';
602 | });
603 | if (jarName !== '{'+'{JAR_NAME}}') {
604 | cmd += ' -jar "'+__dirname+'/'+jarName+'" ';
605 | } else {
606 | cmd += ' -cp "'+classPath+'" '+mainClass+' ';
607 | }
608 |
609 | programArgs.forEach(function(arg) {
610 | cmd += ' "'+arg+'"';
611 | });
612 | var child = exec(cmd, {async: true});
613 | process.stdin.setEncoding('utf8');
614 |
615 | process.stdin.on('readable', function() {
616 | var chunk = null;
617 | while (null !== (chunk = process.stdin.read())) {
618 | try {
619 | child.stdin.write(chunk);
620 | } catch(e){}
621 | }
622 | });
623 | child.on('close', function(code) {
624 | process.exit(code);
625 | });
626 |
627 | }
628 |
--------------------------------------------------------------------------------