├── .dockerignore ├── .gitignore ├── .idea ├── codeStyleSettings.xml ├── codeStyles │ ├── Project.xml │ └── codeStyleConfig.xml ├── compiler.xml ├── copyright │ ├── bentolor.xml │ └── profiles_settings.xml ├── dynamic.xml ├── encodings.xml ├── inspectionProfiles │ ├── bentolor_2018.xml │ └── profiles_settings.xml ├── misc.xml ├── modules.xml ├── modules │ ├── ben.projects.idea-cli-inspector.git.iml │ ├── projects.idea-cli-inspector.git.main.iml │ └── projects.idea-cli-inspector.git.test.iml ├── runConfigurations │ ├── Execute_idea_cli_inspector__Dry_run_.xml │ └── bentolor_idea_cli_inspector.xml ├── scopes │ └── inspector_code.xml └── vcs.xml ├── .ideainspect ├── .travis.yml ├── CHANGES.md ├── DOCKER.md ├── Dockerfile ├── LICENSE.txt ├── README.md ├── build.gradle ├── docker-entrypoint.sh ├── etc └── travis-agent │ └── .IntelliJIdea2018.1 │ └── config │ └── options │ └── jdk.table.xml ├── home └── ideainspect │ ├── .bashrc │ └── .config │ └── JetBrains │ └── IdeaIC2021.1 │ └── options │ ├── filetypes.xml │ ├── ide.general.xml │ ├── jdk.table.xml │ └── shared-indexes.xml └── idea-cli-inspector /.dockerignore: -------------------------------------------------------------------------------- 1 | /.gradle 2 | /build* 3 | /etc 4 | /idea 5 | /target 6 | /out 7 | 8 | */*/*/.java/fonts 9 | */*/*/system 10 | */*/*/config/options/*.xml 11 | */*/*/config/*.xml 12 | */*/*/config/plugins 13 | !*/*/*/config/options/jdk.table.xml 14 | */*/.cache 15 | */*/.gradle 16 | */*/.java 17 | */*/.local 18 | 19 | # IDEA files 20 | .idea/codeStyles 21 | .idea/copyright 22 | .idea/libraries 23 | .idea/workspace.xml 24 | .idea/tasks.xml 25 | .idea/dictionaries 26 | .idea/jsLibraryMappings.xml 27 | .idea/dataSources.ids 28 | .idea/dataSources.xml 29 | .idea/dataSources.local.xml 30 | .idea/sqlDataSources.xml 31 | .idea/uiDesigner.xml -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by .ignore support plugin (hsz.mobi) 2 | ### JetBrains template 3 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm 4 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 5 | 6 | # User-specific stuff: 7 | .idea/workspace.xml 8 | .idea/tasks.xml 9 | .idea/dictionaries 10 | .idea/jsLibraryMappings.xml 11 | 12 | # Sensitive or high-churn files: 13 | .idea/dataSources.ids 14 | .idea/dataSources.xml 15 | .idea/dataSources.local.xml 16 | .idea/sqlDataSources.xml 17 | .idea/uiDesigner.xml 18 | 19 | # Plusings 20 | .idea/libraries 21 | 22 | # IntelliJ 23 | /out/ 24 | /target/ 25 | .cache/ 26 | .local/ 27 | .java/ 28 | port.lock 29 | consentOptions/ 30 | colors.scheme.xml 31 | *.statistics.xml 32 | laf.xml 33 | window.state.xml 34 | other.xml 35 | debugger.xml 36 | actionSummary.xml 37 | pluginAdvertiser.xml 38 | recentProjects.xml 39 | visualizationTool.xml 40 | tasks/ 41 | workspace/ 42 | ide-features-trainer.xml 43 | gradle.xml 44 | path.macros.xml 45 | updates.xml 46 | 47 | 48 | 49 | # Custom link to your idea installation 50 | /idea 51 | /.gradle 52 | /build 53 | -------------------------------------------------------------------------------- /.idea/codeStyleSettings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 9 | -------------------------------------------------------------------------------- /.idea/codeStyles/Project.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 15 | 157 | -------------------------------------------------------------------------------- /.idea/codeStyles/codeStyleConfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /.idea/copyright/bentolor.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | -------------------------------------------------------------------------------- /.idea/copyright/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /.idea/dynamic.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 148 | 149 | -------------------------------------------------------------------------------- /.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /.idea/modules/ben.projects.idea-cli-inspector.git.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /.idea/modules/projects.idea-cli-inspector.git.main.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /.idea/modules/projects.idea-cli-inspector.git.test.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /.idea/runConfigurations/Execute_idea_cli_inspector__Dry_run_.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /.idea/runConfigurations/bentolor_idea_cli_inspector.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /.idea/scopes/inspector_code.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.ideainspect: -------------------------------------------------------------------------------- 1 | # Levels to look for. Default: WARNING,ERROR 2 | #levels: WARNING,ERROR,INFO 3 | 4 | # Apply an "Custom scope" for the analysis run 5 | # This is _the prefered way_ to limit your inspection run to a part of your project files 6 | # as it takes effect within IDEA itself. 7 | # See: https://www.jetbrains.com/help/idea/2016.2/specify-inspection-scope-dialog.html 8 | # 9 | # HOWTO: 10 | # 1) Create a new scope excluding undesired folders/files (node_modules, doc, ...) 11 | # 2) Share the .idea/scopes/scopename.xml with the project 12 | # 3) Use the _name_ of the scope (not the file). 13 | # 4) Stick to a single word for best compability 14 | scope: inspector-code 15 | 16 | # Inspection result files to skip. For example "TodoComment" or "TodoComment.xml". 17 | # 18 | # NOTE: This does not have an effect on which inspections are effectively run by IDEA! 19 | # For the sake of performance better disable these inspections within your 20 | # inspection profile. This here is a last-resort mechanism if you want them 21 | # to appear in your IDE but not your CI process 22 | #skip: TodoComment,Annotator 23 | skip: GroovyAssignabilityCheck 24 | 25 | # Ignore issues affecting source files matching given regex. Example ".*/generated/.*". 26 | # 27 | # NOTE: This does not have an effect on the places IDEA looks. Therefore please prefer 28 | # declaring an "scope" and exclude those locations via the IDEA scoping mechanism 29 | # for the sake of performance. 30 | # This here is a last-resort mechanism if you have no other options to supress 31 | # specific places/warning. 32 | #skipfile: .*/generated/.*,src/main/Foo.java 33 | 34 | # Target directory to place the IDEA inspection XML result files. Default: target/inspection-results 35 | #resultdir: target/inspection-results 36 | 37 | # IDEA installation home directory. Default: IDEA_HOME environment variable or "idea". 38 | # ideahome: /home/ben/devel/idea 39 | 40 | # Limit IDEA inspection to this directory (This overrides scoping) 41 | # dir: . 42 | 43 | # Use this inspection profile file located ".idea/inspectionProfiles". 44 | profile: bentolor_2018.xml 45 | 46 | # IDEA project root directory containing the ".idea" directory 47 | # rootdir: . 48 | 49 | # Full path to the local idea.properties file. More info at: 50 | # http://tools.android.com/tech-docs/configuration 51 | # iprops: /Users/Shared/User/Library/Preferences/AndroidStudio2.1/idea.properties 52 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: required 2 | 3 | services: 4 | - docker 5 | 6 | script: 7 | - docker run --rm -v `pwd`:/project bentolor/idea-cli-inspector -v -r . 8 | -------------------------------------------------------------------------------- /CHANGES.md: -------------------------------------------------------------------------------- 1 | +---------+------------------------------------------------------------+ 2 | | Version | Change | 3 | +=========+============================================================+ 4 | | 1.8 | - Bugfix for Groovy Lang 3.0 | 5 | +---------+------------------------------------------------------------+ 6 | | 1.7 | - Support for Docker-based execution. Available on | 7 | | | Docker Hub. | 8 | | | | 9 | | | - The `-rf` option no longer expects any IDEA | 10 | | | configuration (`.idea`) to be present in the project. | 11 | | | | 12 | | | - More robust processing: No longer need to pass only | 13 | | | absolute paths to `-rf` or `-p` | 14 | +---------+------------------------------------------------------------+ 15 | | 1.6 | - New `--rootfile` option to allow inspection of maven | 16 | | | projects without `.iml` files committed to SCM.\ | 17 | | | | 18 | | | - The `--profile` option now accepts an absolute path to | 19 | | | the inspection profile xml file. If the path is not | 20 | | | absolute, then the fle is supposed to be in | 21 | | | `rootdir/.idea/inspectionProfiles/`. | 22 | | | | 23 | | | - Dry-run option `-n` now does no longer touch the | 24 | | | `idea.properties` file. | 25 | | | | 26 | | | - The `-v` option now also increases the verbosity of | 27 | | | the IDEA process. | 28 | | | | 29 | | | - Add example for usage with Travis CI / Docker | 30 | | | | 31 | | | - **Eat your own dogfood** - `idea-cli-inspector` now | 32 | | | tests itself via Travis CI using Gradle & | 33 | | | `idea-cli-inspector` | 34 | | | | 35 | | | - Fix problem on parsing values with contains `:`, i.e. | 36 | | | like the path to the IDEA installation directory in | 37 | | | windows like `C:\Program Files\…`. Thanks | 38 | | | to \@ColmBhandal. Fixes \#19 | 39 | +---------+------------------------------------------------------------+ 40 | | 1.5.4 | - CLI options override config values inside the | 41 | | | `.ideainspect` file. | 42 | | | | 43 | | | - The `--verbose` option now prints the effective | 44 | | | configuration. | 45 | | | | 46 | | | - Add documentation about scoping.\ | 47 | | | | 48 | | | - Add more FAQ entries. | 49 | +---------+------------------------------------------------------------+ 50 | | 1.5.3 | - Render reported violations to STDERR. This should | 51 | | | display them more prominently i.e. in CI environments. | 52 | +---------+------------------------------------------------------------+ 53 | | 1.5.2 | - Critical bugfix: Return code not returned on failed | 54 | | | analysis. Introduced with 1.5.0. | 55 | +---------+------------------------------------------------------------+ 56 | | 1.5.1 | - Increase process timeout to avoid seeing an unexpected | 57 | | | return code of 143 | 58 | +---------+------------------------------------------------------------+ 59 | | 1.5 | - Support for custom Intellij scopes\ | 60 | +---------+------------------------------------------------------------+ 61 | | 1.4 | - Support for Android Studio installations\ | 62 | +---------+------------------------------------------------------------+ 63 | | 1.3 | - Support for Mac OSX locations of IntelliJ IDEA | 64 | | | executables\ | 65 | +---------+------------------------------------------------------------+ 66 | | 1.2 | - Add support for configuration file | 67 | | | | 68 | | | - Add debugging flag `-v` | 69 | | | | 70 | | | - Replaced all mandatory CLI options with default values | 71 | | | (root directory, IDEA home, Inspection profile name, | 72 | | | ...​) | 73 | +---------+------------------------------------------------------------+ 74 | | 1.1 | - Support for ignoring issues affecting specific source | 75 | | | files using a regular expression (Option `-sf`) | 76 | +---------+------------------------------------------------------------+ 77 | | 1.0 | - First release | 78 | +---------+------------------------------------------------------------+ 79 | -------------------------------------------------------------------------------- /DOCKER.md: -------------------------------------------------------------------------------- 1 | # Analyzing project using the Docker version of idea-cli-inspector 2 | 3 | There is an image `bentolor/idea-cli-inspector` available on Docker Hub 4 | which you can use to start and run `idea-cli-inspector` without any 5 | further configuration. The project to be analyzed is expected to be 6 | available in `/project` within the running container. Example usage: 7 | 8 |
9 | 10 | docker run --rm -v $(pwd):/project bentolor/idea-cli-inspector -rf pom.xml -p inspectionprofile.xml 11 | 12 |
13 | 14 | # General overview creating your own Docker containers 15 | 16 | The `Dockerfile` provided in this repository will create a minimum, 17 | Java-based Docker environment which is able to run a IDEA CLI 18 | inspection. 19 | 20 | It is based on 21 | 22 | - IntelliJ Community Edition (vanilla) 23 | 24 | - AdoptOpenJDK Java 11 25 | 26 | In your practical usage scenario you might probably have different needs 27 | like: 28 | 29 | - Using the commercial Ultimate Edition for more support 30 | 31 | - using additional inspections provided by plugins 32 | 33 | - additional Tooling (like NodeJS) 34 | 35 | This guide tries to explain how to customize your idea-cli-inspector 36 | instance. 37 | 38 | - NOTE 39 | [@sylvainlaurent/docker-intellij-inspect](https://github.com/sylvainlaurent/docker-intellij-inspect) 40 | created a very similar approach which is in some aspects more 41 | advanced than this implementation. You might also have a look there. 42 | 43 | # Visually installing/adjusting IDEA settings 44 | 45 | IntelliJ stores its settings in a `IdeaIC2018.3/config` directory (or 46 | `.IntelliJ2018.3/config` for Ultimate editions). Most of these settings 47 | do not matter so they are excluded in the `.dockerignore` file. 48 | 49 | But if you want i.e. install additional plugins, set the licence server 50 | for the Ultimate Edition or add/define additional SDKs you can do this 51 | interactive. 52 | 53 | Therefore just run 54 | 55 | xhost si:localuser:root 56 | docker run -it --rm -e DISPLAY=$DISPLAY \ 57 | -v /tmp/.X11-unix:/tmp/.X11-unix \ 58 | -v `pwd`/home:/home \ 59 | bentolor/idea-cli-inspector su ideainspect -c '/srv/idea.latest/bin/idea.sh' 60 | 61 | This will launch IDEA inside the docker container. After configuration 62 | check the modifications in `home/ideainspect` and add them to the 63 | Container (i.e. adjusting `.dockerignore`). 64 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # Docker-based IntelliJ IDEA Inspections using idea-cli-inspector 2 | # 3 | # NOTE: 4 | # This Dockerfile provides quite a bunch of commented-out statements 5 | # as template for creatng your own, derived docker image in case i.e. 6 | # you want to use the Ultimate edition or need additional build tools. 7 | # 8 | FROM adoptopenjdk:11-jdk-hotspot 9 | MAINTAINER Benjamin Schmid 10 | LABEL maintainer="Benjamin Schmid " 11 | 12 | # First install some basic tools to get them or their latest versions (wget, apt). 13 | RUN apt-get update -q && apt-get install -q -y wget sudo locales zip unzip git \ 14 | libxtst6 libxrender1 libxi6 && \ 15 | apt-get autoremove --purge -y && apt-get clean && \ 16 | rm /var/lib/apt/lists/*.* && rm -fr /tmp/* /var/tmp/* 17 | 18 | # The default locale is POSIX which breaks UTF-8 based javac files 19 | # NOTE: 20 | # This only taked effect for user root. Check home/ideainspect/.bashrc for main user 21 | # environment variables 22 | RUN locale-gen en_US.UTF-8 && update-locale en_US.UTF8 23 | ENV LANG "en_US.UTF-8" 24 | ENV LC_MESSAGES "C" 25 | 26 | # Provide a non-privileged user for running IntelliJ 27 | RUN useradd -mUs /bin/bash ideainspect 28 | 29 | # Install SDKMAN! 30 | RUN sudo -u ideainspect sh -c 'curl -s "https://get.sdkman.io" | bash' 31 | 32 | # Install Groovy Lang 33 | RUN sudo -u ideainspect bash -ci 'shopt -s expand_aliases ; sdk install groovy' 34 | 35 | # --------------- Install Oracle Java PPAs 36 | #RUN echo "deb http://ppa.launchpad.net/webupd8team/java/ubuntu xenial main" | tee /etc/apt/sources.list.d/webupd8team-java.list 37 | #RUN apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys EEA14886 38 | #RUN echo "deb http://ppa.launchpad.net/linuxuprising/java/ubuntu xenial main" | tee /etc/apt/sources.list.d/linuxuprising-java.list 39 | #RUN apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 73C3DB2A 40 | # 41 | # Mark Oracle license accepted 42 | #RUN echo debconf shared/accepted-oracle-license-v1-1 select true | debconf-set-selections 43 | #RUN echo debconf shared/accepted-oracle-license-v1-1 seen true | debconf-set-selections 44 | #RUN echo oracle-java10-installer shared/accepted-oracle-license-v1-1 select true | debconf-set-selections 45 | # 46 | # Install Java 8, Java 10, Groovy, mongodb-client & graphviz via package repository 47 | #RUN apt-get update && \ 48 | # apt-get install -y --no-install-recommends \ 49 | # oracle-java8-installer \ 50 | # oracle-java8-set-default \ 51 | # oracle-java8-unlimited-jce-policy \ 52 | # oracle-java10-installer \ 53 | # && \ 54 | # apt-get autoremove --purge -y && \ 55 | # apt-get clean && \ 56 | # rm -fr /var/cache/oracle-jdk* && \ 57 | # rm /var/lib/apt/lists/*.* && \ 58 | # rm -fr /tmp/* /var/tmp/* 59 | # 60 | # 61 | # --------------- Install Android SDK Tools 62 | #RUN mkdir -p /srv/android-sdk && cd /srv/android-sdk && \ 63 | # wget -nv https://dl.google.com/android/repository/sdk-tools-linux-4333796.zip && \ 64 | # echo "92ffee5a1d98d856634e8b71132e8a95d96c83a63fde1099be3d86df3106def9 sdk-tools-linux-4333796.zip" | sha256sum -c - && \ 65 | # unzip -q sdk-tools-linux-4333796.zip && \ 66 | # rm sdk-tools-linux-4333796.zip && \ 67 | # find /srv/android-sdk -executable -type f -exec chmod o+x \{\} \; 68 | # 69 | # Install Android SDKs & Build Tools 70 | #RUN yes | /srv/android-sdk/tools/bin/sdkmanager --licenses > /dev/null && \ 71 | # /srv/android-sdk/tools/bin/sdkmanager "platforms;android-26" "platforms;android-27" "build-tools;26.0.3" "build-tools;27.0.3" "platform-tools" > /dev/null 72 | # 73 | # --------------- Install 8.x node - this does an implicit apt-get update! 74 | #RUN ( curl -sL https://deb.nodesource.com/setup_8.x | bash - ) && \ 75 | # apt-get install -y nodejs && \ 76 | # apt-get clean && \ 77 | # rm /var/lib/apt/lists/*.* && \ 78 | # rm -fr /tmp/* /var/tmp/* 79 | 80 | # 81 | # Install IntelliJ IDEA 82 | # 83 | # IMPORTANT NOTES 84 | # 85 | # 1. V_IDEA_EDITION defines, which edition to build. Use C for Community or U for Ultimate 86 | # 87 | # 2. IDEA_CONFDIR is depending on the edition & version: 88 | # I.e. its .IdeaIC2018.3 for the 2018.3 Community edition and .IntelliJIdea2018.3 for the same ultimate dition 89 | # 90 | # 3. The first run to pre-populate the indexes won't work with ultimate edition, yet. This is due to outstanding features in 91 | # the current Docker daemon. See https://github.com/moby/buildkit/issues/763 92 | # 93 | ENV V_IDEA 2021.1.3 94 | ENV V_IDEA_EDITION C 95 | ENV IDEA_CONFDIR .IntelliJIdea2021.1 96 | # For Ultimate it is: ENV IDEA_CONFDIR .IntelliJIdea2019.2 97 | RUN cd /srv && \ 98 | wget -nv https://download.jetbrains.com/idea/ideaI$V_IDEA_EDITION-$V_IDEA.tar.gz && \ 99 | tar xf ideaI$V_IDEA_EDITION-$V_IDEA.tar.gz && \ 100 | ln -s idea-I$V_IDEA_EDITION-* idea.latest && \ 101 | # The idea-cli-inspector needs write access to the IDEA bin directory as a hack for scope 102 | chown -R ideainspect:ideainspect /srv/idea.latest/bin && \ 103 | mkdir /home/ideainspect/$IDEA_CONFDIR && \ 104 | ln -s /home/ideainspect/$IDEA_CONFDIR idea.config.latest && \ 105 | rm ideaI$V_IDEA_EDITION-$V_IDEA.tar.gz 106 | 107 | # Point inspector to the new home 108 | # NOTE: This only takes effect for user `root`. For user ideainspect check home/ideainspect/.bashrc 109 | ENV IDEA_HOME /srv/idea.latest 110 | 111 | # Copy files into container 112 | COPY /idea-cli-inspector / 113 | COPY /docker-entrypoint.sh / 114 | 115 | # Bash Environments & Default IDEA config 116 | COPY /home /home 117 | RUN chown -R ideainspect:ideainspect /home/ideainspect 118 | 119 | # Prepare a sample project 120 | COPY / /project 121 | RUN chown -R ideainspect:ideainspect /project 122 | 123 | # Initial run to populate index i.e. for JDKs. This should reduce startup times. 124 | # NOTE: This won't run for Ultimate Edition, as a licence key is missing during execution and current docker 125 | # version provide no means to inject secrets during build time. JUST COMMENT IT OUT FOR NOW IN CASE OF ISSUES 126 | RUN [ "/docker-entrypoint.sh", "-r", "/project" ] 127 | # 128 | # 129 | # At some time this might work, by providing the idea.key as a secret during build time: 130 | #RUN --mount=type=secret,id=idea.key,target=/srv/idea.config.latest/idea.key,required,mode=0444 [ "/docker-entrypoint.sh", "-r","/project" ] 131 | # 132 | # To get this working you need to: 133 | # 1. add th following line on the very top of this file 134 | # # syntax = docker/dockerfile:experimental 135 | # 2. Build the image with BuildKit enabled: 136 | # DOCKER_BUILDKIT=1 docker build --secret id=idea.key,src=/home/ben/.IntelliJIdea2018.3/config/idea.key \ 137 | # -t bentolor/idea-cli-inspector . 138 | # 139 | 140 | # Provide an entry point script which also creates starts Bamboo with a 141 | # dedicated user 142 | ENTRYPOINT ["/docker-entrypoint.sh"] 143 | 144 | # Define default command. 145 | #CMD ["--help"] 146 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | A little command-line tool to integrate the awesome IntelliJ IDEA code 2 | inspections in your continuous integration (CI) process using Jenkins, 3 | Bamboo, et. al. 4 | 5 | # Quick start (using Docker and Maven) 6 | 7 | 1. Add a valid IDEA inspection profile file to your project (find them 8 | at `.idea/inspectionProfiles`) 9 | 10 | 2. Run within your project directory: 11 | 12 |
13 | 14 | docker run --rm -v $(pwd):/project bentolor/idea-cli-inspector -rf pom.xml -p inspectionprofile.xml 15 | 16 |
17 | 18 | Optionally you can also 19 | 20 | - Use/Add a complete IDEA configuration (`.idea`) to your project. 21 | This allows more fine control (i.e. defining inspection scopes) 22 | 23 | - add a `.ideainspect` to control i.e. ignored files/inspections and 24 | other details. See the example file provided with 25 | idea-cli-inspector. 26 | 27 | # Why this tool? 28 | 29 | IntelliJ IDEA offers a vast amount of very high-quality, built-in code 30 | inspections. Currently it has more than 1073 inspections to offer. Using 31 | project-shared inspection profiles it’s possible **to guide developers** 32 | in a wide range of coding aspects in a **non-annoying way**. 33 | 34 | In contrast to well-known quality tools like 35 | [SonarQube](http://www.sonarqube.org/) these inspections do have quite a 36 | few benefits: 37 | 38 | - **violations are instantly visible** during code writing with freely 39 | configurable severities and warning levels. (i.e. bold red errors, 40 | yellow warning or discreet hints as recommendation) 41 | 42 | - **false alarms can be easily and immediately suppressed.** A simple 43 | Alt-Enter directly at the location where they occur is enough. 44 | Alternatively you can adjust your inspection settings just 45 | on-the-fly (i.e. reduce level, disable inspection or configure 46 | inspection properties) 47 | 48 | - IDEA allows you to **check for new violations introduced with your 49 | changes** *while you are trying to commit them* 50 | 51 | - Many inspections in IDEA provide semi-automatic quickfixes and 52 | auto-corrections. So again sometimes addressing an issue is as 53 | simply as pressing Alt-Enter Right arrow Enter. 54 | 55 | - Because all inspections are based on IDEA´s Psi engine (which is a 56 | sort of permanent running syntax compiler) the inspections 57 | effectively work on an actual AST. So IDEA *understands* the code 58 | and does not only look for (textual) patterns which in return leads 59 | to a **significantly lower rate of false-positives.** 60 | 61 | Nevertheless, though IDEA offers on-the-fly analysis and error 62 | visualization it does not stop the developer in committing code 63 | violating these helpful guidelines into the project repository. 64 | JetBrain´s CI solution [TeamCity](https://www.jetbrains.com/teamcity/) 65 | does offer a easy and good integration. It is also possible to execute 66 | the IDEA project inspection [on the command 67 | line](https://www.jetbrains.com/idea/help/working-with-intellij-idea-features-from-command-line.html), 68 | which will produce a hard-readable set of XML files and no further 69 | support for integrating this into an automated tool chain. 70 | 71 | Therefore I did hack this little Groovy script to easily include & 72 | report inspection checks into your CI chain. 73 | 74 | # What it does 75 | 76 | This tool is aimed to simplify the inclusion of IDEA code inspection in 77 | your CI chain. It executes a command-line based run of an IntelliJ 78 | inspection run. This produces a set of hardly human-readable XML files 79 | in the target directory. 80 | 81 | Therefore the tool subsequently parses the generated inspection result 82 | files, allows to filter out selected result files and looks for messages 83 | with given severity (WARNING and ERROR by default). 84 | 85 | The tool will list issues in a humand-readable form and exit with return 86 | code 1 if it did find any issues (your CI tool should interpret this as 87 | failure) or will happily tell you that everyhing is fine. 88 | 89 | # Prerequisites & Limitations 90 | 91 | The script is developed in Groovy, so this has to be installed. 92 | Furthermore you need a valid installation of IntelliJ IDEA. 93 | 94 |
95 | 96 | Due to a limitation of IDEA itself it is not possible to run more than 97 | one IDEA instance at the same time. Therefore you must ensure, that no 98 | other IDEA is running on your PC / on your CI agents. 99 | 100 |
101 | 102 | # Usage 103 | 104 | ## Configuration file base usage 105 | 106 | IDEA CLI Inspector supports configuration via a `.ideainspect` file in 107 | the project root directory. Below is a example: 108 | 109 | # Levels to look for. Default: WARNING,ERROR 110 | #levels: WARNING,ERROR,INFO 111 | 112 | # Apply an "Custom scope" for the analysis run 113 | # This is _the prefered way_ to limit your inspection run to a part of your project files 114 | # as it takes effect within IDEA itself. 115 | # See: https://www.jetbrains.com/help/idea/2016.2/specify-inspection-scope-dialog.html 116 | # 117 | # HOWTO: 118 | # 1) Create a new scope excluding undesired folders/files (node_modules, doc, ...) 119 | # 2) Share the .idea/scopes/scopename.xml with the project 120 | # 3) Use the _name_ of the scope (not the file). 121 | # 4) Stick to a single word for best compability 122 | scope: inspector-code 123 | 124 | # Inspection result files to skip. For example "TodoComment" or "TodoComment.xml". 125 | # 126 | # NOTE: This does not have an effect on which inspections are effectively run by IDEA! 127 | # For the sake of performance better disable these inspections within your 128 | # inspection profile. This here is a last-resort mechanism if you want them 129 | # to appear in your IDE but not your CI process 130 | #skip: TodoComment,Annotator 131 | skip: GroovyAssignabilityCheck 132 | 133 | # Ignore issues affecting source files matching given regex. Example ".*/generated/.*". 134 | # 135 | # NOTE: This does not have an effect on the places IDEA looks. Therefore please prefer 136 | # declaring an "scope" and exclude those locations via the IDEA scoping mechanism 137 | # for the sake of performance. 138 | # This here is a last-resort mechanism if you have no other options to supress 139 | # specific places/warning. 140 | #skipfile: .*/generated/.*,src/main/Foo.java 141 | 142 | # Target directory to place the IDEA inspection XML result files. Default: target/inspection-results 143 | #resultdir: target/inspection-results 144 | 145 | # IDEA installation home directory. Default: IDEA_HOME environment variable or "idea". 146 | # ideahome: /home/ben/devel/idea 147 | 148 | # Limit IDEA inspection to this directory (This overrides scoping) 149 | # dir: . 150 | 151 | # Use this inspection profile file located ".idea/inspectionProfiles". 152 | profile: bentolor_2018.xml 153 | 154 | # IDEA project root directory containing the ".idea" directory 155 | # rootdir: . 156 | 157 | # Full path to the local idea.properties file. More info at: 158 | # http://tools.android.com/tech-docs/configuration 159 | # iprops: /Users/Shared/User/Library/Preferences/AndroidStudio2.1/idea.properties 160 | 161 | ## Command line based usage 162 | 163 | For a full / up-to-date list of options please run `idea-cli-inspector 164 | -h`: 165 | 166 | = IntellIJ IDEA Code Analysis Wrapper - v1.5.2 - @bentolor 167 | 168 | This tools runs IntelliJ IDEA inspection via command line and 169 | tries to parse the output. 170 | 171 | Example usage: 172 | ./idea-cli-inspector -i ~/devel/idea -r . -p myinspections.xml \ 173 | -d src/main/java -s unused,Annotator,TodoComment.xml -l ERROR 174 | 175 | For more convenience you can pass all options in a `.ideainspect` file 176 | instead of passing it via command line 177 | 178 | usage: groovy idea-cli-inspector [options] 179 | -d,--dir Limit IDEA inspection to this directory. 180 | Overrides the scope argument. 181 | -h,--help Show usage information and quit 182 | -i,--ideahome IDEA or Android Studio installation home 183 | directory. Default: IDEA_HOME env var or `idea` 184 | -ip,--iprops Full path to your `idea.properties`. Only 185 | required if 1) you use --scope and 2) file is 186 | not located under in the default. 187 | Default: `/idea/bin/idea.properties` 188 | -l,--levels Levels to look for. Default: WARNING,ERROR 189 | -n,--dry-run Dry-run: Do not start IDEA, but run parsing 190 | -p,--profile Use this inspection profile file. If given an 191 | absolute path, the target file is used, 192 | otherwise the arg denotes a file located under 193 | `.idea/inspectionProfiles`. 194 | Default: `Project_Default.xml` 195 | -r,--rootdir IDEA project root directory containing the 196 | `.idea` directory. Default: Working directory 197 | -rf,--rootfile full path to the pom.xml or build.gradle file 198 | for the project. Useful if the project is maven 199 | or gradle based and its rootdir does not contain 200 | all the *.iml and .idea/modules.xml files 201 | -s,--skip Analysis result files to skip. For example 202 | `TodoComment` or `TodoComment.xml`. 203 | -sc,--scope The name of the "Custom scope" to apply. Custom 204 | scopes can be defined in the IDE. Share the 205 | resulting file in .idea/scopes/scopename.xml and 206 | provide the name of the scope (not file) here. 207 | -sf,--skipfile Ignore issues affecting source files matching 208 | given regex. Example: `.*/generated/.*`. 209 | -t,--resultdir Target directory to place the IDEA inspection 210 | XML result files. 211 | Default: `target/inspection-results` 212 | -v,--verbose Enable verbose logging 213 | 214 | # Example usage 215 | 216 | $ groovy idea-cli-inspector \ 217 | -i ~/devel/idea \ 218 | -r ~/projects/p1 \ 219 | -p myinspections.xml \ 220 | -d server \ 221 | -s unused,Annotator,TodoComment.xml \ 222 | -l ERROR 223 | 224 | This looks for a IntelliJ installation in `~/devel/idea`, tries to 225 | perform a CLI-based code inspection run on the IDEA project 226 | `~/projects/p1/.idea` with an inspection profile 227 | `~/projects/p1/.idea/inspectionProfiles/myinspections.xml` limiting the 228 | inspection run to the subdirectory `server` within your project. 229 | 230 | The IDEA inspection run will produce a set of `.xml` files. The amount, 231 | levels and result is based on the inspection profile you passed. Option 232 | `-s` tells to skip & ignore the warnings contained in the inspection 233 | result files `unused.xml`, `Annotator.xml` and `TodoComment.xml`. You 234 | can ommit the `.xml` suffix for convenience. 235 | 236 | By default it will then look for entries marked as `[WARNING]` or 237 | `[ERROR]` within the remaining inspection result report files. In our 238 | case we only care for ERROR entries. If it finds entries, it will report 239 | the file joined with a description pointing to the file location and the 240 | inspection rule. 241 | 242 | # Vanilla Maven and Gradle projects 243 | 244 | For many maven and gradle-based projects, .iml files and xml files under 245 | `.idea/libraries` are not committed to SCM as they are generated by 246 | IntelliJ based on maven/gradle files (see 247 | 248 | after "You may consider not to share the following"). 249 | 250 | For such projects, the inspection must be launched by passing the path 251 | to the maven/gradle project file like in the following example: 252 | 253 | $ groovy idea-cli-inspector \ 254 | --ideahome ~/devel/idea \ 255 | --rootdir ~/projects/p1 \ 256 | --rootfile ~/projects/p1/pom.xml 257 | --profile ~/myinspections.xml \ 258 | -l ERROR 259 | 260 | # Example output 261 | 262 | ➜ idea-cli-inspector.git git:(master) ✗ ./idea-cli-inspector -i ~/devel/idea -r ../dashboard.git -p bens_idea15_2015_11.xml -d server -s Annotator,JSUnresolvedLibraryURL.xml,JavaDoc,TodoComment -l ERROR,WARNING 263 | 264 | = IntellIJ IDEA Code Analysis Wrapper - v1.0 - @bentolor 265 | # 266 | # Running IDEA IntelliJ Inspection 267 | # 268 | Executing: /home/ben/devel/idea/bin/idea.sh [/home/ben/devel/idea/bin/idea.sh, inspect, /home/ben/projects/idea-cli-inspector.git/../dashboard.git, /home/ben/projects/idea-cli-inspector.git/../dashboard.git/.idea/inspectionProfiles/bens_idea15_2015_11.xml, /home/ben/projects/idea-cli-inspector.git/../dashboard.git/target/inspection-results, -d, server] 269 | log4j:WARN No appenders could be found for logger (io.netty.util.internal.logging.InternalLoggerFactory). 270 | log4j:WARN Please initialize the log4j system properly. 271 | log4j:WARN See http://logging.apache.org/log4j/1.2/faq.html#noconfig for more info. 272 | Please configure library 'Node.js v4.2.1 Core Modules' which is used in module 'client' 273 | 274 | ... 275 | IDEA spilling out quite a bunch of exceptions during inspection run 276 | ... 277 | 278 | # 279 | # Inspecting produced result files in ../dashboard.git/target/inspection-results 280 | # 281 | # Looking for: [[WARNING], [ERROR]] 282 | # Ignoring : [Annotator, JSUnresolvedLibraryURL, JavaDoc, TodoComment] 283 | --- ClassNamePrefixedWithPackageName.xml 284 | [WARNING] server/src/main/java/de/foo/dashboard/data/DatasetVerticle.java:28 -- Class name DatasetVerticle begins with its package name #loc 285 | [WARNING] server/src/main/java/de/foo/dashboard/data/DatasetBuilder.java:17 -- Class name DatasetBuilder begins with its package name #loc 286 | 287 | --- InterfaceNamingConvention.xml 288 | [WARNING] server/src/main/java/de/foo/dashboard/constants/Events.java:11 -- Interface name Events is too short (6 < 8) #loc 289 | 290 | --- SameParameterValue.xml 291 | [WARNING] server/src/main/java/de/foo/dashboard/data/DatasetBuilder.java:30 -- Actual value of parameter 'type' is always 'de.exxcellent.dashboard.constants.DatasetType.ARRAY' 292 | 293 | --- Skipping JavaDoc.xml 294 | --- Skipping TodoComment.xml 295 | --- DeprecatedClassUsageInspection.xml 296 | [WARNING] server/pom.xml:99 -- 'io.vertx.core.Starter' is deprecated 297 | 298 | --- Skipping JSUnresolvedLibraryURL.xml 299 | --- Skipping Annotator.xml 300 | --- unused.xml 301 | [WARNING] server/src/main/java/de/foo/dashboard/data/DatasetBuilder.java:40 -- Method is never used. 302 | [WARNING] server/src/main/java/de/foo/dashboard/constants/DatasetType.java:14 -- Field has no usages. 303 | [WARNING] server/src/main/java/de/foo/dashboard/constants/DatasetType.java:14 -- Field has no usages. 304 | [WARNING] server/src/main/java/de/foo/dashboard/data/DatasetVerticle.java:28 -- Class is not instantiated. 305 | [WARNING] server/src/main/java/de/foo/dashboard/transformers/History.java:23 -- Class is not instantiated. 306 | 307 | # 308 | # Analysis Result 309 | # 310 | Entries found. return code: 1 311 | 312 | # Running within a Docker container (i.e. Travis CI) 313 | 314 | Here is a `.travis.yml` which demonstrates how to run 315 | `idea-cli-inspector` within a Docker container. You can see this in 316 | practice running [here on Travis 317 | CI](https://www.travis-ci.org/bentolor/microframeworks-showcase/) with 318 | the source inspected [in my microframeworks-showcase 319 | project](https://github.com/bentolor/microframeworks-showcase/) 320 | 321 | Two things to note: 322 | 323 | - IDEA needs some very basic configuration already existing. At least 324 | i.e. the `.IntelliJIdea2018.1/config/options/jdk.table.xml` which 325 | defines the locations of the installed JDKs 326 | 327 | - The IDEA configuration directory location *varies from version to 328 | version and edition to edition*. I.e. it’s `~/.IntelliJIdea2018.1` 329 | for the IDEA 2018.1 Ultimate edition and `~/.IdeaIC2018.1` for the 330 | community edition 331 | 332 | - If you are using i.e. Node, Scala, VueJS etc. in your project please 333 | note, that these plugins bring more inspections to the table. If you 334 | want to have them in your CI/Docker run to, ensure that you add them 335 | to i.e. `.IntelliJIdea2017.3/config/plugins/` directory so they are 336 | picked up and effective. 337 | 338 | - You can build these required configurations either by manually 339 | adjusting and then copying those configuration file into the 340 | container or by i.e. manually starting IDEA within the container 341 | once with the configuration directories mapped as Docker volumes 342 | i.e. like: 343 | 344 | 345 | 346 | xhost si:localuser:root 347 | docker run -it --rm \ 348 | --dns 192.168.144.18 --dns 8.8.8.8 \ 349 | -e DISPLAY=$DISPLAY -v /tmp/.X11-unix:/tmp/.X11-unix \ 350 | -v `pwd`/root/.IntelliJIdea2018.1:/root/.IntelliJIdea2018.1 \ 351 | -v `pwd`/root/.java:/root/.java debug-ideacli-dockeragent \ 352 | /bin/bash 353 | 354 | language: java 355 | 356 | before_cache: 357 | - rm -f $HOME/.gradle/caches/modules-2/modules-2.lock 358 | - rm -fr $HOME/.gradle/caches/*/plugin-resolution/ 359 | 360 | cache: 361 | directories: 362 | - $HOME/.gradle/caches/ 363 | - $HOME/.gradle/wrapper/ 364 | 365 | before_install: 366 | - sudo add-apt-repository ppa:mmk2410/intellij-idea -y 367 | - sudo apt-get update -q 368 | - sudo apt-get install intellij-idea-community -y 369 | - sudo apt-get install groovy -y 370 | 371 | install: 372 | - wget https://github.com/bentolor/idea-cli-inspector/archive/master.zip 373 | - unzip master.zip 374 | - sudo chmod -R aog+w /opt/intellij-idea-community/bin 375 | 376 | script: 377 | # Copy idea configuration template (mostly .IntelliJIdea2018.1/config/options/jdk.table.xml) 378 | - cp -r ./tools/idea-cli-inspector/root/.IntelliJIdea2018.1 /home/travis/ 379 | # Duplicate for community config dir 380 | - cp -r ./tools/idea-cli-inspector/root/.IntelliJIdea2018.1 /home/travis/.IdeaIC2018.1 381 | - ./idea-cli-inspector-master/idea-cli-inspector -i /opt/intellij-idea-community 382 | 383 | # Troubleshooting & FAQ 384 | 385 | **My inspection runs very long and takes to much time. What can I do?.** 386 | 387 | First: Introduce and use a new scope where you exclude all folders 388 | and/or include only those folders which are relevant for your 389 | inspection. Typical folders which are not relevant are i.e. 390 | `node_modules`, `docs` or build output folders. Secondly think about 391 | creating and using a custom inspection profile for the purpose of the 392 | CI. There you i.e. might disable all inspections with INFO/HINT level or 393 | i.e. the spell checking. 394 | 395 | **I receive a error message *Please, specify sdk 'null' for module 396 | 'foo'*.** 397 | 398 | Probably you excluded `misc.xml` from the versionied IDEA project. Which 399 | is fine because this file is quite volatile. But this is the file where 400 | IDEA stores the "Root JDK". 401 | 402 | To fix this error simply assign every module a SDK other than "Project 403 | SDK". 404 | 405 | **The analysis seems to produce different results on subsequent runs on 406 | the same sources (esp. JavaScript).** 407 | 408 | This seems to be an issue with the IDEA caches which IntelliJ keeps i.e. 409 | under `.IntelliJ201X.X/system`. Try if deleting this directory prior to 410 | executing the analysis runs produces stable results. 411 | 412 | **What shall I pass as IDEA home directory for Mac OSX?.** 413 | 414 | `/Applications/IntelliJ\ IDEA.app` should be the default installation 415 | folder. 416 | 417 | **I’m using Android Studio and I can’t find `idea.properties`\!.** 418 | 419 | See the details about the location at 420 | 421 | 422 | **Scoping does not work?.** 423 | 424 | First: Did you versionate the `.idea/scope/scopename.xml` file in your 425 | project? Did you specify the Custom scope *name* and not the filename? 426 | 427 | If no, it might be that there is an issue that you are not pointing the 428 | `--iprops` option to the right `idea.properties` file or you i.e. don’t 429 | have write access for it. 430 | 431 | *Background information and troubleshooting:* Unfortunately IDEA yet 432 | does not offer a direct CLI option for applying scope. Therefore we need 433 | to pass a System property entries via modifying the `idea.properties` 434 | file. Check the output of the script if you spot any issues during this 435 | process. Check the content of `idea.properties` during the run and look 436 | out for you scope name. 437 | 438 | **I receive different results in my IDE vs. on the CI server.** 439 | 440 | Please check the following: 441 | 442 | - ❏ Same version of IDEA installed on CI vs. you installation? 443 | 444 | - ❏ Do you have the same plugins installed? Some bring own inspections 445 | or support fur understanding file formats (like `.vue` files with 446 | the vue plugin). 447 | 448 | - ❏ Do you delete the `.IntelliJ201X.X/system` folder prior to every 449 | run on CI (see above)? 450 | 451 | # Source code & Contributions 452 | 453 | The source code is located under 454 | . 455 | 456 | # License 457 | 458 | Licensed under the Apache License, Version 2.0 (the "License"); you may 459 | not use this file except in compliance with the License. 460 | 461 | You may obtain a copy of the License at 462 | 463 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'groovy' 2 | 3 | repositories { 4 | jcenter() 5 | } 6 | 7 | dependencies { 8 | compile 'org.codehaus.groovy:groovy-all:2.4.15' 9 | } -------------------------------------------------------------------------------- /docker-entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | if [[ "$1" =~ '-' || -z "$1" ]]; then 5 | CMD="cd /project && /idea-cli-inspector $@" 6 | echo "Calling $CMD" 7 | exec sudo -H -n -u ideainspect bash -l -c "$CMD" 8 | fi 9 | 10 | echo "Executing $@" 11 | exec "$@" -------------------------------------------------------------------------------- /etc/travis-agent/.IntelliJIdea2018.1/config/options/jdk.table.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | -------------------------------------------------------------------------------- /home/ideainspect/.bashrc: -------------------------------------------------------------------------------- 1 | # Workaround: openjdk-Base image does not update links to /usr/lib/jvm/default-java/bin/java 2 | export JAVA_HOME=/opt/java/openjdk/ 3 | export PATH=/opt/java/openjdk/bin:$PATH 4 | 5 | # Point to IDEA installation directory 6 | export ENV IDEA_HOME=/srv/idea.latest 7 | 8 | # Language settings from Dockerfile. Adopt also for user bamboo 9 | export LANG="en_US.UTF-8" 10 | export LC_MESSAGES="C" 11 | 12 | #THIS MUST BE AT THE END OF THE FILE FOR SDKMAN TO WORK!!! 13 | export SDKMAN_DIR="/home/ideainspect/.sdkman" 14 | [[ -s "/home/ideainspect/.sdkman/bin/sdkman-init.sh" ]] && source "/home/ideainspect/.sdkman/bin/sdkman-init.sh" -------------------------------------------------------------------------------- /home/ideainspect/.config/JetBrains/IdeaIC2021.1/options/filetypes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /home/ideainspect/.config/JetBrains/IdeaIC2021.1/options/ide.general.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | -------------------------------------------------------------------------------- /home/ideainspect/.config/JetBrains/IdeaIC2021.1/options/jdk.table.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | -------------------------------------------------------------------------------- /home/ideainspect/.config/JetBrains/IdeaIC2021.1/options/shared-indexes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | -------------------------------------------------------------------------------- /idea-cli-inspector: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env groovy 2 | import groovy.io.FileType 3 | import groovy.transform.Field 4 | import org.apache.commons.cli.Option 5 | 6 | import java.nio.file.Files 7 | import java.nio.file.Paths 8 | 9 | /* 10 | * Copyright 2015-2018 Benjamin Schmid, @bentolor 11 | * 12 | * Licensed under the Apache License, Version 2.0 (the "License"); 13 | * you may not use this file except in compliance with the License. 14 | * You may obtain a copy of the License at 15 | * 16 | * http://www.apache.org/licenses/LICENSE-2.0 17 | * 18 | * Unless required by applicable law or agreed to in writing, software 19 | * distributed under the License is distributed on an "AS IS" BASIS, 20 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 21 | * See the License for the specific language governing permissions and 22 | * limitations under the License. 23 | */ 24 | /* 25 | https://github.com/bentolor/idea-cli-inspector 26 | 27 | Note to the reader: 28 | This is my very first Groovy script. Please be nice. 29 | */ 30 | println "=" 31 | println "= IntellIJ IDEA Code Analysis Wrapper - v1.8 - @bentolor" 32 | println "= " 33 | println "Use --help or see https://github.com/bentolor/idea-cli-inspector/ for help" 34 | println "" 35 | 36 | // Defaults 37 | def resultDir = "target/inspection-results" 38 | def acceptedLevels = ["[WARNING]", "[ERROR]"] 39 | def skipResults = [] 40 | def skipIssueFilesRegex = [] 41 | // Process timeout: 42 | // This is more or less broken, because after reaching the timeout value 43 | // it will kill only the Wrapper script and the IDEA process will happily 44 | // continue to run. 45 | // We should never reach this value and IDEA should always termiante on 46 | // its own 47 | def ideaWrapperTimeout = 1200 // Minutes 48 | //noinspection GroovyUnusedAssignment 49 | @Field Boolean verbose = false 50 | 51 | // 52 | // --- Command line option parsing 53 | // 54 | def configOpts = args.toList() 55 | configOpts.addAll(parseConfigFile()) 56 | OptionAccessor cliOpts = parseCli(configOpts) 57 | 58 | // Levels 59 | if (cliOpts.l) { 60 | acceptedLevels.clear() 61 | cliOpts.ls.each { level -> acceptedLevels << "[" + level + "]" } 62 | } 63 | // Skip result XML files 64 | if (cliOpts.s) cliOpts.ss.each { skipFile -> skipResults << skipFile.replace(".xml", "") } 65 | // Skip issues affecting given file name regex 66 | if (cliOpts.sf) cliOpts.sfs.each { skipRegex -> skipIssueFilesRegex << skipRegex } 67 | // target directory 68 | if (cliOpts.t) resultDir = cliOpts.t 69 | 70 | // IDEA home 71 | File ideaPath = findIdeaExecutable(cliOpts) 72 | // Passed project root Directory or working directory 73 | def rootDir = cliOpts.r ? new File(cliOpts.r) : Paths.get(".").toAbsolutePath().normalize().toFile() 74 | //noinspection GroovyAssignabilityCheck 75 | def rootfilePath = cliOpts.rf ? new File(cliOpts.rf) : rootDir 76 | 77 | def dotIdeaDir = new File(rootDir, ".idea") 78 | if (!cliOpts.rf) { 79 | assertPathIsDir(dotIdeaDir, "IDEA project directory", 80 | "Please set the `rootdir` property to the location of your `.idea` project") 81 | } 82 | 83 | // Retrieve path to Inspection Profile file 84 | //noinspection GroovyAssignabilityCheck,GroovyAssignabilityCheck 85 | def profilePath = cliOpts.p ? new File(cliOpts.p) : null 86 | if( profilePath == null || !profilePath.absolute) { 87 | profileName = cliOpts.p ?: "Project_Default.xml" 88 | // if the given arg is not a full path, then firs try withing the root dir 89 | profilePath = new File(rootDir.path + File.separator + profileName) 90 | 91 | // is it there? 92 | if (!profilePath.exists() || !profilePath.file) { 93 | // If not try to find the inspection profile within the the .idea dir 94 | profilePath = new File(dotIdeaDir.path + File.separator + "inspectionProfiles" + File.separator + profileName) 95 | } 96 | } 97 | assertPathIsFile(profilePath, "IDEA inspection profile file") 98 | 99 | // Prepare result directory 100 | def resultPath = new File(resultDir) 101 | if (!resultPath.absolute) resultPath = new File(rootDir, resultDir) 102 | if (resultPath.exists() && !resultPath.deleteDir()) fail "Unable to remove result dir " + resultPath.absolutePath 103 | if (!resultPath.mkdirs()) fail "Unable to create result dir " + resultPath.absolutePath 104 | 105 | // 106 | // --- Actually running IDEA 107 | // 108 | 109 | // ~/projects/dashboard.git/. ~/projects/dashboard.git/.idea/inspectionProfiles/bens_idea15_2015_11.xml /tmp/ -d server 110 | def ideaArgs = [ideaPath.path, "inspect", rootfilePath.absolutePath, profilePath.absolutePath, resultPath.absolutePath] 111 | ideaArgs << ((cliOpts.v) ? "-v2" : "-v1") 112 | if (cliOpts.d) ideaArgs << "-d" << cliOpts.d 113 | 114 | // Did user define a Analysis "Scope"? We need a dirty workaround 115 | File origPropFile = applyScopeViaPropFile(cliOpts) 116 | 117 | if (cliOpts.pre) { 118 | if (!cliOpts.n) { 119 | def preTimeout = 1200 120 | println "Executing '${cliOpts.pre}' for maximum ${preTimeout}s" 121 | def sout = new StringBuilder(), serr = new StringBuilder() 122 | def preScript = cliOpts.pre.execute([], rootDir) 123 | preScript.consumeProcessOutput(sout, serr) 124 | preScript.waitForOrKill(preTimeout * 1000) 125 | preScript.waitForProcessOutput() 126 | print "$sout$serr" 127 | 128 | def exitValue = preScript.exitValue() 129 | if (exitValue != 0) fail("Pre-process script returned with an unexpected return code of $exitValue") 130 | 131 | } else { 132 | println "Dry-run: Would execute: '${cliOpts.pre}'" 133 | } 134 | } 135 | 136 | 137 | println "\n#" 138 | println "# Running IDEA IntelliJ Inspection" 139 | println "#" 140 | println "Executing: " + ideaArgs.join(" ") 141 | def exitValue = 0 142 | if (!cliOpts.n) { 143 | def processBuilder = new ProcessBuilder(ideaArgs) 144 | processBuilder.redirectErrorStream(true) 145 | processBuilder.directory(rootDir) 146 | 147 | println "~" * 80 148 | def ideaProcess = processBuilder.start() 149 | ideaProcess.consumeProcessOutput((OutputStream) System.out, System.err) 150 | ideaProcess.waitForOrKill(1000 * 60 * ideaWrapperTimeout) 151 | ideaProcess.waitForProcessOutput() 152 | exitValue = ideaProcess.exitValue() 153 | println "~" * 80 154 | } else { 155 | println("Dry-run: Not starting IDEA process") 156 | } 157 | 158 | // Scope workaround: Clean up time 159 | cleanupIdeaProps(cliOpts, origPropFile) 160 | 161 | if (exitValue != 0) fail("IDEA process returned with an unexpected return code of $exitValue") 162 | 163 | // 164 | // --- Now lets look on the results 165 | // 166 | def returnCode = analyzeResult(resultPath, acceptedLevels, skipResults, skipIssueFilesRegex) 167 | System.exit(returnCode) 168 | 169 | 170 | // =============================================================================================== 171 | // ==== End of script body 172 | // =============================================================================================== 173 | 174 | 175 | // 176 | // --- Helper functions 177 | // 178 | private List parseConfigFile() { 179 | // Parse root dir with minimal CliBuilder 180 | def cliBuilder = new CliBuilder() 181 | cliBuilder.with { 182 | r argName: 'dir', longOpt: 'rootdir', args: 1, required: false, 183 | 'IDEA project root directory containing the ".idea" directory' 184 | v argName: 'verbose', longOpt: 'verbose', args: 0, required: false, 185 | 'Enable verbose logging & debugging' 186 | } 187 | 188 | def opt = cliBuilder.parse(args) 189 | verbose = verbose ?: (opt && opt.v) 190 | def rootDir = opt != null && opt.r ? opt.r : '.' 191 | 192 | def configFile = new File(rootDir + '/.ideainspect') 193 | def configArgs = [] 194 | if (configFile.exists()) { 195 | if (verbose) println "Parsing " + configFile.absolutePath 196 | 197 | //noinspection GroovyMissingReturnStatement 198 | configFile.eachLine { line -> 199 | def values = line.split(':', 2) 200 | if (!line.startsWith('#') && values.length == 2) { 201 | configArgs << ('--' + values[0].trim()) 202 | configArgs << values[1].trim() 203 | } 204 | } 205 | } 206 | 207 | if (verbose) println "Config file content: " << configArgs 208 | 209 | return configArgs 210 | } 211 | 212 | private OptionAccessor parseCli(List configArgs) { 213 | def cliBuilder = new CliBuilder(usage: 'groovy idea-cli-inspector [options]', 214 | stopAtNonOption: false) 215 | cliBuilder.with { 216 | h argName: 'help', longOpt: 'help', 'Show usage information and quit' 217 | l argName: 'level', longOpt: 'levels', args: Option.UNLIMITED_VALUES, valueSeparator: ',', 218 | 'Levels to look for. Default: WARNING,ERROR' 219 | s argName: 'file', longOpt: 'skip', args: Option.UNLIMITED_VALUES, valueSeparator: ',', 220 | 'Analysis result files to skip. For example `TodoComment` or `TodoComment.xml`.' 221 | sf argName: 'regex', longOpt: 'skipfile', args: Option.UNLIMITED_VALUES, valueSeparator: ',', 222 | 'Ignore issues affecting source files matching given regex. Example: `.*/generated/.*`.' 223 | sc argName: 'string', longOpt: 'scope', args: 1, 224 | 'The name of the "Custom scope" to apply. Custom scopes can be defined in the IDE. '+ 225 | 'Share the resulting file in .idea/scopes/scopename.xml and provide the name of the scope (not file) here.' 226 | t argName: 'dir', longOpt: 'resultdir', args: 1, 227 | 'Target directory to place the IDEA inspection XML result files. \nDefault: `target/inspection-results`' 228 | i argName: 'dir', longOpt: 'ideahome', args: 1, 229 | 'IDEA or Android Studio installation home directory. Default: IDEA_HOME env var or `idea`' 230 | d argName: 'dir', longOpt: 'dir', args: 1, 'Limit IDEA inspection to this directory. Overrides the scope argument.' 231 | ip argName: 'file', longOpt: 'iprops', args: 1, 'Full path to your `idea.properties`. Only required if 1) you use --scope and 2) ' + 232 | 'file is not located under in the default. \nDefault: `/idea/bin/idea.properties`' 233 | p argName: 'file', longOpt: 'profile', args: 1, 234 | 'Use this inspection profile file. If given an absolute path, the target file is used, otherwise the arg ' + 235 | 'denotes a file located under the projct directory or `.idea/inspectionProfiles` (IDEA default location). ' + 236 | '\nDefault: `Project_Default.xml`' 237 | r argName: 'dir', longOpt: 'rootdir', args: 1, 238 | 'IDEA project root directory containing the `.idea` directory. Default: Current working directory' 239 | rf argName: 'file', longOpt: 'rootfile', args: 1, 240 | 'full path to the pom.xml or build.gradle file for the project. Useful if the project is maven or gradle ' + 241 | 'based and its rootdir does not contain all the *.iml and .idea/modules.xml files' 242 | v argName: 'verbose', longOpt: 'verbose', args: 0, 243 | 'Enable verbose logging' 244 | n argName: 'dry-run', longOpt: 'dry-run', args: 0, 245 | 'Dry-run: Do not start IDEA, but run parsing' 246 | pre argName: 'Command', longOpt: 'preexec', args: Option.UNLIMITED_VALUES, 247 | 'Execute the passed command prior to starting IDEA.' 248 | //to argName: 'minutes', longOpt: 'timeout', args: 1, 249 | // 'Timeout in Minutes to wait for IDEA to complete the inspection. Default:' 250 | } 251 | 252 | def opt = cliBuilder.parse(configArgs) 253 | 254 | if (!opt) { 255 | System.exit(1) 256 | } // will print usage automatically 257 | if (opt.help) { 258 | println "\nThis tools runs IntelliJ IDEA inspection via command line and" 259 | println "tries to parse the output. \n" 260 | println "Example usage:" 261 | println " ./idea-cli-inspector -i ~/devel/idea -r . -p myinspections.xml \\" 262 | println " -d src/main/java -s unused,Annotator,TodoComment.xml -l ERROR\n" 263 | println "For more convenience you can pass all options in a `.ideainspect` file" 264 | println "instead of passing it via command line\n" 265 | cliBuilder.usage() 266 | System.exit(1) 267 | } 268 | if (verbose) { 269 | List optDebug = [] 270 | //noinspection GroovyAssignabilityCheck 271 | for (Option ocliOpt : cliBuilder.options.options) { 272 | optDebug.add(ocliOpt.longOpt << ": " << opt.getProperty(ocliOpt.longOpt)) 273 | } 274 | println "Effective configuration: " << optDebug.join(", ") 275 | } 276 | 277 | opt 278 | } 279 | 280 | private File findIdeaExecutable(OptionAccessor cliOpts) { 281 | def platform = System.properties['os.name'], scriptPath 282 | def ideaHome = getIdeaHome(cliOpts) 283 | def executable = "idea" 284 | if (ideaHome.toLowerCase().contains("android")) executable = "studio" 285 | 286 | switch (platform) { 287 | case ~/^Windows.*/: 288 | scriptPath = "bin" + File.separator + executable + ".bat" 289 | break 290 | case "Mac OS X": 291 | scriptPath = "Contents/MacOS/" + executable 292 | break 293 | default: 294 | scriptPath = "bin/" + executable + ".sh" 295 | break 296 | } 297 | 298 | def ideaExecutable = new File(ideaHome + File.separator + scriptPath) 299 | assertPathIsFile(ideaExecutable, "IDEA Installation directory", 300 | "Use a IDEA_HOME environment variable or the `ideahome` property in `.ideainspect` \n" + 301 | "or the `-i` command line option to point me to a valid IntelliJ installation") 302 | ideaExecutable 303 | } 304 | 305 | private static String getIdeaHome(OptionAccessor cliOpts) { 306 | cliOpts.i ?: (System.getenv("IDEA_HOME") ?: "idea") 307 | } 308 | 309 | /** 310 | * This method workarounds the lack of a CLI argument for the analysis scope by temporarily adding a 311 | * scope parameter in the {@code idea.properties} file. 312 | * @param propertiesPath Path to `idea.properties` 313 | * @param scopeName The scope to apply 314 | * @return The backup path of the original `idea.properties` 315 | */ 316 | private File applyScopeViaPropFile(OptionAccessor cliOpts) { 317 | 318 | if (!cliOpts.sc) return null 319 | String scopeName = cliOpts.sc 320 | 321 | if (cliOpts.n) { 322 | println "\nDry-run: You defined a analysis scope. We now would temporarily modify `idea.properties`." 323 | return null 324 | } 325 | println "\nYou defined a analysis scope. We need to temporarily modify `idea.properties` to get this working." 326 | 327 | File ideaPropsFile = findIdeaProperties(cliOpts) 328 | def newPropsContent = new ArrayList() 329 | File propertiesBackupFile = null 330 | 331 | if (ideaPropsFile.exists()) { 332 | // If the file already exists we copy it 333 | propertiesBackupFile = new File(ideaPropsFile.absolutePath + ".idea-cli-inspect." + System.currentTimeMillis()) 334 | Files.copy(ideaPropsFile.toPath(), propertiesBackupFile.toPath()) 335 | List lines = ideaPropsFile.readLines() 336 | for (line in lines) { 337 | if (!line.contains("idea.analyze.scope")) newPropsContent.add(line) 338 | } 339 | } 340 | 341 | // If the file does not exist, it is instantiated when written to 342 | newPropsContent.add("idea.analyze.scope=" + scopeName) 343 | ideaPropsFile.write(newPropsContent.join('\n')) 344 | 345 | def backupPath = propertiesBackupFile?.path ?: "" 346 | println "Added scope `" + scopeName + "` to `" + ideaPropsFile.absolutePath + "` Backup: `" + backupPath + "`" 347 | 348 | propertiesBackupFile 349 | } 350 | 351 | /** 352 | * Revert to original IDEA configuration from backup. 353 | */ 354 | private cleanupIdeaProps(OptionAccessor cliOpts, File backupFile) { 355 | if (!cliOpts.sc || backupFile == null) return 356 | File ideaPropsFile = findIdeaProperties(cliOpts) 357 | ideaPropsFile.delete() 358 | if (backupFile?.exists()) { 359 | println "Recovering `" + backupFile.absolutePath + "` back to `" + ideaPropsFile.absolutePath + "`" 360 | backupFile.renameTo(ideaPropsFile) 361 | } else { 362 | println "Deleted temporarily created `" + ideaPropsFile.absolutePath + "`" 363 | } 364 | } 365 | 366 | /** Tries to locate the `idea.properties` file either via convention or via parameters. */ 367 | private File findIdeaProperties(OptionAccessor cliOpts) { 368 | String propertiesPath 369 | if (cliOpts.ip) { 370 | propertiesPath = cliOpts.ip 371 | } else { 372 | propertiesPath = getIdeaHome(cliOpts) + "/bin/idea.properties" 373 | } 374 | def propsFile = new File(propertiesPath) 375 | assertPathIsFile(propsFile, "idea.properties", 376 | "IDEA currently does currently not allow to pass the desired inspection scope as program parameter.\n" + 377 | "Currently the only way is to set a temporary property in the `idea.properties` configuration file\n" + 378 | "of your IntelliJ installation. We did not find that file. Therefore you need to pass the full path\n" + 379 | "to this file if you want to restrict the analysis to a specific scope." 380 | ) 381 | propsFile 382 | } 383 | 384 | @SuppressWarnings("GroovyUntypedAccess") 385 | private analyzeResult(File resultPath, List acceptedLeves, 386 | List skipResults, List skipIssueFilesRegex) { 387 | 388 | printAnalysisHeader(resultPath, acceptedLeves, skipResults, skipIssueFilesRegex) 389 | 390 | def allGood = true 391 | 392 | resultPath.eachFile(FileType.FILES) { file -> 393 | 394 | String fileContents = workaroundUnclosedProblemXmlTags(file.getText('UTF-8')) 395 | 396 | def xmlDocument = new XmlParser().parseText(fileContents) 397 | def fileIssues = [] 398 | def xmlFileName = file.name 399 | 400 | if (skipResults.contains(xmlFileName.replace(".xml", ""))) { 401 | println "--- Skipping $xmlFileName" 402 | return 403 | } 404 | 405 | xmlDocument.problem.each { problem -> 406 | String severity = problem.problem_class.@severity 407 | String affectedFile = problem.file.text() 408 | boolean fileShouldBeIgnored = false 409 | skipIssueFilesRegex.each { String regex -> fileShouldBeIgnored = (fileShouldBeIgnored || affectedFile.matches(regex)) } 410 | if (acceptedLeves.contains(severity) && !fileShouldBeIgnored) { 411 | String problemDesc = problem.description.text() 412 | String line = problem.line.text() 413 | fileIssues << "$severity $affectedFile:$line -- $problemDesc" 414 | } 415 | } 416 | 417 | if (!fileIssues.empty) { 418 | allGood = false 419 | System.err.println("--- $xmlFileName") 420 | System.err.println(fileIssues.join("\n")) 421 | System.err.println("") 422 | } 423 | } 424 | 425 | printAnalysisFooter(allGood) 426 | return allGood ? 0 : 1 427 | } 428 | 429 | private void printAnalysisHeader(File resultPath, List acceptedLeves, List skipResults, List skipIssueFilesRegex) { 430 | println "\n#" 431 | println "# Inspecting produced result files in $resultPath" 432 | println "#" 433 | println "# Looking for levels : $acceptedLeves" 434 | println "# Ignoring result files : $skipResults" 435 | println "# Ignoring source files : $skipIssueFilesRegex" 436 | } 437 | 438 | private void printAnalysisFooter(boolean allGood) { 439 | println "\n#" 440 | println "# Analysis Result" 441 | println "#" 442 | 443 | println allGood ? "Looks great - everything seems to be ok!" 444 | : "Entries found. Returncode: 1" 445 | } 446 | 447 | /** 448 | * Workaround for wrong XML formatting. IDEA clutters "" all-over. 449 | * See Bug report : https://youtrack.jetbrains.com/issue/IDEA-148855 450 | * @param fileContents XML file content string 451 | * @return XML file content with duplicate {@code } entries removed. 452 | */ 453 | private static String workaroundUnclosedProblemXmlTags(String fileContents) { 454 | fileContents = fileContents.replace("", "") 455 | fileContents = fileContents.replace("file://\$PROJECT_DIR\$/", "") 456 | fileContents = "" + fileContents.replaceAll("<.?problems.*>", "") + "" 457 | fileContents 458 | } 459 | 460 | /** 461 | * Fail if the passed File does not exist or is not a directory 462 | * @param path The path to test for being a directory 463 | * @param description A human-readable description what dir we are looking for 464 | * @param hint an optional hint what to do now... 465 | */ 466 | private void assertPathIsDir(File path, String description, String hint = null) { 467 | if (!path.exists() || !path.directory) { 468 | println "PROBLEM: " + description + " `" + path.path + "` not found or not a directory!" 469 | if (hint) println "\n" + hint 470 | println "\nAborting." 471 | System.exit(1) 472 | } 473 | } 474 | /** 475 | * Fail if the passed File does not exist or is not a file 476 | * @param path The path to test for being a file 477 | * @param description A human-readable description what file we are looking for 478 | * @param hint an optional hint what to do now... 479 | */ 480 | private void assertPathIsFile(File path, String description, String hint = null) { 481 | if (!path.exists() || !path.file) { 482 | println "PROBLEM: " + description + " `" + path.path + "` not found or not a file!" 483 | if (hint) println "\n" + hint 484 | println "\nAborting." 485 | System.exit(1) 486 | } 487 | } 488 | 489 | private void fail(String message) { 490 | println "FATAL ERROR: " + message 491 | println "\nAborting." 492 | System.exit(1) 493 | } 494 | --------------------------------------------------------------------------------