Skip to content

Commit

Permalink
Merge branch 'release/v1.2.0-RC1'
Browse files Browse the repository at this point in the history
  • Loading branch information
metasim committed Nov 5, 2016
2 parents 0ff2f03 + 12639ee commit 2fcdb5c
Show file tree
Hide file tree
Showing 20 changed files with 346 additions and 22 deletions.
10 changes: 10 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,13 @@ before_install:
install:
- gem install jekyll:2.5.3 nanoc:3.8.0 asciidoctor --no-rdoc --no-ri
- npm install -g gitbook-cli
- |
mkdir $TRAVIS_BUILD_DIR/download && mkdir $HOME/bin && \
wget https://github.com/spf13/hugo/releases/download/v0.16/hugo_0.16_linux-64bit.tgz -O $TRAVIS_BUILD_DIR/download/hugo.tgz && \
tar xvf $TRAVIS_BUILD_DIR/download/hugo.tgz && \
mv $TRAVIS_BUILD_DIR/hugo $HOME/bin/hugo && \
chmod +x $HOME/bin/hugo && \
rm -rf $TRAVIS_BUILD_DIR/download/
before_script:
- export PATH=$PATH:$HOME/bin/
46 changes: 34 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

[ ![Download](https://api.bintray.com/packages/sbt/sbt-plugin-releases/sbt-site/images/download.svg) ](https://bintray.com/sbt/sbt-plugin-releases/sbt-site-imported/_latestVersion)

This sbt plugin generates project websites from static content, [Jekyll], [Sphinx], [Pamflet], [Nanoc], [GitBook], [Paradox] and/or [Asciidoctor], and can optionally include generated ScalaDoc. It is designed to work hand-in-hand with publishing plugins like [sbt-ghpages].
This sbt plugin generates project websites from static content, [Jekyll], [Sphinx], [Pamflet], [Nanoc], [GitBook], [Paradox], [Hugo] and/or [Asciidoctor], and can optionally include generated ScalaDoc. It is designed to work hand-in-hand with publishing plugins like [sbt-ghpages].

**Table of Contents**

Expand All @@ -20,6 +20,7 @@ This sbt plugin generates project websites from static content, [Jekyll], [Sphin
- [Asciidoctor Site Generation](#asciidoctor-site-generation)
- [GitBook Site Generation](#gitbook-site-generation)
- [Paradox Site Generation](#paradox-site-generation)
- [Hugo Site Generation](#hugo-site-generation)
- [ScalaDoc APIs](#scaladoc-apis)
- [Previewing the Site](#previewing-the-site)
- [Packaging and Publishing](#packaging-and-publishing)
Expand All @@ -30,8 +31,8 @@ This sbt plugin generates project websites from static content, [Jekyll], [Sphin

`sbt-site` is deployed as an `AutoPlugin`. To enable, simply add the following to your `project/plugins.sbt` file:

```
addSbtPlugin("com.typesafe.sbt" % "sbt-site" % "1.1.0")
```sbt
addSbtPlugin("com.typesafe.sbt" % "sbt-site" % "1.2.0-RC1")
```

<big>To upgrade from a previous version (e.g. 0.8.x), please see the **[migration guide]**.</big>
Expand Down Expand Up @@ -223,16 +224,36 @@ gitbookInstallDir in GitBook := Some(baseDirectory.value / "node_modules" / "git

The `sbt-site` plugin has direct support for building [Paradox] projects. To enable Paradox site generation, simply enable the associated plugin in your `build.sbt` file:

```
```sbt
enablePlugins(ParadoxSitePlugin)
```

This assumes you have a Paradox project under the `src/paradox` directory. To change this, set the `sourceDirectory` key in the `Paradox` scope:

```sbt
sourceDirectory in Paradox := sourceDirectory.value / "doc"
```

### Hugo Site Generation

The `sbt-site` plugin has support for building [Hugo] projects. To enable Hugo site generation, simply enable the associated plugin in your `build.sbt` file:

```sbt
enablePlugins(HugoPlugin)
```

The `hugo` binary must be installed on your `$PATH` in order to be accessible to `sbt-site`. In addition, this plugin assumes you have a Hugo project under the `src/hugo` directory. To change this, set the `sourceDirectory` key in the `Hugo` scope:

```sbt
sourceDirectory in Paradox := sourceDirectory.value / "doc"
```

You may also change the [base-url](https://gohugo.io/overview/configuration/) that gets passed to the `hugo` command by adjusting the following setting:

```sbt
baseURL in Hugo := "https://yourdomain.com"
```

## ScalaDoc APIs
To include ScalaDoc with your site, add the following line to your `build.sbt`:

Expand All @@ -252,7 +273,7 @@ siteSubdirName in SiteScaladoc := "api/wip"
See the [sbt-ghpages] plugin for information about publishing to [GitHub Pages]. We expect other publishing mechanisms to be supported in the future.

## Previewing the Site
To preview your generated site, run `previewSite`, which launches a web server on port 4000 and attempts to connect your browser to [http://localhost:4000/](http://localhost:4000/). To change the server port, use the key `previewFixedPort`:
To preview your generated site, you can run `previewSite` which launches a static web server, or `previewAuto` which launches a dynamic server updating its content at each modification in your source files. Both launch the server on port 4000 and attempts to connect your browser to [http://localhost:4000/](http://localhost:4000/). To change the server port, use the key `previewFixedPort`:

```sbt
previewFixedPort := Some(9999)
Expand Down Expand Up @@ -312,11 +333,12 @@ Each of the other generators follow a similar pattern (e.g. `JekyllPlugin.jekyll
[0.8.2]: https://github.com/sbt/sbt-site/tree/v0.8.2
[migration guide]: notes/migrate-0.8.2-to-1.0.md
[sbt-ghpages]: http://github.com/sbt/sbt-ghpages
[jekyll]: http://jekyllrb.com
[pamflet]: http://pamflet.databinder.net
[nanoc]: http://nanoc.ws/
[asciidoctor]: http://asciidoctor.org
[gitbook]: https://toolchain.gitbook.com/
[sphinx]: http://sphinx-doc.org
[Jekyll]: http://jekyllrb.com
[Pamflet]: http://pamflet.databinder.net
[Nanoc]: http://nanoc.ws/
[Asciidoctor]: http://asciidoctor.org
[Sphinx]: http://sphinx-doc.org
[GitHub Pages]: https://pages.github.com
[paradox]: https://github.com/lightbend/paradox
[GitBook]: https://www.gitbook.com
[Paradox]: https://github.com/lightbend/paradox
[Hugo]: http://gohugo.io/
7 changes: 5 additions & 2 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ name := "sbt-site"

organization := "com.typesafe.sbt"

version := "1.1.0"
version := "1.2.0-RC1"

licenses += ("BSD 3-Clause", url("http://opensource.org/licenses/BSD-3-Clause"))

Expand All @@ -18,7 +18,10 @@ scalacOptions ++= Seq("-deprecation", "-unchecked")
resolvers += Resolver.sonatypeRepo("releases")

libraryDependencies ++= Seq(
"net.databinder" %% "unfiltered-jetty" % "0.6.8",
"net.databinder" %% "unfiltered-directives" % "0.8.4",
"net.databinder" %% "unfiltered-filter" % "0.8.4",
"net.databinder" %% "unfiltered-jetty" % "0.8.4",
"net.databinder" %% "unfiltered-specs2" % "0.8.4" % "test",
"net.databinder" %% "pamflet-library" % "0.6.0",
"org.yaml" % "snakeyaml" % "1.13",
"com.typesafe" % "config" % "1.2.1", // Last version to support Java 1.6
Expand Down
9 changes: 9 additions & 0 deletions notes/1.2.0.markdown
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Release 1.2.0-RC1

_Version 1.2.0 is initially coming out in "RC" form due to an upgrade in the `unfiltered` dependency. Please see below._

## Enhancements

1. Support for [Hugo](https://gohugo.io) site generation. Thanks @timperrett!
2. Added `excludeFilter` support to GitBook generator. Thanks @jonas!
3. Added `previewAuto` command, which launches a dynamic HTML server updating content with each modification in site source. Thanks @gsechaud! Note: This change includes an upgrade of the `unfiltered` depenency. Please let us know of any incompatibilities with other plugins that might be caused by this change.
2 changes: 1 addition & 1 deletion notes/about.markdown
Original file line number Diff line number Diff line change
@@ -1 +1 @@
This sbt plugin generates project websites from static content, Jekyll, Sphinx, Pamflet, Nanoc, GitBook, and/or Asciidoctor, and can optionally include generated ScalaDoc.
This sbt plugin generates project websites from static content, Jekyll, Sphinx, Pamflet, Nanoc, GitBook, Paradox, Hugo and/or Asciidoctor, and can optionally include generated ScalaDoc.
3 changes: 3 additions & 0 deletions notes/release-process.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
# Release Process Outline

This project uses GitFlow. PRs should be merged onto `develop`. New releases prepared from `release/vX.Y.Z` branch. `master` updated from `release/...` branches.

1. Run `git clean -fdx`. This makes sure there are no unexpected dependencies or artifacts that could affect the build.
2. Create a branch to do the release work on. Something like `release/<X>.<Y>.<Z>` is good.
3. Write release notes in Markdown with filename `notes/<X>.<Y>.markdown`. Go through the commit logs and collect the major new features, bug fixes, deprecations, and anything else relevant to users. Making note of breaking changes is particularly important.
Expand Down
2 changes: 1 addition & 1 deletion project/build.properties
Original file line number Diff line number Diff line change
@@ -1 +1 @@
sbt.version=0.13.9
sbt.version=0.13.12
27 changes: 21 additions & 6 deletions src/main/scala/com/typesafe/sbt/site/SitePreviewPlugin.scala
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.typesafe.sbt.site

import sbt._
import Keys._
import unfiltered.util._

object SitePreviewPlugin extends AutoPlugin {
Expand All @@ -9,19 +10,23 @@ object SitePreviewPlugin extends AutoPlugin {

object autoImport {
val previewSite = TaskKey[Unit]("previewSite", "Launches a jetty server that serves your generated site from the target directory")
val previewAuto = TaskKey[Unit]("previewAuto", "Launches an automatic jetty server that serves your generated site from the target directory")
val previewFixedPort = SettingKey[Option[Int]]("previewFixedPort") in previewSite
val previewLaunchBrowser = SettingKey[Boolean]("previewLaunchBrowser") in previewSite
}
import SitePlugin.autoImport._
import autoImport._


//@TODO Add configuration to make server just local
override val projectSettings: Seq[Setting[_]] = Seq(
previewSite <<= (makeSite, previewFixedPort, previewLaunchBrowser) map { (file, portOption, browser) =>
previewSite := {
val file = makeSite.value
val portOption = previewFixedPort.value
val browser = previewLaunchBrowser.value

val port = portOption getOrElse Port.any
val server = createServer(file, port) start()
println("SitePreviewPlugin server started on port %d. Press any key to exit." format port)
val sLog = streams.value.log
sLog.info("SitePreviewPlugin server started on port %d. Press any key to exit." format port)
// TODO: use something from sbt-web?
@annotation.tailrec def waitForKey() {
try { Thread sleep 500 } catch { case _: InterruptedException => () }
Expand All @@ -34,11 +39,21 @@ object SitePreviewPlugin extends AutoPlugin {
server stop()
server destroy()
},
previewAuto := {
val port = previewFixedPort.value getOrElse Port.any
val browser = previewLaunchBrowser.value

Preview(port, (target in previewAuto).value, makeSite, watchSources, state.value) run { server =>
if(browser)
Browser open(server.portBindings.head.url)
}
},
previewFixedPort := Some(4000),
previewLaunchBrowser := true
previewLaunchBrowser := true,
target in previewAuto := siteDirectory.value
)

def createServer(siteTarget: File, port: Int) =
unfiltered.jetty.Http(port) resources new URL(siteTarget.toURI.toURL, ".")
unfiltered.jetty.Server.local(port) resources new URL(siteTarget.toURI.toURL, ".")

}
73 changes: 73 additions & 0 deletions src/main/scala/com/typesafe/sbt/site/hugo/HugoPlugin.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package com.typesafe.sbt.site.hugo

import com.typesafe.sbt.site.SitePlugin.autoImport.siteSubdirName
import com.typesafe.sbt.site.SitePreviewPlugin.autoImport.previewFixedPort
import com.typesafe.sbt.site.SitePlugin
import com.typesafe.sbt.site.util.SiteHelpers
import sbt._, Keys._
import java.net.URI

object HugoPlugin extends AutoPlugin {
override def requires = SitePlugin
override def trigger = noTrigger

object autoImport {
val Hugo = config("hugo")
val minimumHugoVersion = settingKey[String]("minimum-hugo-version")
val baseURL = settingKey[URI]("base-url")
val checkHugoVersion = taskKey[Unit]("check-hugo-version")
}

import autoImport._

override def projectSettings = hugoSettings(Hugo)

/** Creates the settings necessary for running hugo in the given configuration. */
def hugoSettings(config: Configuration): Seq[Setting[_]] =
inConfig(config)(
Seq(
includeFilter := ("*.html" | "*.png" | "*.js" | "*.css" | "*.gif" | "CNAME"),
minimumHugoVersion := "0.15",
baseURL := new URI(s"http://127.0.0.1:${previewFixedPort.value.getOrElse(1313)}"),
checkHugoVersion := unsafeCheckHugoVersion(minimumHugoVersion.value, streams.value).get,
mappings := {
val _ = checkHugoVersion.value // sadly relying on effects here, as is the idiom in sbt-site
generate(sourceDirectory.value, target.value, includeFilter.value, baseURL.value, streams.value)
},
siteSubdirName := ""
)
) ++
SiteHelpers.directorySettings(config) ++
SiteHelpers.watchSettings(config) ++
SiteHelpers.addMappingsToSiteDir(mappings in config, siteSubdirName in config)

/** Run hugo via fork. */
private[sbt] def generate(
src: File,
target: File,
inc: FileFilter,
baseURL: URI,
s: TaskStreams): Seq[(File, String)] = {
sbt.Process(Seq("hugo", "-d", target.getAbsolutePath, "--baseURL", baseURL.toString), Some(src)) ! s.log match {
case 0 => ()
case n => sys.error("Could not run hugo binary, error: " + n)
}
for {
(file, name) <- target ** inc --- target pair relativeTo(target)
} yield file -> name
}

import scala.util.{Try, Success, Failure}

def unsafeCheckHugoVersion(minimumVersion: String, s: TaskStreams): Try[Unit] = {
s.log.debug("checking for the installed version of hugo...")
for {
installed <- Try(Seq("hugo", "-", "version").!!)
extracted <- Try("""v(\d\.[1]\d)""".r.findFirstMatchIn(installed.trim))
_ <- extracted.fold(Failure(new RuntimeException("Hugo is not currently installed!")): Try[Unit]
)(v => if(v.group(1) >= minimumVersion) Success(())
else Failure(new RuntimeException("The current version of Hugo installed is not new enough to build this project.")))
} yield ()
}

}
73 changes: 73 additions & 0 deletions src/main/scala/com/typesafe/sbt/site/preview.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package com.typesafe.sbt.site

import sbt._, Keys._
import unfiltered.jetty.Server
import unfiltered.filter.Plan
import unfiltered.response._
import unfiltered.request._
import java.io.File
import java.io.OutputStream
import collection.mutable.Map

object Preview {
def apply(port: Int, base: File, genSite: TaskKey[File], genSources: TaskKey[Seq[File]], state: State): Server = {
val rootFile = runTask(genSite, state)
val rootSources = runTask(genSources, state)

val rootPage: Option[URL] = startPageURL(rootFile)
var mapSources: Map[File, Long] = mapFileToLastModified(rootSources)

val plan: Plan = unfiltered.filter.Planify {
case GET(unfiltered.request.Path(Seg(path))) => {
val newSources = runTask(genSources, state)
val newMapSources = mapFileToLastModified(newSources)
if(mapSources != newMapSources) {
val _ = runTask(genSite, state)
mapSources = newMapSources
}
path match {
case p :: ps =>
val newFile: File = base / path.mkString("/")
responseStreamer(newFile.toURI.toURL)
case Nil =>
rootPage match {
case Some(startingPage) => responseStreamer(startingPage)
case None => ResponseString("No file found, make sure to generate any starting web page at root project (default: \"index.html\")")
}
}
}
}
val http = unfiltered.jetty.Server.local(port)
http.plan(plan).resources(new URL(rootFile.toURI.toURL, "."))
}

def startPageURL(rootFile: File): Option[URL] = {
val files = rootFile.listFiles.filter(!_.isDirectory)
files.toList.filter(_.getName == "index.html") match {
case ind :: Nil => Some(ind.toURI.toURL)
case Nil if(files.length > 0) => Some(files(0).toURI.toURL)
case _ => None
}
}

def responseStreamer(url: URL) =
new ResponseStreamer { def stream(os:OutputStream) {
val is = url.openStream
try {
val buf = new Array[Byte](1024)
Iterator.continually(is.read(buf)).takeWhile(_ != -1)
.foreach(os.write(buf, 0, _))
} finally {
is.close
}
} }

def mapFileToLastModified(files: Seq[File]): Map[File, Long] =
Map(files.filter(!_.toString.endsWith("~")).map(file => file -> file.lastModified()): _*)

def runTask[A](task: TaskKey[A], state: State): A = {
val extracted = Project extract state
val (_, result) = extracted.runTask(task, state)
result
}
}
13 changes: 13 additions & 0 deletions src/sbt-test/hugo/can-use-hugo/build.sbt
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
name := "test"

enablePlugins(HugoPlugin)

siteSubdirName in Hugo := "thisIsHugo"

TaskKey[Unit]("checkContent") := {
val dest = (target in makeSite).value / (siteSubdirName in Hugo).value
val index = dest / "index.html"
assert(index.exists, s"${index.getAbsolutePath} did not exist")
val content = IO.readLines(index)
assert(content.exists(_.contains("Knobs")), s"Did not find expected content in:\n${content.mkString("\n")}")
}
1 change: 1 addition & 0 deletions src/sbt-test/hugo/can-use-hugo/project/plugins.sbt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
addSbtPlugin("com.typesafe.sbt" % "sbt-site" % sys.props("project.version"))
9 changes: 9 additions & 0 deletions src/sbt-test/hugo/can-use-hugo/src/hugo/config.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
baseurl = "http://localhost:1313/"
languageCode = "en-us"
author = "sbt-site"
copyright = "All rights reserved."

[params]
github_host = "github.com"
github_repository = "verizon/knobs"
project_name = "Knobs"
Loading

0 comments on commit 2fcdb5c

Please sign in to comment.