9 |
10 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/META-INF/MANIFEST.MF:
--------------------------------------------------------------------------------
1 | Manifest-Version: 1.0
2 | Main-Class: com.theapache64.gpm.MainKt
3 |
4 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | id 'java'
3 | id 'org.jetbrains.kotlin.jvm' version '1.9.24'
4 | }
5 |
6 | apply plugin: 'kotlin-kapt'
7 |
8 | group 'com.theapache64.gpm'
9 | version '1.0.6'
10 |
11 | repositories {
12 | mavenCentral()
13 | google()
14 | maven { url "https://jitpack.io" }
15 | }
16 |
17 | kapt {
18 | arguments {
19 | arg("project", "${project.group}/${project.name}")
20 | }
21 | }
22 |
23 | dependencies {
24 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.5.21"
25 |
26 | // Picocli
27 | def picocli_version = '4.6.1'
28 | implementation "info.picocli:picocli:$picocli_version"
29 | kapt "info.picocli:picocli-codegen:$picocli_version"
30 |
31 | // Retrofit
32 | def retrofit_version = '2.9.0'
33 | implementation "com.squareup.retrofit2:retrofit:$retrofit_version"
34 | implementation "com.squareup.retrofit2:converter-moshi:$retrofit_version"
35 | implementation "com.squareup.retrofit2:converter-scalars:$retrofit_version"
36 | implementation 'com.squareup.moshi:moshi-kotlin:1.12.0'
37 |
38 | // Inquirer
39 | implementation 'com.github.kotlin-inquirer:kotlin-inquirer:v0.0.2-alpha'
40 |
41 |
42 | // Dagger
43 | def dagger_version = "2.51.1"
44 | implementation "com.google.dagger:dagger:$dagger_version"
45 | kapt "com.google.dagger:dagger-compiler:$dagger_version"
46 |
47 | // Coroutines
48 | def coroutines_version = "1.5.1"
49 | implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutines_version"
50 | testImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-test:$coroutines_version"
51 |
52 | // Junit
53 | testImplementation group: 'junit', name: 'junit', version: '4.12'
54 |
55 | // ExpeKt
56 | testImplementation 'com.winterbe:expekt:0.5.0'
57 |
58 | // Mockito inline
59 | testImplementation('org.mockito:mockito-inline:3.11.2')
60 |
61 | // Mockito-koltin
62 | testImplementation "com.nhaarman.mockitokotlin2:mockito-kotlin:2.2.0"
63 |
64 | // Apache commons text
65 | implementation 'org.apache.commons:commons-text:1.9'
66 |
67 | // DaggerMock
68 |
69 | def dagger_mock_version = '0.8.5'
70 | testImplementation "com.github.fabioCollini.daggermock:daggermock:$dagger_mock_version"
71 | testImplementation "com.github.fabioCollini.daggermock:daggermock-kotlin:$dagger_mock_version"
72 |
73 | // Truth Core:Truth Core
74 | testImplementation 'com.google.truth:truth:1.1.3'
75 |
76 | // Curl Interceptor
77 | implementation 'com.github.mrmike:ok2curl:0.8.0'
78 | }
79 |
80 | compileKotlin {
81 | kotlinOptions.jvmTarget = "1.8"
82 | }
83 | compileTestKotlin {
84 | kotlinOptions.jvmTarget = "1.8"
85 | }
86 |
87 | // Include dependent libraries in archive.
88 | jar {
89 | manifest {
90 | attributes "Main-Class": "com.theapache64.gpm.MainKt"
91 | }
92 | }
93 |
94 | task fatJar(type: Jar) {
95 | manifest.from jar.manifest
96 | archiveFileName.value("gpm.main.jar")
97 | from {
98 | configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) }
99 | } {
100 | exclude "META-INF/*.SF"
101 | exclude "META-INF/*.DSA"
102 | exclude "META-INF/*.RSA"
103 | }
104 | with jar
105 | }
--------------------------------------------------------------------------------
/example.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/theapache64/gpm/ca702ad4f65b5a0ced1d9a8189c0006f2bb34e72/example.gif
--------------------------------------------------------------------------------
/extras/logo/gpm_original_logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/theapache64/gpm/ca702ad4f65b5a0ced1d9a8189c0006f2bb34e72/extras/logo/gpm_original_logo.png
--------------------------------------------------------------------------------
/extras/logo/gpmxcf.xcf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/theapache64/gpm/ca702ad4f65b5a0ced1d9a8189c0006f2bb34e72/extras/logo/gpmxcf.xcf
--------------------------------------------------------------------------------
/extras/logo/npm_original_logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/theapache64/gpm/ca702ad4f65b5a0ced1d9a8189c0006f2bb34e72/extras/logo/npm_original_logo.png
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | kotlin.code.style=official
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/theapache64/gpm/ca702ad4f65b5a0ced1d9a8189c0006f2bb34e72/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/theapache64/gpm/ca702ad4f65b5a0ced1d9a8189c0006f2bb34e72/icon.png
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/jdeploy-bundle/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/theapache64/gpm/ca702ad4f65b5a0ced1d9a8189c0006f2bb34e72/jdeploy-bundle/icon.png
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 | }
--------------------------------------------------------------------------------
/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 | }
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | rootProject.name = 'gpm'
2 |
3 |
--------------------------------------------------------------------------------
/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/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 | }
--------------------------------------------------------------------------------
/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/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 | }
--------------------------------------------------------------------------------
/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 | }
--------------------------------------------------------------------------------
/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 | }
--------------------------------------------------------------------------------
/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 | }
--------------------------------------------------------------------------------
/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/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 | }
--------------------------------------------------------------------------------
/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/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/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/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/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/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/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/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/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/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/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 | }
--------------------------------------------------------------------------------
/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/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/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/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/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/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 | "