Skip to content

Commit 0fe40e9

Browse files
authoredAug 7, 2023
Allow picking up credentials from environment variables (#629)
* Upgrade Coursier to respect COURSIER_CREDENTIALS string * Make sure we respect COURSIER_REPOSITORIES * Add tests for Coursier env variables with a local server
1 parent 8480863 commit 0fe40e9

File tree

4 files changed

+175
-14
lines changed

4 files changed

+175
-14
lines changed
 

‎build.sbt

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@ lazy val V =
1111
val protobuf = "3.15.6"
1212
val protoc =
1313
"3.17.3" // the oldest protoc version with Apple M1 support, see https://github.com/scalapb/ScalaPB/issues/1024#issuecomment-860126568
14-
val coursier = "2.0.8"
14+
val coursier = "2.1.5"
15+
val scalaXml = "2.1.0"
1516
val bsp = "2.0.0-M13"
1617
val moped = "0.1.11"
1718
val gradle = "7.0"
@@ -250,7 +251,7 @@ lazy val cli = project
250251
List(
251252
"io.get-coursier" %% "coursier" % V.coursier,
252253
"org.scalameta" % "mtags-interfaces" % V.metals,
253-
"org.scala-lang.modules" %% "scala-xml" % "1.3.0",
254+
"org.scala-lang.modules" %% "scala-xml" % V.scalaXml,
254255
"com.lihaoyi" %% "requests" % V.requests,
255256
"org.scalameta" %% "moped" % V.moped,
256257
"org.scalameta" %% "ascii-graphs" % "0.1.2",
@@ -502,7 +503,12 @@ lazy val buildTools = project
502503
s"-Dsemanticdb.pluginpath=${(javacPlugin / Compile / Keys.`package`).value}",
503504
s"-Dsemanticdb.sourceroot=${(ThisBuild / baseDirectory).value}",
504505
s"-Dsemanticdb.targetroot=${(agent / Compile / target).value / "semanticdb-targetroot"}"
505-
)
506+
),
507+
Test / envVars ++=
508+
Map(
509+
"SCIP_JAVA_CLI" -> ((cli / pack).value / "bin" / "scip-java").toString
510+
),
511+
Test / fork := true
506512
)
507513
.dependsOn(agent, unit)
508514

‎scip-java/src/main/scala/com/sourcegraph/scip_java/Dependencies.scala

