You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@groovy.apache.org by pa...@apache.org on 2017/04/25 22:57:59 UTC

[20/20] groovy-user-site git commit: initial version

initial version


Project: http://git-wip-us.apache.org/repos/asf/groovy-user-site/repo
Commit: http://git-wip-us.apache.org/repos/asf/groovy-user-site/commit/33e32ad0
Tree: http://git-wip-us.apache.org/repos/asf/groovy-user-site/tree/33e32ad0
Diff: http://git-wip-us.apache.org/repos/asf/groovy-user-site/diff/33e32ad0

Branch: refs/heads/asf-site
Commit: 33e32ad01ee7730325da4c109a82eaab28b74ff7
Parents: 
Author: paulk <pa...@asert.com.au>
Authored: Wed Apr 26 08:23:07 2017 +1000
Committer: paulk <pa...@asert.com.au>
Committed: Wed Apr 26 08:23:07 2017 +1000

----------------------------------------------------------------------
 .gitignore                                      |   10 +
 .travis.yml                                     |    5 +
 LICENSE                                         |  201 +
 README.adoc                                     |   77 +
 build.gradle                                    |   23 +
 generator/build.gradle                          |   17 +
 .../groovy/generator/AsciidoctorFactory.groovy  |    9 +
 .../groovy/generator/ChangelogParser.groovy     |  104 +
 .../src/main/groovy/generator/DocUtils.groovy   |    5 +
 .../generator/DocumentationHTMLCleaner.groovy   |  101 +
 .../main/groovy/generator/PageTemplate.groovy   |   61 +
 .../main/groovy/generator/SiteGenerator.groovy  |  251 +
 generator/src/main/groovy/model/Book.groovy     |   17 +
 .../src/main/groovy/model/Changelog.groovy      |   11 +
 generator/src/main/groovy/model/Course.groovy   |   34 +
 .../src/main/groovy/model/Distribution.groovy   |   19 +
 .../main/groovy/model/DownloadPackage.groovy    |   23 +
 .../src/main/groovy/model/Ecosystem.groovy      |   13 +
 .../src/main/groovy/model/EcosystemEntry.groovy |   18 +
 generator/src/main/groovy/model/Event.groovy    |   19 +
 generator/src/main/groovy/model/Events.groovy   |   13 +
 generator/src/main/groovy/model/Issue.groovy    |   14 +
 generator/src/main/groovy/model/Library.groovy  |   13 +
 generator/src/main/groovy/model/Menu.groovy     |   21 +
 generator/src/main/groovy/model/MenuItem.groovy |   13 +
 generator/src/main/groovy/model/Page.groovy     |   10 +
 generator/src/main/groovy/model/Section.groovy  |   21 +
 .../src/main/groovy/model/SectionItem.groovy    |   14 +
 generator/src/main/groovy/model/SiteMap.groovy  |  134 +
 .../src/main/groovy/model/UserGroup.groovy      |   28 +
 generator/src/main/groovy/model/Video.groovy    |   38 +
 gradle.properties                               |    1 +
 gradle/wrapper/gradle-wrapper.jar               |  Bin 0 -> 52797 bytes
 gradle/wrapper/gradle-wrapper.properties        |    6 +
 gradlew                                         |  169 +
 gradlew.bat                                     |   84 +
 settings.gradle                                 |    4 +
 site/build.gradle                               |  230 +
 site/gradle/templates/deadlinks.groovy          |   25 +
 site/src/site/assets/css/book.css               |  680 ++
 site/src/site/assets/css/bootstrap.css          | 6203 ++++++++++++++++++
 site/src/site/assets/css/docstyle.css           |   65 +
 site/src/site/assets/css/documentation.css      |    1 +
 site/src/site/assets/css/font-awesome.min.css   |    4 +
 site/src/site/assets/css/main.css               |  304 +
 site/src/site/assets/css/normalize.css          |  527 ++
 site/src/site/assets/css/style.css              | 1098 ++++
 site/src/site/assets/fonts/FontAwesome.otf      |  Bin 0 -> 75188 bytes
 .../site/assets/fonts/fontawesome-webfont.eot   |  Bin 0 -> 72449 bytes
 .../site/assets/fonts/fontawesome-webfont.svg   |  504 ++
 .../site/assets/fonts/fontawesome-webfont.ttf   |  Bin 0 -> 141564 bytes
 .../site/assets/fonts/fontawesome-webfont.woff  |  Bin 0 -> 83760 bytes
 site/src/site/assets/img/asf_logo.png           |  Bin 0 -> 21243 bytes
 site/src/site/assets/img/books/Kousen-MJG.png   |  Bin 0 -> 113588 bytes
 site/src/site/assets/img/books/g2cook.jpg       |  Bin 0 -> 16565 bytes
 site/src/site/assets/img/books/gdsl.jpg         |  Bin 0 -> 181096 bytes
 site/src/site/assets/img/books/ggood.jpg        |  Bin 0 -> 34046 bytes
 .../img/books/pratical-grails-3-book-cover.png  |  Bin 0 -> 59923 bytes
 site/src/site/assets/img/books/regina.png       |  Bin 0 -> 25121 bytes
 site/src/site/assets/img/books/vslg2.jpg        |  Bin 0 -> 105938 bytes
 .../img/books/weissmann_groovy_grails.png       |  Bin 0 -> 76365 bytes
 site/src/site/assets/img/confs/g3summit2016.png |  Bin 0 -> 42130 bytes
 site/src/site/assets/img/confs/gr8confeu.png    |  Bin 0 -> 15006 bytes
 site/src/site/assets/img/confs/gr8confin.png    |  Bin 0 -> 15470 bytes
 site/src/site/assets/img/confs/gr8confus.png    |  Bin 0 -> 32301 bytes
 site/src/site/assets/img/confs/greach2017.png   |  Bin 0 -> 91218 bytes
 .../assets/img/courses/groovy-course-cover.png  |  Bin 0 -> 97283 bytes
 site/src/site/assets/img/ecosystem/geb.png      |  Bin 0 -> 9785 bytes
 site/src/site/assets/img/ecosystem/gpars.png    |  Bin 0 -> 17029 bytes
 site/src/site/assets/img/ecosystem/gradle.png   |  Bin 0 -> 18405 bytes
 site/src/site/assets/img/ecosystem/grails.png   |  Bin 0 -> 16001 bytes
 site/src/site/assets/img/ecosystem/griffon.png  |  Bin 0 -> 14209 bytes
 site/src/site/assets/img/ecosystem/ratpack.png  |  Bin 0 -> 5589 bytes
 site/src/site/assets/img/ecosystem/sdkman.png   |  Bin 0 -> 6435 bytes
 site/src/site/assets/img/favicon.ico            |  Bin 0 -> 3813 bytes
 site/src/site/assets/img/groovy-logo-black.svg  |   36 +
 .../src/site/assets/img/groovy-logo-colored.svg |  444 ++
 site/src/site/assets/img/groovy-logo-white.png  |  Bin 0 -> 55066 bytes
 site/src/site/assets/img/groovy-logo-white.svg  |   36 +
 site/src/site/assets/img/groovy-logo.png        |  Bin 0 -> 12303 bytes
 site/src/site/assets/img/icons-colset-2-its.png |  Bin 0 -> 5196 bytes
 site/src/site/assets/img/logo.png               |  Bin 0 -> 435 bytes
 site/src/site/assets/img/logos/airbusgroup.png  |  Bin 0 -> 1894 bytes
 site/src/site/assets/img/logos/amadeus.png      |  Bin 0 -> 3009 bytes
 site/src/site/assets/img/logos/bestbuy.png      |  Bin 0 -> 11763 bytes
 site/src/site/assets/img/logos/carfax.png       |  Bin 0 -> 7381 bytes
 site/src/site/assets/img/logos/carriots.png     |  Bin 0 -> 3655 bytes
 site/src/site/assets/img/logos/cisco.png        |  Bin 0 -> 4331 bytes
 site/src/site/assets/img/logos/commerzbank.png  |  Bin 0 -> 3052 bytes
 .../src/site/assets/img/logos/credit-suisse.png |  Bin 0 -> 2678 bytes
 site/src/site/assets/img/logos/eads.png         |  Bin 0 -> 2768 bytes
 .../site/assets/img/logos/energy-transfer.png   |  Bin 0 -> 2893 bytes
 site/src/site/assets/img/logos/epo.png          |  Bin 0 -> 6403 bytes
 site/src/site/assets/img/logos/fanniemae.png    |  Bin 0 -> 3719 bytes
 site/src/site/assets/img/logos/google.png       |  Bin 0 -> 3708 bytes
 site/src/site/assets/img/logos/hypoport.png     |  Bin 0 -> 6236 bytes
 site/src/site/assets/img/logos/ibm.png          |  Bin 0 -> 2544 bytes
 .../src/site/assets/img/logos/jpmorganchase.png |  Bin 0 -> 2716 bytes
 site/src/site/assets/img/logos/linkedin.png     |  Bin 0 -> 3015 bytes
 site/src/site/assets/img/logos/mastercard.png   |  Bin 0 -> 6312 bytes
 site/src/site/assets/img/logos/mtv.png          |  Bin 0 -> 9553 bytes
 .../site/assets/img/logos/mutual-of-omaha.png   |  Bin 0 -> 6540 bytes
 .../img/logos/national-cancer-institute.png     |  Bin 0 -> 10116 bytes
 site/src/site/assets/img/logos/nestle.png       |  Bin 0 -> 3523 bytes
 site/src/site/assets/img/logos/netflix.png      |  Bin 0 -> 3352 bytes
 site/src/site/assets/img/logos/oracle.png       |  Bin 0 -> 4455 bytes
 .../img/logos/paterson-cancer-institute.png     |  Bin 0 -> 5518 bytes
 site/src/site/assets/img/logos/roche.png        |  Bin 0 -> 5308 bytes
 site/src/site/assets/img/logos/sas.png          |  Bin 0 -> 4680 bytes
 site/src/site/assets/img/logos/sky.png          |  Bin 0 -> 3137 bytes
 site/src/site/assets/img/logos/smartthings.png  |  Bin 0 -> 7829 bytes
 site/src/site/assets/img/logos/sony.png         |  Bin 0 -> 2762 bytes
 site/src/site/assets/img/logos/target.png       |  Bin 0 -> 7259 bytes
 site/src/site/assets/img/logos/thales.png       |  Bin 0 -> 2551 bytes
 site/src/site/assets/img/logos/ubs.png          |  Bin 0 -> 6852 bytes
 site/src/site/assets/img/logos/vodafone.png     |  Bin 0 -> 4623 bytes
 site/src/site/assets/img/logos/voyages-sncf.png |  Bin 0 -> 5621 bytes
 site/src/site/assets/img/logos/walmart.png      |  Bin 0 -> 3246 bytes
 site/src/site/assets/img/logos/wells-fargo.png  |  Bin 0 -> 5709 bytes
 .../img/videos/groovy-ecosystem-revisited.png   |  Bin 0 -> 71130 bytes
 .../site/assets/img/videos/groovy-puzzlers.png  |  Bin 0 -> 85894 bytes
 .../img/videos/metaprogramming-part-1.png       |  Bin 0 -> 67863 bytes
 site/src/site/assets/js/plugins.js              |   24 +
 site/src/site/assets/js/vendor/bootstrap.js     | 2114 ++++++
 site/src/site/assets/js/vendor/bootstrap.min.js |    6 +
 site/src/site/assets/js/vendor/classie.js       |   80 +
 .../site/assets/js/vendor/jquery-1.10.2.min.js  |    6 +
 .../assets/js/vendor/modernizr-2.6.2.min.js     |    4 +
 .../src/site/assets/js/vendor/sidebarEffects.js |   65 +
 site/src/site/html/index.html                   |   68 +
 site/src/site/html/they-use-groovy.html         |   45 +
 site/src/site/html/twittersearch.html           |    2 +
 site/src/site/includes/bottommenu.groovy        |   29 +
 site/src/site/includes/community-navbar.groovy  |   22 +
 site/src/site/includes/contribute-button.groovy |    7 +
 site/src/site/includes/topmenu.groovy           |   31 +
 site/src/site/layouts/iframedoc.groovy          |   16 +
 site/src/site/layouts/main.groovy               |   21 +
 site/src/site/layouts/page.groovy               |   90 +
 site/src/site/pages/404.groovy                  |   27 +
 site/src/site/pages/api.groovy                  |    2 +
 site/src/site/pages/buildstatus.groovy          |   77 +
 site/src/site/pages/changelog.groovy            |   39 +
 site/src/site/pages/changelogs.groovy           |   43 +
 site/src/site/pages/community.groovy            |   58 +
 site/src/site/pages/contribute.groovy           |  197 +
 site/src/site/pages/docpage.groovy              |   37 +
 site/src/site/pages/documentation.groovy        |   79 +
 site/src/site/pages/download.groovy             |  323 +
 site/src/site/pages/ecosystem.groovy            |   59 +
 site/src/site/pages/events.groovy               |   61 +
 site/src/site/pages/faq.groovy                  |   52 +
 site/src/site/pages/gdk.groovy                  |    2 +
 site/src/site/pages/groovy-weekly.groovy        |  103 +
 site/src/site/pages/index.groovy                |   44 +
 site/src/site/pages/indy.groovy                 |  174 +
 site/src/site/pages/learn.groovy                |  193 +
 site/src/site/pages/mailing-lists.groovy        |   76 +
 site/src/site/pages/release-notes.groovy        |   48 +
 site/src/site/pages/releases.groovy             |   36 +
 site/src/site/pages/search.groovy               |  113 +
 site/src/site/pages/security.groovy             |  150 +
 .../site/pages/singlepagedocumentation.groovy   |    2 +
 site/src/site/pages/thanks.groovy               |   51 +
 site/src/site/pages/usergroups.groovy           |   66 +
 site/src/site/pages/versioning.groovy           |   69 +
 site/src/site/pages/wiki.groovy                 |   68 +
 site/src/site/releasenotes/groovy-1.5.adoc      |  858 +++
 site/src/site/releasenotes/groovy-1.6.adoc      | 1436 ++++
 site/src/site/releasenotes/groovy-1.7.adoc      |  433 ++
 site/src/site/releasenotes/groovy-1.8.adoc      | 1554 +++++
 site/src/site/releasenotes/groovy-2.0.adoc      |  886 +++
 site/src/site/releasenotes/groovy-2.1.adoc      |  963 +++
 site/src/site/releasenotes/groovy-2.2.adoc      |  256 +
 site/src/site/releasenotes/groovy-2.3.adoc      |  834 +++
 site/src/site/releasenotes/groovy-2.4.adoc      |  220 +
 site/src/site/releasenotes/groovy-2.5.adoc      |  112 +
 site/src/site/sitemap.groovy                    |  473 ++
 site/src/site/wiki/groovy-release.adoc          |  132 +
 .../site/wiki/incubation-release-process.adoc   |  263 +
 180 files changed, 25057 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/groovy-user-site/blob/33e32ad0/.gitignore
