├── .gitignore
├── README.md
├── build.gradle
└── src
├── main
├── groovy
│ └── com
│ │ └── siprell
│ │ └── plugin
│ │ └── gradle
│ │ └── bootstrap
│ │ └── BootstrapGradlePlugin.groovy
└── resources
│ └── META-INF
│ └── gradle-plugins
│ └── com.siprell.plugins.bootstrap-framework.properties
└── test
└── groovy
└── com
└── siprell
└── plugin
└── gradle
└── bootstrap
└── GradlePluginSpec.groovy
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | *.iml
3 | *.ipr
4 | *.iws
5 | *.log
6 | /*.sha1
7 | /*.zip
8 | /.classpath
9 | /.project
10 | /.settings
11 | *Db.properties
12 | *Db.script
13 | /*DB.*
14 | /.gradle
15 | /.idea
16 | /.link_to_grails_plugins
17 | /build
18 | /userHome
19 | /grails-app
20 | /src/assets
21 | /src/main/webapp
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## This plugin is no longer maintained.
2 |
3 | ### Gradle plugin for integrating the Bootstrap Framework and Font Awesome
4 |
5 | [  ](https://bintray.com/kensiprell/gradle-plugins/bootstrap-framework/_latestVersion)
6 |
7 | [Bootstrap](http://getbootstrap.com) bills itself as "the most popular HTML, CSS, and JS framework for developing responsive, mobile first projects on the web."
8 |
9 | [Font Awesome](http://fortawesome.github.io/Font-Awesome/) is the "iconic font and CSS toolkit."
10 |
11 | If you have a question, suggestion, or want to report a bug, please submit an [issue](https://github.com/kensiprell/bootstrap-framework/issues). I will reply as soon as I can.
12 |
13 | ### Highlights
14 |
15 | * The Bootstrap version can be configured in your application's ```build.gradle``` file, which means you do not have to install a different version of the plugin to get a different Bootstrap version, nor do you have to wait on a plugin update to use the latest Bootstrap release.
16 |
17 | * The plugin can also manage Font Awesome files, including LESS support.
18 |
19 | * The plugin supports the [asset-pipeline-core](https://github.com/bertramdev/asset-pipeline-core) plugin and its [less-asset-pipeline](https://github.com/bertramdev/less-asset-pipeline) module out of the box.
20 |
21 | ### Sample Application
22 |
23 | This Grails [sample application](https://github.com/kensiprell/bootstrap-framework-sample) demonstrates how to use the plugin.
24 |
25 | ### Installation
26 |
27 | Add the following lines to your application's ```build.gradle``` file. The commented-out lines show the plugin default values, and if you are using Grails, they are not required for the plugin to work. See the [Configuration Options](https://github.com/kensiprell/bootstrap-framework#configuration-options) section for the details.
28 |
29 | buildscript {
30 | ext {
31 | //bootstrapFramework = [
32 | // version : "3.3.5",
33 | // cssPath : "grails-app/assets/stylesheets",
34 | // jsPath : "grails-app/assets/javascripts",
35 | // useIndividualJs : false,
36 | // useLess : false,
37 | // invalidVersionFails : false,
38 | // fontAwesome : [
39 | // install : false
40 | // version : "4.3.0",
41 | // useLess : false
42 | // invalidVersionFails : false
43 | // ]
44 | //]
45 | }
46 | repositories {
47 | jcenter()
48 | }
49 | dependencies {
50 | classpath "com.siprell.plugins:bootstrap-framework:1.0.3"
51 | }
52 | }
53 |
54 | apply plugin: "com.siprell.plugins.bootstrap-framework"
55 |
56 | ### Configuration Options
57 |
58 | The following options can be configured in the ```bootstrapFramework``` Map.
59 |
60 | #### bootstrapFramework.version
61 |
62 | Use the property below to change the Bootstrap Framework version used by your application.
63 |
64 | version : "3.3.1"
65 |
66 | #### bootstrapFramework.cssPath
67 |
68 | Use the property below to define the location where the Bootstrap CSS, fonts, and LESS files are copied.
69 |
70 | cssPath : "src/main/web-app/resources/css"
71 |
72 | #### bootstrapFramework.jsPath
73 |
74 | Use the property below to define the location where the Bootstrap JavaScript files are copied.
75 |
76 | jsPath : "src/main/web-app/resources/js"
77 |
78 | #### bootstrapFramework.useIndividualJs
79 |
80 | If the property below is set to ```true```, the plugin will copy all individual JavaScript files to ```"${bootstrapFramework.jsPath}/bootstrap"```. Otherwise, it will only copy the ```bootstrap.js``` file to the directory.
81 |
82 | useIndividualJs : true
83 |
84 | #### bootstrapFramework.useLess
85 |
86 | If the property below is set to ```true```, the plugin will copy all Bootstrap Framework LESS and mixin files to ```"${bootstrapFramework.cssPath}/bootstrap/less"```.
87 |
88 | useLess : true
89 |
90 | #### invalidVersionFails
91 |
92 | The ```invalidVersionFails``` property can be configured individually for ```bootstrapFramework``` and ```bootstrapFramework.fontAwesome```. When the property is at its default of ```false``` and you have entered an invalid version number, the plugin will search the ```build/tmp``` directory for other versions of the appropriate zip file and use the one with the highest version number. It will also display a warning message in the console.
93 |
94 | You can disable this behavior by setting this property to ```true```. If the plugin cannot download the version you specified, it will throw an ```org.gradle.api.InvalidUserDataException```.
95 |
96 | bootstrapFramework = [
97 | invalidVersionFails : true
98 | fontAwesome : [
99 | install : true,
100 | invalidVersionFails : true
101 | ]
102 | ]
103 |
104 | #### bootstrapFramework.fontAwesome.install
105 |
106 | If ```bootstrapFramework.fontAwesome.install``` is set to ```true```, the plugin will install the Font Awesome fonts using the default plugin version without LESS support.
107 |
108 | bootstrapFramework = [
109 | fontAwesome : [
110 | install : true
111 | ]
112 | ]
113 |
114 | #### bootstrapFramework.fontAwesome.version
115 |
116 | You can change the Font Awesome version by setting the ```bootstrapFramework.fontAwesome.version``` property.
117 |
118 | bootstrapFramework = [
119 | fontAwesome : [
120 | install : true
121 | version : "4.2.0"
122 | ]
123 | ]
124 |
125 | #### bootstrapFramework.fontAwesome.useLess
126 |
127 | You can add LESS support for Font Awesome by setting the ```bootstrapFramework.fontAwesome.useLess``` property.
128 |
129 | bootstrapFramework = [
130 | fontAwesome : [
131 | install : true
132 | useLess : true
133 | ]
134 | ]
135 |
136 | ### How the Plugin Works
137 |
138 | The plugin downloads the appropriate Bootstrap or Font Awesome zip file and copies it to your application's ```build/tmp``` directory. The plugin will extract the necessary files and copy them to the directories defined by the ```bootstrapFramework.cssPath``` and ```bootstrapFramework.jsPath``` properties.
139 |
140 | The files are copied into the directory trees shown below. It is important that you do not put any files in the two ```bootstrap``` directories (```"${bootstrapFramework.cssPath}/bootstrap"``` and ```"${bootstrapFramework.jsPath}/bootstrap"```) or the ```font-awesome``` directory (```"${bootstrapFramework.cssPath}/font-awesome"```) because they will be overwritten.
141 |
142 | The ```bootstrap-all.js```, ```bootstrap-all.css```, ```bootstrap-less.less```, ```font-awesome-all.css```, and ```font-awesome-less.less``` files are generated for the asset-pipeline plugin if you are using Grails or the word "assets" is contained in the ```bootstrapFramework.jsPath``` property.
143 |
144 | Directory ```bootstrapFramework.jsPath```:
145 |
146 | | bootstrap-all.js
147 | |----bootstrap/
148 | | | affix.js
149 | | | alert.js
150 | | | bootstrap.js
151 | | | etc.
152 |
153 | Directory ```bootstrapFramework.cssPath```:
154 |
155 | | bootstrap-all.css
156 | | bootstrap-less.less
157 | |----bootstrap/
158 | | |----css/
159 | | | | bootstrap-theme.css
160 | | | | bootstrap.css
161 | | |----fonts/
162 | | | | glyphicons-halflings-regular.eot
163 | | | | etc.
164 | | |----less/
165 | | | | alerts.less
166 | | | | badges.less
167 | | | | etc.
168 | | | |----mixins/
169 | | | | | alerts.less
170 | | | | | background-variant.less
171 | | | | | etc.
172 | | font-awesome-all.css
173 | | font-awesome-less.less
174 | |----font-awesome/
175 | | |----css/
176 | | | | font-awesome.css
177 | | |----fonts/
178 | | | | FontAwesome.otf
179 | | | | etc.
180 | | |----less/
181 | | | | animated.less
182 | | | | etc.
183 |
184 | ### User Task
185 |
186 | You can use the task shown below to display the default Bootstrap Framework and Font Awesome versions used by the plugin.
187 |
188 | ./gradlew bootstrapFrameworkVersions
189 |
190 | or
191 |
192 | ./gradlew bFV
193 |
194 | The output will be similar to:
195 |
196 | 3.3.5 is the default Bootstrap Framework version.
197 | 4.3.0 is the default Font Awesome version.
198 |
199 | ### Glyphicons
200 |
201 | The Glyphicons icons are available as described in the [Bootstrap Components](http://getbootstrap.com/components/) section of the Bootstrap Framework documentation.
202 |
203 | ### Asset Pipeline Usage
204 |
205 | The remaining sections outline how to include Bootstrap Framework and Font Awesome in your application using the ```asset-pipeline-core``` plugin and its ```less-asset-pipeline``` module.
206 |
207 | #### JavaScript
208 |
209 | The instructions below assume the manifest file is in the ```grails-app/assets/javascripts``` directory.
210 |
211 | ##### Add all Bootstrap JavaScript Files
212 |
213 | Add the line below to a manifest:
214 |
215 | //= require bootstrap-all
216 |
217 | Or add the line below to a GSP:
218 |
219 |
220 |
221 | ##### Add individual Bootstrap JavaScript files
222 |
223 | Ensure you set the parameter below to true as described [above](https://github.com/kensiprell/bootstrap-framework#bootstrapframeworkuseindividualjs):
224 |
225 | bootstrapFrameworkUseIndividualJs = true
226 |
227 | Add a line similar to the one below to a manifest:
228 |
229 | //= require bootstrap/bootstrap-affix
230 |
231 | Or add the line below to a GSP:
232 |
233 |
234 |
235 | #### Stylesheets
236 |
237 | The instructions below assume the manifest file is in the ```grails-app/assets/stylesheets``` directory.
238 |
239 | ##### Add all Bootstrap and Font Awesome CSS Files
240 |
241 | Add the lines below to a manifest:
242 |
243 | *= require bootstrap-all
244 | *= require font-awesome-all
245 |
246 | Or add the lines below to a GSP:
247 |
248 |
249 |
250 |
251 | ##### Add individual Bootstrap CSS Files
252 |
253 | Add the line below to a manifest:
254 |
255 | *= require bootstrap/css/bootstrap-theme
256 |
257 | Or add the line below to a GSP:
258 |
259 |
260 |
261 | ### LESS
262 |
263 | #### Add LESS Files
264 |
265 | Add the lines below to a manifest:
266 |
267 | *= require bootstrap-less
268 | *= require font-awesome-less
269 |
270 | Or add the line below to a GSP:
271 |
272 |
273 |
274 |
275 | #### LESS Customizations
276 |
277 | If LESS support is configured for either Bootstrap Framework or Font Awesome, the plugin will create the appropriate LESS file in your application's ```bootstrapFramework.cssPath``` directory. If you later decide not to use LESS, the plugin will delete the LESS and mixin files, but it will not delete the ```bootstrap-less.less``` or the ```font-awesome-less.less``` file. Should you decide to turn LESS support back on, your customized LESS files will still be available.
278 |
279 | Use ```bootstrap-less.less``` for customizing the Bootstrap Framework:
280 |
281 | /*
282 | * This file is for your Bootstrap Framework less and mixin customizations.
283 | * It was created by the bootstrap-framework plugin.
284 | * It will not be overwritten.
285 | *
286 | * You can import all less and mixin files as shown below,
287 | * or you can import them individually.
288 | * See https://github.com/kensiprell/bootstrap-framework/blob/master/README.md#less
289 | */
290 |
291 | @import "bootstrap/less/bootstrap.less";
292 |
293 | /*
294 | * Your customizations go below this section.
295 | */
296 |
297 | Use ```font-awesome-less.less``` for customizing Font Awesome:
298 |
299 | * Font Awesome by Dave Gandy - http://fontawesome.io
300 | *
301 | * This file is for your Font Awesome less and mixin customizations.
302 | * It was created by the bootstrap-framework plugin.
303 | * It will not be overwritten.
304 | *
305 | * You can import all less and mixin files as shown below,
306 | * or you can import them individually.
307 | * See https://github.com/kensiprell/bootstrap-framework/blob/master/README.md#font-awesome-less
308 | */
309 |
310 | @import "font-awesome/less/font-awesome.less";
311 |
312 | @fa-font-path: "/assets/font-awesome/fonts";
313 |
314 | /*
315 | * Your customizations go below this section.
316 | */
317 |
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | buildscript {
2 | repositories {
3 | mavenLocal()
4 | jcenter()
5 | maven {
6 | url "https://plugins.gradle.org/m2/"
7 | }
8 | }
9 | dependencies {}
10 | }
11 |
12 | plugins {
13 | id "groovy"
14 | id "maven"
15 | id "maven-publish"
16 | id "com.gradle.plugin-publish" version "0.9.1"
17 | id "com.jfrog.bintray" version "1.2"
18 | }
19 |
20 | version "1.0.3"
21 | group = "com.siprell.plugins"
22 | sourceCompatibility = "1.7"
23 | targetCompatibility = "1.7"
24 |
25 | ext {
26 | isReleaseVersion = !version.endsWith("SNAPSHOT")
27 | }
28 |
29 | repositories {
30 | jcenter()
31 | mavenLocal()
32 | mavenCentral()
33 | }
34 |
35 | dependencies {
36 | compile gradleApi()
37 | compile localGroovy()
38 | testCompile("org.spockframework:spock-core:1.0-groovy-2.3")
39 | testCompile("org.springframework.boot:spring-boot:1.2.1.RELEASE")
40 | }
41 |
42 | task sourcesJar(type: Jar) {
43 | classifier = "sources"
44 | from sourceSets.main.allSource
45 | }
46 |
47 | task(console, dependsOn: "classes", type: JavaExec) {
48 | main = "groovy.ui.Console"
49 | classpath = sourceSets.main.runtimeClasspath
50 | }
51 |
52 | test {
53 | testLogging {
54 | showStandardStreams = true
55 | }
56 | }
57 |
58 | publishing {
59 | publications {
60 | maven(MavenPublication) {
61 | artifactId "bootstrap-framework"
62 | pom.withXml {
63 | asNode().children().last() + {
64 | resolveStrategy = Closure.DELEGATE_FIRST
65 | name "bootstrap-framework"
66 | description "Gradle plugin for integrating the Bootstrap Framework and Font Awesome"
67 | url "https://github.com/kensiprell/bootstrap-framework"
68 | scm {
69 | url "https://github.com/kensiprell/bootstrap-framework"
70 | connection "scm:https://github.com/kensiprell/bootstrap-framework.git"
71 | developerConnection "scm:git://github.com/kensiprell/bootstrap-framework.git"
72 | }
73 | licenses {
74 | license {
75 | name "The Apache Software License, Version 2.0"
76 | url "http://www.apache.org/license/LICENSE-2.0.txt"
77 | distribution "repo"
78 | }
79 | }
80 | developers {
81 | developer {
82 | id "kensiprell"
83 | name "Ken Siprell"
84 | email "ken.siprell@gmail.com"
85 | }
86 | }
87 | }
88 | }
89 | from components.java
90 | artifact sourcesJar
91 | }
92 | }
93 | }
94 |
95 | bintray {
96 | if (project.hasProperty("bintrayUser")) {
97 | user = bintrayUser
98 | key = bintrayKey
99 | }
100 | publications = ["maven"]
101 | pkg {
102 | repo = "gradle-plugins"
103 | name = "bootstrap-framework"
104 | desc = "Gradle plugin for integrating the Bootstrap Framework and Font Awesome"
105 | websiteUrl = "https://github.com/kensiprell/bootstrap-framework"
106 | issueTrackerUrl = "https://github.com/kensiprell/bootstrap-framework/issues"
107 | vcsUrl = "https://github.com/kensiprell/bootstrap-framework.git"
108 | licenses = ["Apache-2.0"]
109 | labels = ["bootstrap", "fontawesome", "font awesome", "gradle"]
110 | publicDownloadNumbers = true
111 | }
112 | }
113 |
114 | pluginBundle {
115 | website = "https://github.com/kensiprell/bootstrap-framework"
116 | vcsUrl = "https://github.com/kensiprell/bootstrap-framework.git"
117 | description = "Gradle plugin for integrating the Bootstrap Framework and Font Awesome"
118 | tags = ["bootstrap", "fontawesome", "font awesome", "gradle"]
119 |
120 | plugins {
121 | bootstrapFrameworkPlugin {
122 | id = "com.siprell.plugins.bootstrap-framework"
123 | displayName = "Bootstrap Framework Plugin for Gradle"
124 | }
125 | }
126 | }
127 |
128 | bintrayUpload.dependsOn build, sourcesJar
129 |
--------------------------------------------------------------------------------
/src/main/groovy/com/siprell/plugin/gradle/bootstrap/BootstrapGradlePlugin.groovy:
--------------------------------------------------------------------------------
1 | package com.siprell.plugin.gradle.bootstrap
2 |
3 | import org.gradle.api.file.FileTree
4 | import org.gradle.api.InvalidUserDataException
5 | import org.gradle.api.Plugin
6 | import org.gradle.api.Project
7 | import org.gradle.api.tasks.Copy
8 | import org.gradle.api.tasks.Sync
9 |
10 | class BootstrapGradlePlugin implements Plugin {
11 | final BOOTSTRAP_DEFAULT_VERSION = "3.3.5"
12 | final FA_DEFAULT_VERSION = "4.3.0"
13 |
14 | void apply(Project project) {
15 |
16 | // Shared properties
17 | def template = new Template()
18 | def zipFile = new ZipFile()
19 | def properties = project.hasProperty("bootstrapFramework") ? project.bootstrapFramework : [:]
20 | String tmpDir = "${project.buildDir}/tmp"
21 |
22 | // Bootstrap Framework properties
23 | String bootstrapVersion = properties.version ?: BOOTSTRAP_DEFAULT_VERSION
24 | boolean useIndividualJs = properties.useIndividualJs != null ? properties.useIndividualJs : false
25 | boolean useLess = properties.useLess != null ? properties.useLess : false
26 | String jsPath = properties.jsPath ? properties.jsPath : "grails-app/assets/javascripts"
27 | String cssPath = properties.cssPath ? properties.cssPath : "grails-app/assets/stylesheets"
28 | String bootstrapJsPath = "${project.projectDir}/$jsPath/bootstrap"
29 | String bootstrapCssPath = "${project.projectDir}/$cssPath/bootstrap/css"
30 | String bootstrapFontsPath = "${project.projectDir}/$cssPath/bootstrap/fonts"
31 | String bootstrapLessPath = "${project.projectDir}/$cssPath/bootstrap/less"
32 | String bootstrapMixinsPath = "$bootstrapLessPath/mixins"
33 | boolean useAssetPipeline = jsPath.contains("assets")
34 | boolean bootstrapInvalidVersionFails = properties.invalidVersionFails != null ? properties.invalidVersionFails : false
35 | FileTree bootstrapZipTree
36 |
37 | // Font Awesome properties
38 | def fontAwesome = properties.fontAwesome
39 | boolean fontAwesomeInstall = fontAwesome?.install != null ? fontAwesome.install : false
40 | String fontAwesomeVersion = fontAwesome?.version ?: FA_DEFAULT_VERSION
41 | boolean fontAwesomeUseLess = fontAwesome?.useLess != null ? fontAwesome.useLess : false
42 | boolean fontAwesomeInvalidVersionFails = fontAwesome?.invalidVersionFails != null ? fontAwesome.invalidVersionFails : false
43 | String fontAwesomePath = "${project.projectDir}/$cssPath/font-awesome"
44 | String fontAwesomeCssPath = "$fontAwesomePath/css"
45 | String fontAwesomeFontsPath = "$fontAwesomePath/fonts"
46 | String fontAwesomeLessPath = "$fontAwesomePath/less"
47 | FileTree fontAwesomeZipTree
48 |
49 | project.afterEvaluate {
50 | project.tasks.processResources.dependsOn("createFontAwesomeLess")
51 | }
52 |
53 | project.task("bootstrapFrameworkVersions") << {
54 | println "$BOOTSTRAP_DEFAULT_VERSION is the default Bootstrap Framework version."
55 | println "$FA_DEFAULT_VERSION is the default Font Awesome version."
56 | }
57 |
58 | project.task("checkDirectories") {
59 | if (!project.file(cssPath).exists()) {
60 | throw new InvalidUserDataException("bootstrapFramework.cssPath directory ($cssPath) does not exist.")
61 | }
62 | if (!project.file(jsPath).exists()) {
63 | throw new InvalidUserDataException("bootstrapFramework.jsPath directory ($jsPath) does not exist.")
64 | }
65 | }
66 |
67 | project.task("downloadBootstrapZip") {
68 | String description = "Bootstrap Framework"
69 | String filePrefix = "bootstrap-v"
70 | String url = "https://github.com/twbs/bootstrap/archive/v${bootstrapVersion}.zip"
71 | String zipFilename = "${filePrefix}${bootstrapVersion}.zip"
72 | def file = zipFile.download(tmpDir, description, filePrefix, url, bootstrapVersion, zipFilename, bootstrapInvalidVersionFails)
73 | if (file instanceof File) {
74 | bootstrapZipTree = project.zipTree(file)
75 | } else if (file instanceof String) {
76 | throw new InvalidUserDataException(file)
77 | } else {
78 | throw new InvalidUserDataException("An unknown error occurred trying to download $url")
79 | }
80 | }
81 |
82 | project.task("downloadFontAwesomeZip") {
83 | if (fontAwesomeInstall) {
84 | String description = "Font Awesome"
85 | String filePrefix = "fontAwesome-v"
86 | String url = "https://github.com/FortAwesome/Font-Awesome/archive/v${fontAwesomeVersion}.zip"
87 | String zipFilename = "${filePrefix}${fontAwesomeVersion}.zip"
88 | def file = zipFile.download(tmpDir, description, filePrefix, url, fontAwesomeVersion, zipFilename, fontAwesomeInvalidVersionFails)
89 | if (file instanceof File) {
90 | fontAwesomeZipTree = project.zipTree(file)
91 | } else if (file instanceof String) {
92 | throw new InvalidUserDataException(file)
93 | } else {
94 | throw new InvalidUserDataException("An unknown error occurred trying to download $url")
95 | }
96 | }
97 | }
98 |
99 | project.task("manageBootstrapDirs") {
100 | if (!project.file(bootstrapJsPath).exists()) {
101 | project.mkdir(bootstrapJsPath)
102 | }
103 | if (!project.file(bootstrapCssPath).exists()) {
104 | project.mkdir(bootstrapCssPath)
105 | }
106 | if (!project.file(bootstrapFontsPath).exists()) {
107 | project.mkdir(bootstrapFontsPath)
108 | }
109 | if (useLess) {
110 | if (!project.file(bootstrapMixinsPath).exists()) {
111 | project.mkdir(bootstrapMixinsPath)
112 | }
113 | } else {
114 | project.delete(bootstrapLessPath)
115 | }
116 | if (fontAwesomeInstall) {
117 | def dirs = ["css", "fonts"]
118 | if (fontAwesomeUseLess) {
119 | dirs << "less"
120 | } else {
121 | project.delete(fontAwesomeLessPath)
122 | }
123 | dirs.each {
124 | project.mkdir("$fontAwesomePath/$it")
125 | }
126 | } else {
127 | project.delete(fontAwesomePath)
128 | }
129 | }
130 |
131 | project.task("createBootstrapJsAll", type: Copy, dependsOn: project.tasks.manageBootstrapDirs) {
132 | def path = "${project.projectDir}/$jsPath"
133 | def filename = "bootstrap-all.js"
134 | if (useAssetPipeline) {
135 | from template.getFile(project, "createBootstrapJsAll")
136 | rename ".*", filename
137 | into path
138 | onlyIf { !project.file("$path/$filename").exists() }
139 | } else {
140 | project.delete("$path/$filename")
141 | }
142 | }
143 |
144 | project.task("createBootstrapJs", type: Sync, dependsOn: project.tasks.createBootstrapJsAll) {
145 | def files = bootstrapZipTree.matching {
146 | include "*/dist/js/bootstrap.js"
147 | if (useIndividualJs) {
148 | include "*/js/*.js"
149 | }
150 | }.collect()
151 | from files
152 | into bootstrapJsPath
153 | }
154 |
155 | project.task("createBootstrapCssAll", type: Copy, dependsOn: project.tasks.createBootstrapJs) {
156 | def path = "${project.projectDir}/$cssPath"
157 | def filename = "bootstrap-all.css"
158 | if (useAssetPipeline) {
159 | from template.getFile(project, "createBootstrapCssAll")
160 | rename ".*", filename
161 | into path
162 | onlyIf { !project.file("$path/$filename").exists() }
163 | } else {
164 | project.delete("$path/$filename")
165 | }
166 | }
167 |
168 | project.task("createBootstrapFonts", type: Sync, dependsOn: project.tasks.createBootstrapCssAll) {
169 | def files = bootstrapZipTree.matching {
170 | include "*/fonts/*"
171 | }.collect()
172 | from files
173 | into bootstrapFontsPath
174 | }
175 |
176 | project.task("createBootstrapCssIndividual", type: Sync, dependsOn: project.tasks.createBootstrapFonts) {
177 | def files = bootstrapZipTree.matching {
178 | include "*/dist/css/*.css"
179 | exclude "*/dist/css/*.min.css"
180 | }.collect()
181 | from files
182 | into bootstrapCssPath
183 | }
184 |
185 | project.task("createBootstrapLessLess", type: Copy, dependsOn: project.tasks.createBootstrapCssIndividual) {
186 | def path = "${project.projectDir}/$cssPath"
187 | def filename = "bootstrap-less.less"
188 | if (useLess && useAssetPipeline) {
189 | from template.getFile(project, "createBootstrapLessLess")
190 | rename ".*", filename
191 | into path
192 | onlyIf { !project.file("$path/$filename").exists() }
193 | }
194 | }
195 |
196 | project.task("createBootstrapLess", type: Sync, dependsOn: project.tasks.createBootstrapLessLess) {
197 | def files = []
198 | if (useLess) {
199 | files = bootstrapZipTree.matching {
200 | include "*/less/*.less"
201 | }.collect()
202 | }
203 | from files
204 | into bootstrapLessPath
205 | }
206 |
207 | project.task("createBootstrapMixins", type: Sync, dependsOn: project.tasks.createBootstrapLess) {
208 | def files = []
209 | if (useLess) {
210 | files = bootstrapZipTree.matching {
211 | include "*/less/mixins/*.less"
212 | }.collect()
213 | }
214 | from files
215 | into bootstrapMixinsPath
216 | }
217 |
218 | project.task("createFontAwesomeCssAll", type: Copy, dependsOn: project.tasks.createBootstrapMixins) {
219 | def path = "${project.projectDir}/$cssPath"
220 | def filename = "font-awesome-all.css"
221 | if (fontAwesomeInstall && useAssetPipeline) {
222 | from template.getFile(project, "createFontAwesomeCssAll")
223 | rename ".*", filename
224 | into path
225 | onlyIf { !project.file("$path/$filename").exists() }
226 | } else {
227 | project.delete("$path/$filename")
228 | }
229 | }
230 |
231 | project.task("createFontAwesomeCssIndividual", type: Sync, dependsOn: project.tasks.createFontAwesomeCssAll) {
232 | def files = []
233 | if (fontAwesomeInstall) {
234 | files = fontAwesomeZipTree.matching {
235 | include "*/css/font-awesome.css"
236 | }.collect()
237 | }
238 | from files
239 | into fontAwesomeCssPath
240 | }
241 |
242 | project.task("createFontAwesomeFonts", type: Sync, dependsOn: project.tasks.createFontAwesomeCssIndividual) {
243 | def files = []
244 | if (fontAwesomeInstall) {
245 | files = fontAwesomeZipTree.matching {
246 | include "*/fonts/*"
247 | }.collect()
248 | }
249 | from files
250 | into fontAwesomeFontsPath
251 | }
252 |
253 | project.task("createFontAwesomeLessLess", type: Copy, dependsOn: project.tasks.createFontAwesomeFonts) {
254 | def path = "${project.projectDir}/$cssPath"
255 | def filename = "font-awesome-less.less"
256 | if (fontAwesomeInstall && fontAwesomeUseLess) {
257 | if (cssPath.contains("assets")) {
258 | from template.getFile(project, "createFontAwesomeLessLessAssets")
259 | } else {
260 | from template.getFile(project, "createFontAwesomeLessLess")
261 | }
262 | rename ".*", filename
263 | into path
264 | onlyIf { !project.file("$path/$filename").exists() }
265 | }
266 | }
267 |
268 | project.task("createFontAwesomeLess", type: Sync, dependsOn: project.tasks.createFontAwesomeLessLess) {
269 | def files = []
270 | if (fontAwesomeInstall && fontAwesomeUseLess) {
271 | files = fontAwesomeZipTree.matching {
272 | include "*/less/*.less"
273 | }.collect()
274 | }
275 | from files
276 | into fontAwesomeLessPath
277 | }
278 | }
279 | }
280 |
281 | class Template {
282 | static getText(String template) {
283 | def text = ""
284 | switch (template) {
285 | case "createBootstrapJsAll":
286 | text = """//
287 | // Do not edit this file. It will be overwritten by the bootstrap-framework plugin.
288 | //
289 | //= require bootstrap/bootstrap.js
290 | //
291 | """
292 | break
293 | case "createBootstrapCssAll":
294 | text = """/*
295 | * Do not edit this file. It will be overwritten by the bootstrap-framework plugin.
296 | *
297 | *= require_tree bootstrap/css
298 | */
299 | """
300 | break
301 | case "createBootstrapLessLess":
302 | text = """/*
303 | * This file is for your Bootstrap Framework less and mixin customizations.
304 | * It was created by the bootstrap-framework plugin.
305 | * It will not be overwritten.
306 | *
307 | * You can import all less and mixin files as shown below,
308 | * or you can import them individually.
309 | * See https://github.com/kensiprell/bootstrap-framework/blob/master/README.md#less
310 | */
311 |
312 | @import "bootstrap/less/bootstrap.less";
313 |
314 | /*
315 | * Your customizations go below this section.
316 | */
317 | """
318 | break
319 | case "createFontAwesomeCssAll":
320 | text = """/*
321 | * Font Awesome by Dave Gandy - http://fontawesome.io
322 | *
323 | * Do not edit this file. It will be overwritten by the bootstrap-framework plugin.
324 | *
325 | *= require font-awesome/css/font-awesome.css
326 | *= require_tree font-awesome/fonts
327 | */
328 | """
329 | break
330 | case "createFontAwesomeLessLessAssets":
331 | text = """/*
332 | * Font Awesome by Dave Gandy - http://fontawesome.io
333 | *
334 | * This file is for your Font Awesome less and mixin customizations.
335 | * It was created by the bootstrap-framework plugin.
336 | * It will not be overwritten.
337 | *
338 | * You can import all less and mixin files as shown below,
339 | * or you can import them individually.
340 | * See https://github.com/kensiprell/bootstrap-framework/blob/master/README.md#font-awesome-less
341 | */
342 |
343 | @import "font-awesome/less/font-awesome.less";
344 |
345 | @fa-font-path: "/assets/font-awesome/fonts";
346 |
347 | /*
348 | * Your customizations go below this section.
349 | */
350 | """
351 | break
352 | case "createFontAwesomeLessLess":
353 | text = """/*
354 | * Font Awesome by Dave Gandy - http://fontawesome.io
355 | *
356 | * This file is for your Font Awesome less and mixin customizations.
357 | * It was created by the bootstrap-framework plugin.
358 | * It will not be overwritten.
359 | *
360 | * You can import all less and mixin files as shown below,
361 | * or you can import them individually.
362 | * See https://github.com/kensiprell/bootstrap-framework/blob/master/README.md#font-awesome-less
363 | */
364 |
365 | @import "font-awesome/less/font-awesome.less";
366 |
367 | @fa-font-path: "/font-awesome/fonts";
368 |
369 | /*
370 | * Your customizations go below this section.
371 | */
372 | """
373 | break
374 | }
375 | text.toString()
376 | }
377 |
378 | static getFile(Project project, String template) {
379 | project.resources.text.fromString(getText(template)).asFile()
380 | }
381 | }
382 |
383 | class ZipFile {
384 | String fileSuffix = ".zip"
385 |
386 | def download(String tmp, String description, String filePrefix, String url, String version, String zipFilename, boolean invalidVersionFails) {
387 | def zipFile = new File("$tmp/$zipFilename")
388 | if (zipFile.exists()) {
389 | return zipFile
390 | }
391 | HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection()
392 | def invalidVersionMessage = "Could not download $url.\n$version is an invalid $description version, or you are not connected to the Internet."
393 | def tmpDir = new File("$tmp")
394 | if (!tmpDir.exists()) {
395 | tmpDir.mkdirs()
396 | }
397 | connection.setRequestMethod("GET")
398 | if (connection.getResponseCode() == 200) {
399 | def file = zipFile.newOutputStream()
400 | file << new URL(url).openStream()
401 | file.close()
402 | return zipFile
403 | } else {
404 | zipFile.delete()
405 | if (invalidVersionFails) {
406 | return invalidVersionMessage.toString()
407 | } else {
408 | println "Error: $invalidVersionMessage"
409 | }
410 | }
411 | List zipFiles = []
412 | tmpDir.listFiles().each {
413 | if (it.name.startsWith(filePrefix)) {
414 | zipFiles << it
415 | }
416 | }
417 | if (zipFiles.size() > 0) {
418 | File zipFileOld
419 | if (zipFiles.size() == 1) {
420 | zipFileOld = zipFiles[0]
421 | } else {
422 | zipFileOld = zipFiles.sort(false) { a, b ->
423 | def tokens = [a.name.minus(filePrefix).minus(fileSuffix), b.name.minus(filePrefix).minus(fileSuffix)]
424 | tokens*.tokenize('.')*.collect { it as int }.with { u, v ->
425 | [u, v].transpose().findResult { x, y -> x <=> y ?: null } ?: u.size() <=> v.size()
426 | }
427 | }[-1]
428 | }
429 | String newVersion = zipFileOld.name.minus(filePrefix).minus(fileSuffix)
430 | println "Using $description version $newVersion instead of $version."
431 | return zipFileOld
432 | } else {
433 | return "No old $description zip files found in $tmpDir.".toString()
434 | }
435 | }
436 | }
437 |
--------------------------------------------------------------------------------
/src/main/resources/META-INF/gradle-plugins/com.siprell.plugins.bootstrap-framework.properties:
--------------------------------------------------------------------------------
1 | implementation-class=com.siprell.plugin.gradle.bootstrap.BootstrapGradlePlugin
2 |
--------------------------------------------------------------------------------
/src/test/groovy/com/siprell/plugin/gradle/bootstrap/GradlePluginSpec.groovy:
--------------------------------------------------------------------------------
1 | package com.siprell.plugin.gradle.bootstrap
2 |
3 | import java.text.SimpleDateFormat
4 | import org.gradle.api.InvalidUserDataException
5 | import org.gradle.api.Project
6 | import org.gradle.api.internal.plugins.PluginApplicationException
7 | import org.gradle.testfixtures.ProjectBuilder
8 | import org.junit.Rule
9 | import org.springframework.boot.test.OutputCapture
10 | import spock.lang.Specification
11 |
12 | class GradlePluginSpec extends Specification {
13 |
14 | @Rule
15 | OutputCapture capture = new OutputCapture()
16 |
17 | static final bootstrapDefaultVersion = "3.3.5"
18 | static final fontAwesomeDefaultVersion = "4.3.0"
19 | static final cssPath = "src/main/webapp/css"
20 | static final jsPath = "src/main/webapp/js"
21 | static final grailsCssPath = "grails-app/assets/stylesheets"
22 | static final grailsJsPath = "grails-app/assets/javascripts"
23 | static boolean useAssetPipeline = true
24 |
25 | def setupSpec() {
26 | deleteDirs()
27 | deleteZipFiles()
28 | }
29 |
30 | def setup() {
31 | useAssetPipeline = true
32 | }
33 |
34 | def cleanupSpec() {
35 | deleteDirs()
36 | }
37 |
38 | void "CSS resource directory does not exist"() {
39 | when:
40 | def properties = defaultProperties
41 | createProject(properties)
42 |
43 | then:
44 | final List lines = capture.toString().tokenize(System.properties["line.separator"])
45 | lines.size() == 0
46 | PluginApplicationException exception = thrown()
47 | exception.cause.class == InvalidUserDataException
48 | exception.cause.message == "bootstrapFramework.cssPath directory ($grailsCssPath) does not exist.".toString()
49 | }
50 |
51 | void "JavaScript resource directory does not exist"() {
52 | given:
53 | new File("$filePath.root/$grailsCssPath").mkdirs()
54 |
55 | when:
56 | def properties = defaultProperties
57 | createProject(properties)
58 |
59 | then:
60 | final List lines = capture.toString().tokenize(System.properties["line.separator"])
61 | lines.size() == 0
62 | PluginApplicationException exception = thrown()
63 | exception.cause.class == InvalidUserDataException
64 | exception.cause.message == "bootstrapFramework.jsPath directory ($grailsJsPath) does not exist.".toString()
65 | }
66 |
67 | void "use invalid Bootstrap Framework version with invalidVersionFails = false and no zip files available"() {
68 | given:
69 | createDirs()
70 |
71 | when:
72 | def version = "3.2.99"
73 | def properties = defaultProperties
74 | properties.version = version
75 | createProject(properties)
76 |
77 | then:
78 | final List lines = capture.toString().tokenize(System.properties["line.separator"])
79 | lines[0] == "Error: Could not download https://github.com/twbs/bootstrap/archive/v${version}.zip.".toString()
80 | lines[1] == "${version} is an invalid Bootstrap Framework version, or you are not connected to the Internet.".toString()
81 | PluginApplicationException exception = thrown()
82 | exception.cause.class == InvalidUserDataException
83 | exception.cause.message.startsWith("No old Bootstrap Framework zip files found in")
84 | }
85 |
86 | void "use invalid Bootstrap Framework version with invalidVersionFails = true and no zip files available"() {
87 | when:
88 | def version = "3.2.99"
89 | def invalidVersionMessageStart = "Could not download".toString()
90 | def invalidVersionMessageEnd = "$version is an invalid Bootstrap Framework version, or you are not connected to the Internet.".toString()
91 | def properties = defaultProperties
92 | properties.version = version
93 | properties.invalidVersionFails = true
94 | createProject(properties)
95 |
96 | then:
97 | final List lines = capture.toString().tokenize(System.properties["line.separator"])
98 | lines.size() == 0
99 | PluginApplicationException exception = thrown()
100 | exception.cause.class == InvalidUserDataException
101 | exception.cause.message.startsWith(invalidVersionMessageStart)
102 | exception.cause.message.endsWith(invalidVersionMessageEnd)
103 | }
104 |
105 | void "use invalid Font Awesome version with invalidVersionFails = false and no zip files available"() {
106 | when:
107 | def version = "3.2.99"
108 | def properties = defaultProperties
109 | properties.fontAwesome.install = true
110 | properties.fontAwesome.version = version
111 | createProject(properties)
112 |
113 | then:
114 | final List lines = capture.toString().tokenize(System.properties["line.separator"])
115 | lines[0] == "Error: Could not download https://github.com/FortAwesome/Font-Awesome/archive/v${version}.zip.".toString()
116 | lines[1] == "${version} is an invalid Font Awesome version, or you are not connected to the Internet.".toString()
117 | PluginApplicationException exception = thrown()
118 | exception.cause.class == InvalidUserDataException
119 | exception.cause.message.startsWith("No old Font Awesome zip files found in")
120 | }
121 |
122 | void "use invalid Font Awesome version with invalidVersionFails = true and no zip files available"() {
123 | when:
124 | def version = "3.2.99"
125 | def invalidVersionMessageStart = "Could not download".toString()
126 | def invalidVersionMessageEnd = "$version is an invalid Font Awesome version, or you are not connected to the Internet.".toString()
127 | def properties = defaultProperties
128 | properties.fontAwesome.install = true
129 | properties.fontAwesome.version = version
130 | properties.fontAwesome.invalidVersionFails = true
131 | createProject(properties)
132 |
133 | then:
134 | final List lines = capture.toString().tokenize(System.properties["line.separator"])
135 | lines.size() == 0
136 | PluginApplicationException exception = thrown()
137 | exception.cause.class == InvalidUserDataException
138 | exception.cause.message.startsWith(invalidVersionMessageStart)
139 | exception.cause.message.endsWith(invalidVersionMessageEnd)
140 | }
141 |
142 | void "change Bootstrap Framework version to #testVersion"() {
143 | when:
144 | def testVersion = "3.3.4"
145 | def prefix = "* Bootstrap v"
146 | def suffix = " (http://getbootstrap.com)"
147 | def properties = defaultProperties
148 | properties.version = testVersion
149 | createProject(properties)
150 | def cssFile = new File("$filePath.css/bootstrap.css")
151 | def jsFile = new File("$filePath.js/bootstrap.js")
152 | def cssFileVersion = cssFile.readLines().get(1).trim() - prefix - suffix
153 | def jsFilesVersion = jsFile.readLines().get(1).trim() - prefix - suffix
154 |
155 | then:
156 | testVersion == cssFileVersion
157 | testVersion == jsFilesVersion
158 | }
159 |
160 | void "apply plugin using default settings"() {
161 | when:
162 | def properties = defaultProperties
163 | createProject(properties)
164 | def data = currentData
165 |
166 | then:
167 | data.bootstrapJsAll.exists()
168 | data.javascriptsCount == 2
169 | data.bootstrapJs.exists()
170 | data.jsCount == 1
171 | data.bootstrapCssAll.exists()
172 | !data.bootstrapLessLess.exists()
173 | data.stylesheetsCount == 2
174 | data.stylesheetsBootstrapCount == 2
175 | data.bootstrapCss.exists()
176 | data.bootstrapThemeCss.exists()
177 | data.css.exists()
178 | data.cssCount == 2
179 | data.fonts.exists()
180 | data.fontsCount == 5
181 | !data.bootstrapLess.exists()
182 | !data.mixinsLess.exists()
183 | !data.less.exists()
184 | data.lessCount == 0
185 | !data.mixins.exists()
186 | data.mixinsCount == 0
187 | !data.fontAwesomeCssAll.exists()
188 | !data.fontAwesomeLessLess.exists()
189 | data.stylesheetsFontAwesomeCount == 0
190 | !data.fontAwesomeCss.exists()
191 | !data.fontAwesomeFonts.exists()
192 | data.fontAwesomeFontsCount == 0
193 | !data.fontAwesomeLess.exists()
194 | data.fontAwesomeLessCount == 0
195 | }
196 |
197 | void "use invalid Bootstrap Framework version with invalidVersionFails = false and zip files are available"() {
198 | when:
199 | def version = "3.2.99"
200 | def prefix = "* Bootstrap v"
201 | def suffix = " (http://getbootstrap.com)"
202 | def properties = defaultProperties
203 | properties.version = version
204 | createProject(properties)
205 | def cssFile = new File("$filePath.css/bootstrap.css")
206 | def jsFile = new File("$filePath.js/bootstrap.js")
207 | def cssFileVersion = cssFile.readLines().get(1).trim() - prefix - suffix
208 | def jsFilesVersion = jsFile.readLines().get(1).trim() - prefix - suffix
209 |
210 | then:
211 | bootstrapDefaultVersion == cssFileVersion
212 | bootstrapDefaultVersion == jsFilesVersion
213 | final List lines = capture.toString().tokenize(System.properties["line.separator"])
214 | lines[0] == "Error: Could not download https://github.com/twbs/bootstrap/archive/v${version}.zip.".toString()
215 | lines[1] == "${version} is an invalid Bootstrap Framework version, or you are not connected to the Internet.".toString()
216 | lines[2] == "Using Bootstrap Framework version $bootstrapDefaultVersion instead of $version.".toString()
217 | notThrown(PluginApplicationException)
218 | }
219 |
220 | void "apply plugin without asset-pipeline"() {
221 | given:
222 | useAssetPipeline = false
223 |
224 | when:
225 | def properties = defaultProperties
226 | properties.cssPath = cssPath
227 | properties.jsPath = jsPath
228 | createProject(properties)
229 | def data = currentData
230 |
231 | then:
232 | !data.bootstrapJsAll.exists()
233 | data.javascriptsCount == 1
234 | data.bootstrapJs.exists()
235 | data.jsCount == 1
236 | !data.bootstrapCssAll.exists()
237 | !data.bootstrapLessLess.exists()
238 | data.stylesheetsCount == 1
239 | data.stylesheetsBootstrapCount == 2
240 | data.bootstrapCss.exists()
241 | data.bootstrapThemeCss.exists()
242 | data.css.exists()
243 | data.cssCount == 2
244 | data.fonts.exists()
245 | data.fontsCount == 5
246 | !data.bootstrapLess.exists()
247 | !data.mixinsLess.exists()
248 | !data.less.exists()
249 | data.lessCount == 0
250 | !data.mixins.exists()
251 | data.mixinsCount == 0
252 | !data.fontAwesomeCssAll.exists()
253 | !data.fontAwesomeLessLess.exists()
254 | data.stylesheetsFontAwesomeCount == 0
255 | !data.fontAwesomeCss.exists()
256 | !data.fontAwesomeFonts.exists()
257 | data.fontAwesomeFontsCount == 0
258 | !data.fontAwesomeLess.exists()
259 | data.fontAwesomeLessCount == 0
260 | }
261 |
262 | void "apply plugin using Bootstrap Framework individual JavaScript files"() {
263 | when:
264 | def properties = defaultProperties
265 | properties.useIndividualJs = true
266 | createProject(properties)
267 | def data = currentData
268 |
269 | then:
270 | data.bootstrapJsAll.exists()
271 | data.javascriptsCount == 2
272 | data.bootstrapJs.exists()
273 | data.jsCount == 13
274 | data.bootstrapCssAll.exists()
275 | !data.bootstrapLessLess.exists()
276 | data.stylesheetsCount == 2
277 | data.stylesheetsBootstrapCount == 2
278 | data.bootstrapCss.exists()
279 | data.bootstrapThemeCss.exists()
280 | data.css.exists()
281 | data.cssCount == 2
282 | data.fonts.exists()
283 | data.fontsCount == 5
284 | !data.bootstrapLess.exists()
285 | !data.mixinsLess.exists()
286 | !data.less.exists()
287 | data.lessCount == 0
288 | !data.mixins.exists()
289 | data.mixinsCount == 0
290 | !data.fontAwesomeCssAll.exists()
291 | !data.fontAwesomeLessLess.exists()
292 | data.stylesheetsFontAwesomeCount == 0
293 | !data.fontAwesomeCss.exists()
294 | !data.fontAwesomeFonts.exists()
295 | data.fontAwesomeFontsCount == 0
296 | !data.fontAwesomeLess.exists()
297 | data.fontAwesomeLessCount == 0
298 | }
299 |
300 | void "apply plugin using Bootstrap Framework LESS support"() {
301 | when:
302 | def properties = defaultProperties
303 | properties.useLess = true
304 | createProject(properties)
305 | def data = currentData
306 |
307 | then:
308 | data.bootstrapJsAll.exists()
309 | data.javascriptsCount == 2
310 | data.bootstrapJs.exists()
311 | data.jsCount == 1
312 | data.bootstrapCssAll.exists()
313 | data.bootstrapLessLess.exists()
314 | data.stylesheetsCount == 3
315 | data.stylesheetsBootstrapCount == 3
316 | data.bootstrapCss.exists()
317 | data.bootstrapThemeCss.exists()
318 | data.css.exists()
319 | data.cssCount == 2
320 | data.fonts.exists()
321 | data.fontsCount == 5
322 | data.bootstrapLess.exists()
323 | data.mixinsLess.exists()
324 | data.less.exists()
325 | data.lessCount == 42
326 | data.mixins.exists()
327 | data.mixinsCount == 30
328 | !data.fontAwesomeCssAll.exists()
329 | !data.fontAwesomeLessLess.exists()
330 | data.stylesheetsFontAwesomeCount == 0
331 | !data.fontAwesomeCss.exists()
332 | !data.fontAwesomeFonts.exists()
333 | data.fontAwesomeFontsCount == 0
334 | !data.fontAwesomeLess.exists()
335 | data.fontAwesomeLessCount == 0
336 | }
337 |
338 | void "apply plugin using Bootstrap Framework version LESS support and individual JavaScript files"() {
339 | when:
340 | def properties = defaultProperties
341 | properties.useIndividualJs = true
342 | properties.useLess = true
343 | createProject(properties)
344 | def data = currentData
345 |
346 | then:
347 | data.bootstrapJsAll.exists()
348 | data.javascriptsCount == 2
349 | data.bootstrapJs.exists()
350 | data.jsCount == 13
351 | data.bootstrapCssAll.exists()
352 | data.bootstrapLessLess.exists()
353 | data.stylesheetsCount == 3
354 | data.stylesheetsBootstrapCount == 3
355 | data.bootstrapCss.exists()
356 | data.bootstrapThemeCss.exists()
357 | data.css.exists()
358 | data.cssCount == 2
359 | data.fonts.exists()
360 | data.fontsCount == 5
361 | data.bootstrapLess.exists()
362 | data.mixinsLess.exists()
363 | data.less.exists()
364 | data.lessCount == 42
365 | data.mixins.exists()
366 | data.mixinsCount == 30
367 | !data.fontAwesomeCssAll.exists()
368 | !data.fontAwesomeLessLess.exists()
369 | data.stylesheetsFontAwesomeCount == 0
370 | !data.fontAwesomeCss.exists()
371 | !data.fontAwesomeFonts.exists()
372 | data.fontAwesomeFontsCount == 0
373 | !data.fontAwesomeLess.exists()
374 | data.fontAwesomeLessCount == 0
375 | }
376 |
377 | void "change Font Awesome version to #testVersion"() {
378 | when:
379 | def testVersion = "4.2.0"
380 | def prefix = "* Font Awesome "
381 | def suffix = " by @davegandy - http://fontawesome.io - @fontawesome"
382 | def properties = defaultProperties
383 | properties.fontAwesome.install = true
384 | properties.fontAwesome.version = testVersion
385 | createProject(properties)
386 | def cssFile = new File("$filePath.faCss/font-awesome.css")
387 | def cssFileVersion = cssFile.readLines().get(1).trim() - prefix - suffix
388 |
389 | then:
390 | testVersion == cssFileVersion
391 | }
392 |
393 | void "apply plugin using default Bootstrap Framework and Font Awesome settings"() {
394 | when:
395 | def properties = defaultProperties
396 | properties.fontAwesome.install = true
397 | createProject(properties)
398 | def data = currentData
399 |
400 | then:
401 | data.bootstrapJsAll.exists()
402 | data.javascriptsCount == 2
403 | data.bootstrapJs.exists()
404 | data.jsCount == 1
405 | data.bootstrapCssAll.exists()
406 | data.bootstrapLessLess.exists()
407 | data.stylesheetsCount == 5
408 | data.stylesheetsBootstrapCount == 2
409 | data.bootstrapCss.exists()
410 | data.bootstrapThemeCss.exists()
411 | data.css.exists()
412 | data.cssCount == 2
413 | data.fonts.exists()
414 | data.fontsCount == 5
415 | !data.bootstrapLess.exists()
416 | !data.mixinsLess.exists()
417 | !data.less.exists()
418 | data.lessCount == 0
419 | !data.mixins.exists()
420 | data.mixinsCount == 0
421 | data.fontAwesomeCssAll.exists()
422 | !data.fontAwesomeLessLess.exists()
423 | data.fontAwesomeCss.exists()
424 | data.fontAwesomeFonts.exists()
425 | data.fontAwesomeFontsCount == 6
426 | !data.fontAwesomeLess.exists()
427 | data.fontAwesomeLessCount == 0
428 | }
429 |
430 | void "apply plugin using Bootstrap Framework default settings and Font Awesome with LESS support"() {
431 | when:
432 | def properties = defaultProperties
433 | properties.fontAwesome.install = true
434 | properties.fontAwesome.useLess = true
435 | createProject(properties)
436 | def data = currentData
437 |
438 | then:
439 | data.bootstrapJsAll.exists()
440 | data.javascriptsCount == 2
441 | data.bootstrapJs.exists()
442 | data.jsCount == 1
443 | data.bootstrapCssAll.exists()
444 | data.bootstrapLessLess.exists()
445 | data.stylesheetsCount == 6
446 | data.stylesheetsBootstrapCount == 2
447 | data.bootstrapCss.exists()
448 | data.bootstrapThemeCss.exists()
449 | data.css.exists()
450 | data.cssCount == 2
451 | data.fonts.exists()
452 | data.fontsCount == 5
453 | !data.bootstrapLess.exists()
454 | !data.mixinsLess.exists()
455 | !data.less.exists()
456 | data.lessCount == 0
457 | !data.mixins.exists()
458 | data.mixinsCount == 0
459 | data.fontAwesomeCssAll.exists()
460 | data.fontAwesomeLessLess.exists()
461 | data.stylesheetsFontAwesomeCount == 3
462 | data.fontAwesomeCss.exists()
463 | data.fontAwesomeFonts.exists()
464 | data.fontAwesomeFontsCount == 6
465 | data.fontAwesomeLess.exists()
466 | data.fontAwesomeLessCount == 13
467 | }
468 |
469 | void "apply plugin with all options enabled and delete files"() {
470 | given:
471 | def properties = defaultProperties
472 | properties.useIndividualJs = true
473 | properties.useLess = true
474 | properties.fontAwesome.install = true
475 | properties.fontAwesome.useLess = true
476 |
477 | when:
478 | createProject(properties)
479 | def data = currentData
480 |
481 | then:
482 | data.bootstrapJsAll.exists()
483 | data.javascriptsCount == 2
484 | data.bootstrapJs.exists()
485 | data.jsCount == 13
486 | data.bootstrapCssAll.exists()
487 | data.bootstrapLessLess.exists()
488 | data.stylesheetsCount == 6
489 | data.stylesheetsBootstrapCount == 3
490 | data.bootstrapCss.exists()
491 | data.bootstrapThemeCss.exists()
492 | data.css.exists()
493 | data.cssCount == 2
494 | data.fonts.exists()
495 | data.fontsCount == 5
496 | data.bootstrapLess.exists()
497 | data.mixinsLess.exists()
498 | data.less.exists()
499 | data.lessCount == 42
500 | data.mixins.exists()
501 | data.mixinsCount == 30
502 | data.fontAwesomeCssAll.exists()
503 | data.fontAwesomeLessLess.exists()
504 | data.stylesheetsFontAwesomeCount == 3
505 | data.fontAwesomeCss.exists()
506 | data.fontAwesomeFonts.exists()
507 | data.fontAwesomeFontsCount == 6
508 | data.fontAwesomeLess.exists()
509 | data.fontAwesomeLessCount == 13
510 |
511 | when:
512 | new File("${filePath.javascripts}/bootstrap-all.js").delete()
513 | new File("${filePath.js}/affix.js").delete()
514 | new File("${filePath.stylesheets}/bootstrap-all.css").delete()
515 | new File("${filePath.stylesheets}/bootstrap-less.less").delete()
516 | new File("${filePath.stylesheets}/font-awesome-all.css").delete()
517 | new File("${filePath.stylesheets}/font-awesome-less.less").delete()
518 | new File("${filePath.css}/bootstrap.css").delete()
519 | new File("${filePath.fonts}/glyphicons-halflings-regular.eot").delete()
520 | new File("${filePath.less}/alerts.less").delete()
521 | new File("${filePath.mixins}/alerts.less").delete()
522 | new File("${filePath.faCss}/font-awesome.css").delete()
523 | new File("${filePath.faFonts}/fontawesome-webfont.eot").delete()
524 | new File("${filePath.faLess}/animated.less").delete()
525 | createProject(properties)
526 | data = currentData
527 |
528 | then:
529 | data.bootstrapJsAll.exists()
530 | data.javascriptsCount == 2
531 | data.bootstrapJs.exists()
532 | data.jsCount == 13
533 | data.bootstrapCssAll.exists()
534 | data.bootstrapLessLess.exists()
535 | data.stylesheetsCount == 6
536 | data.stylesheetsBootstrapCount == 3
537 | data.bootstrapCss.exists()
538 | data.bootstrapThemeCss.exists()
539 | data.css.exists()
540 | data.cssCount == 2
541 | data.fonts.exists()
542 | data.fontsCount == 5
543 | data.bootstrapLess.exists()
544 | data.mixinsLess.exists()
545 | data.less.exists()
546 | data.lessCount == 42
547 | data.mixins.exists()
548 | data.mixinsCount == 30
549 | data.fontAwesomeCssAll.exists()
550 | data.fontAwesomeLessLess.exists()
551 | data.stylesheetsFontAwesomeCount == 3
552 | data.fontAwesomeCss.exists()
553 | data.fontAwesomeFonts.exists()
554 | data.fontAwesomeFontsCount == 6
555 | data.fontAwesomeLess.exists()
556 | data.fontAwesomeLessCount == 13
557 | }
558 |
559 | void "use invalid Font Awesome version with invalidVersionFails = false and zip files are available"() {
560 | given:
561 | def version = "3.2.99"
562 | def prefix = "* Font Awesome "
563 | def suffix = " by @davegandy - http://fontawesome.io - @fontawesome"
564 |
565 | when:
566 | def properties = defaultProperties
567 | properties.fontAwesome.install = true
568 | properties.fontAwesome.version = version
569 | createProject(properties)
570 | def cssFile = new File("$filePath.faCss/font-awesome.css")
571 | def cssFileVersion = cssFile.readLines().get(1).trim() - prefix - suffix
572 |
573 | then:
574 | fontAwesomeDefaultVersion == cssFileVersion
575 | final List lines = capture.toString().tokenize(System.properties["line.separator"])
576 | lines[0] == "Error: Could not download https://github.com/FortAwesome/Font-Awesome/archive/v${version}.zip.".toString()
577 | lines[1] == "${version} is an invalid Font Awesome version, or you are not connected to the Internet.".toString()
578 | lines[2] == "Using Font Awesome version $fontAwesomeDefaultVersion instead of $version.".toString()
579 | notThrown(PluginApplicationException)
580 | }
581 |
582 | static createDirs() {
583 | [
584 | "$filePath.root/$cssPath",
585 | "$filePath.root/$jsPath",
586 | "$filePath.root/$grailsCssPath",
587 | "$filePath.root/$grailsJsPath"
588 | ].each {
589 | new File("$it").mkdirs()
590 | }
591 | }
592 |
593 | static deleteDirs() {
594 | new File("$filePath.root/grails-app").deleteDir()
595 | new File("$filePath.root/src/main/webapp").deleteDir()
596 | }
597 |
598 | static deleteZipFiles() {
599 | new File("$filePath.root/build/tmp").listFiles().each {
600 | if (it.name.startsWith("bootstrap") || it.name.startsWith("fontAwesome")) {
601 | it.delete()
602 | }
603 | }
604 | }
605 |
606 | static getDefaultProperties() {
607 | [
608 | version : bootstrapDefaultVersion,
609 | cssPath : grailsCssPath,
610 | jsPath : grailsJsPath,
611 | useIndividualJs : false,
612 | useLess : false,
613 | invalidVersionFails: false,
614 | fontAwesome : [
615 | install : false,
616 | version : fontAwesomeDefaultVersion,
617 | useLess : false,
618 | invalidVersionFails: false
619 | ]
620 | ]
621 | }
622 |
623 | static createProject(properties) {
624 | Project project = ProjectBuilder.builder().withProjectDir(new File(filePath.root)).build()
625 | project.ext.bootstrapFramework = properties
626 | project.pluginManager.apply "com.siprell.plugins.bootstrap-framework"
627 | project.tasks["downloadBootstrapZip"].execute()
628 | project.tasks["downloadFontAwesomeZip"].execute()
629 | project.tasks["manageBootstrapDirs"].execute()
630 | project.tasks["createBootstrapJsAll"].execute()
631 | project.tasks["createBootstrapJs"].execute()
632 | project.tasks["createBootstrapCssAll"].execute()
633 | project.tasks["createBootstrapFonts"].execute()
634 | project.tasks["createBootstrapCssIndividual"].execute()
635 | project.tasks["createBootstrapLessLess"].execute()
636 | project.tasks["createBootstrapLess"].execute()
637 | project.tasks["createBootstrapMixins"].execute()
638 | project.tasks["createFontAwesomeCssAll"].execute()
639 | project.tasks["createFontAwesomeCssIndividual"].execute()
640 | project.tasks["createFontAwesomeFonts"].execute()
641 | project.tasks["createFontAwesomeLessLess"].execute()
642 | project.tasks["createFontAwesomeLess"].execute()
643 | }
644 |
645 | static getFilePath() {
646 | String root = new File("").absolutePath.toString()
647 | String javascripts = useAssetPipeline ? grailsJsPath : jsPath
648 | String stylesheets = useAssetPipeline ? grailsCssPath : cssPath
649 | String js = "$javascripts/bootstrap"
650 | String css = "$stylesheets/bootstrap/css"
651 | String fonts = "$stylesheets/bootstrap/fonts"
652 | String less = "$stylesheets/bootstrap/less"
653 | String mixins = "$less/mixins"
654 | String faCss = "$stylesheets/font-awesome/css"
655 | String faFonts = "$stylesheets/font-awesome/fonts"
656 | String faLess = "$stylesheets/font-awesome/less"
657 | [
658 | root : root,
659 | javascripts: javascripts,
660 | stylesheets: stylesheets,
661 | js : js,
662 | css : css,
663 | fonts : fonts,
664 | less : less,
665 | mixins : mixins,
666 | faCss : faCss,
667 | faFonts : faFonts,
668 | faLess : faLess
669 | ]
670 | }
671 |
672 | static getCurrentData() {
673 | def bootstrapJsAll = new File("${filePath.javascripts}/bootstrap-all.js")
674 | def javascriptsCount = new File(filePath.javascripts).listFiles().size()
675 | def bootstrapJs = new File("${filePath.js}/bootstrap.js")
676 | def jsCount = new File(filePath.js).listFiles().size()
677 | def bootstrapCssAll = new File("${filePath.stylesheets}/bootstrap-all.css")
678 | def bootstrapLessLess = new File("${filePath.stylesheets}/bootstrap-less.less")
679 | def stylesheetsCount = new File(filePath.stylesheets).listFiles().size()
680 | def stylesheetsBootstrapCount = new File("${filePath.stylesheets}/bootstrap").listFiles().size()
681 | def bootstrapCss = new File("${filePath.css}/bootstrap.css")
682 | def bootstrapThemeCss = new File("${filePath.css}/bootstrap-theme.css")
683 | def css = new File(filePath.css)
684 | def cssCount = css.exists() ? css.listFiles().size() : 0
685 | def fonts = new File(filePath.fonts)
686 | def fontsCount = fonts.exists() ? fonts.listFiles().size() : 0
687 | def bootstrapLess = new File("${filePath.less}/bootstrap.less")
688 | def mixinsLess = new File("${filePath.less}/mixins.less")
689 | def less = new File(filePath.less)
690 | def lessCount = less.exists() ? less.listFiles().size() : 0
691 | def mixins = new File(filePath.mixins)
692 | def mixinsCount = mixins.exists() ? mixins.listFiles().size() : 0
693 | def fontAwesomeCssAll = new File("${filePath.stylesheets}/font-awesome-all.css")
694 | def fontAwesomeLessLess = new File("${filePath.stylesheets}/font-awesome-less.less")
695 | def stylesheetsFontAwesome = new File("${filePath.stylesheets}/font-awesome")
696 | def stylesheetsFontAwesomeCount = stylesheetsFontAwesome.exists() ? stylesheetsFontAwesome.listFiles().size() : 0
697 | def fontAwesomeCss = new File("${filePath.faCss}/font-awesome.css")
698 | def fontAwesomeFonts = new File(filePath.faFonts)
699 | def fontAwesomeFontsCount = fontAwesomeFonts.exists() ? fontAwesomeFonts.listFiles().size() : 0
700 | def fontAwesomeLess = new File(filePath.faLess)
701 | def fontAwesomeLessCount = fontAwesomeLess.exists() ? fontAwesomeLess.listFiles().size() : 0
702 | [
703 | bootstrapJsAll : bootstrapJsAll,
704 | javascriptsCount : javascriptsCount,
705 | bootstrapJs : bootstrapJs,
706 | jsCount : jsCount,
707 | bootstrapCssAll : bootstrapCssAll,
708 | bootstrapLessLess : bootstrapLessLess,
709 | stylesheetsCount : stylesheetsCount,
710 | stylesheetsBootstrapCount : stylesheetsBootstrapCount,
711 | bootstrapCss : bootstrapCss,
712 | bootstrapThemeCss : bootstrapThemeCss,
713 | css : css,
714 | cssCount : cssCount,
715 | fonts : fonts,
716 | fontsCount : fontsCount,
717 | bootstrapLess : bootstrapLess,
718 | mixinsLess : mixinsLess,
719 | less : less,
720 | lessCount : lessCount,
721 | mixins : mixins,
722 | mixinsCount : mixinsCount,
723 | fontAwesomeCssAll : fontAwesomeCssAll,
724 | fontAwesomeLessLess : fontAwesomeLessLess,
725 | stylesheetsFontAwesomeCount: stylesheetsFontAwesomeCount,
726 | fontAwesomeCss : fontAwesomeCss,
727 | fontAwesomeFonts : fontAwesomeFonts,
728 | fontAwesomeFontsCount : fontAwesomeFontsCount,
729 | fontAwesomeLess : fontAwesomeLess,
730 | fontAwesomeLessCount : fontAwesomeLessCount
731 | ]
732 | }
733 |
734 | static getPrettyDate(Long millis) {
735 | SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss.S yyyy.MM.dd")
736 | sdf.format(new Date(millis))
737 | }
738 |
739 | static getPrettyDate(Date date) {
740 | SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss.S yyyy.MM.dd")
741 | sdf.format(date)
742 | }
743 | }
744 |
--------------------------------------------------------------------------------