API for Isabelle Jenkins continuous integration services;
authorwenzelm
Tue Aug 09 23:26:51 2016 +0200 (2016-08-09)
changeset 6364674604a9fc4c8
parent 63645 d7e0004d4321
child 63647 437bd400d808
API for Isabelle Jenkins continuous integration services;
Admin/etc/settings
src/Pure/Tools/ci_api.scala
src/Pure/build-jars
     1.1 --- a/Admin/etc/settings	Tue Aug 09 23:19:35 2016 +0200
     1.2 +++ b/Admin/etc/settings	Tue Aug 09 23:26:51 2016 +0200
     1.3 @@ -1,3 +1,5 @@
     1.4  # -*- shell-script -*- :mode=shellscript:
     1.5  
     1.6  ISABELLE_TOOLS="$ISABELLE_TOOLS:$COMPONENT/lib/Tools"
     1.7 +
     1.8 +ISABELLE_JENKINS_ROOT="https://ci.isabelle.systems/jenkins"
     2.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     2.2 +++ b/src/Pure/Tools/ci_api.scala	Tue Aug 09 23:26:51 2016 +0200
     2.3 @@ -0,0 +1,74 @@
     2.4 +/*  Title:      Pure/Tools/build.scala
     2.5 +    Author:     Makarius
     2.6 +
     2.7 +API for Isabelle Jenkins continuous integration services.
     2.8 +*/
     2.9 +
    2.10 +package isabelle
    2.11 +
    2.12 +
    2.13 +import java.net.URL
    2.14 +
    2.15 +import scala.util.matching.Regex
    2.16 +
    2.17 +
    2.18 +object CI_API
    2.19 +{
    2.20 +  /* CI service */
    2.21 +
    2.22 +  def root(): String =
    2.23 +    Isabelle_System.getenv_strict("ISABELLE_JENKINS_ROOT")
    2.24 +
    2.25 +  def invoke(url: String, args: String*): Any =
    2.26 +  {
    2.27 +    val req = url + "/api/json?" + args.mkString("&")
    2.28 +    val result = Url.read(req)
    2.29 +    try { JSON.parse(result) }
    2.30 +    catch { case ERROR(_) => error("Malformed JSON from " + quote(req)) }
    2.31 +  }
    2.32 +
    2.33 +
    2.34 +  /* build jobs */
    2.35 +
    2.36 +  def build_jobs(): List[String] =
    2.37 +    for {
    2.38 +      job <- JSON.array(invoke(root()), "jobs")
    2.39 +      _class <- JSON.string(job, "_class")
    2.40 +      if _class == "hudson.model.FreeStyleProject"
    2.41 +      name <- JSON.string(job, "name")
    2.42 +    } yield name
    2.43 +
    2.44 +  sealed case class Build_Info(
    2.45 +    job_name: String,
    2.46 +    timestamp: Long,
    2.47 +    output: URL,
    2.48 +    session_logs: List[(String, URL)])
    2.49 +  {
    2.50 +    def read_output(): String = Url.read(output)
    2.51 +    def read_log(full_stats: Boolean, name: String): Build.Log_Info =
    2.52 +      Build.parse_log(full_stats,
    2.53 +        session_logs.collectFirst({ case (a, b) if a == name => Url.read_gzip(b) }) getOrElse "")
    2.54 +  }
    2.55 +
    2.56 +  def build_job_builds(job_name: String): List[Build_Info] =
    2.57 +  {
    2.58 +    val Log_Session = new Regex("""^.*/log/([^/]+)\.gz$""")
    2.59 +
    2.60 +    for {
    2.61 +      build <- JSON.array(
    2.62 +        invoke(root() + "/job/" + job_name, "tree=builds[number,timestamp,artifacts[*]]"), "builds")
    2.63 +      number <- JSON.int(build, "number")
    2.64 +      timestamp <- JSON.long(build, "timestamp")
    2.65 +    } yield {
    2.66 +      val job_prefix = root() + "/job/" + job_name + "/" + number
    2.67 +      val output = Url(job_prefix + "/consoleText")
    2.68 +      val session_logs =
    2.69 +        for {
    2.70 +          artifact <- JSON.array(build, "artifacts")
    2.71 +          log_path <- JSON.string(artifact, "relativePath")
    2.72 +          session <- (log_path match { case Log_Session(name) => Some(name) case _ => None })
    2.73 +        } yield (session -> Url(job_prefix + "/artifact/" + log_path))
    2.74 +      Build_Info(job_name, timestamp, output, session_logs)
    2.75 +    }
    2.76 +  }
    2.77 +}
     3.1 --- a/src/Pure/build-jars	Tue Aug 09 23:19:35 2016 +0200
     3.2 +++ b/src/Pure/build-jars	Tue Aug 09 23:26:51 2016 +0200
     3.3 @@ -104,6 +104,7 @@
     3.4    Tools/build_doc.scala
     3.5    Tools/check_keywords.scala
     3.6    Tools/check_sources.scala
     3.7 +  Tools/ci_api.scala
     3.8    Tools/ci_profile.scala
     3.9    Tools/debugger.scala
    3.10    Tools/doc.scala