----------------------------------------------------------------------
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..6ba533c
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,10 @@
+.idea
+*.iml
+*.ipr
+*.iws
+build
+.gradle
+.classpath
+atlassian-ide-plugin.xml
+
+

http://git-wip-us.apache.org/repos/asf/groovy-user-site/blob/33e32ad0/.travis.yml
----------------------------------------------------------------------
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..dc7b712
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,5 @@
+script: ./gradlew -q -s generate
+jdk:
+- openjdk7
+
+

http://git-wip-us.apache.org/repos/asf/groovy-user-site/blob/33e32ad0/LICENSE
----------------------------------------------------------------------
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..ad410e1
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,201 @@
+Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "{}"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright {yyyy} {name of copyright owner}
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/groovy-user-site/blob/33e32ad0/README.adoc
----------------------------------------------------------------------
diff --git a/README.adoc b/README.adoc
new file mode 100644
index 0000000..230d929
--- /dev/null
+++ b/README.adoc
@@ -0,0 +1,77 @@
+= Groovy website
+The Groovy development team
+:revdate: 24-02-2014
+:build-icon: http://ci.groovy-lang.org:8111/app/rest/builds/buildType:(id:Groovy_Website)/statusIcon
+:noheader:
+:groovy-www: http://groovy-lang.org/
+:groovy-ci: http://ci.groovy-lang.org/viewType.html?buildTypeId=Groovy_Website&guest=1
+:gradle: http://www.gradle.org
+:markupte: http://docs.groovy-lang.org/latest/html/documentation/markup-template-engine.html
+
+[.left.text-left]
+image::http://groovy-lang.org/img/groovy-logo.png[]
+{groovy-www}[Groovy] is an agile and dynamic language for the Java Virtual Machine. It builds upon the strengths of Java, but has additional power features inspired by languages like Python, Ruby and Smalltalk.
+
+Groovy makes modern programming features available to Java developers with almost-zero learning curve as well as supports Domain-Specific Languages and other compact syntax so your code becomes easy to read and maintain.
+
+Groovy makes writing shell and build scripts easy with its powerful processing primitives, OO abilities and an Ant DSL.
+
+It also increases developer productivity by reducing scaffolding code when developing web, GUI, database or console applications. Groovy simplifies testing by supporting unit testing and mocking out-of-the-box. Groovy also seamlessly integrates with all existing Java classes and libraries and compiles straight to Java bytecode so you can use it anywhere you can use Java.
+
+== Sources for the Groovy website
+
+This project builds the Groovy website. It is using {gradle}[Gradle] and is fully statically generated.
+
+Build is image:{build-icon}[build status, link={groovy-ci}].
+
+== Generating the site
+
+----
+git clone http://git-wip-us.apache.org/repos/asf/groovy-user-site.git
+cd groovy-user-site
+./gradlew webzip
+----
+
+The output can be found in the `build` directory:
+
+----
+build
+  |---- site            : the generated static website
+  |---- reports         : deadlinks report
+  |---- distributions   : zip of the website
+----
+
+== Contributing
+
+The website is generated thanks to Gradle and makes use of the {markupte}[Markup Template Engine]. The structure of the
+project consists of two modules:
+
+----
+generator                       : utility classes and model for generating the website
+site                            : the website itself
+----
+
+The website subproject consists of:
+
+----
+src/main/site                   : sources for the static website
+          |--- assets           : static resources such as images, CSS files, ...
+          |--- html             : elements that templates include as raw HTML contents
+          |--- includes         : includes used by templates
+          |--- layouts          : layouts for the various pages
+          |--- pages            : individual pages
+build.gradle                    : website weaving logic
+----
+
+Additional details can be found in this http://melix.github.io/blog/2014/07/new-groovy-website.html[blog post].
+
+== Continuous Integration
+
+The official CI server runs {groovy-ci}[here] (login as user guest and leave the password blank) and is sponsored by http://www.jetbrains.com[JetBrains].
+
+WARNING: The website is continuously updated from the _master_ branch. This means that *every merge on master is immediately published*. Changes that need to be
+applied on a specific date need to be done on a dedicated branch.
+
+== License
+
+Groovy is licensed under the terms of the http://www.apache.org/licenses/LICENSE-2.0.html[Apache License, Version 2.0]

