avoid odd (harmless) problem with Mercurial 4.5.3 provided by Ubuntu 18.04 on first push: "couldn't write revision branch cache names";
authorwenzelm
Sat, 14 Dec 2019 16:56:26 +0100
changeset 71280 5a2033fc8f3d
parent 71279 2e873da296ae
child 71281 5b3a813853bb
avoid odd (harmless) problem with Mercurial 4.5.3 provided by Ubuntu 18.04 on first push: "couldn't write revision branch cache names";
src/Doc/System/Phabricator.thy
src/Pure/System/linux.scala
src/Pure/Tools/phabricator.scala
--- a/src/Doc/System/Phabricator.thy	Thu Dec 12 16:12:17 2019 +0100
+++ b/src/Doc/System/Phabricator.thy	Sat Dec 14 16:56:26 2019 +0100
@@ -86,7 +86,7 @@
 
   The initial setup works as follows (with full Linux package upgrade):
 
-  @{verbatim [display] \<open>  isabelle phabricator_setup -U\<close>}
+  @{verbatim [display] \<open>  isabelle phabricator_setup -U -M:\<close>}
 
   After installing many packages, cloning the Phabricator distribution,
   initializing the MySQL database and Apache, the tool prints an URL for
@@ -353,6 +353,7 @@
   @{verbatim [display] \<open>Usage: isabelle phabricator_setup [OPTIONS]
 
   Options are:
+    -M SOURCE    install Mercurial from source: local PATH, or URL, or ":"
     -R DIR       repository directory (default: "/var/www/phabricator-NAME/repo")
     -U           full update of system packages before installation
     -n NAME      Phabricator installation name (default: "vcs")
@@ -382,6 +383,12 @@
   Option \<^verbatim>\<open>-U\<close> ensures a full update of system packages, before installing
   further packages required by Phabricator. This might require to reboot.
 
+  Option \<^verbatim>\<open>-M:\<close> installs a standard Mercurial release from source: this works
+  better than the package provided by Ubuntu 18.04. Alternatively, an explicit
+  file path or URL the source archive (\<^verbatim>\<open>.tar.gz\<close>) may be here. This option is
+  recommended for production use, but it requires to \<^emph>\<open>uninstall\<close> existing
+  Mercurial packages provided by the operating system.
+
   Option \<^verbatim>\<open>-n\<close> provides an alternative installation name. The default name
   \<^verbatim>\<open>vcs\<close> means ``version control system''. The name appears in the URL for SSH
   access, and thus has some relevance to end-users. The initial server URL
--- a/src/Pure/System/linux.scala	Thu Dec 12 16:12:17 2019 +0100
+++ b/src/Pure/System/linux.scala	Sat Dec 14 16:56:26 2019 +0100
@@ -70,6 +70,9 @@
   def package_install(packages: List[String], progress: Progress = No_Progress): Unit =
     progress.bash("apt-get install -y -- " + Bash.strings(packages), echo = true).check
 
+  def package_installed(name: String): Boolean =
+    Isabelle_System.bash("dpkg-query -s " + Bash.string(name)).ok
+
 
   /* users */
 
--- a/src/Pure/Tools/phabricator.scala	Thu Dec 12 16:12:17 2019 +0100
+++ b/src/Pure/Tools/phabricator.scala	Sat Dec 14 16:56:26 2019 +0100
@@ -27,7 +27,9 @@
       "git", "mysql-server", "apache2", "libapache2-mod-php", "php", "php-mysql",
       "php-gd", "php-curl", "php-apcu", "php-cli", "php-json", "php-mbstring",
       // more packages
-      "php-zip", "python-pygments", "ssh", "subversion", "mercurial")
+      "php-zip", "python-pygments", "ssh", "subversion",
+      // mercurial build packages
+      "make", "gcc", "python", "python-dev", "python-docutils", "python-pygments", "python-openssl")
 
 
   /* global system resources */
@@ -60,6 +62,8 @@
   val alternative_system_port = 222
   val default_server_port = 2222
 
+  val standard_mercurial_source = "https://www.mercurial-scm.org/release/mercurial-5.2.1.tar.gz"
+
 
 
   /** global configuration **/
@@ -185,11 +189,38 @@
     command
   }
 
