Permalink
Browse files

Merge branch 'master' of git://github.com/jto/giter8 into jgit

  • Loading branch information...
2 parents ef9a0ae + 717855e commit 8a65756144cda2fa4b75764514e86d610e5df3a7 @n8han n8han committed Jul 9, 2012
View
23 README.markdown
@@ -57,7 +57,12 @@ The `.g8` suffix is assumed:
Either way, giter8 resolves this to the `softprops/unfiltered.g8`
repository and queries github for the project's template
-parameters. You'll be prompted for each parameter, with its default
+parameters.
+Alternatively, you can also use a git repository full name>
+
+ $ g8 https://github.com/softprops/unfiltered.g8.git
+
+You'll be prompted for each parameter, with its default
value in square brackets:
name [My Web Project]:
@@ -81,21 +86,7 @@ Any unsupplied parameters are assigned their default values.
Private Repositories
--------------------
-Giter8 accesses GitHub anonymously by default, but for private
-templates you can authenticate.
-
- $ g8 --auth yourname:yourpass
-
-This stores a [github OAuth](http://develop.github.com/p/oauth.html)
-token in `~/.g8/config` which is then used for future `g8`
-invocations. The token can be revoked at any time in your
-[github settings][settings].
-
-[settings]: https://github.com/settings/applications
-
-If you wish to use basic authentication instead, you can specify a
-`github.user` and `github.password` in your global git properties. The
-OAuth token is preferred and will be used when both are present.
+Giter8 will use your ssh key to access private repositories, just like git does.
Making your own templates
-------------------------
View
190 app/src/main/scala/apply.scala
@@ -2,7 +2,7 @@ package giter8
case class FileInfo(name: String, hash: String, mode: String)
-trait Apply extends Defaults { self: Giter8 =>
+trait Apply extends GitRepo { self: Giter8 =>
import dispatch._
import dispatch.liftjson.Js._
import net.liftweb.json.JsonAST._
@@ -15,71 +15,72 @@ trait Apply extends Defaults { self: Giter8 =>
val Text = """^(text|application)/.+""".r
val DefaultBranch = "master"
- def inspect(repo: String,
- branch: Option[String],
- arguments: Seq[String]) =
- fetchInfo(repo, branch).right.flatMap {
- case (defaults, templates) =>
- val parameters = arguments.headOption.map { _ =>
- (defaults /: arguments) {
- case (map, Param(key, value)) if map.contains(key) =>
- map + (key -> value)
- case (map, Param(key, _)) =>
- println("Ignoring unrecognized parameter: " + key)
- map
- }
- }.getOrElse { interact(defaults) }
- val base = new File(
- parameters.get("name").map(G8.normalize).getOrElse(".")
- )
- write(repo, templates, parameters, base)
+ def setFileMode(f: File, mode: String) = allCatch opt {
+ if ((mode(3).toString.toInt & 0x1) > 0) {
+ f.setExecutable(true)
}
+ }
- def fetchInfo(repo: String, branch: Option[String]) = {
- repoFiles(repo, branch.getOrElse(DefaultBranch)).right.flatMap { files =>
- val (propertiesFiles, templates) = files.partition {
- _.name == "default.properties"
- }
- prepareDefaults(repo, propertiesFiles.headOption).right.map {
- defaults => (defaults, templates)
+ private def use[C <: { def close(): Unit }, T](c: C)(f: C => T): T =
+ try { f(c) } finally { c.close() }
+}
+
+trait GitRepo { self: Giter8 =>
+
+ import java.io.File
+ import org.eclipse.jgit.api._
+ import scala.io.Source
+ import scala.util.control.Exception.{allCatch,catching}
+
+ val TMP = new File(System.getProperty("java.io.tmpdir"), java.util.UUID.randomUUID().toString)
+ val TEMPLATES_FOLDER = new File(TMP,"src/main/g8")
+
+ def inspect(repo: String,
+ branch: Option[String],
+ arguments: Seq[String]) = {
+ val tmpl = clone(repo, branch)
+
+ val (defaults, templates) = fetchInfo(tmpl)
+
+ val parameters = arguments.headOption.map { _ =>
+ (defaults /: arguments) {
+ case (map, Param(key, value)) if map.contains(key) =>
+ map + (key -> value)
+ case (map, Param(key, _)) =>
+ println("Ignoring unrecognized parameter: " + key)
+ map
}
- }
+ }.getOrElse { interact(defaults) }
+
+ val base = new File(
+ parameters.get("name").map(G8.normalize).getOrElse(".")
+ )
+
+ write(repo, templates, parameters, base)
}
- def repoFiles(repo: String, branch: String): Either[String,Seq[FileInfo]] =
- allCatch.either {
- val shas =
- http(gh / repo / "git" / "refs" / "heads" / branch ># { js =>
- for {
- JField("object", JObject(obj)) <- js
- JField("sha", JString(sha)) <- obj
- } yield sha
- })
- shas.flatMap { sha =>
- http(gh / repo / "git" / "trees" / sha <<? Map(
- "recursive" -> "1"
- ) ># { js =>
- for {
- JField("tree", JArray(tree)) <- js
- JObject(blob) <- tree
- JField("type", JString("blob")) <- blob
- JField("path", JString(name)) <- blob
- JField("sha", JString(hash)) <- blob
- JField("mode", JString(mode)) <- blob
- m <- Root.findFirstMatchIn(name)
- } yield FileInfo(m.group(1), hash, mode)
- })
- }
- }.left.flatMap {
- case StatusCode(404, _) => Right(Seq.empty)
- case e => Left("Exception fetching from github " + e.getMessage)
- }.right.flatMap { seq =>
- if (seq.isEmpty)
- Left("Unable to find github repository: %s (%s)".format(repo, branch))
- else
- Right(seq)
+ def fetchInfo(f: File) = {
+ import java.io.FileInputStream
+
+ def getFiles(filter: File => Boolean)(f: File): Stream[File] =
+ f #:: (if (f.isDirectory) f.listFiles().toStream.filter(filter).flatMap(getFiles(filter)) else Stream.empty)
+
+ def getVisibleFiles = getFiles(!_.isHidden) _
+
+ val fs = getVisibleFiles(f)
+ val (propertiesFiles, tmpls) = fs.partition {
+ _.getName == "default.properties"
}
+ val parameters = propertiesFiles.headOption.map{ f =>
+ val props = GIO.readProps(new FileInputStream(f))
+ Ls.lookup(props).right.toOption.getOrElse(props)
+ }.getOrElse(Map.empty)
+
+ val g8templates = tmpls.filter(!_.isDirectory)
+ (parameters, g8templates)
+ }
+
def interact(params: Map[String, String]) = {
val (desc, others) = params partition { case (k,_) => k == "description" }
desc.values.foreach { d =>
@@ -106,55 +107,48 @@ trait Apply extends Defaults { self: Giter8 =>
}
}
+ // TODO: exeptions handling
+ def clone(repo: String, branch: Option[String]) = {
+ val cmd = new CloneCommand()
+ for(b <- branch)
+ cmd.setBranch(b)
+ cmd.setURI(repo)
+ cmd.setDirectory(TMP)
+ cmd.call()
+ TMP.deleteOnExit()
+ TMP
+ }
+
def write(repo: String,
- templates: Iterable[FileInfo],
+ templates: Iterable[File],
parameters: Map[String,String],
base: File) = {
import java.nio.charset.MalformedInputException
val renderer = new StringRenderer
- templates.foreach { case FileInfo(name, hash, mode) =>
- val f = G8.expandPath(name, base, parameters)
- if (f.exists)
- println("Skipping existing file: %s" format f.toString)
+
+ templates.map{ in =>
+ val name = TEMPLATES_FOLDER.toURI().relativize(in.toURI).getPath
+ val out = G8.expandPath(name, base, parameters)
+ (in, out)
+ }.foreach { case (in, out) =>
+ if (out.exists) {
+ println("Skipping existing file: %s" format out.toString)
+ }
else {
- f.getParentFile.mkdirs()
- (if (G8.verbatim(f, parameters)) None
- else catching(classOf[MalformedInputException]).opt {
- http(show(repo, hash) >- { in =>
- Some(G8.write(f, in, parameters))
- })
- }) getOrElse {
- http(show(repo, hash) >> { is =>
- use(is) { in =>
- use(new FileOutputStream(f)) { out =>
- def consume(buf: Array[Byte]): Unit =
- in.read(buf) match {
- case -1 => ()
- case n =>
- out.write(buf, 0, n)
- consume(buf)
- }
- consume(new Array[Byte](1024))
- }
- }
- })
+ out.getParentFile.mkdirs()
+ if (G8.verbatim(out, parameters))
+ GIO.copyFile(in, out)
+ else {
+ catching(classOf[MalformedInputException]).opt {
+ Some(G8.write(out, Source.fromFile(in).mkString, parameters))
+ }.getOrElse {
+ GIO.copyFile(in, out)
+ }
}
- setFileMode(f, mode)
}
}
+
Right("Applied %s in %s" format (repo, base.toString))
}
-
- def setFileMode(f: File, mode: String) = allCatch opt {
- if ((mode(3).toString.toInt & 0x1) > 0) {
- f.setExecutable(true)
- }
- }
- def show(repo: String, hash: String) =
- gh / repo / "git" / "blobs" / hash <:< Map(
- "Accept" -> "application/vnd.github.raw"
- )
- private def use[C <: { def close(): Unit }, T](c: C)(f: C => T): T =
- try { f(c) } finally { c.close() }
}
View
35 app/src/main/scala/authorize.scala
@@ -1,35 +0,0 @@
-package giter8
-
-import dispatch._
-import dispatch.liftjson.Js._
-import net.liftweb.json._
-import net.liftweb.json.JsonDSL._
-
-object Authorize {
- val authorizations = :/("api.github.com").secure / "authorizations"
-}
-
-trait Authorize { self: Giter8 =>
- import Authorize._
-
- def auth(user: String, pass: String): Either[String, String] =
- http x (authorizations.POST.as_!(user, pass) << compact(render(
- ("note" -> "Giter8") ~
- ("note_url" -> "https://github.com/n8han/giter8") ~
- ("scopes" -> ("repo" :: Nil))
- )) ># { _ \ "token" match {
- case JString(tok) => Some(tok)
- case _ => None
- }}) {
- case (201, _, _, token) => token().map { access =>
- Config.properties {
- _.setProperty("gh.access", access)
- }
- Right("Authorization stored")
- } getOrElse Left("Authorization failed")
- case (401, _, _, _) =>
- Left("Invalid github.com login/password combination")
- case (status, _, _, _) =>
- Left("Unexpected error communicating with api.github.com")
- }
-}
View
37 app/src/main/scala/credentials.scala
@@ -1,37 +0,0 @@
-package giter8
-
-trait Credentials { self: Apply =>
- import scala.util.control.Exception.allCatch
- import dispatch._
-
- def withCredentials(req: dispatch.Request) =
- (oauth map { case token => req <:< Map("Authorization" -> "token %s".format(token)) }).orElse(
- credentials map { case (user, pass) => req as_! (user, pass) }) getOrElse req
-
- def oauth: Option[String] =
- Config.get("gh.access")
-
- lazy val credentials: Option[(String, String)] =
- gitConfig("github.user") flatMap { user =>
- gitConfig("github.password") map { password =>
- (user, password)
- }
- }
-
- // https://github.com/defunkt/gist/blob/master/lib/gist.rb#L237
- def gitConfig(key: String): Option[String] =
- allCatch opt {
- Option(System.getenv(key.toUpperCase.replaceAll("""\.""", "_"))) map { Some(_) } getOrElse {
- val gitExec = windows map {_ => "git.exe"} getOrElse {"git"}
- val p = new java.lang.ProcessBuilder(gitExec, "config", "--global", key).start()
- val reader = new java.io.BufferedReader(new java.io.InputStreamReader(p.getInputStream))
- Option(reader.readLine)
- }
- } getOrElse {None}
-
- def windows =
- System.getProperty("os.name") match {
- case x: String if x contains "Windows" => Some(x)
- case _ => None
- }
-}
View
13 app/src/main/scala/defaults.scala
@@ -1,13 +0,0 @@
-package giter8
-
-trait Defaults { self: Giter8 =>
- def prepareDefaults(
- repo: String,
- properties: Option[FileInfo]
- ) = Ls.lookup(fetchDefaults(repo, properties))
-
- def fetchDefaults(repo: String, properties: Option[FileInfo]) =
- properties.map { fileinfo =>
- http(show(repo, fileinfo.hash) >> GIO.readProps _ )
- }.getOrElse { Map.empty }
-}
View
36 app/src/main/scala/discover.scala
@@ -1,36 +0,0 @@
-package giter8
-
-trait Discover { self: Giter8 =>
- import dispatch._
- import dispatch.liftjson.Js._
- import net.liftweb.json.JsonAST._
-
- val RepoNamed = """(\S+)\.g8""".r
- def discover(query: Option[String]) =
- remoteTemplates(query).right.flatMap { templates =>
- templates match {
- case Nil => Right("No templates matching %s" format query.get)
- case _ => Right(templates.sortBy { _.name } map { t =>
- val padding =
- if (t.name.length < 20 || t.desc == "") ""
- else "\n "
- "%20s/%-20s%s%s" format(t.user, t.name, padding, t.desc)
- } mkString("\n"))
- }
- }
-
- def remoteTemplates(query: Option[String]) = try { Right(for {
- repos <- http(repoSearch(query) ># ('repositories ? ary))
- JObject(fields) <- repos
- JField("name", JString(repo)) <- fields
- JField("username", JString(user_name)) <- fields
- JField("description", JString(desc)) <- fields
- repo_name <- RepoNamed.findFirstMatchIn(repo)
- } yield Template(user_name, repo_name.group(1), desc)) } catch {
- case StatusCode(404, _) => Left("Unable to find github repositories like : %s" format query.get)
- }
-
- case class Template(user: String, name: String, desc: String)
-
- def repoSearch(query: Option[String]) = gh / "repos" / "search" / query.getOrElse("g8")
-}
View
29 app/src/main/scala/giter8.scala
@@ -1,13 +1,12 @@
package giter8
-class Giter8 extends xsbti.AppMain
- with Discover with Apply with Authorize with Credentials {
+class Giter8 extends xsbti.AppMain with Apply {
import dispatch._
val Repo = """^(\S+)/(\S+?)(?:\.g8)?$""".r
val Branch = """^-(b|-branch)$""".r
val RemoteTemplates = """^-(l|-list)$""".r
- val Auth = """^-(a|-auth)$""".r
+ val Git = """^(.*\.g8\.git)$""".r
java.util.logging.Logger.getLogger("").setLevel(java.util.logging.Level.SEVERE)
@@ -18,17 +17,14 @@ class Giter8 extends xsbti.AppMain
/** Runner shared my main-class runner */
def run(args: Array[String]): Int = {
(args.partition { s => Param.pattern.matcher(s).matches } match {
+ case (params, Array(Git(remote))) =>
+ inspect(remote, None, params)
+ case (params, Array(Git(remote), Branch(_), branch)) =>
+ inspect(remote, Some(branch), params)
case (params, Array(Repo(user, proj))) =>
- inspect("%s/%s.g8".format(user, proj), None, params)
+ inspect("git@github.com:%s/%s.g8.git".format(user, proj), None, params)
case (params, Array(Repo(user, proj), Branch(_), branch)) =>
- inspect("%s/%s.g8".format(user, proj), Some(branch), params)
- case (params, Array(Auth(param), userpass)) =>
- userpass.split(":", 2) match {
- case Array(user, pass) => auth(user, pass)
- case _ =>
- Left("-%s requires username and password separated by `:`".format(
- param))
- }
+ inspect("git@github.com:%s/%s.g8.git".format(user, proj), Some(branch), params)
case _ => Left(usage)
}) fold ({ error =>
System.err.println("\n%s\n" format error)
@@ -39,8 +35,6 @@ class Giter8 extends xsbti.AppMain
})
}
- def gh = withCredentials(:/("api.github.com").secure) / "repos"
-
def http = new Http {
override def make_logger = new dispatch.Logger {
val jdklog = java.util.logging.Logger.getLogger("dispatch")
@@ -57,8 +51,6 @@ class Giter8 extends xsbti.AppMain
|Apply specified template.
|
|OPTIONS
- | -a, --auth <login>:<password>
- | Authorizes oauth access to Github
| -b, --branch
| Resolves a template within a given branch
| --paramname=paramvalue
@@ -68,14 +60,15 @@ class Giter8 extends xsbti.AppMain
|Apply template and interactively fulfill parameters.
| g8 n8han/giter8
|
+ |Or
+ | g8 git://github.com/n8han/giter8.git
+ |
|Apply template from a remote branch
| g8 n8han/giter8 -b some-branch
|
|Apply given name parameter and use defaults for all others.
| g8 n8han/giter8 --name=template-test
|
- |Acquire Github authorization
- | g8 -a login:password
|""".stripMargin format (BuildInfo.version)
}
View
15 project/build.scala
@@ -6,6 +6,9 @@ object Builds extends sbt.Build {
import sbtbuildinfo.Plugin._
val g8version = "0.4.5"
+
+ val typesafeRepo = "Typesafe repo" at "http://repo.typesafe.com/typesafe/repo/"
+ val jgitRepo = "jGit repo" at "http://download.eclipse.org/jgit/maven/"
lazy val buildSettings = Defaults.defaultSettings ++ lsSettings ++ Seq(
organization := "net.databinder.giter8",
@@ -48,19 +51,25 @@ object Builds extends sbt.Build {
description :=
"Command line tool to apply templates defined on github",
name := "giter8",
- libraryDependencies +=
+ libraryDependencies ++= Seq(
"net.databinder" %% "dispatch-lift-json" % "0.8.5",
+ "org.eclipse.jgit" % "org.eclipse.jgit" % "1.3.0.201202151440-r"
+ ),
sourceGenerators in Compile <+= buildInfo,
buildInfoKeys := Seq[Scoped](name, version, scalaVersion, sbtVersion),
- buildInfoPackage := "giter8"
+ buildInfoPackage := "giter8",
+ resolvers += typesafeRepo
)) dependsOn (lib)
lazy val plugin = Project("giter8-plugin", file("plugin"),
settings = buildSettings ++ Seq(
description :=
"sbt 0.11 plugin for testing giter8 templates",
sbtPlugin := true,
- resolvers += Resolver.url("Typesafe repository", new java.net.URL("http://typesafe.artifactoryonline.com/typesafe/ivy-releases/"))(Resolver.defaultIvyPatterns),
+ resolvers ++= Seq(
+ Resolver.url("Typesafe repository", new java.net.URL("http://typesafe.artifactoryonline.com/typesafe/ivy-releases/"))(Resolver.defaultIvyPatterns),
+ typesafeRepo
+ ),
libraryDependencies <++= (sbtDependency, sbtVersion) { (sd, sv) =>
Seq(sd,
"org.scala-sbt" %% "scripted-plugin" % sv

0 comments on commit 8a65756

Please sign in to comment.