From 3a026bd4e56327882536695e5d859b2fad58e1cb Mon Sep 17 00:00:00 2001 From: Stefan Zeiger Date: Tue, 18 Aug 2020 19:02:05 +0200 Subject: [PATCH] Allow multiple concurrent devbox instances - They have to be started with different --url arguments. - The local git proxy is now bound to an automatically allocated port which gets mapped to 20280 on the devbox. - The tray icon tooltip shows the URL so you can identify the individual devbox instances. --- README.md | 2 ++ devbox/src/DevboxMain.scala | 21 ++++++++++------ devbox/src/cmdproxy/ProxyMain.scala | 2 +- devbox/src/cmdproxy/ProxyServer.scala | 2 +- devbox/src/logger/SyncLogger.scala | 9 ++++--- devbox/src/syncer/AgentApi.scala | 16 ++++++------ devbox/test/src/devbox/DevboxTestMain.scala | 3 ++- devbox/test/src/devbox/DevboxTests.scala | 9 ++++--- launcher/src/Main.scala | 28 +++++++++++---------- 9 files changed, 55 insertions(+), 37 deletions(-) diff --git a/README.md b/README.md index 372d0c5..0d75105 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,8 @@ To prepare an assembly jar, ready to be tested and deployed in the universe/ $ ./mill launcher.assembly ``` +The result can be found in `out/launcher/assembly/dest/out.jar` + ## Tests To run all tests (takes a long time): diff --git a/devbox/src/DevboxMain.scala b/devbox/src/DevboxMain.scala index afbe737..6e0698e 100644 --- a/devbox/src/DevboxMain.scala +++ b/devbox/src/DevboxMain.scala @@ -109,7 +109,7 @@ object DevboxMain { prepResult.exitCode == 0 }, - connect + (_ => connect) ) } System.exit(0) @@ -123,7 +123,7 @@ object DevboxMain { throw e } - def main0(urlOpt: Option[String], config: Config, prepareWithlogs: (String => Unit) => Boolean, connect: Seq[String]) = { + def main0(urlOpt: Option[String], config: Config, prepareWithlogs: (String => Unit) => Boolean, connect: Option[Int] => Seq[String]) = { implicit val ac = new castor.Context.Test( castor.Context.Simple.executionContext, e => { @@ -152,11 +152,12 @@ object DevboxMain { implicit lazy val logger: devbox.logger.SyncLogger = new devbox.logger.SyncLogger.Impl( n => logFileBase / s"$logFileName$n.$logFileExt", 50 * 1024 * 1024, - new castor.ProxyActor((_: Unit) => AgentReadWriteActor.ForceRestart(), syncer.agentActor) + new castor.ProxyActor((_: Unit) => AgentReadWriteActor.ForceRestart(), syncer.agentActor), + urlOpt.map(url => s"$url:") ) lazy val syncer = new Syncer( - new ReliableAgent( + new ReliableAgent[Option[Int]]( log => { val res = prepareWithlogs(log) if (config.proxyGit) { @@ -170,11 +171,17 @@ object DevboxMain { s => os.home / ".devbox" / s"cmdproxy$s.log", 1024 * 1024, ) - server.foreach(_.socket.close()) - server = Some(new ProxyServer(dirMapping)) + var port = 0 + server.foreach { s => + val p = s.socket.getLocalPort + if(p > 0) port = p // make sure to rebind to same port + s.socket.close() + } + server = Some(new ProxyServer(dirMapping, port)) server.foreach(_.start()) } - res + if(res) Some(server.map(_.socket.getLocalPort)) + else None }, connect, os.pwd diff --git a/devbox/src/cmdproxy/ProxyMain.scala b/devbox/src/cmdproxy/ProxyMain.scala index aae2be1..768cf38 100644 --- a/devbox/src/cmdproxy/ProxyMain.scala +++ b/devbox/src/cmdproxy/ProxyMain.scala @@ -19,6 +19,6 @@ object ProxyMain { 1024 * 1024 ) - new ProxyServer(dirMapping = Seq.empty)(logger).start() + new ProxyServer(dirMapping = Seq.empty, ProxyServer.DEFAULT_PORT)(logger).start() } } diff --git a/devbox/src/cmdproxy/ProxyServer.scala b/devbox/src/cmdproxy/ProxyServer.scala index 4fbefed..ab9bd14 100644 --- a/devbox/src/cmdproxy/ProxyServer.scala +++ b/devbox/src/cmdproxy/ProxyServer.scala @@ -34,7 +34,7 @@ object Request { * the end of the output stream */ class ProxyServer(dirMapping: Seq[(os.Path, os.RelPath)], - port: Int = ProxyServer.DEFAULT_PORT) + port: Int) (implicit logger: FileLogger) { // this may throw when binding if the socket is used, but for the moment we just assume there is no other diff --git a/devbox/src/logger/SyncLogger.scala b/devbox/src/logger/SyncLogger.scala index 998e027..0cf1d6b 100644 --- a/devbox/src/logger/SyncLogger.scala +++ b/devbox/src/logger/SyncLogger.scala @@ -32,7 +32,8 @@ object SyncLogger{ class Impl(val dest: String => os.Path, val rotationSize: Long, - onClick: => castor.Actor[Unit]) + onClick: => castor.Actor[Unit], + titleOpt: Option[String]) (implicit ac: castor.Context) extends castor.SimpleActor[Msg] with SyncLogger { def init() = this.send(Init()) @@ -67,7 +68,7 @@ object SyncLogger{ val consoleLogger = new ConsoleLogger(dest, rotationSize) val statusActor = new StatusActor( imageName => IconHandler.icon.setImage(IconHandler.images(imageName)), - tooltip => IconHandler.icon.setToolTip(fansi.Str(tooltip).plainText) + tooltip => IconHandler.setToolTip(fansi.Str(tooltip).plainText) ) def run(msg: Msg) = if (!closed) msg match{ @@ -135,10 +136,12 @@ object SyncLogger{ val icon = new java.awt.TrayIcon(images("blue-sync")) - icon.setToolTip("Devbox Initializing") + setToolTip("Devbox Initializing") val tray = java.awt.SystemTray.getSystemTray() + def setToolTip(s: String) = + icon.setToolTip(titleOpt.map(_ + "\n").getOrElse("") + s) icon.addMouseListener(new MouseListener { def mouseClicked(e: MouseEvent): Unit = onClick.send(()) diff --git a/devbox/src/syncer/AgentApi.scala b/devbox/src/syncer/AgentApi.scala index 55c6734..901786a 100644 --- a/devbox/src/syncer/AgentApi.scala +++ b/devbox/src/syncer/AgentApi.scala @@ -12,19 +12,21 @@ trait AgentApi { def start(logPrepOutput: String => Unit): Boolean } -class ReliableAgent(prepareWithLogs: (String => Unit) => Boolean, - cmd: Seq[String], +class ReliableAgent[T](prepareWithLogs: (String => Unit) => Option[T], + cmd: T => Seq[String], cwd: os.Path) extends AgentApi { var process: os.SubProcess = _ override def start(logPrepOutput: String => Unit): Boolean = { assert(process == null) - val prepPassed = prepareWithLogs(logPrepOutput) - - if (prepPassed) process = os.proc(cmd).spawn(cwd = cwd) - - prepPassed + prepareWithLogs(logPrepOutput) match { + case Some(prepRes) => + process = os.proc(cmd(prepRes)).spawn(cwd = cwd) + true + case _ => + false + } } def stderr = process.stderr def stdout = process.stdout diff --git a/devbox/test/src/devbox/DevboxTestMain.scala b/devbox/test/src/devbox/DevboxTestMain.scala index 6468186..51de10d 100644 --- a/devbox/test/src/devbox/DevboxTestMain.scala +++ b/devbox/test/src/devbox/DevboxTestMain.scala @@ -93,7 +93,8 @@ object DevboxTestMain { implicit lazy val logger: devbox.logger.SyncLogger.Impl = new devbox.logger.SyncLogger.Impl( n => os.pwd / "out" / "scratch" / config.label / s"log$n.txt", 5 * 1024 * 1024, - new castor.ProxyActor((_: Unit) => AgentReadWriteActor.ForceRestart(), syncer.agentActor) + new castor.ProxyActor((_: Unit) => AgentReadWriteActor.ForceRestart(), syncer.agentActor), + None ) lazy val syncer = instantiateSyncer( src, dest, diff --git a/devbox/test/src/devbox/DevboxTests.scala b/devbox/test/src/devbox/DevboxTests.scala index 577cbb4..a284a5d 100644 --- a/devbox/test/src/devbox/DevboxTests.scala +++ b/devbox/test/src/devbox/DevboxTests.scala @@ -127,7 +127,8 @@ object DevboxTests extends TestSuite{ implicit lazy val logger: SyncLogger.Impl = new SyncLogger.Impl( n => logFileBase / s"$logFileName$n.$logFileExt", 5 * 1024 * 1024, - new castor.ProxyActor((_: Unit) => AgentReadWriteActor.ForceRestart(), syncer.agentActor) + new castor.ProxyActor((_: Unit) => AgentReadWriteActor.ForceRestart(), syncer.agentActor), + None ) lazy val syncer = instantiateSyncer( @@ -268,9 +269,9 @@ object DevboxTests extends TestSuite{ logger: SyncLogger) = { val syncer = new Syncer( - new ReliableAgent( - log => /*do nothing*/true, - Seq( + new ReliableAgent[Unit]( + log => /*do nothing*/ Some(()), + _ => Seq( "java", "-cp", System.getenv("AGENT_EXECUTABLE"), "devbox.agent.DevboxAgentMain", "--ignore-strategy", ignoreStrategy, diff --git a/launcher/src/Main.scala b/launcher/src/Main.scala index 9064016..90e29f8 100644 --- a/launcher/src/Main.scala +++ b/launcher/src/Main.scala @@ -29,11 +29,12 @@ object Main { println(s"Unknown arguments: ${remaining.mkString(", ")}") System.exit(1) } - val portFwdArgs = - if (config.proxyGit) - Seq("-R", s"${ProxyServer.DEFAULT_PORT}:localhost:${ProxyServer.DEFAULT_PORT}") - else + def portFwdArgs(port: Option[Int]): Seq[String] = port match { + case Some(p) if(config.proxyGit) => + Seq("-R", s"${ProxyServer.DEFAULT_PORT}:localhost:$p") + case _ => Seq() + } devbox.DevboxMain.main0( Some(ensureInstanceRunning.url), config, @@ -58,15 +59,16 @@ object Main { prepResult.exitCode == 0 }, - Seq( - "ssh", "-C", - "-o", "ExitOnForwardFailure=yes", - "-o", "ServerAliveInterval=4", - "-o", "ServerAliveCountMax=4") ++ - portFwdArgs ++ Seq( - ensureInstanceRunning.url, - s"java -cp ~/.devbox/agent.jar devbox.agent.DevboxAgentMain --log-path ~/.devbox/log.txt --ignore-strategy gitignore --proxy-git-commands ${config.proxyGit}" - ) + { (port: Option[Int]) => Seq( + "ssh", "-C", + "-o", "ExitOnForwardFailure=yes", + "-o", "ServerAliveInterval=4", + "-o", "ServerAliveCountMax=4" + ) ++ portFwdArgs(port) ++ Seq( + ensureInstanceRunning.url, + s"java -cp ~/.devbox/agent.jar devbox.agent.DevboxAgentMain --log-path ~/.devbox/log.txt --ignore-strategy gitignore --proxy-git-commands ${config.proxyGit}" + ) + } ) } }