http://git-wip-us.apache.org/repos/asf/groovy-user-site/blob/33e32ad0/build.gradle
----------------------------------------------------------------------
diff --git a/build.gradle b/build.gradle
new file mode 100644
index 0000000..d56e185
--- /dev/null
+++ b/build.gradle
@@ -0,0 +1,23 @@
+// Grab the plugin from a Maven Repo automatically
+buildscript {
+    repositories {
+        jcenter()
+    }
+    dependencies {
+        classpath 'com.yahoo.platform.yui:yuicompressor:2.4.7'
+    }
+}
+
+plugins {
+    id 'com.gradle.build-scan' version '1.0'
+}
+
+buildScan {
+    licenseAgreementUrl = 'https://gradle.com/terms-of-service'
+    licenseAgree = 'yes'
+}
+
+allprojects {
+    apply plugin: 'idea'
+}
+

http://git-wip-us.apache.org/repos/asf/groovy-user-site/blob/33e32ad0/generator/build.gradle
----------------------------------------------------------------------
diff --git a/generator/build.gradle b/generator/build.gradle
new file mode 100644
index 0000000..5f36eb0
--- /dev/null
+++ b/generator/build.gradle
@@ -0,0 +1,17 @@
+apply plugin: 'groovy'
+
+repositories {
+    jcenter()
+}
+
+dependencies {
+    ext.groovyVersion = '2.4.8'
+    ext.asciidocVersion = '1.5.4.1'
+    compile "org.codehaus.groovy:groovy:$groovyVersion"
+    compile "org.codehaus.groovy:groovy-json:$groovyVersion"
+    compile "org.codehaus.groovy:groovy-templates:$groovyVersion"
+    compile "org.asciidoctor:asciidoctorj:$asciidocVersion"
+}
+
+compileGroovy.sourceCompatibility = '1.7'
+compileGroovy.targetCompatibility = '1.7'
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/groovy-user-site/blob/33e32ad0/generator/src/main/groovy/generator/AsciidoctorFactory.groovy
----------------------------------------------------------------------
diff --git a/generator/src/main/groovy/generator/AsciidoctorFactory.groovy b/generator/src/main/groovy/generator/AsciidoctorFactory.groovy
new file mode 100644
index 0000000..72d48dd
--- /dev/null
+++ b/generator/src/main/groovy/generator/AsciidoctorFactory.groovy
@@ -0,0 +1,9 @@
+package generator
+
+import groovy.transform.CompileStatic
+import org.asciidoctor.Asciidoctor
+
+@CompileStatic
+class AsciidoctorFactory {
+    @Lazy static Asciidoctor instance = Asciidoctor.Factory.create()
+}

http://git-wip-us.apache.org/repos/asf/groovy-user-site/blob/33e32ad0/generator/src/main/groovy/generator/ChangelogParser.groovy
----------------------------------------------------------------------
diff --git a/generator/src/main/groovy/generator/ChangelogParser.groovy b/generator/src/main/groovy/generator/ChangelogParser.groovy
new file mode 100644
index 0000000..ee84faa
--- /dev/null
+++ b/generator/src/main/groovy/generator/ChangelogParser.groovy
@@ -0,0 +1,104 @@
+package generator
+
+import groovy.json.JsonSlurper
+import model.Changelog
+import model.Issue
+
+import java.util.regex.Pattern
+
+class ChangelogParser {
+    private static final String JIRA_SERVER = 'https://issues.apache.org/jira'
+    private static final String PROJECT_NAME = 'GROOVY'
+    private static final String PROJECT_ID = '12318123'
+
+    private static final String LOGNOTES_INTRO = /Release Notes - Groovy/
+    private static final String LOGNOTES_END = /<\/textarea>/
+    private static final String BUGTYPE_MARK = '** '
+    private static final String ITEM_MARK = '    * '
+    private static final Pattern ITEM_PATTERN = ~/\[(GROOVY-[0-9]+)\] - (.+)/
+    private static final String VERSION_PATTERN = /^((1\.)|[23]\.)/
+
+    static List<Changelog> fetchReleaseNotes(File cacheDirectory) {
+        def slurper = new JsonSlurper()
+        def versions = slurper.parse("$JIRA_SERVER/rest/api/2/project/$PROJECT_NAME/versions".toURL())
+        def versionMap = versions.findAll {
+            it.name =~ VERSION_PATTERN &&
+                    it.released == true
+        }.collectEntries {
+            [fixName(it.name), it.id]
+        }
+
+        def raw = versionMap.collect { name, id ->
+            println "Fetching changelog for version $name"
+            new Changelog(groovyVersion: name, issues: changelogHTML(id, cacheDirectory))
+        }
+        createAggregates(raw, versionMap.keySet())
+    }
+
+    private static String fixName(String name) {
+        String id = name
+        String classifier = ''
+
+        int idx = name.indexOf('-')
+        if (idx>0) {
+            classifier = name.substring(idx)
+            id = name - classifier
+        }
+        if (id.count('.')<2) {
+            // groovy 2.0 instead of 2.0.0
+            id = "${id}.0"
+        }
+        "$id$classifier"
+    }
+
+    private static List<Changelog> createAggregates(final List<Changelog> changelogs, final Set<String> releasedVersions) {
+        def allMajor = changelogs.groupBy {
+            def v = it.groovyVersion
+            v.contains('-')?v-v.substring(v.indexOf('-')):v
+        }.findAll { ver, logs -> ver in releasedVersions }
+        allMajor.collect { k,v ->
+            def changelog = changelogs.find { it.groovyVersion == k }
+            if (!changelog) {
+                println "Not found: $k"
+                changelog = new Changelog(groovyVersion: k, issues:[])
+                changelogs << changelog
+            }
+            v.each {
+                changelog.issues = [*changelog.issues, *it.issues].unique().sort { it.id }
+            }
+        }
+        changelogs
+    }
+
+    private static List<Issue> changelogHTML(String id, File cacheDir) {
+        def cache = new File(cacheDir, "changelog-${id}.html")
+        def log
+        if (cache.exists()) {
+            log = cache.getText('UTF-8')
+        } else {
+            log = new URL("$JIRA_SERVER/secure/ReleaseNote.jspa?version=$id&styleName=Text&projectId=$PROJECT_ID").getText('UTF-8')
+            cache.write(log, 'UTF-8')
+        }
+        boolean inNotes = false
+        String type = null
+        List<Issue> issues = []
+
+        log.eachLine { line ->
+            if (line.startsWith(LOGNOTES_INTRO)) {
+                inNotes = true
+            } else if (line.startsWith(LOGNOTES_END)) {
+                inNotes = false
+            } else if (inNotes) {
+                if (line.startsWith(BUGTYPE_MARK)) {
+                    type = line - BUGTYPE_MARK
+                } else if (line.startsWith(ITEM_MARK)) {
+                    def m = ITEM_PATTERN.matcher(line)
+                    m.find()
+                    issues << new Issue(id: m.group(1), description: m.group(2), type: type)
+                }
+            }
+        }
+
+        issues
+    }
+}

http://git-wip-us.apache.org/repos/asf/groovy-user-site/blob/33e32ad0/generator/src/main/groovy/generator/DocUtils.groovy
----------------------------------------------------------------------
diff --git a/generator/src/main/groovy/generator/DocUtils.groovy b/generator/src/main/groovy/generator/DocUtils.groovy
new file mode 100644
index 0000000..49d8609
--- /dev/null
+++ b/generator/src/main/groovy/generator/DocUtils.groovy
@@ -0,0 +1,5 @@
+package generator
+
+class DocUtils {
+    @Lazy public static final String DOCS_BASEURL = System.getProperty('docs_baseurl')
+}

