├── .gitignore ├── composer.json ├── LICENSE ├── README.md └── coke /.gitignore: -------------------------------------------------------------------------------- 1 | composer.lock 2 | vendor 3 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "m6web/coke", 3 | "description": "PHP Code Sniffer configurator", 4 | "license": "MIT", 5 | "require": { 6 | "squizlabs/php_codesniffer": "~2.3" 7 | }, 8 | "suggest": { 9 | "m6web/symfony2-coding-standard": "Symfony2 PHP CodeSniffer Coding Standard" 10 | }, 11 | "bin": ["coke"] 12 | } 13 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013 M6Web 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is furnished 8 | to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Coke - Enjoy sniffing your code 2 | 3 | Coke is a Shell/Bash command using PHP Code Sniffer allowing rules management per project. 4 | 5 | ## Configuration file 6 | 7 | Create a `.coke` file at your project root : 8 | 9 | ``` 10 | # Command used to launch PHP CodeSniffer (optional - default: phpcs) 11 | command=phpcs 12 | 13 | # Path used to load Standards (optional) 14 | standard-path=path/to/PHPCS/Standards/ 15 | 16 | # Standard used by PHP CodeSniffer (required) 17 | standard=Symfony2 18 | 19 | # Verbose mode (optional - default: false) 20 | verbose=true 21 | 22 | # Only Git changed mode (optional - default: false) 23 | only-git-changed=true 24 | 25 | # White list of files and directories (optional) 26 | src/ 27 | test.php 28 | 29 | # Black list of files and directories (optional) 30 | !Tests 31 | !src/OldFile.php 32 | ``` 33 | 34 | and just launch the command : 35 | 36 | ```shell 37 | $ coke 38 | ``` 39 | 40 | ## Run the command with arguments 41 | 42 | You can override `.coke` settings by passing directly configuration as arguments to the command : 43 | 44 | ```shell 45 | $ coke src test.php --standard=Symfony2 --ignore=Tests,src/OldFile.php -v 46 | ``` 47 | 48 | The order of arguments is not important 49 | 50 | `src test.php` Files/Directories to include in the check 51 | `--standard=Symfony2` Standard to use for check 52 | `--ignore=Tests,src/OldFile.php` URL patterns to ignore in the check 53 | `--only-git-changed` Check only changed files 54 | `-v` Use verbose mode 55 | 56 | 57 | ## Additional arguments 58 | 59 | You can use any phpcs arguments ([documentation](https://github.com/squizlabs/PHP_CodeSniffer/wiki/Configuration-Options)) 60 | 61 | For example if you want to generate a report with your favorite CI tools you can run 62 | 63 | ```shell 64 | $ coke --report-checkstyle=checkstyle.xml 65 | ``` 66 | 67 | ## Installation via composer 68 | 69 | Add coke in the require-dev section of your composer.json : 70 | 71 | ``` 72 | "require-dev": { 73 | "m6web/coke" : "~2.0" 74 | } 75 | ``` 76 | 77 | By default composer will add a symlink to coke in vendor/bin/coke. 78 | 79 | If you want to change it, add this in your composer.json (more information about this in the [composer documentation](http://getcomposer.org/doc/articles/vendor-binaries.md)) : 80 | 81 | ``` 82 | "config": { 83 | "bin-dir": "bin" 84 | } 85 | ``` 86 | 87 | Then you can call coke via : 88 | 89 | ``` 90 | ./bin/coke 91 | ``` 92 | 93 | ## Git pre-commit hook 94 | 95 | You can use a dedicated [pre-commit hook](https://gist.github.com/JJK801/5867810) : 96 | 97 | ``` 98 | $ wget --output-document=.git/hooks/pre-commit https://gist.githubusercontent.com/JJK801/5867810/raw/f26ec4778273b3f7140428252ab31951de2faba4/pre-commit.sh 99 | ``` 100 | 101 | Or 102 | 103 | ``` 104 | $ curl -L https://gist.githubusercontent.com/JJK801/5867810/raw/f26ec4778273b3f7140428252ab31951de2faba4/pre-commit.sh > .git/hooks/pre-commit 105 | ``` 106 | 107 | Then 108 | 109 | ``` 110 | $ chmod +x .git/hooks/pre-commit 111 | ``` 112 | 113 | ## Credits 114 | 115 | Developped by the [Cytron Team](http://cytron.fr/) of [M6 Web](http://tech.m6web.fr/). 116 | 117 | ## License 118 | 119 | Coke is licensed under the [MIT license](LICENSE). 120 | -------------------------------------------------------------------------------- /coke: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | file=".coke" 4 | params="" 5 | options="" 6 | standard="" 7 | standardPath="" 8 | command="phpcs"; 9 | commandPath="" 10 | composerPath="vendor/squizlabs/php_codesniffer/scripts/" 11 | filesAllowed="" 12 | filesIgnored="" 13 | 14 | ignored=0 15 | allowed=0 16 | verbose=0 17 | fix=0 18 | onlyGitChanged=0 19 | 20 | function allow 21 | { 22 | if [ -e "$1" ] 23 | then 24 | if [ -z "$filesAllowed" ] 25 | then 26 | filesAllowed="$1" 27 | else 28 | filesAllowed="$filesAllowed $1" 29 | fi 30 | fi 31 | } 32 | 33 | function ignore 34 | { 35 | if [ -z "$filesIgnored" ] 36 | then 37 | filesIgnored="$1" 38 | else 39 | filesIgnored="$filesIgnored,$1" 40 | fi 41 | } 42 | 43 | function output 44 | { 45 | OUTPUT="[%s%s$1%s] %s$2%s" 46 | 47 | if [ -t 1 ] && [ $- != "*i*" ] && [ -n "$TERM" ] && which tput >/dev/null 48 | then 49 | OUTPUT=$(printf "$OUTPUT" "$(tput bold)" "$(tput setaf $3)" "$(tput sgr0)" "$(tput setaf $4)" "$(tput sgr0)" ) 50 | else 51 | OUTPUT=$(printf "$OUTPUT" "" "" "" "" "" ) 52 | fi 53 | 54 | echo "$OUTPUT" 55 | } 56 | 57 | function success 58 | { 59 | output "SUCCESS" "$1" 2 6 60 | exit 0 61 | } 62 | 63 | function fail 64 | { 65 | output "FAIL" "$1" 1 6 66 | exit 1 67 | } 68 | 69 | function warning 70 | { 71 | output "WARNING" "$1" 3 6 72 | exit 0 73 | } 74 | 75 | function info 76 | { 77 | output "INFO" "$1" 4 6 78 | } 79 | 80 | function debug 81 | { 82 | if [ "$verbose" -eq 1 ] 83 | then 84 | info "$1" 85 | fi 86 | } 87 | 88 | # Resolve options 89 | while [ $# -ne 0 ] 90 | do 91 | CUR_PARAM="$1" 92 | 93 | if [ "${CUR_PARAM:0:2}" = "--" ] # it's a parameter 94 | then 95 | if [ "${CUR_PARAM:2:9}" = "standard=" ] 96 | then 97 | standard="${CUR_PARAM:11}" 98 | elif [ "${CUR_PARAM:2:7}" = "ignore=" ] 99 | then 100 | ignore "${CUR_PARAM:9}" 101 | ignored=1 102 | elif [ "${CUR_PARAM:2:14}" = "standard-path=" ] 103 | then 104 | standardPath="${CUR_PARAM:16}" 105 | elif [ "${CUR_PARAM:2:5}" = "fix" ] 106 | then 107 | fix=1 108 | elif [ "${CUR_PARAM:2:16}" = "only-git-changed" ] 109 | then 110 | onlyGitChanged=1 111 | else 112 | params="$params $CUR_PARAM" 113 | fi 114 | elif [ "${CUR_PARAM:0:1}" = "-" ] # it's an option 115 | then 116 | case "${CUR_PARAM:1:1}" in 117 | v) 118 | verbose=1 119 | ;; 120 | f) 121 | fix=1 122 | ;; 123 | esac 124 | 125 | options="$options ${CUR_PARAM}" 126 | else # it's a file 127 | allow "$CUR_PARAM" 128 | allowed=1 129 | fi 130 | 131 | shift 132 | done 133 | 134 | # Resolve commands path to use 135 | if [ -d "$composerPath" ] 136 | then 137 | debug "Command path used is \"$composerPath\"" 138 | commandPath="$composerPath" 139 | fi 140 | 141 | if [ $fix -eq 1 ] 142 | then 143 | debug "Use phpcbf command instead of phpcs" 144 | command="phpcbf" 145 | fi 146 | 147 | # Resolve files to test 148 | if [ -e "$file" ] 149 | then 150 | output=$( grep "^[^\#]" $file ) 151 | 152 | for ligne in $output 153 | do 154 | if [ "${ligne:0:8}" = "command=" ] 155 | then 156 | command="${ligne:8}" 157 | elif [ -z "$standardPath" ] && [ "${ligne:0:14}" = "standard-path=" ] 158 | then 159 | standardPath="${ligne:14}" 160 | elif [ -z "$standard" ] && [ "${ligne:0:9}" = "standard=" ] 161 | then 162 | standard="${ligne:9}" 163 | elif [ "$verbose" -eq 0 ] && [ "${ligne:0:8}" = "verbose=" ] && [ "${ligne:8}" = "true" ] 164 | then 165 | verbose=1 166 | elif [ "$onlyGitChanged" -eq 0 ] && [ "${ligne:0:17}" = "only-git-changed=" ] && [ "${ligne:17}" = "true" ] 167 | then 168 | onlyGitChanged=1 169 | elif [ "$ignored" -eq 0 ] && [ "${ligne:0:1}" = "!" ] 170 | then 171 | ignore "${ligne:1}" 172 | elif [ "$allowed" -eq 0 ] && [ -n "$ligne" ] 173 | then 174 | allow "$ligne" 175 | fi 176 | done 177 | else 178 | warning "No config file found" 179 | fi 180 | 181 | debug "Starting sniffing" 182 | 183 | # Prepare command 184 | 185 | if [ -n "$filesIgnored" ] 186 | then 187 | debug "Ignore files \"$filesIgnored\"" 188 | filesIgnored="--ignore=$filesIgnored" 189 | fi 190 | 191 | if [ -n "$standard" ] 192 | then 193 | debug "Standard used is \"$standard\"" 194 | standard="--standard=$standard" 195 | else 196 | fail "No coding standard definition provided" 197 | fi 198 | 199 | if [ -n "$standardPath" ] 200 | then 201 | debug "Change standard path to \"$standardPath\"" 202 | $commandPath$command --config-set installed_paths $standardPath 203 | fi 204 | 205 | # Check Git changes 206 | 207 | if [ $onlyGitChanged -eq 1 ] 208 | then 209 | command -v git >/dev/null 2>&1 || { fail "Cannot find git. Git is required when --only-git-changed is used."; } 210 | 211 | filesAllowedAndChanged="" 212 | for file in $(git --no-pager diff --name-only --diff-filter=MARC) 213 | do 214 | allowed=0 215 | 216 | for ligne in $filesAllowed 217 | do 218 | if [ "${ligne:0:1}" = '!' ] && [[ "$file" == *"${ligne:1}"* ]] 219 | then 220 | continue 2 221 | elif [[ "$file" == "$ligne"* ]] 222 | then 223 | allowed=1 224 | fi 225 | done 226 | 227 | if [ "$allowed" -eq 1 ] 228 | then 229 | filesAllowedAndChanged="$filesAllowedAndChanged $file" 230 | fi 231 | done 232 | 233 | filesAllowed=$filesAllowedAndChanged 234 | fi 235 | 236 | # Punch it! 237 | 238 | if [ -n "$filesAllowed" ] 239 | then 240 | debug "Files allowed : \"$filesAllowed\"" 241 | 242 | $commandPath$command $filesAllowed $filesIgnored $standard $options $params 243 | 244 | if [ $? -ne 0 ] 245 | then 246 | fail "Some files do not match with \"${standard:11}\" requirements" 247 | fi 248 | else 249 | warning "There was no file to test" 250 | fi 251 | 252 | success "All files match with \"${standard:11}\" requirements" 253 | 254 | if [ -n "$standardPath" ] 255 | then 256 | debug "Reset standard path" 257 | $commandPath$command --config-delete installed_paths 258 | fi 259 | --------------------------------------------------------------------------------