aboutsummaryrefslogtreecommitdiff
path: root/plugins/sonatype-release/src/sonatype/SonatypeLib.scala
blob: 4e5d613a783abc793572eb456aa32d8f36ee39d0 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
package cbt
package sonatype

import java.io.File
import java.net.URL
import java.nio.file.Files._
import java.nio.file.Paths

/**
  * Sonatype release process is:
  * • get your profile info to publish artifacts
  * • open staging repository to publish artifacts
  * • publish signed artifacts and signatures to staging repository
  * • close staging repository
  * • promote staging repository
  * • drop staging repository
  */

object SonatypeLib {

  val serviceURI: String = "https://oss.sonatype.org/service/local"

  val snapshotsURI: String = "https://oss.sonatype.org/content/repositories/snapshots"

  /**
    * login:password for Sonatype access.
    * Order of credentials lookup:
    * • environment variables SONATYPE_USERNAME and SONATYPE_PASSWORD
    * • ~/.cbt/sonatype-credentials
    */
  def credentials: String = {
    def fromEnv = for {
      username <- Option(System.getenv("SONATYPE_USERNAME"))
      password <- Option(System.getenv("SONATYPE_PASSWORD"))
    } yield s"$username:$password"

    def fromFile = {
      for {
        home <- Option(System.getProperty("user.home"))
        credsPath = Paths.get(home, ".cbt", "sonatype-credentials")
      } yield new String(readAllBytes(credsPath)).trim
    }

    fromEnv
      .orElse(fromFile)
      .getOrElse(throw new Exception(
        "No Sonatype credentials found! You can provide them via SONATYPE_USERNAME, SONATYPE_PASSWORD env variables, " +
          "or in ~/.cbt/sonatype-credentials file as login:password"
      ))
  }
}

final case class SonatypeLib(
  profileName: String,
  serviceURI: String = SonatypeLib.serviceURI,
  snapshotsURI: String = SonatypeLib.snapshotsURI,
  credentials: String = SonatypeLib.credentials // FIXME: maybe hide this from cli ?
)( implicit logger: Logger ){
  private val lib: Lib = new Lib(logger)
  private def log: String => Unit = logger.log("sonatype-release",_)

  val api = new SonatypeHttpApi(serviceURI, credentials, profileName)(log)
  /*
   * Signed publish steps:
   * • create new staging repo
   * • create artifacts and sign them
   * • publish jars to created repo
  */
  def publishSigned( artifacts: Seq[File], releaseFolder: String ) = {
    import api._
    System.err.println(lib.blue("Publishing to Sonatype"))

    def publish(deployURI: String) = lib.publishSigned(
      artifacts, new URL(deployURI ++ releaseFolder), Some(credentials)
    )

    if (releaseFolder.endsWith("-SNAPSHOT")){
      val urls = publish(snapshotsURI)
      System.err.println(lib.green("Successfully published snapshot on Sonatype!"))
      urls
    } else {
      val profile = getStagingProfile
      val repoId = createStagingRepo(profile)
      val urls = publish(
        serviceURI ++ "/staging/deployByRepositoryId/" ++ repoId.string
      )
      System.err.println(lib.green("Successfully uploaded jars to Sonatype!"))
      finishRelease( getStagingRepoById(repoId), profile )
      System.err.println(lib.green("Successfully released uploaded jars to Maven Central!"))
      urls
    }
  }

  /*
  /**
    * Release is:
    * • find staging repo related to current profile;
    * • close this staging repo;
    * • wait until this repo is released;
    * • drop this repo.
    */
  private def release: ExitCode = {
    val profile = getStagingProfile()

    System.err.println("Releasing jars to Maven Central!")

    getStagingRepos(profile).toList match {
      case Nil =>
        System.err.println(lib.red("No staging repositories found, you need to publish artifacts first."))
        ExitCode.Failure
      case repo :: Nil =>
        finishRelease(repo, profile)
        log(lib.green(s"Successfully released artifact"))
        ExitCode.Success
      case repos =>
        lib.pickOne(
          lib.blue(s"More than one staging repo found. Select one of them:"),
          repos
        ){ repo => s"${repo.repositoryId} in state: ${repo.state}" }.map{ repo =>
          finishRelease(repo, profile)
          log(lib.green(s"Successfully released artifact"))
          ExitCode.Success
        } getOrElse {
          System.err.println(lib.red("Wrong repository number, try again please."))
          ExitCode.Failure
        }
    }
  }
  */
}