diff --git a/README.md b/README.md index e55aa7c..2cf0c85 100644 --- a/README.md +++ b/README.md @@ -25,6 +25,15 @@ csrConfiguration := csrConfiguration.value.withProtocolHandlerDependencies( ) ``` +For publish settings in `build.sbt` use: +```scala +publishTo := Some( + dev.rolang.sbt.gar.ArtifactRegistryIvyResolver.create( + "My Registry", "https://${LOCATION}-maven.pkg.dev/${GCP_PROJECT}/maven" + ) +) +``` + The plugin is automatically enabled for all projects and will install a global `artifactregistry://` handler as well as adding the protocol handler to coursier. diff --git a/build.sbt b/build.sbt index f664e53..100b940 100644 --- a/build.sbt +++ b/build.sbt @@ -67,7 +67,7 @@ lazy val core = project Seq(file) }.taskValue, libraryDependencies ++= Seq( - "com.google.cloud" % "google-cloud-storage" % "2.62.0" + "com.google.cloud" % "google-cloud-storage" % "2.67.0" ) ) diff --git a/modules/core/src/main/scala/dev/rolang/gar/ArtifactregistryHandler.scala b/modules/core/src/main/scala/dev/rolang/gar/ArtifactregistryHandler.scala index d21cd64..c640f9a 100644 --- a/modules/core/src/main/scala/dev/rolang/gar/ArtifactregistryHandler.scala +++ b/modules/core/src/main/scala/dev/rolang/gar/ArtifactregistryHandler.scala @@ -46,18 +46,18 @@ object ArtifactRegistryUrlHandlerFactory { new NetHttpTransport() } - def createURLStreamHandler(logger: Logger): ArtifactRegistryUrlHandler = { + def createRequestFactory(logger: Logger): HttpRequestFactory = { val httpTransport = httpTransportFactory.create() - val googleHttpRequestFactory = googleCredentials(logger) match { + googleCredentials(logger) match { case Some(credentials) => - val requestInitializer = new HttpCredentialsAdapter(credentials) - httpTransport.createRequestFactory(requestInitializer) + httpTransport.createRequestFactory(new HttpCredentialsAdapter(credentials)) case None => httpTransport.createRequestFactory() } - - new ArtifactRegistryUrlHandler(googleHttpRequestFactory)(logger) } + def createURLStreamHandler(logger: Logger): ArtifactRegistryUrlHandler = + new ArtifactRegistryUrlHandler(createRequestFactory(logger))(logger) + def install(logger: Logger) = try { new URL("artifactregistry://example.com") diff --git a/modules/sbt/src/main/scala/dev/rolang/sbt/gar/ArtifactRegistryIvyRepository.scala b/modules/sbt/src/main/scala/dev/rolang/sbt/gar/ArtifactRegistryIvyRepository.scala new file mode 100644 index 0000000..01eb064 --- /dev/null +++ b/modules/sbt/src/main/scala/dev/rolang/sbt/gar/ArtifactRegistryIvyRepository.scala @@ -0,0 +1,70 @@ +package dev.rolang.sbt.gar + +import com.google.api.client.http.{ByteArrayContent, GenericUrl, HttpRequestFactory} +import dev.rolang.gar.{ArtifactRegistryUrlHandlerFactory, Logger} +import org.apache.ivy.core.module.descriptor.Artifact +import org.apache.ivy.plugins.repository.{AbstractRepository, Resource} +import org.apache.ivy.plugins.repository.url.URLResource +import org.apache.ivy.plugins.resolver.IBiblioResolver +import org.apache.ivy.util.Message +import sbt.librarymanagement.{RawRepository, Resolver} + +import java.io.File +import java.net.{URI, URL} +import java.nio.file.{Files, StandardCopyOption} + +class ArtifactRegistryIvyRepository(logger: Logger) extends AbstractRepository { + + private lazy val requestFactory: HttpRequestFactory = + ArtifactRegistryUrlHandlerFactory.createRequestFactory(logger) + + override def getName: String = "ArtifactRegistry" + + override def getResource(source: String): Resource = + new URLResource(URI.create(source).toURL) + + override def get(source: String, destination: File): Unit = { + val response = requestFactory.buildGetRequest(toHttpsUrl(source)).execute() + val is = response.getContent + try Files.copy(is, destination.toPath, StandardCopyOption.REPLACE_EXISTING) + finally is.close() + } + + override def put(artifact: Artifact, src: File, destination: String, overwrite: Boolean): Unit = { + logger.info(s"Uploading artifact to: $destination") + val bytes = Files.readAllBytes(src.toPath) + requestFactory + .buildPutRequest(toHttpsUrl(destination), new ByteArrayContent(null, bytes)) + .execute() + } + + override def list(parent: String): java.util.List[String] = + java.util.Collections.emptyList() + + private def toHttpsUrl(raw: String): GenericUrl = { + val url = URI.create(raw).toURL + val g = new GenericUrl() + g.setScheme("https") + g.setHost(url.getHost) + g.appendRawPath(url.getPath) + g + } +} + +object ArtifactRegistryIvyResolver { + + def create(name: String, root: String): Resolver = { + val logger = new Logger { + def info(msg: String): Unit = Message.info(msg) + def error(msg: String): Unit = Message.error(msg) + def debug(msg: String): Unit = Message.debug(msg) + } + val resolver = new IBiblioResolver + resolver.setName(name) + resolver.setRoot(root) + resolver.setM2compatible(true) + resolver.setUseMavenMetadata(true) + resolver.setRepository(new ArtifactRegistryIvyRepository(logger)) + new RawRepository(resolver, name) + } +} diff --git a/modules/sbt/src/main/scala/dev/rolang/sbt/gar/GarPlugin.scala b/modules/sbt/src/main/scala/dev/rolang/sbt/gar/GarPlugin.scala index 52dd1b3..bbcce78 100644 --- a/modules/sbt/src/main/scala/dev/rolang/sbt/gar/GarPlugin.scala +++ b/modules/sbt/src/main/scala/dev/rolang/sbt/gar/GarPlugin.scala @@ -2,8 +2,6 @@ package dev.rolang.sbt.gar import sbt._ import sbt.Keys._ -import java.io.File - import scala.util.{Failure, Success, Try} object GarPlugin extends AutoPlugin { @@ -37,6 +35,11 @@ object GarPlugin extends AutoPlugin { }, csrConfiguration := csrConfiguration.value.withProtocolHandlerDependencies( Seq("dev.rolang" % "gar-coursier_2.13" % dev.rolang.gar.version.value) - ) + ), + publishTo := publishTo.value.map { + case m: sbt.librarymanagement.MavenRepository if m.root.startsWith("artifactregistry://") => + ArtifactRegistryIvyResolver.create(m.name, m.root) + case other => other + } ) ++ super.projectSettings } diff --git a/project/build.properties b/project/build.properties index 30b7fd9..dabdb15 100644 --- a/project/build.properties +++ b/project/build.properties @@ -1 +1 @@ -sbt.version=1.12.0 +sbt.version=1.12.11