Skip to content

Commit d22aa44

Browse files
authored
Merge pull request #186 from lwronski/feature/install-home
Add more interactive install-home command
2 parents 51291f9 + f447d89 commit d22aa44

File tree

9 files changed

+244
-45
lines changed

9 files changed

+244
-45
lines changed

modules/cli/src/main/scala/scala/cli/ScalaCli.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,8 @@ object ScalaCli extends CommandsEntryPoint {
3131
Package,
3232
Run,
3333
SetupIde,
34-
Test
34+
Test,
35+
Version
3536
)
3637

3738
lazy val progName = (new Argv0).get("scala-cli")

modules/cli/src/main/scala/scala/cli/commands/InstallHome.scala

Lines changed: 78 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -4,60 +4,98 @@ import caseapp._
44
import coursier.env.{EnvironmentUpdate, ProfileUpdater}
55

66
import scala.io.StdIn.readLine
7-
import scala.util.Properties
7+
import scala.util.{Properties, Try}
88

99
object InstallHome extends ScalaCommand[InstallHomeOptions] {
1010
override def hidden: Boolean = true
11-
def run(options: InstallHomeOptions, args: RemainingArgs): Unit = {
1211

13-
val binDirPath = scala.build.Directories.default().binRepoDir
14-
val scalaCliBinPath = binDirPath / "scala-cli"
15-
16-
if (os.exists(scalaCliBinPath))
17-
if (options.force) os.remove.all(scalaCliBinPath)
18-
else if (coursier.paths.Util.useAnsiOutput()) {
19-
println(
20-
"scala-cli already exists. Do you want to override it [Y/n]"
21-
)
22-
val replace = readLine()
23-
if (replace == "Y")
24-
os.remove.all(scalaCliBinPath)
25-
else {
26-
System.err.println("Abort")
27-
sys.exit(1)
28-
}
29-
}
30-
else {
31-
System.err.println(
32-
s"Error: scala-cli already exists. Pass -f or --force to force erasing it."
33-
)
12+
private def isOutOfDate(newVersion: String, oldVersion: String): Boolean = {
13+
import coursier.core.Version
14+
15+
Version(newVersion) > Version(oldVersion)
16+
}
17+
18+
private def logEqual(version: String) = {
19+
System.err.println(
20+
s"Scala-cli $version is already installed and up-to-date."
21+
)
22+
sys.exit(0)
23+
}
24+
25+
private def logUpdate(env: Boolean, newVersion: String, oldVersion: String) =
26+
if (!env) println(
27+
s"""scala-cli $oldVersion is already installed and out-of-date.
28+
|scala-cli will be updated to version $newVersion
29+
|""".stripMargin
30+
)
31+
32+
private def logDowngrade(env: Boolean, newVersion: String, oldVersion: String) =
33+
if (!env && coursier.paths.Util.useAnsiOutput()) {
34+
println(s"scala-cli $oldVersion is already installed and up-to-date.")
35+
println(s"Do you want to downgrade scala-cli to version $newVersion [Y/n]")
36+
val response = readLine()
37+
if (response != "Y") {
38+
System.err.println("Abort")
3439
sys.exit(1)
3540
}
41+
}
42+
else {
43+
System.err.println(
44+
s"Error: scala-cli is already installed $oldVersion and up-to-date. Downgrade to $newVersion pass -f or --force."
45+
)
46+
sys.exit(1)
47+
}
48+
49+
def run(options: InstallHomeOptions, args: RemainingArgs): Unit = {
50+
51+
val binDirPath =
52+
options.binDirPath.getOrElse(scala.build.Directories.default().binRepoDir / "scala-cli")
53+
val newScalaCliBinPath = os.Path(options.scalaCliBinaryPath, os.pwd)
54+
55+
val newVersion: String =
56+
os.proc(newScalaCliBinPath, "version").call(cwd = os.pwd).out.text.trim
57+
58+
// Backward compatibility - previous versions not have the `--version` parameter
59+
val oldVersion: String = Try {
60+
os.proc(binDirPath / options.binaryName, "version").call(cwd = os.pwd).out.text.trim
61+
}.toOption.getOrElse("0.0.0")
62+
63+
if (os.exists(binDirPath))
64+
if (options.force) () // skip logging
65+
else if (newVersion == oldVersion) logEqual(newVersion)
66+
else if (isOutOfDate(newVersion, oldVersion))
67+
logUpdate(options.env, newVersion, oldVersion)
68+
else logDowngrade(options.env, newVersion, oldVersion)
69+
70+
os.remove.all(binDirPath)
3671

3772
os.copy(
38-
from = os.Path(options.scalaCliBinaryPath, os.pwd),
39-
to = scalaCliBinPath / "scala-cli",
73+
from = newScalaCliBinPath,
74+
to = binDirPath / options.binaryName,
4075
createFolders = true
4176
)
4277
if (!Properties.isWin)
43-
os.perms.set(scalaCliBinPath / "scala-cli", os.PermSet.fromString("rwxrwxr-x"))
78+
os.perms.set(binDirPath / options.binaryName, os.PermSet.fromString("rwxr-xr-x"))
4479

45-
val update = EnvironmentUpdate(Nil, Seq("PATH" -> scalaCliBinPath.toString()))
80+
if (options.env)
81+
println(s"""export PATH="$binDirPath:$$PATH"""")
82+
else {
4683

47-
val didUpdate =
48-
if (Properties.isWin) {
49-
val updater = CustomWindowsEnvVarUpdater().withUseJni(Some(coursier.paths.Util.useJni()))
50-
updater.applyUpdate(update)
51-
}
52-
else {
53-
val updater = ProfileUpdater()
54-
updater.applyUpdate(update)
55-
}
84+
val update = EnvironmentUpdate(Nil, Seq("PATH" -> binDirPath.toString()))
85+
86+
val didUpdate =
87+
if (Properties.isWin) {
88+
val updater = CustomWindowsEnvVarUpdater().withUseJni(Some(coursier.paths.Util.useJni()))
89+
updater.applyUpdate(update)
90+
}
91+
else {
92+
val updater = ProfileUpdater()
93+
updater.applyUpdate(update)
94+
}
5695

57-
if (didUpdate)
58-
println("Successfully installed scala-cli")
59-
else
60-
System.err.println(s"scala-cli is already installed")
96+
if (didUpdate) "Profile was updated"
6197

98+
println(s"Successfully installed scala-cli $newVersion")
99+
}
62100
}
63101
}

modules/cli/src/main/scala/scala/cli/commands/InstallHomeOptions.scala

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,16 @@ final case class InstallHomeOptions(
1111
@Name("f")
1212
@HelpMessage("Overwrite scala-cli if exists")
1313
force: Boolean = false,
14-
)
15-
// format: on
14+
@HelpMessage("Binary name")
15+
binaryName: String = "scala-cli",
16+
@HelpMessage("Print the env update")
17+
env: Boolean = false,
18+
@HelpMessage("Binary directory")
19+
binDir: Option[String] = None
20+
) {
21+
// format: on
22+
lazy val binDirPath = binDir.map(os.Path(_, os.pwd))
23+
}
1624

1725
object InstallHomeOptions {
1826
implicit lazy val parser: Parser[InstallHomeOptions] = Parser.derive

modules/cli/src/main/scala/scala/cli/commands/SharedCompilationServerOptions.scala

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ package scala.cli.commands
22

33
import caseapp._
44
import com.google.gson.Gson
5-
import coursier.core.Version
65

76
import java.io.{BufferedReader, File, FileReader}
87
import java.nio.file.{AtomicMoveNotSupportedException, FileAlreadyExistsException, Files}
@@ -168,7 +167,9 @@ final case class SharedCompilationServerOptions(
168167
parseDuration("connection server startup timeout", bloopStartupTimeout)
169168

170169
def minimumBloopVersion = Constants.bloopVersion
171-
def acceptBloopVersion = Some((v: String) => Version(v) < Version(minimumBloopVersion))
170+
171+
import coursier.core.Version
172+
def acceptBloopVersion = Some((v: String) => Version(v) < Version(minimumBloopVersion))
172173

173174
def bloopDefaultJvmOptions(logger: Logger): List[String] = {
174175
val file = new File(bloopGlobalOptionsFile)
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package scala.cli.commands
2+
3+
import caseapp._
4+
5+
import scala.build.internal.Constants
6+
7+
object Version extends ScalaCommand[VersionOptions] {
8+
override def group = "Miscellaneous"
9+
def run(options: VersionOptions, args: RemainingArgs): Unit = {
10+
println(Constants.version)
11+
}
12+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package scala.cli.commands
2+
3+
import caseapp._
4+
5+
@HelpMessage("Print scala-cli version")
6+
final case class VersionOptions()
7+
8+
object VersionOptions {
9+
implicit lazy val parser: Parser[VersionOptions] = Parser.derive
10+
implicit lazy val help: Help[RunOptions] = Help.derive
11+
}
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
package scala.cli.integration
2+
3+
import com.eed3si9n.expecty.Expecty.expect
4+
5+
import scala.util.Properties
6+
7+
class InstallHomeTests extends munit.FunSuite {
8+
9+
val firstVersion = "0.0.1"
10+
val secondVersion = "0.0.2"
11+
val dummyScalaCliFirstName = "DummyScalaCli-1.scala"
12+
val dummyScalaCliSecondName = "DummyScalaCli-2.scala"
13+
val dummyScalaCliBinName = "scala-cli-dummy-test"
14+
val testInputs = TestInputs(
15+
Seq(
16+
os.rel / dummyScalaCliFirstName ->
17+
s"""
18+
|object DummyScalaCli extends App {
19+
| println(\"$firstVersion\")
20+
|}""".stripMargin,
21+
os.rel / dummyScalaCliSecondName ->
22+
s"""
23+
|object DummyScalaCli extends App {
24+
| println(\"$secondVersion\")
25+
|}""".stripMargin
26+
)
27+
)
28+
29+
private def packageDummyScalaCli(root: os.Path, dummyScalaCliFileName: String, output: String) = {
30+
// format: off
31+
val cmd = Seq[os.Shellable](
32+
TestUtil.cli, "package", dummyScalaCliFileName, "-o", output
33+
)
34+
// format: on
35+
os.proc(cmd).call(
36+
cwd = root,
37+
stdin = os.Inherit,
38+
stdout = os.Inherit
39+
)
40+
}
41+
42+
private def installScalaCli(
43+
root: os.Path,
44+
binVersion: String,
45+
binDirPath: os.Path,
46+
force: Boolean
47+
) = {
48+
// format: off
49+
val cmdInstallVersion = Seq[os.Shellable](
50+
TestUtil.cli, "install-home",
51+
"--env",
52+
"--scala-cli-binary-path", binVersion ,
53+
"--binary-name", dummyScalaCliBinName,
54+
"--bin-dir", binDirPath
55+
) ++ (if(force) Seq[os.Shellable]("--force") else Seq.empty)
56+
// format: on
57+
os.proc(cmdInstallVersion).call(
58+
cwd = root,
59+
stdin = os.Inherit,
60+
stdout = os.Inherit
61+
)
62+
}
63+
64+
def runInstallHome(): Unit = {
65+
66+
testInputs.fromRoot { root =>
67+
val binDirPath = root / ".scala" / "scala-cli"
68+
69+
val binDummyScalaCliFirst = dummyScalaCliFirstName.stripSuffix(".scala").toLowerCase
70+
val binDummyScalaCliSecond = dummyScalaCliSecondName.stripSuffix(".scala").toLowerCase
71+
72+
packageDummyScalaCli(root, dummyScalaCliFirstName, binDummyScalaCliFirst)
73+
packageDummyScalaCli(root, dummyScalaCliSecondName, binDummyScalaCliSecond)
74+
75+
// install 1 version
76+
installScalaCli(root, binDummyScalaCliFirst, binDirPath, force = true)
77+
78+
println(binDirPath / dummyScalaCliBinName)
79+
80+
val v1Install = os.proc(binDirPath / dummyScalaCliBinName).call(
81+
cwd = root,
82+
stdin = os.Inherit
83+
).out.text.trim
84+
expect(v1Install == firstVersion)
85+
86+
// update to 2 version
87+
installScalaCli(root, binDummyScalaCliSecond, binDirPath, force = false)
88+
89+
val v2Update = os.proc(binDirPath / dummyScalaCliBinName).call(
90+
cwd = root,
91+
stdin = os.Inherit
92+
).out.text.trim
93+
expect(v2Update == secondVersion)
94+
95+
// downgrade to 1 version with force
96+
installScalaCli(root, binDummyScalaCliFirst, binDirPath, force = true)
97+
98+
val v1Downgrade = os.proc(binDirPath / dummyScalaCliBinName).call(
99+
cwd = root,
100+
stdin = os.Inherit
101+
).out.text.trim
102+
expect(v1Downgrade == firstVersion)
103+
}
104+
}
105+
106+
if (!Properties.isWin && TestUtil.isCI)
107+
test("updating and downgrading dummy scala-cli using install-home command") {
108+
runInstallHome()
109+
}
110+
111+
}

website/docs/reference/cli-options.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -285,6 +285,7 @@ Available in commands:
285285
- [`run`](./commands#run)
286286
- [`setup-ide`](./commands#setup-ide)
287287
- [`test`](./commands#test)
288+
- [`version`](./commands#version)
288289

289290

290291
<!-- Automatically generated, DO NOT EDIT MANUALLY -->
@@ -343,6 +344,18 @@ Aliases: `-f`
343344

344345
Overwrite scala-cli if exists
345346

347+
#### `--binary-name`
348+
349+
Binary name
350+
351+
#### `--env`
352+
353+
Print the env update
354+
355+
#### `--bin-dir`
356+
357+
Binary directory
358+
346359
## Java options
347360

348361
Available in commands:

website/docs/reference/commands.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,10 @@ Accepts options:
216216
- [test](./cli-options.md#test-options)
217217
- [watch](./cli-options.md#watch-options)
218218

219+
## `version`
220+
221+
Print scala-cli version
222+
219223
## Hidden commands
220224

221225
### `add-path`

0 commit comments

Comments
 (0)