Compare commits

...

10 Commits

Author SHA1 Message Date
Naoki Takezoe
a913a95d5b Release 4.35.2 2020-12-30 00:37:53 +09:00
Naoki Takezoe
870a20721c Bump oauth2-oidc-sdk to 8.29.1 (#2617) 2020-12-30 00:36:04 +09:00
Naoki Takezoe
df81f6e364 Upgrade gitbucket-notifications-plugin to 1.10.0 (#2616) 2020-12-29 22:42:23 +09:00
pbg42
d4a892bf7f Update CONTRIBUTING.md (#2613)
corrected just a typo
2020-12-29 11:11:47 +09:00
Naoki Takezoe
41d13db5d4 Release 4.35.1 2020-12-29 00:31:22 +09:00
Naoki Takezoe
c63e20ce7d Resurrect ActivityComponent for binary compatibility in 4.35.1 (#2612) 2020-12-29 00:26:58 +09:00
Naoki Takezoe
736fdafea4 Call webhook when pull request is merged (#2611) 2020-12-28 10:54:48 +09:00
Naoki Takezoe
1dfe76e21c Fix WEB_HOOK table migration issue in 4.35.0 (#2610) 2020-12-27 19:10:07 +09:00
Naoki Takezoe
e7ddfc7ebb Bump to sbt 1.4.6, Scala 2.13.3, and Scalatra 2.7.1 (#2609)
* Bump to sbt 1.4.6, Scala 2.13.3, and Scalatra 2.7.1
* Fix warnings in Scala 2.13
2020-12-27 13:35:57 +09:00
Naoki Takezoe
66be84289d Show commit status at commits tab of pull request (#2606) 2020-12-25 22:34:47 +09:00
53 changed files with 501 additions and 210 deletions

View File

@@ -1,6 +1,6 @@
# The guidelines for contributing
- At first, see [Wiki](https://github.com/gitbucket/gitbucket/wiki) and check issues and pull requests whether there is a same request in the past.
- The highest priority of GitBucket is the ease of installation and API compatibility with GitHub, so your feature request might be rejected if they go against those principles. If you don't wanna waste your time to make a pull request, ask us about your idea at [gitter room](https://gitter.im/gitbucket/gitbucket) before staring your work.
- The highest priority of GitBucket is the ease of installation and API compatibility with GitHub, so your feature request might be rejected if they go against those principles. If you don't wanna waste your time to make a pull request, ask us about your idea at [gitter room](https://gitter.im/gitbucket/gitbucket) before starting your work.
- You can edit the GitBucket documentation on Wiki if you have a GitHub account. When you find any mistakes or lacks in the documentation, please update it directly.
- All your contributions are handled as [Apache Software License, Version 2.0](https://github.com/gitbucket/gitbucket/blob/master/LICENSE). When you create a pull request or update the documentation, we assume you agreed this clause.

View File

@@ -1,6 +1,15 @@
# Changelog
All changes to the project will be documented in this file.
### 4.35.2 - 30 Dec 2020
- Upgrade gitbucket-notifications-plugin to 1.10.0
- Upgrade oauth2-oidc-sdk to 8.29.1 to solve dependency issue
### 4.35.1 - 29 Dec 2020
- Fix database migration issue which happens if webhook is configured
- Call push webhook when pull request is merged
- Show commit status at commits tab of pull request
### 4.35.0 - 25 Dec 2020
- Editor and source viewer color theme
- Auto completion for issues and pull requests

View File

@@ -55,8 +55,18 @@ Support
- If you can't find same question and report, send it to [gitter room](https://gitter.im/gitbucket/gitbucket) before raising an issue.
- The highest priority of GitBucket is the ease of installation and API compatibility with GitHub, so your feature request might be rejected if they go against those principles.
What's New in 4.34.x
What's New in 4.35.x
-------------
### 4.35.2 - 30 Dec 2020
- Upgrade gitbucket-notifications-plugin to 1.10.0
- Upgrade oauth2-oidc-sdk to 8.29.1 to solve dependency issue
### 4.35.1 - 29 Dec 2020
- Fix database migration issue which happens if webhook is configured
- Call push webhook when pull request is merged
- Show commit status at commits tab of pull request
### 4.35.0 - 25 Dec 2020
- Editor and source viewer color theme

View File

@@ -3,8 +3,8 @@ import com.typesafe.sbt.pgp.PgpKeys._
val Organization = "io.github.gitbucket"
val Name = "gitbucket"
val GitBucketVersion = "4.35.0"
val ScalatraVersion = "2.7.0"
val GitBucketVersion = "4.35.2"
val ScalatraVersion = "2.7.1"
val JettyVersion = "9.4.32.v20200930"
val JgitVersion = "5.9.0.202009080501-r"
@@ -17,7 +17,7 @@ sourcesInBase := false
organization := Organization
name := Name
version := GitBucketVersion
scalaVersion := "2.13.1"
scalaVersion := "2.13.3"
scalafmtOnCompile := true
@@ -59,7 +59,7 @@ libraryDependencies ++= Seq(
"org.cache2k" % "cache2k-all" % "1.2.4.Final",
"net.coobird" % "thumbnailator" % "0.4.12",
"com.github.zafarkhaja" % "java-semver" % "0.9.0",
"com.nimbusds" % "oauth2-oidc-sdk" % "5.64.4",
"com.nimbusds" % "oauth2-oidc-sdk" % "8.29.1",
"org.eclipse.jetty" % "jetty-webapp" % JettyVersion % "provided",
"javax.servlet" % "javax.servlet-api" % "3.1.0" % "provided",
"junit" % "junit" % "4.13" % "test",
@@ -75,7 +75,7 @@ libraryDependencies ++= Seq(
)
// Compiler settings
scalacOptions := Seq("-deprecation", "-language:postfixOps", "-opt:l:method")
scalacOptions := Seq("-deprecation", "-language:postfixOps", "-opt:l:method", "-feature")
javacOptions in compile ++= Seq("-target", "8", "-source", "8")
javaOptions in Jetty += "-Dlogback.configurationFile=/logback-dev.xml"

View File

@@ -1 +1 @@
sbt.version=1.4.4
sbt.version=1.4.6

View File

@@ -1,4 +1,4 @@
notifications:1.9.0
notifications:1.10.0
gist:4.20.0
emoji:4.6.0
pages:1.9.0

View File

@@ -1,17 +1,35 @@
<?xml version="1.0" encoding="UTF-8"?>
<changeSet>
<!--================================================================================================-->
<!-- WEB_HOOK -->
<!--================================================================================================-->
<dropForeignKeyConstraint constraintName="IDX_WEB_HOOK_EVENT_FK0" baseTableName="WEB_HOOK_EVENT"/>
<dropForeignKeyConstraint constraintName="IDX_WEB_HOOK_FK0" baseTableName="WEB_HOOK"/>
<dropPrimaryKey tableName="WEB_HOOK" constraintName="IDX_WEB_HOOK_PK"/>
<addColumn tableName="WEB_HOOK">
<column name="HOOK_ID" type="int" nullable="false" unique="true"/>
</addColumn>
<addPrimaryKey constraintName="IDX_WEB_HOOK_PK" tableName="WEB_HOOK" columnNames="USER_NAME, REPOSITORY_NAME, URL, HOOK_ID"/>
<dropPrimaryKey constraintName="IDX_WEB_HOOK_PK" tableName="WEB_HOOK"/>
<createTable tableName="WEB_HOOK_2">
<column name="HOOK_ID" type="int" nullable="true" autoIncrement="true" unique="false" primaryKeyName="IDX_WEB_HOOK_PK" primaryKey="true" />
<column name="USER_NAME" type="varchar(100)" nullable="false"/>
<column name="REPOSITORY_NAME" type="varchar(100)" nullable="false"/>
<column name="URL" type="varchar(200)" nullable="false"/>
<column name="TOKEN" type="varchar(100)" nullable="true"/>
<column name="CTYPE" type="varchar(10)" nullable="true"/>
</createTable>
<sql>
INSERT INTO WEB_HOOK_2 (USER_NAME, REPOSITORY_NAME, URL, TOKEN, CTYPE) SELECT USER_NAME, REPOSITORY_NAME, URL, TOKEN, CTYPE FROM WEB_HOOK
</sql>
<renameTable newTableName="WEB_HOOK_BK" oldTableName="WEB_HOOK"/>
<renameTable newTableName="WEB_HOOK" oldTableName="WEB_HOOK_2"/>
<addUniqueConstraint constraintName="IDX_WEB_HOOK_1" tableName="WEB_HOOK" columnNames="USER_NAME, REPOSITORY_NAME, URL"/>
<addForeignKeyConstraint constraintName="IDX_WEB_HOOK_FK0" baseTableName="WEB_HOOK" baseColumnNames="USER_NAME, REPOSITORY_NAME" referencedTableName="REPOSITORY" referencedColumnNames="USER_NAME, REPOSITORY_NAME"/>
<addForeignKeyConstraint constraintName="IDX_WEB_HOOK_EVENT_FK0" baseTableName="WEB_HOOK_EVENT" baseColumnNames="USER_NAME, REPOSITORY_NAME, URL" referencedTableName="WEB_HOOK" referencedColumnNames="USER_NAME, REPOSITORY_NAME, URL" onDelete="CASCADE" onUpdate="CASCADE"/>
<addAutoIncrement columnName="HOOK_ID" columnDataType="int" tableName="WEB_HOOK"/>
<!--================================================================================================-->
<!-- ACCOUNT_PREFERENCE -->
<!--================================================================================================-->
<createTable tableName="ACCOUNT_PREFERENCE">
<column name="USER_NAME" type="varchar(100)" nullable="false"/>
<column name="HIGHLIGHTER_THEME" type="varchar(100)" nullable="false" defaultValue="prettify"/>

View File

@@ -115,4 +115,6 @@ object GitBucketCoreModule
new LiquibaseMigration("update/gitbucket-core_4.34.xml")
),
new Version("4.35.0", new LiquibaseMigration("update/gitbucket-core_4.35.xml")),
new Version("4.35.1"),
new Version("4.35.2"),
)

View File

@@ -421,7 +421,7 @@ trait AccountManagementControllerBase extends ControllerBase {
"new"
)
protected def reservedNames(): Constraint = new Constraint() {
protected def reservedNames: Constraint = new Constraint() {
override def validate(name: String, value: String, messages: Messages): Option[String] =
if (allReservedNames.contains(value.toLowerCase)) {
Some(s"${value} is reserved")

View File

@@ -37,7 +37,7 @@ class FileUploadController
execute(
{ (file, fileId) =>
FileUtils
.writeByteArrayToFile(new File(getTemporaryDir(session.getId), FileUtil.checkFilename(fileId)), file.get)
.writeByteArrayToFile(new File(getTemporaryDir(session.getId), FileUtil.checkFilename(fileId)), file.get())
session += Keys.Session.Upload(fileId) -> file.name
},
FileUtil.isImage
@@ -49,7 +49,7 @@ class FileUploadController
execute(
{ (file, fileId) =>
FileUtils
.writeByteArrayToFile(new File(getTemporaryDir(session.getId), FileUtil.checkFilename(fileId)), file.get)
.writeByteArrayToFile(new File(getTemporaryDir(session.getId), FileUtil.checkFilename(fileId)), file.get())
session += Keys.Session.Upload(fileId) -> file.name
},
_ => true
@@ -65,7 +65,7 @@ class FileUploadController
getAttachedDir(params("owner"), params("repository")),
FileUtil.checkFilename(fileId + "." + FileUtil.getExtension(file.getName))
),
file.get
file.get()
)
},
_ => true
@@ -144,7 +144,7 @@ class FileUploadController
{ (file, fileId) =>
FileUtils.writeByteArrayToFile(
new File(getReleaseFilesDir(owner, repository), FileUtil.checkFilename(tag + "/" + fileId)),
file.get
file.get()
)
},
_ => true

View File

@@ -169,10 +169,16 @@ trait PullRequestsControllerBase extends ControllerBase {
val (commits, diffs) =
getRequestCompareInfo(owner, name, pullreq.commitIdFrom, owner, name, pullreq.commitIdTo)
val commitsWithStatus = commits.map { day =>
day.map { commit =>
(commit, getCommitStatusWithSummary(repository.owner, repository.name, commit.id))
}
}
html.commits(
issue,
pullreq,
commits,
commitsWithStatus,
getPullRequestComments(owner, name, issue.issueId, commits.flatten),
diffs.size,
isManageable(repository),

View File

@@ -230,9 +230,9 @@ trait ApiPullRequestControllerBase extends ControllerBase {
if (checkConflict(repository.owner, repository.name, pullReq.branch, issueId).isDefined) {
NoContent
} else {
NotFound
NotFound()
}
}).getOrElse(NotFound)
}).getOrElse(NotFound())
})
/*

View File

@@ -10,7 +10,7 @@ trait AccessTokenComponent { self: Profile =>
val userName = column[String]("USER_NAME")
val tokenHash = column[String]("TOKEN_HASH")
val note = column[String]("NOTE")
def * = (accessTokenId, userName, tokenHash, note) <> (AccessToken.tupled, AccessToken.unapply)
def * = (accessTokenId, userName, tokenHash, note).<>(AccessToken.tupled, AccessToken.unapply)
}
}
case class AccessToken(

View File

@@ -35,7 +35,7 @@ trait AccountComponent { self: Profile =>
groupAccount,
removed,
description.?
) <> (Account.tupled, Account.unapply)
).<>(Account.tupled, Account.unapply)
}
}

View File

@@ -9,7 +9,7 @@ trait AccountExtraMailAddressComponent { self: Profile =>
val userName = column[String]("USER_NAME", O PrimaryKey)
val extraMailAddress = column[String]("EXTRA_MAIL_ADDRESS", O PrimaryKey)
def * =
(userName, extraMailAddress) <> (AccountExtraMailAddress.tupled, AccountExtraMailAddress.unapply)
(userName, extraMailAddress).<>(AccountExtraMailAddress.tupled, AccountExtraMailAddress.unapply)
}
}

View File

@@ -9,7 +9,7 @@ trait AccountFederationComponent { self: Profile =>
val issuer = column[String]("ISSUER")
val subject = column[String]("SUBJECT")
val userName = column[String]("USER_NAME")
def * = (issuer, subject, userName) <> (AccountFederation.tupled, AccountFederation.unapply)
def * = (issuer, subject, userName).<>(AccountFederation.tupled, AccountFederation.unapply)
def byPrimaryKey(issuer: String, subject: String): Rep[Boolean] =
(this.issuer === issuer.bind) && (this.subject === subject.bind)

View File

@@ -9,7 +9,7 @@ trait AccountPreferenceComponent { self: Profile =>
val userName = column[String]("USER_NAME", O PrimaryKey)
val highlighterTheme = column[String]("HIGHLIGHTER_THEME")
def * =
(userName, highlighterTheme) <> (AccountPreference.tupled, AccountPreference.unapply)
(userName, highlighterTheme).<>(AccountPreference.tupled, AccountPreference.unapply)
def byPrimaryKey(userName: String): Rep[Boolean] = this.userName === userName.bind
}

View File

@@ -12,7 +12,7 @@ trait AccountWebHookComponent extends TemplateComponent { self: Profile =>
val url = column[String]("URL")
val token = column[Option[String]]("TOKEN")
val ctype = column[WebHookContentType]("CTYPE")
def * = (userName, url, ctype, token) <> ((AccountWebHook.apply _).tupled, AccountWebHook.unapply)
def * = (userName, url, ctype, token).<>((AccountWebHook.apply _).tupled, AccountWebHook.unapply)
def byPrimaryKey(userName: String, url: String) = (this.userName === userName.bind) && (this.url === url.bind)
}

View File

@@ -14,7 +14,7 @@ trait AccountWebHookEventComponent extends TemplateComponent {
val url = column[String]("URL")
val event = column[WebHook.Event]("EVENT")
def * = (userName, url, event) <> ((AccountWebHookEvent.apply _).tupled, AccountWebHookEvent.unapply)
def * = (userName, url, event).<>((AccountWebHookEvent.apply _).tupled, AccountWebHookEvent.unapply)
def byAccountWebHook(userName: String, url: String) = (this.userName === userName.bind) && (this.url === url.bind)

View File

@@ -8,7 +8,7 @@ trait CollaboratorComponent extends TemplateComponent { self: Profile =>
class Collaborators(tag: Tag) extends Table[Collaborator](tag, "COLLABORATOR") with BasicTemplate {
val collaboratorName = column[String]("COLLABORATOR_NAME")
val role = column[String]("ROLE")
def * = (userName, repositoryName, collaboratorName, role) <> (Collaborator.tupled, Collaborator.unapply)
def * = (userName, repositoryName, collaboratorName, role).<>(Collaborator.tupled, Collaborator.unapply)
def byPrimaryKey(owner: String, repository: String, collaborator: String) =
byRepository(owner, repository) && (collaboratorName === collaborator.bind)

View File

@@ -20,7 +20,8 @@ trait IssueCommentComponent extends TemplateComponent { self: Profile =>
val registeredDate = column[java.util.Date]("REGISTERED_DATE")
val updatedDate = column[java.util.Date]("UPDATED_DATE")
def * =
(userName, repositoryName, issueId, commentId, action, commentedUserName, content, registeredDate, updatedDate) <> (IssueComment.tupled, IssueComment.unapply)
(userName, repositoryName, issueId, commentId, action, commentedUserName, content, registeredDate, updatedDate)
.<>(IssueComment.tupled, IssueComment.unapply)
def byPrimaryKey(commentId: Int) = this.commentId === commentId.bind
}
@@ -74,7 +75,7 @@ trait CommitCommentComponent extends TemplateComponent { self: Profile =>
originalCommitId,
originalOldLine,
originalNewLine
) <> (CommitComment.tupled, CommitComment.unapply)
).<>(CommitComment.tupled, CommitComment.unapply)
def byPrimaryKey(commentId: Int) = this.commentId === commentId.bind
}

View File

@@ -29,7 +29,7 @@ trait CommitStatusComponent extends TemplateComponent { self: Profile =>
creator,
registeredDate,
updatedDate
) <> ((CommitStatus.apply _).tupled, CommitStatus.unapply)
).<>((CommitStatus.apply _).tupled, CommitStatus.unapply)
def byPrimaryKey(id: Int) = commitStatusId === id.bind
}
}

View File

@@ -11,7 +11,7 @@ trait DeployKeyComponent extends TemplateComponent { self: Profile =>
val publicKey = column[String]("PUBLIC_KEY")
val allowWrite = column[Boolean]("ALLOW_WRITE")
def * =
(userName, repositoryName, deployKeyId, title, publicKey, allowWrite) <> (DeployKey.tupled, DeployKey.unapply)
(userName, repositoryName, deployKeyId, title, publicKey, allowWrite).<>(DeployKey.tupled, DeployKey.unapply)
def byPrimaryKey(userName: String, repositoryName: String, deployKeyId: Int) =
(this.userName === userName.bind) && (this.repositoryName === repositoryName.bind) && (this.deployKeyId === deployKeyId.bind)

View File

@@ -11,7 +11,7 @@ trait GpgKeyComponent { self: Profile =>
val gpgKeyId = column[Long]("GPG_KEY_ID")
val title = column[String]("TITLE")
val publicKey = column[String]("PUBLIC_KEY")
def * = (userName, keyId, gpgKeyId, title, publicKey) <> (GpgKey.tupled, GpgKey.unapply)
def * = (userName, keyId, gpgKeyId, title, publicKey).<>(GpgKey.tupled, GpgKey.unapply)
def byPrimaryKey(userName: String, keyId: Int) =
(this.userName === userName.bind) && (this.keyId === keyId.bind)

View File

@@ -9,7 +9,7 @@ trait GroupMemberComponent { self: Profile =>
val groupName = column[String]("GROUP_NAME", O PrimaryKey)
val userName = column[String]("USER_NAME", O PrimaryKey)
val isManager = column[Boolean]("MANAGER")
def * = (groupName, userName, isManager) <> (GroupMember.tupled, GroupMember.unapply)
def * = (groupName, userName, isManager).<>(GroupMember.tupled, GroupMember.unapply)
}
}

View File

@@ -49,7 +49,7 @@ trait IssueComponent extends TemplateComponent { self: Profile =>
registeredDate,
updatedDate,
pullRequest
) <> (Issue.tupled, Issue.unapply)
).<>(Issue.tupled, Issue.unapply)
def byPrimaryKey(owner: String, repository: String, issueId: Int) = byIssue(owner, repository, issueId)
}

View File

@@ -6,7 +6,7 @@ trait IssueLabelComponent extends TemplateComponent { self: Profile =>
lazy val IssueLabels = TableQuery[IssueLabels]
class IssueLabels(tag: Tag) extends Table[IssueLabel](tag, "ISSUE_LABEL") with IssueTemplate with LabelTemplate {
def * = (userName, repositoryName, issueId, labelId) <> (IssueLabel.tupled, IssueLabel.unapply)
def * = (userName, repositoryName, issueId, labelId).<>(IssueLabel.tupled, IssueLabel.unapply)
def byPrimaryKey(owner: String, repository: String, issueId: Int, labelId: Int) =
byIssue(owner, repository, issueId) && (this.labelId === labelId.bind)
}

View File

@@ -9,7 +9,7 @@ trait LabelComponent extends TemplateComponent { self: Profile =>
override val labelId = column[Int]("LABEL_ID", O AutoInc)
override val labelName = column[String]("LABEL_NAME")
val color = column[String]("COLOR")
def * = (userName, repositoryName, labelId, labelName, color) <> (Label.tupled, Label.unapply)
def * = (userName, repositoryName, labelId, labelName, color).<>(Label.tupled, Label.unapply)
def byPrimaryKey(owner: String, repository: String, labelId: Int) = byLabel(owner, repository, labelId)
def byPrimaryKey(userName: Rep[String], repositoryName: Rep[String], labelId: Rep[Int]) =

View File

@@ -13,7 +13,8 @@ trait MilestoneComponent extends TemplateComponent { self: Profile =>
val dueDate = column[Option[java.util.Date]]("DUE_DATE")
val closedDate = column[Option[java.util.Date]]("CLOSED_DATE")
def * =
(userName, repositoryName, milestoneId, title, description, dueDate, closedDate) <> (Milestone.tupled, Milestone.unapply)
(userName, repositoryName, milestoneId, title, description, dueDate, closedDate)
.<>(Milestone.tupled, Milestone.unapply)
def byPrimaryKey(owner: String, repository: String, milestoneId: Int) = byMilestone(owner, repository, milestoneId)
def byPrimaryKey(userName: Rep[String], repositoryName: Rep[String], milestoneId: Rep[Int]) =

View File

@@ -13,7 +13,8 @@ trait PriorityComponent extends TemplateComponent { self: Profile =>
val isDefault = column[Boolean]("IS_DEFAULT")
val color = column[String]("COLOR")
def * =
(userName, repositoryName, priorityId, priorityName, description.?, isDefault, ordering, color) <> (Priority.tupled, Priority.unapply)
(userName, repositoryName, priorityId, priorityName, description.?, isDefault, ordering, color)
.<>(Priority.tupled, Priority.unapply)
def byPrimaryKey(owner: String, repository: String, priorityId: Int) = byPriority(owner, repository, priorityId)
def byPrimaryKey(userName: Rep[String], repositoryName: Rep[String], priorityId: Rep[Int]) =

View File

@@ -45,7 +45,7 @@ trait CoreProfile
with Profile
with AccessTokenComponent
with AccountComponent
with ActivityComponent // ActivityComponent has been deprecated, but keep it for binary compatibility
with ActivityComponent
with CollaboratorComponent
with CommitCommentComponent
with CommitStatusComponent

View File

@@ -7,7 +7,7 @@ trait ProtectedBranchComponent extends TemplateComponent { self: Profile =>
lazy val ProtectedBranches = TableQuery[ProtectedBranches]
class ProtectedBranches(tag: Tag) extends Table[ProtectedBranch](tag, "PROTECTED_BRANCH") with BranchTemplate {
val statusCheckAdmin = column[Boolean]("STATUS_CHECK_ADMIN")
def * = (userName, repositoryName, branch, statusCheckAdmin) <> (ProtectedBranch.tupled, ProtectedBranch.unapply)
def * = (userName, repositoryName, branch, statusCheckAdmin).<>(ProtectedBranch.tupled, ProtectedBranch.unapply)
def byPrimaryKey(userName: String, repositoryName: String, branch: String) =
byBranch(userName, repositoryName, branch)
def byPrimaryKey(userName: Rep[String], repositoryName: Rep[String], branch: Rep[String]) =
@@ -20,7 +20,7 @@ trait ProtectedBranchComponent extends TemplateComponent { self: Profile =>
with BranchTemplate {
val context = column[String]("CONTEXT")
def * =
(userName, repositoryName, branch, context) <> (ProtectedBranchContext.tupled, ProtectedBranchContext.unapply)
(userName, repositoryName, branch, context).<>(ProtectedBranchContext.tupled, ProtectedBranchContext.unapply)
}
}

View File

@@ -25,7 +25,7 @@ trait PullRequestComponent extends TemplateComponent { self: Profile =>
commitIdFrom,
commitIdTo,
isDraft
) <> (PullRequest.tupled, PullRequest.unapply)
).<>(PullRequest.tupled, PullRequest.unapply)
def byPrimaryKey(userName: String, repositoryName: String, issueId: Int) =
byIssue(userName, repositoryName, issueId)

View File

@@ -21,7 +21,8 @@ trait ReleaseAssetComponent extends TemplateComponent {
val updatedDate = column[Date]("UPDATED_DATE")
def * =
(userName, repositoryName, tag, releaseAssetId, fileName, label, size, uploader, registeredDate, updatedDate) <> (ReleaseAsset.tupled, ReleaseAsset.unapply)
(userName, repositoryName, tag, releaseAssetId, fileName, label, size, uploader, registeredDate, updatedDate)
.<>(ReleaseAsset.tupled, ReleaseAsset.unapply)
def byPrimaryKey(owner: String, repository: String, tag: String, fileName: String) =
byTag(owner, repository, tag) && (this.fileName === fileName.bind)
def byTag(owner: String, repository: String, tag: String) =

View File

@@ -17,7 +17,8 @@ trait ReleaseTagComponent extends TemplateComponent {
val updatedDate = column[java.util.Date]("UPDATED_DATE")
def * =
(userName, repositoryName, name, tag, author, content, registeredDate, updatedDate) <> (ReleaseTag.tupled, ReleaseTag.unapply)
(userName, repositoryName, name, tag, author, content, registeredDate, updatedDate)
.<>(ReleaseTag.tupled, ReleaseTag.unapply)
def byPrimaryKey(owner: String, repository: String, tag: String) = byTag(owner, repository, tag)
def byTag(owner: String, repository: String, tag: String) =
byRepository(owner, repository) && (this.tag === tag.bind)

View File

@@ -42,46 +42,48 @@ trait RepositoryComponent extends TemplateComponent { self: Profile =>
parentRepositoryName.?
),
(issuesOption, externalIssuesUrl.?, wikiOption, externalWikiUrl.?, allowFork, mergeOptions, defaultMergeOption)
).shaped <> ({
case (repository, options) =>
Repository(
repository._1,
repository._2,
repository._3,
repository._4,
repository._5,
repository._6,
repository._7,
repository._8,
repository._9,
repository._10,
repository._11,
repository._12,
RepositoryOptions.tupled.apply(options)
)
}, { (r: Repository) =>
Some(
(
).shaped.<>(
{
case (repository, options) =>
Repository(
repository._1,
repository._2,
repository._3,
repository._4,
repository._5,
repository._6,
repository._7,
repository._8,
repository._9,
repository._10,
repository._11,
repository._12,
RepositoryOptions.tupled.apply(options)
)
}, { (r: Repository) =>
Some(
(
r.userName,
r.repositoryName,
r.isPrivate,
r.description,
r.defaultBranch,
r.registeredDate,
r.updatedDate,
r.lastActivityDate,
r.originUserName,
r.originRepositoryName,
r.parentUserName,
r.parentRepositoryName
),
(
RepositoryOptions.unapply(r.options).get
(
r.userName,
r.repositoryName,
r.isPrivate,
r.description,
r.defaultBranch,
r.registeredDate,
r.updatedDate,
r.lastActivityDate,
r.originUserName,
r.originRepositoryName,
r.parentUserName,
r.parentRepositoryName
),
(
RepositoryOptions.unapply(r.options).get
)
)
)
)
})
}
)
def byPrimaryKey(owner: String, repository: String) = byRepository(owner, repository)
}

View File

@@ -14,7 +14,8 @@ trait RepositoryWebHookComponent extends TemplateComponent { self: Profile =>
val token = column[Option[String]]("TOKEN")
val ctype = column[WebHookContentType]("CTYPE")
def * =
(userName, repositoryName, hookId, url, ctype, token) <> ((RepositoryWebHook.apply _).tupled, RepositoryWebHook.unapply)
(userName, repositoryName, hookId, url, ctype, token)
.<>((RepositoryWebHook.apply _).tupled, RepositoryWebHook.unapply)
def byRepositoryUrl(owner: String, repository: String, url: String) =
byRepository(owner, repository) && (this.url === url.bind)

View File

@@ -12,7 +12,7 @@ trait RepositoryWebHookEventComponent extends TemplateComponent { self: Profile
val url = column[String]("URL")
val event = column[WebHook.Event]("EVENT")
def * =
(userName, repositoryName, url, event) <> ((RepositoryWebHookEvent.apply _).tupled, RepositoryWebHookEvent.unapply)
(userName, repositoryName, url, event).<>((RepositoryWebHookEvent.apply _).tupled, RepositoryWebHookEvent.unapply)
def byRepositoryWebHook(owner: String, repository: String, url: String) =
byRepository(owner, repository) && (this.url === url.bind)

View File

@@ -10,7 +10,7 @@ trait SshKeyComponent { self: Profile =>
val sshKeyId = column[Int]("SSH_KEY_ID", O AutoInc)
val title = column[String]("TITLE")
val publicKey = column[String]("PUBLIC_KEY")
def * = (userName, sshKeyId, title, publicKey) <> (SshKey.tupled, SshKey.unapply)
def * = (userName, sshKeyId, title, publicKey).<>(SshKey.tupled, SshKey.unapply)
def byPrimaryKey(userName: String, sshKeyId: Int) =
(this.userName === userName.bind) && (this.sshKeyId === sshKeyId.bind)

View File

@@ -105,13 +105,13 @@ trait AccountService {
}
def getAccountByUserName(userName: String, includeRemoved: Boolean = false)(implicit s: Session): Option[Account] =
Accounts filter (t => (t.userName === userName.bind) && (t.removed === false.bind, !includeRemoved)) firstOption
Accounts filter (t => (t.userName === userName.bind).&&(t.removed === false.bind, !includeRemoved)) firstOption
def getAccountByUserNameIgnoreCase(userName: String, includeRemoved: Boolean = false)(
implicit s: Session
): Option[Account] =
Accounts filter (
t => (t.userName.toLowerCase === userName.toLowerCase.bind) && (t.removed === false.bind, !includeRemoved)
t => (t.userName.toLowerCase === userName.toLowerCase.bind).&&(t.removed === false.bind, !includeRemoved)
) firstOption
def getAccountsByUserNames(userNames: Set[String], knowns: Set[Account], includeRemoved: Boolean = false)(
@@ -123,7 +123,7 @@ trait AccountService {
map
} else {
map ++ Accounts
.filter(t => (t.userName inSetBind needs) && (t.removed === false.bind, !includeRemoved))
.filter(t => (t.userName inSetBind needs).&&(t.removed === false.bind, !includeRemoved))
.list
.map(a => a.userName -> a)
.toMap
@@ -140,15 +140,15 @@ trait AccountService {
(x.map { e =>
e.extraMailAddress.toLowerCase === mailAddress.toLowerCase.bind
}
.getOrElse(false.bind))) && (a.removed === false.bind, !includeRemoved)
.getOrElse(false.bind))).&&(a.removed === false.bind, !includeRemoved)
}
.map { case (a, e) => a } firstOption
def getAllUsers(includeRemoved: Boolean = true, includeGroups: Boolean = true)(implicit s: Session): List[Account] = {
Accounts filter { t =>
(1.bind === 1.bind) &&
(t.groupAccount === false.bind, !includeGroups) &&
(t.removed === false.bind, !includeRemoved)
(1.bind === 1.bind)
.&&(t.groupAccount === false.bind, !includeGroups)
.&&(t.removed === false.bind, !includeRemoved)
} sortBy (_.userName) list
}

View File

@@ -98,6 +98,8 @@ trait ActivityService {
}
}
def recordActivity[T <: { def toActivity: Activity }](info: T): Unit =
def recordActivity[T <: { def toActivity: Activity }](info: T): Unit = {
import scala.language.reflectiveCalls
writeLog(info.toActivity)
}
}

View File

@@ -351,49 +351,64 @@ trait IssuesService {
} else {
((t1.userName ++ "/" ++ t1.repositoryName) inSetBind (repos.map { case (owner, repo) => s"$owner/$repo" }))
}) &&
(t1.closed === (condition.state == "closed").bind) &&
(t1.milestoneId.? isEmpty, condition.milestone == Some(None)) &&
(t1.priorityId.? isEmpty, condition.priority == Some(None)) &&
(t1.assignedUserName.? isEmpty, condition.assigned == Some(None)) &&
(t1.openedUserName === condition.author.get.bind, condition.author.isDefined) &&
(t1.closed === (condition.state == "closed").bind)
.&&(t1.milestoneId.? isEmpty, condition.milestone == Some(None))
.&&(t1.priorityId.? isEmpty, condition.priority == Some(None))
.&&(t1.assignedUserName.? isEmpty, condition.assigned == Some(None))
.&&(t1.openedUserName === condition.author.get.bind, condition.author.isDefined) &&
(searchOption match {
case IssueSearchOption.Issues => t1.pullRequest === false
case IssueSearchOption.PullRequests => t1.pullRequest === true
case IssueSearchOption.Both => t1.pullRequest === false || t1.pullRequest === true
}) &&
})
// Milestone filter
(Milestones filter { t2 =>
(t2.byPrimaryKey(t1.userName, t1.repositoryName, t1.milestoneId)) &&
(t2.title === condition.milestone.get.get.bind)
} exists, condition.milestone.flatten.isDefined) &&
// Priority filter
(Priorities filter { t2 =>
(t2.byPrimaryKey(t1.userName, t1.repositoryName, t1.priorityId)) &&
(t2.priorityName === condition.priority.get.get.bind)
} exists, condition.priority.flatten.isDefined) &&
// Assignee filter
(t1.assignedUserName === condition.assigned.get.get.bind, condition.assigned.flatten.isDefined) &&
// Label filter
(IssueLabels filter { t2 =>
(t2.byIssue(t1.userName, t1.repositoryName, t1.issueId)) &&
(t2.labelId in
(Labels filter { t3 =>
(t3.byRepository(t1.userName, t1.repositoryName)) &&
(t3.labelName inSetBind condition.labels)
} map (_.labelId)))
} exists, condition.labels.nonEmpty) &&
// Visibility filter
(Repositories filter { t2 =>
(t2.byRepository(t1.userName, t1.repositoryName)) &&
(t2.isPrivate === (condition.visibility == Some("private")).bind)
} exists, condition.visibility.nonEmpty) &&
// Organization (group) filter
(t1.userName inSetBind condition.groups, condition.groups.nonEmpty) &&
// Mentioned filter
((t1.openedUserName === condition.mentioned.get.bind) || t1.assignedUserName === condition.mentioned.get.bind ||
(IssueComments filter { t2 =>
(t2.byIssue(t1.userName, t1.repositoryName, t1.issueId)) && (t2.commentedUserName === condition.mentioned.get.bind)
} exists), condition.mentioned.isDefined)
.&&(
Milestones filter { t2 =>
(t2.byPrimaryKey(t1.userName, t1.repositoryName, t1.milestoneId)) &&
(t2.title === condition.milestone.get.get.bind)
} exists,
condition.milestone.flatten.isDefined
)
// Priority filter
.&&(
Priorities filter { t2 =>
(t2.byPrimaryKey(t1.userName, t1.repositoryName, t1.priorityId)) &&
(t2.priorityName === condition.priority.get.get.bind)
} exists,
condition.priority.flatten.isDefined
)
// Assignee filter
.&&(t1.assignedUserName === condition.assigned.get.get.bind, condition.assigned.flatten.isDefined)
// Label filter
.&&(
IssueLabels filter { t2 =>
(t2.byIssue(t1.userName, t1.repositoryName, t1.issueId)) &&
(t2.labelId in
(Labels filter { t3 =>
(t3.byRepository(t1.userName, t1.repositoryName)) &&
(t3.labelName inSetBind condition.labels)
} map (_.labelId)))
} exists,
condition.labels.nonEmpty
)
// Visibility filter
.&&(
Repositories filter { t2 =>
(t2.byRepository(t1.userName, t1.repositoryName)) &&
(t2.isPrivate === (condition.visibility == Some("private")).bind)
} exists,
condition.visibility.nonEmpty
)
// Organization (group) filter
.&&(t1.userName inSetBind condition.groups, condition.groups.nonEmpty)
// Mentioned filter
.&&(
(t1.openedUserName === condition.mentioned.get.bind) || t1.assignedUserName === condition.mentioned.get.bind ||
(IssueComments filter { t2 =>
(t2.byIssue(t1.userName, t1.repositoryName, t1.issueId)) && (t2.commentedUserName === condition.mentioned.get.bind)
} exists),
condition.mentioned.isDefined
)
}
def insertIssue(
@@ -692,8 +707,8 @@ trait IssuesService {
case (t1, t2) =>
keywords
.map { keyword =>
(t1.title.toLowerCase like (s"%${likeEncode(keyword)}%", '^')) ||
(t1.content.toLowerCase like (s"%${likeEncode(keyword)}%", '^'))
(t1.title.toLowerCase.like(s"%${likeEncode(keyword)}%", '^')) ||
(t1.content.toLowerCase.like(s"%${likeEncode(keyword)}%", '^'))
}
.reduceLeft(_ && _)
}
@@ -720,7 +735,7 @@ trait IssuesService {
t2.pullRequest === pullRequest.bind &&
keywords
.map { query =>
t1.content.toLowerCase like (s"%${likeEncode(query)}%", '^')
t1.content.toLowerCase.like(s"%${likeEncode(query)}%", '^')
}
.reduceLeft(_ && _)
}

View File

@@ -10,6 +10,7 @@ import gitbucket.core.util.{JGitUtil, LockUtil}
import gitbucket.core.model.Profile.profile.blockingApi._
import gitbucket.core.model.activity.{CloseIssueInfo, MergeInfo, PushInfo}
import gitbucket.core.service.SystemSettingsService.SystemSettings
import gitbucket.core.service.WebHookService.WebHookPushPayload
import gitbucket.core.util.JGitUtil.CommitInfo
import org.eclipse.jgit.merge.{MergeStrategy, Merger, RecursiveMerger}
import org.eclipse.jgit.api.Git
@@ -27,7 +28,8 @@ trait MergeService {
with IssuesService
with RepositoryService
with PullRequestService
with WebHookPullRequestService =>
with WebHookPullRequestService
with WebHookService =>
import MergeService._
@@ -61,40 +63,91 @@ trait MergeService {
/** merge the pull request with a merge commit */
def mergeWithMergeCommit(
git: Git,
userName: String,
repositoryName: String,
repository: RepositoryInfo,
branch: String,
issueId: Int,
message: String,
committer: PersonIdent
)(implicit s: Session): ObjectId = {
new MergeCacheInfo(git, userName, repositoryName, branch, issueId, getReceiveHooks()).merge(message, committer)
loginAccount: Account,
settings: SystemSettings
)(implicit s: Session, c: JsonFormat.Context): ObjectId = {
val beforeCommitId = git.getRepository.resolve(s"refs/heads/${branch}")
val afterCommitId = new MergeCacheInfo(git, repository.owner, repository.name, branch, issueId, getReceiveHooks())
.merge(message, new PersonIdent(loginAccount.fullName, loginAccount.mailAddress))
callWebHook(git, repository, branch, beforeCommitId, afterCommitId, loginAccount, settings)
afterCommitId
}
/** rebase to the head of the pull request branch */
def mergeWithRebase(
git: Git,
userName: String,
repositoryName: String,
repository: RepositoryInfo,
branch: String,
issueId: Int,
commits: Seq[RevCommit],
committer: PersonIdent
)(implicit s: Session): ObjectId = {
new MergeCacheInfo(git, userName, repositoryName, branch, issueId, getReceiveHooks()).rebase(committer, commits)
loginAccount: Account,
settings: SystemSettings
)(implicit s: Session, c: JsonFormat.Context): ObjectId = {
val beforeCommitId = git.getRepository.resolve(s"refs/heads/${branch}")
val afterCommitId =
new MergeCacheInfo(git, repository.owner, repository.name, branch, issueId, getReceiveHooks())
.rebase(new PersonIdent(loginAccount.fullName, loginAccount.mailAddress), commits)
callWebHook(git, repository, branch, beforeCommitId, afterCommitId, loginAccount, settings)
afterCommitId
}
/** squash commits in the pull request and append it */
def mergeWithSquash(
git: Git,
userName: String,
repositoryName: String,
repository: RepositoryInfo,
branch: String,
issueId: Int,
message: String,
committer: PersonIdent
)(implicit s: Session): ObjectId = {
new MergeCacheInfo(git, userName, repositoryName, branch, issueId, getReceiveHooks()).squash(message, committer)
loginAccount: Account,
settings: SystemSettings
)(implicit s: Session, c: JsonFormat.Context): ObjectId = {
val beforeCommitId = git.getRepository.resolve(s"refs/heads/${branch}")
val afterCommitId =
new MergeCacheInfo(git, repository.owner, repository.name, branch, issueId, getReceiveHooks())
.squash(message, new PersonIdent(loginAccount.fullName, loginAccount.mailAddress))
callWebHook(git, repository, branch, beforeCommitId, afterCommitId, loginAccount, settings)
afterCommitId
}
private def callWebHook(
git: Git,
repository: RepositoryInfo,
branch: String,
beforeCommitId: ObjectId,
afterCommitId: ObjectId,
loginAccount: Account,
settings: SystemSettings
)(
implicit s: Session,
c: JsonFormat.Context
): Unit = {
callWebHookOf(repository.owner, repository.name, WebHook.Push, settings) {
getAccountByUserName(repository.owner).map { ownerAccount =>
WebHookPushPayload(
git,
loginAccount,
s"refs/heads/${branch}",
repository,
git
.log()
.addRange(beforeCommitId, afterCommitId)
.call()
.asScala
.map { commit =>
new JGitUtil.CommitInfo(commit)
}
.toList
.reverse,
ownerAccount,
oldId = beforeCommitId,
newId = afterCommitId
)
}
}
}
/** fetch remote branch to my repository refs/pull/{issueId}/head */
@@ -303,7 +356,8 @@ trait MergeService {
message,
strategy,
commits,
getReceiveHooks()
getReceiveHooks(),
settings
) match {
case Some(newCommitId) =>
// mark issue as merged and close.
@@ -428,8 +482,9 @@ trait MergeService {
message: String,
strategy: String,
commits: Seq[Seq[CommitInfo]],
receiveHooks: Seq[ReceiveHook]
)(implicit s: Session): Option[ObjectId] = {
receiveHooks: Seq[ReceiveHook],
settings: SystemSettings
)(implicit s: Session, c: JsonFormat.Context): Option[ObjectId] = {
val revCommits = Using
.resource(new RevWalk(git.getRepository)) { revWalk =>
commits.flatten.map { commit =>
@@ -443,36 +498,36 @@ trait MergeService {
Some(
mergeWithMergeCommit(
git,
repository.owner,
repository.name,
repository,
pullRequest.branch,
issue.issueId,
s"Merge pull request #${issue.issueId} from ${pullRequest.requestUserName}/${pullRequest.requestBranch}\n\n" + message,
new PersonIdent(loginAccount.fullName, loginAccount.mailAddress)
loginAccount,
settings
)
)
case "rebase" =>
Some(
mergeWithRebase(
git,
repository.owner,
repository.name,
repository,
pullRequest.branch,
issue.issueId,
revCommits,
new PersonIdent(loginAccount.fullName, loginAccount.mailAddress)
loginAccount,
settings
)
)
case "squash" =>
Some(
mergeWithSquash(
git,
repository.owner,
repository.name,
repository,
pullRequest.branch,
issue.issueId,
s"${issue.title} (#${issue.issueId})\n\n" + message,
new PersonIdent(loginAccount.fullName, loginAccount.mailAddress)
loginAccount,
settings
)
)
case _ =>
@@ -561,7 +616,7 @@ object MergeService {
}
def checkConflict(): Option[String] = {
checkConflictCache.getOrElse(checkConflictForce)
checkConflictCache().getOrElse(checkConflictForce())
}
def checkConflictForce(): Option[String] = {

View File

@@ -101,7 +101,10 @@ trait OpenIDConnectService {
redirectURI: URI
): Option[AuthenticationSuccessResponse] =
try {
AuthenticationResponseParser.parse(redirectURI, params.asJava) match {
AuthenticationResponseParser.parse(
redirectURI,
params.map { case (key, value) => (key, List(value).asJava) }.asJava
) match {
case response: AuthenticationSuccessResponse =>
if (response.getState == state) {
Some(response)

View File

@@ -77,9 +77,9 @@ trait PullRequestService {
}
.filter {
case (t1, t2) =>
(t2.closed === closed.bind) &&
(t1.userName === owner.get.bind, owner.isDefined) &&
(t1.repositoryName === repository.get.bind, repository.isDefined)
(t2.closed === closed.bind)
.&&(t1.userName === owner.get.bind, owner.isDefined)
.&&(t1.repositoryName === repository.get.bind, repository.isDefined)
}
.groupBy { case (t1, t2) => t2.openedUserName }
.map { case (userName, t) => userName -> t.length }
@@ -183,10 +183,10 @@ trait PullRequestService {
}
.filter {
case (t1, t2) =>
(t1.requestUserName === userName.bind) &&
(t1.requestRepositoryName === repositoryName.bind) &&
(t1.requestBranch === branch.bind) &&
(t2.closed === closed.get.bind, closed.isDefined)
(t1.requestUserName === userName.bind)
.&&(t1.requestRepositoryName === repositoryName.bind)
.&&(t1.requestBranch === branch.bind)
.&&(t2.closed === closed.get.bind, closed.isDefined)
}
.map { case (t1, t2) => t1 }
.list
@@ -201,10 +201,10 @@ trait PullRequestService {
}
.filter {
case (t1, t2) =>
(t1.requestUserName === userName.bind) &&
(t1.requestRepositoryName === repositoryName.bind) &&
(t1.branch === branch.bind) &&
(t2.closed === closed.get.bind, closed.isDefined)
(t1.requestUserName === userName.bind)
.&&(t1.requestRepositoryName === repositoryName.bind)
.&&(t1.branch === branch.bind)
.&&(t2.closed === closed.get.bind, closed.isDefined)
}
.map { case (t1, t2) => t1 }
.list

View File

@@ -170,9 +170,11 @@ trait WebHookService {
}
}
// Records in WEB_HOOK_EVENT will be deleted automatically by cascaded constraint
def deleteWebHook(owner: String, repository: String, url: String)(implicit s: Session): Unit =
RepositoryWebHooks.filter(_.byRepositoryUrl(owner, repository, url)).delete
// Records in WEB_HOOK_EVENT will be deleted automatically by cascaded constraint
def deleteWebHookById(id: Int)(implicit s: Session): Unit =
RepositoryWebHooks.filter(_.byId(id)).delete
@@ -255,11 +257,11 @@ trait WebHookService {
)(implicit s: Session, c: JsonFormat.Context): Unit = {
val webHooks = getWebHooksByEvent(owner, repository, event)
if (webHooks.nonEmpty) {
makePayload.map(callWebHook(event, webHooks, _, settings))
makePayload.foreach(callWebHook(event, webHooks, _, settings))
}
val accountWebHooks = getAccountWebHooksByEvent(owner, event)
if (accountWebHooks.nonEmpty) {
makePayload.map(callWebHook(event, accountWebHooks, _, settings))
makePayload.foreach(callWebHook(event, accountWebHooks, _, settings))
}
}
@@ -283,7 +285,7 @@ trait WebHookService {
val json = JsonFormat(payload)
webHooks.map { webHook =>
val reqPromise = Promise[HttpRequest]
val reqPromise = Promise[HttpRequest]()
val f = Future {
val itcp = new org.apache.http.HttpRequestInterceptor {
def process(res: HttpRequest, ctx: HttpContext): Unit = {

View File

@@ -202,7 +202,7 @@ object JDBCUtil {
}
private def allTablesOrderByDependencies(meta: DatabaseMetaData): Seq[String] = {
val tables = allTableNames.map { tableName =>
val tables = allTableNames().map { tableName =>
TableDependency(tableName, childTables(meta, tableName))
}

View File

@@ -125,6 +125,8 @@ object LDAPUtil {
}
private def getSslProvider(): Provider = {
import scala.language.existentials
val cachedInstance = provider.get()
if (cachedInstance == null) {
val cls = try {

View File

@@ -1,17 +1,17 @@
@(issue: gitbucket.core.model.Issue,
pullreq: gitbucket.core.model.PullRequest,
commits: Seq[Seq[gitbucket.core.util.JGitUtil.CommitInfo]],
commits: Seq[Seq[(gitbucket.core.util.JGitUtil.CommitInfo, Option[(gitbucket.core.model.CommitState, List[gitbucket.core.model.CommitStatus])])]],
comments: Seq[gitbucket.core.model.Comment],
changedFileSize: Int,
isManageable: Boolean,
repository: gitbucket.core.service.RepositoryService.RepositoryInfo)(implicit context: gitbucket.core.controller.Context)
@import gitbucket.core.view.helpers
@gitbucket.core.pulls.html.menu("commits", issue, pullreq, commits.flatten, comments, changedFileSize, isManageable, repository){
@gitbucket.core.pulls.html.menu("commits", issue, pullreq, commits.flatten.map(_._1), comments, changedFileSize, isManageable, repository){
<table class="table table-bordered table-hover">
@commits.map { day =>
<tr>
<th rowspan="@day.size" width="100">@helpers.date(day.head.commitTime)</th>
@day.zipWithIndex.map { case (commit, i) =>
<th rowspan="@day.size" width="100">@helpers.date(day.head._1.commitTime)</th>
@day.zipWithIndex.map { case ((commit, status), i) =>
@if(i != 0){ <tr> }
<td>
<div class="pull-right text-right">
@@ -37,6 +37,9 @@
}
@helpers.user(commit.committerName, commit.committerEmailAddress, "username")
<span class="muted">committed @gitbucket.core.helper.html.datetimeago(commit.commitTime)</span>
@status.map { case (summary, statuses) =>
@gitbucket.core.helper.html.commitstatus(commit.id, summary, statuses)
}
</div>
</div>
</div>

View File

@@ -71,7 +71,7 @@ class AccessTokenServiceSpec extends AnyFunSuite with ServiceSpecBase {
withTestDB { implicit session =>
val tokenIt = List("token1", "token1", "token1", "token2").iterator
val service = new AccessTokenService {
override def makeAccessTokenString: String = tokenIt.next
override def makeAccessTokenString: String = tokenIt.next()
}
assert(service.generateAccessToken("root", "note1")._2 == "token1")

View File

@@ -1,18 +1,34 @@
package gitbucket.core.service
import gitbucket.core.util.Directory._
import gitbucket.core.util.GitSpecUtil._
import org.eclipse.jgit.api.Git
import org.eclipse.jgit.lib._
import org.eclipse.jgit.revwalk._
import org.eclipse.jetty.server.handler.AbstractHandler
import org.scalatest.funspec.AnyFunSpec
import java.io.File
import java.util.Date
import java.net.InetSocketAddress
import java.nio.charset.StandardCharsets
import gitbucket.core.plugin.ReceiveHook
import javax.servlet.http.{HttpServletRequest, HttpServletResponse}
import scala.util.Using
import scala.jdk.CollectionConverters._
import gitbucket.core.controller.Context
import gitbucket.core.plugin.ReceiveHook
import gitbucket.core.util.Directory._
import gitbucket.core.util.GitSpecUtil._
import gitbucket.core.service.RepositoryService.RepositoryInfo
import gitbucket.core.model._
import gitbucket.core.model.Profile._
import gitbucket.core.model.Profile.profile.blockingApi._
import gitbucket.core.service.WebHookService.WebHookPushPayload
import org.eclipse.jetty.webapp.WebAppContext
import org.eclipse.jetty.server.{Request, Server}
import org.json4s.jackson.JsonMethods._
import MergeServiceSpec._
import org.json4s.JsonAST.{JArray, JString}
class MergeServiceSpec extends AnyFunSpec {
class MergeServiceSpec extends AnyFunSpec with ServiceSpecBase {
val service = new MergeService with AccountService with ActivityService with IssuesService with LabelsService
with MilestonesService with RepositoryService with PrioritiesService with PullRequestService with CommitsService
with WebHookPullRequestService with WebHookPullRequestReviewCommentService with RequestCache {
@@ -115,22 +131,161 @@ class MergeServiceSpec extends AnyFunSpec {
}
describe("mergePullRequest") {
it("can merge") {
val repo8Dir = initRepository("user1", "repo8")
Using.resource(Git.open(repo8Dir)) { git =>
createFile(git, s"refs/pull/${issueId}/head", "test.txt", "hoge2")
val committer = new PersonIdent("dummy2", "dummy2@example.com")
assert(getFile(git, branch, "test.txt").content.get == "hoge")
val requestBranchId = git.getRepository.resolve(s"refs/pull/${issueId}/head")
val masterId = git.getRepository.resolve(branch)
service.mergeWithMergeCommit(git, "user1", "repo8", branch, issueId, "merged", committer)(null)
val lastCommitId = git.getRepository.resolve(branch)
val commit = Using.resource(new RevWalk(git.getRepository))(_.parseCommit(lastCommitId))
assert(commit.getCommitterIdent() == committer)
assert(commit.getAuthorIdent() == committer)
assert(commit.getFullMessage() == "merged")
assert(commit.getParents.toSet == Set(requestBranchId, masterId))
assert(getFile(git, branch, "test.txt").content.get == "hoge2")
implicit val jsonFormats = gitbucket.core.api.JsonFormat.jsonFormats
import gitbucket.core.util.Implicits._
withTestDB { implicit session =>
generateNewUserWithDBRepository("user1", "repo8")
initRepository("user1", "repo8")
implicit val context = Context(
createSystemSettings(),
Some(createAccount("dummy2", "dummy2-fullname", "dummy2@example.com")),
request
)
Using.resource(Git.open(getRepositoryDir("user1", "repo8"))) { git =>
val commitId = createFile(git, s"refs/pull/${issueId}/head", "test.txt", "hoge2")
assert(getFile(git, branch, "test.txt").content.get == "hoge")
val requestBranchId = git.getRepository.resolve(s"refs/pull/${issueId}/head")
val masterId = git.getRepository.resolve(branch)
val repository = createRepositoryInfo("user1", "repo8")
registerWebHook("user1", "repo8", "http://localhost:9999")
Using.resource(new TestServer()) { server =>
service.mergeWithMergeCommit(
git,
repository,
branch,
issueId,
"merged",
context.loginAccount.get,
context.settings
)
Thread.sleep(5000)
val json = parse(new String(server.lastRequestContent, StandardCharsets.UTF_8))
// 2 commits (create file + merge commit)
assert((json \ "commits").asInstanceOf[JArray].arr.length == 2)
// verify id of file creation commit
assert((json \ "commits" \ "id").asInstanceOf[JArray].arr(0).asInstanceOf[JString].s == commitId.getName)
}
val lastCommitId = git.getRepository.resolve(branch)
val commit = Using.resource(new RevWalk(git.getRepository))(_.parseCommit(lastCommitId))
assert(commit.getCommitterIdent().getName == "dummy2-fullname")
assert(commit.getCommitterIdent().getEmailAddress == "dummy2@example.com")
assert(commit.getAuthorIdent().getName == "dummy2-fullname")
assert(commit.getAuthorIdent().getEmailAddress == "dummy2@example.com")
assert(commit.getFullMessage() == "merged")
assert(commit.getParents.toSet == Set(requestBranchId, masterId))
assert(getFile(git, branch, "test.txt").content.get == "hoge2")
}
}
}
}
private def registerWebHook(userName: String, repositoryName: String, url: String)(implicit s: Session): Unit = {
RepositoryWebHooks insert RepositoryWebHook(
userName = userName,
repositoryName = repositoryName,
url = url,
ctype = WebHookContentType.JSON,
token = None
)
RepositoryWebHookEvents insert RepositoryWebHookEvent(userName, repositoryName, url, WebHook.Push)
}
private def createAccount(userName: String, fullName: String, mailAddress: String): Account =
Account(
userName = userName,
fullName = fullName,
mailAddress = mailAddress,
password = "password",
isAdmin = false,
url = None,
registeredDate = new Date(),
updatedDate = new Date(),
lastLoginDate = None,
image = None,
isGroupAccount = false,
isRemoved = false,
description = None
)
private def createRepositoryInfo(userName: String, repositoryName: String): RepositoryInfo =
RepositoryInfo(
owner = userName,
name = repositoryName,
repository = gitbucket.core.model.Repository(
userName = userName,
repositoryName = repositoryName,
isPrivate = false,
description = None,
defaultBranch = "master",
registeredDate = new Date(),
updatedDate = new Date(),
lastActivityDate = new Date(),
originUserName = None,
originRepositoryName = None,
parentUserName = None,
parentRepositoryName = None,
options = RepositoryOptions(
issuesOption = "PUBLIC",
externalIssuesUrl = None,
wikiOption = "PUBLIC",
externalWikiUrl = None,
allowFork = true,
mergeOptions = "merge-commit,squash,rebase",
defaultMergeOption = "merge-commit"
)
),
issueCount = 0,
pullCount = 0,
forkedCount = 0,
milestoneCount = 0,
branchList = Nil,
tags = Nil,
managers = Nil
)
}
object MergeServiceSpec {
class TestServer extends AutoCloseable {
var lastRequestURI: String = null
var lastRequestHeaders: Map[String, String] = null
var lastRequestContent: Array[Byte] = null
val server = new Server(new InetSocketAddress(9999))
val context = new WebAppContext()
context.setServer(server)
server.setStopAtShutdown(true)
server.setStopTimeout(500)
server.setHandler(new AbstractHandler {
override def handle(
target: String,
baseRequest: Request,
request: HttpServletRequest,
response: HttpServletResponse
): Unit = {
lastRequestURI = request.getRequestURI
lastRequestHeaders = request.getHeaderNames.asScala.map { key =>
key -> request.getHeader(key)
}.toMap
val bytes = new Array[Byte](request.getContentLength)
if (bytes.length > 0) {
request.getInputStream.read(bytes)
lastRequestContent = bytes
}
}
})
server.start()
override def close(): Unit = {
server.stop()
}
}
}

View File

@@ -28,7 +28,7 @@ trait ServiceSpecBase {
when(request.getContextPath).thenReturn("")
when(request.getSession).thenReturn(session)
private def createSystemSettings() =
def createSystemSettings() =
SystemSettings(
baseUrl = None,
information = None,

View File

@@ -46,7 +46,7 @@ object GitSpecUtil {
authorName: String = "dummy",
authorEmail: String = "dummy@example.com",
message: String = "test commit"
): Unit = {
): ObjectId = {
val builder = DirCache.newInCore.builder()
val inserter = git.getRepository.newObjectInserter()
val headId = git.getRepository.resolve(branch + "^{commit}")
@@ -65,7 +65,7 @@ object GitSpecUtil {
)
)
builder.finish()
JGitUtil.createNewCommit(
val commitId = JGitUtil.createNewCommit(
git,
inserter,
headId,
@@ -77,6 +77,7 @@ object GitSpecUtil {
)
inserter.flush()
inserter.close()
commitId
}
def getFile(git: Git, branch: String, path: String) = {