All Downloads are FREE. Search and download functionalities are using the official Maven repository.

izumi.idealingua.compiler.ArtifactPublisher.scala Maven / Gradle / Ivy

The newest version!
package izumi.idealingua.compiler

import izumi.fundamentals.platform.files.IzFiles
import izumi.idealingua.model.publishing.BuildManifest
import izumi.idealingua.model.publishing.manifests.{GoLangBuildManifest, ProtobufBuildManifest}
import izumi.idealingua.translator.IDLLanguage

import java.nio.file.*
import java.time.ZonedDateTime
import scala.annotation.nowarn
import scala.jdk.CollectionConverters.*
import scala.sys.process.*
import scala.util.Try

@nowarn("msg=lazyLines")
class ArtifactPublisher(targetDir: Path, lang: IDLLanguage, creds: Credentials, manifest: BuildManifest) {
  private val log: CompilerLog = CompilerLog.Default

  def publish(): Either[Throwable, Unit] = ((creds, lang, manifest): @unchecked) match {
    case (c: ScalaCredentials, IDLLanguage.Scala, _)                              => publishScala(targetDir, c)
    case (c: TypescriptCredentials, IDLLanguage.Typescript, _)                    => publishTypescript(targetDir, c)
    case (c: GoCredentials, IDLLanguage.Go, m: GoLangBuildManifest)               => publishGo(targetDir, c, m)
    case (c: CsharpCredentials, IDLLanguage.CSharp, _)                            => publishCsharp(targetDir, c)
    case (c: ProtobufCredentials, IDLLanguage.Protobuf, m: ProtobufBuildManifest) => publishProtobuf(targetDir, c, m)
    case (c, l, _) if c.lang != l =>
      Left(
        new IllegalArgumentException(
          "Language and credentials type didn't match. " +
          s"Got credentials for $l, expect for ${c.lang}"
        )
      )
  }

  private def publishScala(targetDir: Path, creds: ScalaCredentials): Either[Throwable, Unit] = Try {
    log.log("Prepare to package Scala sources")
    Process(
      "sbt clean package",
      targetDir.toFile,
    ).lineStream.foreach(log.log)

    log.log("Writing credentials file to ")
    val buildFile    = targetDir.toAbsolutePath.resolve("build.sbt")
    val sbtCredsFile = targetDir.toAbsolutePath.resolve(".credentials")

    val credsLines = Seq(
      "\n",
      // TODO: Gigahorse apears to be cause of `Too many follow-up requests: 21` exception during publishing
      "ThisBuild / updateOptions := updateOptions.value.withGigahorse(false)",
      "\n",
      s"""credentials += Credentials(Path("${sbtCredsFile.toAbsolutePath.toString}").asFile)""",
      "\n",
      s"""
         |ThisBuild / publishTo := {
         |  if (isSnapshot.value)
         |    Some("snapshots" at "${creds.sbtSnapshotsRepo}")
         |  else
         |    Some("releases"  at "${creds.sbtReleasesRepo}")
         |}
      """.stripMargin,
    )

    Files.write(buildFile, credsLines.asJava, StandardOpenOption.WRITE, StandardOpenOption.APPEND)

    Files.write(
      sbtCredsFile,
      Seq[String](
        s"realm=${creds.sbtRealm}",
        s"host=${creds.sbtHost}",
        s"user=${creds.sbtUser}",
        s"password=${creds.sbtPassword}",
      ).asJava,
    )

    Process(
      "sbt publish",
      targetDir.toFile,
    ).lineStream.foreach(log.log)
  }.toEither

