src/Pure/Admin/jenkins.scala
author wenzelm
Sun, 14 Mar 2021 21:02:34 +0100
changeset 73436 e92f2e44e4d8
parent 73342 0bf768567d9f
child 75393 87ebf5a50283
permissions -rw-r--r--
removed spurious references to perl / libwww-perl;
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
65650
48ef286b847b clarified modules;
wenzelm
parents: 64545
diff changeset
     1
/*  Title:      Pure/Admin/jenkins.scala
63646
74604a9fc4c8 API for Isabelle Jenkins continuous integration services;
wenzelm
parents:
diff changeset
     2
    Author:     Makarius
74604a9fc4c8 API for Isabelle Jenkins continuous integration services;
wenzelm
parents:
diff changeset
     3
65650
48ef286b847b clarified modules;
wenzelm
parents: 64545
diff changeset
     4
Support for Jenkins continuous integration service.
63646
74604a9fc4c8 API for Isabelle Jenkins continuous integration services;
wenzelm
parents:
diff changeset
     5
*/
74604a9fc4c8 API for Isabelle Jenkins continuous integration services;
wenzelm
parents:
diff changeset
     6
74604a9fc4c8 API for Isabelle Jenkins continuous integration services;
wenzelm
parents:
diff changeset
     7
package isabelle
74604a9fc4c8 API for Isabelle Jenkins continuous integration services;
wenzelm
parents:
diff changeset
     8
74604a9fc4c8 API for Isabelle Jenkins continuous integration services;
wenzelm
parents:
diff changeset
     9
74604a9fc4c8 API for Isabelle Jenkins continuous integration services;
wenzelm
parents:
diff changeset
    10
import java.net.URL
74604a9fc4c8 API for Isabelle Jenkins continuous integration services;
wenzelm
parents:
diff changeset
    11
74604a9fc4c8 API for Isabelle Jenkins continuous integration services;
wenzelm
parents:
diff changeset
    12
import scala.util.matching.Regex
74604a9fc4c8 API for Isabelle Jenkins continuous integration services;
wenzelm
parents:
diff changeset
    13
74604a9fc4c8 API for Isabelle Jenkins continuous integration services;
wenzelm
parents:
diff changeset
    14
65650
48ef286b847b clarified modules;
wenzelm
parents: 64545
diff changeset
    15
