Skip to content

Commit

Permalink
Merge pull request #48 from alun/master
Browse files Browse the repository at this point in the history
--clean-boot support for windows OSes
  • Loading branch information
Nathan Hamblen committed May 20, 2013
2 parents 68f4bd4 + 07ac227 commit cd92850
Show file tree
Hide file tree
Showing 3 changed files with 110 additions and 13 deletions.
111 changes: 103 additions & 8 deletions src/main/scala/clean.scala 100644 → 100755
@@ -1,16 +1,111 @@
package conscript

object Clean {
def clean(files: Array[java.io.File]): Option[String] =
(Option.empty[String] /: files) { (a, e) =>
a orElse {
if (e.isDirectory)
clean(e.listFiles).orElse(delete(e))
else delete(e)
import java.io.{PrintWriter, File}
import java.text.SimpleDateFormat
import java.util.Date
import scala.util.control.Exception._
import scala.util.Random
import java.util.regex.Pattern
import scala.util.matching.Regex

object Clean extends OsDetect {

/**
* Windows java runtime can't success with File.delete() while cleaning SBT boot directory.
* This helper object adds workaround of the issue by the using cmd scripts and windows task scheduler.
*/
private object Windows {
lazy val timeFormat = new SimpleDateFormat("HH:mm:ss")

def exec(args: String*) = allCatch.either { sys.runtime.exec(args.toArray) }

// TODO: this could be rid off when will switch scala 2.10 by the using of string substitution
def resolvePlaceholders(text:String)(implicit placeholders: Map[String, Any]) =
(text /: placeholders) {
case (text, (k, v)) => text.replaceAll(
Pattern.quote("${" + k + "}"),
Regex.quoteReplacement(v.toString)
)
}

def writeScript(f:File)(text:String)(implicit placeholders: Map[String, Any]) = {
var printer = Option.empty[PrintWriter]
allCatch.andFinally(printer.map(_.close())).either {
printer = Some(new PrintWriter(f, "UTF-8"))
resolvePlaceholders(text).split("\\n").map(printer.get.println)
}
}

def scheduleClean(file:File) = {
def time = timeFormat.format(new Date)
val taskName = "ConscriptClean" + Random.nextInt()
val cleanScript = File.createTempFile("conscript-clean-boot", ".bat")
val scheduleScript = File.createTempFile("conscript-schedule-clean", ".bat")
val createFlags = if (isXP) "/ru SYSTEM " else "/f" // win XP uses different version of schtasks

implicit val scriptPlaceholders = Map(
"createFlags" -> createFlags,
"taskName" -> taskName,
"scheduleScript" -> scheduleScript,
"cleanScript" -> cleanScript,
"time" -> time,
"file" -> file
)

for {
_ <- writeScript(scheduleScript) {
"""
|@echo off
|schtasks /Create ${createFlags} /tn ${taskName} /sc ONCE /tr "${cleanScript}" /st ${time}
|schtasks /Run /tn ${taskName}
""".stripMargin
} .right
_ <- writeScript(cleanScript) {
"""
|@echo off
|schtasks /Delete /f /tn ${taskName}
|rmdir /q /s "${file}"
|dir "${file}" && (
| "${scheduleScript}"
|) || (
| del /q "${cleanScript}"
| del /q "${scheduleScript}"
|)
""".stripMargin
} .right
} yield {
sys.addShutdownHook {
exec(scheduleScript.getAbsolutePath)
}
"Clean script scheduled successfully"
}
}
}

/**
* Doing recursive removing of the file (or dir).
*
* On windows SBT launcher boot directory cannot be cleaned while launcher is running so we do schedule
* this procedure with the system task manager to run from parallel service process
*
* @param file a file to cleanRec
* @return some error message or none if success
*/
def clean(file:File):Option[String] =
cleanRec(file).flatMap { error =>
windows.map(_ => Windows.scheduleClean(file).left.toOption.map(_.getMessage)) getOrElse(Some(error))
}

private def cleanRec(file:File):Option[String] =
if (file.isDirectory)
(Option.empty[String] /: file.listFiles) { (a, f) =>
a orElse cleanRec(f)
} .orElse(delete(file))
else delete(file)

private def delete(file: java.io.File) =
private def delete(file: File) =
if (file.delete()) None
else Some("Unable to delete %s".format(file))

}

2 changes: 1 addition & 1 deletion src/main/scala/conscript.scala
Expand Up @@ -73,7 +73,7 @@ object Conscript {
parsed.map {
case c if c.clean_boot =>
if (Apply.bootdir.exists && Apply.bootdir.isDirectory)
Clean.clean(Apply.bootdir.listFiles).toLeft("Cleaned boot directory (%s)".format(Apply.bootdir))
Clean.clean(Apply.bootdir).toLeft("Cleaned boot directory (%s)".format(Apply.bootdir))
else Left("No boot directory found at " + Apply.bootdir)
case c if c.usage =>
Right(parser.usage)
Expand Down
10 changes: 6 additions & 4 deletions src/main/scala/credentials.scala
@@ -1,11 +1,8 @@
package conscript

import dispatch._
import com.ning.http.client.RequestBuilder

trait Credentials {
import scala.util.control.Exception.allCatch

trait Credentials extends OsDetect {
def withCredentials(req: RequestBuilder) =
(oauth map {
case token => req.addHeader("Authorization", "token %s".format(token))
Expand All @@ -14,9 +11,14 @@ trait Credentials {
def oauth: Option[String] =
Config.get("gh.access")

}

trait OsDetect {
def windows =
System.getProperty("os.name") match {
case x: String if x contains "Windows" => Some(x)
case _ => None
}

def isXP = windows.map(_.contains("XP")).getOrElse(false)
}

0 comments on commit cd92850

Please sign in to comment.