Lines changed: 25 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,13 @@ case class Dependencies(
2929

3030
object Dependencies {
3131
val empty = Dependencies(Nil, Fetch.Result(), Fetch.Result())
32+
33+
private val cachePolicies = List(CachePolicy.LocalOnly, CachePolicy.Update)
3234
private val cache: FileCache[Task] = FileCache[Task]()
33-
.noCredentials
34-
.withCachePolicies(List(CachePolicy.LocalOnly, CachePolicy.Update))
35+
.withCachePolicies(cachePolicies)
3536
.withTtl(Duration.Inf)
3637
.withChecksums(Nil)
38+
3739
private val defaultExtraRepositories = List[Repository](
3840
Repositories.google,
3941
Repositories.clojars,
@@ -79,10 +81,17 @@ object Dependencies {
7981
val deps = dependencies.map(parseDependency)
8082
val provided = deps.flatMap(d => resolveProvidedDeps(d))
8183
def nonTransitiveDeps = deps.map(_.withTransitive(false))
82-
val fetch = Fetch[Task](Cache.default)
83-
.addDependencies(deps: _*)
84-
.addDependencies(provided: _*)
85-
.addRepositories(defaultExtraRepositories: _*)
84+
85+
val fetch = {
86+
val fetch0 = Fetch[Task](cache)
87+
.addDependencies(deps: _*)
88+
.addDependencies(provided: _*)
89+
90+
if (!sys.env.contains("COURSIER_REPOSITORIES")) {
91+
fetch0.addRepositories(defaultExtraRepositories: _*)
92+
} else
93+
fetch0
94+
}
8695

8796
val classpath = fetch.runResult()
8897
val sources = fetch
@@ -102,11 +111,16 @@ object Dependencies {
102111
}
103112

104113
def resolveProvidedDeps(dep: Dependency): Seq[Dependency] = {
105-
val artifacts = Resolve[Task](Cache.default)
106-
.addDependencies(dep)
107-
.addRepositories(defaultExtraRepositories: _*)
108-
.run()
109-
.artifacts()
114+
val resolve = {
115+
val resolve0 = Resolve[Task](cache).addDependencies(dep)
116+
if (!sys.env.contains("COURSIER_REPOSITORIES")) {
117+
resolve0.addRepositories(defaultExtraRepositories: _*)
118+
} else
119+
resolve0
120+
}
121+
122+
val artifacts = resolve.run().artifacts()
123+
110124
for {
111125
artifact <- artifacts
112126
metadata <- artifact.extra.get("metadata").toList
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
package tests
2+
3+
import java.net.URI
4+
5+
import scala.collection.mutable.ListBuffer
6+
import scala.jdk.CollectionConverters._
7+
8+
import com.sun.net.httpserver._
9+
10+
case class PasswordProtectedServer(user: String, pwd: String) {
11+
import PasswordProtectedServer._
12+
private class MyHandler(storage: ListBuffer[SimpleHttpRequest])
13+
extends HttpHandler {
14+
val auth =
15+
new BasicAuthenticator("") {
16+
override def checkCredentials(
17+
username: String,
18+
password: String
19+
): Boolean = username == user && password == pwd
20+
}
21+
override def handle(t: HttpExchange): Unit = {
22+
val headers = t
23+
.getRequestHeaders()
24+
.asScala
25+
.toMap
26+
.flatMap { case (k, v) =>
27+
v.asScala.headOption.map(k -> _)
28+
}
29+
30+
val url = t.getRequestURI()
31+
storage.synchronized {
32+
storage.addOne(SimpleHttpRequest(url, headers))
33+
}
34+
35+
auth.authenticate(t) match {
36+
case f: Authenticator.Failure =>
37+
t.sendResponseHeaders(f.getResponseCode(), 0L)
38+
case r: Authenticator.Retry =>
39+
t.sendResponseHeaders(r.getResponseCode(), 0L)
40+
case r: Authenticator.Success =>
41+
t.sendResponseHeaders(404, 0L)
42+
}
43+
44+
t.close()
45+
}
46+
}
47+
def runWith[A](f: RunningServer => A): (List[SimpleHttpRequest], A) = {
48+
val storage = ListBuffer.empty[SimpleHttpRequest]
49+
val server = HttpServer
50+
.create(new java.net.InetSocketAddress("localhost", 0), 0);
51+
val result =
52+
try {
53+
server.createContext("/", new MyHandler(storage))
54+
server.setExecutor(null) // creates a default executor
55+
server.start()
56+
f(
57+
RunningServer(
58+
new URI(
59+
s"http://${server.getAddress().getHostName()}:${server.getAddress().getPort()}"
60+
),
61+
() => server.stop(0)
62+
)
63+
)
64+
} finally {
65+
server.stop(0)
66+
}
67+
68+
storage.toList -> result
69+
}
70+
}
71+
72+
object PasswordProtectedServer {
73+
case class SimpleHttpRequest(url: URI, simpleHeaders: Map[String, String])
74+
75+
case class RunningServer(address: URI, shutdown: () => Unit)
76+
}

‎tests/buildTools/src/test/scala/tests/ScipBuildToolSuite.scala

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,71 @@ import com.sourcegraph.scip_java.{BuildInfo => V}
44

55
class ScipBuildToolSuite extends BaseBuildToolSuite {
66
override def tags = List(SkipWindows)
7+
8+
test("COURSIER_CREDENTIALS and COURSIER_REPOSITORIES are respected") {
9+
10+
val cli = sys.env.getOrElse("SCIP_JAVA_CLI", fail("wwaaaa"))
11+
12+
val Username = "hello"
13+
val Password = "world"
14+
15+
val (requests, _) = PasswordProtectedServer(Username, Password).runWith {
16+
run =>
17+
val env = Map(
18+
"COURSIER_REPOSITORIES" -> run.address.toString(),
19+
"COURSIER_CREDENTIALS" -> s"localhost $Username:$Password"
20+
)
21+
22+
val tmp = os.temp.dir(prefix = "scip-java")
23+
os.write(
24+
tmp / "lsif-java.json",
25+
// We use non-existent library to make sure caches are never used
26+
s""" {"dependencies": ["bla.bla.nonexistent-library:junit:4.13.1"]} """
27+
.trim
28+
)
29+
os.write(
30+
tmp / "foo" / "Example.java",
31+
"package foo;\npublic class Example{}",
32+
createFolders = true
33+
)
34+
35+
val result = os
36+
.proc(cli, "index", "--build-tool=scip")
37+
.call(cwd = tmp, env = env, check = false)
38+
39+
os.remove.all(tmp)
40+
41+
assertNotEquals(result.exitCode, 0)
42+
}
43+
44+
assert(
45+
requests.nonEmpty,
46+
"No requests were sent to the local server - suggesting that COURSIER_REPOSITORIES is not respected by ScipBuildTool"
47+
)
48+
49+
assert(
50+
clue(requests)
51+
.filter { r =>
52+
r.simpleHeaders.contains("Authorization")
53+
}
54+
.nonEmpty,
55+
"No requests with Authorization header were sent to local server - suggesting that COURSIER_CREDENTIALS is not respected"
56+
)
57+
58+
requests.flatMap(_.simpleHeaders.get("Authorization")).distinct match {
59+
case List(value) =>
60+
assertEquals(clue(value), "Basic " + base64("hello:world"))
61+
case other =>
62+
fail(
63+
s"Multiple credential variations were passed to local server: $other"
64+
)
65+
}
66+
67+
}
68+
69+
private def base64(str: String) =
70+
new String(java.util.Base64.getEncoder().encode(str.getBytes))
71+
772
checkBuild(
873
"basic",
974
"""|/lsif-java.json

0 commit comments

Comments
 (0)
Please sign in to comment.