sbt-assembly icon indicating copy to clipboard operation
sbt-assembly copied to clipboard

Provide new feature to "shade" configuration keys

Open juanmirocks opened this issue 7 years ago • 3 comments

Shading some libraries (specifically found often with shading Akka) may cause trouble as the configuration keys are changed in the java bytecode, but not in the reference.conf/application.conf files. This problem has been extensively reported (see below links).

I guess one solution would be to "shade" the configuration keys. In other words, rewrite with sbt-assembly the configuration keys programmatically.

Is this feasible, or otherwise already possible?

1 2 3 4

juanmirocks avatar Dec 16 '17 08:12 juanmirocks

Update: I tried a dirty-quick sed (gsed -i.bak 's/\bakka\b/shade.akka/' reference.conf) on the unzipped.jar or create a new MergeStrategy that does the "sedding" in scala.

This fails. Due to the hierarchical HOCON syntax of the conf.files, you cannot wrongly avoid rewriting rules such as play { akka: ... } with simple regexes.

So the only left option I guess would be to, 1) concat the conf files as per usual, 2) load the concatenated file back with lightbend/config and rewrite the keys there. -- However, if included in sbt-assembly, this requires a dependency on lightbend/config. Perhaps it could be a provided dep.

juanmirocks avatar Dec 16 '17 08:12 juanmirocks

Below a quick implementation useful for my use case.

If the author is interested @eed3si9n , I can provide a pull request with a generalized version.

import sbt._
import sbt.Keys._

import java.io.{File, FileOutputStream}
import com.typesafe.config._
import scala.collection.JavaConversions._

import sbtassembly._

import sbt.internal.util._

object Utils {

  private val log = ConsoleOut.systemOut.print _

  object MyMergeStrategy {

    val concatAndShadeConfig: MergeStrategy = new MergeStrategy {
      val name = "concatAndShadeConfig"

      def apply(tempDir: File, path: String, files: Seq[File]): Either[String, Seq[(File, String)]] = {
        val Right(Seq((initFile: File, _))) = MergeStrategy.concat(tempDir, path, files)
        val outputFile = MergeStrategy.createMergeTarget(tempDir, path + ".2")
        shadeConfigFile(initFile, outputFile)
        Right(Seq(outputFile -> path))
      }
    }

    def shadeConfigFile(initFile: File, outputFile: File): Unit = {
      val oldConfig = ConfigFactory.parseFile(initFile).resolve()

      val shadedSet = oldConfig.root.entrySet.map { case entry =>
        val (key, value) = (entry.getKey(), entry.getValue)
        (if (key.startsWith("akka")) s"shade.${key}" else key) -> value
      }

      val shadedConfig = ConfigFactory.parseMap((Map.newBuilder ++= shadedSet).result)
      val shadedConfigSerialized = shadedConfig.root.render(ConfigRenderOptions.concise())

      IO.write(outputFile, shadedConfigSerialized)
    }

  }
}

juanmirocks avatar Dec 16 '17 12:12 juanmirocks

I am having the same issue when trying to shade com.twitter.finagle libraries. More specifically the finagle-toggle artifact. The library will try to read a json file that has the same prefix as com.twitter.finagle which is what I am renaming. While the shading rules handle correctly, moving the resource "json" files to the new shaded folder, the file name itself is the same, which causes error when loading since the new shaded library will try to load the file with modified name

niander avatar Jul 24 '24 22:07 niander