Skip to content

Commit 3bcb49a

Browse files
authored
Merge branch 'master' into starfix-cli
2 parents fbde120 + 355d9e6 commit 3bcb49a

File tree

2 files changed

+227
-62
lines changed

2 files changed

+227
-62
lines changed

cli/src/main/java/dev/starfix/Starfix.java

Lines changed: 23 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@
44
//DEPS io.quarkus:quarkus-picocli:2.1.0.CR1
55
//DEPS org.zeroturnaround:zt-exec:1.12
66
//FILES application.properties=../../../resources/application.properties
7+
//SOURCES YAMLDefaultProvider.java
78

89
package dev.starfix;
9-
import io.quarkus.runtime.annotations.RegisterForReflection;
1010
import org.zeroturnaround.exec.ProcessExecutor;
1111
import org.zeroturnaround.exec.ProcessResult;
1212
import picocli.CommandLine;
@@ -23,18 +23,25 @@
2323
import java.nio.file.Paths;
2424
import java.util.Arrays;
2525
import java.io.File;
26+
import java.util.Properties;
2627
import java.util.concurrent.TimeoutException;
27-
import java.util.regex.Matcher;
2828
import java.util.regex.Pattern;
2929
import com.fasterxml.jackson.databind.ObjectMapper;
3030
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
3131

