mirror of
https://github.com/gitbucket/gitbucket.git
synced 2026-05-07 15:36:22 +02:00
Fix refs API as far as Jenkins github-branch-source plugin can detect tags (#2936)
Co-authored-by: Thomas Geier <thomas.geier@solidat.de>
This commit is contained in:
@@ -1,5 +1,49 @@
|
||||
package gitbucket.core.api
|
||||
|
||||
case class ApiObject(sha: String)
|
||||
import gitbucket.core.util.JGitUtil.TagInfo
|
||||
import gitbucket.core.util.RepositoryName
|
||||
import org.eclipse.jgit.lib.Ref
|
||||
|
||||
case class ApiRef(ref: String, `object`: ApiObject)
|
||||
case class ApiRefCommit(
|
||||
sha: String,
|
||||
`type`: String,
|
||||
url: ApiPath
|
||||
)
|
||||
|
||||
case class ApiRef(
|
||||
ref: String,
|
||||
node_id: String = "",
|
||||
url: ApiPath,
|
||||
`object`: ApiRefCommit,
|
||||
)
|
||||
|
||||
object ApiRef {
|
||||
|
||||
def fromRef(
|
||||
repositoryName: RepositoryName,
|
||||
ref: Ref
|
||||
): ApiRef =
|
||||
ApiRef(
|
||||
ref = ref.getName,
|
||||
url = ApiPath(s"/api/v3/repos/${repositoryName.fullName}/git/${ref.getName}"),
|
||||
`object` = ApiRefCommit(
|
||||
sha = ref.getObjectId.getName,
|
||||
url = ApiPath(s"/api/v3/repos/${repositoryName.fullName}/git/commits/${ref.getObjectId.getName}"),
|
||||
`type` = "commit"
|
||||
)
|
||||
)
|
||||
|
||||
def fromTag(
|
||||
repositoryName: RepositoryName,
|
||||
tagInfo: TagInfo
|
||||
): ApiRef =
|
||||
ApiRef(
|
||||
ref = s"refs/tags/${tagInfo.name}",
|
||||
url = ApiPath(s"/api/v3/repos/${repositoryName.fullName}/refs/tags/${tagInfo.name}"),
|
||||
`object` = ApiRefCommit(
|
||||
sha = tagInfo.id,
|
||||
url = ApiPath(s"/api/v3/repos/${repositoryName.fullName}/git/tags/${tagInfo.id}"), // TODO This URL is not yet available?
|
||||
`type` = "commit"
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
package gitbucket.core.api
|
||||
|
||||
import gitbucket.core.util.RepositoryName
|
||||
|
||||
case class ApiTagCommit(
|
||||
sha: String,
|
||||
url: ApiPath
|
||||
)
|
||||
|
||||
case class ApiTag(
|
||||
name: String,
|
||||
commit: ApiTagCommit,
|
||||
zipball_url: ApiPath,
|
||||
tarball_url: ApiPath
|
||||
)
|
||||
|
||||
object ApiTag {
|
||||
def apply(
|
||||
tagName: String,
|
||||
repositoryName: RepositoryName,
|
||||
commitId: String
|
||||
): ApiTag =
|
||||
ApiTag(
|
||||
name = tagName,
|
||||
commit = ApiTagCommit(sha = commitId, url = ApiPath(s"/${repositoryName.fullName}/commits/${commitId}")),
|
||||
zipball_url = ApiPath(s"/${repositoryName.fullName}/archive/${tagName}.zip"),
|
||||
tarball_url = ApiPath(s"/${repositoryName.fullName}/archive/${tagName}.tar.gz")
|
||||
)
|
||||
}
|
||||
@@ -1,9 +1,10 @@
|
||||
package gitbucket.core.controller.api
|
||||
import gitbucket.core.api.{ApiObject, ApiRef, CreateARef, JsonFormat, UpdateARef}
|
||||
import gitbucket.core.api.{ApiRef, CreateARef, JsonFormat, UpdateARef}
|
||||
import gitbucket.core.controller.ControllerBase
|
||||
import gitbucket.core.service.RepositoryService.RepositoryInfo
|
||||
import gitbucket.core.util.Directory.getRepositoryDir
|
||||
import gitbucket.core.util.ReferrerAuthenticator
|
||||
import gitbucket.core.util.Implicits._
|
||||
import gitbucket.core.util.{ReferrerAuthenticator, RepositoryName}
|
||||
import org.eclipse.jgit.api.Git
|
||||
import org.eclipse.jgit.lib.ObjectId
|
||||
import org.eclipse.jgit.lib.RefUpdate.Result
|
||||
@@ -23,37 +24,43 @@ trait ApiGitReferenceControllerBase extends ControllerBase {
|
||||
* https://docs.github.com/en/free-pro-team@latest/rest/reference/git#get-a-reference
|
||||
*/
|
||||
get("/api/v3/repos/:owner/:repository/git/ref/*")(referrersOnly { repository =>
|
||||
getRef()
|
||||
val revstr = multiParams("splat").head
|
||||
getRef(revstr, repository)
|
||||
})
|
||||
|
||||
// Some versions of GHE support this path
|
||||
get("/api/v3/repos/:owner/:repository/git/refs/*")(referrersOnly { repository =>
|
||||
logger.warn("git/refs/ endpoint may not be compatible with GitHub API v3. Consider using git/ref/ endpoint instead")
|
||||
getRef()
|
||||
val revstr = multiParams("splat").head
|
||||
getRef(revstr, repository)
|
||||
})
|
||||
|
||||
private def getRef() = {
|
||||
val revstr = multiParams("splat").head
|
||||
Using.resource(Git.open(getRepositoryDir(params("owner"), params("repository")))) { git =>
|
||||
val ref = git.getRepository().findRef(revstr)
|
||||
protected def getRef(revstr: String, repository: RepositoryInfo): String = {
|
||||
logger.debug(s"getRef: path '${revstr}'")
|
||||
|
||||
if (ref != null) {
|
||||
val sha = ref.getObjectId().name()
|
||||
JsonFormat(ApiRef(revstr, ApiObject(sha)))
|
||||
val name = RepositoryName(repository)
|
||||
val result = JsonFormat(revstr match {
|
||||
case tags if tags == "tags" =>
|
||||
repository.tags.map(ApiRef.fromTag(name, _))
|
||||
case other =>
|
||||
Using.resource(Git.open(getRepositoryDir(repository.owner, repository.name))) { git =>
|
||||
git.getRepository().findRef(other) match {
|
||||
case null =>
|
||||
val refs = git
|
||||
.getRepository()
|
||||
.getRefDatabase()
|
||||
.getRefsByPrefix("refs/")
|
||||
.asScala
|
||||
|
||||
} else {
|
||||
val refs = git
|
||||
.getRepository()
|
||||
.getRefDatabase()
|
||||
.getRefsByPrefix("refs/")
|
||||
.asScala
|
||||
refs.map(ApiRef.fromRef(name, _))
|
||||
case hit =>
|
||||
ApiRef.fromRef(name, hit)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
JsonFormat(refs.map { ref =>
|
||||
val sha = ref.getObjectId().name()
|
||||
ApiRef(revstr, ApiObject(sha))
|
||||
})
|
||||
}
|
||||
}
|
||||
logger.debug(s"json result: $result")
|
||||
result
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -65,17 +72,17 @@ trait ApiGitReferenceControllerBase extends ControllerBase {
|
||||
* iii. Create a reference
|
||||
* https://docs.github.com/en/free-pro-team@latest/rest/reference/git#create-a-reference
|
||||
*/
|
||||
post("/api/v3/repos/:owner/:repository/git/refs")(referrersOnly { _ =>
|
||||
post("/api/v3/repos/:owner/:repository/git/refs")(referrersOnly { repository =>
|
||||
extractFromJsonBody[CreateARef].map {
|
||||
data =>
|
||||
Using.resource(Git.open(getRepositoryDir(params("owner"), params("repository")))) { git =>
|
||||
Using.resource(Git.open(getRepositoryDir(repository.owner, repository.owner))) { git =>
|
||||
val ref = git.getRepository.findRef(data.ref)
|
||||
if (ref == null) {
|
||||
val update = git.getRepository.updateRef(data.ref)
|
||||
update.setNewObjectId(ObjectId.fromString(data.sha))
|
||||
val result = update.update()
|
||||
result match {
|
||||
case Result.NEW => JsonFormat(ApiRef(update.getName, ApiObject(update.getNewObjectId.getName)))
|
||||
case Result.NEW => JsonFormat(ApiRef.fromRef(RepositoryName(repository.owner, repository.name), ref))
|
||||
case _ => UnprocessableEntity(result.name())
|
||||
}
|
||||
} else {
|
||||
@@ -89,11 +96,11 @@ trait ApiGitReferenceControllerBase extends ControllerBase {
|
||||
* iv. Update a reference
|
||||
* https://docs.github.com/en/free-pro-team@latest/rest/reference/git#update-a-reference
|
||||
*/
|
||||
patch("/api/v3/repos/:owner/:repository/git/refs/*")(referrersOnly { _ =>
|
||||
patch("/api/v3/repos/:owner/:repository/git/refs/*")(referrersOnly { repository =>
|
||||
val refName = multiParams("splat").mkString("/")
|
||||
extractFromJsonBody[UpdateARef].map {
|
||||
data =>
|
||||
Using.resource(Git.open(getRepositoryDir(params("owner"), params("repository")))) { git =>
|
||||
Using.resource(Git.open(getRepositoryDir(repository.owner, repository.owner))) { git =>
|
||||
val ref = git.getRepository.findRef(refName)
|
||||
if (ref == null) {
|
||||
UnprocessableEntity("Ref does not exist.")
|
||||
@@ -104,7 +111,7 @@ trait ApiGitReferenceControllerBase extends ControllerBase {
|
||||
val result = update.update()
|
||||
result match {
|
||||
case Result.FORCED | Result.FAST_FORWARD | Result.NO_CHANGE =>
|
||||
JsonFormat(ApiRef(update.getName, ApiObject(update.getNewObjectId.getName)))
|
||||
JsonFormat(ApiRef.fromRef(RepositoryName(repository), update.getRef))
|
||||
case _ => UnprocessableEntity(result.name())
|
||||
}
|
||||
}
|
||||
@@ -116,7 +123,7 @@ trait ApiGitReferenceControllerBase extends ControllerBase {
|
||||
* v. Delete a reference
|
||||
* https://docs.github.com/en/free-pro-team@latest/rest/reference/git#delete-a-reference
|
||||
*/
|
||||
delete("/api/v3/repos/:owner/:repository/git/refs/*")(referrersOnly { _ =>
|
||||
delete("/api/v3/repos/:owner/:repository/git/refs/*")(referrersOnly { repository =>
|
||||
val refName = multiParams("splat").mkString("/")
|
||||
Using.resource(Git.open(getRepositoryDir(params("owner"), params("repository")))) { git =>
|
||||
val ref = git.getRepository.findRef(refName)
|
||||
|
||||
@@ -16,6 +16,7 @@ import scala.util.Using
|
||||
|
||||
trait ApiRepositoryControllerBase extends ControllerBase {
|
||||
self: RepositoryService
|
||||
with ApiGitReferenceControllerBase
|
||||
with RepositoryCreationService
|
||||
with AccountService
|
||||
with OwnerAuthenticator
|
||||
@@ -184,9 +185,11 @@ trait ApiRepositoryControllerBase extends ControllerBase {
|
||||
* https://docs.github.com/en/rest/reference/repos#list-repository-tags
|
||||
*/
|
||||
get("/api/v3/repos/:owner/:repository/tags")(referrersOnly { repository =>
|
||||
JsonFormat(
|
||||
repository.tags.map(tagInfo => ApiTag(tagInfo.name, RepositoryName(repository), tagInfo.id))
|
||||
)
|
||||
Using.resource(Git.open(getRepositoryDir(repository.owner, repository.name))) { git =>
|
||||
JsonFormat(
|
||||
self.getRef("tags", repository)
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
/*
|
||||
|
||||
29
src/test/resources/logback-test.xml
Normal file
29
src/test/resources/logback-test.xml
Normal file
@@ -0,0 +1,29 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<configuration>
|
||||
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
|
||||
<encoder>
|
||||
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<!--
|
||||
<appender name="FILE" class="ch.qos.logback.core.FileAppender">
|
||||
<file>gitbucket.log</file>
|
||||
<encoder>
|
||||
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
-->
|
||||
|
||||
<logger name="gitbucket" level="DEBUG"/>
|
||||
<root level="WARN">
|
||||
<appender-ref ref="STDOUT" />
|
||||
</root>
|
||||
|
||||
<!--
|
||||
<logger name="service.WebHookService" level="DEBUG" />
|
||||
<logger name="servlet" level="DEBUG" />
|
||||
<logger name="scala.slick.jdbc.JdbcBackend.statement" level="DEBUG" />
|
||||
-->
|
||||
|
||||
</configuration>
|
||||
@@ -134,6 +134,24 @@ class ApiIntegrationTest extends AnyFunSuite {
|
||||
assert(statusList.get(1).getState == GHCommitState.FAILURE)
|
||||
assert(statusList.get(1).getContext == "context")
|
||||
}
|
||||
|
||||
// get master ref
|
||||
{
|
||||
val ref = repo.getRef("heads/master")
|
||||
assert(ref.getRef == "refs/heads/master")
|
||||
assert(
|
||||
ref.getUrl.toString == "http://localhost:19999/api/v3/repos/root/create_status_test/git/refs/heads/master"
|
||||
)
|
||||
assert(ref.getObject.getType == "commit")
|
||||
}
|
||||
|
||||
// // get tag v1.0
|
||||
// {
|
||||
// val ref = repo.getRef("heads/tags/v1.0")
|
||||
// assert(ref.getRef == "heads/tags/v1.0")
|
||||
// assert(ref.getUrl == "tbd")
|
||||
// assert(ref.getObject.getType == "tag")
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -432,9 +432,29 @@ object ApiSpecModels {
|
||||
|
||||
val apiPusher = ApiPusher(account)
|
||||
|
||||
val apiRef = ApiRef(
|
||||
ref = "refs/heads/featureA",
|
||||
`object` = ApiObject(sha1)
|
||||
//have both urls as https, as the expected samples are using https
|
||||
val gitHubContext = JsonFormat.Context("https://api.github.com", Some("https://api.github.com"))
|
||||
|
||||
val apiRefHeadsMaster = ApiRef(
|
||||
ref = "refs/heads/master",
|
||||
url = ApiPath("/repos/gitbucket/gitbucket/git/refs/heads/master"),
|
||||
node_id = "MDM6UmVmOTM1MDc0NjpyZWZzL2hlYWRzL21hc3Rlcg==",
|
||||
`object` = ApiRefCommit(
|
||||
sha = "6b2d124d092402f2c2b7131caada05ead9e7de6d",
|
||||
`type` = "commit",
|
||||
url = ApiPath("/repos/gitbucket/gitbucket/git/commits/6b2d124d092402f2c2b7131caada05ead9e7de6d")
|
||||
)
|
||||
)
|
||||
|
||||
val apiRefTag = ApiRef(
|
||||
ref = "refs/tags/1.0",
|
||||
url = ApiPath("/repos/gitbucket/gitbucket/git/refs/tags/1.0"),
|
||||
node_id = "MDM6UmVmOTM1MDc0NjpyZWZzL3RhZ3MvMS4w",
|
||||
`object` = ApiRefCommit(
|
||||
sha = "1f164ecf2f59190afc8d7204a221c739e707df4c",
|
||||
`type` = "tag",
|
||||
url = ApiPath("/repos/gitbucket/gitbucket/git/tags/1f164ecf2f59190afc8d7204a221c739e707df4c")
|
||||
)
|
||||
)
|
||||
|
||||
val assetFileName = "010203040a0b0c0d"
|
||||
@@ -765,8 +785,33 @@ object ApiSpecModels {
|
||||
|
||||
val jsonPusher = """{"name":"octocat","email":"octocat@example.com"}"""
|
||||
|
||||
//I checked all refs in gitbucket repo, and there appears to be only type "commit" and type "tag"
|
||||
val jsonRef = """{"ref":"refs/heads/featureA","object":{"sha":"6dcb09b5b57875f334f61aebed695e2e4193db5e"}}"""
|
||||
|
||||
val jsonRefHeadsMaster =
|
||||
"""{
|
||||
|"ref": "refs/heads/master",
|
||||
|"node_id": "MDM6UmVmOTM1MDc0NjpyZWZzL2hlYWRzL21hc3Rlcg==",
|
||||
|"url": "https://api.github.com/repos/gitbucket/gitbucket/git/refs/heads/master",
|
||||
|"object": {
|
||||
|"sha": "6b2d124d092402f2c2b7131caada05ead9e7de6d",
|
||||
|"type": "commit",
|
||||
|"url": "https://api.github.com/repos/gitbucket/gitbucket/git/commits/6b2d124d092402f2c2b7131caada05ead9e7de6d"
|
||||
|}
|
||||
|}""".stripMargin
|
||||
|
||||
val jsonRefTag =
|
||||
"""{
|
||||
|"ref": "refs/tags/1.0",
|
||||
|"node_id": "MDM6UmVmOTM1MDc0NjpyZWZzL3RhZ3MvMS4w",
|
||||
|"url": "https://api.github.com/repos/gitbucket/gitbucket/git/refs/tags/1.0",
|
||||
|"object": {
|
||||
|"sha": "1f164ecf2f59190afc8d7204a221c739e707df4c",
|
||||
|"type": "tag",
|
||||
|"url": "https://api.github.com/repos/gitbucket/gitbucket/git/tags/1f164ecf2f59190afc8d7204a221c739e707df4c"
|
||||
|}
|
||||
|}""".stripMargin
|
||||
|
||||
val jsonReleaseAsset =
|
||||
s"""{
|
||||
|"name":"release.zip",
|
||||
|
||||
@@ -8,6 +8,12 @@ class JsonFormatSpec extends AnyFunSuite {
|
||||
implicit val format = JsonFormat.jsonFormats
|
||||
|
||||
private def expected(json: String) = json.replaceAll("\n", "")
|
||||
def normalizeJson(json: String) = {
|
||||
org.json4s.jackson.parseJson(json)
|
||||
}
|
||||
def assertEqualJson(actual: String, expected: String) = {
|
||||
assert(normalizeJson(actual) == normalizeJson(expected))
|
||||
}
|
||||
|
||||
test("apiUser") {
|
||||
assert(JsonFormat(apiUser) == expected(jsonUser))
|
||||
@@ -76,8 +82,11 @@ class JsonFormatSpec extends AnyFunSuite {
|
||||
test("apiPusher") {
|
||||
assert(JsonFormat(apiPusher) == expected(jsonPusher))
|
||||
}
|
||||
test("apiRef") {
|
||||
assert(JsonFormat(apiRef) == expected(jsonRef))
|
||||
test("apiRefHead") {
|
||||
assertEqualJson(JsonFormat(apiRefHeadsMaster)(gitHubContext), jsonRefHeadsMaster)
|
||||
}
|
||||
test("apiRefTag") {
|
||||
assertEqualJson(JsonFormat(apiRefTag)(gitHubContext), jsonRefTag)
|
||||
}
|
||||
test("apiReleaseAsset") {
|
||||
assert(JsonFormat(apiReleaseAsset) == expected(jsonReleaseAsset))
|
||||
|
||||
Reference in New Issue
Block a user