├── .gitignore ├── .travis.yml ├── LICENSE.txt ├── README.md ├── grunt-maven-logo.svg ├── pom.xml ├── project-resources └── ci-settings.xml └── src ├── main ├── java │ └── pl │ │ └── allegro │ │ └── tdr │ │ └── gruntmaven │ │ ├── AbstractExecutableMojo.java │ │ ├── BaseMavenGruntMojo.java │ │ ├── CleanResourcesMojo.java │ │ ├── CreateResourcesMojo.java │ │ ├── ExecBowerMojo.java │ │ ├── ExecGruntMojo.java │ │ ├── ExecNpmMojo.java │ │ ├── ExecNpmOfflineMojo.java │ │ ├── ExecutableFactory.java │ │ ├── archive │ │ └── TarUtil.java │ │ ├── executable │ │ └── Executable.java │ │ └── resources │ │ ├── Filter.java │ │ ├── Resource.java │ │ └── ResourceCreationException.java └── resources │ └── grunt-maven.json └── test ├── java └── pl │ └── allegro │ └── tdr │ └── gruntmaven │ └── resources │ ├── FilterTest.java │ └── ResourceTest.java └── resources └── test-resource /.gitignore: -------------------------------------------------------------------------------- 1 | .settings/ 2 | 3 | .classpath 4 | .project 5 | .idea/ 6 | *.iml 7 | target/ 8 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: java 2 | jdk: 3 | - openjdk6 4 | matrix: 5 | include: 6 | - jdk: openjdk7 7 | script: "mvn deploy --settings project-resources/ci-settings.xml" 8 | 9 | branches: 10 | only: 11 | - master 12 | 13 | env: 14 | global: 15 | - secure: "IuT3Pny92PfBMd2pZXqmWKneem57iR7rwAmozpTN2mlWnrxgFABE3tZMWDonUTDRzQaE5rplMjj+X9CR1Yo7SUOHdmz4OBhYSXteWk2hshYEJ1Y4NFEz/jSNUvMweivk56jM2Q/RmARDH9IxZ2phkEKVc5Zxraz7s7ReZgHMXUA=" 16 | - secure: "H/bc1P+9TdaY7s2HL0GI7A8h7RIFNrXJ4vFhVhcQPJLZUranXIBBmWHIF2h4eHcxm5GN1TH42o8rIknF4qe/vrjt0+wqvvBMM08PVznm7neZYO8ea1Y4lStnWbbY8s1offTP2NnaJyKCnr+OG+8wltxyS6O0I5f8y7nMhCrfAZ4=" 17 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright 2013 Allegro Group 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # grunt-maven-plugin 2 | 3 | ## Deprecation note 4 | 5 | > **This plugin will no longer be maintained**. I can accept pull requests with some minor improvements and release new 6 | > versions, but there will be no active development. 7 | > 8 | > It has been almost two years since we moved from Maven to Gradle in most of our projects at Allegro. Since we don't use 9 | > the plugin nor even Maven, it is impossible to notice what new features are needed as frontend tools develop. 10 | > 11 | > If you are looking for a simple replacement, try [frontend-maven-plugin](https://github.com/eirslett/frontend-maven-plugin). 12 | 13 | --- 14 | 15 | 16 | **grunt-maven-plugin** plugin allows you to integrate **Grunt** tasks into the **Maven** build process. [**Grunt**](http://gruntjs.com/) is the JavaScript task runner utility. **grunt-maven-plugin** works on both Windows and \*nix systems. 17 | 18 | **grunt-maven-plugin** comes with unique Maven+Grunt Integrated Workflow which removes all impediments present when trying to build project using two different build tools. 19 | 20 | *Version 1.5.0 requires grunt-maven NPM plugin 1.2.0 version for Integrated Workflow to work.* 21 | 22 | ## Prerequisites 23 | 24 | The only required dependency is [**nodejs**](http://nodejs.org/) with **npm**. 25 | Globally installed [**grunt-cli**](http://gruntjs.com/getting-started) is optional and preferred, but not necessary, as installing custom node modules can be problematic in some environments (ex. CI servers). Additional configuration is needed when using local **grunt-cli**. 26 | 27 | **grunt-maven-plugin** can also run `bower install` from [**bower**](http://bower.io/) to install front-end dependencies. 28 | 29 | grunt-maven-plugin is compatible with JDK 6+ and Maven 2.1+. 30 | 31 | ## Motivation 32 | 33 | **grunt-maven-plugin** came to life because I needed a good tool to integrate Maven and Grunt. By good i mean not just firing off a **grunt** process, but a tool that would respect rules from both backend (Maven) and frontend (Grunt) worlds. No *node_modules* in Maven sources, no Maven *src/main/webapp/..* paths in *Gruntfile.js*. 34 | 35 | **grunt-maven-plugin** allows you to create a usual NPM/Grunt project that could be built and understood by any Node developer, and put it somewhere inside Maven project. It can be extracted at any time and nothing should break. On the other side backend developers don't need to worry about pesky *node_modules* wandering around src/ - all dependencies, generated sources and deliverables live in dedicated **target-grunt** directory, doing this part of build the Maven way. 36 | 37 | 38 | ## Usage 39 | 40 | Add **grunt-maven-plugin** to application build process in your *pom.xml*: 41 | 42 | ```xml 43 | 44 | pl.allegro 45 | grunt-maven-plugin 46 | 1.5.0 47 | 48 | 49 | path_to_js_project 50 | 51 | 52 | 53 | --verbose 54 | 55 | 56 | 57 | 58 | http://cnpmjs.org/downloads 59 | 60 | 61 | 62 | 63 | maven-properties.json 64 | 65 | 66 | 67 | 68 | 69 | 70 | create-resources 71 | npm 72 | 73 | bower 74 | grunt 75 | 76 | 77 | 78 | 79 | ``` 80 | 81 | ### Usage with local grunt-cli 82 | 83 | In order to use local **grunt-cli**, add 84 | 85 | ```xml 86 | node_modules/grunt-cli/bin/grunt 87 | true 88 | ``` 89 | 90 | options to plugin configuration and add **grunt-cli** to JS project **package.json**: 91 | 92 | ```javascript 93 | { 94 | "devDependencies": { 95 | "grunt-cli": "~0.1.6", 96 | "grunt": "~0.4.2" 97 | /*...*/ 98 | } 99 | } 100 | ``` 101 | 102 | ### Using NPM in offline mode 103 | 104 | NPM downtimes can be painful for (some) development and (all) release builds. **grunt-maven-plugin** contains 105 | **npm-offline** goal that uses tar-ed *node_modules* instead of running `npm install` during each build. 106 | 107 | **npm-offline** flow: 108 | 109 | * extract `node_modules.tar` in `target-grunt` 110 | * run `npm-install --ignore-scripts` in case there are any dependencies to download that were not tar-ed 111 | * run `npm rebuild` 112 | 113 | If *node_modules* dir already exists in *target-grunt*, it is not overriden. 114 | Offline flow is based on [this blogpost](http://www.letscodejavascript.com/v3/blog/2014/03/the_npm_debacle). 115 | 116 | #### Why only tar, not gz? 117 | 118 | * GIT uses compression internally anyway 119 | * TAR is lightweight and easy to extract 120 | * TAR is easier to diff 121 | 122 | If there are some compelling arguments for compressing this archive, please post an issue and we might add support in future 123 | releases. 124 | 125 | #### Preparing node_modules.tar 126 | 127 | In `target-grunt`: 128 | 129 | ``` 130 | rm -rf node_modules/ 131 | npm install --ignore-scripts 132 | tar cf node_modules.tar node_modules/ 133 | mv node_modules.tar ../src/main/webapp/static/ 134 | ``` 135 | 136 | ### Using linked node_modules 137 | 138 | Removed in 1.3.0 release, use **npm-offline** instead. 139 | 140 | ### Working example 141 | 142 | [Sandbox](https://github.com/kielo/grunt-maven-plugin-sandbox) project contains simple usage example. It is used to PoC/develop/test new features, so it always stays up to date with SNAPSHOT version. 143 | 144 | 145 | ## How it works? 146 | 147 | 1. JavaScript project sources from 148 | 149 | sourceDirectory/jsSourceDirectory (default: src/main/webapp/static) 150 | 151 | are copied to 152 | 153 | gruntBuildDirectory (default: target-grunt) 154 | 155 | Copied directory has to contain **package.json** and **Gruntfile.js** files 156 | 157 | 1. `npm install` is called, fetching all dependencies 158 | 159 | 1. `grunt` is run to complete the build process 160 | 161 | Because the plugin creates its own target dir, it should be added to ignored resources in SCM configuration (like .gitignore). 162 | 163 | ## grunt-maven-plugin options 164 | 165 | Plugin options available in `...` are: 166 | 167 | #### misc 168 | 169 | * **showColors** : should Grunt and npm use color output; defaults to *false* 170 | * **filteredResources** : list of files (or expressions) that will be filtered using **maven-resources-plugin** when creating resources, 171 | remember to exclude those files from integrated workflow config, as else Grunt will override filtered values 172 | * **excludedResources** : list of files (or expressions) that will be excluded when creating resources, 173 | remember to exclude those files from integrated workflow config, as else Grunt will override filtered values 174 | * **disabled** : skip execution of plugin; defaults to *false* 175 | 176 | #### environment 177 | 178 | * **gruntBuildDirectory** : path to Grunt build directory (target for Grunt); defaults to *${basedir}/target-grunt* 179 | * **sourceDirectory** : path to directory containing source JavaScript project directory; defaults to *${basedir}/src/main/webapp* 180 | * **jsSourceDirectory** : name of directory relative to *sourceDirectory*, which contents are going to be copied to *gruntBuildDirectory*; defaults to *static* 181 | * **warTargetDirectory**: name of directory relative to WAR root, where artifacts from **grunt** build will be copied; defaults to *jsSourceDirectory*, use */* for root of WAR 182 | 183 | #### node 184 | 185 | * **nodeExecutable** : name of globally available **node** executable; defaults to *node* 186 | 187 | #### npm 188 | 189 | * **npmExecutable** : name of globally available **npm** executable; defaults to *npm* 190 | * **npmEnvironmentVar** : map of environmental variables passed down to npm install command; might be useful for npm repo customization 191 | * **npmOptions** : list of custom options passed to **npm** when calling `npm install` (defaults to empty) 192 | 193 | #### offline 194 | 195 | * **npmOfflineModulesFile** : name of tar-ed **node_modules** file; defaults to *node_modules.tar* 196 | * **npmOfflineModulesFilePath** : path to **node_modules** file, relative to project basedir; defaults to *sourceDirectory/jsSourceDirectory* 197 | * **npmRebuildOptions** : list of custom options passed to **npm** when calling `npm rebuild` (defaults to empty) 198 | 199 | #### bower 200 | 201 | * **bowerExecutable** : name of globally available **bower** executable; defaults to *bower* 202 | 203 | #### grunt 204 | 205 | * **target** : name of Grunt target to run (defaults to null, default Grunt target is run) 206 | * **gruntOptions** : list of custom options passed to grunt (defaults to empty) 207 | * **ignoreTasksErrors** : ignore failing Grunt tasks errors and finish Maven build with success (ignoring 3 and 6 exit code, more on [Grunt exit codes](http://gruntjs.com/api/exit-codes)) 208 | * **ignoreAllErrors** : ignore all Grunt errors and finish Maven build with success (ignoring all exit codes, more on [Grunt exit codes](http://gruntjs.com/api/exit-codes)) 209 | * **gruntExecutable** : name of **grunt** executable; defaults to *grunt* 210 | * **runGruntWithNode** : if Grunt executable is a js script, it needs to be run using node, ex: `node path/to/grunt`; defaults to *false* 211 | 212 | ## Execution goals 213 | 214 | * **create-resources** : copies all files and *filteredResources* from *sourceDirectory/jsSourceDirectory* to *gruntBuildDirectory* 215 | * **npm** : executes `npm install` in target directory 216 | * **npm-offline** : reuses packed node modules instead of fetching them from npm 217 | * **bower** : executes `bower install` in target directory 218 | * **grunt** : executes Grunt in target directory 219 | * **clean** : deletes *gruntBuildDirectory* 220 | 221 | ## Maven+Grunt Integrated workflow 222 | 223 | Using grunt-maven-plugin is convenient, but there is still a huge gap between frontend and backend development workflow. Various IDEs (Netbeans, IntelliJ Idea, Eclipse) 224 | try to ease webapp development by synchronizing changes made in *src/webapp/* to exploded WAR directory in *target/*. This naive approach works as long as there is no 225 | need to minify JavaScript sources, compile less files or project does not follow "single WAR" rule and can afford using Maven profiles. Rebuilding project each time a 226 | change was made in \*.js is a horrible thing. Fortunately grunt-maven-plugin is a great tool to solve this problem. 227 | 228 | Idea is to ignore IDE mechanisms and run Grunt build each time a change in static files was made. Traditional workflow looks like: 229 | 230 | 1. user changes *src/webapp/static/hello.js* 231 | 1. IDE detects change 232 | 1. IDE copies *hello.js* to *target/_war_name_/static/hello.js* 233 | 234 | This gives little room to integrate other processes in between. Workflow utilizing **grunt-maven-plugin**: 235 | 236 | 1. run [Grunt watch process](https://github.com/gruntjs/grunt-contrib-watch) 237 | 1. user changes *src/webapp/static/hello.js* 238 | 1. Grunt detects changes, copies *hello.js* to *target-grunt/hello.js* 239 | 1. run Grunt tasks, produce *target-grunt/dist/hello.min.js* with source map *target-grunt/dist/hello.map.js* 240 | 1. Grunt copies results to *target/warname/static* 241 | 242 | Now what happens inside *target-grunt* is for us to decide, it can be virtually anything - minification, less compilation, running 243 | JS tests, JS linting. Anything Grunt can do, just like during *heavy* build process. 244 | 245 | ### Configuring Maven 246 | 247 | Since we want grunt-maven-plugin to take control of what ends up in WAR, we need to tell Maven WAR plugin to ignore our statics dir when creating WAR: 248 | 249 | ```xml 250 | 251 | 252 | 253 | maven-war-plugin 254 | 2.3 255 | 256 | jsSourceDirectory/** 257 | 258 | 259 | 260 | 261 | ``` 262 | 263 | ### Configuring Grunt 264 | 265 | **grunt-maven-plugin** has a dedicated NPM Grunt multitasks that make integrated workflow work. 266 | 267 | ```js 268 | grunt.initConfig({ 269 | mavenPrepare: { 270 | options: { 271 | resources: ['**'] 272 | }, 273 | prepare: {} 274 | }, 275 | 276 | mavenDist: { 277 | options: { 278 | warName: '<%= gruntMavenProperties.warName %>', 279 | deliverables: ['**', '!non-deliverable.js'], 280 | gruntDistDir: 'dist' 281 | }, 282 | dist: {} 283 | }, 284 | 285 | gruntMavenProperties: grunt.file.readJSON('grunt-maven.json'), 286 | 287 | watch: { 288 | maven: { 289 | files: ['<%= gruntMavenProperties.filesToWatch %>'], 290 | tasks: 'default' 291 | } 292 | } 293 | }); 294 | 295 | 296 | grunt.loadNpmTasks('grunt-maven'); 297 | 298 | grunt.registerTask('default', ['mavenPrepare', 'jshint', 'karma', 'less', 'uglify', 'mavenDist']); 299 | 300 | ``` 301 | 302 | For more information please consult documentation of [grunt-maven-npm project](https://github.com/allegro/grunt-maven-npm). 303 | 304 | 305 | ### Deep customization of workflow 306 | 307 | It is possible to override inner workflow configuration during runtime. Inner properties are extracted from **pom.xml** 308 | and used internally inside workflow Grunt tasks. They reside in **target-grunt/grunt-maven.json**. 309 | After reading inner properties JSON file, workflow task seeks for **grunt-maven-custom.json** file in 310 | **target-grunt** and overrides original properties with custom ones. 311 | 312 | ### Configuring IDE 313 | 314 | Depending on IDE, synchronization processes behave differently. Having JSP files synchronized is a nice feature, so it is up to you 315 | to decide what should be turned off. In Netbeans and IntelliJ no configuration is needed, they play nicely with new workflow. 316 | 317 | You still have to remember to run Grunt watch process in background so it can monitor changes. It can be run from IDE via grunt-maven-plugin. 318 | Define custom runner that will run: 319 | 320 | mvn grunt:grunt -Dtarget=watch 321 | 322 | You should see process output each time static sources change. 323 | 324 | #### Configuring Eclipse 325 | 326 | Eclipse is a special case. Unfortunately it does not read WAR from Maven target, instead it keeps own file hierarchy. 327 | Eclipse developers on the team should use deep workflow customization to override properties used by others. Proposed 328 | way is to create **grunt-maven-custom.json** in Maven sources and add it to **.gitignore**, so it will not pollute 329 | source repository. Since i have little experience with Eclipse, i would welcome a contribution of sample configuration, 330 | but most probably it is enough to override **targetPath** property. 331 | 332 | 333 | ## Changelog 334 | * **1.5.9** (26.12.2014) 335 | * added option to change destination of all deliverables within WAR 336 | * **1.4.1** (02.08.2014) 337 | * added option to exclude custom resources in create-resources phase 338 | * **1.4.0** (07.07.2014) 339 | * changed default lifecycle bindings 340 | * support for disabling execution based on flag 341 | * **1.3.2** (19.06.2014) 342 | * support for defining npm command line parameters 343 | * fixed bug from 1.3.1 - null pointer when no npm env variables specified 344 | * **1.3.1** (26.05.2014) 345 | * support for Maven 2.1+ and 3.* 346 | * option to specify NPM env variables 347 | * **1.3.0** (22.04.2014) 348 | * **dropped** support for *link-node-modules* 349 | * **1.2.2** (31.03.2014) 350 | * support for npm offline mode 351 | * **1.2.1** (25.02.2014) 352 | * executing `bower install` 353 | * **1.2.0** (07.02.2014) 354 | * new Maven+Grunt NPM multitasks 355 | * **1.1.4** (05.02.2014) 356 | * added option to ignore Grunt task errors (failing tests etc) (#22) 357 | * **1.1.3** (25.01.2014) 358 | * fixed issue with using custom Grunt target and CLI options together (#20) 359 | * **1.1.2** (06.01.2014) 360 | * fixed compatibility issue of 1.1.x branch - once again compatible with JDK 6+ 361 | * **1.1.1** (31.12.2013) 362 | * fixed bug with filtering all resources instead of only chosen 363 | * fixed bug with Windows paths in workflow properties 364 | * **1.1.0** (30.12.2013) 365 | * integrated workflow as a separate, auto-configured Grunt task 366 | * **1.0.4** (8.12.2013) 367 | * explicit declaration of resources filtered on create-resources goal 368 | * **1.0.3** (24.11.2013) 369 | * passing custom options to grunt executable 370 | * ability to use external or preinstalled node_modules 371 | * **1.0.2** (15.10.2013) 372 | * option to disable npm and grunt color output, by default no colors are shown as it looks bad in Maven logs 373 | * **1.0.1** (13.09.2013) 374 | * compatibility with Maven 3.1.x 375 | 376 | 377 | ## License 378 | 379 | **grunt-maven-plugin** is published under [Apache License 2.0](http://www.apache.org/licenses/LICENSE-2.0). 380 | -------------------------------------------------------------------------------- /grunt-maven-logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | image/svg+xml 99 | 102 | 105 | 111 | 112 | 113 | 114 | 115 | 288 | 291 | 294 | 295 | 304 | 305 | 306 | 307 | 309 | 311 | 315 | 319 | 323 | 327 | 331 | 335 | 339 | 343 | 347 | 351 | 355 | 359 | 363 | 367 | 371 | 375 | 379 | 383 | 387 | 391 | 395 | 399 | 403 | 407 | 411 | 415 | 417 | 421 | 422 | 426 | 428 | 430 | 434 | 435 | 437 | 441 | 442 | 443 | 445 | 447 | 451 | 452 | 454 | 458 | 459 | 460 | 464 | 468 | 470 | 474 | 475 | 479 | 483 | 487 | 491 | 495 | 499 | 503 | 507 | 511 | 515 | 519 | 523 | 527 | 531 | 535 | 539 | 543 | 547 | 551 | 555 | 559 | 563 | 567 | 571 | 575 | 579 | 583 | 587 | 591 | 593 | 597 | 598 | 602 | 606 | 608 | 610 | 614 | 615 | 617 | 621 | 622 | 623 | 625 | 627 | 631 | 632 | 634 | 638 | 639 | 640 | 644 | 648 | 652 | 656 | 658 | 662 | 663 | 667 | 671 | 675 | 677 | 681 | 685 | 686 | 690 | 691 | 692 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4.0.0 3 | 4 | 5 | org.sonatype.oss 6 | oss-parent 7 | 7 8 | 9 | 10 | pl.allegro 11 | grunt-maven-plugin 12 | 1.5.2-SNAPSHOT 13 | maven-plugin 14 | 15 | grunt-maven-plugin 16 | Integrates GruntJS with Maven build. 17 | 18 | https://github.com/allegro/grunt-maven-plugin 19 | 20 | 21 | 22 | Adam Dubiel 23 | Allegro Group 24 | 25 | 26 | 27 | 28 | 29 | The Apache Software License, Version 2.0 30 | http://www.apache.org/licenses/LICENSE-2.0.txt 31 | 32 | 33 | 34 | 35 | scm:git:git@github.com:allegro/grunt-maven-plugin.git 36 | scm:git:git@github.com:allegro/grunt-maven-plugin.git 37 | https://github.com/allegro/grunt-maven-plugin 38 | 39 | 40 | 41 | UTF-8 42 | 43 | 44 | 45 | 46 | org.apache.maven 47 | maven-core 48 | 3.1.1 49 | 50 | 51 | org.apache.maven 52 | maven-plugin-api 53 | 3.1.1 54 | 55 | 56 | org.apache.maven.plugin-tools 57 | maven-plugin-tools-annotations 58 | 3.2 59 | 60 | 61 | 62 | org.apache.commons 63 | commons-compress 64 | 1.9 65 | 66 | 67 | commons-io 68 | commons-io 69 | 2.4 70 | 71 | 72 | 73 | 74 | org.twdata.maven 75 | mojo-executor 76 | 1.5.2 77 | 78 | 79 | org.apache.maven.plugins 80 | maven-resources-plugin 81 | 2.6 82 | 83 | 84 | org.apache.maven.plugins 85 | maven-clean-plugin 86 | 2.5 87 | 88 | 89 | 90 | 91 | org.testng 92 | testng 93 | 6.8.7 94 | test 95 | 96 | 97 | 98 | org.assertj 99 | assertj-core 100 | 1.5.0 101 | test 102 | 103 | 104 | 105 | org.mockito 106 | mockito-core 107 | 1.9.5 108 | test 109 | 110 | 111 | 112 | com.googlecode.catch-exception 113 | catch-exception 114 | 1.2.0 115 | test 116 | 117 | 118 | 119 | com.google.guava 120 | guava 121 | 15.0 122 | test 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | org.apache.maven.plugins 131 | maven-compiler-plugin 132 | 2.3.2 133 | 134 | 1.6 135 | 1.6 136 | 137 | 138 | 139 | org.apache.maven.plugins 140 | maven-plugin-plugin 141 | 3.2 142 | 143 | true 144 | 145 | 146 | 147 | mojo-descriptor 148 | process-classes 149 | 150 | descriptor 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | release-sign-artifacts 161 | 162 | 163 | performRelease 164 | true 165 | 166 | 167 | 168 | 169 | 170 | org.apache.maven.plugins 171 | maven-gpg-plugin 172 | 1.4 173 | 174 | 175 | sign-artifacts 176 | verify 177 | 178 | sign 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | sonatype-staging-grunt-maven 191 | https://oss.sonatype.org/service/local/staging/deploy/maven2/ 192 | 193 | 194 | sonatype-snapshots-grunt-maven 195 | https://oss.sonatype.org/content/repositories/snapshots/ 196 | 197 | 198 | 199 | 200 | -------------------------------------------------------------------------------- /project-resources/ci-settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | sonatype-staging-grunt-maven 9 | ${env.NEXUS_USER} 10 | ${env.NEXUS_PASSWORD} 11 | 12 | 13 | sonatype-snapshots-grunt-maven 14 | ${env.NEXUS_USER} 15 | ${env.NEXUS_PASSWORD} 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /src/main/java/pl/allegro/tdr/gruntmaven/AbstractExecutableMojo.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package pl.allegro.tdr.gruntmaven; 17 | 18 | import pl.allegro.tdr.gruntmaven.executable.Executable; 19 | 20 | import java.util.ArrayList; 21 | import java.util.Arrays; 22 | import java.util.List; 23 | import java.util.Map; 24 | 25 | import org.apache.maven.plugin.MojoExecutionException; 26 | import org.apache.maven.plugin.MojoFailureException; 27 | import org.apache.maven.plugins.annotations.Parameter; 28 | import static org.twdata.maven.mojoexecutor.MojoExecutor.*; 29 | 30 | /** 31 | * Abstract mojo which uses MojoExecutor to execute exec-maven-plugin, 32 | * which in turn executes system command - command line only. 33 | * 34 | * Compatible with Windows via 35 | *
cmd /C
. 36 | * 37 | * @author Adam Dubiel 38 | */ 39 | public abstract class AbstractExecutableMojo extends BaseMavenGruntMojo { 40 | 41 | /** 42 | * Windows OS name. 43 | */ 44 | private static final String WINDOWS_OS_FAMILY = "Windows"; 45 | 46 | /** 47 | * Maven executor plugin group id. 48 | */ 49 | private static final String EXEC_MAVEN_GROUP = "org.codehaus.mojo"; 50 | 51 | /** 52 | * Maven executor plugin artifact id. 53 | */ 54 | private static final String EXEC_MAVEN_ARTIFACT = "exec-maven-plugin"; 55 | 56 | /** 57 | * Maven executor plugin goal. 58 | */ 59 | private static final String EXEC_GOAL = "exec"; 60 | 61 | private static final String EXEC_SUCCESS_CODES_ELEMENT = "successCodes"; 62 | 63 | private static final String EXEC_SUCCESS_CODE_ELEMENT = "successCode"; 64 | 65 | /** 66 | * Inject building OS name. 67 | */ 68 | @Parameter(defaultValue = "${os.name}") 69 | private String osName; 70 | 71 | /** 72 | * Should npm/grunt print colors in output (default to false). 73 | */ 74 | @Parameter(property = "showColors", defaultValue = "false") 75 | protected boolean showColors; 76 | 77 | /** 78 | * Version of exec plugin to use (defaults to 1.2.1). 79 | */ 80 | @Parameter(property = "execMavenPluginVersion", defaultValue = "1.2.1") 81 | protected String execMavenPluginVersion; 82 | 83 | @Override 84 | public void executeInternal() throws MojoExecutionException, MojoFailureException { 85 | for (Executable executable : getExecutables()) { 86 | runExecutable(executable); 87 | } 88 | } 89 | 90 | private void runExecutable(Executable executable) throws MojoExecutionException, MojoFailureException { 91 | Element[] configuration = buildConfigForOS(executable); 92 | if (executable.overrideSuccessCodes()) { 93 | Element customSuccessCodes = overwriteSuccessCodes(executable); 94 | configuration = concat(configuration, customSuccessCodes); 95 | } 96 | 97 | executeMojo(plugin( 98 | groupId(EXEC_MAVEN_GROUP), 99 | artifactId(EXEC_MAVEN_ARTIFACT), 100 | version(execMavenPluginVersion)), 101 | goal(EXEC_GOAL), 102 | configuration(configuration), 103 | pluginExecutionEnvironment()); 104 | } 105 | 106 | /** 107 | * Create 108 | *
configuration
element for host OS. 109 | * 110 | * @return
configuration
element 111 | */ 112 | private Element[] buildConfigForOS(Executable executable) { 113 | Element[] configuration; 114 | 115 | getLog().info("OS Name: " + osName); 116 | 117 | if (osName.toUpperCase().contains(WINDOWS_OS_FAMILY.toUpperCase())) { 118 | configuration = buildConfigForWindows(executable); 119 | } else { 120 | configuration = buildConfigForProperOS(executable); 121 | } 122 | 123 | configuration = concat(configuration, new Element[]{element(name("workingDirectory"), gruntBuildDirectory)}); 124 | 125 | if (executable.hasEnvironmentVars()) { 126 | configuration = concat(configuration, element("environmentVariables", elementsFromMap(executable.environmentVars()))); 127 | } 128 | 129 | return configuration; 130 | } 131 | 132 | /** 133 | * Create 134 | *
configuration
element for proper *nix OSes. 135 | * 136 | * @return configuration 137 | */ 138 | private Element[] buildConfigForProperOS(Executable executable) { 139 | Element[] osConfiguration = new Element[]{ 140 | element(name("executable"), executable.executableName()), 141 | element(name("arguments"), executable.argumentsArray()) 142 | }; 143 | 144 | return osConfiguration; 145 | } 146 | 147 | /** 148 | * Create 149 | *
configuration
element for strange Windows OS. 150 | * 151 | * @return configuration 152 | */ 153 | private Element[] buildConfigForWindows(Executable executable) { 154 | Element[] arguments = new Element[]{ 155 | element(name("argument"), "/C"), 156 | element(name("argument"), executable.executableName()) 157 | }; 158 | arguments = concat(arguments, executable.argumentsArray()); 159 | 160 | Element[] osConfiguration = new Element[]{ 161 | element(name("executable"), "cmd"), 162 | element(name("arguments"), arguments) 163 | }; 164 | 165 | return osConfiguration; 166 | } 167 | 168 | /** 169 | * Don't want to use any dependencies like Apache Commons - sorry. 170 | * 171 | * @param type of array entries 172 | * @param array1 first array 173 | * @param array2 second array 174 | * @return first array + second array 175 | */ 176 | protected T[] concat(T[] array1, T... array2) { 177 | T[] result = Arrays.copyOf(array1, array1.length + array2.length); 178 | System.arraycopy(array2, 0, result, array1.length, array2.length); 179 | return result; 180 | } 181 | 182 | private Element[] elementsFromMap(Map elements) { 183 | List elementList = new ArrayList(); 184 | for (Map.Entry entry : elements.entrySet()) { 185 | elementList.add(element(entry.getKey(), entry.getValue())); 186 | } 187 | 188 | return elementList.toArray(new Element[elementList.size()]); 189 | } 190 | 191 | /** 192 | * Return executable form maven exec 193 | *
executable element
, return 194 | * executable name for *nix OS, not Windows! 195 | * 196 | * @return executable name 197 | */ 198 | protected abstract List getExecutables(); 199 | 200 | private Element overwriteSuccessCodes(Executable executable) { 201 | Element[] successCodesElements = new Element[executable.successCodes().length]; 202 | for (int index = 0; index < executable.successCodes().length; ++index) { 203 | successCodesElements[index] = element(EXEC_SUCCESS_CODE_ELEMENT, executable.successCodes()[index]); 204 | } 205 | return element(EXEC_SUCCESS_CODES_ELEMENT, successCodesElements); 206 | } 207 | } 208 | -------------------------------------------------------------------------------- /src/main/java/pl/allegro/tdr/gruntmaven/BaseMavenGruntMojo.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package pl.allegro.tdr.gruntmaven; 17 | 18 | import java.io.File; 19 | import java.io.IOException; 20 | import org.apache.maven.execution.MavenSession; 21 | import org.apache.maven.plugin.AbstractMojo; 22 | import org.apache.maven.plugin.BuildPluginManager; 23 | import org.apache.maven.plugin.MojoExecutionException; 24 | import org.apache.maven.plugin.MojoFailureException; 25 | import org.apache.maven.plugin.PluginManager; 26 | import org.apache.maven.plugins.annotations.Component; 27 | import org.apache.maven.plugins.annotations.Parameter; 28 | import org.apache.maven.project.MavenProject; 29 | import org.codehaus.plexus.component.repository.exception.ComponentLookupException; 30 | import org.twdata.maven.mojoexecutor.MojoExecutor; 31 | 32 | /** 33 | * Common properties for all maven-grunt goals. 34 | * 35 | * @author Adam Dubiel 36 | */ 37 | public abstract class BaseMavenGruntMojo extends AbstractMojo { 38 | 39 | /** 40 | * Path to build directory (target for grunt sources), defaults to ${basedir}/target-grunt. 41 | */ 42 | @Parameter(property = "gruntBuildDirectory", defaultValue = "${basedir}/target-grunt") 43 | protected String gruntBuildDirectory; 44 | 45 | /** 46 | * Path to dir, where jsSourceDir is located, defaults to src/main/webapp. 47 | */ 48 | @Parameter(property = "sourceDirectory", defaultValue = "src/main/webapp") 49 | protected String sourceDirectory; 50 | 51 | /** 52 | * Path to dir from where to copy all files that add to grunt environment - has to include package.json and Gruntfile.js, defaults to 53 | * "static". 54 | */ 55 | @Parameter(property = "jsSourceDirectory", defaultValue = "static") 56 | protected String jsSourceDirectory; 57 | 58 | /** 59 | * Path to dir inside WAR to which grunt build artifacts will be copied, defaults to jsSourceDirectory value. 60 | */ 61 | @Parameter(property = "warTargetDirectory") 62 | protected String warTargetDirectory = null; 63 | 64 | /** 65 | * Name of packed node_modules TAR file, defaults to node_modules.tar. 66 | */ 67 | @Parameter(property = "npmOfflineModulesFile", defaultValue = "node_modules.tar") 68 | protected String npmOfflineModulesFile; 69 | 70 | @Parameter(property = "disabled", defaultValue = "false") 71 | private boolean disabled; 72 | 73 | /** 74 | * Path to packed node_modules TAR file directory relative to basedir, defaults to statics directory (ex webapp/static/). 75 | */ 76 | @Parameter(property = "npmOfflineModulesFilePath", defaultValue = "") 77 | protected String npmOfflineModulesFilePath; 78 | 79 | @Parameter(property = "project", readonly = true, required = true) 80 | private MavenProject mavenProject; 81 | 82 | @Parameter(property = "session", readonly = true, required = true) 83 | private MavenSession mavenSession; 84 | 85 | /** 86 | * Maven 2.x compatibility. 87 | */ 88 | @Component 89 | @SuppressWarnings("deprecation") 90 | private PluginManager pluginManager; 91 | 92 | @Override 93 | public void execute() throws MojoExecutionException, MojoFailureException { 94 | initializeParameters(); 95 | if (!disabled) { 96 | executeInternal(); 97 | } else { 98 | getLog().info("Execution disabled using configuration option."); 99 | } 100 | } 101 | 102 | protected abstract void executeInternal() throws MojoExecutionException, MojoFailureException; 103 | 104 | private void initializeParameters() { 105 | if (warTargetDirectory == null) { 106 | warTargetDirectory = jsSourceDirectory; 107 | } 108 | } 109 | 110 | protected String basedir() { 111 | try { 112 | return mavenProject.getBasedir().getCanonicalPath(); 113 | } catch (IOException exception) { 114 | throw new IllegalStateException("Could not extract basedir of project.", exception); 115 | } 116 | } 117 | 118 | protected String target() { 119 | return mavenProject.getBuild().getDirectory(); 120 | } 121 | 122 | protected String fullJsSourceDirectory() { 123 | return basedir() + File.separator + sourceDirectory + File.separator + jsSourceDirectory; 124 | } 125 | 126 | protected String relativeJsSourceDirectory() { 127 | return sourceDirectory + File.separator + jsSourceDirectory; 128 | } 129 | 130 | protected MojoExecutor.ExecutionEnvironment pluginExecutionEnvironment() { 131 | MojoExecutor.ExecutionEnvironment environment; 132 | try { 133 | Object o = mavenSession.lookup("org.apache.maven.plugin.BuildPluginManager"); 134 | environment = MojoExecutor.executionEnvironment(mavenProject, mavenSession, (BuildPluginManager) o); 135 | } catch (ComponentLookupException e) { 136 | environment = MojoExecutor.executionEnvironment(mavenProject, mavenSession, pluginManager); 137 | } 138 | return environment; 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /src/main/java/pl/allegro/tdr/gruntmaven/CleanResourcesMojo.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package pl.allegro.tdr.gruntmaven; 17 | 18 | import org.apache.maven.plugin.MojoExecutionException; 19 | import org.apache.maven.plugin.MojoFailureException; 20 | import org.apache.maven.plugins.annotations.LifecyclePhase; 21 | import org.apache.maven.plugins.annotations.Mojo; 22 | import org.apache.maven.plugins.annotations.Parameter; 23 | import static org.twdata.maven.mojoexecutor.MojoExecutor.artifactId; 24 | import static org.twdata.maven.mojoexecutor.MojoExecutor.configuration; 25 | import static org.twdata.maven.mojoexecutor.MojoExecutor.element; 26 | import static org.twdata.maven.mojoexecutor.MojoExecutor.executeMojo; 27 | import static org.twdata.maven.mojoexecutor.MojoExecutor.goal; 28 | import static org.twdata.maven.mojoexecutor.MojoExecutor.groupId; 29 | import static org.twdata.maven.mojoexecutor.MojoExecutor.name; 30 | import static org.twdata.maven.mojoexecutor.MojoExecutor.plugin; 31 | import static org.twdata.maven.mojoexecutor.MojoExecutor.version; 32 | 33 | /** 34 | * Mojo executing clean task on Grunt build directory. 35 | * 36 | * @author Adam Dubiel 37 | */ 38 | @Mojo(name = "clean", defaultPhase = LifecyclePhase.CLEAN, threadSafe = true) 39 | public class CleanResourcesMojo extends BaseMavenGruntMojo { 40 | 41 | /** 42 | * Clean plugin groupId. 43 | */ 44 | private static final String CLEAN_MAVEN_GROUP = "org.apache.maven.plugins"; 45 | 46 | /** 47 | * Clean plugin artefactId. 48 | */ 49 | private static final String CLEAN_MAVEN_ARTIFACT = "maven-clean-plugin"; 50 | 51 | /** 52 | * Clean plugin goal. 53 | */ 54 | private static final String CLEAN_GOAL = "clean"; 55 | 56 | /** 57 | * Change clean plugin version if needed, defaults to 2.5. 58 | */ 59 | @Parameter(property = "mavenCleanPluginVersion", defaultValue = "2.5") 60 | protected String mavenCleanPluginVersion; 61 | 62 | @Override 63 | public void executeInternal() throws MojoExecutionException, MojoFailureException { 64 | executeMojo(plugin( 65 | groupId(CLEAN_MAVEN_GROUP), 66 | artifactId(CLEAN_MAVEN_ARTIFACT), 67 | version(mavenCleanPluginVersion)), 68 | goal(CLEAN_GOAL), 69 | configuration( 70 | element( 71 | name("filesets"), 72 | element(name("fileset"), 73 | element(name("directory"), gruntBuildDirectory))), 74 | element(name("excludeDefaultDirectories"), "true")), 75 | pluginExecutionEnvironment()); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/main/java/pl/allegro/tdr/gruntmaven/CreateResourcesMojo.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package pl.allegro.tdr.gruntmaven; 17 | 18 | import java.io.File; 19 | import java.util.ArrayList; 20 | import java.util.List; 21 | import org.apache.maven.plugin.MojoExecutionException; 22 | import org.apache.maven.plugin.MojoFailureException; 23 | import org.apache.maven.plugins.annotations.LifecyclePhase; 24 | import org.apache.maven.plugins.annotations.Mojo; 25 | import org.apache.maven.plugins.annotations.Parameter; 26 | import pl.allegro.tdr.gruntmaven.resources.Resource; 27 | import static org.twdata.maven.mojoexecutor.MojoExecutor.*; 28 | 29 | /** 30 | * MOJO executing maven-resources-plugin to create target/{jsTargetDir} directory containing all statics. 31 | * 32 | * @author Adam Dubiel 33 | */ 34 | @Mojo(name = "create-resources", defaultPhase = LifecyclePhase.GENERATE_SOURCES, threadSafe = true) 35 | public class CreateResourcesMojo extends BaseMavenGruntMojo { 36 | 37 | private static final String INNER_PROPERTIES_RESOURCE_NAME = "grunt-maven.json"; 38 | 39 | private static final int FILTERED_RESOURCES_JSON_LENGTH = 100; 40 | 41 | /** 42 | * Resources plugin groupId. 43 | */ 44 | private static final String RESOURCES_MAVEN_GROUP = "org.apache.maven.plugins"; 45 | 46 | /** 47 | * Reosurces plugin artefactId. 48 | */ 49 | private static final String RESOURCES_MAVEN_ARTIFACT = "maven-resources-plugin"; 50 | 51 | /** 52 | * Resources plugin version. 53 | */ 54 | private static final String RESOURCES_GOAL = "copy-resources"; 55 | 56 | /** 57 | * Change resources plugin version if needed, defaults to 2.6. 58 | */ 59 | @Parameter(property = "mavenResourcesPluginVersion", defaultValue = "2.6") 60 | private String mavenResourcesPluginVersion; 61 | 62 | /** 63 | * Should copy-resources plugin overwrite resources even if target has newer version (see maven-resources-plugin documentation for more 64 | * details), defaults to true. 65 | */ 66 | @Parameter(property = "overwriteResources", defaultValue = "true") 67 | private boolean overwriteResources; 68 | 69 | /** 70 | * Name of resources that should be filtered by Maven. When using integrated workflow, be sure to make Grunt ignore there resources, as 71 | * it will overwrite filtered values. 72 | */ 73 | @Parameter(property = "filteredResources") 74 | private String[] filteredResources; 75 | 76 | /** 77 | * Resources that should not be copied during create-resources phase. 78 | */ 79 | @Parameter(property = "excludedResources") 80 | private String[] excludedResources; 81 | 82 | @Override 83 | public void executeInternal() throws MojoExecutionException, MojoFailureException { 84 | executeMojo(plugin( 85 | groupId(RESOURCES_MAVEN_GROUP), 86 | artifactId(RESOURCES_MAVEN_ARTIFACT), 87 | version(mavenResourcesPluginVersion)), 88 | goal(RESOURCES_GOAL), 89 | configuration( 90 | element(name("overwrite"), Boolean.toString(overwriteResources)), 91 | element(name("outputDirectory"), gruntBuildDirectory), 92 | element(name("resources"), createResourceElements()) 93 | ), 94 | pluginExecutionEnvironment()); 95 | 96 | createWorkflowTasksDirectory(); 97 | createInnerPropertiesResource(); 98 | } 99 | 100 | private Element[] createResourceElements() { 101 | List resourceElements = new ArrayList(2); 102 | 103 | Element normalResourcesElement = element(name("resource"), 104 | element(name("directory"), sourceDirectory + "/" + jsSourceDirectory), 105 | element(name("includes"), 106 | element(name("include"), "**/*") 107 | ), 108 | element(name("excludes"), 109 | createResourcesListElement(excludedResources, "exclude", element(name("exclude"), "**/" + npmOfflineModulesFile)) 110 | ), 111 | element(name("filtering"), "false") 112 | ); 113 | resourceElements.add(normalResourcesElement); 114 | 115 | if (filteredResources.length > 0) { 116 | Element filteredResourcesElement = element(name("resource"), 117 | element(name("directory"), sourceDirectory + "/" + jsSourceDirectory), 118 | element(name("includes"), createResourcesListElement(filteredResources, "include")), 119 | element(name("filtering"), "true") 120 | ); 121 | resourceElements.add(filteredResourcesElement); 122 | } 123 | 124 | return resourceElements.toArray(new Element[resourceElements.size()]); 125 | } 126 | 127 | private void createWorkflowTasksDirectory() { 128 | File file = new File(pathToWorkflowTasksDirectory()); 129 | if (!file.exists()) { 130 | file.mkdirs(); 131 | } 132 | } 133 | 134 | private Element[] createResourcesListElement(String[] resources, String elementName, Element... append) { 135 | Element[] elements = new Element[resources.length + append.length]; 136 | 137 | int index = 0; 138 | for (; index < resources.length; ++index) { 139 | elements[index] = element(name(elementName), resources[index]); 140 | } 141 | for (int appendIndex = 0; appendIndex < append.length; appendIndex++, ++index) { 142 | elements[index] = append[appendIndex]; 143 | } 144 | 145 | return elements; 146 | } 147 | 148 | private void createInnerPropertiesResource() { 149 | Resource.from("/" + INNER_PROPERTIES_RESOURCE_NAME, getLog()) 150 | .withFilter("filesToWatch", pathToWatchDirectory() + File.separator + "**") 151 | .withFilter("directoryToWatch", pathToWatchDirectory()) 152 | .withFilter("projectRootPath", basedir()) 153 | .withFilter("targetPath", target()) 154 | .withFilter("sourceDirectory", sourceDirectory) 155 | .withFilter("jsSourceDirectory", jsSourceDirectory) 156 | .withFilter("warTargetDirectory", warTargetDirectory) 157 | .withFilter("filteredFiles", filteredResourcesAsJSONArray()) 158 | .copyAndOverwrite(pathToWorkflowTasksDirectory() + INNER_PROPERTIES_RESOURCE_NAME); 159 | } 160 | 161 | private String filteredResourcesAsJSONArray() { 162 | StringBuilder builder = new StringBuilder(FILTERED_RESOURCES_JSON_LENGTH); 163 | builder.append("["); 164 | 165 | builder.append("\"").append("**/").append(npmOfflineModulesFile).append("\"").append(", "); 166 | for (int index = 0; index < filteredResources.length; ++index) { 167 | builder.append("\"").append(filteredResources[index]).append("\"").append(", "); 168 | } 169 | for (int index = 0; index < excludedResources.length; ++index) { 170 | builder.append("\"").append(excludedResources[index]).append("\"").append(", "); 171 | } 172 | builder.delete(builder.length() - 2, builder.length()); 173 | 174 | builder.append("]"); 175 | return builder.toString(); 176 | } 177 | 178 | private String pathToWatchDirectory() { 179 | return fullJsSourceDirectory(); 180 | } 181 | 182 | private String pathToWorkflowTasksDirectory() { 183 | return pathToGruntBuildDirectory() + File.separator; 184 | } 185 | 186 | private String pathToGruntBuildDirectory() { 187 | return gruntBuildDirectory + File.separator; 188 | } 189 | } 190 | -------------------------------------------------------------------------------- /src/main/java/pl/allegro/tdr/gruntmaven/ExecBowerMojo.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package pl.allegro.tdr.gruntmaven; 17 | 18 | import java.util.Arrays; 19 | import java.util.List; 20 | import org.apache.maven.plugins.annotations.LifecyclePhase; 21 | import org.apache.maven.plugins.annotations.Mojo; 22 | import org.apache.maven.plugins.annotations.Parameter; 23 | 24 | import pl.allegro.tdr.gruntmaven.executable.Executable; 25 | 26 | /** 27 | * Executes bower install to download all dependencies declared in bower.json. 28 | */ 29 | @Mojo(name = "bower", defaultPhase = LifecyclePhase.COMPILE, threadSafe = true) 30 | public class ExecBowerMojo extends AbstractExecutableMojo { 31 | 32 | private static final String BOWER_INSTALL_COMMAND = "install"; 33 | private static final String BOWER_NON_INTERACTIVE_MODE = "--config.interactive=false"; 34 | 35 | /** 36 | * Name of bower executable in PATH, defaults to bower. 37 | */ 38 | @Parameter(property = "bowerExecutable", defaultValue = "bower") 39 | private String bowerExecutable; 40 | 41 | /** 42 | * List of options passed to bower. 43 | */ 44 | @Parameter(property = "bowerOptions") 45 | private String[] bowerOptions; 46 | 47 | @Override 48 | protected List getExecutables() { 49 | Executable executable = new Executable(bowerExecutable); 50 | 51 | executable.addArgument(BOWER_INSTALL_COMMAND); 52 | executable.addArgument(BOWER_NON_INTERACTIVE_MODE); 53 | if (!showColors) { 54 | executable.addArgument("--color=false"); 55 | } 56 | 57 | for(String opt: bowerOptions) { 58 | executable.addNormalizedArgument(opt, "="); 59 | } 60 | 61 | return Arrays.asList(executable); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/main/java/pl/allegro/tdr/gruntmaven/ExecGruntMojo.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package pl.allegro.tdr.gruntmaven; 17 | 18 | import java.util.Arrays; 19 | import java.util.List; 20 | import org.apache.maven.plugins.annotations.LifecyclePhase; 21 | import org.apache.maven.plugins.annotations.Mojo; 22 | import org.apache.maven.plugins.annotations.Parameter; 23 | import pl.allegro.tdr.gruntmaven.executable.Executable; 24 | 25 | /** 26 | * Executes grunt. 27 | * 28 | * @author Adam Dubiel 29 | */ 30 | @Mojo(name = "grunt", defaultPhase = LifecyclePhase.COMPILE, threadSafe = true) 31 | public class ExecGruntMojo extends AbstractExecutableMojo { 32 | 33 | /** 34 | * Custom success codes to be registered in maven-exec plugin when we want to ignore 35 | * Grunt errors. 36 | */ 37 | private static final String[] IGNORE_GRUNT_TASKS_ERRORS_CUSTOM_CODES = {"0", "3", "6"}; 38 | 39 | private static final String[] IGNORE_ALL_GRUNT_ERRORS_CUSTOM_CODES = {"0", "1", "2", "3", "4", "5", "6"}; 40 | 41 | /** 42 | * Name of grunt target, will be passed directly to grunt. 43 | */ 44 | @Parameter(property = "target", defaultValue = "") 45 | private String target; 46 | 47 | /** 48 | * Name of node executable in PATH, defaults to node. 49 | */ 50 | @Parameter(property = "nodeExecutable", defaultValue = "node") 51 | private String nodeExecutable; 52 | 53 | /** 54 | * Path to local grunt executable, defaults to grunt (global PATH). 55 | */ 56 | @Parameter(property = "gruntExecutable", defaultValue = "grunt") 57 | private String gruntExecutable; 58 | 59 | /** 60 | * Should grunt be run as node module (ex: node node_modules/grunt-cli/bin/grunt). 61 | * By default grunt is run as independent command line executable. 62 | */ 63 | @Parameter(property = "runGruntWithNode", defaultValue = "false") 64 | private boolean runGruntWithNode; 65 | 66 | /** 67 | * List of options passed to grunt. 68 | */ 69 | @Parameter(property = "gruntOptions") 70 | private String[] gruntOptions; 71 | 72 | /** 73 | * Should Maven ignore grunt task errors (for example failing tests) and always finish grunt execution with success. 74 | */ 75 | @Parameter(property = "ignoreTasksErrors", defaultValue = "false") 76 | private boolean ignoreTasksErrors; 77 | 78 | /** 79 | * Should Maven ignore all grunt errors including fatal ones and always finish grunt execution with success. 80 | */ 81 | @Parameter(property = "ignoreAllErrors", defaultValue = "false") 82 | private boolean ignoreAllErrors; 83 | 84 | @Override 85 | protected List getExecutables() { 86 | Executable executable; 87 | if (runGruntWithNode) { 88 | executable = new Executable(nodeExecutable, customSuccessCodes()); 89 | } else { 90 | executable = new Executable(gruntExecutable, customSuccessCodes()); 91 | } 92 | 93 | appendArguments(executable); 94 | 95 | return Arrays.asList(executable); 96 | } 97 | 98 | private void appendArguments(Executable executable) { 99 | if (runGruntWithNode) { 100 | executable.addArgument(gruntExecutable); 101 | } 102 | if (target != null && !target.isEmpty()) { 103 | executable.addArgument(target); 104 | } 105 | if (!showColors) { 106 | executable.addArgument("--no-color"); 107 | } 108 | if (gruntOptions != null) { 109 | for (String option : gruntOptions) { 110 | executable.addNormalizedArgument(option, "="); 111 | } 112 | } 113 | } 114 | 115 | private String[] customSuccessCodes() { 116 | if (ignoreAllErrors) { 117 | return IGNORE_ALL_GRUNT_ERRORS_CUSTOM_CODES; 118 | } else if (ignoreTasksErrors) { 119 | return IGNORE_GRUNT_TASKS_ERRORS_CUSTOM_CODES; 120 | } 121 | return null; 122 | } 123 | 124 | } 125 | -------------------------------------------------------------------------------- /src/main/java/pl/allegro/tdr/gruntmaven/ExecNpmMojo.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package pl.allegro.tdr.gruntmaven; 17 | 18 | import org.apache.maven.plugins.annotations.LifecyclePhase; 19 | import org.apache.maven.plugins.annotations.Mojo; 20 | import org.apache.maven.plugins.annotations.Parameter; 21 | import pl.allegro.tdr.gruntmaven.executable.Executable; 22 | 23 | import java.util.Arrays; 24 | import java.util.List; 25 | import java.util.Map; 26 | 27 | /** 28 | * Executes npm install to download all dependencies declared in 29 | * package.json. 30 | * 31 | * @author Adam Dubiel 32 | */ 33 | @Mojo(name = "npm", defaultPhase = LifecyclePhase.COMPILE, threadSafe = true) 34 | public class ExecNpmMojo extends AbstractExecutableMojo { 35 | 36 | protected static final String NPM_INSTALL_COMMAND = "install"; 37 | 38 | /** 39 | * Name of npm executable in PATH, defaults to npm. 40 | */ 41 | @Parameter(property = "npmExecutable", defaultValue = "npm") 42 | protected String npmExecutable; 43 | 44 | /** 45 | * List of additional options passed to npm when calling install. 46 | */ 47 | @Parameter(property = "npmOptions") 48 | private String[] npmOptions; 49 | 50 | /** 51 | * Map of environment variables passed to npm install. 52 | */ 53 | @Parameter 54 | protected Map npmEnvironmentVar; 55 | 56 | @Override 57 | protected List getExecutables() { 58 | Executable executable = new Executable(npmExecutable); 59 | 60 | executable.addEnvironmentVars(npmEnvironmentVar); 61 | 62 | executable.addArgument(NPM_INSTALL_COMMAND); 63 | appendNoColorsArgument(executable); 64 | appendNpmOptions(executable); 65 | 66 | return Arrays.asList(executable); 67 | } 68 | 69 | protected void appendNpmOptions(Executable executable) { 70 | executable.addNormalizedArguments(npmOptions, "="); 71 | } 72 | 73 | protected void appendNoColorsArgument(Executable executable) { 74 | if (!showColors) { 75 | executable.addArgument("--color=false"); 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/main/java/pl/allegro/tdr/gruntmaven/ExecNpmOfflineMojo.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package pl.allegro.tdr.gruntmaven; 17 | 18 | import java.io.*; 19 | import java.util.Arrays; 20 | import java.util.List; 21 | import org.apache.maven.plugins.annotations.LifecyclePhase; 22 | import org.apache.maven.plugins.annotations.Mojo; 23 | import org.apache.maven.plugins.annotations.Parameter; 24 | import pl.allegro.tdr.gruntmaven.archive.TarUtil; 25 | import pl.allegro.tdr.gruntmaven.executable.Executable; 26 | 27 | /** 28 | * Run NPM rebuild. 29 | * 30 | * @author Adam Dubiel 31 | */ 32 | @Mojo(name = "npm-offline", defaultPhase = LifecyclePhase.COMPILE, threadSafe = true) 33 | public class ExecNpmOfflineMojo extends ExecNpmMojo { 34 | 35 | private static final String NODE_MODULES_DIR_NAME = "node_modules"; 36 | 37 | private static final String NPM_REBUILD_COMMAND = "rebuild"; 38 | 39 | /** 40 | * List of additional options passed to npm when callign rebuild. 41 | */ 42 | @Parameter(property = "npmRebuildOptions") 43 | private String[] npmRebuildOptions; 44 | 45 | @Override 46 | protected List getExecutables() { 47 | unpackModules(); 48 | return Arrays.asList(createNpmInstallExecutable(), createNpmRebuildExecutable()); 49 | } 50 | 51 | private void unpackModules() { 52 | String nodeModulesPath = gruntBuildDirectory + File.separator + NODE_MODULES_DIR_NAME; 53 | File targetModulesPath = new File(nodeModulesPath); 54 | if (targetModulesPath.exists()) { 55 | getLog().info("Found existing node_modules at " + nodeModulesPath + " , not going to overwrite them."); 56 | return; 57 | } 58 | 59 | if (npmOfflineModulesFilePath == null) { 60 | npmOfflineModulesFilePath = relativeJsSourceDirectory(); 61 | } 62 | 63 | File offlineModules = new File(basedir() + File.separator + npmOfflineModulesFilePath + File.separator + npmOfflineModulesFile); 64 | File targetPath = new File(gruntBuildDirectory); 65 | 66 | TarUtil.untar(offlineModules, targetPath, getLog()); 67 | } 68 | 69 | private Executable createNpmInstallExecutable() { 70 | Executable executable = new Executable(npmExecutable); 71 | executable.addArgument(NPM_INSTALL_COMMAND); 72 | executable.addArgument("--ignore-scripts"); 73 | appendNoColorsArgument(executable); 74 | appendNpmOptions(executable); 75 | 76 | executable.addEnvironmentVars(npmEnvironmentVar); 77 | 78 | return executable; 79 | } 80 | 81 | private Executable createNpmRebuildExecutable() { 82 | Executable executable = new Executable(npmExecutable); 83 | executable.addArgument(NPM_REBUILD_COMMAND); 84 | appendNoColorsArgument(executable); 85 | executable.addNormalizedArguments(npmRebuildOptions, "="); 86 | 87 | return executable; 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /src/main/java/pl/allegro/tdr/gruntmaven/ExecutableFactory.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 Adam Dubiel. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package pl.allegro.tdr.gruntmaven; 18 | 19 | /** 20 | * 21 | * @author Adam Dubiel 22 | */ 23 | public class ExecutableFactory { 24 | 25 | 26 | 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/pl/allegro/tdr/gruntmaven/archive/TarUtil.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 Adam Dubiel. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package pl.allegro.tdr.gruntmaven.archive; 17 | 18 | import org.apache.commons.compress.archivers.ArchiveEntry; 19 | import org.apache.commons.compress.archivers.tar.TarArchiveInputStream; 20 | import org.apache.commons.io.IOUtils; 21 | import org.apache.maven.plugin.logging.Log; 22 | 23 | import java.io.*; 24 | 25 | /** 26 | * 27 | * @author Adam Dubiel 28 | */ 29 | public final class TarUtil { 30 | 31 | private TarUtil() { 32 | } 33 | 34 | public static void untar(File source, File target, Log logger) { 35 | TarArchiveInputStream tarInput = null; 36 | ArchiveEntry entry; 37 | OutputStream output = null; 38 | 39 | try { 40 | tarInput = new TarArchiveInputStream(new FileInputStream(source)); 41 | 42 | entry = tarInput.getNextEntry(); 43 | while (entry != null) { 44 | File outputFile = new File(target.getCanonicalPath() + File.separator + entry.getName()); 45 | if (entry.isDirectory()) { 46 | logger.debug("creating dir at: " + outputFile.getCanonicalPath()); 47 | outputFile.mkdirs(); 48 | } else { 49 | logger.debug("creating file at: " + outputFile.getCanonicalPath()); 50 | output = new FileOutputStream(outputFile); 51 | IOUtils.copy(tarInput, output); 52 | output.flush(); 53 | output.close(); 54 | } 55 | 56 | entry = tarInput.getNextEntry(); 57 | } 58 | } catch (IOException exception) { 59 | throw new IllegalStateException(exception); 60 | } finally { 61 | IOUtils.closeQuietly(tarInput); 62 | IOUtils.closeQuietly(output); 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/main/java/pl/allegro/tdr/gruntmaven/executable/Executable.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 Adam Dubiel. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package pl.allegro.tdr.gruntmaven.executable; 17 | 18 | import java.util.ArrayList; 19 | import java.util.Collections; 20 | import java.util.HashMap; 21 | import java.util.List; 22 | import java.util.Map; 23 | import java.util.regex.Matcher; 24 | import java.util.regex.Pattern; 25 | import org.twdata.maven.mojoexecutor.MojoExecutor; 26 | 27 | import static org.twdata.maven.mojoexecutor.MojoExecutor.element; 28 | import static org.twdata.maven.mojoexecutor.MojoExecutor.name; 29 | 30 | /** 31 | * 32 | * @author Adam Dubiel 33 | */ 34 | public class Executable { 35 | 36 | /** 37 | * Pattern for detecting options with whitespace characters. Anything after 38 | * first whitespace is ignored by exec-maven-plugin, so they need to be transformed, ex: 39 | * --option true == --option=true 40 | */ 41 | private static final Pattern WHITESPACED_OPTION_PATTERN = Pattern.compile("^-{1,2}?[\\w-]*\\s+"); 42 | 43 | private static final String ARGUMENT_NAME = "argument"; 44 | 45 | private final String executableName; 46 | 47 | private List arguments = new ArrayList(); 48 | 49 | private final String[] successCodes; 50 | 51 | private Map environmentVars = new HashMap(); 52 | 53 | public Executable(String executableName, String[] successCodes) { 54 | this.executableName = executableName; 55 | this.successCodes = successCodes; 56 | } 57 | 58 | public Executable(String executableName) { 59 | this(executableName, null); 60 | } 61 | 62 | Executable(Executable executable) { 63 | this.executableName = executable.executableName(); 64 | this.arguments = new ArrayList(executable.arguments()); 65 | this.successCodes = executable.successCodes(); 66 | } 67 | 68 | public void addArgument(String value) { 69 | arguments.add(element(name(ARGUMENT_NAME), value)); 70 | } 71 | 72 | /** 73 | * Normalization checks if argument contains whitespace character between 74 | * argument name and it's value, if so it replaces the whitespace with provided 75 | * replacement. Normalization is needed, because mojo-exec discards all 76 | * characters after first whitespace. 77 | */ 78 | public void addNormalizedArgument(String value, String whitespaceReplacement) { 79 | arguments.add(element(name(ARGUMENT_NAME), normalizeArgument(value, whitespaceReplacement))); 80 | } 81 | 82 | public void addNormalizedArguments(String[] values, String whitespaceReplacement) { 83 | if(values != null) { 84 | for(String value : values) { 85 | addNormalizedArgument(value, whitespaceReplacement); 86 | } 87 | } 88 | } 89 | 90 | public boolean hasEnvironmentVars() { 91 | return !environmentVars.isEmpty(); 92 | } 93 | 94 | public Map environmentVars() { 95 | return Collections.unmodifiableMap(environmentVars); 96 | } 97 | 98 | public void addEnvironmentVars(Map environmentVars) { 99 | if(environmentVars != null) { 100 | this.environmentVars.putAll(environmentVars); 101 | } 102 | } 103 | 104 | private String normalizeArgument(String argument, String whitespaceReplacement) { 105 | Matcher matcher = WHITESPACED_OPTION_PATTERN.matcher(argument); 106 | if (matcher.find()) { 107 | return argument.replaceFirst("\\s+", whitespaceReplacement); 108 | } 109 | return argument; 110 | } 111 | 112 | public String executableName() { 113 | return executableName; 114 | } 115 | 116 | public List arguments() { 117 | return Collections.unmodifiableList(arguments); 118 | } 119 | 120 | public MojoExecutor.Element[] argumentsArray() { 121 | return arguments.toArray(new MojoExecutor.Element[arguments.size()]); 122 | } 123 | 124 | public boolean overrideSuccessCodes() { 125 | return successCodes != null; 126 | } 127 | 128 | public String[] successCodes() { 129 | return successCodes; 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /src/main/java/pl/allegro/tdr/gruntmaven/resources/Filter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 Adam Dubiel, Przemek Hertel. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package pl.allegro.tdr.gruntmaven.resources; 17 | 18 | /** 19 | * 20 | * @author Adam Dubiel 21 | */ 22 | class Filter { 23 | 24 | private final String placeholderFormat = "\\$\\{%s\\}"; 25 | 26 | private final String placeholder; 27 | 28 | private final String value; 29 | 30 | Filter(String placeholder, String value) { 31 | this.placeholder = String.format(placeholderFormat, placeholder); 32 | this.value = value.replaceAll("\\\\", "/"); 33 | } 34 | 35 | String filter(String text) { 36 | return text.replaceAll(placeholder, value); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/pl/allegro/tdr/gruntmaven/resources/Resource.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 Adam Dubiel, Przemek Hertel. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package pl.allegro.tdr.gruntmaven.resources; 17 | 18 | import java.io.ByteArrayInputStream; 19 | import java.io.File; 20 | import java.io.IOException; 21 | import java.io.InputStream; 22 | import java.io.StringWriter; 23 | import java.util.HashSet; 24 | import java.util.Set; 25 | 26 | import org.apache.commons.io.FileUtils; 27 | import org.apache.commons.io.IOUtils; 28 | import org.apache.maven.plugin.logging.Log; 29 | 30 | /** 31 | * 32 | * @author Adam Dubiel 33 | */ 34 | public class Resource { 35 | 36 | private final String resourceName; 37 | 38 | private final Set filters = new HashSet(); 39 | 40 | private final Log logger; 41 | 42 | public static Resource from(String resourceName, Log logger) { 43 | return new Resource(resourceName, logger); 44 | } 45 | 46 | public Resource(String resourceName, Log logger) { 47 | this.resourceName = resourceName; 48 | this.logger = logger; 49 | } 50 | 51 | public Resource withFilter(String placeholder, String value) { 52 | this.filters.add(new Filter(placeholder, value)); 53 | return this; 54 | } 55 | 56 | public void copy(String to) { 57 | copyResource(to, false); 58 | } 59 | 60 | public void copyAndOverwrite(String to) { 61 | copyResource(to, true); 62 | } 63 | 64 | private void copyResource(String targetPath, boolean overwrite) { 65 | try { 66 | String contents = filter(read()); 67 | 68 | File targetFile = new File(targetPath); 69 | if (!targetFile.exists() || overwrite) { 70 | FileUtils.copyInputStreamToFile(contentAsInputStream(contents), targetFile); 71 | } else { 72 | logger.debug("Not overwriting file " + targetPath); 73 | } 74 | } catch (IOException exception) { 75 | throw new ResourceCreationException(resourceName, targetPath, exception); 76 | } 77 | } 78 | 79 | private InputStream contentAsInputStream(String content) { 80 | return new ByteArrayInputStream(content.getBytes()); 81 | } 82 | 83 | private String read() throws IOException { 84 | InputStream stream = Resource.class.getResourceAsStream(resourceName); 85 | StringWriter contentsWriter = new StringWriter(); 86 | 87 | IOUtils.copy(stream, contentsWriter); 88 | return contentsWriter.toString(); 89 | } 90 | 91 | private String filter(String contents) { 92 | String filteredContents = contents; 93 | for (Filter filter : filters) { 94 | filteredContents = filter.filter(filteredContents); 95 | } 96 | return filteredContents; 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /src/main/java/pl/allegro/tdr/gruntmaven/resources/ResourceCreationException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 Adam Dubiel, Przemek Hertel. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package pl.allegro.tdr.gruntmaven.resources; 17 | 18 | /** 19 | * 20 | * @author Adam Dubiel 21 | */ 22 | @SuppressWarnings("serial") 23 | public class ResourceCreationException extends RuntimeException { 24 | 25 | ResourceCreationException(String resourceName, String targetPath, Throwable cause) { 26 | super(String.format("Failed to copy resource %s to %s.", resourceName, targetPath), cause); 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /src/main/resources/grunt-maven.json: -------------------------------------------------------------------------------- 1 | { 2 | "filesToWatch": "${filesToWatch}", 3 | "directoryToWatch": "${directoryToWatch}", 4 | "projectRootPath": "${projectRootPath}", 5 | "targetPath": "${targetPath}", 6 | "sourceDirectory": "${sourceDirectory}", 7 | "jsSourceDirectory": "${jsSourceDirectory}", 8 | "warTargetDirectory": "${warTargetDirectory}", 9 | "filteredFiles": ${filteredFiles} 10 | } -------------------------------------------------------------------------------- /src/test/java/pl/allegro/tdr/gruntmaven/resources/FilterTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 Adam Dubiel, Przemek Hertel. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package pl.allegro.tdr.gruntmaven.resources; 17 | 18 | import org.testng.annotations.Test; 19 | import static org.assertj.core.api.Assertions.assertThat; 20 | 21 | /** 22 | * 23 | * @author Adam Dubiel 24 | */ 25 | public class FilterTest { 26 | 27 | @Test 28 | public void shouldFilterOutPlaceholderInBrackets() { 29 | // given 30 | Filter filter = new Filter("placeholder", "Sparta!"); 31 | String text = "This is ${placeholder}"; 32 | 33 | // when 34 | String filteredText = filter.filter(text); 35 | 36 | // then 37 | assertThat(filteredText).isEqualTo("This is Sparta!"); 38 | } 39 | 40 | @Test 41 | public void shouldReplaceAllOccurencesOfPlaceholder() { 42 | // given 43 | Filter filter = new Filter("placeholder", "Sparta!"); 44 | String text = "This is ${placeholder}\n" 45 | + "Nice place, this ${placeholder}"; 46 | 47 | // when 48 | String filteredText = filter.filter(text); 49 | 50 | // then 51 | assertThat(filteredText).isEqualTo("This is Sparta!\n" 52 | + "Nice place, this Sparta!"); 53 | } 54 | 55 | @Test 56 | public void shouldNotReplaceUnbracketedPlaceholder() { 57 | // given 58 | Filter filter = new Filter("placeholder", "Sparta!"); 59 | String text = "This is placeholder"; 60 | 61 | // when 62 | String filteredText = filter.filter(text); 63 | 64 | // then 65 | assertThat(filteredText).isEqualTo("This is placeholder"); 66 | } 67 | 68 | @Test 69 | public void shouldReplaceBackslashesWithSlashes() { 70 | Filter filter = new Filter("placeholder", "c:\\sparta\\!"); 71 | String text = "This is ${placeholder}"; 72 | 73 | // when 74 | String filteredText = filter.filter(text); 75 | 76 | // then 77 | assertThat(filteredText).isEqualTo("This is c:/sparta/!"); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/test/java/pl/allegro/tdr/gruntmaven/resources/ResourceTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 Adam Dubiel, Przemek Hertel. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package pl.allegro.tdr.gruntmaven.resources; 17 | 18 | import com.google.common.io.Files; 19 | import java.io.File; 20 | import java.io.IOException; 21 | import org.apache.maven.plugin.logging.Log; 22 | import org.codehaus.plexus.util.FileUtils; 23 | import org.testng.annotations.AfterMethod; 24 | import org.testng.annotations.BeforeMethod; 25 | import org.testng.annotations.Test; 26 | import static org.assertj.core.api.Assertions.assertThat; 27 | import static org.mockito.Mockito.*; 28 | 29 | /** 30 | * 31 | * @author Adam Dubiel 32 | */ 33 | public class ResourceTest { 34 | 35 | public static final String TMP_DIR_NAME = "grunt-maven-plugin"; 36 | 37 | private String baseTargetPath; 38 | 39 | @BeforeMethod 40 | public void setUpEnv() throws IOException { 41 | baseTargetPath = Files.createTempDir().getPath(); 42 | } 43 | 44 | @AfterMethod 45 | public void tearDownEnv() throws IOException { 46 | FileUtils.forceDelete(new File(baseTargetPath)); 47 | } 48 | 49 | @Test 50 | public void shouldCopyContentsOfClasspathFileToTmpDirectory() { 51 | // given 52 | Resource resource = new Resource("/test-resource", mock(Log.class)); 53 | 54 | // when 55 | resource.copy(baseTargetPath + File.separator + "test-resource"); 56 | 57 | // then 58 | assertThat(new File(baseTargetPath + File.separator + "test-resource")).exists(); 59 | } 60 | 61 | @Test 62 | public void shouldApplyRegisteredFiltersWhenCopying() { 63 | // given 64 | Resource resource = new Resource("/test-resource", mock(Log.class)).withFilter("placeholder", "world"); 65 | 66 | // when 67 | resource.copy(baseTargetPath + File.separator + "test-resource"); 68 | 69 | // then 70 | assertThat(new File(baseTargetPath + File.separator + "test-resource")).hasContent("hello world"); 71 | } 72 | 73 | @Test 74 | public void shouldChangeWindowsPathBackslashToUnixSlashWhenApplyingFilters() { 75 | // given 76 | Resource resource = new Resource("/test-resource", mock(Log.class)).withFilter("placeholder", "c:\\windows\\is\\wierd"); 77 | 78 | // when 79 | resource.copy(baseTargetPath + File.separator + "test-resource"); 80 | 81 | // then 82 | assertThat(new File(baseTargetPath + File.separator + "test-resource")).hasContent("hello c:/windows/is/wierd"); 83 | } 84 | 85 | @Test 86 | public void shouldLogDebugButLeaveFileAloneWhenFileAlreadyExistsAndNotOverwriting() { 87 | // given 88 | Log logger = mock(Log.class); 89 | Resource resource = new Resource("/test-resource", logger); 90 | resource.copy(baseTargetPath + File.separator + "test-resource"); 91 | 92 | // when 93 | resource.copy(baseTargetPath + File.separator + "test-resource"); 94 | 95 | // then 96 | verify(logger).debug(anyString()); 97 | } 98 | 99 | @Test 100 | public void shouldOverwriteExitingFileWhenCopyingWithOverwriting() { 101 | // given 102 | Resource resource = new Resource("/test-resource", mock(Log.class)); 103 | resource.copy(baseTargetPath + File.separator + "test-resource"); 104 | 105 | // when 106 | resource.copyAndOverwrite(baseTargetPath + File.separator + "test-resource"); 107 | 108 | // then 109 | assertThat(new File(baseTargetPath + File.separator + "test-resource")).exists(); 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /src/test/resources/test-resource: -------------------------------------------------------------------------------- 1 | hello ${placeholder} --------------------------------------------------------------------------------