  private def publishTypescript(targetDir: Path, creds: TypescriptCredentials): Either[Throwable, Unit] = Try {
    log.log("Prepare to package Typescript sources")

    val packagesDir = Files.list(targetDir.resolve("packages")).filter(_.toFile.isDirectory).iterator().asScala.toSeq.head
    val credsFile   = Paths.get(System.getProperty("user.home")).resolve("~/.npmrc")
    val repoName    = creds.npmRepo.replaceAll("http://", "").replaceAll("https://", "")
    val scope       = packagesDir.getFileName
    val processDir  = targetDir.toFile

    log.log(s"Writing credentials in ${credsFile.toAbsolutePath.getFileName}")
    val scriptLines = List(
      Seq("echo", s"Setting NPM registry for scope $scope to $repoName using user & _password method..."),
      Seq("npm", "config", "set", s"$scope:registry", s"${creds.npmRepo}"),
      Seq("npm", "config", "set", s"//$repoName:email", s"${creds.npmEmail}"),
      Seq("npm", "config", "set", s"//$repoName:username", s"${creds.npmUser}"),
      Seq("npm", "config", "set", s"//$repoName:_password", (Seq("echo", "-n", s"${creds.npmPassword}") #| Seq("openssl", "base64")).!!),
    )

    scriptLines.foreach(s => Process(s, processDir).lineStream.foreach(log.log))

    log.log("Publishing NPM packages")

    log.log("Yarn installing")
    Process("yarn install", processDir).lineStream.foreach(log.log)

    log.log("Yarn building ES5")
    Process("yarn build", processDir).lineStream.foreach(log.log)

    log.log("Yarn building ESNext")
    Process("yarn build-es", processDir).lineStream.foreach(log.log)

    def publishToNpm(dir: Path, packageFileName: String): Unit = {
      Files.list(dir).filter(_.toFile.isDirectory).iterator().asScala.foreach {
        module =>
          val cmd = s"npm publish --force --registry ${creds.npmRepo} ${module.toAbsolutePath.toString}"
          log.log(s"Publish ${module.getFileName}. Cmd: `$cmd`")
          Files.copy(packagesDir.resolve(s"${module.getFileName}/$packageFileName"), module.resolve("package.json"))
          Process(cmd, processDir).lineStream.foreach(log.log)
      }
    }

    publishToNpm(targetDir.resolve("dist"), "package.json")
    publishToNpm(targetDir.resolve("dist-es"), "package.es.json")
  }.toEither

  private def publishCsharp(targetDir: Path, creds: CsharpCredentials): Either[Throwable, Unit] = Try {
    val nuspecDir  = targetDir.resolve("nuspec")
    val nuspecFile = nuspecDir.toFile

    log.log("Prepare to package C# sources")

    log.log("Preparing credentials")
    Process(
      s"nuget sources Add -Name IzumiPublishSource -Source ${creds.nugetRepo}",
      nuspecFile,
    ).#||("true").lineStream.foreach(log.log)

    Process(
      s"nuget setapikey ${creds.nugetUser}:${creds.nugetPassword} -Source IzumiPublishSource",
      nuspecFile,
    ).lineStream.foreach(log.log)

    log.log("Publishing")
    Files.list(nuspecDir).filter(_.getFileName.toString.endsWith(".nuspec")).iterator().asScala.foreach {
      module =>
        Try(
          Process(
            s"nuget pack ${module.getFileName.toString}",
            nuspecFile,
          ).lineStream.foreach(log.log)
        )
    }

    IzFiles.walk(nuspecFile).filter(_.getFileName.toString.endsWith(".nupkg")).foreach {
      pack =>
        Process(
          s"nuget push ${pack.getFileName.toString} -Source IzumiPublishSource",
          nuspecFile,
        ).lineStream.foreach(log.log)
    }
  }.toEither

  private def publishGo(targetDir: Path, creds: GoCredentials, manifest: GoLangBuildManifest): Either[Throwable, Unit] = Try {
    log.log("Prepare to package GoLang sources")

    val env = Seq(
      "GOPATH"      -> s"${targetDir.toAbsolutePath.toString}",
      "GO111MODULE" -> "off",
    )
    IzFiles.recreateDir(targetDir.resolve("src"))

    Files.move(targetDir.resolve("github.com"), targetDir.resolve("src/github.com"), StandardCopyOption.REPLACE_EXISTING)

    Process(
      "go get github.com/gorilla/websocket",
      targetDir.toFile,
      env*
    ).lineStream.foreach(log.log)

    if (manifest.enableTesting) {
      log.log("Testing")
      Process(
        "go test ./...",
        targetDir.resolve("src").toFile,
        env*
      ).lineStream.foreach(log.log)
      log.log("Testing - OK")
    } else {
      log.log("Testing is disabled. Skipping.")
    }

    log.log("Publishing to github repo")

    log.log("Seting up Git")
    val pubKey = targetDir.resolve("go-key.pub")
    Files.write(pubKey, Seq(creds.gitPubKey).asJava)

    Process(Seq("git", "config", "--global", "--replace-all", "user.name", creds.gitUser)).lineStream.foreach(log.log)
    Process(Seq("git", "config", "--global", "--replace-all", "user.email", creds.gitEmail)).lineStream.foreach(println)
    Process(
      Seq(
        "git",
        "config",
        "--global",
        "--replace-all",
        "core.sshCommand",
        s"ssh -i ${pubKey.toAbsolutePath.toString} -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no",
      )
    ).lineStream.foreach(println)

    Process(
      "git config --global --list",
      targetDir.toFile,
    ).lineStream.foreach(log.log)

    Process(
      s"git clone ${creds.gitRepoUrl}",
      targetDir.toFile,
    ).lineStream.foreach(log.log)

    Files
      .list(targetDir.resolve(creds.gitRepoName)).iterator().asScala
      .filter(_.getFileName.toString.charAt(0) != '.')
      .foreach {
        path =>
          IzFiles.erase(path)
      }

    Files.list(targetDir.resolve("src").resolve(manifest.repository.repository)).iterator().asScala.foreach {
      srcDir =>
        Files.move(srcDir, targetDir.resolve(creds.gitRepoName).resolve(srcDir.getFileName.toString))
    }

    Files.write(
      targetDir.resolve(creds.gitRepoName).resolve(".timestamp"),
      Seq(
        ZonedDateTime.now().toString
      ).asJava,
    )
    Files.write(
      targetDir.resolve(creds.gitRepoName).resolve("README.md"),
      Seq(
        s"# ${creds.gitRepoName}",
        s"Auto-generated golang apis, ${manifest.common.version.toString}",
      ).asJava,
    )

    Process(
      "git add .",
      targetDir.resolve(creds.gitRepoName).toFile,
    ).lineStream.foreach(log.log)

    log.log(s"Git commit: 'golang-api-update,version=${manifest.common.version}'")
    Process(
      s"""git commit --no-edit -am 'golang-api-update,version=${manifest.common.version}'""",
      targetDir.resolve(creds.gitRepoName).toFile,
    ).lineStream.foreach(log.log)

    log.log(s"Setting git tag: v${manifest.common.version.toString}")
    Process(
      s"git tag -f v${manifest.common.version.toString}",
      targetDir.resolve(creds.gitRepoName).toFile,
    ).lineStream.foreach(log.log)

    log.log("Git push")
    Process(
      "git push --all -f",
      targetDir.resolve(creds.gitRepoName).toFile,
    ).lineStream.foreach(log.log)
    Process(
      "git push --tags -f",
      targetDir.resolve(creds.gitRepoName).toFile,
    ).lineStream.foreach(log.log)
  }.toEither

  private def publishProtobuf(targetDir: Path, creds: ProtobufCredentials, manifest: ProtobufBuildManifest): Either[Throwable, Unit] = Try {
    log.log("Publishing Protobuf sources to github repo")
    // copy all files to tmp
    val sources    = Files.list(targetDir).iterator().asScala.toList
    val sourcesDir = targetDir.resolve("src_tmp")
    val repoDir    = targetDir.resolve(creds.gitRepoName)

    IzFiles.recreateDir(sourcesDir)
    sources.foreach(src => Files.move(src, sourcesDir.resolve(src.getFileName)))

    log.log("Seting up Git")
    val pubKey = targetDir.resolve("protobuf-key.pub")
    Files.write(pubKey, Seq(creds.gitPubKey).asJava)

    Process(Seq("git", "config", "--global", "--replace-all", "user.name", creds.gitUser)).lineStream.foreach(log.log)
    Process(Seq("git", "config", "--global", "--replace-all", "user.email", creds.gitEmail)).lineStream.foreach(println)
    Process(
      Seq(
        "git",
        "config",
        "--global",
        "--replace-all",
        "core.sshCommand",
        s"ssh -i ${pubKey.toAbsolutePath.toString} -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no",
      )
    ).lineStream.foreach(println)

    Process(
      "git config --global --list",
      targetDir.toFile,
    ).lineStream.foreach(log.log)

    // clone repo
    Process(
      s"git clone ${creds.gitRepoUrl}",
      targetDir.toFile,
    ).lineStream.foreach(log.log)

    // cleanup repo
    Files
      .list(repoDir).iterator().asScala
      .filter(_.getFileName.toString.charAt(0) != '.')
      .foreach(path => IzFiles.erase(path))

    // move everything from tmp to repo
    Files.list(sourcesDir).iterator().asScala.foreach {
      srcDir =>
        Files.move(srcDir, repoDir.resolve(srcDir.getFileName.toString))
    }

    // add def files
    Files.write(
      repoDir.resolve(".timestamp"),
      Seq(
        ZonedDateTime.now().toString
      ).asJava,
    )
    Files.write(
      repoDir.resolve("README.md"),
      Seq(
        s"# ${creds.gitRepoName}",
        s"Auto-generated protobuf sources, ${manifest.common.version.toString}",
      ).asJava,
    )

    // commit repo
    Process(
      "git add .",
      repoDir.toFile,
    ).lineStream.foreach(log.log)

    log.log(s"Git commit: 'protobuf-sources-update,version=${manifest.common.version}'")
    Process(
      s"""git commit --no-edit -am 'protobuf-sources-update,version=${manifest.common.version}'""",
      repoDir.toFile,
    ).lineStream.foreach(log.log)

    log.log(s"Setting git tag: v${manifest.common.version.toString}")
    Process(
      s"git tag -f v${manifest.common.version.toString}",
      repoDir.toFile,
    ).lineStream.foreach(log.log)

    log.log("Git push")
    Process(
      "git push --all -f",
      repoDir.toFile,
    ).lineStream.foreach(log.log)
    Process(
      "git push --tags -f",
      repoDir.toFile,
    ).lineStream.foreach(log.log)
  }.toEither
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy