| author | wenzelm | 
| Fri, 25 Aug 2023 15:31:14 +0200 | |
| changeset 78577 | a945b541efff | 
| parent 77567 | b975f5aaf6b8 | 
| child 78602 | 92b6958e8787 | 
| permissions | -rw-r--r-- | 
| 70967 | 1  | 
/* Title: Pure/Tools/phabricator.scala  | 
2  | 
Author: Makarius  | 
|
3  | 
||
| 
76104
 
5ee70e689eb3
proper comment: Phabricator remains on Ubuntu 20.04, which is still required as build environment for old Mercurial 3.9.2 with Python 2;
 
wenzelm 
parents: 
75659 
diff
changeset
 | 
4  | 
Support for Phabricator server, notably for Ubuntu 20.04 LTS.  | 
| 
71068
 
510b89906d86
discontinued somewhat pointless Isabelle options: setup implicitly assumes Ubuntu 18.04;
 
wenzelm 
parents: 
71066 
diff
changeset
 | 
5  | 
|
| 
 
510b89906d86
discontinued somewhat pointless Isabelle options: setup implicitly assumes Ubuntu 18.04;
 
wenzelm 
parents: 
71066 
diff
changeset
 | 
6  | 
See also:  | 
| 70967 | 7  | 
- https://www.phacility.com/phabricator  | 
8  | 
- https://secure.phabricator.com/book/phabricator  | 
|
9  | 
*/  | 
|
10  | 
||
11  | 
package isabelle  | 
|
12  | 
||
13  | 
||
| 71330 | 14  | 
import scala.collection.mutable  | 
| 70969 | 15  | 
import scala.util.matching.Regex  | 
16  | 
||
17  | 
||
| 75393 | 18  | 
object Phabricator {
 | 
| 70967 | 19  | 
/** defaults **/  | 
20  | 
||
| 71049 | 21  | 
/* required packages */  | 
22  | 
||
| 
73534
 
e7fb17bca374
discontinue old Ubuntu 18.04 LTS, e.g. it cannot build documentation "prog-prove";
 
wenzelm 
parents: 
73415 
diff
changeset
 | 
23  | 
val packages_ubuntu_20_04: List[String] =  | 
| 
77567
 
b975f5aaf6b8
renamed "isabelle build_docker" to "isabelle docker_build" (unrelated to "isabelle build");
 
wenzelm 
parents: 
77369 
diff
changeset
 | 
24  | 
Docker_Build.packages :::  | 
| 71049 | 25  | 
List(  | 
26  | 
// https://secure.phabricator.com/source/phabricator/browse/master/scripts/install/install_ubuntu.sh 15e6e2adea61  | 
|
27  | 
"git", "mysql-server", "apache2", "libapache2-mod-php", "php", "php-mysql",  | 
|
28  | 
"php-gd", "php-curl", "php-apcu", "php-cli", "php-json", "php-mbstring",  | 
|
29  | 
// more packages  | 
|
| 
73534
 
e7fb17bca374
discontinue old Ubuntu 18.04 LTS, e.g. it cannot build documentation "prog-prove";
 
wenzelm 
parents: 
73415 
diff
changeset
 | 
30  | 
"php-xml", "php-zip", "python3-pygments", "ssh", "subversion", "python-pygments",  | 
| 
71280
 
5a2033fc8f3d
avoid odd (harmless) problem with Mercurial 4.5.3 provided by Ubuntu 18.04 on first push: "couldn't write revision branch cache names";
 
wenzelm 
parents: 
71277 
diff
changeset
 | 
31  | 
// mercurial build packages  | 
| 
73534
 
e7fb17bca374
discontinue old Ubuntu 18.04 LTS, e.g. it cannot build documentation "prog-prove";
 
wenzelm 
parents: 
73415 
diff
changeset
 | 
32  | 
"make", "gcc", "python", "python2-dev", "python-docutils", "python-openssl")  | 
| 72521 | 33  | 
|
| 75393 | 34  | 
  def packages: List[String] = {
 | 
| 72521 | 35  | 
val release = Linux.Release()  | 
| 
73534
 
e7fb17bca374
discontinue old Ubuntu 18.04 LTS, e.g. it cannot build documentation "prog-prove";
 
wenzelm 
parents: 
73415 
diff
changeset
 | 
36  | 
if (release.is_ubuntu_20_04) packages_ubuntu_20_04  | 
| 
 
e7fb17bca374
discontinue old Ubuntu 18.04 LTS, e.g. it cannot build documentation "prog-prove";
 
wenzelm 
parents: 
73415 
diff
changeset
 | 
37  | 
    else error("Bad Linux version: expected Ubuntu 20.04 LTS")
 | 
| 72521 | 38  | 
}  | 
39  | 
||
| 71049 | 40  | 
|
41  | 
/* global system resources */  | 
|
42  | 
||
| 
71068
 
510b89906d86
discontinued somewhat pointless Isabelle options: setup implicitly assumes Ubuntu 18.04;
 
wenzelm 
parents: 
71066 
diff
changeset
 | 
43  | 
val www_user = "www-data"  | 
| 
 
510b89906d86
discontinued somewhat pointless Isabelle options: setup implicitly assumes Ubuntu 18.04;
 
wenzelm 
parents: 
71066 
diff
changeset
 | 
44  | 
|
| 71049 | 45  | 
val daemon_user = "phabricator"  | 
46  | 
||
| 71601 | 47  | 
  val sshd_config: Path = Path.explode("/etc/ssh/sshd_config")
 | 
| 71049 | 48  | 
|
49  | 
||
50  | 
/* installation parameters */  | 
|
51  | 
||
| 70967 | 52  | 
val default_name = "vcs"  | 
53  | 
||
| 
71052
 
6bf53035baf0
clarified name prefixes: global config always uses "isabelle-phabricator";
 
wenzelm 
parents: 
71051 
diff
changeset
 | 
54  | 
def phabricator_name(name: String = "", ext: String = ""): String =  | 
| 77368 | 55  | 
"phabricator" + if_proper(name, "-" + name) + if_proper(ext, "." + ext)  | 
| 
71052
 
6bf53035baf0
clarified name prefixes: global config always uses "isabelle-phabricator";
 
wenzelm 
parents: 
71051 
diff
changeset
 | 
56  | 
|
| 
 
6bf53035baf0
clarified name prefixes: global config always uses "isabelle-phabricator";
 
wenzelm 
parents: 
71051 
diff
changeset
 | 
57  | 
def isabelle_phabricator_name(name: String = "", ext: String = ""): String =  | 
| 
 
6bf53035baf0
clarified name prefixes: global config always uses "isabelle-phabricator";
 
wenzelm 
parents: 
71051 
diff
changeset
 | 
58  | 
"isabelle-" + phabricator_name(name = name, ext = ext)  | 
| 70967 | 59  | 
|
| 
71068
 
510b89906d86
discontinued somewhat pointless Isabelle options: setup implicitly assumes Ubuntu 18.04;
 
wenzelm 
parents: 
71066 
diff
changeset
 | 
60  | 
def default_root(name: String): Path =  | 
| 
 
510b89906d86
discontinued somewhat pointless Isabelle options: setup implicitly assumes Ubuntu 18.04;
 
wenzelm 
parents: 
71066 
diff
changeset
 | 
61  | 
    Path.explode("/var/www") + Path.basic(phabricator_name(name = name))
 | 
| 70967 | 62  | 
|
| 
71068
 
510b89906d86
discontinued somewhat pointless Isabelle options: setup implicitly assumes Ubuntu 18.04;
 
wenzelm 
parents: 
71066 
diff
changeset
 | 
63  | 
  def default_repo(name: String): Path = default_root(name) + Path.basic("repo")
 | 
| 70967 | 64  | 
|
| 71072 | 65  | 
  val default_mailers: Path = Path.explode("mailers.json")
 | 
| 71066 | 66  | 
|
| 76129 | 67  | 
val default_system_port: Int = 22  | 
| 
71109
 
8c1c717a830b
configure SSH hosting via "isabelle phabricator_setup_ssh";
 
wenzelm 
parents: 
71103 
diff
changeset
 | 
68  | 
val alternative_system_port = 222  | 
| 
 
8c1c717a830b
configure SSH hosting via "isabelle phabricator_setup_ssh";
 
wenzelm 
parents: 
71103 
diff
changeset
 | 
69  | 
val default_server_port = 2222  | 
| 
 
8c1c717a830b
configure SSH hosting via "isabelle phabricator_setup_ssh";
 
wenzelm 
parents: 
71103 
diff
changeset
 | 
70  | 
|
| 
71440
 
8b0b8b9ea653
afford newer Mercurial version, just before odd problems in 4.0 and 4.1;
 
wenzelm 
parents: 
71439 
diff
changeset
 | 
71  | 
val standard_mercurial_source = "https://www.mercurial-scm.org/release/mercurial-3.9.2.tar.gz"  | 
| 
71280
 
5a2033fc8f3d
avoid odd (harmless) problem with Mercurial 4.5.3 provided by Ubuntu 18.04 on first push: "couldn't write revision branch cache names";
 
wenzelm 
parents: 
71277 
diff
changeset
 | 
72  | 
|
| 70967 | 73  | 
|
74  | 
||
75  | 
/** global configuration **/  | 
|
76  | 
||
| 71601 | 77  | 
  val global_config: Path = Path.explode("/etc/" + isabelle_phabricator_name(ext = "conf"))
 | 
| 70967 | 78  | 
|
| 71122 | 79  | 
def global_config_script(  | 
80  | 
init: String = "",  | 
|
81  | 
body: String = "",  | 
|
| 75393 | 82  | 
    exit: String = ""): String = {
 | 
| 71282 | 83  | 
"""#!/bin/bash  | 
| 77369 | 84  | 
""" + if_proper(init, "\n" + init) + """  | 
| 71284 | 85  | 
{
 | 
| 71122 | 86  | 
  while { unset REPLY; read -r; test "$?" = 0 -o -n "$REPLY"; }
 | 
87  | 
do  | 
|
88  | 
NAME="$(echo "$REPLY" | cut -d: -f1)"  | 
|
89  | 
ROOT="$(echo "$REPLY" | cut -d: -f2)"  | 
|
| 71284 | 90  | 
    {
 | 
| 73736 | 91  | 
""" + Library.indent_lines(6, body) + """  | 
| 71284 | 92  | 
} < /dev/null  | 
93  | 
done  | 
|
94  | 
} < """ + File.bash_path(global_config) + "\n" +  | 
|
| 77369 | 95  | 
if_proper(exit, "\n" + exit + "\n")  | 
| 71122 | 96  | 
}  | 
97  | 
||
| 75393 | 98  | 
  sealed case class Config(name: String, root: Path) {
 | 
| 
71052
 
6bf53035baf0
clarified name prefixes: global config always uses "isabelle-phabricator";
 
wenzelm 
parents: 
71051 
diff
changeset
 | 
99  | 
def home: Path = root + Path.explode(phabricator_name())  | 
| 70969 | 100  | 
|
101  | 
def execute(command: String): Process_Result =  | 
|
| 71102 | 102  | 
      Isabelle_System.bash("bin/" + command, cwd = home.file, redirect = true).check
 | 
| 70968 | 103  | 
}  | 
| 70967 | 104  | 
|
| 75393 | 105  | 
  def read_config(): List[Config] = {
 | 
| 70967 | 106  | 
    if (global_config.is_file) {
 | 
107  | 
for (entry <- Library.trim_split_lines(File.read(global_config)) if entry.nonEmpty)  | 
|
108  | 
      yield {
 | 
|
109  | 
        space_explode(':', entry) match {
 | 
|
110  | 
case List(name, root) => Config(name, Path.explode(root))  | 
|
111  | 
          case _ => error("Malformed config file " + global_config + "\nentry " + quote(entry))
 | 
|
112  | 
}  | 
|
113  | 
}  | 
|
114  | 
}  | 
|
115  | 
else Nil  | 
|
116  | 
}  | 
|
117  | 
||
| 75393 | 118  | 
  def write_config(configs: List[Config]): Unit = {
 | 
| 70967 | 119  | 
File.write(global_config,  | 
120  | 
      configs.map(config => config.name + ":" + config.root.implode).mkString("", "\n", "\n"))
 | 
|
121  | 
}  | 
|
122  | 
||
123  | 
def get_config(name: String): Config =  | 
|
124  | 
read_config().find(config => config.name == name) getOrElse  | 
|
125  | 
      error("Bad Isabelle/Phabricator installation " + quote(name))
 | 
|
126  | 
||
127  | 
||
128  | 
||
| 71299 | 129  | 
/** administrative tools **/  | 
| 71097 | 130  | 
|
131  | 
/* Isabelle tool wrapper */  | 
|
132  | 
||
133  | 
val isabelle_tool1 =  | 
|
| 72763 | 134  | 
    Isabelle_Tool("phabricator", "invoke command-line tool within Phabricator home directory",
 | 
| 75393 | 135  | 
Scala_Project.here,  | 
| 75394 | 136  | 
      { args =>
 | 
137  | 
var list = false  | 
|
138  | 
var name = default_name  | 
|
| 71097 | 139  | 
|
| 75394 | 140  | 
        val getopts = Getopts("""
 | 
| 71097 | 141  | 
Usage: isabelle phabricator [OPTIONS] COMMAND [ARGS...]  | 
142  | 
||
143  | 
Options are:  | 
|
| 71101 | 144  | 
-l list available Phabricator installations  | 
| 71097 | 145  | 
-n NAME Phabricator installation name (default: """ + quote(default_name) + """)  | 
146  | 
||
| 71103 | 147  | 
Invoke a command-line tool within the home directory of the named  | 
148  | 
Phabricator installation.  | 
|
| 71097 | 149  | 
""",  | 
| 71101 | 150  | 
"l" -> (_ => list = true),  | 
| 71097 | 151  | 
"n:" -> (arg => name = arg))  | 
152  | 
||
| 75394 | 153  | 
val more_args = getopts(args)  | 
154  | 
if (more_args.isEmpty && !list) getopts.usage()  | 
|
| 71097 | 155  | 
|
| 75394 | 156  | 
val progress = new Console_Progress  | 
| 71097 | 157  | 
|
| 75394 | 158  | 
        if (list) {
 | 
159  | 
          for (config <- read_config()) {
 | 
|
160  | 
            progress.echo("phabricator " + quote(config.name) + " root " + config.root)
 | 
|
161  | 
}  | 
|
| 71101 | 162  | 
}  | 
| 75394 | 163  | 
        else {
 | 
164  | 
val config = get_config(name)  | 
|
165  | 
val result = progress.bash(Bash.strings(more_args), cwd = config.home.file, echo = true)  | 
|
166  | 
if (!result.ok) error(result.print_return_code)  | 
|
167  | 
}  | 
|
168  | 
})  | 
|
| 71097 | 169  | 
|
170  | 
||
171  | 
||
| 70967 | 172  | 
/** setup **/  | 
173  | 
||
| 75393 | 174  | 
  def user_setup(name: String, description: String, ssh_setup: Boolean = false): Unit = {
 | 
| 71049 | 175  | 
    if (!Linux.user_exists(name)) {
 | 
| 
71054
 
b64fc38327ae
prefer system user setup, e.g. avoid occurrence on login screen;
 
wenzelm 
parents: 
71053 
diff
changeset
 | 
176  | 
Linux.user_add(name, description = description, system = true, ssh_setup = ssh_setup)  | 
| 71049 | 177  | 
}  | 
178  | 
    else if (Linux.user_description(name) != description) {
 | 
|
179  | 
      error("User " + quote(name) + " already exists --" +
 | 
|
180  | 
" for Phabricator it should have the description:\n " + quote(description))  | 
|
181  | 
}  | 
|
182  | 
}  | 
|
183  | 
||
| 71282 | 184  | 
def command_setup(name: String,  | 
185  | 
init: String = "",  | 
|
186  | 
body: String = "",  | 
|
| 75393 | 187  | 
exit: String = ""  | 
188  | 
  ): Path = {
 | 
|
| 71270 | 189  | 
    val command = Path.explode("/usr/local/bin") + Path.basic(name)
 | 
| 71282 | 190  | 
File.write(command, global_config_script(init = init, body = body, exit = exit))  | 
| 71270 | 191  | 
    Isabelle_System.chmod("755", command)
 | 
192  | 
    Isabelle_System.chown("root:root", command)
 | 
|
193  | 
command  | 
|
194  | 
}  | 
|
195  | 
||
| 75393 | 196  | 
  def mercurial_setup(mercurial_source: String, progress: Progress = new Progress): Unit = {
 | 
| 71281 | 197  | 
    progress.echo("\nMercurial installation from source " + quote(mercurial_source) + " ...")
 | 
| 75394 | 198  | 
    Isabelle_System.with_tmp_dir("mercurial") { tmp_dir =>
 | 
| 
71280
 
5a2033fc8f3d
avoid odd (harmless) problem with Mercurial 4.5.3 provided by Ubuntu 18.04 on first push: "couldn't write revision branch cache names";
 
wenzelm 
parents: 
71277 
diff
changeset
 | 
199  | 
val archive =  | 
| 
 
5a2033fc8f3d
avoid odd (harmless) problem with Mercurial 4.5.3 provided by Ubuntu 18.04 on first push: "couldn't write revision branch cache names";
 
wenzelm 
parents: 
71277 
diff
changeset
 | 
200  | 
        if (Url.is_wellformed(mercurial_source)) {
 | 
| 
 
5a2033fc8f3d
avoid odd (harmless) problem with Mercurial 4.5.3 provided by Ubuntu 18.04 on first push: "couldn't write revision branch cache names";
 
wenzelm 
parents: 
71277 
diff
changeset
 | 
201  | 
          val archive = tmp_dir + Path.basic("mercurial.tar.gz")
 | 
| 73566 | 202  | 
Isabelle_System.download_file(mercurial_source, archive)  | 
| 
71280
 
5a2033fc8f3d
avoid odd (harmless) problem with Mercurial 4.5.3 provided by Ubuntu 18.04 on first push: "couldn't write revision branch cache names";
 
wenzelm 
parents: 
71277 
diff
changeset
 | 
203  | 
archive  | 
| 
 
5a2033fc8f3d
avoid odd (harmless) problem with Mercurial 4.5.3 provided by Ubuntu 18.04 on first push: "couldn't write revision branch cache names";
 
wenzelm 
parents: 
71277 
diff
changeset
 | 
204  | 
}  | 
| 
 
5a2033fc8f3d
avoid odd (harmless) problem with Mercurial 4.5.3 provided by Ubuntu 18.04 on first push: "couldn't write revision branch cache names";
 
wenzelm 
parents: 
71277 
diff
changeset
 | 
205  | 
else Path.explode(mercurial_source)  | 
| 
 
5a2033fc8f3d
avoid odd (harmless) problem with Mercurial 4.5.3 provided by Ubuntu 18.04 on first push: "couldn't write revision branch cache names";
 
wenzelm 
parents: 
71277 
diff
changeset
 | 
206  | 
|
| 
76540
 
83de6e9ae983
clarified signature: prefer Scala functions instead of shell scripts;
 
wenzelm 
parents: 
76529 
diff
changeset
 | 
207  | 
Isabelle_System.extract(archive, tmp_dir)  | 
| 76529 | 208  | 
val build_dir = File.get_dir(tmp_dir, title = mercurial_source)  | 
| 
71280
 
5a2033fc8f3d
avoid odd (harmless) problem with Mercurial 4.5.3 provided by Ubuntu 18.04 on first push: "couldn't write revision branch cache names";
 
wenzelm 
parents: 
71277 
diff
changeset
 | 
209  | 
|
| 72442 | 210  | 
      progress.bash("make all && make install", cwd = build_dir.file, echo = true).check
 | 
| 75394 | 211  | 
}  | 
| 
71280
 
5a2033fc8f3d
avoid odd (harmless) problem with Mercurial 4.5.3 provided by Ubuntu 18.04 on first push: "couldn't write revision branch cache names";
 
wenzelm 
parents: 
71277 
diff
changeset
 | 
212  | 
}  | 
| 
 
5a2033fc8f3d
avoid odd (harmless) problem with Mercurial 4.5.3 provided by Ubuntu 18.04 on first push: "couldn't write revision branch cache names";
 
wenzelm 
parents: 
71277 
diff
changeset
 | 
213  | 
|
| 70967 | 214  | 
def phabricator_setup(  | 
| 
71422
 
5d5be87330b5
allow to override repository versions at runtime;
 
wenzelm 
parents: 
71421 
diff
changeset
 | 
215  | 
options: Options,  | 
| 70967 | 216  | 
name: String = default_name,  | 
217  | 
root: String = "",  | 
|
218  | 
repo: String = "",  | 
|
| 71047 | 219  | 
package_update: Boolean = false,  | 
| 
71280
 
5a2033fc8f3d
avoid odd (harmless) problem with Mercurial 4.5.3 provided by Ubuntu 18.04 on first push: "couldn't write revision branch cache names";
 
wenzelm 
parents: 
71277 
diff
changeset
 | 
220  | 
mercurial_source: String = "",  | 
| 75393 | 221  | 
progress: Progress = new Progress  | 
222  | 
  ): Unit = {
 | 
|
| 70967 | 223  | 
/* system environment */  | 
224  | 
||
225  | 
Linux.check_system_root()  | 
|
226  | 
||
| 71079 | 227  | 
    progress.echo("System packages ...")
 | 
228  | 
||
| 71047 | 229  | 
    if (package_update) {
 | 
230  | 
Linux.package_update(progress = progress)  | 
|
231  | 
Linux.check_reboot_required()  | 
|
232  | 
}  | 
|
| 70967 | 233  | 
|
234  | 
Linux.package_install(packages, progress = progress)  | 
|
235  | 
Linux.check_reboot_required()  | 
|
236  | 
||
237  | 
||
| 
71280
 
5a2033fc8f3d
avoid odd (harmless) problem with Mercurial 4.5.3 provided by Ubuntu 18.04 on first push: "couldn't write revision branch cache names";
 
wenzelm 
parents: 
71277 
diff
changeset
 | 
238  | 
    if (mercurial_source.nonEmpty) {
 | 
| 
 
5a2033fc8f3d
avoid odd (harmless) problem with Mercurial 4.5.3 provided by Ubuntu 18.04 on first push: "couldn't write revision branch cache names";
 
wenzelm 
parents: 
71277 
diff
changeset
 | 
239  | 
      for { name <- List("mercurial", "mercurial-common") if Linux.package_installed(name) } {
 | 
| 71326 | 240  | 
        error("Cannot install Mercurial from source:\n" +
 | 
| 
71280
 
5a2033fc8f3d
avoid odd (harmless) problem with Mercurial 4.5.3 provided by Ubuntu 18.04 on first push: "couldn't write revision branch cache names";
 
wenzelm 
parents: 
71277 
diff
changeset
 | 
241  | 
"package package " + quote(name) + " already installed")  | 
| 
 
5a2033fc8f3d
avoid odd (harmless) problem with Mercurial 4.5.3 provided by Ubuntu 18.04 on first push: "couldn't write revision branch cache names";
 
wenzelm 
parents: 
71277 
diff
changeset
 | 
242  | 
}  | 
| 
 
5a2033fc8f3d
avoid odd (harmless) problem with Mercurial 4.5.3 provided by Ubuntu 18.04 on first push: "couldn't write revision branch cache names";
 
wenzelm 
parents: 
71277 
diff
changeset
 | 
243  | 
mercurial_setup(mercurial_source, progress = progress)  | 
| 
 
5a2033fc8f3d
avoid odd (harmless) problem with Mercurial 4.5.3 provided by Ubuntu 18.04 on first push: "couldn't write revision branch cache names";
 
wenzelm 
parents: 
71277 
diff
changeset
 | 
244  | 
}  | 
| 
 
5a2033fc8f3d
avoid odd (harmless) problem with Mercurial 4.5.3 provided by Ubuntu 18.04 on first push: "couldn't write revision branch cache names";
 
wenzelm 
parents: 
71277 
diff
changeset
 | 
245  | 
|
| 
 
5a2033fc8f3d
avoid odd (harmless) problem with Mercurial 4.5.3 provided by Ubuntu 18.04 on first push: "couldn't write revision branch cache names";
 
wenzelm 
parents: 
71277 
diff
changeset
 | 
246  | 
|
| 71049 | 247  | 
/* users */  | 
248  | 
||
| 72520 | 249  | 
if (name.exists((c: Char) => !(Symbol.is_ascii_letter(c) || Symbol.is_ascii_digit(c))) ||  | 
| 71269 | 250  | 
        Set("", "ssh", "phd", "dump", daemon_user).contains(name)) {
 | 
| 71125 | 251  | 
      error("Bad installation name: " + quote(name))
 | 
| 71049 | 252  | 
}  | 
253  | 
||
254  | 
user_setup(daemon_user, "Phabricator Daemon User", ssh_setup = true)  | 
|
255  | 
user_setup(name, "Phabricator SSH User")  | 
|
256  | 
||
257  | 
||
| 70967 | 258  | 
/* basic installation */  | 
259  | 
||
| 71079 | 260  | 
    progress.echo("\nPhabricator installation ...")
 | 
| 71076 | 261  | 
|
| 
71068
 
510b89906d86
discontinued somewhat pointless Isabelle options: setup implicitly assumes Ubuntu 18.04;
 
wenzelm 
parents: 
71066 
diff
changeset
 | 
262  | 
val root_path = if (root.nonEmpty) Path.explode(root) else default_root(name)  | 
| 
 
510b89906d86
discontinued somewhat pointless Isabelle options: setup implicitly assumes Ubuntu 18.04;
 
wenzelm 
parents: 
71066 
diff
changeset
 | 
263  | 
val repo_path = if (repo.nonEmpty) Path.explode(repo) else default_repo(name)  | 
| 70967 | 264  | 
|
265  | 
val configs = read_config()  | 
|
266  | 
||
267  | 
    for (config <- configs if config.name == name) {
 | 
|
268  | 
      error("Duplicate Phabricator installation " + quote(name) + " in " + config.root)
 | 
|
269  | 
}  | 
|
270  | 
||
271  | 
    if (!Isabelle_System.bash("mkdir -p " + File.bash_path(root_path)).ok) {
 | 
|
272  | 
      error("Failed to create root directory " + root_path)
 | 
|
273  | 
}  | 
|
274  | 
||
| 71116 | 275  | 
Isabelle_System.chown(Bash.string(www_user) + ":" + Bash.string(www_user), root_path)  | 
276  | 
    Isabelle_System.chmod("755", root_path)
 | 
|
277  | 
||
| 70967 | 278  | 
progress.bash(cwd = root_path.file, echo = true,  | 
279  | 
script = """  | 
|
280  | 
set -e  | 
|
| 71126 | 281  | 
echo "Cloning distribution repositories:"  | 
| 
71287
 
71fd25a7bbe2
more robust setup: avoid blind shot at "the latest" version;
 
wenzelm 
parents: 
71285 
diff
changeset
 | 
282  | 
|
| 
71422
 
5d5be87330b5
allow to override repository versions at runtime;
 
wenzelm 
parents: 
71421 
diff
changeset
 | 
283  | 
git clone --branch stable https://github.com/phacility/arcanist.git  | 
| 
 
5d5be87330b5
allow to override repository versions at runtime;
 
wenzelm 
parents: 
71421 
diff
changeset
 | 
284  | 
git -C arcanist reset --hard """ +  | 
| 
 
5d5be87330b5
allow to override repository versions at runtime;
 
wenzelm 
parents: 
71421 
diff
changeset
 | 
285  | 
          Bash.string(options.string("phabricator_version_arcanist")) + """
 | 
| 
71287
 
71fd25a7bbe2
more robust setup: avoid blind shot at "the latest" version;
 
wenzelm 
parents: 
71285 
diff
changeset
 | 
286  | 
|
| 71283 | 287  | 
git clone --branch stable https://github.com/phacility/phabricator.git  | 
| 
71422
 
5d5be87330b5
allow to override repository versions at runtime;
 
wenzelm 
parents: 
71421 
diff
changeset
 | 
288  | 
git -C phabricator reset --hard """ +  | 
| 
 
5d5be87330b5
allow to override repository versions at runtime;
 
wenzelm 
parents: 
71421 
diff
changeset
 | 
289  | 
          Bash.string(options.string("phabricator_version_phabricator")) + """
 | 
| 70967 | 290  | 
""").check  | 
291  | 
||
292  | 
val config = Config(name, root_path)  | 
|
293  | 
write_config(configs ::: List(config))  | 
|
| 70968 | 294  | 
|
| 71051 | 295  | 
    config.execute("config set pygments.enabled true")
 | 
296  | 
||
| 70968 | 297  | 
|
| 71050 | 298  | 
/* local repository directory */  | 
299  | 
||
| 
71109
 
8c1c717a830b
configure SSH hosting via "isabelle phabricator_setup_ssh";
 
wenzelm 
parents: 
71103 
diff
changeset
 | 
300  | 
    progress.echo("\nRepository hosting setup ...")
 | 
| 
 
8c1c717a830b
configure SSH hosting via "isabelle phabricator_setup_ssh";
 
wenzelm 
parents: 
71103 
diff
changeset
 | 
301  | 
|
| 71050 | 302  | 
    if (!Isabelle_System.bash("mkdir -p " + File.bash_path(repo_path)).ok) {
 | 
303  | 
      error("Failed to create local repository directory " + repo_path)
 | 
|
304  | 
}  | 
|
305  | 
||
| 71114 | 306  | 
Isabelle_System.chown(  | 
307  | 
"-R " + Bash.string(daemon_user) + ":" + Bash.string(daemon_user), repo_path)  | 
|
308  | 
    Isabelle_System.chmod("755", repo_path)
 | 
|
| 71050 | 309  | 
|
310  | 
    config.execute("config set repository.default-local-path " + File.bash_path(repo_path))
 | 
|
311  | 
||
312  | 
||
| 71277 | 313  | 
val sudoers_file =  | 
314  | 
      Path.explode("/etc/sudoers.d") + Path.basic(isabelle_phabricator_name(name = name))
 | 
|
| 
71109
 
8c1c717a830b
configure SSH hosting via "isabelle phabricator_setup_ssh";
 
wenzelm 
parents: 
71103 
diff
changeset
 | 
315  | 
File.write(sudoers_file,  | 
| 
71280
 
5a2033fc8f3d
avoid odd (harmless) problem with Mercurial 4.5.3 provided by Ubuntu 18.04 on first push: "couldn't write revision branch cache names";
 
wenzelm 
parents: 
71277 
diff
changeset
 | 
316  | 
      www_user + " ALL=(" + daemon_user + ") SETENV: NOPASSWD: /usr/bin/git, /usr/local/bin/hg, /usr/bin/hg, /usr/bin/ssh, /usr/bin/id\n" +
 | 
| 
 
5a2033fc8f3d
avoid odd (harmless) problem with Mercurial 4.5.3 provided by Ubuntu 18.04 on first push: "couldn't write revision branch cache names";
 
wenzelm 
parents: 
71277 
diff
changeset
 | 
317  | 
      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")
 | 
| 
71109
 
8c1c717a830b
configure SSH hosting via "isabelle phabricator_setup_ssh";
 
wenzelm 
parents: 
71103 
diff
changeset
 | 
318  | 
|
| 71115 | 319  | 
    Isabelle_System.chmod("440", sudoers_file)
 | 
| 
71109
 
8c1c717a830b
configure SSH hosting via "isabelle phabricator_setup_ssh";
 
wenzelm 
parents: 
71103 
diff
changeset
 | 
320  | 
|
| 
 
8c1c717a830b
configure SSH hosting via "isabelle phabricator_setup_ssh";
 
wenzelm 
parents: 
71103 
diff
changeset
 | 
321  | 
    config.execute("config set diffusion.ssh-user " + Bash.string(config.name))
 | 
| 
 
8c1c717a830b
configure SSH hosting via "isabelle phabricator_setup_ssh";
 
wenzelm 
parents: 
71103 
diff
changeset
 | 
322  | 
|
| 
 
8c1c717a830b
configure SSH hosting via "isabelle phabricator_setup_ssh";
 
wenzelm 
parents: 
71103 
diff
changeset
 | 
323  | 
|
| 70969 | 324  | 
/* MySQL setup */  | 
325  | 
||
| 71079 | 326  | 
    progress.echo("\nMySQL setup ...")
 | 
| 70969 | 327  | 
|
| 
71055
 
27a998cdc0f4
back to plain name, to have it accepted my mysql;
 
wenzelm 
parents: 
71054 
diff
changeset
 | 
328  | 
    File.write(Path.explode("/etc/mysql/mysql.conf.d/" + phabricator_name(ext = "cnf")),
 | 
| 71051 | 329  | 
"""[mysqld]  | 
330  | 
max_allowed_packet = 32M  | 
|
331  | 
innodb_buffer_pool_size = 1600M  | 
|
332  | 
local_infile = 0  | 
|
333  | 
""")  | 
|
334  | 
||
335  | 
    Linux.service_restart("mysql")
 | 
|
336  | 
||
337  | 
||
| 75393 | 338  | 
    def mysql_conf(R: Regex, which: String): String = {
 | 
| 
71266
 
8451c86ffa85
proper mysql user setup: avoid superuser powers in production;
 
wenzelm 
parents: 
71265 
diff
changeset
 | 
339  | 
      val conf = Path.explode("/etc/mysql/debian.cnf")
 | 
| 
 
8451c86ffa85
proper mysql user setup: avoid superuser powers in production;
 
wenzelm 
parents: 
71265 
diff
changeset
 | 
340  | 
      split_lines(File.read(conf)).collectFirst({ case R(a) => a }) match {
 | 
| 
 
8451c86ffa85
proper mysql user setup: avoid superuser powers in production;
 
wenzelm 
parents: 
71265 
diff
changeset
 | 
341  | 
case Some(res) => res  | 
| 
 
8451c86ffa85
proper mysql user setup: avoid superuser powers in production;
 
wenzelm 
parents: 
71265 
diff
changeset
 | 
342  | 
        case None => error("Cannot determine " + which + " from " + conf)
 | 
| 
 
8451c86ffa85
proper mysql user setup: avoid superuser powers in production;
 
wenzelm 
parents: 
71265 
diff
changeset
 | 
343  | 
}  | 
| 70969 | 344  | 
}  | 
345  | 
||
| 
71266
 
8451c86ffa85
proper mysql user setup: avoid superuser powers in production;
 
wenzelm 
parents: 
71265 
diff
changeset
 | 
346  | 
    val mysql_root_user = mysql_conf("""^user\s*=\s*(\S*)\s*$""".r, "superuser name")
 | 
| 
 
8451c86ffa85
proper mysql user setup: avoid superuser powers in production;
 
wenzelm 
parents: 
71265 
diff
changeset
 | 
347  | 
    val mysql_root_password = mysql_conf("""^password\s*=\s*(\S*)\s*$""".r, "superuser password")
 | 
| 
 
8451c86ffa85
proper mysql user setup: avoid superuser powers in production;
 
wenzelm 
parents: 
71265 
diff
changeset
 | 
348  | 
|
| 
 
8451c86ffa85
proper mysql user setup: avoid superuser powers in production;
 
wenzelm 
parents: 
71265 
diff
changeset
 | 
349  | 
    val mysql_name = phabricator_name(name = name).replace("-", "_")
 | 
| 
 
8451c86ffa85
proper mysql user setup: avoid superuser powers in production;
 
wenzelm 
parents: 
71265 
diff
changeset
 | 
350  | 
val mysql_user_string = SQL.string(mysql_name) + "@'localhost'"  | 
| 
 
8451c86ffa85
proper mysql user setup: avoid superuser powers in production;
 
wenzelm 
parents: 
71265 
diff
changeset
 | 
351  | 
val mysql_password = Linux.generate_password()  | 
| 70969 | 352  | 
|
| 
71266
 
8451c86ffa85
proper mysql user setup: avoid superuser powers in production;
 
wenzelm 
parents: 
71265 
diff
changeset
 | 
353  | 
    Isabelle_System.bash("mysql --user=" + Bash.string(mysql_root_user) +
 | 
| 
 
8451c86ffa85
proper mysql user setup: avoid superuser powers in production;
 
wenzelm 
parents: 
71265 
diff
changeset
 | 
354  | 
" --password=" + Bash.string(mysql_root_password) + " --execute=" +  | 
| 
 
8451c86ffa85
proper mysql user setup: avoid superuser powers in production;
 
wenzelm 
parents: 
71265 
diff
changeset
 | 
355  | 
Bash.string(  | 
| 71274 | 356  | 
"""DROP USER IF EXISTS """ + mysql_user_string + "; " +  | 
357  | 
"""CREATE USER """ + mysql_user_string +  | 
|
| 
71266
 
8451c86ffa85
proper mysql user setup: avoid superuser powers in production;
 
wenzelm 
parents: 
71265 
diff
changeset
 | 
358  | 
""" IDENTIFIED BY """ + SQL.string(mysql_password) + """ PASSWORD EXPIRE NEVER; """ +  | 
| 
 
8451c86ffa85
proper mysql user setup: avoid superuser powers in production;
 
wenzelm 
parents: 
71265 
diff
changeset
 | 
359  | 
        """GRANT ALL ON `""" + (mysql_name + "_%").replace("_", "\\_") +
 | 
| 
72522
 
6e27af808c17
more privileges for the sake of mysqldump (avoid workaround --no-tablespaces);
 
wenzelm 
parents: 
72521 
diff
changeset
 | 
360  | 
"""`.* TO """ + mysql_user_string + ";" +  | 
| 
 
6e27af808c17
more privileges for the sake of mysqldump (avoid workaround --no-tablespaces);
 
wenzelm 
parents: 
72521 
diff
changeset
 | 
361  | 
"""GRANT PROCESS ON *.* TO """ + mysql_user_string + ";")).check  | 
| 70969 | 362  | 
|
| 
71266
 
8451c86ffa85
proper mysql user setup: avoid superuser powers in production;
 
wenzelm 
parents: 
71265 
diff
changeset
 | 
363  | 
    config.execute("config set mysql.user " + Bash.string(mysql_name))
 | 
| 
 
8451c86ffa85
proper mysql user setup: avoid superuser powers in production;
 
wenzelm 
parents: 
71265 
diff
changeset
 | 
364  | 
    config.execute("config set mysql.pass " + Bash.string(mysql_password))
 | 
| 
 
8451c86ffa85
proper mysql user setup: avoid superuser powers in production;
 
wenzelm 
parents: 
71265 
diff
changeset
 | 
365  | 
|
| 
 
8451c86ffa85
proper mysql user setup: avoid superuser powers in production;
 
wenzelm 
parents: 
71265 
diff
changeset
 | 
366  | 
    config.execute("config set phabricator.cache-namespace " + Bash.string(mysql_name))
 | 
| 
 
8451c86ffa85
proper mysql user setup: avoid superuser powers in production;
 
wenzelm 
parents: 
71265 
diff
changeset
 | 
367  | 
    config.execute("config set storage.default-namespace " + Bash.string(mysql_name))
 | 
| 71051 | 368  | 
    config.execute("config set storage.mysql-engine.max-size 8388608")
 | 
369  | 
||
| 71102 | 370  | 
    progress.bash("bin/storage upgrade --force", cwd = config.home.file, echo = true).check
 | 
| 70969 | 371  | 
|
372  | 
||
| 71269 | 373  | 
/* database dump */  | 
374  | 
||
375  | 
val dump_name = isabelle_phabricator_name(name = "dump")  | 
|
| 71282 | 376  | 
command_setup(dump_name, body =  | 
| 71269 | 377  | 
"""mkdir -p "$ROOT/database" && chown root:root "$ROOT/database" && chmod 700 "$ROOT/database"  | 
378  | 
[ -e "$ROOT/database/dump.sql.gz" ] && mv -f "$ROOT/database/dump.sql.gz" "$ROOT/database/dump-old.sql.gz"  | 
|
| 71328 | 379  | 
echo -n "Creating $ROOT/database/dump.sql.gz ..."  | 
380  | 
"$ROOT/phabricator/bin/storage" dump --compress --output "$ROOT/database/dump.sql.gz" 2>&1 | fgrep -v '[Warning] Using a password on the command line interface can be insecure'  | 
|
381  | 
echo " $(ls -hs "$ROOT/database/dump.sql.gz" | cut -d" " -f1)" """)  | 
|
| 71269 | 382  | 
|
383  | 
||
| 71283 | 384  | 
/* Phabricator upgrade */  | 
385  | 
||
386  | 
command_setup(isabelle_phabricator_name(name = "upgrade"),  | 
|
387  | 
init =  | 
|
| 71285 | 388  | 
"""BRANCH="${1:-stable}"
 | 
| 71283 | 389  | 
if [ "$BRANCH" != "master" -a "$BRANCH" != "stable" ]  | 
390  | 
then  | 
|
391  | 
echo "Bad branch: \"$BRANCH\""  | 
|
392  | 
exit 1  | 
|
393  | 
fi  | 
|
394  | 
||
395  | 
systemctl stop isabelle-phabricator-phd  | 
|
396  | 
systemctl stop apache2  | 
|
397  | 
""",  | 
|
398  | 
body =  | 
|
399  | 
"""echo -e "\nUpgrading phabricator \"$NAME\" root \"$ROOT\" ..."  | 
|
| 71845 | 400  | 
for REPO in arcanist phabricator  | 
| 71283 | 401  | 
do  | 
402  | 
cd "$ROOT/$REPO"  | 
|
403  | 
echo -e "\nUpdating \"$REPO\" ..."  | 
|
404  | 
git checkout "$BRANCH"  | 
|
405  | 
git pull  | 
|
406  | 
done  | 
|
407  | 
echo -e "\nUpgrading storage ..."  | 
|
408  | 
"$ROOT/phabricator/bin/storage" upgrade --force  | 
|
409  | 
""",  | 
|
410  | 
exit =  | 
|
411  | 
"""systemctl start apache2  | 
|
412  | 
systemctl start isabelle-phabricator-phd""")  | 
|
413  | 
||
414  | 
||
| 71051 | 415  | 
/* PHP setup */  | 
416  | 
||
417  | 
val php_version =  | 
|
418  | 
      Isabelle_System.bash("""php --run 'echo PHP_MAJOR_VERSION . "." . PHP_MINOR_VERSION;'""")
 | 
|
419  | 
.check.out  | 
|
420  | 
||
421  | 
val php_conf =  | 
|
422  | 
      Path.explode("/etc/php") + Path.basic(php_version) +  // educated guess
 | 
|
| 
71052
 
6bf53035baf0
clarified name prefixes: global config always uses "isabelle-phabricator";
 
wenzelm 
parents: 
71051 
diff
changeset
 | 
423  | 
        Path.explode("apache2/conf.d") +
 | 
| 
 
6bf53035baf0
clarified name prefixes: global config always uses "isabelle-phabricator";
 
wenzelm 
parents: 
71051 
diff
changeset
 | 
424  | 
Path.basic(isabelle_phabricator_name(ext = "ini"))  | 
| 71051 | 425  | 
|
426  | 
File.write(php_conf,  | 
|
427  | 
"post_max_size = 32M\n" +  | 
|
428  | 
"opcache.validate_timestamps = 0\n" +  | 
|
| 71129 | 429  | 
"memory_limit = 512M\n" +  | 
430  | 
"max_execution_time = 120\n")  | 
|
| 71051 | 431  | 
|
432  | 
||
| 70968 | 433  | 
/* Apache setup */  | 
434  | 
||
| 71079 | 435  | 
    progress.echo("Apache setup ...")
 | 
| 70968 | 436  | 
|
| 
71068
 
510b89906d86
discontinued somewhat pointless Isabelle options: setup implicitly assumes Ubuntu 18.04;
 
wenzelm 
parents: 
71066 
diff
changeset
 | 
437  | 
    val apache_root = Path.explode("/etc/apache2")
 | 
| 70968 | 438  | 
    val apache_sites = apache_root + Path.explode("sites-available")
 | 
439  | 
||
440  | 
    if (!apache_sites.is_dir) error("Bad Apache sites directory " + apache_sites)
 | 
|
441  | 
||
| 71058 | 442  | 
val server_name = phabricator_name(name = name, ext = "lvh.me") // alias for "localhost" for testing  | 
| 
71052
 
6bf53035baf0
clarified name prefixes: global config always uses "isabelle-phabricator";
 
wenzelm 
parents: 
71051 
diff
changeset
 | 
443  | 
val server_url = "http://" + server_name  | 
| 
 
6bf53035baf0
clarified name prefixes: global config always uses "isabelle-phabricator";
 
wenzelm 
parents: 
71051 
diff
changeset
 | 
444  | 
|
| 71058 | 445  | 
File.write(apache_sites + Path.basic(isabelle_phabricator_name(name = name, ext = "conf")),  | 
| 70968 | 446  | 
"""<VirtualHost *:80>  | 
| 
71052
 
6bf53035baf0
clarified name prefixes: global config always uses "isabelle-phabricator";
 
wenzelm 
parents: 
71051 
diff
changeset
 | 
447  | 
ServerName """ + server_name + """  | 
| 70968 | 448  | 
ServerAdmin webmaster@localhost  | 
| 70969 | 449  | 
DocumentRoot """ + config.home.implode + """/webroot  | 
| 70968 | 450  | 
|
451  | 
    ErrorLog ${APACHE_LOG_DIR}/error.log
 | 
|
| 
71439
 
760e19aa9b09
afford more logging (following defaults on Ubuntu);
 
wenzelm 
parents: 
71422 
diff
changeset
 | 
452  | 
    CustomLog ${APACHE_LOG_DIR}/access.log combined
 | 
| 
 
760e19aa9b09
afford more logging (following defaults on Ubuntu);
 
wenzelm 
parents: 
71422 
diff
changeset
 | 
453  | 
|
| 70968 | 454  | 
RewriteEngine on  | 
455  | 
RewriteRule ^(.*)$ /index.php?__path__=$1 [B,L,QSA]  | 
|
456  | 
</VirtualHost>  | 
|
457  | 
||
458  | 
# vim: syntax=apache ts=4 sw=4 sts=4 sr noet  | 
|
459  | 
""")  | 
|
460  | 
||
| 71051 | 461  | 
Isabelle_System.bash( """  | 
| 70968 | 462  | 
set -e  | 
463  | 
a2enmod rewrite  | 
|
| 71058 | 464  | 
a2ensite """ + Bash.string(isabelle_phabricator_name(name = name))).check  | 
| 71051 | 465  | 
|
| 71057 | 466  | 
    config.execute("config set phabricator.base-uri " + Bash.string(server_url))
 | 
467  | 
||
| 71051 | 468  | 
    Linux.service_restart("apache2")
 | 
| 70968 | 469  | 
|
| 71328 | 470  | 
    progress.echo("\nFurther manual configuration via " + server_url)
 | 
| 
71128
 
f79006c533b0
clarified errors: PHP daemon can fail under odd circumstances;
 
wenzelm 
parents: 
71127 
diff
changeset
 | 
471  | 
|
| 71053 | 472  | 
|
473  | 
/* PHP daemon */  | 
|
474  | 
||
| 
71128
 
f79006c533b0
clarified errors: PHP daemon can fail under odd circumstances;
 
wenzelm 
parents: 
71127 
diff
changeset
 | 
475  | 
    progress.echo("\nPHP daemon setup ...")
 | 
| 71053 | 476  | 
|
| 72376 | 477  | 
    val phd_log_path = Isabelle_System.make_directory(Path.explode("/var/tmp/phd"))
 | 
| 71273 | 478  | 
Isabelle_System.chown(  | 
479  | 
"-R " + Bash.string(daemon_user) + ":" + Bash.string(daemon_user), phd_log_path)  | 
|
480  | 
    Isabelle_System.chmod("755", phd_log_path)
 | 
|
481  | 
||
| 71053 | 482  | 
    config.execute("config set phd.user " + Bash.string(daemon_user))
 | 
| 71112 | 483  | 
    config.execute("config set phd.log-directory /var/tmp/phd/" +
 | 
484  | 
isabelle_phabricator_name(name = name) + "/log")  | 
|
| 71053 | 485  | 
|
| 
71124
 
7dbadecdc118
just one isabelle-phabricator-phd service, which manages all processes uniformly (NB: "bin/phd stop" affects all installations);
 
wenzelm 
parents: 
71122 
diff
changeset
 | 
486  | 
val phd_name = isabelle_phabricator_name(name = "phd")  | 
| 71127 | 487  | 
Linux.service_shutdown(phd_name)  | 
| 71282 | 488  | 
val phd_command = command_setup(phd_name, body = """"$ROOT/phabricator/bin/phd" "$@" """)  | 
| 
71128
 
f79006c533b0
clarified errors: PHP daemon can fail under odd circumstances;
 
wenzelm 
parents: 
71127 
diff
changeset
 | 
489  | 
    try {
 | 
| 
 
f79006c533b0
clarified errors: PHP daemon can fail under odd circumstances;
 
wenzelm 
parents: 
71127 
diff
changeset
 | 
490  | 
Linux.service_install(phd_name,  | 
| 71053 | 491  | 
"""[Unit]  | 
| 
71124
 
7dbadecdc118
just one isabelle-phabricator-phd service, which manages all processes uniformly (NB: "bin/phd stop" affects all installations);
 
wenzelm 
parents: 
71122 
diff
changeset
 | 
492  | 
Description=PHP daemon manager for Isabelle/Phabricator  | 
| 71053 | 493  | 
After=syslog.target network.target apache2.service mysql.service  | 
494  | 
||
495  | 
[Service]  | 
|
496  | 
Type=oneshot  | 
|
497  | 
User=""" + daemon_user + """  | 
|
498  | 
Group=""" + daemon_user + """  | 
|
499  | 
Environment=PATH=/sbin:/usr/sbin:/usr/local/sbin:/usr/local/bin:/usr/bin:/bin  | 
|
| 
71124
 
7dbadecdc118
just one isabelle-phabricator-phd service, which manages all processes uniformly (NB: "bin/phd stop" affects all installations);
 
wenzelm 
parents: 
71122 
diff
changeset
 | 
500  | 
ExecStart=""" + phd_command.implode + """ start --force  | 
| 
 
7dbadecdc118
just one isabelle-phabricator-phd service, which manages all processes uniformly (NB: "bin/phd stop" affects all installations);
 
wenzelm 
parents: 
71122 
diff
changeset
 | 
501  | 
ExecStop=""" + phd_command.implode + """ stop  | 
| 71053 | 502  | 
RemainAfterExit=yes  | 
503  | 
||
504  | 
[Install]  | 
|
505  | 
WantedBy=multi-user.target  | 
|
506  | 
""")  | 
|
| 
71128
 
f79006c533b0
clarified errors: PHP daemon can fail under odd circumstances;
 
wenzelm 
parents: 
71127 
diff
changeset
 | 
507  | 
}  | 
| 
 
f79006c533b0
clarified errors: PHP daemon can fail under odd circumstances;
 
wenzelm 
parents: 
71127 
diff
changeset
 | 
508  | 
    catch {
 | 
| 
 
f79006c533b0
clarified errors: PHP daemon can fail under odd circumstances;
 
wenzelm 
parents: 
71127 
diff
changeset
 | 
509  | 
case ERROR(msg) =>  | 
| 
 
f79006c533b0
clarified errors: PHP daemon can fail under odd circumstances;
 
wenzelm 
parents: 
71127 
diff
changeset
 | 
510  | 
        progress.bash("bin/phd status", cwd = config.home.file, echo = true).check
 | 
| 
 
f79006c533b0
clarified errors: PHP daemon can fail under odd circumstances;
 
wenzelm 
parents: 
71127 
diff
changeset
 | 
511  | 
error(msg)  | 
| 
 
f79006c533b0
clarified errors: PHP daemon can fail under odd circumstances;
 
wenzelm 
parents: 
71127 
diff
changeset
 | 
512  | 
}  | 
| 70967 | 513  | 
}  | 
514  | 
||
515  | 
||
516  | 
/* Isabelle tool wrapper */  | 
|
517  | 
||
| 71097 | 518  | 
val isabelle_tool2 =  | 
| 72763 | 519  | 
    Isabelle_Tool("phabricator_setup", "setup Phabricator server on Ubuntu Linux",
 | 
| 75393 | 520  | 
Scala_Project.here,  | 
| 75394 | 521  | 
      { args =>
 | 
522  | 
var mercurial_source = ""  | 
|
523  | 
var repo = ""  | 
|
524  | 
var package_update = false  | 
|
525  | 
var name = default_name  | 
|
526  | 
var options = Options.init()  | 
|
527  | 
var root = ""  | 
|
| 70967 | 528  | 
|
| 75394 | 529  | 
        val getopts = Getopts("""
 | 
| 71078 | 530  | 
Usage: isabelle phabricator_setup [OPTIONS]  | 
| 70967 | 531  | 
|
532  | 
Options are:  | 
|
| 
71280
 
5a2033fc8f3d
avoid odd (harmless) problem with Mercurial 4.5.3 provided by Ubuntu 18.04 on first push: "couldn't write revision branch cache names";
 
wenzelm 
parents: 
71277 
diff
changeset
 | 
533  | 
-M SOURCE install Mercurial from source: local PATH, or URL, or ":" for  | 
| 
 
5a2033fc8f3d
avoid odd (harmless) problem with Mercurial 4.5.3 provided by Ubuntu 18.04 on first push: "couldn't write revision branch cache names";
 
wenzelm 
parents: 
71277 
diff
changeset
 | 
534  | 
""" + standard_mercurial_source + """  | 
| 
71068
 
510b89906d86
discontinued somewhat pointless Isabelle options: setup implicitly assumes Ubuntu 18.04;
 
wenzelm 
parents: 
71066 
diff
changeset
 | 
535  | 
    -R DIR       repository directory (default: """ + default_repo("NAME") + """)
 | 
| 71047 | 536  | 
-U full update of system packages before installation  | 
| 71078 | 537  | 
-n NAME Phabricator installation name (default: """ + quote(default_name) + """)  | 
| 
71422
 
5d5be87330b5
allow to override repository versions at runtime;
 
wenzelm 
parents: 
71421 
diff
changeset
 | 
538  | 
-o OPTION override Isabelle system OPTION (via NAME=VAL or NAME)  | 
| 
71068
 
510b89906d86
discontinued somewhat pointless Isabelle options: setup implicitly assumes Ubuntu 18.04;
 
wenzelm 
parents: 
71066 
diff
changeset
 | 
539  | 
    -r DIR       installation root directory (default: """ + default_root("NAME") + """)
 | 
| 70967 | 540  | 
|
| 71103 | 541  | 
Install Phabricator as LAMP application (Linux, Apache, MySQL, PHP).  | 
| 70967 | 542  | 
|
| 71078 | 543  | 
The installation name (default: """ + quote(default_name) + """) is mapped to a regular  | 
544  | 
Unix user; this is relevant for public SSH access.  | 
|
| 70967 | 545  | 
""",  | 
| 
71280
 
5a2033fc8f3d
avoid odd (harmless) problem with Mercurial 4.5.3 provided by Ubuntu 18.04 on first push: "couldn't write revision branch cache names";
 
wenzelm 
parents: 
71277 
diff
changeset
 | 
546  | 
"M:" -> (arg => mercurial_source = (if (arg == ":") standard_mercurial_source else arg)),  | 
| 70967 | 547  | 
"R:" -> (arg => repo = arg),  | 
| 71047 | 548  | 
"U" -> (_ => package_update = true),  | 
| 71078 | 549  | 
"n:" -> (arg => name = arg),  | 
| 
71422
 
5d5be87330b5
allow to override repository versions at runtime;
 
wenzelm 
parents: 
71421 
diff
changeset
 | 
550  | 
"o:" -> (arg => options = options + arg),  | 
| 70967 | 551  | 
"r:" -> (arg => root = arg))  | 
552  | 
||
| 75394 | 553  | 
val more_args = getopts(args)  | 
554  | 
if (more_args.nonEmpty) getopts.usage()  | 
|
| 70967 | 555  | 
|
| 75394 | 556  | 
val progress = new Console_Progress  | 
| 70967 | 557  | 
|
| 75394 | 558  | 
phabricator_setup(options, name = name, root = root, repo = repo,  | 
559  | 
package_update = package_update, mercurial_source = mercurial_source, progress = progress)  | 
|
560  | 
})  | 
|
| 70967 | 561  | 
|
562  | 
||
563  | 
||
| 71066 | 564  | 
/** setup mail **/  | 
| 70967 | 565  | 
|
| 71072 | 566  | 
val mailers_template: String =  | 
| 
75659
 
9bd92ac9328f
more robust Scala 3 indentation, for the sake of IntelliJ IDEA;
 
wenzelm 
parents: 
75394 
diff
changeset
 | 
567  | 
"""[  | 
| 71072 | 568  | 
  {
 | 
569  | 
"key": "example.org",  | 
|
570  | 
"type": "smtp",  | 
|
571  | 
    "options": {
 | 
|
572  | 
"host": "mail.example.org",  | 
|
573  | 
"port": 465,  | 
|
574  | 
"user": "phabricator@example.org",  | 
|
575  | 
"password": "********",  | 
|
576  | 
"protocol": "ssl",  | 
|
577  | 
"message-id": true  | 
|
578  | 
}  | 
|
579  | 
}  | 
|
580  | 
]"""  | 
|
581  | 
||
| 71066 | 582  | 
def phabricator_setup_mail(  | 
583  | 
name: String = default_name,  | 
|
584  | 
config_file: Option[Path] = None,  | 
|
585  | 
test_user: String = "",  | 
|
| 75393 | 586  | 
progress: Progress = new Progress  | 
587  | 
  ): Unit = {
 | 
|
| 70967 | 588  | 
Linux.check_system_root()  | 
589  | 
||
| 71066 | 590  | 
val config = get_config(name)  | 
| 71073 | 591  | 
val default_config_file = config.root + default_mailers  | 
| 71066 | 592  | 
|
593  | 
val mail_config = config_file getOrElse default_config_file  | 
|
594  | 
||
| 75393 | 595  | 
    def setup_mail: Unit = {
 | 
| 71066 | 596  | 
      progress.echo("Using mail configuration from " + mail_config)
 | 
597  | 
      config.execute("config set cluster.mailers --stdin < " + File.bash_path(mail_config))
 | 
|
598  | 
||
599  | 
      if (test_user.nonEmpty) {
 | 
|
600  | 
        progress.echo("Sending test mail to " + quote(test_user))
 | 
|
601  | 
progress.bash(cwd = config.home.file, echo = true,  | 
|
| 71102 | 602  | 
script = """echo "Test from Phabricator ($(date))" | bin/mail send-test --subject "Test" --to """ +  | 
| 71066 | 603  | 
Bash.string(test_user)).check  | 
604  | 
}  | 
|
605  | 
}  | 
|
606  | 
||
607  | 
    if (config_file.isEmpty) {
 | 
|
| 71070 | 608  | 
      if (!default_config_file.is_file) {
 | 
609  | 
File.write(default_config_file, mailers_template)  | 
|
| 71114 | 610  | 
        Isabelle_System.chmod("600", default_config_file)
 | 
| 71070 | 611  | 
}  | 
| 71066 | 612  | 
      if (File.read(default_config_file) == mailers_template) {
 | 
| 71131 | 613  | 
        progress.echo("Please invoke the tool again, after providing details in\n  " +
 | 
614  | 
default_config_file.implode + "\n")  | 
|
| 71066 | 615  | 
}  | 
616  | 
else setup_mail  | 
|
617  | 
}  | 
|
618  | 
else setup_mail  | 
|
| 70967 | 619  | 
}  | 
620  | 
||
621  | 
||
622  | 
/* Isabelle tool wrapper */  | 
|
623  | 
||
| 71097 | 624  | 
val isabelle_tool3 =  | 
| 72763 | 625  | 
    Isabelle_Tool("phabricator_setup_mail", "setup mail for one Phabricator installation",
 | 
| 75393 | 626  | 
Scala_Project.here,  | 
| 75394 | 627  | 
      { args =>
 | 
628  | 
var test_user = ""  | 
|
629  | 
var name = default_name  | 
|
630  | 
var config_file: Option[Path] = None  | 
|
| 71066 | 631  | 
|
| 75394 | 632  | 
        val getopts = Getopts("""
 | 
| 71066 | 633  | 
Usage: isabelle phabricator_setup_mail [OPTIONS]  | 
634  | 
||
635  | 
Options are:  | 
|
636  | 
-T USER send test mail to Phabricator user  | 
|
| 71103 | 637  | 
-f FILE config file (default: """ + default_mailers + """ within Phabricator root)  | 
| 71066 | 638  | 
-n NAME Phabricator installation name (default: """ + quote(default_name) + """)  | 
| 70967 | 639  | 
|
| 71077 | 640  | 
Provide mail configuration for existing Phabricator installation.  | 
| 71066 | 641  | 
""",  | 
642  | 
"T:" -> (arg => test_user = arg),  | 
|
643  | 
"f:" -> (arg => config_file = Some(Path.explode(arg))),  | 
|
644  | 
"n:" -> (arg => name = arg))  | 
|
| 70967 | 645  | 
|
| 75394 | 646  | 
val more_args = getopts(args)  | 
647  | 
if (more_args.nonEmpty) getopts.usage()  | 
|
| 70967 | 648  | 
|
| 75394 | 649  | 
val progress = new Console_Progress  | 
| 70967 | 650  | 
|
| 75394 | 651  | 
phabricator_setup_mail(name = name, config_file = config_file,  | 
652  | 
test_user = test_user, progress = progress)  | 
|
653  | 
})  | 
|
| 
71109
 
8c1c717a830b
configure SSH hosting via "isabelle phabricator_setup_ssh";
 
wenzelm 
parents: 
71103 
diff
changeset
 | 
654  | 
|
| 
 
8c1c717a830b
configure SSH hosting via "isabelle phabricator_setup_ssh";
 
wenzelm 
parents: 
71103 
diff
changeset
 | 
655  | 
|
| 
 
8c1c717a830b
configure SSH hosting via "isabelle phabricator_setup_ssh";
 
wenzelm 
parents: 
71103 
diff
changeset
 | 
656  | 
|
| 
 
8c1c717a830b
configure SSH hosting via "isabelle phabricator_setup_ssh";
 
wenzelm 
parents: 
71103 
diff
changeset
 | 
657  | 
/** setup ssh **/  | 
| 
 
8c1c717a830b
configure SSH hosting via "isabelle phabricator_setup_ssh";
 
wenzelm 
parents: 
71103 
diff
changeset
 | 
658  | 
|
| 
 
8c1c717a830b
configure SSH hosting via "isabelle phabricator_setup_ssh";
 
wenzelm 
parents: 
71103 
diff
changeset
 | 
659  | 
/* sshd config */  | 
| 
 
8c1c717a830b
configure SSH hosting via "isabelle phabricator_setup_ssh";
 
wenzelm 
parents: 
71103 
diff
changeset
 | 
660  | 
|
| 
 
8c1c717a830b
configure SSH hosting via "isabelle phabricator_setup_ssh";
 
wenzelm 
parents: 
71103 
diff
changeset
 | 
661  | 
private val Port = """^\s*Port\s+(\d+)\s*$""".r  | 
| 
 
8c1c717a830b
configure SSH hosting via "isabelle phabricator_setup_ssh";
 
wenzelm 
parents: 
71103 
diff
changeset
 | 
662  | 
private val No_Port = """^#\s*Port\b.*$""".r  | 
| 
 
8c1c717a830b
configure SSH hosting via "isabelle phabricator_setup_ssh";
 
wenzelm 
parents: 
71103 
diff
changeset
 | 
663  | 
private val Any_Port = """^#?\s*Port\b.*$""".r  | 
| 
 
8c1c717a830b
configure SSH hosting via "isabelle phabricator_setup_ssh";
 
wenzelm 
parents: 
71103 
diff
changeset
 | 
664  | 
|
| 
 
8c1c717a830b
configure SSH hosting via "isabelle phabricator_setup_ssh";
 
wenzelm 
parents: 
71103 
diff
changeset
 | 
665  | 
def conf_ssh_port(port: Int): String =  | 
| 76129 | 666  | 
if (port == default_system_port) "#Port " + default_system_port else "Port " + port  | 
| 
71109
 
8c1c717a830b
configure SSH hosting via "isabelle phabricator_setup_ssh";
 
wenzelm 
parents: 
71103 
diff
changeset
 | 
667  | 
|
| 75393 | 668  | 
  def read_ssh_port(conf: Path): Int = {
 | 
| 
71109
 
8c1c717a830b
configure SSH hosting via "isabelle phabricator_setup_ssh";
 
wenzelm 
parents: 
71103 
diff
changeset
 | 
669  | 
val lines = split_lines(File.read(conf))  | 
| 
 
8c1c717a830b
configure SSH hosting via "isabelle phabricator_setup_ssh";
 
wenzelm 
parents: 
71103 
diff
changeset
 | 
670  | 
val ports =  | 
| 
 
8c1c717a830b
configure SSH hosting via "isabelle phabricator_setup_ssh";
 
wenzelm 
parents: 
71103 
diff
changeset
 | 
671  | 
      lines.flatMap({
 | 
| 
 
8c1c717a830b
configure SSH hosting via "isabelle phabricator_setup_ssh";
 
wenzelm 
parents: 
71103 
diff
changeset
 | 
672  | 
case Port(Value.Int(p)) => Some(p)  | 
| 76129 | 673  | 
case No_Port() => Some(default_system_port)  | 
| 
71109
 
8c1c717a830b
configure SSH hosting via "isabelle phabricator_setup_ssh";
 
wenzelm 
parents: 
71103 
diff
changeset
 | 
674  | 
case _ => None  | 
| 
 
8c1c717a830b
configure SSH hosting via "isabelle phabricator_setup_ssh";
 
wenzelm 
parents: 
71103 
diff
changeset
 | 
675  | 
})  | 
| 
 
8c1c717a830b
configure SSH hosting via "isabelle phabricator_setup_ssh";
 
wenzelm 
parents: 
71103 
diff
changeset
 | 
676  | 
    ports match {
 | 
| 
 
8c1c717a830b
configure SSH hosting via "isabelle phabricator_setup_ssh";
 
wenzelm 
parents: 
71103 
diff
changeset
 | 
677  | 
case List(port) => port  | 
| 
 
8c1c717a830b
configure SSH hosting via "isabelle phabricator_setup_ssh";
 
wenzelm 
parents: 
71103 
diff
changeset
 | 
678  | 
      case Nil => error("Missing Port specification in " + conf)
 | 
| 
 
8c1c717a830b
configure SSH hosting via "isabelle phabricator_setup_ssh";
 
wenzelm 
parents: 
71103 
diff
changeset
 | 
679  | 
      case _ => error("Multiple Port specifications in " + conf)
 | 
| 
 
8c1c717a830b
configure SSH hosting via "isabelle phabricator_setup_ssh";
 
wenzelm 
parents: 
71103 
diff
changeset
 | 
680  | 
}  | 
| 
 
8c1c717a830b
configure SSH hosting via "isabelle phabricator_setup_ssh";
 
wenzelm 
parents: 
71103 
diff
changeset
 | 
681  | 
}  | 
| 
 
8c1c717a830b
configure SSH hosting via "isabelle phabricator_setup_ssh";
 
wenzelm 
parents: 
71103 
diff
changeset
 | 
682  | 
|
| 75393 | 683  | 
  def write_ssh_port(conf: Path, port: Int): Boolean = {
 | 
| 
71109
 
8c1c717a830b
configure SSH hosting via "isabelle phabricator_setup_ssh";
 
wenzelm 
parents: 
71103 
diff
changeset
 | 
684  | 
val old_port = read_ssh_port(conf)  | 
| 
 
8c1c717a830b
configure SSH hosting via "isabelle phabricator_setup_ssh";
 
wenzelm 
parents: 
71103 
diff
changeset
 | 
685  | 
if (old_port == port) false  | 
| 
 
8c1c717a830b
configure SSH hosting via "isabelle phabricator_setup_ssh";
 
wenzelm 
parents: 
71103 
diff
changeset
 | 
686  | 
    else {
 | 
| 
 
8c1c717a830b
configure SSH hosting via "isabelle phabricator_setup_ssh";
 
wenzelm 
parents: 
71103 
diff
changeset
 | 
687  | 
val lines = split_lines(File.read(conf))  | 
| 
 
8c1c717a830b
configure SSH hosting via "isabelle phabricator_setup_ssh";
 
wenzelm 
parents: 
71103 
diff
changeset
 | 
688  | 
      val lines1 = lines.map({ case Any_Port() => conf_ssh_port(port) case line => line })
 | 
| 
 
8c1c717a830b
configure SSH hosting via "isabelle phabricator_setup_ssh";
 
wenzelm 
parents: 
71103 
diff
changeset
 | 
689  | 
File.write(conf, cat_lines(lines1))  | 
| 
 
8c1c717a830b
configure SSH hosting via "isabelle phabricator_setup_ssh";
 
wenzelm 
parents: 
71103 
diff
changeset
 | 
690  | 
true  | 
| 
 
8c1c717a830b
configure SSH hosting via "isabelle phabricator_setup_ssh";
 
wenzelm 
parents: 
71103 
diff
changeset
 | 
691  | 
}  | 
| 
 
8c1c717a830b
configure SSH hosting via "isabelle phabricator_setup_ssh";
 
wenzelm 
parents: 
71103 
diff
changeset
 | 
692  | 
}  | 
| 
 
8c1c717a830b
configure SSH hosting via "isabelle phabricator_setup_ssh";
 
wenzelm 
parents: 
71103 
diff
changeset
 | 
693  | 
|
| 
 
8c1c717a830b
configure SSH hosting via "isabelle phabricator_setup_ssh";
 
wenzelm 
parents: 
71103 
diff
changeset
 | 
694  | 
|
| 
 
8c1c717a830b
configure SSH hosting via "isabelle phabricator_setup_ssh";
 
wenzelm 
parents: 
71103 
diff
changeset
 | 
695  | 
/* phabricator_setup_ssh */  | 
| 
 
8c1c717a830b
configure SSH hosting via "isabelle phabricator_setup_ssh";
 
wenzelm 
parents: 
71103 
diff
changeset
 | 
696  | 
|
| 
 
8c1c717a830b
configure SSH hosting via "isabelle phabricator_setup_ssh";
 
wenzelm 
parents: 
71103 
diff
changeset
 | 
697  | 
def phabricator_setup_ssh(  | 
| 
 
8c1c717a830b
configure SSH hosting via "isabelle phabricator_setup_ssh";
 
wenzelm 
parents: 
71103 
diff
changeset
 | 
698  | 
server_port: Int = default_server_port,  | 
| 
 
8c1c717a830b
configure SSH hosting via "isabelle phabricator_setup_ssh";
 
wenzelm 
parents: 
71103 
diff
changeset
 | 
699  | 
system_port: Int = default_system_port,  | 
| 75393 | 700  | 
progress: Progress = new Progress  | 
701  | 
  ): Unit = {
 | 
|
| 
71109
 
8c1c717a830b
configure SSH hosting via "isabelle phabricator_setup_ssh";
 
wenzelm 
parents: 
71103 
diff
changeset
 | 
702  | 
Linux.check_system_root()  | 
| 
 
8c1c717a830b
configure SSH hosting via "isabelle phabricator_setup_ssh";
 
wenzelm 
parents: 
71103 
diff
changeset
 | 
703  | 
|
| 
 
8c1c717a830b
configure SSH hosting via "isabelle phabricator_setup_ssh";
 
wenzelm 
parents: 
71103 
diff
changeset
 | 
704  | 
val configs = read_config()  | 
| 
 
8c1c717a830b
configure SSH hosting via "isabelle phabricator_setup_ssh";
 
wenzelm 
parents: 
71103 
diff
changeset
 | 
705  | 
|
| 
 
8c1c717a830b
configure SSH hosting via "isabelle phabricator_setup_ssh";
 
wenzelm 
parents: 
71103 
diff
changeset
 | 
706  | 
    if (server_port == system_port) {
 | 
| 
 
8c1c717a830b
configure SSH hosting via "isabelle phabricator_setup_ssh";
 
wenzelm 
parents: 
71103 
diff
changeset
 | 
707  | 
      error("Port for Phabricator sshd coincides with system port: " + system_port)
 | 
| 
 
8c1c717a830b
configure SSH hosting via "isabelle phabricator_setup_ssh";
 
wenzelm 
parents: 
71103 
diff
changeset
 | 
708  | 
}  | 
| 
 
8c1c717a830b
configure SSH hosting via "isabelle phabricator_setup_ssh";
 
wenzelm 
parents: 
71103 
diff
changeset
 | 
709  | 
|
| 
 
8c1c717a830b
configure SSH hosting via "isabelle phabricator_setup_ssh";
 
wenzelm 
parents: 
71103 
diff
changeset
 | 
710  | 
    val sshd_conf_system = Path.explode("/etc/ssh/sshd_config")
 | 
| 
 
8c1c717a830b
configure SSH hosting via "isabelle phabricator_setup_ssh";
 
wenzelm 
parents: 
71103 
diff
changeset
 | 
711  | 
val sshd_conf_server = sshd_conf_system.ext(isabelle_phabricator_name())  | 
| 
 
8c1c717a830b
configure SSH hosting via "isabelle phabricator_setup_ssh";
 
wenzelm 
parents: 
71103 
diff
changeset
 | 
712  | 
|
| 
 
8c1c717a830b
configure SSH hosting via "isabelle phabricator_setup_ssh";
 
wenzelm 
parents: 
71103 
diff
changeset
 | 
713  | 
val ssh_name = isabelle_phabricator_name(name = "ssh")  | 
| 
71111
 
cd166c3904dd
more robust: system ssh service is required for Phabricator ssh service;
 
wenzelm 
parents: 
71109 
diff
changeset
 | 
714  | 
Linux.service_shutdown(ssh_name)  | 
| 
 
cd166c3904dd
more robust: system ssh service is required for Phabricator ssh service;
 
wenzelm 
parents: 
71109 
diff
changeset
 | 
715  | 
|
| 
71109
 
8c1c717a830b
configure SSH hosting via "isabelle phabricator_setup_ssh";
 
wenzelm 
parents: 
71103 
diff
changeset
 | 
716  | 
val old_system_port = read_ssh_port(sshd_conf_system)  | 
| 
 
8c1c717a830b
configure SSH hosting via "isabelle phabricator_setup_ssh";
 
wenzelm 
parents: 
71103 
diff
changeset
 | 
717  | 
    if (old_system_port != system_port) {
 | 
| 
 
8c1c717a830b
configure SSH hosting via "isabelle phabricator_setup_ssh";
 
wenzelm 
parents: 
71103 
diff
changeset
 | 
718  | 
      progress.echo("Reconfigurig system ssh service")
 | 
| 
71111
 
cd166c3904dd
more robust: system ssh service is required for Phabricator ssh service;
 
wenzelm 
parents: 
71109 
diff
changeset
 | 
719  | 
      Linux.service_shutdown("ssh")
 | 
| 
71109
 
8c1c717a830b
configure SSH hosting via "isabelle phabricator_setup_ssh";
 
wenzelm 
parents: 
71103 
diff
changeset
 | 
720  | 
write_ssh_port(sshd_conf_system, system_port)  | 
| 
71111
 
cd166c3904dd
more robust: system ssh service is required for Phabricator ssh service;
 
wenzelm 
parents: 
71109 
diff
changeset
 | 
721  | 
      Linux.service_start("ssh")
 | 
| 
71109
 
8c1c717a830b
configure SSH hosting via "isabelle phabricator_setup_ssh";
 
wenzelm 
parents: 
71103 
diff
changeset
 | 
722  | 
}  | 
| 
 
8c1c717a830b
configure SSH hosting via "isabelle phabricator_setup_ssh";
 
wenzelm 
parents: 
71103 
diff
changeset
 | 
723  | 
|
| 
 
8c1c717a830b
configure SSH hosting via "isabelle phabricator_setup_ssh";
 
wenzelm 
parents: 
71103 
diff
changeset
 | 
724  | 
    progress.echo("Configuring " + ssh_name + " service")
 | 
| 
 
8c1c717a830b
configure SSH hosting via "isabelle phabricator_setup_ssh";
 
wenzelm 
parents: 
71103 
diff
changeset
 | 
725  | 
|
| 71282 | 726  | 
val ssh_command = command_setup(ssh_name, body =  | 
| 71122 | 727  | 
"""if [ "$1" = "$NAME" ]  | 
728  | 
then  | 
|
729  | 
exec "$ROOT/phabricator/bin/ssh-auth" "$@"  | 
|
| 71270 | 730  | 
fi""", exit = "exit 1")  | 
| 
71109
 
8c1c717a830b
configure SSH hosting via "isabelle phabricator_setup_ssh";
 
wenzelm 
parents: 
71103 
diff
changeset
 | 
731  | 
|
| 
 
8c1c717a830b
configure SSH hosting via "isabelle phabricator_setup_ssh";
 
wenzelm 
parents: 
71103 
diff
changeset
 | 
732  | 
File.write(sshd_conf_server,  | 
| 
 
8c1c717a830b
configure SSH hosting via "isabelle phabricator_setup_ssh";
 
wenzelm 
parents: 
71103 
diff
changeset
 | 
733  | 
"""# OpenBSD Secure Shell server for Isabelle/Phabricator  | 
| 
 
8c1c717a830b
configure SSH hosting via "isabelle phabricator_setup_ssh";
 
wenzelm 
parents: 
71103 
diff
changeset
 | 
734  | 
AuthorizedKeysCommand """ + ssh_command.implode + """  | 
| 
 
8c1c717a830b
configure SSH hosting via "isabelle phabricator_setup_ssh";
 
wenzelm 
parents: 
71103 
diff
changeset
 | 
735  | 
AuthorizedKeysCommandUser """ + daemon_user + """  | 
| 
 
8c1c717a830b
configure SSH hosting via "isabelle phabricator_setup_ssh";
 
wenzelm 
parents: 
71103 
diff
changeset
 | 
736  | 
AuthorizedKeysFile none  | 
| 
 
8c1c717a830b
configure SSH hosting via "isabelle phabricator_setup_ssh";
 
wenzelm 
parents: 
71103 
diff
changeset
 | 
737  | 
AllowUsers """ + configs.map(_.name).mkString(" ") + """
 | 
| 
 
8c1c717a830b
configure SSH hosting via "isabelle phabricator_setup_ssh";
 
wenzelm 
parents: 
71103 
diff
changeset
 | 
738  | 
Port """ + server_port + """  | 
| 
 
8c1c717a830b
configure SSH hosting via "isabelle phabricator_setup_ssh";
 
wenzelm 
parents: 
71103 
diff
changeset
 | 
739  | 
Protocol 2  | 
| 
 
8c1c717a830b
configure SSH hosting via "isabelle phabricator_setup_ssh";
 
wenzelm 
parents: 
71103 
diff
changeset
 | 
740  | 
PermitRootLogin no  | 
| 
 
8c1c717a830b
configure SSH hosting via "isabelle phabricator_setup_ssh";
 
wenzelm 
parents: 
71103 
diff
changeset
 | 
741  | 
AllowAgentForwarding no  | 
| 
 
8c1c717a830b
configure SSH hosting via "isabelle phabricator_setup_ssh";
 
wenzelm 
parents: 
71103 
diff
changeset
 | 
742  | 
AllowTcpForwarding no  | 
| 
 
8c1c717a830b
configure SSH hosting via "isabelle phabricator_setup_ssh";
 
wenzelm 
parents: 
71103 
diff
changeset
 | 
743  | 
PrintMotd no  | 
| 
 
8c1c717a830b
configure SSH hosting via "isabelle phabricator_setup_ssh";
 
wenzelm 
parents: 
71103 
diff
changeset
 | 
744  | 
PrintLastLog no  | 
| 
 
8c1c717a830b
configure SSH hosting via "isabelle phabricator_setup_ssh";
 
wenzelm 
parents: 
71103 
diff
changeset
 | 
745  | 
PasswordAuthentication no  | 
| 
 
8c1c717a830b
configure SSH hosting via "isabelle phabricator_setup_ssh";
 
wenzelm 
parents: 
71103 
diff
changeset
 | 
746  | 
ChallengeResponseAuthentication no  | 
| 
 
8c1c717a830b
configure SSH hosting via "isabelle phabricator_setup_ssh";
 
wenzelm 
parents: 
71103 
diff
changeset
 | 
747  | 
PidFile /var/run/""" + ssh_name + """.pid  | 
| 
 
8c1c717a830b
configure SSH hosting via "isabelle phabricator_setup_ssh";
 
wenzelm 
parents: 
71103 
diff
changeset
 | 
748  | 
""")  | 
| 
 
8c1c717a830b
configure SSH hosting via "isabelle phabricator_setup_ssh";
 
wenzelm 
parents: 
71103 
diff
changeset
 | 
749  | 
|
| 
 
8c1c717a830b
configure SSH hosting via "isabelle phabricator_setup_ssh";
 
wenzelm 
parents: 
71103 
diff
changeset
 | 
750  | 
Linux.service_install(ssh_name,  | 
| 
 
8c1c717a830b
configure SSH hosting via "isabelle phabricator_setup_ssh";
 
wenzelm 
parents: 
71103 
diff
changeset
 | 
751  | 
"""[Unit]  | 
| 
 
8c1c717a830b
configure SSH hosting via "isabelle phabricator_setup_ssh";
 
wenzelm 
parents: 
71103 
diff
changeset
 | 
752  | 
Description=OpenBSD Secure Shell server for Isabelle/Phabricator  | 
| 
 
8c1c717a830b
configure SSH hosting via "isabelle phabricator_setup_ssh";
 
wenzelm 
parents: 
71103 
diff
changeset
 | 
753  | 
After=network.target auditd.service  | 
| 
 
8c1c717a830b
configure SSH hosting via "isabelle phabricator_setup_ssh";
 
wenzelm 
parents: 
71103 
diff
changeset
 | 
754  | 
ConditionPathExists=!/etc/ssh/sshd_not_to_be_run  | 
| 
 
8c1c717a830b
configure SSH hosting via "isabelle phabricator_setup_ssh";
 
wenzelm 
parents: 
71103 
diff
changeset
 | 
755  | 
|
| 
 
8c1c717a830b
configure SSH hosting via "isabelle phabricator_setup_ssh";
 
wenzelm 
parents: 
71103 
diff
changeset
 | 
756  | 
[Service]  | 
| 
 
8c1c717a830b
configure SSH hosting via "isabelle phabricator_setup_ssh";
 
wenzelm 
parents: 
71103 
diff
changeset
 | 
757  | 
EnvironmentFile=-/etc/default/ssh  | 
| 
 
8c1c717a830b
configure SSH hosting via "isabelle phabricator_setup_ssh";
 
wenzelm 
parents: 
71103 
diff
changeset
 | 
758  | 
ExecStartPre=/usr/sbin/sshd -f """ + sshd_conf_server.implode + """ -t  | 
| 
 
8c1c717a830b
configure SSH hosting via "isabelle phabricator_setup_ssh";
 
wenzelm 
parents: 
71103 
diff
changeset
 | 
759  | 
ExecStart=/usr/sbin/sshd -f """ + sshd_conf_server.implode + """ -D $SSHD_OPTS  | 
| 
 
8c1c717a830b
configure SSH hosting via "isabelle phabricator_setup_ssh";
 
wenzelm 
parents: 
71103 
diff
changeset
 | 
760  | 
ExecReload=/usr/sbin/sshd -f """ + sshd_conf_server.implode + """ -t  | 
| 
 
8c1c717a830b
configure SSH hosting via "isabelle phabricator_setup_ssh";
 
wenzelm 
parents: 
71103 
diff
changeset
 | 
761  | 
ExecReload=/bin/kill -HUP $MAINPID  | 
| 
 
8c1c717a830b
configure SSH hosting via "isabelle phabricator_setup_ssh";
 
wenzelm 
parents: 
71103 
diff
changeset
 | 
762  | 
KillMode=process  | 
| 
 
8c1c717a830b
configure SSH hosting via "isabelle phabricator_setup_ssh";
 
wenzelm 
parents: 
71103 
diff
changeset
 | 
763  | 
Restart=on-failure  | 
| 
 
8c1c717a830b
configure SSH hosting via "isabelle phabricator_setup_ssh";
 
wenzelm 
parents: 
71103 
diff
changeset
 | 
764  | 
RestartPreventExitStatus=255  | 
| 
 
8c1c717a830b
configure SSH hosting via "isabelle phabricator_setup_ssh";
 
wenzelm 
parents: 
71103 
diff
changeset
 | 
765  | 
Type=notify  | 
| 
 
8c1c717a830b
configure SSH hosting via "isabelle phabricator_setup_ssh";
 
wenzelm 
parents: 
71103 
diff
changeset
 | 
766  | 
RuntimeDirectory=sshd-phabricator  | 
| 
 
8c1c717a830b
configure SSH hosting via "isabelle phabricator_setup_ssh";
 
wenzelm 
parents: 
71103 
diff
changeset
 | 
767  | 
RuntimeDirectoryMode=0755  | 
| 
 
8c1c717a830b
configure SSH hosting via "isabelle phabricator_setup_ssh";
 
wenzelm 
parents: 
71103 
diff
changeset
 | 
768  | 
|
| 
 
8c1c717a830b
configure SSH hosting via "isabelle phabricator_setup_ssh";
 
wenzelm 
parents: 
71103 
diff
changeset
 | 
769  | 
[Install]  | 
| 
 
8c1c717a830b
configure SSH hosting via "isabelle phabricator_setup_ssh";
 
wenzelm 
parents: 
71103 
diff
changeset
 | 
770  | 
WantedBy=multi-user.target  | 
| 
 
8c1c717a830b
configure SSH hosting via "isabelle phabricator_setup_ssh";
 
wenzelm 
parents: 
71103 
diff
changeset
 | 
771  | 
Alias=""" + ssh_name + """.service  | 
| 
 
8c1c717a830b
configure SSH hosting via "isabelle phabricator_setup_ssh";
 
wenzelm 
parents: 
71103 
diff
changeset
 | 
772  | 
""")  | 
| 
 
8c1c717a830b
configure SSH hosting via "isabelle phabricator_setup_ssh";
 
wenzelm 
parents: 
71103 
diff
changeset
 | 
773  | 
|
| 
 
8c1c717a830b
configure SSH hosting via "isabelle phabricator_setup_ssh";
 
wenzelm 
parents: 
71103 
diff
changeset
 | 
774  | 
    for (config <- configs) {
 | 
| 
 
8c1c717a830b
configure SSH hosting via "isabelle phabricator_setup_ssh";
 
wenzelm 
parents: 
71103 
diff
changeset
 | 
775  | 
      progress.echo("phabricator " + quote(config.name) + " port " +  server_port)
 | 
| 
 
8c1c717a830b
configure SSH hosting via "isabelle phabricator_setup_ssh";
 
wenzelm 
parents: 
71103 
diff
changeset
 | 
776  | 
      config.execute("config set diffusion.ssh-port " + Bash.string(server_port.toString))
 | 
| 76129 | 777  | 
      if (server_port == default_system_port) config.execute("config delete diffusion.ssh-port")
 | 
| 
71109
 
8c1c717a830b
configure SSH hosting via "isabelle phabricator_setup_ssh";
 
wenzelm 
parents: 
71103 
diff
changeset
 | 
778  | 
}  | 
| 
 
8c1c717a830b
configure SSH hosting via "isabelle phabricator_setup_ssh";
 
wenzelm 
parents: 
71103 
diff
changeset
 | 
779  | 
}  | 
| 
 
8c1c717a830b
configure SSH hosting via "isabelle phabricator_setup_ssh";
 
wenzelm 
parents: 
71103 
diff
changeset
 | 
780  | 
|
| 
 
8c1c717a830b
configure SSH hosting via "isabelle phabricator_setup_ssh";
 
wenzelm 
parents: 
71103 
diff
changeset
 | 
781  | 
|
| 
 
8c1c717a830b
configure SSH hosting via "isabelle phabricator_setup_ssh";
 
wenzelm 
parents: 
71103 
diff
changeset
 | 
782  | 
/* Isabelle tool wrapper */  | 
| 
 
8c1c717a830b
configure SSH hosting via "isabelle phabricator_setup_ssh";
 
wenzelm 
parents: 
71103 
diff
changeset
 | 
783  | 
|
| 
 
8c1c717a830b
configure SSH hosting via "isabelle phabricator_setup_ssh";
 
wenzelm 
parents: 
71103 
diff
changeset
 | 
784  | 
val isabelle_tool4 =  | 
| 72763 | 785  | 
    Isabelle_Tool("phabricator_setup_ssh", "setup ssh service for all Phabricator installations",
 | 
| 75393 | 786  | 
Scala_Project.here,  | 
| 75394 | 787  | 
      { args =>
 | 
788  | 
var server_port = default_server_port  | 
|
789  | 
var system_port = default_system_port  | 
|
| 
71109
 
8c1c717a830b
configure SSH hosting via "isabelle phabricator_setup_ssh";
 
wenzelm 
parents: 
71103 
diff
changeset
 | 
790  | 
|
| 75394 | 791  | 
        val getopts = Getopts("""
 | 
| 
71109
 
8c1c717a830b
configure SSH hosting via "isabelle phabricator_setup_ssh";
 
wenzelm 
parents: 
71103 
diff
changeset
 | 
792  | 
Usage: isabelle phabricator_setup_ssh [OPTIONS]  | 
| 
 
8c1c717a830b
configure SSH hosting via "isabelle phabricator_setup_ssh";
 
wenzelm 
parents: 
71103 
diff
changeset
 | 
793  | 
|
| 
 
8c1c717a830b
configure SSH hosting via "isabelle phabricator_setup_ssh";
 
wenzelm 
parents: 
71103 
diff
changeset
 | 
794  | 
Options are:  | 
| 
 
8c1c717a830b
configure SSH hosting via "isabelle phabricator_setup_ssh";
 
wenzelm 
parents: 
71103 
diff
changeset
 | 
795  | 
-p PORT sshd port for Phabricator servers (default: """ + default_server_port + """)  | 
| 
 
8c1c717a830b
configure SSH hosting via "isabelle phabricator_setup_ssh";
 
wenzelm 
parents: 
71103 
diff
changeset
 | 
796  | 
-q PORT sshd port for the operating system (default: """ + default_system_port + """)  | 
| 
 
8c1c717a830b
configure SSH hosting via "isabelle phabricator_setup_ssh";
 
wenzelm 
parents: 
71103 
diff
changeset
 | 
797  | 
|
| 
 
8c1c717a830b
configure SSH hosting via "isabelle phabricator_setup_ssh";
 
wenzelm 
parents: 
71103 
diff
changeset
 | 
798  | 
Configure ssh service for all Phabricator installations: a separate sshd  | 
| 
 
8c1c717a830b
configure SSH hosting via "isabelle phabricator_setup_ssh";
 
wenzelm 
parents: 
71103 
diff
changeset
 | 
799  | 
is run in addition to the one of the operating system, and ports need to  | 
| 
 
8c1c717a830b
configure SSH hosting via "isabelle phabricator_setup_ssh";
 
wenzelm 
parents: 
71103 
diff
changeset
 | 
800  | 
be distinct.  | 
| 
 
8c1c717a830b
configure SSH hosting via "isabelle phabricator_setup_ssh";
 
wenzelm 
parents: 
71103 
diff
changeset
 | 
801  | 
|
| 
 
8c1c717a830b
configure SSH hosting via "isabelle phabricator_setup_ssh";
 
wenzelm 
parents: 
71103 
diff
changeset
 | 
802  | 
A particular Phabricator installation is addressed by using its  | 
| 
 
8c1c717a830b
configure SSH hosting via "isabelle phabricator_setup_ssh";
 
wenzelm 
parents: 
71103 
diff
changeset
 | 
803  | 
name as the ssh user; the actual Phabricator user is determined via  | 
| 
 
8c1c717a830b
configure SSH hosting via "isabelle phabricator_setup_ssh";
 
wenzelm 
parents: 
71103 
diff
changeset
 | 
804  | 
stored ssh keys.  | 
| 
 
8c1c717a830b
configure SSH hosting via "isabelle phabricator_setup_ssh";
 
wenzelm 
parents: 
71103 
diff
changeset
 | 
805  | 
""",  | 
| 
 
8c1c717a830b
configure SSH hosting via "isabelle phabricator_setup_ssh";
 
wenzelm 
parents: 
71103 
diff
changeset
 | 
806  | 
"p:" -> (arg => server_port = Value.Int.parse(arg)),  | 
| 
71295
 
6aadbd650280
eliminated pointless option -T: it merely tests ssh config of root, which is not required later;
 
wenzelm 
parents: 
71292 
diff
changeset
 | 
807  | 
"q:" -> (arg => system_port = Value.Int.parse(arg)))  | 
| 
71109
 
8c1c717a830b
configure SSH hosting via "isabelle phabricator_setup_ssh";
 
wenzelm 
parents: 
71103 
diff
changeset
 | 
808  | 
|
| 75394 | 809  | 
val more_args = getopts(args)  | 
810  | 
if (more_args.nonEmpty) getopts.usage()  | 
|
| 
71109
 
8c1c717a830b
configure SSH hosting via "isabelle phabricator_setup_ssh";
 
wenzelm 
parents: 
71103 
diff
changeset
 | 
811  | 
|
| 75394 | 812  | 
val progress = new Console_Progress  | 
| 
71109
 
8c1c717a830b
configure SSH hosting via "isabelle phabricator_setup_ssh";
 
wenzelm 
parents: 
71103 
diff
changeset
 | 
813  | 
|
| 75394 | 814  | 
phabricator_setup_ssh(  | 
815  | 
server_port = server_port, system_port = system_port, progress = progress)  | 
|
816  | 
})  | 
|
| 71299 | 817  | 
|
818  | 
||
819  | 
||
820  | 
/** conduit API **/  | 
|
821  | 
||
| 75393 | 822  | 
  object API {
 | 
| 71332 | 823  | 
/* user information */  | 
824  | 
||
825  | 
sealed case class User(  | 
|
826  | 
id: Long,  | 
|
827  | 
phid: String,  | 
|
828  | 
name: String,  | 
|
829  | 
real_name: String,  | 
|
| 75393 | 830  | 
roles: List[String]  | 
831  | 
    ) {
 | 
|
| 71332 | 832  | 
def is_valid: Boolean =  | 
833  | 
        roles.contains("verified") &&
 | 
|
834  | 
        roles.contains("approved") &&
 | 
|
835  | 
        roles.contains("activated")
 | 
|
836  | 
      def is_admin: Boolean = roles.contains("admin")
 | 
|
837  | 
      def is_regular: Boolean = !(roles.contains("bot") || roles.contains("list"))
 | 
|
838  | 
}  | 
|
839  | 
||
840  | 
||
| 71314 | 841  | 
/* repository information */  | 
842  | 
||
843  | 
sealed case class Repository(  | 
|
844  | 
vcs: VCS.Value,  | 
|
845  | 
id: Long,  | 
|
846  | 
phid: String,  | 
|
847  | 
name: String,  | 
|
848  | 
callsign: String,  | 
|
849  | 
short_name: String,  | 
|
850  | 
importing: Boolean,  | 
|
| 75393 | 851  | 
ssh_url: String  | 
852  | 
    ) {
 | 
|
| 71314 | 853  | 
def is_hg: Boolean = vcs == VCS.hg  | 
854  | 
}  | 
|
855  | 
||
| 75393 | 856  | 
    object VCS extends Enumeration {
 | 
| 71314 | 857  | 
val hg, git, svn = Value  | 
858  | 
def read(s: String): Value =  | 
|
859  | 
        try { withName(s) }
 | 
|
| 74944 | 860  | 
        catch { case _: java.util.NoSuchElementException => error("Unknown vcs type " + quote(s)) }
 | 
| 71314 | 861  | 
}  | 
862  | 
||
863  | 
def edits(typ: String, value: JSON.T): List[JSON.Object.T] =  | 
|
864  | 
      List(JSON.Object("type" -> typ, "value" -> value))
 | 
|
865  | 
||
866  | 
def opt_edits(typ: String, value: Option[JSON.T]): List[JSON.Object.T] =  | 
|
867  | 
value.toList.flatMap(edits(typ, _))  | 
|
868  | 
||
869  | 
||
870  | 
/* result with optional error */  | 
|
871  | 
||
| 75393 | 872  | 
    sealed case class Result(result: JSON.T, error: Option[String]) {
 | 
| 71314 | 873  | 
def ok: Boolean = error.isEmpty  | 
874  | 
def get: JSON.T = if (ok) result else Exn.error(error.get)  | 
|
875  | 
||
876  | 
def get_value[A](unapply: JSON.T => Option[A]): A =  | 
|
877  | 
        unapply(get) getOrElse Exn.error("Bad JSON result: " + JSON.Format(result))
 | 
|
878  | 
||
879  | 
def get_string: String = get_value(JSON.Value.String.unapply)  | 
|
880  | 
}  | 
|
881  | 
||
| 75393 | 882  | 
    def make_result(json: JSON.T): Result = {
 | 
| 71314 | 883  | 
val result = JSON.value(json, "result").getOrElse(JSON.Object.empty)  | 
884  | 
val error_info = JSON.string(json, "error_info")  | 
|
885  | 
val error_code = JSON.string(json, "error_code")  | 
|
886  | 
Result(result, error_info orElse error_code)  | 
|
887  | 
}  | 
|
888  | 
||
889  | 
||
890  | 
/* context for operations */  | 
|
891  | 
||
| 76173 | 892  | 
def apply(server: String, port: Int = 0): API = new API(server, port)  | 
| 71299 | 893  | 
}  | 
894  | 
||
| 76172 | 895  | 
  final class API private(server: String, port: Int) {
 | 
| 71299 | 896  | 
/* connection */  | 
897  | 
||
| 76172 | 898  | 
private def port_suffix: String = if (port > 0) ":" + port else ""  | 
899  | 
override def toString: String = server + port_suffix  | 
|
900  | 
def hg_url: String = "ssh://" + server + port_suffix  | 
|
| 71299 | 901  | 
|
902  | 
||
903  | 
/* execute methods */  | 
|
904  | 
||
| 75393 | 905  | 
    def execute_raw(method: String, params: JSON.T = JSON.Object.empty): JSON.T = {
 | 
| 75394 | 906  | 
      Isabelle_System.with_tmp_file("params", "json") { params_file =>
 | 
| 71300 | 907  | 
        File.write(params_file, JSON.Format(JSON.Object("params" -> JSON.Format(params))))
 | 
| 71299 | 908  | 
val result =  | 
909  | 
Isabelle_System.bash(  | 
|
| 76172 | 910  | 
SSH.client_command(port = port) + " -- " + Bash.string(server) +  | 
| 71300 | 911  | 
" conduit " + Bash.string(method) + " < " + File.bash_path(params_file)).check  | 
| 71299 | 912  | 
JSON.parse(result.out, strict = false)  | 
| 75394 | 913  | 
}  | 
| 71299 | 914  | 
}  | 
915  | 
||
| 71300 | 916  | 
def execute(method: String, params: JSON.T = JSON.Object.empty): API.Result =  | 
917  | 
API.make_result(execute_raw(method, params = params))  | 
|
| 71299 | 918  | 
|
| 71330 | 919  | 
def execute_search[A](  | 
| 75393 | 920  | 
method: String,  | 
921  | 
params: JSON.Object.T,  | 
|
922  | 
unapply: JSON.T => Option[A]  | 
|
923  | 
    ): List[A] = {
 | 
|
| 71330 | 924  | 
val results = new mutable.ListBuffer[A]  | 
925  | 
var after = ""  | 
|
926  | 
||
| 
75382
 
81673c441ce3
tuned: eliminted do-while for the sake of scala3;
 
wenzelm 
parents: 
74944 
diff
changeset
 | 
927  | 
var cont = true  | 
| 
 
81673c441ce3
tuned: eliminted do-while for the sake of scala3;
 
wenzelm 
parents: 
74944 
diff
changeset
 | 
928  | 
      while (cont) {
 | 
| 71330 | 929  | 
val result =  | 
930  | 
          execute(method, params = params ++ JSON.optional("after" -> proper_string(after)))
 | 
|
931  | 
results ++= result.get_value(JSON.list(_, "data", unapply))  | 
|
932  | 
after = result.get_value(JSON.value(_, "cursor", JSON.string0(_, "after")))  | 
|
| 
75382
 
81673c441ce3
tuned: eliminted do-while for the sake of scala3;
 
wenzelm 
parents: 
74944 
diff
changeset
 | 
933  | 
cont = after.nonEmpty  | 
| 
 
81673c441ce3
tuned: eliminted do-while for the sake of scala3;
 
wenzelm 
parents: 
74944 
diff
changeset
 | 
934  | 
}  | 
| 71330 | 935  | 
|
936  | 
results.toList  | 
|
937  | 
}  | 
|
938  | 
||
| 71332 | 939  | 
    def ping(): String = execute("conduit.ping").get_string
 | 
| 71299 | 940  | 
|
941  | 
||
| 71332 | 942  | 
/* users */  | 
| 71299 | 943  | 
|
| 71300 | 944  | 
    lazy val user_phid: String = execute("user.whoami").get_value(JSON.string(_, "phid"))
 | 
945  | 
    lazy val user_name: String = execute("user.whoami").get_value(JSON.string(_, "userName"))
 | 
|
| 71301 | 946  | 
|
| 71332 | 947  | 
def get_users(  | 
948  | 
all: Boolean = false,  | 
|
949  | 
phid: String = "",  | 
|
| 75393 | 950  | 
name: String = ""  | 
951  | 
    ): List[API.User] = {
 | 
|
| 71332 | 952  | 
val constraints: JSON.Object.T =  | 
953  | 
        (for { (key, value) <- List("phids" -> phid, "usernames" -> name) if value.nonEmpty }
 | 
|
954  | 
yield (key, List(value))).toMap  | 
|
955  | 
||
956  | 
      execute_search("user.search",
 | 
|
957  | 
          JSON.Object("queryKey" -> (if (all) "all" else "active"), "constraints" -> constraints),
 | 
|
958  | 
data => JSON.value(data, "fields", fields =>  | 
|
959  | 
              for {
 | 
|
960  | 
id <- JSON.long(data, "id")  | 
|
961  | 
phid <- JSON.string(data, "phid")  | 
|
962  | 
name <- JSON.string(fields, "username")  | 
|
963  | 
real_name <- JSON.string0(fields, "realName")  | 
|
964  | 
roles <- JSON.strings(fields, "roles")  | 
|
965  | 
} yield API.User(id, phid, name, real_name, roles)))  | 
|
966  | 
}  | 
|
967  | 
||
968  | 
def the_user(phid: String): API.User =  | 
|
969  | 
      get_users(phid = phid) match {
 | 
|
970  | 
case List(user) => user  | 
|
971  | 
        case _ => error("Bad user PHID " + quote(phid))
 | 
|
972  | 
}  | 
|
973  | 
||
974  | 
||
975  | 
/* repositories */  | 
|
976  | 
||
| 71306 | 977  | 
def get_repositories(  | 
| 71331 | 978  | 
all: Boolean = false,  | 
979  | 
phid: String = "",  | 
|
980  | 
callsign: String = "",  | 
|
| 75393 | 981  | 
short_name: String = ""  | 
982  | 
    ): List[API.Repository] = {
 | 
|
| 71306 | 983  | 
val constraints: JSON.Object.T =  | 
984  | 
        (for {
 | 
|
985  | 
          (key, value) <- List("phids" -> phid, "callsigns" -> callsign, "shortNames" -> short_name)
 | 
|
986  | 
if value.nonEmpty  | 
|
987  | 
} yield (key, List(value))).toMap  | 
|
988  | 
||
| 71330 | 989  | 
      execute_search("diffusion.repository.search",
 | 
| 71331 | 990  | 
          JSON.Object("queryKey" -> (if (all) "all" else "active"), "constraints" -> constraints),
 | 
| 71330 | 991  | 
data => JSON.value(data, "fields", fields =>  | 
992  | 
              for {
 | 
|
993  | 
vcs_name <- JSON.string(fields, "vcs")  | 
|
994  | 
id <- JSON.long(data, "id")  | 
|
995  | 
phid <- JSON.string(data, "phid")  | 
|
996  | 
name <- JSON.string(fields, "name")  | 
|
997  | 
callsign <- JSON.string0(fields, "callsign")  | 
|
998  | 
short_name <- JSON.string0(fields, "shortName")  | 
|
999  | 
importing <- JSON.bool(fields, "isImporting")  | 
|
| 71306 | 1000  | 
}  | 
| 71330 | 1001  | 
              yield {
 | 
1002  | 
val vcs = API.VCS.read(vcs_name)  | 
|
1003  | 
val url_path =  | 
|
1004  | 
if (short_name.isEmpty) "/diffusion/" + id else "/source/" + short_name  | 
|
1005  | 
val ssh_url =  | 
|
1006  | 
                  vcs match {
 | 
|
1007  | 
case API.VCS.hg => hg_url + url_path  | 
|
1008  | 
case API.VCS.git => hg_url + url_path + ".git"  | 
|
1009  | 
case API.VCS.svn => ""  | 
|
1010  | 
}  | 
|
1011  | 
API.Repository(vcs, id, phid, name, callsign, short_name, importing, ssh_url)  | 
|
1012  | 
}))  | 
|
| 71306 | 1013  | 
}  | 
| 71309 | 1014  | 
|
| 71311 | 1015  | 
def the_repository(phid: String): API.Repository =  | 
1016  | 
      get_repositories(phid = phid) match {
 | 
|
1017  | 
case List(repo) => repo  | 
|
| 71314 | 1018  | 
        case _ => error("Bad repository PHID " + quote(phid))
 | 
| 71311 | 1019  | 
}  | 
1020  | 
||
| 71309 | 1021  | 
def create_repository(  | 
1022  | 
name: String,  | 
|
1023  | 
callsign: String = "", // unique name, UPPERCASE  | 
|
1024  | 
short_name: String = "", // unique name  | 
|
1025  | 
description: String = "",  | 
|
1026  | 
public: Boolean = false,  | 
|
| 75393 | 1027  | 
vcs: API.VCS.Value = API.VCS.hg  | 
1028  | 
    ): API.Repository = {
 | 
|
| 
73120
 
c3589f2dff31
more informative errors: simplify diagnosis of spurious failures reported by users;
 
wenzelm 
parents: 
72763 
diff
changeset
 | 
1029  | 
require(name.nonEmpty, "bad repository name")  | 
| 71309 | 1030  | 
|
1031  | 
val transactions =  | 
|
1032  | 
        API.edits("vcs", vcs.toString) :::
 | 
|
1033  | 
        API.edits("name", name) :::
 | 
|
1034  | 
        API.opt_edits("callsign", proper_string(callsign)) :::
 | 
|
1035  | 
        API.opt_edits("shortName", proper_string(short_name)) :::
 | 
|
1036  | 
        API.opt_edits("description", proper_string(description)) :::
 | 
|
1037  | 
(if (public) Nil  | 
|
1038  | 
         else API.edits("view", user_phid) ::: API.edits("policy.push", user_phid)) :::
 | 
|
1039  | 
        API.edits("status", "active")
 | 
|
1040  | 
||
| 71310 | 1041  | 
val phid =  | 
| 71309 | 1042  | 
        execute("diffusion.repository.edit", params = JSON.Object("transactions" -> transactions))
 | 
1043  | 
.get_value(JSON.value(_, "object", JSON.string(_, "phid")))  | 
|
1044  | 
||
| 71310 | 1045  | 
      execute("diffusion.looksoon", params = JSON.Object("repositories" -> List(phid))).get
 | 
| 71309 | 1046  | 
|
| 71311 | 1047  | 
the_repository(phid)  | 
| 71309 | 1048  | 
}  | 
| 71299 | 1049  | 
}  | 
| 70967 | 1050  | 
}  |