Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 34 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,23 +12,22 @@ addSbtPlugin("com.codecommit" % "sbt-github-packages" % "<version>")

Published for sbt 1.x. No dependencies.

In order to *publish* artifacts via this plugin, you will need to override the `githubOwner` and `githubRepository` setting keys to the relevant values for your project. For example:
In order to *publish* artifacts via this plugin, you will need to override the `githubPublishToRepository` setting key to the relevant values for your project. For example:

```sbt
githubOwner := "djspiewak"
githubRepository := "sbt-github-packages"
githubPublishToRepository := GitHubRepository("djspiewak", "sbt-github-packages", TokenSource.Environment("GH_TOKEN"))
```

In the event that you do *not* override these values, you will see a warning like the following:

```
[warn] undefined keys `githubOwner` and `githubRepository`
[warn] undefined keys `githubPublishToRepository`
[warn] retaining pre-existing publication settings
```

The reason this functionality is disabled by default is you may wish to utilize the `Resolver` syntax (described below) *without* overriding your default publication mechanism. In general, I wouldn't necessarily recommend this, since it means that your publication will not be self-contained within a single artifact realm, but that's a relatively common problem anyway these days so it's probably not a big deal.

As a note on publication, *only* `publishMavenStyle := true` (the default) is supported. If you explicitly override this setting to `false`, the sbt-github-packages plugin will produce an error and refuse to load (unless `githubOwner` and/or `githubRepository` are undefined). The reason for this is to remove a bit of a foot-gun: GitHub Packages will silently allow you to publish Ivy-style packages, and will even show it within the UI, but will not allow you to *resolve* them.
As a note on publication, *only* `publishMavenStyle := true` (the default) is supported. If you explicitly override this setting to `false`, the sbt-github-packages plugin will produce an error and refuse to load (unless `githubPublishToRepository` is undefined). The reason for this is to remove a bit of a foot-gun: GitHub Packages will silently allow you to publish Ivy-style packages, and will even show it within the UI, but will not allow you to *resolve* them.

Once everything is configured, run `sbt publish` to publish the package.

