From 5e0b9a0c2f4bf6f5e5f6a59286abf29379538d14 Mon Sep 17 00:00:00 2001 From: Ethan Atkins Date: Sat, 8 Jun 2019 12:19:26 -0700 Subject: [PATCH] Add welcome banner to sbt shell We want to notify users about the new features available in sbt 1.3.0 to increase visibility. Turbo mode especially can benefit many builds, but we have opted to leave it off by default for now. The banner will be displayed the first time sbt enters the shell command on each sbt run. The banner can be disabled globally with the sbt.banner system property. It can be displayed on a per sbt version by running the skipWelcomeBanner command. That command touches a file in the ~/.sbt/1.0 directory to make it persistent across all projects. --- main/src/main/scala/sbt/Main.scala | 36 ++++++++++++++++++- main/src/main/scala/sbt/internal/Banner.scala | 21 +++++++++++ .../src/main/scala/sbt/internal/SysProp.scala | 2 ++ 3 files changed, 58 insertions(+), 1 deletion(-) create mode 100644 main/src/main/scala/sbt/internal/Banner.scala diff --git a/main/src/main/scala/sbt/Main.scala b/main/src/main/scala/sbt/Main.scala index 3fb29afca..c883c42dd 100644 --- a/main/src/main/scala/sbt/Main.scala +++ b/main/src/main/scala/sbt/Main.scala @@ -9,6 +9,7 @@ package sbt import java.io.{ File, IOException } import java.net.URI +import java.nio.file.{ FileAlreadyExistsException, Files } import java.util.concurrent.ForkJoinPool import java.util.concurrent.atomic.AtomicBoolean import java.util.{ Locale, Properties } @@ -214,6 +215,7 @@ object BuiltinCommands { plugins, addPluginSbtFile, writeSbtVersion, + skipBanner, notifyUsersAboutShell, shell, startServer, @@ -862,7 +864,8 @@ object BuiltinCommands { def shell: Command = Command.command(Shell, Help.more(Shell, ShellDetailed)) { s0 => import sbt.internal.{ ConsolePromptEvent, ConsoleUnpromptEvent } val exchange = StandardMain.exchange - val s1 = exchange run s0 + val welcomeState = displayWelcomeBanner(s0) + val s1 = exchange run welcomeState exchange publishEventMessage ConsolePromptEvent(s0) val minGCInterval = Project .extract(s1) @@ -943,4 +946,35 @@ object BuiltinCommands { Command.command(NotifyUsersAboutShell) { state => notifyUsersAboutShell(state); state } + + private[this] def skipWelcomeFile(state: State, version: String) = { + val base = BuildPaths.getGlobalBase(state).toPath + base.resolve("preferences").resolve(version).resolve(SkipBannerFileName) + } + private def displayWelcomeBanner(state: State): State = { + if (!state.get(bannerHasBeenShown).getOrElse(false)) { + try { + val version = sbtVersion(state) + val skipFile = skipWelcomeFile(state, version) + Files.createDirectories(skipFile.getParent) + val suppress = !SysProp.banner || Files.exists(skipFile) + if (!suppress) state.log.info(Banner(version)) + } catch { case _: IOException => /* Don't let errors in this command prevent startup */ } + state.put(bannerHasBeenShown, true) + } else state + } + private[this] val bannerHasBeenShown = + AttributeKey[Boolean]("banner-has-been-shown", Int.MaxValue) + private[this] val SkipBannerFileName = "skip-banner" + private[this] val SkipBanner = "skipBanner" + private[this] def skipBanner: Command = Command.command(SkipBanner)(skipBanner) + private def skipBanner(state: State): State = { + val skipFile = skipWelcomeFile(state, sbtVersion(state)) + try Files.createFile(skipFile) + catch { + case _: FileAlreadyExistsException => + case e: IOException => state.log.error(s"Couldn't create file $skipFile: $e") + } + state + } } diff --git a/main/src/main/scala/sbt/internal/Banner.scala b/main/src/main/scala/sbt/internal/Banner.scala new file mode 100644 index 000000000..767903a83 --- /dev/null +++ b/main/src/main/scala/sbt/internal/Banner.scala @@ -0,0 +1,21 @@ +/* + * sbt + * Copyright 2011 - 2018, Lightbend, Inc. + * Copyright 2008 - 2010, Mark Harrah + * Licensed under Apache License 2.0 (see LICENSE) + */ + +package sbt.internal + +private[sbt] object Banner { + def apply(version: String): String = + s""" + |Welcome to sbt $version. + |Here are some highlights of this release: + | - Coursier: new default library management using https://get-coursier.io + | - Super shell: displays actively running tasks + | - Turbo mode: makes `test` and `run` faster in interactive sessions. Try it by running `set ThisBuild / turbo := true`. + |See https://www.lightbend.com/blog/sbt-1.3.0-release for full release notes. + |Hide the banner for this release by running `skipBanner`. + |""".stripMargin.linesIterator.filter(_.nonEmpty).mkString("\n") +} diff --git a/main/src/main/scala/sbt/internal/SysProp.scala b/main/src/main/scala/sbt/internal/SysProp.scala index c5dd716f0..138ec14c6 100644 --- a/main/src/main/scala/sbt/internal/SysProp.scala +++ b/main/src/main/scala/sbt/internal/SysProp.scala @@ -93,6 +93,8 @@ object SysProp { coursierOpt.orElse(notIvyOpt).getOrElse(true) } + def banner: Boolean = getOrTrue("sbt.banner") + def turbo: Boolean = getOrFalse("sbt.turbo") def taskTimings: Boolean = getOrFalse("sbt.task.timings")