http://git-wip-us.apache.org/repos/asf/groovy-user-site/blob/33e32ad0/generator/src/main/groovy/generator/DocumentationHTMLCleaner.groovy
----------------------------------------------------------------------
diff --git a/generator/src/main/groovy/generator/DocumentationHTMLCleaner.groovy b/generator/src/main/groovy/generator/DocumentationHTMLCleaner.groovy
new file mode 100644
index 0000000..fafa9db
--- /dev/null
+++ b/generator/src/main/groovy/generator/DocumentationHTMLCleaner.groovy
@@ -0,0 +1,101 @@
+package generator
+
+import groovy.transform.CompileStatic
+
+/**
+ * This class is responsible for downloading a documentation page as generated through the Asciidoctor task
+ * of the Groovy build, then filter its contents in order to return only the body of the documentation, as HTML.
+ *
+ * @author C�dric Champeau
+ */
+@CompileStatic
+class DocumentationHTMLCleaner {
+    private final static String BODY_START = /<body/
+    private final static String BODY_END = /<\/body/
+    private final static String TOC_START = /<div id="toc"/
+    private final static String MAIN_START = /<div id="content"/
+    private final static String MAIN_END = /<div id="footer"/
+
+    /**
+     * A list of links which are badly generated, but we know how to fix them
+     */
+    private final static Map<String,String> KNOWN_REPLACEMENTS = [
+        /docs\.groovy-lang\.org\/(latest|next)\/html\/documentation\/gdk\.html/: 'groovy-lang.org/gdk.html',
+        /\/maven\/groovy-/: '/maven/apache-groovy-'
+    ]
+
+    private static String cleanupPage(String location) {
+        def url = location.toURL()
+        try {
+            def fullHTML = url.getText('utf-8')
+            return extractBetween(fullHTML, BODY_START, BODY_END)
+        } catch (FileNotFoundException e) {
+            // 404 not found
+        }
+
+        null
+    }
+
+    private static String extractBetween(String html, String startString, String endString) {
+        def start = html.indexOf(startString)
+        if (start > 0) {
+            start = html.indexOf('>', start) + 1
+        }
+        if (start > 1) {
+            def end = html.indexOf(endString, start)
+            if (end > start) {
+                return html.substring(start, end)
+            }
+        }
+        null
+    }
+
+    public static DocPage parsePage(String location) {
+        String contents = cleanupPage(location)
+        if (contents==null) {
+            return new DocPage(content: "Contents not found for <a href='$location'>$location</a>, most likely because this section has not yet been written.")
+        }
+        String toc = extractTOC(contents)?:''
+        String main = extractBetween(contents, MAIN_START, MAIN_END)?:"Main body not found for <a href='$location'>$location</a>"
+        main = replaceInternalLinks(main)
+        new DocPage(toc: toc, content: main)
+    }
+
+    private static String replaceInternalLinks(String html) {
+        def replacer = { List<String> it ->
+            def (String tag, String attr, String url) = [it[1], it[2], it[3]]
+            url = url.replaceAll(/x(.+)\.(?:pagespeed.+)/, '$1')
+            if (!url.startsWith('http') && !url.startsWith('#') && 'target.html'!=url) {
+                "$tag $attr'${DocUtils.DOCS_BASEURL}/html/documentation/$url'"
+            } else {
+                it[0]
+            }
+        }
+        html = html.replaceAll(/(a)\s+(href=)["'](.+?)["']/,replacer)
+        html = html.replaceAll(/(img)\s+(src=)["'](.+?)["']/,replacer)
+        KNOWN_REPLACEMENTS.each { link, repl ->
+            html = html.replaceAll(link, repl)
+        }
+        html
+    }
+
+    private static String extractTOC(final String html) {
+        int start = html.indexOf(TOC_START)
+        if (start > 0) {
+            int end = html.indexOf(MAIN_START)
+            if (end>0) {
+                def out = html.substring(start, end).replace("<div id=\"toctitle\">Table of Contents</div>", "")
+                end = out.size()-1
+                while (!out.substring(end, out.size()).startsWith('</div>')) end--
+                return out.substring(0, end)
+            }
+        }
+        null
+    }
+
+    static class DocPage {
+        String toc = ''
+        String content
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/groovy-user-site/blob/33e32ad0/generator/src/main/groovy/generator/PageTemplate.groovy
----------------------------------------------------------------------
diff --git a/generator/src/main/groovy/generator/PageTemplate.groovy b/generator/src/main/groovy/generator/PageTemplate.groovy
new file mode 100644
index 0000000..cb753f8
--- /dev/null
+++ b/generator/src/main/groovy/generator/PageTemplate.groovy
@@ -0,0 +1,61 @@
+package generator
+
+import groovy.text.markup.BaseTemplate
+import groovy.text.markup.MarkupTemplateEngine
+import groovy.text.markup.TemplateConfiguration
+import groovy.transform.CompileStatic
+import org.asciidoctor.Asciidoctor
+import org.asciidoctor.AttributesBuilder
+
+@CompileStatic
+abstract class PageTemplate extends BaseTemplate {
+    public static final String BASEDIR = "basePath";
+
+    private final Map model
+
+    PageTemplate(
+            final MarkupTemplateEngine templateEngine,
+            final Map model, final Map<String, String> modelTypes, final TemplateConfiguration configuration) {
+        super(templateEngine, model, modelTypes, configuration)
+        this.model = model
+    }
+
+    String relative(String path) {
+        String base = (String) model.get(BASEDIR)
+        if (base && !path.startsWith('http') && !path.startsWith(File.separator)) {
+            String up = "..${File.separator}"
+            "${up*(1+base.count(File.separator))}$path"
+        } else {
+            path
+        }
+    }
+
+    /**
+     * Converts and outputs asciidoctor markup into HTML
+     * @param body the asciidoctor markup
+     */
+    void asciidoc(String body, Map options=[:]) {
+        yieldUnescaped asciidocText(body, options)
+    }
+
+    /**
+     * Converts and returns asciidoctor markup into HTML. This method
+     * does *not* automatically renders the result so it is possible
+     * to post-process the generated HTML.
+     * @param body the asciidoctor markup
+     */
+    String asciidocText(String body, Map options=[:]) {
+        def asciidoctor = AsciidoctorFactory.instance
+        def attributes = options.attributes
+        if (!attributes) {
+            attributes = [:]
+            options.put('attributes', attributes)
+        }
+        attributes['source-highlighter'] = 'prettify'
+        asciidoctor.convert(body,options)
+    }
+
+    String latestDocURL(String target) {
+        "${DocUtils.DOCS_BASEURL}/html/$target"
+    }
+}

http://git-wip-us.apache.org/repos/asf/groovy-user-site/blob/33e32ad0/generator/src/main/groovy/generator/SiteGenerator.groovy
----------------------------------------------------------------------
diff --git a/generator/src/main/groovy/generator/SiteGenerator.groovy b/generator/src/main/groovy/generator/SiteGenerator.groovy
new file mode 100644
index 0000000..77c9c48
--- /dev/null
+++ b/generator/src/main/groovy/generator/SiteGenerator.groovy
@@ -0,0 +1,251 @@
+package generator
+
+import groovy.text.markup.MarkupTemplateEngine
+import groovy.text.markup.TemplateConfiguration
+import groovy.transform.CompileStatic
+import model.Changelog
+import model.Page
+import model.Section
+import model.SectionItem
+import model.SiteMap
+
+import java.nio.file.FileSystems
+import java.nio.file.Path
+import java.nio.file.WatchEvent
+
+import static generator.DocumentationHTMLCleaner.cleanupPage
+import static generator.DocumentationHTMLCleaner.parsePage
+import static java.nio.file.StandardWatchEventKinds.*
+
+@CompileStatic
+class SiteGenerator {
+
+    private final static Closure SEMANTIC_SORT = { String v1, String v2 ->
+        List<String> items1 = decomposeVersion(v1)
+        List<String> items2 = decomposeVersion(v2)
+        for (int i=0; i<Math.max(items1.size(),items2.size());i++) {
+            if (i>=items2.size()) {
+                return 1
+            }
+            if (i>=items1.size()) {
+                return -1
+            }
+            def p1 = items1[i]
+            def p2 = items2[i]
+            if (p1.isNumber()) {
+                if (p2.isNumber()) {
+                    def pi1 = p1.toInteger()
+                    def pi2 = p2.toInteger()
+
+                    if (pi1 < pi2) {
+                        return 1
+                    } else if (pi1 > pi2) {
+                        return -1
+                    }
+                } else {
+                    return -1
+                }
+            } else if (p2.isNumber()) {
+                return 1
+            } else {
+                return p2 <=> p1
+            }
+        }
+        0
+    }
+
+    File sourcesDir
+    File outputDir
+
+    private MarkupTemplateEngine tplEngine
+    private SiteMap siteMap
+
+    void setup() {
+
+        println "Generating website using Groovy ${GroovySystem.version}"
+
+        def tplConf = new TemplateConfiguration()
+        tplConf.autoIndent = true
+        tplConf.autoNewLine = true
+        tplConf.baseTemplateClass = PageTemplate
+
+        def classLoader = new URLClassLoader([sourcesDir.toURI().toURL()] as URL[], this.class.classLoader)
+        tplEngine = new MarkupTemplateEngine(classLoader, tplConf, new MarkupTemplateEngine.CachingTemplateResolver())
+
+        siteMap = SiteMap.from(new File(sourcesDir, "sitemap.groovy"))
+
+    }
+
+    void render(String page, String target = null, Map model = [:], String baseDir=null) {
+        model.menu = siteMap.menu
+        model.currentPage = target
+        target = target ?: page
+        File root
+        if (baseDir) {
+            root = new File(outputDir, baseDir)
+            model[PageTemplate.BASEDIR] = baseDir
+            root.mkdirs()
+        } else {
+            root = outputDir
+        }
+
+        new File(root,"${target}.html").write(tplEngine.createTemplateByPath("pages/${page}.groovy").make(model).toString(), 'utf-8')
+    }
+
+    void generateSite() {
+        long sd = System.currentTimeMillis()
+        setup()
+
+        def cacheDir = new File(new File('build'), 'cache')
+        cacheDir.mkdirs()
+        println "Cache directory: $cacheDir"
+        def changelogs = ChangelogParser.fetchReleaseNotes(cacheDir);
+
+        renderDocumentation()
+
+        renderPages(changelogs)
+
+        renderChangelogs(changelogs)
+
+        renderReleaseNotes()
+
+        renderWiki()
+
+
+        long dur = System.currentTimeMillis() - sd
+        println "Generated site into $outputDir in ${dur}ms"
+    }
+
+    private List<Section> renderDocumentation() {
+        siteMap.documentationSections.each { Section section ->
+            section.items.each { SectionItem item ->
+                if (item.generate) {
+                    println "Generating documentation page [$item.name]"
+                    render 'docpage', item.targetFilename, [
+                            category: 'Learn',
+                            title   : item.name,
+                            page    : parsePage("${DocUtils.DOCS_BASEURL}/html/documentation/${item.sourceFilename}.html")]
+                }
+            }
+        }
+    }
+
+    private List<Page> renderPages(List<Changelog> changelogs) {
+        siteMap.pages.each { Page page ->
+            println "Rendering individual page [$page.source]"
+            if ('changelogs' == page.source) {
+                page.model.versions = changelogs.groovyVersion.sort(SEMANTIC_SORT)
+            }
+            render page.source, page.target, page.model
+        }
+    }
+
+    private List<Changelog> renderChangelogs(List<Changelog> changelogs) {
+        changelogs.each {
+            println "Rendering changelog for Groovy $it.groovyVersion"
+            render 'changelog', "changelog-$it.groovyVersion", [groovyVersion: it.groovyVersion, issues: it.issues], 'changelogs'
+        }
+    }
+
+    private void renderReleaseNotes() {
+        def releaseNotesVersions = new TreeSet<String>(new Comparator<String>() {
+            @Override
+            int compare(final String v1, final String v2) {
+                v2.toDouble() <=> v1.toDouble()
+            }
+        })
+        new File(sourcesDir, 'releasenotes').eachFile { File file ->
+            def name = file.name.substring(0, file.name.lastIndexOf('.adoc'))
+            def version = name - 'groovy-'
+            releaseNotesVersions << version
+            println "Rendering release notes for Groovy $version"
+            render 'release-notes', name, [notes: file.getText('utf-8'), groovyVersion: version], 'releasenotes'
+        }
+        render 'releases', 'releases', [versions: releaseNotesVersions]
+    }
+
+    private void renderWiki() {
+        def asciidoctor = AsciidoctorFactory.instance
+        println "Rendering wiki"
+
+        def wikiDir = new File(sourcesDir, "wiki")
+        wikiDir.eachFileRecurse { f->
+            if (f.name.endsWith('.adoc')) {
+                def header = asciidoctor.readDocumentHeader(f)
+                def bn = f.name.substring(0,f.name.lastIndexOf('.adoc'))
+                println "Rendering $header.documentTitle.main by ${header.author?.fullName}"
+                def relativePath = []
+                def p = f.parentFile
+                while (p!=wikiDir) {
+                    relativePath << p.name
+                    p = p.parentFile
+                }
+                String baseDir = relativePath?"wiki${File.separator}${relativePath.join(File.separator)}":'wiki'
+                render 'wiki', bn, [notes:f.getText('utf-8'), header: header], baseDir
+                println baseDir
+            }
+        }
+    }
+
+    static void main(String... args) {
+        def sourcesDir = args[0] as File
+        def outputDir = args[1] as File
+        def generator = new SiteGenerator(sourcesDir: sourcesDir, outputDir: outputDir)
+        boolean watchMode = args.length > 2 ? Boolean.valueOf(args[2]) : false
+        generator.generateSite()
+
+        if (watchMode) {
+            println "Started watch mode"
+            def watcher = FileSystems.default.newWatchService()
+
+            sourcesDir.toPath().register(watcher,
+                    ENTRY_CREATE,
+                    ENTRY_DELETE,
+                    ENTRY_MODIFY)
+
+            sourcesDir.eachDirRecurse { File f ->
+                f.toPath().register(watcher,
+                        ENTRY_CREATE,
+                        ENTRY_DELETE,
+                        ENTRY_MODIFY)
+            }
+
+            def existingDirectories = ['pages', 'layouts', 'includes', 'html', 'assets', 'css', 'fonts', 'img', 'js', 'vendor']
+
+            while (true) {
+                def key = watcher.take()
+                def pollEvents = (List<WatchEvent<Path>>) key.pollEvents()
+
+                def changed = pollEvents.collect { "${it.context()}".toString() }.join(', ')
+
+                // only generate when the event refers to the actual file modified / created / added
+                // as otherwise the watcher service generates two events:
+                // 1) one for directory containing the modified file, and
+                // 2) one for the actual file being modified
+                // this checks avoid getting two events for one change
+                if (existingDirectories.every { !changed.contains(it) }) {
+                    try {
+                        println "Regenerating site due to changes in: ${changed}"
+                        // todo: selective regeneration
+                        generator.generateSite()
+                    } finally {
+                        key.reset()
+                    }
+                }
+            }
+        }
+    }
+
+    static List<String> decomposeVersion(String version) {
+        String qualifier = ''
+        if (version.indexOf('-')>0) {
+            qualifier = version.substring(version.indexOf('-'))
+            version = version - qualifier
+        }
+        List<String> parts = version.split(/\./).toList()
+        if (qualifier) {
+            parts << qualifier
+        }
+        parts
+    }
+}

http://git-wip-us.apache.org/repos/asf/groovy-user-site/blob/33e32ad0/generator/src/main/groovy/model/Book.groovy
----------------------------------------------------------------------
diff --git a/generator/src/main/groovy/model/Book.groovy b/generator/src/main/groovy/model/Book.groovy
new file mode 100644
index 0000000..4537ace
--- /dev/null
+++ b/generator/src/main/groovy/model/Book.groovy
@@ -0,0 +1,17 @@
+package model
+
+import groovy.transform.CompileStatic
+
+@CompileStatic
+class Book {
+    String title
+    String authors
+    String cover
+    String url
+    String description
+
+    void authors    (String authors)     { this.authors     = authors }
+    void cover      (String cover)       { this.cover       = cover }
+    void url        (String url)         { this.url         = url }
+    void description(String description) { this.description = description }
+}

http://git-wip-us.apache.org/repos/asf/groovy-user-site/blob/33e32ad0/generator/src/main/groovy/model/Changelog.groovy
----------------------------------------------------------------------
diff --git a/generator/src/main/groovy/model/Changelog.groovy b/generator/src/main/groovy/model/Changelog.groovy
new file mode 100644
index 0000000..b7ba910
--- /dev/null
+++ b/generator/src/main/groovy/model/Changelog.groovy
@@ -0,0 +1,11 @@
+package model
+
+import groovy.transform.CompileStatic
+import groovy.transform.ToString
+
+@CompileStatic
+@ToString
+class Changelog {
+    String groovyVersion
+    List<Issue> issues
+}

http://git-wip-us.apache.org/repos/asf/groovy-user-site/blob/33e32ad0/generator/src/main/groovy/model/Course.groovy
----------------------------------------------------------------------
diff --git a/generator/src/main/groovy/model/Course.groovy b/generator/src/main/groovy/model/Course.groovy
new file mode 100644
index 0000000..68f8618
--- /dev/null
+++ b/generator/src/main/groovy/model/Course.groovy
@@ -0,0 +1,34 @@
+package model
+
+import groovy.transform.CompileStatic
+
+@CompileStatic
+class Course {
+
+    String title
+    String instructor
+    String url
+    String description
+    String cover
+
+    void title(String title) {
+        this.title = title
+    }
+
+    void instructor(String instructor) {
+        this.instructor = instructor
+    }
+
+    void url(String url){
+        this.url = url
+    }
+
+    void description(String description){
+        this.description = description
+    }
+
+    void cover(String cover){
+        this.cover = cover
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/groovy-user-site/blob/33e32ad0/generator/src/main/groovy/model/Distribution.groovy
----------------------------------------------------------------------
diff --git a/generator/src/main/groovy/model/Distribution.groovy b/generator/src/main/groovy/model/Distribution.groovy
new file mode 100644
index 0000000..55ef17c
--- /dev/null
+++ b/generator/src/main/groovy/model/Distribution.groovy
@@ -0,0 +1,19 @@
+package model
+
+import groovy.transform.CompileStatic
+
+@CompileStatic
+class Distribution {
+    String name
+    Closure description
+    List<DownloadPackage> packages = []
+
+    void description(Closure cl) { this.description = cl }
+
+    void version(String name, Closure versionSpec) {
+        DownloadPackage pkg = new DownloadPackage(version:name)
+        def clone = versionSpec.rehydrate(pkg,pkg,pkg)
+        clone()
+        packages.add(pkg)
+    }
+}

http://git-wip-us.apache.org/repos/asf/groovy-user-site/blob/33e32ad0/generator/src/main/groovy/model/DownloadPackage.groovy
----------------------------------------------------------------------
diff --git a/generator/src/main/groovy/model/DownloadPackage.groovy b/generator/src/main/groovy/model/DownloadPackage.groovy
new file mode 100644
index 0000000..152fa8d
--- /dev/null
+++ b/generator/src/main/groovy/model/DownloadPackage.groovy
@@ -0,0 +1,23 @@
+package model
+
+import groovy.transform.CompileStatic
+
+@CompileStatic
+class DownloadPackage {
+    String version
+    String releaseNotes
+    String windowsInstaller
+    boolean stable = false
+
+    void releaseNotes(String notes) {
+        releaseNotes = notes
+    }
+
+    void windowsInstaller(String installer) {
+        windowsInstaller = installer
+    }
+
+    void stable(boolean b) {
+        stable = b
+    }
+}

http://git-wip-us.apache.org/repos/asf/groovy-user-site/blob/33e32ad0/generator/src/main/groovy/model/Ecosystem.groovy
----------------------------------------------------------------------
diff --git a/generator/src/main/groovy/model/Ecosystem.groovy b/generator/src/main/groovy/model/Ecosystem.groovy
new file mode 100644
index 0000000..17a102f
--- /dev/null
+++ b/generator/src/main/groovy/model/Ecosystem.groovy
@@ -0,0 +1,13 @@
+package model
+
+import groovy.transform.CompileStatic
+
+@CompileStatic
+class Ecosystem extends LinkedHashMap<String,EcosystemEntry> {
+    void project(String name, Closure entrySpec) {
+        def entry = new EcosystemEntry(name:name)
+        def clone = entrySpec.rehydrate(entry,entry,entry)
+        clone()
+        put(name, entry)
+    }
+}

http://git-wip-us.apache.org/repos/asf/groovy-user-site/blob/33e32ad0/generator/src/main/groovy/model/EcosystemEntry.groovy
----------------------------------------------------------------------
diff --git a/generator/src/main/groovy/model/EcosystemEntry.groovy b/generator/src/main/groovy/model/EcosystemEntry.groovy
new file mode 100644
index 0000000..55dc9fd
--- /dev/null
+++ b/generator/src/main/groovy/model/EcosystemEntry.groovy
@@ -0,0 +1,18 @@
+package model
+
+import groovy.transform.ToString
+
+import groovy.transform.CompileStatic
+
+@CompileStatic
+@ToString(includeNames=true)
+class EcosystemEntry {
+    String name
+    String url
+    String description
+    String logo
+
+    void url(String url) { this.url = url }
+    void description(String description) { this.description = description }
+    void logo(String logo) { this.logo = logo }
+}

http://git-wip-us.apache.org/repos/asf/groovy-user-site/blob/33e32ad0/generator/src/main/groovy/model/Event.groovy
----------------------------------------------------------------------
diff --git a/generator/src/main/groovy/model/Event.groovy b/generator/src/main/groovy/model/Event.groovy
new file mode 100644
index 0000000..6eaf7d7
--- /dev/null
+++ b/generator/src/main/groovy/model/Event.groovy
@@ -0,0 +1,19 @@
+package model
+
+import groovy.transform.CompileStatic
+
+@CompileStatic
+class Event {
+    String name
+    String location
+    String date
+    String description
+    String url
+    String logo
+
+    void location   (String location)    { this.location    = location }
+    void date       (String date)        { this.date        = date }
+    void description(String description) { this.description = description }
+    void url        (String url)         { this.url         = url }
+    void logo       (String logo)        { this.logo        = logo }
+}

http://git-wip-us.apache.org/repos/asf/groovy-user-site/blob/33e32ad0/generator/src/main/groovy/model/Events.groovy
----------------------------------------------------------------------
diff --git a/generator/src/main/groovy/model/Events.groovy b/generator/src/main/groovy/model/Events.groovy
new file mode 100644
index 0000000..8767dec
--- /dev/null
+++ b/generator/src/main/groovy/model/Events.groovy
@@ -0,0 +1,13 @@
+package model
+
+import groovy.transform.CompileStatic
+
+@CompileStatic
+class Events extends LinkedHashMap<String, Event> {
+    void event(String name, Closure eventClosure) {
+        def entry = new Event(name: name)
+        def clone = eventClosure.rehydrate(entry, entry, entry)
+        clone()
+        put(name, entry)
+    }
+}

http://git-wip-us.apache.org/repos/asf/groovy-user-site/blob/33e32ad0/generator/src/main/groovy/model/Issue.groovy
----------------------------------------------------------------------
diff --git a/generator/src/main/groovy/model/Issue.groovy b/generator/src/main/groovy/model/Issue.groovy
new file mode 100644
index 0000000..db4b1ee
--- /dev/null
+++ b/generator/src/main/groovy/model/Issue.groovy
@@ -0,0 +1,14 @@
+package model
+
+import groovy.transform.CompileStatic
+import groovy.transform.EqualsAndHashCode
+import groovy.transform.ToString
+
+@CompileStatic
+@ToString
+@EqualsAndHashCode
+class Issue {
+    String id
+    String type
+    String description
+}

http://git-wip-us.apache.org/repos/asf/groovy-user-site/blob/33e32ad0/generator/src/main/groovy/model/Library.groovy
----------------------------------------------------------------------
diff --git a/generator/src/main/groovy/model/Library.groovy b/generator/src/main/groovy/model/Library.groovy
new file mode 100644
index 0000000..879cd98
--- /dev/null
+++ b/generator/src/main/groovy/model/Library.groovy
@@ -0,0 +1,13 @@
+package model
+
+import groovy.transform.CompileStatic
+
+@CompileStatic
+class Library extends LinkedHashMap<String, Book> {
+    void book(String title, Closure bookClosure) {
+        def book = new Book(title: title)
+        def clone = bookClosure.rehydrate(book, book, book)
+        clone()
+        put(title, book)
+    }
+}

http://git-wip-us.apache.org/repos/asf/groovy-user-site/blob/33e32ad0/generator/src/main/groovy/model/Menu.groovy
----------------------------------------------------------------------
diff --git a/generator/src/main/groovy/model/Menu.groovy b/generator/src/main/groovy/model/Menu.groovy
new file mode 100644
index 0000000..9e495ed
--- /dev/null
+++ b/generator/src/main/groovy/model/Menu.groovy
@@ -0,0 +1,21 @@
+package model
+
+import groovy.transform.CompileStatic
+
+@CompileStatic
+class Menu extends LinkedHashMap<String, List<MenuItem>> {
+
+    void group(String name, Closure groupSpec) {
+        Group g = new Group()
+        def clone = groupSpec.rehydrate(g,g,g)
+        clone()
+        put(name, g.items)
+    }
+
+    private static class Group {
+        List<MenuItem> items = []
+        void item(String name, String link, String style=null) {
+            items << new MenuItem(name:name, link: link, style:style)
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/groovy-user-site/blob/33e32ad0/generator/src/main/groovy/model/MenuItem.groovy
----------------------------------------------------------------------
diff --git a/generator/src/main/groovy/model/MenuItem.groovy b/generator/src/main/groovy/model/MenuItem.groovy
new file mode 100644
index 0000000..6e58074
--- /dev/null
+++ b/generator/src/main/groovy/model/MenuItem.groovy
@@ -0,0 +1,13 @@
+package model
+
+import groovy.transform.ToString
+
+import groovy.transform.CompileStatic
+
+@CompileStatic
+@ToString(includeNames=true)
+class MenuItem {
+    String name
+    String link
+    String style
+}

http://git-wip-us.apache.org/repos/asf/groovy-user-site/blob/33e32ad0/generator/src/main/groovy/model/Page.groovy
----------------------------------------------------------------------
diff --git a/generator/src/main/groovy/model/Page.groovy b/generator/src/main/groovy/model/Page.groovy
new file mode 100644
index 0000000..5b29679
--- /dev/null
+++ b/generator/src/main/groovy/model/Page.groovy
@@ -0,0 +1,10 @@
+package model
+
+import groovy.transform.CompileStatic
+
+@CompileStatic
+class Page {
+    String source
+    String target
+    Map model = [:]
+}

http://git-wip-us.apache.org/repos/asf/groovy-user-site/blob/33e32ad0/generator/src/main/groovy/model/Section.groovy
----------------------------------------------------------------------
diff --git a/generator/src/main/groovy/model/Section.groovy b/generator/src/main/groovy/model/Section.groovy
new file mode 100644
index 0000000..b0b14fd
--- /dev/null
+++ b/generator/src/main/groovy/model/Section.groovy
@@ -0,0 +1,21 @@
+package model
+
+import groovy.transform.ToString
+
+@ToString(includeNames=true)
+import groovy.transform.CompileStatic
+
+@CompileStatic
+class Section {
+    String name
+    String icon
+    List<SectionItem> items = []
+
+    void item(String name, String targetFile, String sourceFile, boolean generate = true) {
+        items.add(new SectionItem(name: name, sourceFilename: sourceFile, targetFilename: targetFile, generate:generate))
+    }
+
+    String getAnchor() {
+        name.replaceAll('[^a-zA-Z0-9]','').toLowerCase()
+    }
+}

http://git-wip-us.apache.org/repos/asf/groovy-user-site/blob/33e32ad0/generator/src/main/groovy/model/SectionItem.groovy
----------------------------------------------------------------------
diff --git a/generator/src/main/groovy/model/SectionItem.groovy b/generator/src/main/groovy/model/SectionItem.groovy
new file mode 100644
index 0000000..7379b23
--- /dev/null
+++ b/generator/src/main/groovy/model/SectionItem.groovy
@@ -0,0 +1,14 @@
+package model
+
+import groovy.transform.ToString
+
+import groovy.transform.CompileStatic
+
+@CompileStatic
+@ToString(includeNames=true)
+class SectionItem {
+    String name
+    String targetFilename
+    String sourceFilename
+    boolean generate = true
+}

http://git-wip-us.apache.org/repos/asf/groovy-user-site/blob/33e32ad0/generator/src/main/groovy/model/SiteMap.groovy
----------------------------------------------------------------------
diff --git a/generator/src/main/groovy/model/SiteMap.groovy b/generator/src/main/groovy/model/SiteMap.groovy
new file mode 100644
index 0000000..8742dbc
--- /dev/null
+++ b/generator/src/main/groovy/model/SiteMap.groovy
@@ -0,0 +1,134 @@
+package model
+
+import groovy.transform.ToString
+import org.codehaus.groovy.control.CompilerConfiguration
+
+import groovy.transform.CompileStatic
+import org.codehaus.groovy.control.customizers.ImportCustomizer
+
+@CompileStatic
+@ToString(includeNames=true)
+class SiteMap {
+    final List<Section> documentationSections = []
+    final List<Distribution> distributions = []
+    final Menu menu = new Menu()
+    final Ecosystem ecosystem = new Ecosystem()
+    final Events allEvents = new Events()
+    final Library library = new Library()
+    final List<String> allDocVersions = []
+    final List<Page> pages = []
+    final List<UserGroup> usergroups = []
+    final List<Video> videos = []
+    final List<Course> courses = []
+
+    private SiteMap() {}
+
+    public static SiteMap from(File source) {
+        CompilerConfiguration config = new CompilerConfiguration()
+        def customizer = new ImportCustomizer()
+        config.addCompilationCustomizers(customizer)
+        customizer.addStaticImport('generator.DocUtils','DOCS_BASEURL')
+        config.scriptBaseClass = 'groovy.util.DelegatingScript'
+        GroovyShell shell = new GroovyShell(config)
+        def script = shell.parse(source)
+
+        def result = new SiteMap()
+        ((DelegatingScript)script).setDelegate(result)
+        script.run()
+
+        result
+    }
+
+    private void documentation(Closure docSpec) {
+        def clone = docSpec.rehydrate(this, this, this)
+        clone()
+    }
+
+    private void section(String name, String icon, Closure sectionSpec) {
+        Section section = new Section(name:name, icon:icon)
+        def spec = sectionSpec.rehydrate(section,section,section)
+        spec()
+        documentationSections.add(section)
+    }
+
+    private void groovyDocumentationVersions(List<String> allDocVersions) {
+        this.allDocVersions.addAll(allDocVersions)
+    }
+
+    private void downloads(Closure dlSpec) {
+        def clone = dlSpec.rehydrate(this, this, this)
+        clone()
+    }
+
+    private void pages(Closure pagesSpec) {
+        def clone = pagesSpec.rehydrate(this, this, this)
+        clone()
+    }
+
+    private void usergroups(Closure groupsSpec) {
+        def clone = groupsSpec.rehydrate(this ,this ,this)
+        clone()
+    }
+
+    private void videos(Closure videosSpec) {
+        def clone = videosSpec.rehydrate(this, this, this)
+        clone()
+    }
+
+    private void courses(Closure coursesSpec) {
+        def clone = coursesSpec.rehydrate(this, this, this)
+        clone()
+    }
+
+    private void distribution(String name, Closure distSpec) {
+        Distribution dist = new Distribution(name: name)
+        def clone = distSpec.rehydrate(dist, dist, dist)
+        clone()
+        distributions.add(dist)
+    }
+
+    private void menu(Closure menuSpec) {
+        def clone = menuSpec.rehydrate(menu, menu, menu)
+        clone()
+    }
+
+    private void ecosystem(Closure ecoSpec) {
+        def clone = ecoSpec.rehydrate(ecosystem, ecosystem, ecosystem)
+        clone()
+    }
+
+    private void allEvents(Closure eventsSpec) {
+        def clone = eventsSpec.rehydrate(allEvents, allEvents, allEvents)
+        clone()
+    }
+
+    private void books(Closure booksSpec) {
+        def clone = booksSpec.rehydrate(library, library, library)
+        clone()
+    }
+
+    private void page(String source, String target, Map model = [:]) {
+        pages.add(new Page(source:source, target: target, model: model))
+    }
+
+    private void userGroup(String name, Closure groupSpec) {
+        def group = new UserGroup(name: name)
+        def clone = groupSpec.rehydrate(group,group,group)
+        clone()
+        usergroups.add(group)
+    }
+
+    private void video(String title, Closure videoSpec) {
+        def video = new Video(title: title)
+        def clone = videoSpec.rehydrate(video, video, video)
+        clone()
+        videos.add(video)
+    }
+
+    private void course(String title, Closure courseSpec) {
+        def course = new Course(title: title)
+        def clone = courseSpec.rehydrate(course,course,course)
+        clone()
+        courses.add(course)
+    }
+}

http://git-wip-us.apache.org/repos/asf/groovy-user-site/blob/33e32ad0/generator/src/main/groovy/model/UserGroup.groovy
----------------------------------------------------------------------
diff --git a/generator/src/main/groovy/model/UserGroup.groovy b/generator/src/main/groovy/model/UserGroup.groovy
new file mode 100644
index 0000000..84007e3
--- /dev/null
+++ b/generator/src/main/groovy/model/UserGroup.groovy
@@ -0,0 +1,28 @@
+package model
+
+import groovy.transform.CompileStatic
+import groovy.transform.ToString
+
+@CompileStatic
+@ToString(includeNames=true)
+class UserGroup {
+    String location
+    String name
+    String url
+
+    void location(String location) {
+        this.location = location
+    }
+
+    void name(String name) {
+        this.name = name
+    }
+
+    void url(String url) {
+        this.url = url
+    }
+
+    String[] getLocationParts() {
+        location.split('/')
+    }
+}

http://git-wip-us.apache.org/repos/asf/groovy-user-site/blob/33e32ad0/generator/src/main/groovy/model/Video.groovy
----------------------------------------------------------------------
diff --git a/generator/src/main/groovy/model/Video.groovy b/generator/src/main/groovy/model/Video.groovy
new file mode 100644
index 0000000..a5a4667
--- /dev/null
+++ b/generator/src/main/groovy/model/Video.groovy
@@ -0,0 +1,38 @@
+package model
+
+import groovy.transform.CompileStatic
+
+@CompileStatic
+class Video {
+    String title
+    String speaker
+    String summary
+    String pictureUrl
+    String videoUrl = ""
+    String slidesUrl = ""
+    String codeUrl = ""
+
+    void speaker(String speaker) {
+        this.speaker = speaker
+    }
+
+    void summary(String summary) {
+        this.summary = summary
+    }
+
+    void pictureUrl(String pictureUrl) {
+        this.pictureUrl = pictureUrl
+    }
+
+    void videoUrl(String videoUrl) {
+        this.videoUrl = videoUrl
+    }
+
+    void slidesUrl(String slidesUrl) {
+        this.slidesUrl = slidesUrl
+    }
+
+    void codeUrl(String codeUrl) {
+        this.codeUrl = codeUrl
+    }
+}

http://git-wip-us.apache.org/repos/asf/groovy-user-site/blob/33e32ad0/gradle.properties
----------------------------------------------------------------------
diff --git a/gradle.properties b/gradle.properties
new file mode 100644
index 0000000..c9d5166
--- /dev/null
+++ b/gradle.properties
@@ -0,0 +1 @@
+systemProp.docs_baseurl=http://docs.groovy-lang.org/latest

http://git-wip-us.apache.org/repos/asf/groovy-user-site/blob/33e32ad0/gradle/wrapper/gradle-wrapper.jar
----------------------------------------------------------------------
diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..1d7437d
Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ

http://git-wip-us.apache.org/repos/asf/groovy-user-site/blob/33e32ad0/gradle/wrapper/gradle-wrapper.properties
----------------------------------------------------------------------
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..da60a3a
--- /dev/null
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Mon Apr 03 10:41:25 AEST 2017
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-3.4.1-bin.zip

http://git-wip-us.apache.org/repos/asf/groovy-user-site/blob/33e32ad0/gradlew
----------------------------------------------------------------------
diff --git a/gradlew b/gradlew
new file mode 100644
index 0000000..9aa616c
--- /dev/null
+++ b/gradlew
@@ -0,0 +1,169 @@
+#!/usr/bin/env bash
+
+##############################################################################
+##
+##  Gradle start up script for UN*X
+##
+##############################################################################
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+    ls=`ls -ld "$PRG"`
+    link=`expr "$ls" : '.*-> \(.*\)$'`
+    if expr "$link" : '/.*' > /dev/null; then
+        PRG="$link"
+    else
+        PRG=`dirname "$PRG"`"/$link"
+    fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn ( ) {
+    echo "$*"
+}
+
+die ( ) {
+    echo
+    echo "$*"
+    echo
+    exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "`uname`" in
+  CYGWIN* )
+    cygwin=true
+    ;;
+  Darwin* )
+    darwin=true
+    ;;
+  MINGW* )
+    msys=true
+    ;;
+  NONSTOP* )
+    nonstop=true
+    ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+    if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+        # IBM's JDK on AIX uses strange locations for the executables
+        JAVACMD="$JAVA_HOME/jre/sh/java"
+    else
+        JAVACMD="$JAVA_HOME/bin/java"
+    fi
+    if [ ! -x "$JAVACMD" ] ; then
+        die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+    fi
+else
+    JAVACMD="java"
+    which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
+    MAX_FD_LIMIT=`ulimit -H -n`
+    if [ $? -eq 0 ] ; then
+        if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+            MAX_FD="$MAX_FD_LIMIT"
+        fi
+        ulimit -n $MAX_FD
+        if [ $? -ne 0 ] ; then
+            warn "Could not set maximum file descriptor limit: $MAX_FD"
+        fi
+    else
+        warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+    fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+    GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+    APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+    CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+    JAVACMD=`cygpath --unix "$JAVACMD"`
+
+    # We build the pattern for arguments to be converted via cygpath
+    ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+    SEP=""
+    for dir in $ROOTDIRSRAW ; do
+        ROOTDIRS="$ROOTDIRS$SEP$dir"
+        SEP="|"
+    done
+    OURCYGPATTERN="(^($ROOTDIRS))"
+    # Add a user-defined pattern to the cygpath arguments
+    if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+        OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+    fi
+    # Now convert the arguments - kludge to limit ourselves to /bin/sh
+    i=0
+    for arg in "$@" ; do
+        CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+        CHECK2=`echo "$arg"|egrep -c "^-"`                                 ### Determine if an option
+
+        if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then                    ### Added a condition
+            eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+        else
+            eval `echo args$i`="\"$arg\""
+        fi
+        i=$((i+1))
+    done
+    case $i in
+        (0) set -- ;;
+        (1) set -- "$args0" ;;
+        (2) set -- "$args0" "$args1" ;;
+        (3) set -- "$args0" "$args1" "$args2" ;;
+        (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+        (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+        (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+        (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+        (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+        (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+    esac
+fi
+
+# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
+function splitJvmOpts() {
+    JVM_OPTS=("$@")
+}
+eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
+JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
+
+# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
+if [[ "$(uname)" == "Darwin" ]] && [[ "$HOME" == "$PWD" ]]; then
+  cd "$(dirname "$0")"
+fi
+
+exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"

http://git-wip-us.apache.org/repos/asf/groovy-user-site/blob/33e32ad0/gradlew.bat
----------------------------------------------------------------------
diff --git a/gradlew.bat b/gradlew.bat
new file mode 100644
index 0000000..f955316
--- /dev/null
+++ b/gradlew.bat
@@ -0,0 +1,84 @@
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem  Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windows variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if  not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega

http://git-wip-us.apache.org/repos/asf/groovy-user-site/blob/33e32ad0/settings.gradle
----------------------------------------------------------------------
diff --git a/settings.gradle b/settings.gradle
new file mode 100644
index 0000000..2fd2033
--- /dev/null
+++ b/settings.gradle
@@ -0,0 +1,4 @@
+rootProject.name = 'groovy-user-site'
+
+include 'generator','site'
+

http://git-wip-us.apache.org/repos/asf/groovy-user-site/blob/33e32ad0/site/build.gradle
----------------------------------------------------------------------
diff --git a/site/build.gradle b/site/build.gradle
new file mode 100644
index 0000000..d63bb9a
--- /dev/null
+++ b/site/build.gradle
@@ -0,0 +1,230 @@
+import org.apache.http.client.config.RequestConfig
+import org.apache.http.client.methods.CloseableHttpResponse
+import org.apache.http.client.methods.HttpGet
+import org.apache.http.impl.client.CloseableHttpClient
+import org.apache.http.impl.client.HttpClients
+import groovy.text.markup.MarkupTemplateEngine
+import groovy.text.markup.TemplateConfiguration
+import org.gradle.internal.logging.ConsoleRenderer
+import com.yahoo.platform.yui.compressor.CssCompressor
+import com.yahoo.platform.yui.compressor.JavaScriptCompressor
+import org.apache.tools.ant.filters.BaseFilterReader
+
+buildscript {
+    repositories {
+        jcenter()
+    }
+    dependencies {
+        classpath 'org.apache.httpcomponents:httpclient:4.5.1'
+    }
+}
+
+ext.watchmode = project.hasProperty('watchmode')?project.getProperty('watchmode'):'false'
+
+apply plugin: 'base'
+
+task copyAssets(type:Copy) {
+    from file('src/site/assets')
+    into file("$buildDir/site")
+    filesMatching('**/*.css') { f->
+        if (!f.name.contains('.min.')) {
+            filter(CssFilter)
+        }
+    }
+    filesMatching('**/*.js') { f->
+        if (!f.name.contains('.min.')) {
+            filter(JsFilter)
+        }
+    }
+}
+
+task generateSite(type:JavaExec) {
+
+    description = 'Generates the Groovy Website'
+    dependsOn copyAssets
+
+    ext.sources = file('src/site')
+    ext.outputDir = file("$buildDir/site")
+
+    inputs.files fileTree(sources)
+    outputs.files fileTree(outputDir)
+
+    classpath = project(':generator').sourceSets.main.runtimeClasspath
+    main = 'generator.SiteGenerator'
+    args = [sources, outputDir, project.watchmode]
+    systemProperties.docs_baseurl = System.getProperty('docs_baseurl')
+}
+
+task checkDeadLinks(dependsOn: generateSite) {
+    description = "Checks for dead links in the generated Groovy website"
+
+    ext.outputDir = file("$buildDir/reports")
+    ext.reportFile = file("$outputDir/deadlinks.html")
+
+    inputs.files fileTree(generateSite.outputDir)
+    outputs.file reportFile
+
+    // a collection of links which have either been validated
+    // or are dummy links
+    def excludeFromChecks = [
+            'http://issues.apache.org/jira',
+            'https://issues.apache.org/jira',
+            'target.html',
+            'foo.html',
+            'http://www.acme.com/cars',
+            'http://localhost:8080/groovy/hello.groovy'
+    ]
+
+    doLast {
+        def deadLinks = [:]
+
+        def currentPath
+        def isDead = [:].withDefault { String link ->
+            if (excludeFromChecks.any { link.startsWith(it) }) {
+                // skip checking those links because they dramatically increase build time
+                // while being most likely ok because generated through changelog parsing
+                return false
+            }
+
+            try {
+                URL url
+                try {
+                    url = URI.create(link).toURL()
+                } catch (e) {
+                    if (e.message.contains('URI is not absolute')) {
+                        url = URI.create("file://${file("${generateSite.outputDir}/$currentPath/$link").absoluteFile}").toURL()
+                    }
+                }
+                logger.debug("Checking URL: $url")
+                def cx = url.openConnection()
+                if (cx instanceof HttpURLConnection) {
+                    CloseableHttpClient httpclient = HttpClients.createDefault()
+                    RequestConfig requestConfig = RequestConfig.custom()
+                            .setSocketTimeout(5_000)
+                            .setConnectTimeout(5_000)
+                            .setConnectionRequestTimeout(5_000)
+                            .build()
+                    HttpGet httpget = new HttpGet(link)
+                    httpget.config = requestConfig
+                    CloseableHttpResponse response
+                    try {
+                        response = httpclient.execute(httpget)
+                        if (response.statusLine.statusCode == 404) {
+                            return true
+                        }
+                    } finally {
+                        response.close()
+                    }
+                }
+            } catch (e) {
+                return true
+            }
+            return false
+        }
+
+        def checkLink = { List dead, int line, String link ->
+            if (isDead[link]) {
+                dead << [line:line, link:link]
+            }
+        }
+
+        def checkPage = { File f ->
+            currentPath = GFileUtils.relativePath(generateSite.outputDir, f.parentFile)
+            f.eachLine('utf-8') { String line, int nb ->
+                def dead = []
+                [/\shref=['"](.+?)['"]/, /src=['"](.+?)['"]/].each { regex ->
+                    def matcher = line =~ regex
+                    if (matcher) {
+                        matcher.each {
+                            def path = it[1]
+                            checkLink(dead, nb, path)
+                        }
+                    }
+                }
+                if (dead) {
+                    deadLinks[f] = dead
+                }
+            }
+        }
+
+        file(generateSite.outputDir).eachFileRecurse {
+            if (it.name.endsWith('.html')) {
+                checkPage(it)
+            }
+        }
+
+        outputDir.mkdirs()
+        def tplConf = new TemplateConfiguration()
+        tplConf.with {
+            autoIndent = true
+            autoNewLine = true
+        }
+        def tplEngine = new MarkupTemplateEngine(this.class.classLoader, file('gradle/templates'), tplConf)
+
+        def report = tplEngine.createTemplateByPath("deadlinks.groovy").make(deadLinks: deadLinks).toString()
+
+        reportFile.write(report, 'utf-8')
+        def reportURL = new ConsoleRenderer().asClickableFileUrl(reportFile)
+        logger.lifecycle "Dead links report written at $reportURL"
+    }
+}
+
+task webzip(type:Zip, dependsOn: checkDeadLinks) {
+    description = "Creates a zip with the generated website and the deadlink report"
+    destinationDir = file("$buildDir/distributions")
+
+    baseName = 'groovy'
+    appendix = 'website'
+    from(generateSite.outputDir) {
+        into 'site'
+    }
+    from (checkDeadLinks.outputDir) {
+        into 'reports'
+    }
+}
+
+// Resource filtering classes
+
+class JsFilter extends BaseFilterReader {
+    Writer writer
+    Thread worker
+
+    JsFilter(Reader reader) {
+        super(new PipedReader())
+        writer = new PipedWriter(this.@in)
+        def compressor = new JavaScriptCompressor(reader, null)
+        reader.close()
+        worker = Thread.start {
+            compressor.compress(writer, -1, true, false, false, false)
+            writer.close()
+        }
+    }
+
+    void close() {
+        worker.join()
+        super.close()
+    }
+
+}
+
+class CssFilter extends BaseFilterReader {
+    Writer writer
+    Thread worker
+
+    CssFilter(Reader reader) {
+        super(new PipedReader())
+        writer = new PipedWriter(this.@in)
+        def compressor = new CssCompressor(reader)
+        reader.close()
+        worker = Thread.start {
+            compressor.compress(writer, -1)
+            writer.close()
+        }
+    }
+
+    void close() {
+        worker.join()
+        super.close()
+    }
+
+}