+  def mercurial_setup(mercurial_source: String, progress: Progress = No_Progress)
+  {
+    progress.echo("\nBuilding Mercurial from source: " + quote(mercurial_source))
+    Isabelle_System.with_tmp_dir("mercurial")(tmp_dir =>
+    {
+      val archive =
+        if (Url.is_wellformed(mercurial_source)) {
+          val archive = tmp_dir + Path.basic("mercurial.tar.gz")
+          Bytes.write(archive, Url.read_bytes(Url(mercurial_source)))
+          archive
+        }
+        else Path.explode(mercurial_source)
+
+      Isabelle_System.gnutar("-xzf " + File.bash_path(archive), dir = tmp_dir).check
+
+      File.read_dir(tmp_dir).filter(name => (tmp_dir + Path.basic(name)).is_dir) match {
+        case List(dir) =>
+          val build_dir = tmp_dir + Path.basic(dir)
+          progress.bash("make all && make install", cwd = build_dir.file, echo = true).check
+        case dirs =>
+          error("Bad archive " + archive +
+            (if (dirs.isEmpty) "" else "\nmultiple directory entries " + commas_quote(dirs)))
+      }
+    })
+  }
+
   def phabricator_setup(
     name: String = default_name,
     root: String = "",
     repo: String = "",
     package_update: Boolean = false,
+    mercurial_source: String = "",
     progress: Progress = No_Progress)
   {
     /* system environment */
@@ -207,6 +238,15 @@
     Linux.check_reboot_required()
 
 
+    if (mercurial_source.nonEmpty) {
+      for { name <- List("mercurial", "mercurial-common") if Linux.package_installed(name) } {
+        error("Cannot install Mercurial from source:" +
+          "package package " + quote(name) + " already installed")
+      }
+      mercurial_setup(mercurial_source, progress = progress)
+    }
+
+
     /* users */
 
     if (name.contains((c: Char) => !(Symbol.is_ascii_letter(c) || Symbol.is_ascii_digit(c))) ||
@@ -271,8 +311,8 @@
     val sudoers_file =
       Path.explode("/etc/sudoers.d") + Path.basic(isabelle_phabricator_name(name = name))
     File.write(sudoers_file,
-      www_user + " ALL=(" + daemon_user + ") SETENV: NOPASSWD: /usr/bin/git, /usr/bin/hg, /usr/bin/ssh, /usr/bin/id\n" +
-      name + " ALL=(" + daemon_user + ") SETENV: NOPASSWD: /usr/bin/git, /usr/bin/git-upload-pack, /usr/bin/git-receive-pack, /usr/bin/hg, /usr/bin/svnserve, /usr/bin/ssh, /usr/bin/id\n")
+      www_user + " ALL=(" + daemon_user + ") SETENV: NOPASSWD: /usr/bin/git, /usr/local/bin/hg, /usr/bin/hg, /usr/bin/ssh, /usr/bin/id\n" +
+      name + " ALL=(" + daemon_user + ") SETENV: NOPASSWD: /usr/bin/git, /usr/bin/git-upload-pack, /usr/bin/git-receive-pack, /usr/local/bin/hg, /usr/bin/hg, /usr/bin/svnserve, /usr/bin/ssh, /usr/bin/id\n")
 
     Isabelle_System.chmod("440", sudoers_file)
 
@@ -443,6 +483,7 @@
   val isabelle_tool2 =
     Isabelle_Tool("phabricator_setup", "setup Phabricator server on Ubuntu Linux", args =>
     {
+      var mercurial_source = ""
       var repo = ""
       var package_update = false
       var name = default_name
@@ -453,6 +494,8 @@
 Usage: isabelle phabricator_setup [OPTIONS]
 
   Options are:
+    -M SOURCE    install Mercurial from source: local PATH, or URL, or ":" for
+                 """ + standard_mercurial_source + """
     -R DIR       repository directory (default: """ + default_repo("NAME") + """)
     -U           full update of system packages before installation
     -n NAME      Phabricator installation name (default: """ + quote(default_name) + """)
@@ -463,6 +506,7 @@
   The installation name (default: """ + quote(default_name) + """) is mapped to a regular
   Unix user; this is relevant for public SSH access.
 """,
+          "M:" -> (arg => mercurial_source = (if (arg == ":") standard_mercurial_source else arg)),
           "R:" -> (arg => repo = arg),
           "U" -> (_ => package_update = true),
           "n:" -> (arg => name = arg),
@@ -477,7 +521,7 @@
       if (!release.is_ubuntu_18_04) error("Bad Linux version: Ubuntu 18.04 LTS required")
 
       phabricator_setup(name = name, root = root, repo = repo,
-        package_update = package_update, progress = progress)
+        package_update = package_update, mercurial_source = mercurial_source, progress = progress)
     })