From 64cacb18a4ddb8d20ab8425fe0de7726d4355395 Mon Sep 17 00:00:00 2001 From: lidice Date: Fri, 22 Jan 2016 07:44:35 +0900 Subject: [PATCH 01/15] Extend API to allow CRUD labels Add Labels API * List all labels for this repository * Get a single label * Create a label * Update a label * Delete a label Reject duplicated label name Add test case for LabelsService --- .../scala/gitbucket/core/api/ApiLabel.scala | 21 ++++ .../gitbucket/core/api/CreateALabel.scala | 18 +++ .../scala/gitbucket/core/api/JsonFormat.scala | 1 + .../core/controller/LabelsController.scala | 99 ++++++++++++++- .../gitbucket/core/model/BasicTemplate.scala | 4 + .../scala/gitbucket/core/model/Labels.scala | 2 +- .../core/service/LabelsService.scala | 3 + .../gitbucket/core/api/JsonFormatSpec.scala | 12 ++ .../core/service/LabelsServiceSpec.scala | 114 ++++++++++++++++++ .../core/service/ServiceSpecBase.scala | 2 +- 10 files changed, 271 insertions(+), 5 deletions(-) create mode 100644 src/main/scala/gitbucket/core/api/ApiLabel.scala create mode 100644 src/main/scala/gitbucket/core/api/CreateALabel.scala create mode 100644 src/test/scala/gitbucket/core/service/LabelsServiceSpec.scala diff --git a/src/main/scala/gitbucket/core/api/ApiLabel.scala b/src/main/scala/gitbucket/core/api/ApiLabel.scala new file mode 100644 index 000000000..2d1842b30 --- /dev/null +++ b/src/main/scala/gitbucket/core/api/ApiLabel.scala @@ -0,0 +1,21 @@ +package gitbucket.core.api + +import gitbucket.core.model.Label +import gitbucket.core.util.RepositoryName + +/** + * https://developer.github.com/v3/issues/labels/ + */ +case class ApiLabel( + name: String, + color: String)(repositoryName: RepositoryName){ + var url = ApiPath(s"/api/v3/repos/${repositoryName.fullName}/labels/${name}") +} + +object ApiLabel{ + def apply(label:Label, repositoryName: RepositoryName): ApiLabel = + ApiLabel( + name = label.labelName, + color = label.color + )(repositoryName) +} \ No newline at end of file diff --git a/src/main/scala/gitbucket/core/api/CreateALabel.scala b/src/main/scala/gitbucket/core/api/CreateALabel.scala new file mode 100644 index 000000000..cfd71d1df --- /dev/null +++ b/src/main/scala/gitbucket/core/api/CreateALabel.scala @@ -0,0 +1,18 @@ +package gitbucket.core.api + +/** + * https://developer.github.com/v3/issues/labels/#create-a-label + * api form + */ +case class CreateALabel( + name: String, + color: String +) { + def isValid: Boolean = { + name.length<=100 && + !name.startsWith("_") && + !name.startsWith("-") && + color.length==6 && + color.matches("[a-fA-F0-9+_.]+") + } +} \ No newline at end of file diff --git a/src/main/scala/gitbucket/core/api/JsonFormat.scala b/src/main/scala/gitbucket/core/api/JsonFormat.scala index 0733ba454..611db3bc9 100644 --- a/src/main/scala/gitbucket/core/api/JsonFormat.scala +++ b/src/main/scala/gitbucket/core/api/JsonFormat.scala @@ -31,6 +31,7 @@ object JsonFormat { FieldSerializer[ApiPullRequest.Commit]() + FieldSerializer[ApiIssue]() + FieldSerializer[ApiComment]() + + FieldSerializer[ApiLabel]() + ApiBranchProtection.enforcementLevelSerializer def apiPathSerializer(c: Context) = new CustomSerializer[ApiPath](format => diff --git a/src/main/scala/gitbucket/core/controller/LabelsController.scala b/src/main/scala/gitbucket/core/controller/LabelsController.scala index 29d840065..97e2242b8 100644 --- a/src/main/scala/gitbucket/core/controller/LabelsController.scala +++ b/src/main/scala/gitbucket/core/controller/LabelsController.scala @@ -1,12 +1,13 @@ package gitbucket.core.controller +import gitbucket.core.api.{ApiError, CreateALabel, ApiLabel, JsonFormat} import gitbucket.core.issues.labels.html import gitbucket.core.service.{RepositoryService, AccountService, IssuesService, LabelsService} -import gitbucket.core.util.{ReferrerAuthenticator, CollaboratorsAuthenticator} +import gitbucket.core.util.{LockUtil, RepositoryName, ReferrerAuthenticator, CollaboratorsAuthenticator} import gitbucket.core.util.Implicits._ import io.github.gitbucket.scalatra.forms._ import org.scalatra.i18n.Messages -import org.scalatra.Ok +import org.scalatra.{UnprocessableEntity, Created, Ok} class LabelsController extends LabelsControllerBase with LabelsService with IssuesService with RepositoryService with AccountService @@ -19,7 +20,7 @@ trait LabelsControllerBase extends ControllerBase { case class LabelForm(labelName: String, color: String) val labelForm = mapping( - "labelName" -> trim(label("Label name", text(required, labelName, maxlength(100)))), + "labelName" -> trim(label("Label name", text(required, labelName, uniqueLabelName, maxlength(100)))), "labelColor" -> trim(label("Color", text(required, color))) )(LabelForm.apply) @@ -31,6 +32,26 @@ trait LabelsControllerBase extends ControllerBase { hasWritePermission(repository.owner, repository.name, context.loginAccount)) }) + /** + * List all labels for this repository + * https://developer.github.com/v3/issues/labels/#list-all-labels-for-this-repository + */ + get("/api/v3/repos/:owner/:repository/labels")(referrersOnly { repository => + getLabels(repository.owner, repository.name).map { label => + JsonFormat(ApiLabel(label, RepositoryName(repository))) + } + }) + + /** + * Get a single label + * https://developer.github.com/v3/issues/labels/#get-a-single-label + */ + get("/api/v3/repos/:owner/:repository/labels/:labelName")(referrersOnly { repository => + getLabel(repository.owner, repository.name, params("labelName")).map { label => + JsonFormat(ApiLabel(label, RepositoryName(repository))) + } getOrElse NotFound() + }) + ajaxGet("/:owner/:repository/issues/labels/new")(collaboratorsOnly { repository => html.edit(None, repository) }) @@ -45,6 +66,31 @@ trait LabelsControllerBase extends ControllerBase { hasWritePermission(repository.owner, repository.name, context.loginAccount)) }) + /** + * Create a label + * https://developer.github.com/v3/issues/labels/#create-a-label + */ + post("/api/v3/repos/:owner/:repository/labels")(collaboratorsOnly { repository => + (for{ + data <- extractFromJsonBody[CreateALabel] if data.isValid + } yield { + LockUtil.lock(RepositoryName(repository).fullName) { + if (getLabel(repository.owner, repository.name, data.name).isEmpty) { + val labelId = createLabel(repository.owner, repository.name, data.name, data.color) + getLabel(repository.owner, repository.name, labelId).map { label => + Created(JsonFormat(ApiLabel(label, RepositoryName(repository)))) + } getOrElse NotFound() + } else { + // TODO ApiError should support errors field to enhance compatibility of GitHub API + UnprocessableEntity(ApiError( + "Validation Failed", + Some("https://developer.github.com/v3/issues/labels/#create-a-label") + )) + } + } + }) getOrElse NotFound() + }) + ajaxGet("/:owner/:repository/issues/labels/:labelId/edit")(collaboratorsOnly { repository => getLabel(repository.owner, repository.name, params("labelId").toInt).map { label => html.edit(Some(label), repository) @@ -61,11 +107,50 @@ trait LabelsControllerBase extends ControllerBase { hasWritePermission(repository.owner, repository.name, context.loginAccount)) }) + /** + * Update a label + * https://developer.github.com/v3/issues/labels/#update-a-label + */ + patch("/api/v3/repos/:owner/:repository/labels/:labelName")(collaboratorsOnly { repository => + (for{ + data <- extractFromJsonBody[CreateALabel] if data.isValid + } yield { + LockUtil.lock(RepositoryName(repository).fullName) { + getLabel(repository.owner, repository.name, params("labelName")).map { label => + if (getLabel(repository.owner, repository.name, data.name).isEmpty) { + updateLabel(repository.owner, repository.name, label.labelId, data.name, data.color) + JsonFormat(ApiLabel( + getLabel(repository.owner, repository.name, label.labelId).get, + RepositoryName(repository))) + } else { + // TODO ApiError should support errors field to enhance compatibility of GitHub API + UnprocessableEntity(ApiError( + "Validation Failed", + Some("https://developer.github.com/v3/issues/labels/#create-a-label"))) + } + } getOrElse NotFound() + } + }) getOrElse NotFound() + }) + ajaxPost("/:owner/:repository/issues/labels/:labelId/delete")(collaboratorsOnly { repository => deleteLabel(repository.owner, repository.name, params("labelId").toInt) Ok() }) + /** + * Delete a label + * https://developer.github.com/v3/issues/labels/#delete-a-label + */ + delete("/api/v3/repos/:owner/:repository/labels/:labelName")(collaboratorsOnly { repository => + LockUtil.lock(RepositoryName(repository).fullName) { + getLabel(repository.owner, repository.name, params("labelName")).map { label => + deleteLabel(repository.owner, repository.name, label.labelId) + Ok() + } getOrElse NotFound() + } + }) + /** * Constraint for the identifier such as user name, repository name or page name. */ @@ -80,4 +165,12 @@ trait LabelsControllerBase extends ControllerBase { } } + private def uniqueLabelName: Constraint = new Constraint(){ + override def validate(name: String, value: String, params: Map[String, String], messages: Messages): Option[String] = { + val owner = params("owner") + val repository = params("repository") + getLabel(owner, repository, value).map(_ => "Name has already been taken.") + } + } + } diff --git a/src/main/scala/gitbucket/core/model/BasicTemplate.scala b/src/main/scala/gitbucket/core/model/BasicTemplate.scala index 2b137642f..97fcb75a6 100644 --- a/src/main/scala/gitbucket/core/model/BasicTemplate.scala +++ b/src/main/scala/gitbucket/core/model/BasicTemplate.scala @@ -26,12 +26,16 @@ protected[model] trait TemplateComponent { self: Profile => trait LabelTemplate extends BasicTemplate { self: Table[_] => val labelId = column[Int]("LABEL_ID") + val labelName = column[String]("LABEL_NAME") def byLabel(owner: String, repository: String, labelId: Int) = byRepository(owner, repository) && (this.labelId === labelId.bind) def byLabel(userName: Column[String], repositoryName: Column[String], labelId: Column[Int]) = byRepository(userName, repositoryName) && (this.labelId === labelId) + + def byLabel(owner: String, repository: String, labelName: String) = + byRepository(userName, repositoryName) && (this.labelName === labelName.bind) } trait MilestoneTemplate extends BasicTemplate { self: Table[_] => diff --git a/src/main/scala/gitbucket/core/model/Labels.scala b/src/main/scala/gitbucket/core/model/Labels.scala index 0143c9e9b..84a4e6d97 100644 --- a/src/main/scala/gitbucket/core/model/Labels.scala +++ b/src/main/scala/gitbucket/core/model/Labels.scala @@ -7,7 +7,7 @@ trait LabelComponent extends TemplateComponent { self: Profile => class Labels(tag: Tag) extends Table[Label](tag, "LABEL") with LabelTemplate { override val labelId = column[Int]("LABEL_ID", O AutoInc) - val labelName = column[String]("LABEL_NAME") + override val labelName = column[String]("LABEL_NAME") val color = column[String]("COLOR") def * = (userName, repositoryName, labelId, labelName, color) <> (Label.tupled, Label.unapply) diff --git a/src/main/scala/gitbucket/core/service/LabelsService.scala b/src/main/scala/gitbucket/core/service/LabelsService.scala index 35b5d2de3..f8026e09f 100644 --- a/src/main/scala/gitbucket/core/service/LabelsService.scala +++ b/src/main/scala/gitbucket/core/service/LabelsService.scala @@ -12,6 +12,9 @@ trait LabelsService { def getLabel(owner: String, repository: String, labelId: Int)(implicit s: Session): Option[Label] = Labels.filter(_.byPrimaryKey(owner, repository, labelId)).firstOption + def getLabel(owner: String, repository: String, labelName: String)(implicit s: Session): Option[Label] = + Labels.filter(_.byLabel(owner, repository, labelName)).firstOption + def createLabel(owner: String, repository: String, labelName: String, color: String)(implicit s: Session): Int = Labels returning Labels.map(_.labelId) += Label( userName = owner, diff --git a/src/test/scala/gitbucket/core/api/JsonFormatSpec.scala b/src/test/scala/gitbucket/core/api/JsonFormatSpec.scala index 5395da6fb..38dee6a53 100644 --- a/src/test/scala/gitbucket/core/api/JsonFormatSpec.scala +++ b/src/test/scala/gitbucket/core/api/JsonFormatSpec.scala @@ -209,6 +209,15 @@ class JsonFormatSpec extends Specification { "url": "${context.baseUrl}/api/v3/repos/octocat/Hello-World/commits/$sha1/status" }""" + val apiLabel = ApiLabel( + name = "bug", + color = "f29513")(RepositoryName("octocat","Hello-World")) + val apiLabelJson = s"""{ + "name": "bug", + "color": "f29513", + "url": "${context.baseUrl}/api/v3/repos/octocat/Hello-World/labels/bug" + }""" + val apiIssue = ApiIssue( number = 1347, title = "Found a bug", @@ -411,6 +420,9 @@ class JsonFormatSpec extends Specification { "apiCombinedCommitStatus" in { JsonFormat(apiCombinedCommitStatus) must beFormatted(apiCombinedCommitStatusJson) } + "apiLabel" in { + JsonFormat(apiLabel) must beFormatted(apiLabelJson) + } "apiIssue" in { JsonFormat(apiIssue) must beFormatted(apiIssueJson) JsonFormat(apiIssuePR) must beFormatted(apiIssuePRJson) diff --git a/src/test/scala/gitbucket/core/service/LabelsServiceSpec.scala b/src/test/scala/gitbucket/core/service/LabelsServiceSpec.scala new file mode 100644 index 000000000..3a6ccc838 --- /dev/null +++ b/src/test/scala/gitbucket/core/service/LabelsServiceSpec.scala @@ -0,0 +1,114 @@ +package gitbucket.core.service + +import gitbucket.core.model._ + +import org.specs2.mutable.Specification + +class LabelsServiceSpec extends Specification with ServiceSpecBase { + "getLabels" should { + "be empty when not have any labels" in { withTestDB { implicit session => + generateNewUserWithDBRepository("user1", "repo1") + + generateNewUserWithDBRepository("user1", "repo2") + dummyService.createLabel("user1", "repo2", "label1", "000000") + + generateNewUserWithDBRepository("user2", "repo1") + dummyService.createLabel("user2", "repo1", "label1", "000000") + + dummyService.getLabels("user1", "repo1") must haveSize(0) + }} + "return contained labels" in { withTestDB { implicit session => + generateNewUserWithDBRepository("user1", "repo1") + val labelId1 = dummyService.createLabel("user1", "repo1", "label1", "000000") + val labelId2 = dummyService.createLabel("user1", "repo1", "label2", "ffffff") + + generateNewUserWithDBRepository("user1", "repo2") + dummyService.createLabel("user1", "repo2", "label1", "000000") + + generateNewUserWithDBRepository("user2", "repo1") + dummyService.createLabel("user2", "repo1", "label1", "000000") + + def getLabels = dummyService.getLabels("user1", "repo1") + + getLabels must haveSize(2) + getLabels must_== List( + Label("user1", "repo1", labelId1, "label1", "000000"), + Label("user1", "repo1", labelId2, "label2", "ffffff")) + }} + } + "getLabel" should { + "return None when the label not exist" in { withTestDB { implicit session => + generateNewUserWithDBRepository("user1", "repo1") + + dummyService.getLabel("user1", "repo1", 1) must beNone + dummyService.getLabel("user1", "repo1", "label1") must beNone + }} + "return a label fetched by label id" in { withTestDB { implicit session => + generateNewUserWithDBRepository("user1", "repo1") + val labelId1 = dummyService.createLabel("user1", "repo1", "label1", "000000") + dummyService.createLabel("user1", "repo1", "label2", "ffffff") + + generateNewUserWithDBRepository("user1", "repo2") + dummyService.createLabel("user1", "repo2", "label1", "000000") + + generateNewUserWithDBRepository("user2", "repo1") + dummyService.createLabel("user2", "repo1", "label1", "000000") + + def getLabel = dummyService.getLabel("user1", "repo1", labelId1) + getLabel must_== Some(Label("user1", "repo1", labelId1, "label1", "000000")) + }} + "return a label fetched by label name" in { withTestDB { implicit session => + generateNewUserWithDBRepository("user1", "repo1") + val labelId1 = dummyService.createLabel("user1", "repo1", "label1", "000000") + dummyService.createLabel("user1", "repo1", "label2", "ffffff") + + generateNewUserWithDBRepository("user1", "repo2") + dummyService.createLabel("user1", "repo2", "label1", "000000") + + generateNewUserWithDBRepository("user2", "repo1") + dummyService.createLabel("user2", "repo1", "label1", "000000") + + def getLabel = dummyService.getLabel("user1", "repo1", "label1") + getLabel must_== Some(Label("user1", "repo1", labelId1, "label1", "000000")) + }} + } + "createLabel" should { + "return accurate label id" in { withTestDB { implicit session => + generateNewUserWithDBRepository("user1", "repo1") + generateNewUserWithDBRepository("user1", "repo2") + generateNewUserWithDBRepository("user2", "repo1") + dummyService.createLabel("user1", "repo1", "label1", "000000") + dummyService.createLabel("user1", "repo2", "label1", "000000") + dummyService.createLabel("user2", "repo1", "label1", "000000") + val labelId = dummyService.createLabel("user1", "repo1", "label2", "000000") + labelId must_== 4 + def getLabel = dummyService.getLabel("user1", "repo1", labelId) + getLabel must_== Some(Label("user1", "repo1", labelId, "label2", "000000")) + }} + } + "updateLabel" should { + "change target label" in { withTestDB { implicit session => + generateNewUserWithDBRepository("user1", "repo1") + generateNewUserWithDBRepository("user1", "repo2") + generateNewUserWithDBRepository("user2", "repo1") + val labelId = dummyService.createLabel("user1", "repo1", "label1", "000000") + dummyService.createLabel("user1", "repo2", "label1", "000000") + dummyService.createLabel("user2", "repo1", "label1", "000000") + dummyService.updateLabel("user1", "repo1", labelId, "updated-label", "ffffff") + def getLabel = dummyService.getLabel("user1", "repo1", labelId) + getLabel must_== Some(Label("user1", "repo1", labelId, "updated-label", "ffffff")) + }} + } + "deleteLabel" should { + "remove target label" in { withTestDB { implicit session => + generateNewUserWithDBRepository("user1", "repo1") + generateNewUserWithDBRepository("user1", "repo2") + generateNewUserWithDBRepository("user2", "repo1") + val labelId = dummyService.createLabel("user1", "repo1", "label1", "000000") + dummyService.createLabel("user1", "repo2", "label1", "000000") + dummyService.createLabel("user2", "repo1", "label1", "000000") + dummyService.deleteLabel("user1", "repo1", labelId) + dummyService.getLabel("user1", "repo1", labelId) must beNone + }} + } +} diff --git a/src/test/scala/gitbucket/core/service/ServiceSpecBase.scala b/src/test/scala/gitbucket/core/service/ServiceSpecBase.scala index f5fd9eaad..a7b383d77 100644 --- a/src/test/scala/gitbucket/core/service/ServiceSpecBase.scala +++ b/src/test/scala/gitbucket/core/service/ServiceSpecBase.scala @@ -38,7 +38,7 @@ trait ServiceSpecBase { def user(name:String)(implicit s:Session):Account = AccountService.getAccountByUserName(name).get lazy val dummyService = new RepositoryService with AccountService with IssuesService with PullRequestService - with CommitStatusService (){} + with CommitStatusService with LabelsService (){} def generateNewUserWithDBRepository(userName:String, repositoryName:String)(implicit s:Session):Account = { val ac = AccountService.getAccountByUserName(userName).getOrElse(generateNewAccount(userName)) From 361babd32774f576b8714036e404de9006b149bc Mon Sep 17 00:00:00 2001 From: lidice Date: Fri, 22 Jan 2016 09:33:41 +0900 Subject: [PATCH 02/15] Fix labels api Fixed invalid json format * List all labels for this repository Fixed wrong http status * 200 -> 204 --- .../gitbucket/core/controller/LabelsController.scala | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/scala/gitbucket/core/controller/LabelsController.scala b/src/main/scala/gitbucket/core/controller/LabelsController.scala index 97e2242b8..4074c6108 100644 --- a/src/main/scala/gitbucket/core/controller/LabelsController.scala +++ b/src/main/scala/gitbucket/core/controller/LabelsController.scala @@ -7,7 +7,7 @@ import gitbucket.core.util.{LockUtil, RepositoryName, ReferrerAuthenticator, Col import gitbucket.core.util.Implicits._ import io.github.gitbucket.scalatra.forms._ import org.scalatra.i18n.Messages -import org.scalatra.{UnprocessableEntity, Created, Ok} +import org.scalatra.{NoContent, UnprocessableEntity, Created, Ok} class LabelsController extends LabelsControllerBase with LabelsService with IssuesService with RepositoryService with AccountService @@ -37,9 +37,9 @@ trait LabelsControllerBase extends ControllerBase { * https://developer.github.com/v3/issues/labels/#list-all-labels-for-this-repository */ get("/api/v3/repos/:owner/:repository/labels")(referrersOnly { repository => - getLabels(repository.owner, repository.name).map { label => - JsonFormat(ApiLabel(label, RepositoryName(repository))) - } + JsonFormat(getLabels(repository.owner, repository.name).map { label => + ApiLabel(label, RepositoryName(repository)) + }) }) /** @@ -146,7 +146,7 @@ trait LabelsControllerBase extends ControllerBase { LockUtil.lock(RepositoryName(repository).fullName) { getLabel(repository.owner, repository.name, params("labelName")).map { label => deleteLabel(repository.owner, repository.name, label.labelId) - Ok() + NoContent() } getOrElse NotFound() } }) From 1a1267dc60ee1e8c025b84acf9515cced88cbe49 Mon Sep 17 00:00:00 2001 From: Herr Ritschwumm Date: Fri, 22 Jan 2016 01:42:25 +0100 Subject: [PATCH 03/15] issue #963: build executable war with sbt, fetch jetty jars at build time. --- executable.sbt | 77 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100755 executable.sbt diff --git a/executable.sbt b/executable.sbt new file mode 100755 index 000000000..c1837fde3 --- /dev/null +++ b/executable.sbt @@ -0,0 +1,77 @@ +val executableConfig = config("executable").hide +val executableKey = TaskKey[File]("executable") + +Keys.ivyConfigurations += executableConfig +libraryDependencies ++= Seq( + "org.eclipse.jetty" % "jetty-security" % "8.1.16.v20140903" % "executable", + "org.eclipse.jetty" % "jetty-webapp" % "8.1.16.v20140903" % "executable", + "org.eclipse.jetty" % "jetty-continuation" % "8.1.16.v20140903" % "executable", + "org.eclipse.jetty" % "jetty-server" % "8.1.16.v20140903" % "executable", + "org.eclipse.jetty" % "jetty-xml" % "8.1.16.v20140903" % "executable", + "org.eclipse.jetty" % "jetty-http" % "8.1.16.v20140903" % "executable", + "org.eclipse.jetty" % "jetty-servlet" % "8.1.16.v20140903" % "executable", + "org.eclipse.jetty" % "jetty-io" % "8.1.16.v20140903" % "executable", + "org.eclipse.jetty" % "jetty-util" % "8.1.16.v20140903" % "executable" +) +executableKey := { + import org.apache.ivy.util.ChecksumHelper + import java.util.jar.{ Manifest => JarManifest } + import java.util.jar.Attributes.{ Name => AttrName } + + val workDir = Keys.target.value / "executable" + var warName = Keys.name.value + "-" + Keys.version.value + "-executable.war" + + val log = streams.value.log + log info s"building executable webapp in ${workDir}" + + // initialize temp directory + val temp = workDir / "webapp" + IO delete temp + + // include jetty classes + val jettyJars = Keys.update.value select configurationFilter(name = executableConfig.name) + jettyJars foreach { jar => + IO unzip (jar, temp, (name:String) => + (name startsWith "javax/") || + (name startsWith "org/") + ) + } + + // include original war file + val warFile = (Keys.`package`).value + IO unzip (warFile, temp) + + // include launcher classes + val classDir = (Keys.classDirectory in Compile).value + val launchClasses = Seq("JettyLauncher.class" /*, "HttpsSupportConnector.class" */) + launchClasses foreach { name => + IO copyFile (classDir / name, temp / name) + } + + // zip it up + IO delete (temp / "META-INF" / "MANIFEST.MF") + val contentMappings = (temp.*** --- PathFinder(temp)).get pair relativeTo(temp) + val manifest = new JarManifest + manifest.getMainAttributes put (AttrName.MANIFEST_VERSION, "1.0") + manifest.getMainAttributes put (AttrName.MAIN_CLASS, "JettyLauncher") + val outputFile = workDir / warName + IO jar (contentMappings, outputFile, manifest) + + // generate checksums + Seq("md5", "sha1") foreach { algorithm => + IO.write( + workDir / (warName + "." + algorithm), + ChecksumHelper computeAsString (outputFile, algorithm) + ) + } + + // done + log info s"built executable webapp ${outputFile}" + outputFile +} +/* +Keys.artifact in (Compile, executableKey) ~= { + _ copy (`type` = "war", extension = "war")) +} +addArtifact(Keys.artifact in (Compile, executableKey), executableKey) +*/ From f3b7318453e4edbf800f1b4a26278b5fef1aa71a Mon Sep 17 00:00:00 2001 From: Naoki Takezoe Date: Sun, 24 Jan 2016 22:44:39 +0900 Subject: [PATCH 04/15] Fix margin of icon and caret of dowpdown menu in the global header --- src/main/twirl/gitbucket/core/main.scala.html | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/main/twirl/gitbucket/core/main.scala.html b/src/main/twirl/gitbucket/core/main.scala.html index 7139b3f04..b1d597a22 100644 --- a/src/main/twirl/gitbucket/core/main.scala.html +++ b/src/main/twirl/gitbucket/core/main.scala.html @@ -78,8 +78,7 @@
- - +
- @avatar(loginAccount.get.userName, 16) - + + @avatar(loginAccount.get.userName, 16)