Repository: 7mind/izumi
Pull request #815 (2020-01-10 14:34:00)

Feature/docker mount
There was an issue with the mounting of local volume. 

codecov-io: # [Codecov](https://codecov.io/gh/7mind/izumi/pull/815?src=pr&el=h1) Report
> :exclamation: No coverage uploaded for pull request base (`series/zio-RC16@ebcad8b`). [Click here to learn what that means](https://docs.codecov.io/docs/error-reference#section-missing-base-commit).
> The diff coverage is `0%`.

[![Impacted file tree graph](https://codecov.io/gh/7mind/izumi/pull/815/graphs/tree.svg?width=650&token=77pa3UU1Je&height=150&src=pr)](https://codecov.io/gh/7mind/izumi/pull/815?src=pr&el=tree)

```diff
@@                Coverage Diff                 @@
##             series/zio-RC16     #815   +/-   ##
==================================================
  Coverage                   ?   70.44%           
==================================================
  Files                      ?      550           
  Lines                      ?    14043           
  Branches                   ?      736           
==================================================
  Hits                       ?     9893           
  Misses                     ?     4150           
  Partials                   ?        0
```


| [Impacted Files](https://codecov.io/gh/7mind/izumi/pull/815?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [...r/src/main/scala/izumi/distage/docker/Docker.scala](https://codecov.io/gh/7mind/izumi/pull/815/diff?src=pr&el=tree#diff-ZGlzdGFnZS9kaXN0YWdlLWZyYW1ld29yay1kb2NrZXIvc3JjL21haW4vc2NhbGEvaXp1bWkvZGlzdGFnZS9kb2NrZXIvRG9ja2VyLnNjYWxh) | `0% <ø> (ø)` | |
| [...n/scala/izumi/distage/docker/DockerContainer.scala](https://codecov.io/gh/7mind/izumi/pull/815/diff?src=pr&el=tree#diff-ZGlzdGFnZS9kaXN0YWdlLWZyYW1ld29yay1kb2NrZXIvc3JjL21haW4vc2NhbGEvaXp1bWkvZGlzdGFnZS9kb2NrZXIvRG9ja2VyQ29udGFpbmVyLnNjYWxh) | `0% <0%> (ø)` | |

------

[Continue to review full report at Codecov](https://codecov.io/gh/7mind/izumi/pull/815?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/7mind/izumi/pull/815?src=pr&el=footer). Last update [ebcad8b...85d4371](https://codecov.io/gh/7mind/izumi/pull/815?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).


Patch:
diff --git a/distage/distage-framework-docker/src/main/scala/izumi/distage/docker/Docker.scala b/distage/distage-framework-docker/src/main/scala/izumi/distage/docker/Docker.scala
index 0a0670070..e682af52a 100644
--- a/distage/distage-framework-docker/src/main/scala/izumi/distage/docker/Docker.scala
+++ b/distage/distage-framework-docker/src/main/scala/izumi/distage/docker/Docker.scala
@@ -55,7 +55,7 @@ object Docker {
 
 
 
-  final case class Mount(host: String, container: String)
+  final case class Mount(host: String, container: String, noCopy: Boolean = false)
 
   final case class ContainerConfig[T](
                                        image: String,
diff --git a/distage/distage-framework-docker/src/main/scala/izumi/distage/docker/DockerContainer.scala b/distage/distage-framework-docker/src/main/scala/izumi/distage/docker/DockerContainer.scala
index 185b9fbc4..8a10dec30 100644
--- a/distage/distage-framework-docker/src/main/scala/izumi/distage/docker/DockerContainer.scala
+++ b/distage/distage-framework-docker/src/main/scala/izumi/distage/docker/DockerContainer.scala
@@ -6,7 +6,7 @@ import com.github.dockerjava.api.command.InspectContainerResponse
 import com.github.dockerjava.api.model._
 import com.github.dockerjava.core.command.PullImageResultCallback
 import distage.TagK
-import izumi.distage.docker.Docker.{AvailablePort, ClientConfig, ContainerConfig, ContainerId, DockerPort, HealthCheckResult, ServicePort}
+import izumi.distage.docker.Docker.{AvailablePort, ClientConfig, ContainerConfig, ContainerId, DockerPort, HealthCheckResult, Mount, ServicePort}
 import izumi.distage.framework.model.exceptions.IntegrationCheckException
 import izumi.distage.model.definition.DIResource
 import izumi.distage.model.effect.DIEffect.syntax._
@@ -43,7 +43,7 @@ trait ContainerDef {
     * To kill all the containers: `docker rm -f $(docker ps -q -a -f 'label=distage.type')`
     *
     */
-  final def make[F[_]: TagK](implicit tag: distage.Tag[Tag]): ProviderMagnet[DIResource[F, Container]] = {
+  final def make[F[_]: TagK](implicit tag: distage.Tag[Container]): ProviderMagnet[DIResource[F, Container]] = {
     tag.discard()
     DockerContainer.resource[F](this)
   }
@@ -204,7 +204,9 @@ object DockerContainer {
         .createContainerCmd(config.image)
         .withLabels((clientw.labels ++ allPortLabels).asJava)
 
-      val volumes = config.mounts.map(m => new Bind(m.host, new Volume(m.container), true))
+      val volumes = config.mounts.map {
+        case Mount(h, c, nc) => new Bind(h, new Volume(c), nc)
+      }
 
       for {
         out <- DIEffect[F].maybeSuspend {
diff --git a/fundamentals/fundamentals-reflection/src/main/scala/izumi/fundamentals/reflection/ReflectionUtil.scala b/fundamentals/fundamentals-reflection/src/main/scala/izumi/fundamentals/reflection/ReflectionUtil.scala
index 274725c7e..8f0971a9f 100644
--- a/fundamentals/fundamentals-reflection/src/main/scala/izumi/fundamentals/reflection/ReflectionUtil.scala
+++ b/fundamentals/fundamentals-reflection/src/main/scala/izumi/fundamentals/reflection/ReflectionUtil.scala
@@ -99,16 +99,8 @@ object ReflectionUtil {
     tpe.typeSymbol.isClass && tpe.typeSymbol.asClass == u.definitions.ByNameParamClass
   }
 
-  final def allPartsStrong(tpe: Universe#Type): Boolean = {
-    def selfStrong = {
-      !tpe.typeSymbol.isParameter ||
-        tpe.typeParams.exists {
-          t =>
-            t == tpe.typeSymbol ||
-              t.typeSignature == tpe.typeSymbol.typeSignature ||
-              (t.name eq tpe.typeSymbol.name)
-        }
-    }
+  def allPartsStrong(tpe: Universe#Type): Boolean = {
+    val selfStrong = isSelfStrong(tpe)
     def prefixStrong = {
       tpe match {
         case t: Universe#TypeRefApi =>
@@ -118,7 +110,7 @@ object ReflectionUtil {
       }
     }
     def argsStrong = {
-      tpe.finalResultType.typeArgs.forall {
+      tpe.dealias.finalResultType.typeArgs.forall {
         arg =>
           tpe.typeParams.contains(arg.typeSymbol) ||
             allPartsStrong(arg)
@@ -137,6 +129,26 @@ object ReflectionUtil {
     selfStrong && prefixStrong && argsStrong && intersectionStructStrong
   }
 
+  def isSelfStrong(tpe: Universe#Type): Boolean = {
+    !(tpe.typeSymbol.isParameter || (
+      tpe.isInstanceOf[Universe#TypeRefApi] &&
+        tpe.asInstanceOf[Universe#TypeRefApi].pre.isInstanceOf[Universe#ThisTypeApi] &&
+        tpe.typeSymbol.isAbstract && !tpe.typeSymbol.isClass && isNotDealiasedFurther(tpe)
+      )) ||
+      tpe.typeParams.exists {
+        t =>
+          t == tpe.typeSymbol ||
+            t.typeSignature == tpe.typeSymbol.typeSignature ||
+            (t.name eq tpe.typeSymbol.name)
+      }
+  }
+
+  def isNotDealiasedFurther(tpe: Universe#Type): Boolean = {
+    val u: Universe = null
+    val tpe1: u.Type = tpe.asInstanceOf[u.Type]
+    tpe1.dealias =:= tpe1
+  }
+
   def intersectionTypeMembers[U <: SingletonUniverse](targetType: U#Type): List[U#Type] = {
     def go(tpe: U#Type): List[U#Type] = {
       tpe match {
diff --git a/fundamentals/fundamentals-reflection/src/main/scala/izumi/fundamentals/reflection/TagMacro.scala b/fundamentals/fundamentals-reflection/src/main/scala/izumi/fundamentals/reflection/TagMacro.scala
index 173aee1c7..3ca266cce 100644
--- a/fundamentals/fundamentals-reflection/src/main/scala/izumi/fundamentals/reflection/TagMacro.scala
+++ b/fundamentals/fundamentals-reflection/src/main/scala/izumi/fundamentals/reflection/TagMacro.scala
@@ -44,9 +44,8 @@ class TagMacro(val c: blackbox.Context) {
   @inline final def makeHKTag[ArgStruct: c.WeakTypeTag]: c.Expr[HKTag[ArgStruct]] = {
     val argStruct = weakTypeOf[ArgStruct]
     val ctor = ltagMacro.unpackArgStruct(argStruct)
-    // FIXME: this check can never succeed for ArgStruct!//it can
-    if (ReflectionUtil.allPartsStrong(argStruct)) {
-      logger.log(s"HK: found Strong ArgStruct, returning $argStruct")
+    if (ReflectionUtil.allPartsStrong(ctor)) {
+      logger.log(s"HK: found Strong ctor=$ctor in ArgStruct, returning $argStruct")
       makeHKTagFromStrongTpe(ctor)
     } else {
       makeHKTagImpl(ctor)
@@ -87,7 +86,7 @@ class TagMacro(val c: blackbox.Context) {
           logger.log(s"HK type B $ctor ${ctor.typeSymbol}")
           val msg = s"  could not find implicit value for ${tagFormat(lambdaResult)}: $lambdaResult is a type parameter without an implicit Tag!"
           addImplicitError(msg)
-          c.abort(c.enclosingPosition, getImplicitError())
+          abortWithImplicitError()
 
         // type constructor is a type parameter AND has type arguments
         // we should resolve type constructor separately from an HKTag
@@ -186,7 +185,7 @@ class TagMacro(val c: blackbox.Context) {
     if (getImplicitError().endsWith(":")) { // yep
       logger.log(s"Got continuation implicit error: ${getImplicitError()}")
     } else {
-      resetImplicitError()
+      resetImplicitError(weakTypeOf[T])
       addImplicitError("\n\n<trace>: ")
     }
 
@@ -227,7 +226,7 @@ class TagMacro(val c: blackbox.Context) {
   @inline
   protected[this] def mkStruct(originalRefinement: Type): c.Expr[LightTypeTag] = {
     originalRefinement.decls
-      .find(_.info.typeSymbol.isParameter)
+      .find(symbol => !ReflectionUtil.isSelfStrong(symbol.info))
       .foreach {
         s =>
           val msg = s"  Encountered a type parameter ${s.info} as a part of structural refinement of $originalRefinement: It's not yet supported to summon a Tag for ${s.info} in that position!"
@@ -259,7 +258,7 @@ class TagMacro(val c: blackbox.Context) {
           logger.log(s"type B $ctor ${ctor.typeSymbol}")
           val msg = s"  could not find implicit value for ${tagFormat(tpe)}: $tpe is a type parameter without an implicit Tag!"
           addImplicitError(msg)
-          c.abort(c.enclosingPosition, getImplicitError())
+          abortWithImplicitError()
 
         // type constructor is a type parameter AND has type arguments
         // we should resolve type constructor separately from an HKTag
@@ -291,7 +290,7 @@ class TagMacro(val c: blackbox.Context) {
 
   @inline
   private[this] final def getCtorKindIfCtorIsTypeParameter(tpe: Type): Option[Kind] = {
-    if (tpe.typeSymbol.isParameter) Some(kindOf(tpe))
+    if (!ReflectionUtil.isSelfStrong(tpe)) Some(kindOf(tpe))
     else None
   }
 
@@ -366,7 +365,7 @@ class TagMacro(val c: blackbox.Context) {
         val error = hktagSummonHelpfulErrorMessage(tpe, kind)
         val msg = s"  could not find implicit value for ${tagFormat(tpe)}$error"
         addImplicitError(msg)
-        c.abort(c.enclosingPosition, getImplicitError())
+        abortWithImplicitError()
     }
   }
 
@@ -384,14 +383,18 @@ class TagMacro(val c: blackbox.Context) {
     ).getOrElse(defaultTagImplicitError)
   }
 
+  def abortWithImplicitError(): Nothing = {
+    c.abort(c.enclosingPosition, getImplicitError())
+  }
+
   @inline
   protected[this] def addImplicitError(err: String): Unit = {
     setImplicitError(s"${getImplicitError()}\n$err")
   }
 
   @inline
-  protected[this] def resetImplicitError(): Unit = {
-    setImplicitError(defaultTagImplicitError)
+  protected[this] def resetImplicitError(tpe: Type): Unit = {
+    setImplicitError(defaultTagImplicitError.replace("${T}", tpe.toString))
   }
 
   @inline


Test patch:
diff --git a/distage/distage-core/src/test/scala/izumi/distage/impl/TagTest.scala b/distage/distage-core/src/test/scala/izumi/distage/impl/TagTest.scala
index 0cfa18352..648370543 100644
--- a/distage/distage-core/src/test/scala/izumi/distage/impl/TagTest.scala
+++ b/distage/distage-core/src/test/scala/izumi/distage/impl/TagTest.scala
@@ -7,11 +7,12 @@ import izumi.fundamentals.platform.functional.Identity
 import izumi.fundamentals.platform.language.Quirks._
 import izumi.fundamentals.reflection.Tags.HKTag
 import izumi.fundamentals.reflection.macrortti._
-import org.scalatest.WordSpec
 import org.scalatest.exceptions.TestFailedException
+import org.scalatest.{Assertions, WordSpec}
 
 import scala.reflect.ClassTag
 import scala.reflect.runtime.{universe => ru}
+import scala.util.Try
 
 trait XY[Y] {
   type Z = id[Y]
@@ -19,11 +20,18 @@ trait XY[Y] {
   implicit def tagZ: Tag[Z]
 }
 
-trait ZY {
+trait ZY extends Assertions {
   type T
+  type U = T
+  type V = List[T]
+  type A = List[Option[Int]]
   val x: String = "5"
-
   object y
+
+  val tagT = intercept[TestFailedException](assertCompiles("Tag[T]"))
+  val tagU = intercept[TestFailedException](assertCompiles("Tag[U]"))
+  val tagV = intercept[TestFailedException](assertCompiles("Tag[V]"))
+  val tagA = Try(assertCompiles("Tag[A]"))
 }
 
 // https://github.com/scala/bug/issues/11139
@@ -93,6 +101,16 @@ class TagTest extends WordSpec with XY[String] {
 
   class ApplePaymentProvider[F0[_]] extends H1
 
+  trait DockerContainer[T]
+  trait ContainerDef {
+    type T
+
+    def make(implicit t: Tag[T]) = {
+      t.discard()
+      Tag[DockerContainer[T]]
+    }
+  }
+
   "Tag" should {
 
     "Work for any concrete type" in {
@@ -490,6 +508,21 @@ class TagTest extends WordSpec with XY[String] {
       assert(tag.tag == Tag[BIOService[Lambda[(E, A) => Either[A, E]]]].tag)
     }
 
+    "correctly resolve abstract types inside traits when summoned inside trait" in {
+      val a = new ContainerDef {}
+      val b = new ContainerDef {}
+
+      assert(a.make.tag == Tag[DockerContainer[a.T]].tag)
+      assert(a.make.tag != Tag[DockerContainer[b.T]].tag)
+      assert(Tag[DockerContainer[a.T]].tag == Tag[DockerContainer[a.T]].tag)
+
+      val zy = new ZY {}
+      assert(zy.tagT.getMessage contains "could not find implicit value for Tag[")
+      assert(zy.tagU.getMessage contains "could not find implicit value for Tag[")
+      assert(zy.tagV.getMessage contains "could not find implicit value for Tag[")
+      assert(zy.tagA.isSuccess)
+    }
+
     "progression test: cannot resolve a higher-kinded type in a higher-kinded tag in a named deeply-nested type lambda" in {
       val t = intercept[TestFailedException] {
         assertCompiles(
diff --git a/distage/distage-framework-docker/src/test/scala/izumi/distage/testkit/docker/DockerContainerProviderTest.scala b/distage/distage-framework-docker/src/test/scala/izumi/distage/testkit/docker/DockerContainerProviderTest.scala
new file mode 100644
index 000000000..83ae8266f
--- /dev/null
+++ b/distage/distage-framework-docker/src/test/scala/izumi/distage/testkit/docker/DockerContainerProviderTest.scala
@@ -0,0 +1,14 @@
+package izumi.distage.testkit.docker
+
+import distage.SafeType
+import izumi.distage.docker.DockerContainer
+import izumi.distage.docker.examples.PostgresDocker
+import izumi.distage.model.definition.DIResource
+import izumi.fundamentals.platform.functional.Identity
+import org.scalatest.WordSpec
+
+final class DockerContainerProviderTest extends WordSpec {
+  "Return type is correct" in {
+    assert(PostgresDocker.make[Identity].get.ret == SafeType.get[DIResource[Identity, DockerContainer[PostgresDocker.Tag]]])
+  }
+}