32-
@CommandLine.Command(mixinStandardHelpOptions = true)
32+
@CommandLine.Command(name = "starfix", mixinStandardHelpOptions = true, defaultValueProvider = YAMLDefaultProvider.class)
3333
public class Starfix implements Runnable{
3434

3535
@Parameters(arity = "0..1")
3636
String uri;
37-
37+
38+
// Holds path to destination Where the Repository Must Be Clonned
39+
@CommandLine.Option(names = "--clone-path", descriptionKey = "clone_path")
40+
String clone_path;
41+
42+
@CommandLine.Option(names = "--editor", descriptionKey = "ide")
43+
String ide = ""; // Holds command for IDE to open
44+
3845
@Command
3946
public int config() throws Exception {
4047
editConfig();
@@ -55,31 +62,13 @@ public int cloneCmd(@Parameters(index = "0") String url) {
5562
throw new IllegalArgumentException("Not a valid URI for git repository");
5663
}
5764

58-
// Configuration identifiers
59-
String clone_path = "";// Holds path to destination Where the Repository Must Be Clonned
60-
String ide = ""; // Holds command for IDE to open upon
61-
62-
File configFile = getConfigFile();// Calling functiono to fetch config file
63-
64-
// Reading Configuration File
65-
try {
66-
if (!configFile.exists()) {
67-
// Incase no config file exists
68-
editConfig(); // Calling function that lets user to configure
69-
}
70-
71-
// System.out.println("\nLoading configurations.....\n");
72-
ObjectMapper mapper = new ObjectMapper(new YAMLFactory());
73-
74-
Config config = mapper.readValue(configFile, Config.class);
7565

76-
ide = config.ide;
77-
clone_path = config.clone_path;
78-
if (ide == null || clone_path == null)
79-
editConfig(); // Incase of absence of configuration in file Launch Config
66+
if(ide==null) {
67+
throw new IllegalArgumentException("Editor not specifed, please specify via --editor or use config to set it up.");
68+
}
8069

81-
} catch (Exception e) {
82-
e.printStackTrace();
70+
if(clone_path==null) {
71+
throw new IllegalArgumentException("Clone path not specifed, please specify via --clone-path or use config to set it up.");
8372
}
8473

8574
try {
@@ -114,7 +103,8 @@ public int cloneCmd(@Parameters(index = "0") String url) {
114103
// Launching Editor on the Cloned Directory
115104
System.out.println("Launching Editor Now...");
116105
getIDE(ide).launch_editor(directory, ide, directory.toAbsolutePath().toString(),filePath);
117-
106+
System.out.println("Opening " + filePath);
107+
launch_editor(directory, ide, directory.toAbsolutePath().toString(),filePath);
118108
} catch (Exception e) {
119109
throw new IllegalStateException(e);
120110
}
@@ -222,7 +212,9 @@ public static void editConfig() throws Exception {
222212
}
223213
// ----------Now we'll write configurations to the YAML FILE------------
224214
ObjectMapper mapper = new ObjectMapper(new YAMLFactory());
225-
Config configuration = new Config(ide, clone_path);
215+
Properties configuration = new Properties();
216+
configuration.put("ide", ide);
217+
configuration.put("clone_path", clone_path);
226218
mapper.writeValue(configFile, configuration); // Writing to YAML File
227219

228220
} catch (Exception e) {
@@ -234,13 +226,14 @@ public static void editConfig() throws Exception {
234226

235227
public static void gitClone(Path directory, String originUrl) throws IOException, InterruptedException {
236228
// Function for git clonning
237-
runCommand(directory.getParent(), "git", "clone", originUrl, directory.getFileName().toString());
229+
runCommand(directory.getParent(), "git", "clone", originUrl, directory.toString());
238230
}
239231

240232
public static String runCommand(Path directory, String... command) throws IOException, InterruptedException {
241233
// Function to Run Commands using Process Builder
242234
ProcessResult presult;
243235
try {
236+
System.out.println("Running " + String.join(" ", command));
244237
presult = new ProcessExecutor().command(command).redirectOutput(System.out).redirectErrorStream(true).readOutput(true)
245238
.execute();
246239
} catch (TimeoutException e) {
@@ -256,38 +249,6 @@ public static String runCommand(Path directory, String... command) throws IOExce
256249
return presult.outputUTF8();
257250
}
258251

259-
@RegisterForReflection
260-
public static class Config {
261-
public String ide;
262-
public String clone_path;
263-
264-
public Config() {
265-
// Default constructor
266-
}
267-
268-
public Config(String ide, String clone_path) {
269-
this.ide = ide;
270-
this.clone_path = clone_path;
271-
272-
}
273-
274-
public String getIde() {
275-
return ide;
276-
}
277-
278-
public void setIde(String ide) {
279-
this.ide = ide;
280-
}
281-
282-
public String getClone_path() {
283-
return clone_path;
284-
}
285-
286-
public void setClone_path(String clone_path) {
287-
this.clone_path = clone_path;
288-
}
289-
290-
}
291252

292253
public static abstract class IDE{
293254
public abstract void launch_editor(Path directory, String ide, String path, String filePath)throws IOException, InterruptedException;
Lines changed: 204 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,204 @@
1+
package dev.starfix;
2+
3+
import com.fasterxml.jackson.databind.ObjectMapper;
4+
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
5+
import picocli.CommandLine;
6+
import picocli.CommandLine.IDefaultValueProvider;
7+
import picocli.CommandLine.Model.ArgSpec;
8+
import picocli.CommandLine.Model.CommandSpec;
9+
import picocli.CommandLine.Model.OptionSpec;
10+
import picocli.CommandLine.Model.PositionalParamSpec;
11+
12+
import java.io.File;
13+
import java.io.FileReader;
14+
import java.io.IOException;
15+
import java.io.Reader;
16+
import java.util.Properties;
17+
18+
/**
19+
* {@link IDefaultValueProvider IDefaultValueProvider} implementation that loads default values for command line
20+
* options and positional parameters from a properties file or {@code Properties} object.
21+
* <h2>Location</h2>
22+
* By default, this implementation tries to find a properties file named
23+
* {@code ".<YOURCOMMAND>.properties"} in the user home directory, where {@code "<YOURCOMMAND>"} is the {@linkplain CommandLine.Command#name() name} of the command.
24+
* If a command has {@linkplain CommandLine.Command#aliases() aliases} in addition to its {@linkplain CommandLine.Command#name() name},
25+
* these aliases are also used to try to find the properties file. For example:
26+
* <pre>{@code
27+
* @Command(name = "git", defaultValueProvider = YAMLDefaultProvider.class)
28+
* class Git { }
29+
* }</pre>
30+
* <p>The above will try to load default values from {@code new File(System.getProperty("user.home"), ".git.properties")}.
31+
* </p>
32+
* <p>
33+
* The location of the properties file can also be controlled with system property {@code "picocli.defaults.<YOURCOMMAND>.path"},
34+
* in which case the value of the property must be the path to the file containing the default values.
35+
* </p>
36+
* <p>
37+
* The location of the properties file may also be specified programmatically. For example:
38+
* </p>
39+
* <pre>
40+
* CommandLine cmd = new CommandLine(new MyCommand());
41+
* File defaultsFile = new File("path/to/config/mycommand.properties");
42+
* cmd.setDefaultValueProvider(new YAMLDefaultProvider(defaultsFile));
43+
* cmd.execute(args);
44+
* </pre>
45+
* <h2>Format</h2>
46+
* <p>
47+
* For options, the key is either the {@linkplain CommandLine.Option#descriptionKey() descriptionKey},
48+
* or the option's {@linkplain OptionSpec#longestName() longest name}.
49+
* </p><p>
50+
* For positional parameters, the key is either the
51+
* {@linkplain CommandLine.Parameters#descriptionKey() descriptionKey},
52+
* or the positional parameter's {@linkplain PositionalParamSpec#paramLabel() param label}.
53+
* </p><p>
54+
* End users may not know what the {@code descriptionKey} of your options and positional parameters are, so be sure
55+
* to document that with your application.
56+
* </p>
57+
* <h2>Subcommands</h2>
58+
* <p>
59+
* The default values for options and positional parameters of subcommands can be included in the
60+
* properties file for the top-level command, so that end users need to maintain only a single file.
61+
* This can be achieved by prefixing the key with the command's qualified name.
62+
* For example, to give the {@code `git commit`} command's {@code --cleanup} option a
63+
* default value of {@code strip}, define a key of {@code git.commit.cleanup} and assign
64+
* it a default value.
65+
* </p><pre>
66+
* # /home/remko/.git.properties
67+
* git.commit.cleanup = strip
68+
* </pre>
69+
*/
70+
public class YAMLDefaultProvider implements IDefaultValueProvider {
71+
72+
private Properties properties;
73+
74+
/**
75+
* Default constructor, used when this default value provider is specified in
76+
* the annotations:
77+
* <pre>
78+
* {@code
79+
* @Command(name = "mycmd",
80+
* defaultValueProvider = YAMLDefaultProvider.class)
81+
* class MyCommand // ...
82+
* }
83+
* </pre>
84+
* <p>
85+
* This loads default values from a properties file named
86+
* {@code ".mycmd.properties"} in the user home directory.
87+
* </p><p>
88+
* The location of the properties file can also be controlled with system property {@code "picocli.defaults.<YOURCOMMAND>.path"},
89+
* in which case the value of the property must be the path to the file containing the default values.
90+
* </p>
91+
* @see YAMLDefaultProvider the YAMLDefaultProvider class description
92+
*/
93+
public YAMLDefaultProvider() {}
94+
95+
/**
96+
* This constructor loads default values from the specified properties object.
97+
* This may be used programmatically. For example:
98+
* <pre>
99+
* CommandLine cmd = new CommandLine(new MyCommand());
100+
* Properties defaults = getProperties();
101+
* cmd.setDefaultValueProvider(new YAMLDefaultProvider(defaults));
102+
* cmd.execute(args);
103+
* </pre>
104+
* @param properties the properties containing the default values
105+
* @see YAMLDefaultProvider the YAMLDefaultProvider class description
106+
*/
107+
public YAMLDefaultProvider(Properties properties) {
108+
this.properties = properties;
109+
}
110+
111+
/**
112+
* This constructor loads default values from the specified properties file.
113+
* This may be used programmatically. For example:
114+
* <pre>
115+
* CommandLine cmd = new CommandLine(new MyCommand());
116+
* File defaultsFile = new File("path/to/config/file.properties");
117+
* cmd.setDefaultValueProvider(new YAMLDefaultProvider(defaultsFile));
118+
* cmd.execute(args);
119+
* </pre>
120+
* @param file the file to load default values from. Must be non-{@code null} and
121+
* must contain default values in the standard java {@link Properties} format.
122+
* @see YAMLDefaultProvider the YAMLDefaultProvider class description
123+
*/
124+
public YAMLDefaultProvider(File file) {
125+
this(createProperties(file));
126+
}
127+
128+
private static Properties createProperties(File file) {
129+
if (file == null) {
130+
throw new NullPointerException("file is null");
131+
}
132+
133+
Properties result = new Properties();
134+
if (file.exists() && file.canRead()) {
135+
136+
try {
137+
ObjectMapper mapper = new ObjectMapper(new YAMLFactory());
138+
Properties config = mapper.readValue(file, Properties.class);
139+
result.putAll(config);
140+
} catch (IOException ioe) {
141+
System.err.println("WARN - could not read defaults from " + file.getAbsolutePath() + ": " + ioe);
142+
}
143+
} else {
144+
System.err.println("WARN - defaults configuration file " + file.getAbsolutePath() + " does not exist or is not readable");
145+
}
146+
return result;
147+
}
148+
149+
private static Properties loadProperties(CommandSpec commandSpec) {
150+
if (commandSpec == null) { return null; }
151+
Properties p = System.getProperties();
152+
for (String name : commandSpec.names()) {
153+
String path = p.getProperty("picocli.defaults." + name + ".path");
154+
File defaultPath = new File(p.getProperty("user.home") + File.separator + "/.config", name + ".yaml");
155+
File file = path == null ? defaultPath : new File(path);
156+
if (file.canRead()) {
157+
return createProperties(file);
158+
}
159+
}
160+
return loadProperties(commandSpec.parent());
161+
}
162+
163+
@Override
164+
public String defaultValue(ArgSpec argSpec) throws Exception {
165+
if (properties == null) {
166+
properties = loadProperties(argSpec.command());
167+
}
168+
if (properties == null || properties.isEmpty()) {
169+
return null;
170+
}
171+
return argSpec.isOption()
172+
? optionDefaultValue((OptionSpec) argSpec)
173+
: positionalDefaultValue((PositionalParamSpec) argSpec);
174+
}
175+
176+
private String optionDefaultValue(OptionSpec option) {
177+
String result = getValue(option.descriptionKey(), option.command());
178+
result = result != null ? result : getValue(stripPrefix(option.longestName()), option.command());
179+
return result;
180+
}
181+
private static String stripPrefix(String prefixed) {
182+
for (int i = 0; i < prefixed.length(); i++) {
183+
if (Character.isJavaIdentifierPart(prefixed.charAt(i))) {
184+
return prefixed.substring(i);
185+
}
186+
}
187+
return prefixed;
188+
}
189+
190+
private String positionalDefaultValue(PositionalParamSpec positional) {
191+
String result = getValue(positional.descriptionKey(), positional.command());
192+
result = result != null ? result : getValue(positional.paramLabel(), positional.command());
193+
return result;
194+
}
195+
196+
private String getValue(String key, CommandSpec spec) {
197+
String result = null;
198+
if (spec != null) {
199+
String cmd = spec.qualifiedName(".");
200+
result = properties.getProperty(cmd + "." + key);
201+
}
202+
return result != null ? result : properties.getProperty(key);
203+
}
204+
}

0 commit comments

Comments
 (0)