2424*/
2525import groovy.io.FileType
2626import org.apache.commons.cli.Option
27+ import java.nio.file.Paths
2728
28- println " = IntellIJ IDEA Code Analysis Wrapper - v1.1 - @bentolor"
29+ println " = IntellIJ IDEA Code Analysis Wrapper - v1.2 - @bentolor"
2930
3031// Defaults
3132def resultDir = " target/inspection-results"
3233def acceptedLeves = [" [WARNING]" , " [ERROR]" ]
3334def skipResults = []
3435def skipIssueFilesRegex = []
3536def ideaTimeout = 20 // broken - has no effect
36-
37+ verbose = false
3738
3839//
3940// --- Command line option parsing
4041//
41- OptionAccessor opt = parseCli()
42-
43- if (! opt) System . exit(1 ) // will print usage automatically
44- if (opt. help) { cliBuilder. usage(); System . exit(1 ); }
42+ def configOpts = parseConfigFile()
43+ def OptionAccessor cliOpts = parseCli((configOpts << args). flatten())
4544
4645// Levels
47- if (opt . l) {
46+ if (cliOpts . l) {
4847 acceptedLeves. clear()
49- opt . ls. each { level -> acceptedLeves << " [" + level + " ]" }
48+ cliOpts . ls. each { level -> acceptedLeves << " [" + level + " ]" }
5049}
5150// Skip result XML files
52- if (opt. s) opt. ss. each { skipFile -> skipResults << skipFile. replace(" .xml" , " " ) }
51+ if (cliOpts. s) {
52+ cliOpts. ss. each { skipFile -> skipResults << skipFile. replace(" .xml" , " " ) }
53+ }
5354// Skip issues affecting given file name regex
54- if (opt. sf) opt. sfs. each { skipRegex -> skipIssueFilesRegex << skipRegex }
55+ if (cliOpts. sf) {
56+ cliOpts. sfs. each { skipRegex -> skipIssueFilesRegex << skipRegex }
57+ }
5558// target directory
56- if (opt. t) resultDir = opt. t
59+ if (cliOpts. t) {
60+ resultDir = cliOpts. t
61+ }
5762// timeout
58- // if (opt .to) ideaTimeout = opt .to.toInteger();
63+ // if (cliOpts .to) ideaTimeout = cliOpts .to.toInteger();
5964// IDEA home
6065def scriptExtension = (System . properties[' os.name' ]. toLowerCase(). contains(' windows' )) ? " .bat" : " .sh"
6166def pathSep = File . separator
62- def ideaPath = new File (opt. i + pathSep + " bin" + pathSep + " idea" + scriptExtension)
63- assertPath(ideaPath, " IDEA Installation directory" )
64- // Root Diretory
65- def rootDir = new File (opt. r)
66- def dotIdeaDir = new File (opt. r + pathSep + " .idea" )
67- assertPath(dotIdeaDir, " IDEA project directory" )
67+ def ideaHome = cliOpts. i ?: (System . getenv(" IDEA_HOME" ) ?: " idea" )
68+ def ideaPath = new File (ideaHome + pathSep + " bin" + pathSep + " idea" + scriptExtension)
69+ assertPath(ideaPath, " IDEA Installation directory" ,
70+ " Use a IDEA_HOME environment variable or the `ideahome` property in `.ideainspect` \n " +
71+ " or the `-i` command line option to point me to a valid IntelliJ installation" )
72+ // Passed project root Directory or working directory
73+ def rootDir = cliOpts. r ? new File (cliOpts. r) : Paths . get(" ." ). toAbsolutePath(). normalize(). toFile()
74+ def dotIdeaDir = new File (rootDir, " .idea" )
75+ assertPath(dotIdeaDir, " IDEA project directory" , " Please set the `rootdir` property to the location of your `.idea` project" )
6876// Inspection Profile
69- def profilePath = new File (dotIdeaDir. path + pathSep + " inspectionProfiles" + pathSep + opt. p)
77+ def profileName = cliOpts. p ?: " Project_Default.xml"
78+ def profilePath = new File (dotIdeaDir. path + pathSep + " inspectionProfiles" + pathSep + profileName)
7079assertPath(profilePath, " IDEA inspection profile file" )
7180
7281// Prepare result directory
7382def resultPath = new File (resultDir);
74- if (! resultPath. isAbsolute()) resultPath = new File (rootDir, resultDir);
75- if (resultPath. exists() && ! resultPath. deleteDir()) fail " Unable to remove result dir " + resultPath. absolutePath
76- if (! resultPath. mkdirs()) fail " Unable to create result dir " + resultPath. absolutePath
77-
83+ if (! resultPath. absolute) {
84+ resultPath = new File (rootDir, resultDir)
85+ };
86+ if (resultPath. exists() && ! resultPath. deleteDir()) {
87+ fail " Unable to remove result dir " + resultPath. absolutePath
88+ }
89+ if (! resultPath. mkdirs()) {
90+ fail " Unable to create result dir " + resultPath. absolutePath
91+ }
7892
7993//
8094// --- Actually running IDEA
8195//
8296
8397// ~/projects/dashboard.git/. ~/projects/dashboard.git/.idea/inspectionProfiles/bens_idea15_2015_11.xml /tmp/ -d server
8498def ideaArgs = [ideaPath. path, " inspect" , rootDir. absolutePath, profilePath. absolutePath, resultPath. absolutePath, " -v1" ]
85- if (opt. d) ideaArgs << " -d" << opt. d
99+ if (cliOpts. d) {
100+ ideaArgs << " -d" << cliOpts. d
101+ }
86102
87103println " #"
88104println " # Running IDEA IntelliJ Inspection"
89105println " #"
90106println " Executing: " + ideaArgs. join(" " )
91107
92- def processBuilder= new ProcessBuilder (ideaArgs)
108+ def processBuilder = new ProcessBuilder (ideaArgs)
93109processBuilder. redirectErrorStream(true )
94110processBuilder. directory(rootDir) // <--
95111def ideaProcess = processBuilder. start()
96112ideaProcess. consumeProcessOutput(System . out, System . err)
97113ideaProcess. waitForOrKill(1000 * 60 * ideaTimeout)
98114def exitValue = ideaProcess. exitValue()
99- if (exitValue != 0 ) fail (" IDEA Process returned with an unexpected return code of $exitValue " )
100-
115+ if (exitValue != 0 ) {
116+ fail (" IDEA Process returned with an unexpected return code of $exitValue " )
117+ }
101118
102119//
103120// --- Now lets look on the results
104121//
105122analyzeResult(resultPath, acceptedLeves, skipResults, skipIssueFilesRegex)
106123
107-
108124//
109125// --- Helper functions
110126//
111127
112- void assertPath (File profilePath , String description ) {
128+ void assertPath (File profilePath , String description , String hint = null ) {
113129 if (! profilePath. exists()) {
114130 println description + " " + profilePath. path + " not found!"
131+ if (hint) {
132+ println hint
133+ }
115134 System . exit(1 )
116135 }
117136}
@@ -123,43 +142,81 @@ void fail(String message) {
123142 System . exit(1 )
124143}
125144
145+ private parseConfigFile () {
146+ // Parse root dir with minimal CliBuilder
147+ def cliBuilder = new CliBuilder ()
148+ cliBuilder. with {
149+ r argName : ' dir' , longOpt : ' rootdir' , args : 1 , required : false ,
150+ ' IDEA project root directory containing the ".idea" directory'
151+ v argName : ' verbose' , longOpt : ' verbose' , args : 0 , required : false ,
152+ ' Enable verbose logging & debugging'
153+ }
126154
127- private OptionAccessor parseCli () {
128- def cliBuilder = new CliBuilder (usage : ' groovy ideainspect.groovy\n -i <IDEA_HOME> -p <PROFILEXML> -r <PROJDIR>' )
155+ def opt = cliBuilder. parse(args)
156+ verbose = verbose ?: (opt && opt. v)
157+ def rootDir = opt != null && opt. r ? opt. r : ' .'
158+
159+ def configFile = new File (rootDir + ' /.ideainspect' )
160+ def configArgs = []
161+ if (configFile. exists()) {
162+ if (verbose) {
163+ println " Parsing " + configFile. absolutePath
164+ }
165+ configFile. eachLine { line ->
166+ def values = line. split(' :' )
167+ if (! line. startsWith(' #' ) && values. length == 2 ) {
168+ configArgs. push(' --' + values[0 ]. trim())
169+ configArgs. push(values[1 ]. trim())
170+ }
171+ }
172+ }
173+ if (verbose) {
174+ println configArgs
175+ }
176+ return configArgs
177+ }
178+
179+ private OptionAccessor parseCli (configArgs ) {
180+ def cliBuilder = new CliBuilder (usage : ' groovy ideainspect.groovy\n -i <IDEA_HOME> -p <PROFILEXML> -r <PROJDIR>' ,
181+ stopAtNonOption : false )
129182 cliBuilder. with {
130183 h argName : ' help' , longOpt : ' help' , ' Show usage information and quit'
131184 l argName : ' level' , longOpt : ' levels' , args : Option . UNLIMITED_VALUES , valueSeparator : ' ,' ,
132185 ' Levels to look for. Default: WARNING,ERROR'
133186 s argName : ' file' , longOpt : ' skip' , args : Option . UNLIMITED_VALUES , valueSeparator : ' ,' ,
134187 ' Analysis result files to skip. For example "TodoComment" or "TodoComment.xml".'
135188 sf argName : ' regex' , longOpt : ' skipfile' , args : Option . UNLIMITED_VALUES , valueSeparator : ' ,' ,
136- ' Ignore issues affecting source files matching given regex. Example ".*/generated/.*".'
189+ ' Ignore issues affecting source files matching given regex. Example ".*/generated/.*".'
137190 t argName : ' dir' , longOpt : ' resultdir' , args : 1 ,
138191 ' Target directory to place the IDEA inspection XML result files. Default: target/inspection-results'
139- i argName : ' dir' , longOpt : ' ideahome' , args : 1 , required : true ,
140- ' IDEA installation home directory. Required '
192+ i argName : ' dir' , longOpt : ' ideahome' , args : 1 ,
193+ ' IDEA installation home directory. Default: IDEA_HOME environment variable or "idea" '
141194 d argName : ' dir' , longOpt : ' dir' , args : 1 , ' Limit IDEA inspection to this directory'
142- p argName : ' file' , longOpt : ' profile' , args : 1 , required : true ,
143- ' Use this inspection profile file located ".idea/inspectionProfiles". \n Example: "myprofile.xml"'
144- r argName : ' dir' , longOpt : ' rootdir' , args : 1 , required : true ,
145- ' IDEA project root directory containing the ".idea" directory'
195+ p argName : ' file' , longOpt : ' profile' , args : 1 ,
196+ ' Use this inspection profile file located ".idea/inspectionProfiles". \n Example: "myprofile.xml". Default: "Project_Default.xml"'
197+ r argName : ' dir' , longOpt : ' rootdir' , args : 1 ,
198+ ' IDEA project root directory containing the ".idea" directory. Default: Working directory'
199+ v argName : ' verbose' , longOpt : ' verbose' , args : 0 ,
200+ ' Enable verbose logging & debugging'
146201 // to argName: 'minutes', longOpt: 'timeout', args: 1,
147202 // 'Timeout in Minutes to wait for IDEA to complete the inspection. Default:'
148203 }
149204
150- def opt = cliBuilder. parse(args )
205+ def opt = cliBuilder. parse(configArgs )
151206
152- if (! opt) System . exit(1 ); // will print usage automatically
207+ if (! opt) {
208+ System . exit(1 )
209+ }; // will print usage automatically
153210 if (opt. help) {
154- println " "
155- println " This tools runs IntelliJ IDEA inspection via command line and"
156- println " tries to parse the output. \n "
157- println " Example usage:"
158- println " ./ideainspect.groovy -i ~/devel/idea -r . -p myinspections.xml \\ "
159- println " -d src/main/java -s unused,Annotator,TodoComment.xml -l ERROR"
160- println " "
161- cliBuilder. usage();
162- System . exit(1 );
211+ println " "
212+ println " This tools runs IntelliJ IDEA inspection via command line and"
213+ println " tries to parse the output. \n "
214+ println " Example usage:"
215+ println " ./ideainspect.groovy -i ~/devel/idea -r . -p myinspections.xml \\ "
216+ println " -d src/main/java -s unused,Annotator,TodoComment.xml -l ERROR"
217+ println " "
218+ cliBuilder. usage();
219+ System . exit(1 );
163220 }
164221 opt
165222}
0 commit comments