object Jenkins
63646
74604a9fc4c8 API for Isabelle Jenkins continuous integration services;
wenzelm
parents:
diff changeset
    16
{
65653
f433c0e73307 read ml_statistics from session logs: .db or .gz files;
wenzelm
parents: 65652
diff changeset
    17
  /* server API */
63646
74604a9fc4c8 API for Isabelle Jenkins continuous integration services;
wenzelm
parents:
diff changeset
    18
74604a9fc4c8 API for Isabelle Jenkins continuous integration services;
wenzelm
parents:
diff changeset
    19
  def root(): String =
74604a9fc4c8 API for Isabelle Jenkins continuous integration services;
wenzelm
parents:
diff changeset
    20
    Isabelle_System.getenv_strict("ISABELLE_JENKINS_ROOT")
74604a9fc4c8 API for Isabelle Jenkins continuous integration services;
wenzelm
parents:
diff changeset
    21
74604a9fc4c8 API for Isabelle Jenkins continuous integration services;
wenzelm
parents:
diff changeset
    22
  def invoke(url: String, args: String*): Any =
74604a9fc4c8 API for Isabelle Jenkins continuous integration services;
wenzelm
parents:
diff changeset
    23
  {
74604a9fc4c8 API for Isabelle Jenkins continuous integration services;
wenzelm
parents:
diff changeset
    24
    val req = url + "/api/json?" + args.mkString("&")
74604a9fc4c8 API for Isabelle Jenkins continuous integration services;
wenzelm
parents:
diff changeset
    25
    val result = Url.read(req)
74604a9fc4c8 API for Isabelle Jenkins continuous integration services;
wenzelm
parents:
diff changeset
    26
    try { JSON.parse(result) }
74604a9fc4c8 API for Isabelle Jenkins continuous integration services;
wenzelm
parents:
diff changeset
    27
    catch { case ERROR(_) => error("Malformed JSON from " + quote(req)) }
74604a9fc4c8 API for Isabelle Jenkins continuous integration services;
wenzelm
parents:
diff changeset
    28
  }
74604a9fc4c8 API for Isabelle Jenkins continuous integration services;
wenzelm
parents:
diff changeset
    29
74604a9fc4c8 API for Isabelle Jenkins continuous integration services;
wenzelm
parents:
diff changeset
    30
74604a9fc4c8 API for Isabelle Jenkins continuous integration services;
wenzelm
parents:
diff changeset
    31
  /* build jobs */
74604a9fc4c8 API for Isabelle Jenkins continuous integration services;
wenzelm
parents:
diff changeset
    32
65658
be817b7b8354 tuned signature;
wenzelm
parents: 65657
diff changeset
    33
  def build_job_names(): List[String] =
63646
74604a9fc4c8 API for Isabelle Jenkins continuous integration services;
wenzelm
parents:
diff changeset
    34
    for {
64545
25045094d7bb clarified JSON operations (see isabelle_vscode/a7931dc2a1ab);
wenzelm
parents: 64160
diff changeset
    35
      job <- JSON.array(invoke(root()), "jobs").getOrElse(Nil)
63646
74604a9fc4c8 API for Isabelle Jenkins continuous integration services;
wenzelm
parents:
diff changeset
    36
      _class <- JSON.string(job, "_class")
74604a9fc4c8 API for Isabelle Jenkins continuous integration services;
wenzelm
parents:
diff changeset
    37
      if _class == "hudson.model.FreeStyleProject"
74604a9fc4c8 API for Isabelle Jenkins continuous integration services;
wenzelm
parents:
diff changeset
    38
      name <- JSON.string(job, "name")
74604a9fc4c8 API for Isabelle Jenkins continuous integration services;
wenzelm
parents:
diff changeset
    39
    } yield name
74604a9fc4c8 API for Isabelle Jenkins continuous integration services;
wenzelm
parents:
diff changeset
    40
65657
2773b6859c55 download Jenkins logs with inlined ml_statistics;
wenzelm
parents: 65656
diff changeset
    41
68209
aeffd8f1f079 support Store with options;
wenzelm
parents: 67008
diff changeset
    42
  def download_logs(
73340
0ffcad1f6130 tuned --- fewer warnings;
wenzelm
parents: 72575
diff changeset
    43
    options: Options, job_names: List[String], dir: Path, progress: Progress = new Progress): Unit =
65657
2773b6859c55 download Jenkins logs with inlined ml_statistics;
wenzelm
parents: 65656
diff changeset
    44
  {
68209
aeffd8f1f079 support Store with options;
wenzelm
parents: 67008
diff changeset
    45
    val store = Sessions.store(options)
71601
97ccf48c2f0c misc tuning based on hints by IntelliJ IDEA;
wenzelm
parents: 69980
diff changeset
    46
    val infos = job_names.flatMap(build_job_infos)
65662
3db6a13fdffd more parallelism;
wenzelm
parents: 65661
diff changeset
    47
    Par_List.map((info: Job_Info) => info.download_log(store, dir, progress), infos)
65657
2773b6859c55 download Jenkins logs with inlined ml_statistics;
wenzelm
parents: 65656
diff changeset
    48
  }
2773b6859c55 download Jenkins logs with inlined ml_statistics;
wenzelm
parents: 65656
diff changeset
    49
2773b6859c55 download Jenkins logs with inlined ml_statistics;
wenzelm
parents: 65656
diff changeset
    50
65747
5a3052b2095f tuned signature;
wenzelm
parents: 65743
diff changeset
    51
  /* build log status */
65736
2e7230b66a32 performance statistics from build log database;
wenzelm
parents: 65674
diff changeset
    52
65788
bc00ac4dba25 more Jenkins test results;
wenzelm
parents: 65787
diff changeset
    53
  val build_log_jobs = List("isabelle-nightly-benchmark", "isabelle-nightly-slow")
65736
2e7230b66a32 performance statistics from build log database;
wenzelm
parents: 65674
diff changeset
    54
65747
5a3052b2095f tuned signature;
wenzelm
parents: 65743
diff changeset
    55
  val build_status_profiles: List[Build_Status.Profile] =
65736
2e7230b66a32 performance statistics from build log database;
wenzelm
parents: 65674
diff changeset
    56
    build_log_jobs.map(job_name =>
66880
486f4af28db9 more thorough treatment of afp_version and afp_pull_date;
wenzelm
parents: 65935
diff changeset
    57
      Build_Status.Profile("jenkins " + job_name,
486f4af28db9 more thorough treatment of afp_version and afp_pull_date;
wenzelm
parents: 65935
diff changeset
    58
        sql =
73342
0bf768567d9f tuned --- avoid deprecated Predef.any2stringadd;
wenzelm
parents: 73340
diff changeset
    59
          Build_Log.Prop.build_engine.toString + " = " + SQL.string(Build_Log.Jenkins.engine) +
0bf768567d9f tuned --- avoid deprecated Predef.any2stringadd;
wenzelm
parents: 73340
diff changeset
    60
          " AND " + Build_Log.Data.session_name + " <> " + SQL.string("Pure") + " AND " +
66880
486f4af28db9 more thorough treatment of afp_version and afp_pull_date;
wenzelm
parents: 65935
diff changeset
    61
          Build_Log.Data.status + " = " + SQL.string(Build_Log.Session_Status.finished.toString) +
486f4af28db9 more thorough treatment of afp_version and afp_pull_date;
wenzelm
parents: 65935
diff changeset
    62
          " AND " + Build_Log.Data.log_name + " LIKE " + SQL.string("%" + job_name)))
65736
2e7230b66a32 performance statistics from build log database;
wenzelm
parents: 65674
diff changeset
    63
2e7230b66a32 performance statistics from build log database;
wenzelm
parents: 65674
diff changeset
    64
65657
2773b6859c55 download Jenkins logs with inlined ml_statistics;
wenzelm
parents: 65656
diff changeset
    65
  /* job info */
2773b6859c55 download Jenkins logs with inlined ml_statistics;
wenzelm
parents: 65656
diff changeset
    66
64054
1fc9ab31720d clarified modules;
wenzelm
parents: 64045
diff changeset
    67
  sealed case class Job_Info(
63646
74604a9fc4c8 API for Isabelle Jenkins continuous integration services;
wenzelm
parents:
diff changeset
    68
    job_name: String,
74604a9fc4c8 API for Isabelle Jenkins continuous integration services;
wenzelm
parents:
diff changeset
    69
    timestamp: Long,
65655
1b84d4109215 clarified signature;
wenzelm
parents: 65654
diff changeset
    70
    main_log: URL,
65653
f433c0e73307 read ml_statistics from session logs: .db or .gz files;
wenzelm
parents: 65652
diff changeset
    71
    session_logs: List[(String, String, URL)])
63646
74604a9fc4c8 API for Isabelle Jenkins continuous integration services;
wenzelm
parents:
diff changeset
    72
  {
69980
f2e3adfd916f tuned signature;
wenzelm
parents: 68209
diff changeset
    73
    val date: Date = Date(Time.ms(timestamp), Date.timezone_berlin)
65656
wenzelm
parents: 65655
diff changeset
    74
wenzelm
parents: 65655
diff changeset
    75
    def log_filename: Path =
wenzelm
parents: 65655
diff changeset
    76
      Build_Log.log_filename(Build_Log.Jenkins.engine, date, List(job_name))
65653
f433c0e73307 read ml_statistics from session logs: .db or .gz files;
wenzelm
parents: 65652
diff changeset
    77
f433c0e73307 read ml_statistics from session logs: .db or .gz files;
wenzelm
parents: 65652
diff changeset
    78
    def read_ml_statistics(store: Sessions.Store, session_name: String): List[Properties.T] =
f433c0e73307 read ml_statistics from session logs: .db or .gz files;
wenzelm
parents: 65652
diff changeset
    79
    {
65655
1b84d4109215 clarified signature;
wenzelm
parents: 65654
diff changeset
    80
      def get_log(ext: String): Option[URL] =
1b84d4109215 clarified signature;
wenzelm
parents: 65654
diff changeset
    81
        session_logs.collectFirst({ case (a, b, url) if a == session_name && b == ext => url })
1b84d4109215 clarified signature;
wenzelm
parents: 65654
diff changeset
    82
1b84d4109215 clarified signature;
wenzelm
parents: 65654
diff changeset
    83
      get_log("db") match {
65653
f433c0e73307 read ml_statistics from session logs: .db or .gz files;
wenzelm
parents: 65652
diff changeset
    84
        case Some(url) =>
f433c0e73307 read ml_statistics from session logs: .db or .gz files;
wenzelm
parents: 65652
diff changeset
    85
          Isabelle_System.with_tmp_file(session_name, "db") { database =>
f433c0e73307 read ml_statistics from session logs: .db or .gz files;
wenzelm
parents: 65652
diff changeset
    86
            Bytes.write(database, Bytes.read(url))
65935
73c099fa96a4 more selective database access;
wenzelm
parents: 65896
diff changeset
    87
            using(SQLite.open_database(database))(db => store.read_ml_statistics(db, session_name))
65653
f433c0e73307 read ml_statistics from session logs: .db or .gz files;
wenzelm
parents: 65652
diff changeset
    88
          }
f433c0e73307 read ml_statistics from session logs: .db or .gz files;
wenzelm
parents: 65652
diff changeset
    89
        case None =>
65655
1b84d4109215 clarified signature;
wenzelm
parents: 65654
diff changeset
    90
          get_log("gz") match {
65653
f433c0e73307 read ml_statistics from session logs: .db or .gz files;
wenzelm
parents: 65652
diff changeset
    91
            case Some(url) =>
f433c0e73307 read ml_statistics from session logs: .db or .gz files;
wenzelm
parents: 65652
diff changeset
    92
              val log_file = Build_Log.Log_File(session_name, Url.read_gzip(url))
f433c0e73307 read ml_statistics from session logs: .db or .gz files;
wenzelm
parents: 65652
diff changeset
    93
              log_file.parse_session_info(ml_statistics = true).ml_statistics
f433c0e73307 read ml_statistics from session logs: .db or .gz files;
wenzelm
parents: 65652
diff changeset
    94
            case None => Nil
f433c0e73307 read ml_statistics from session logs: .db or .gz files;
wenzelm
parents: 65652
diff changeset
    95
          }
f433c0e73307 read ml_statistics from session logs: .db or .gz files;
wenzelm
parents: 65652
diff changeset
    96
      }
f433c0e73307 read ml_statistics from session logs: .db or .gz files;
wenzelm
parents: 65652
diff changeset
    97
    }
65657
2773b6859c55 download Jenkins logs with inlined ml_statistics;
wenzelm
parents: 65656
diff changeset
    98
73340
0ffcad1f6130 tuned --- fewer warnings;
wenzelm
parents: 72575
diff changeset
    99
    def download_log(store: Sessions.Store, dir: Path, progress: Progress = new Progress): Unit =
65657
2773b6859c55 download Jenkins logs with inlined ml_statistics;
wenzelm
parents: 65656
diff changeset
   100
    {
2773b6859c55 download Jenkins logs with inlined ml_statistics;
wenzelm
parents: 65656
diff changeset
   101
      val log_dir = dir + Build_Log.log_subdir(date)
72575
c7ab83a0c564 tuned signature;
wenzelm
parents: 72375
diff changeset
   102
      val log_path = log_dir + log_filename.xz
65657
2773b6859c55 download Jenkins logs with inlined ml_statistics;
wenzelm
parents: 65656
diff changeset
   103
2773b6859c55 download Jenkins logs with inlined ml_statistics;
wenzelm
parents: 65656
diff changeset
   104
      if (!log_path.is_file) {
65660
dfecaf0fc069 proper log_path check;
wenzelm
parents: 65659
diff changeset
   105
        progress.echo(log_path.expand.implode)
72375
e48d93811ed7 clarified signature;
wenzelm
parents: 71726
diff changeset
   106
        Isabelle_System.make_directory(log_dir)
65660
dfecaf0fc069 proper log_path check;
wenzelm
parents: 65659
diff changeset
   107
67008
eed58245b579 superseded by plain_identify;
wenzelm
parents: 66880
diff changeset
   108
        val ml_statistics =
71602
wenzelm
parents: 71601
diff changeset
   109
          session_logs.map(_._1).distinct.sorted.flatMap(session_name =>
67008
eed58245b579 superseded by plain_identify;
wenzelm
parents: 66880
diff changeset
   110
            read_ml_statistics(store, session_name).
eed58245b579 superseded by plain_identify;
wenzelm
parents: 66880
diff changeset
   111
              map(props => (Build_Log.SESSION_NAME -> session_name) :: props))
65657
2773b6859c55 download Jenkins logs with inlined ml_statistics;
wenzelm
parents: 65656
diff changeset
   112
67008
eed58245b579 superseded by plain_identify;
wenzelm
parents: 66880
diff changeset
   113
        File.write_xz(log_path,
eed58245b579 superseded by plain_identify;
wenzelm
parents: 66880
diff changeset
   114
          terminate_lines(Url.read(main_log) ::
71630
50425e4c3910 clarified modules: global quasi-scope for markers;
wenzelm
parents: 71620
diff changeset
   115
            ml_statistics.map(Protocol.ML_Statistics_Marker.apply)),
67008
eed58245b579 superseded by plain_identify;
wenzelm
parents: 66880
diff changeset
   116
          XZ.options(6))
65657
2773b6859c55 download Jenkins logs with inlined ml_statistics;
wenzelm
parents: 65656
diff changeset
   117
      }
2773b6859c55 download Jenkins logs with inlined ml_statistics;
wenzelm
parents: 65656
diff changeset
   118
    }
63646
74604a9fc4c8 API for Isabelle Jenkins continuous integration services;
wenzelm
parents:
diff changeset
   119
  }
74604a9fc4c8 API for Isabelle Jenkins continuous integration services;
wenzelm
parents:
diff changeset
   120
65654
0fbaa9286331 tuned signature;
wenzelm
parents: 65653
diff changeset
   121
  def build_job_infos(job_name: String): List[Job_Info] =
63646
74604a9fc4c8 API for Isabelle Jenkins continuous integration services;
wenzelm
parents:
diff changeset
   122
  {
65653
f433c0e73307 read ml_statistics from session logs: .db or .gz files;
wenzelm
parents: 65652
diff changeset
   123
    val Session_Log = new Regex("""^.*/log/([^/]+)\.(db|gz)$""")
63646
74604a9fc4c8 API for Isabelle Jenkins continuous integration services;
wenzelm
parents:
diff changeset
   124
65659
293141fb093d ensure canonical order: latest first;
wenzelm
parents: 65658
diff changeset
   125
    val infos =
293141fb093d ensure canonical order: latest first;
wenzelm
parents: 65658
diff changeset
   126
      for {
293141fb093d ensure canonical order: latest first;
wenzelm
parents: 65658
diff changeset
   127
        build <-
293141fb093d ensure canonical order: latest first;
wenzelm
parents: 65658
diff changeset
   128
          JSON.array(
67008
eed58245b579 superseded by plain_identify;
wenzelm
parents: 66880
diff changeset
   129
            invoke(root() + "/job/" + job_name, "tree=allBuilds[number,timestamp,artifacts[*]]"),
65659
293141fb093d ensure canonical order: latest first;
wenzelm
parents: 65658
diff changeset
   130
            "allBuilds").getOrElse(Nil)
293141fb093d ensure canonical order: latest first;
wenzelm
parents: 65658
diff changeset
   131
        number <- JSON.int(build, "number")
293141fb093d ensure canonical order: latest first;
wenzelm
parents: 65658
diff changeset
   132
        timestamp <- JSON.long(build, "timestamp")
293141fb093d ensure canonical order: latest first;
wenzelm
parents: 65658
diff changeset
   133
      } yield {
67008
eed58245b579 superseded by plain_identify;
wenzelm
parents: 66880
diff changeset
   134
        val job_prefix = root() + "/job/" + job_name + "/" + number
65659
293141fb093d ensure canonical order: latest first;
wenzelm
parents: 65658
diff changeset
   135
        val main_log = Url(job_prefix + "/consoleText")
293141fb093d ensure canonical order: latest first;
wenzelm
parents: 65658
diff changeset
   136
        val session_logs =
67008
eed58245b579 superseded by plain_identify;
wenzelm
parents: 66880
diff changeset
   137
          for {
eed58245b579 superseded by plain_identify;
wenzelm
parents: 66880
diff changeset
   138
            artifact <- JSON.array(build, "artifacts").getOrElse(Nil)
eed58245b579 superseded by plain_identify;
wenzelm
parents: 66880
diff changeset
   139
            log_path <- JSON.string(artifact, "relativePath")
eed58245b579 superseded by plain_identify;
wenzelm
parents: 66880
diff changeset
   140
            (name, ext) <- (log_path match { case Session_Log(a, b) => Some((a, b)) case _ => None })
eed58245b579 superseded by plain_identify;
wenzelm
parents: 66880
diff changeset
   141
          } yield (name, ext, Url(job_prefix + "/artifact/" + log_path))
eed58245b579 superseded by plain_identify;
wenzelm
parents: 66880
diff changeset
   142
        Job_Info(job_name, timestamp, main_log, session_logs)
65659
293141fb093d ensure canonical order: latest first;
wenzelm
parents: 65658
diff changeset
   143
      }
293141fb093d ensure canonical order: latest first;
wenzelm
parents: 65658
diff changeset
   144
293141fb093d ensure canonical order: latest first;
wenzelm
parents: 65658
diff changeset
   145
    infos.sortBy(info => - info.timestamp)
63646
74604a9fc4c8 API for Isabelle Jenkins continuous integration services;
wenzelm
parents:
diff changeset
   146
  }
74604a9fc4c8 API for Isabelle Jenkins continuous integration services;
wenzelm
parents:
diff changeset
   147
}