Expand All @@ -54,6 +53,7 @@ sealed trait TokenSource extends Product with Serializable {

object TokenSource {
final case class Environment(variable: String) extends TokenSource
final case class Property(variable: String) extends TokenSource
final case class GitConfig(key: String) extends TokenSource
final case class Or(primary: TokenSource, secondary: TokenSource) extends TokenSource
}
Expand All @@ -80,6 +80,18 @@ This assumes you have your token stored there like this:
token = TOKEN_DATA
```

To use a token from the process properties it should be specified when running sbt:
```bash
$ sbt -DGITHUB_TOKEN=abcdef12345cafebab
```

or create `.sbtopts` file in the project directory with the following content:
```
-DGITHUB_TOKEN=abcdef12345cafeba
```

The option above is suitable for importing the project into IntelliJ IDEA.

The `||` combinator allows you to configure multiple token sources which will be tried in order on first-read of the setting.

Note that your CI server will need to set the `GITHUB_TOKEN` environment variable as well (if using the `Environment` token source), as well as any collaborators on your project. The environment-specific nature of these login credentials are a major part of why they are *not* just strings sitting in the `build.sbt` file. As an example, if you're using Travis, you can do something like the following:
Expand Down Expand Up @@ -134,16 +146,27 @@ It will NOT allow for read access to *private* packages within the same organiza

The following setting keys are defined:

- `githubOwner : String` Defines the organization or user name that owns the package registry to which this project will be published
- `githubRepository : String` The repository which hosts this project under the organization/user defined in the other setting
- `githubTokenSource : TokenSource` (*defaults to `Environment("GITHUB_TOKEN")`*) Where the plugin should go to read the GitHub API token to use in authentication. `TokenSource` has two possible values: `Environment(variable: String)` and `GitConfig(key: String)`. You can compose multiple sources together using `||`, which will result in each being attempted in order from left to right. This is mostly just a convenience. You're free to do whatever you want. Just don't, like, put it in your build.
- `githubSuppressPublicationWarning : Boolean` (*defaults to `false`*) If you're just using this plugin as a means to *resolve* artifacts, not to publish them, the publication warning may serve as an annoyance more than anything else. Setting this to `true` will suppress the normal warning text when you fail to define `githubOwner` or `githubRepository`.
- `githubOwner : String` **deprecated** Defines the organization or user name that owns the package registry to which this project will be published
- `githubRepository : String` **deprecated** The repository which hosts this project under the organization/user defined in the other setting
- `githubTokenSource : TokenSource` **deprecated** (*defaults to `Environment("GITHUB_TOKEN")`*) Where the plugin should go to read the GitHub API token to use in authentication. `TokenSource` has three possible values: `Environment(variable: String)`, `Property(variable: String)`, and `GitConfig(key: String)`. You can compose multiple sources together using `||`, which will result in each being attempted in order from left to right. This is mostly just a convenience. You're free to do whatever you want. Just don't, like, put it in your build.
- `githubRepositories: Seq[GitHubRepository]` Defines GitHub repositories that should be used for the dependencies lookup. A different token source can be used per repository.
```scala
githubRepositories ++= Seq(
GitHubRepositry("owner1", "public-repo", TokenSource.Property("PUBLIC_TOKEN")),
GitHubRepositry("owner1", "private-repo", TokenSource.Property("PRIVATE_TOKEN"))
)
```
- `githubPublishToRepository: GitHubRepository` Defines a GitHub repository the package should be published to.
```scala
githubPublishToRepository := GitHubRepository("owner2", "private-repo", TokenSource.Property("PRIVATE_TOKEN"))
```
- `githubSuppressPublicationWarning : Boolean` (*defaults to `false`*) If you're just using this plugin as a means to *resolve* artifacts, not to publish them, the publication warning may serve as an annoyance more than anything else. Setting this to `true` will suppress the normal warning text when you fail to define `githubPublishToRepository`.
- `githubPublishTo : Option[Resolver]` The default `publishTo` target for GitHub Packages. This setting is useful for switching `publishTo` target to [sbt-sonatype](https://github.com/xerial/sbt-sonatype) or GitHub Packages:

```scala
// Switch publishTo target for using Sonatype if RELEASE_SONATYPE env is true,
// otherwise publish to GitHub Packages:
val RELEASE_TO_SONATYPE = sys.env.getOrElse("RELEASE_SONATYPE", "false").toBoolean
publishTo := if(RELEASE_SONATYPE) sonatypePublishTo.value else githubPublishTo.value

`homepage` and `scmInfo` will be automatically set for you if `githubOwner` and `githubRepository` are themselves set.
```
`homepage` and `scmInfo` will be automatically set for you if `githubPublishToRepository` is set.
47 changes: 39 additions & 8 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,36 @@
* limitations under the License.
*/

name := "sbt-github-packages"
lazy val `sbt-github-packages` = project
.in(file("."))
.enablePlugins(SbtPlugin)
.settings(
name := "sbt-github-packages"
)
.dependsOn(common, `gh-publisher`, `gh-resolver`)
.aggregate(common, `gh-publisher`, `gh-resolver`)

lazy val `gh-publisher` = project
.in(file("modules/publisher"))
.enablePlugins(SbtPlugin)
.settings(
name := "sbt-github-packages-publisher"
)
.dependsOn(common)

lazy val `gh-resolver` = project
.in(file("modules/resolver"))
.enablePlugins(SbtPlugin)
.settings(
name := "sbt-github-packages-resolver"
)
.dependsOn(common)

lazy val common = project
.in(file("modules/common"))
.settings(
name := "sbt-github-packages-common"
)

ThisBuild / baseVersion := "0.5"

Expand All @@ -23,13 +52,11 @@ ThisBuild / publishGithubUser := "djspiewak"
ThisBuild / publishFullName := "Daniel Spiewak"

ThisBuild / sbtPlugin := true
ThisBuild / sbtVersion := "1.3.3"
ThisBuild / sbtVersion := "1.5.6"

enablePlugins(SbtPlugin)
ThisBuild / homepage := Some(url("https://github.com/djspiewak/sbt-github-packages"))

homepage := Some(url("https://github.com/djspiewak/sbt-github-packages"))

scmInfo := Some(
ThisBuild / scmInfo := Some(
ScmInfo(
url("https://github.com/djspiewak/sbt-github-packages"),
"scm:git@github.com:djspiewak/sbt-github-packages.git"))
Expand All @@ -39,5 +66,9 @@ developers := List(

publishMavenStyle := true

scriptedLaunchOpts ++= Seq("-Dplugin.version=" + version.value)
scriptedBufferLog := true
ThisBuild / scriptedLaunchOpts ++= Seq(
"-Dplugin.version=" + version.value,
"-DSTARTUP_PROPERTY_TOKEN=ghp_123321"
)

ThisBuild / scriptedBufferLog := true
38 changes: 38 additions & 0 deletions modules/common/src/main/scala/sbtghpackages/GitHubRepository.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
* Copyright 2019 Daniel Spiewak
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package sbtghpackages

final class GitHubRepository(
val owner: String,
val repository: String,
val tokenSource: TokenSource
) {
def realm: String = s"GitHub Package Registry ($owner${if (repository != "_") s"/$repository" else ""})"

def mavenUrl: String = s"https://maven.pkg.github.com/$owner/$repository"

def repoUrl: String = s"https://github.com/$owner/$repository"

def scm: String = s"scm:git@github.com:$owner/$repository.git"
}

object GitHubRepository {

def apply(owner: String, repository: String, source: TokenSource): GitHubRepository =
new GitHubRepository(owner, repository, source)

}
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ sealed trait TokenSource extends Product with Serializable {

object TokenSource {
final case class Environment(variable: String) extends TokenSource
final case class Property(key: String) extends TokenSource
final case class GitConfig(key: String) extends TokenSource
final case class Or(primary: TokenSource, secondary: TokenSource) extends TokenSource
}
73 changes: 73 additions & 0 deletions modules/common/src/main/scala/sbtghpackages/Utils.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
/*
* Copyright 2019 Daniel Spiewak
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package sbtghpackages

import sbt.{Credentials, Logger}

import scala.sys.process._
import scala.util.Try

private[sbtghpackages] object Utils {

def reportUnresolvedToken(log: Logger, repo: GitHubRepository): Unit =
log.error(
s"""|Cannot resolve token for ${repo.realm}
|Make sure at least one token source is configured properly:
${helpMessages(repo.tokenSource).map(message => s"| * $message").mkString("\n")}
|""".stripMargin
)

def inferredGitHubCredentials(repo: GitHubRepository): Option[Credentials] =
for {
token <- resolveTokenSource(repo.tokenSource)
} yield Credentials(repo.realm, "maven.pkg.github.com", "_", token) // user is ignored by GitHub, so just use "_"

def resolveTokenSource(tokenSource: TokenSource): Option[String] =
tokenSource match {
case TokenSource.Or(primary, secondary) =>
resolveTokenSource(primary).orElse(resolveTokenSource(secondary))

case TokenSource.Environment(variable) =>
sys.env.get(variable)

case TokenSource.Property(key) =>
sys.props.get(key)

case TokenSource.GitConfig(key) =>
Try(s"git config $key".!!).map(_.trim).toOption
}

def helpMessages(tokenSource: TokenSource): List[String] = {
def loop(input: TokenSource, output: List[String]): List[String] =
input match {
case TokenSource.Or(primary, secondary) =>
loop(primary, Nil) ::: loop(secondary, Nil) ::: output

case TokenSource.Environment(variable) =>
s"Use `$variable=<token> sbt` or `export $variable=<token>; sbt`." :: output

case TokenSource.Property(key) =>
s"Use `sbt -D$key=<token>` or update `.sbtopts` file." :: output

case TokenSource.GitConfig(key) =>
s"Use `git config $key <token>`." :: output
}

loop(tokenSource, Nil)
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/*
* Copyright 2019 Daniel Spiewak
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package sbtghpackages

import sbt._

trait GitHubPackagesPublisherKeys {
val githubPublishToRepository = settingKey[GitHubRepository]("The GitHub repository the package should be published to")
val githubSuppressPublicationWarning = settingKey[Boolean]("Whether or not to suppress the publication warning (default: false, meaning the warning will be printed)")
val githubPublishTo = taskKey[Option[Resolver]]("publishTo setting for deploying artifacts to GitHub packages")
}

object GitHubPackagesPublisherKeys extends GitHubPackagesPublisherKeys
Loading