Merge "Add REST API endpoint to create a tag on a project"
This commit is contained in:
commit
43b88be9a9
@ -1568,6 +1568,57 @@ describes the child project.
|
||||
[[tag-endpoints]]
|
||||
== Tag Endpoints
|
||||
|
||||
[[create-tag]]
|
||||
=== Create Tag
|
||||
|
||||
--
|
||||
'PUT /projects/link:#project-name[\{project-name\}]/tags/link:#tag-id[\{tag-id\}]'
|
||||
--
|
||||
|
||||
Create a new tag on the project.
|
||||
|
||||
In the request body additional data for the tag can be provided as
|
||||
link:#tag-input[TagInput].
|
||||
|
||||
If a message is provided in the input, the tag is created as an
|
||||
annotated tag with the current user as tagger. Signed tags are not
|
||||
supported.
|
||||
|
||||
.Request
|
||||
----
|
||||
PUT /projects/MyProject/tags/v1.0 HTTP/1.0
|
||||
Content-Type: application/json; charset=UTF-8
|
||||
|
||||
{
|
||||
"message": "annotation",
|
||||
"revision": "c83117624b5b5d8a7f86093824e2f9c1ed309d63"
|
||||
}
|
||||
----
|
||||
|
||||
As response a link:#tag-info[TagInfo] entity is returned that describes
|
||||
the created tag.
|
||||
|
||||
.Response
|
||||
----
|
||||
HTTP/1.1 201 Created
|
||||
Content-Disposition: attachment
|
||||
Content-Type: application/json; charset=UTF-8
|
||||
|
||||
)]}'
|
||||
|
||||
"object": "d48d304adc4b6674e11dc2c018ea05fcbdda32fd",
|
||||
"message": "annotation",
|
||||
"tagger": {
|
||||
"name": "David Pursehouse",
|
||||
"email": "dpursehouse@collab.net",
|
||||
"date": "2016-06-06 01:22:03.000000000",
|
||||
"tz": 540
|
||||
},
|
||||
"ref": "refs/tags/v1.0",
|
||||
"revision": "c83117624b5b5d8a7f86093824e2f9c1ed309d63"
|
||||
}
|
||||
----
|
||||
|
||||
[[list-tags]]
|
||||
=== List Tags
|
||||
--
|
||||
@ -2615,6 +2666,22 @@ the signature.
|
||||
link:rest-api-changes.html#git-person-info[GitPersonInfo] entity.
|
||||
|=========================
|
||||
|
||||
[[tag-input]]
|
||||
=== TagInput
|
||||
|
||||
The `TagInput` entity contains information for creating a tag.
|
||||
|
||||
[options="header",cols="1,^2,4"]
|
||||
|=========================
|
||||
|Field Name ||Description
|
||||
|`ref` ||The name of the tag. The leading `refs/tags/` is optional.
|
||||
|`revision` |optional|The revision to which the tag should point. If not
|
||||
specified, the project's `HEAD` will be used.
|
||||
|`message` |optional|The tag message. When set, the tag will be created
|
||||
as an annotated tag.
|
||||
|=========================
|
||||
|
||||
|
||||
[[theme-info]]
|
||||
=== ThemeInfo
|
||||
The `ThemeInfo` entity describes a theme.
|
||||
|
@ -15,23 +15,25 @@
|
||||
package com.google.gerrit.acceptance.rest.project;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static com.google.gerrit.server.group.SystemGroupBackend.REGISTERED_USERS;
|
||||
import static org.eclipse.jgit.lib.Constants.R_TAGS;
|
||||
|
||||
import com.google.common.collect.FluentIterable;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.gerrit.acceptance.AbstractDaemonTest;
|
||||
import com.google.gerrit.acceptance.NoHttpd;
|
||||
import com.google.gerrit.acceptance.PushOneCommit;
|
||||
import com.google.gerrit.common.data.Permission;
|
||||
import com.google.gerrit.extensions.api.projects.ProjectApi.ListRefsRequest;
|
||||
import com.google.gerrit.extensions.api.projects.TagApi;
|
||||
import com.google.gerrit.extensions.api.projects.TagInfo;
|
||||
import com.google.gerrit.extensions.api.projects.TagInput;
|
||||
import com.google.gerrit.extensions.restapi.AuthException;
|
||||
import com.google.gerrit.extensions.restapi.BadRequestException;
|
||||
import com.google.gerrit.extensions.restapi.MethodNotAllowedException;
|
||||
import com.google.gerrit.extensions.restapi.ResourceConflictException;
|
||||
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
|
||||
|
||||
import org.eclipse.jgit.api.PushCommand;
|
||||
import org.eclipse.jgit.transport.PushResult;
|
||||
import org.eclipse.jgit.transport.RefSpec;
|
||||
import org.eclipse.jgit.transport.RemoteRefUpdate.Status;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.List;
|
||||
@ -41,6 +43,19 @@ public class TagsIT extends AbstractDaemonTest {
|
||||
private static final List<String> testTags = ImmutableList.of(
|
||||
"tag-A", "tag-B", "tag-C", "tag-D", "tag-E", "tag-F", "tag-G", "tag-H");
|
||||
|
||||
private static final String SIGNED_ANNOTATION = "annotation\n"
|
||||
+ "-----BEGIN PGP SIGNATURE-----\n"
|
||||
+ "Version: GnuPG v1\n"
|
||||
+ "\n"
|
||||
+ "iQEcBAABAgAGBQJVeGg5AAoJEPfTicJkUdPkUggH/RKAeI9/i/LduuiqrL/SSdIa\n"
|
||||
+ "9tYaSqJKLbXz63M/AW4Sp+4u+dVCQvnAt/a35CVEnpZz6hN4Kn/tiswOWVJf4CO7\n"
|
||||
+ "htNubGs5ZMwvD6sLYqKAnrM3WxV/2TbbjzjZW6Jkidz3jz/WRT4SmjGYiEO7aA+V\n"
|
||||
+ "4ZdIS9f7sW5VsHHYlNThCA7vH8Uu48bUovFXyQlPTX0pToSgrWV3JnTxDNxfn3iG\n"
|
||||
+ "IL0zTY/qwVCdXgFownLcs6J050xrrBWIKqfcWr3u4D2aCLyR0v+S/KArr7ulZygY\n"
|
||||
+ "+SOklImn8TAZiNxhWtA6ens66IiammUkZYFv7SSzoPLFZT4dC84SmGPWgf94NoQ=\n"
|
||||
+ "=XFeC\n"
|
||||
+ "-----END PGP SIGNATURE-----";
|
||||
|
||||
@Test
|
||||
public void listTagsOfNonExistingProject() throws Exception {
|
||||
exception.expect(ResourceNotFoundException.class);
|
||||
@ -105,83 +120,198 @@ public class TagsIT extends AbstractDaemonTest {
|
||||
@Test
|
||||
public void listTagsOfNonVisibleBranch() throws Exception {
|
||||
grantTagPermissions();
|
||||
grant(Permission.SUBMIT, project, "refs/for/refs/heads/hidden");
|
||||
|
||||
PushOneCommit.Tag tag1 = new PushOneCommit.Tag("v1.0");
|
||||
PushOneCommit push1 = pushFactory.create(db, admin.getIdent(), testRepo);
|
||||
push1.setTag(tag1);
|
||||
PushOneCommit.Result r1 = push1.to("refs/for/master%submit");
|
||||
PushOneCommit.Result r1 = push1.to("refs/heads/master");
|
||||
r1.assertOkStatus();
|
||||
TagInput tag1 = new TagInput();
|
||||
tag1.ref = "v1.0";
|
||||
tag1.revision = r1.getCommit().getName();
|
||||
TagInfo result = tag(tag1.ref).create(tag1).get();
|
||||
assertThat(result.ref).isEqualTo(R_TAGS + tag1.ref);
|
||||
assertThat(result.revision).isEqualTo(tag1.revision);
|
||||
|
||||
pushTo("refs/heads/hidden");
|
||||
PushOneCommit.Tag tag2 = new PushOneCommit.Tag("v2.0");
|
||||
PushOneCommit push2 = pushFactory.create(db, admin.getIdent(), testRepo);
|
||||
push2.setTag(tag2);
|
||||
PushOneCommit.Result r2 = push2.to("refs/for/hidden%submit");
|
||||
PushOneCommit.Result r2 = push2.to("refs/heads/hidden");
|
||||
r2.assertOkStatus();
|
||||
|
||||
List<TagInfo> result = getTags().get();
|
||||
assertThat(result).hasSize(2);
|
||||
assertThat(result.get(0).ref).isEqualTo(R_TAGS + tag1.name);
|
||||
assertThat(result.get(0).revision).isEqualTo(r1.getCommit().getName());
|
||||
assertThat(result.get(1).ref).isEqualTo(R_TAGS + tag2.name);
|
||||
assertThat(result.get(1).revision).isEqualTo(r2.getCommit().getName());
|
||||
TagInput tag2 = new TagInput();
|
||||
tag2.ref = "v2.0";
|
||||
tag2.revision = r2.getCommit().getName();
|
||||
result = tag(tag2.ref).create(tag2).get();
|
||||
assertThat(result.ref).isEqualTo(R_TAGS + tag2.ref);
|
||||
assertThat(result.revision).isEqualTo(tag2.revision);
|
||||
|
||||
List<TagInfo> tags = getTags().get();
|
||||
assertThat(tags).hasSize(2);
|
||||
assertThat(tags.get(0).ref).isEqualTo(R_TAGS + tag1.ref);
|
||||
assertThat(tags.get(0).revision).isEqualTo(tag1.revision);
|
||||
assertThat(tags.get(1).ref).isEqualTo(R_TAGS + tag2.ref);
|
||||
assertThat(tags.get(1).revision).isEqualTo(tag2.revision);
|
||||
|
||||
blockRead("refs/heads/hidden");
|
||||
result = getTags().get();
|
||||
assertThat(result).hasSize(1);
|
||||
assertThat(result.get(0).ref).isEqualTo(R_TAGS + tag1.name);
|
||||
assertThat(result.get(0).revision).isEqualTo(r1.getCommit().getName());
|
||||
tags = getTags().get();
|
||||
assertThat(tags).hasSize(1);
|
||||
assertThat(tags.get(0).ref).isEqualTo(R_TAGS + tag1.ref);
|
||||
assertThat(tags.get(0).revision).isEqualTo(tag1.revision);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void lightweightTag() throws Exception {
|
||||
grantTagPermissions();
|
||||
|
||||
PushOneCommit.Tag tag = new PushOneCommit.Tag("v1.0");
|
||||
PushOneCommit push = pushFactory.create(db, admin.getIdent(), testRepo);
|
||||
push.setTag(tag);
|
||||
PushOneCommit.Result r = push.to("refs/for/master%submit");
|
||||
PushOneCommit.Result r = push.to("refs/heads/master");
|
||||
r.assertOkStatus();
|
||||
|
||||
TagInfo tagInfo = getTag(tag.name);
|
||||
assertThat(tagInfo.ref).isEqualTo(R_TAGS + tag.name);
|
||||
assertThat(tagInfo.revision).isEqualTo(r.getCommit().getName());
|
||||
TagInput input = new TagInput();
|
||||
input.ref = "v1.0";
|
||||
input.revision = r.getCommit().getName();
|
||||
|
||||
TagInfo result = tag(input.ref).create(input).get();
|
||||
assertThat(result.ref).isEqualTo(R_TAGS + input.ref);
|
||||
assertThat(result.revision).isEqualTo(input.revision);
|
||||
|
||||
input.ref = "refs/tags/v2.0";
|
||||
result = tag(input.ref).create(input).get();
|
||||
assertThat(result.ref).isEqualTo(input.ref);
|
||||
assertThat(result.revision).isEqualTo(input.revision);
|
||||
|
||||
eventRecorder.assertRefUpdatedEvents(project.get(), result.ref,
|
||||
null, result.revision);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void annotatedTag() throws Exception {
|
||||
grantTagPermissions();
|
||||
|
||||
PushOneCommit.AnnotatedTag tag =
|
||||
new PushOneCommit.AnnotatedTag("v2.0", "annotation", admin.getIdent());
|
||||
PushOneCommit push = pushFactory.create(db, admin.getIdent(), testRepo);
|
||||
push.setTag(tag);
|
||||
PushOneCommit.Result r = push.to("refs/for/master%submit");
|
||||
PushOneCommit.Result r = push.to("refs/heads/master");
|
||||
r.assertOkStatus();
|
||||
|
||||
TagInfo tagInfo = getTag(tag.name);
|
||||
assertThat(tagInfo.ref).isEqualTo(R_TAGS + tag.name);
|
||||
assertThat(tagInfo.object).isEqualTo(r.getCommit().getName());
|
||||
assertThat(tagInfo.message).isEqualTo(tag.message);
|
||||
assertThat(tagInfo.tagger.name).isEqualTo(tag.tagger.getName());
|
||||
assertThat(tagInfo.tagger.email).isEqualTo(tag.tagger.getEmailAddress());
|
||||
TagInput input = new TagInput();
|
||||
input.ref = "v1.0";
|
||||
input.revision = r.getCommit().getName();
|
||||
input.message = "annotation message";
|
||||
|
||||
TagInfo result = tag(input.ref).create(input).get();
|
||||
assertThat(result.ref).isEqualTo(R_TAGS + input.ref);
|
||||
assertThat(result.object).isEqualTo(input.revision);
|
||||
assertThat(result.message).isEqualTo(input.message);
|
||||
assertThat(result.tagger.name).isEqualTo(admin.fullName);
|
||||
assertThat(result.tagger.email).isEqualTo(admin.email);
|
||||
|
||||
eventRecorder.assertRefUpdatedEvents(project.get(), result.ref,
|
||||
null, result.revision);
|
||||
|
||||
// A second tag pushed on the same ref should have the same ref
|
||||
String tag2ref = R_TAGS + "v2.0.1";
|
||||
PushCommand pushCmd = testRepo.git().push();
|
||||
pushCmd.setRefSpecs(new RefSpec(tag.name + ":" + tag2ref));
|
||||
Iterable<PushResult> result = pushCmd.call();
|
||||
assertThat(
|
||||
Iterables.getOnlyElement(result).getRemoteUpdate(tag2ref).getStatus())
|
||||
.isEqualTo(Status.OK);
|
||||
TagInput input2 = new TagInput();
|
||||
input2.ref = "refs/tags/v2.0";
|
||||
input2.revision = input.revision;
|
||||
input2.message = "second annotation message";
|
||||
TagInfo result2 = tag(input2.ref).create(input2).get();
|
||||
assertThat(result2.ref).isEqualTo(input2.ref);
|
||||
assertThat(result2.object).isEqualTo(input2.revision);
|
||||
assertThat(result2.message).isEqualTo(input2.message);
|
||||
assertThat(result2.tagger.name).isEqualTo(admin.fullName);
|
||||
assertThat(result2.tagger.email).isEqualTo(admin.email);
|
||||
|
||||
tagInfo = getTag(tag2ref);
|
||||
assertThat(tagInfo.ref).isEqualTo(tag2ref);
|
||||
assertThat(tagInfo.object).isEqualTo(r.getCommit().getName());
|
||||
assertThat(tagInfo.message).isEqualTo(tag.message);
|
||||
assertThat(tagInfo.tagger.name).isEqualTo(tag.tagger.getName());
|
||||
assertThat(tagInfo.tagger.email).isEqualTo(tag.tagger.getEmailAddress());
|
||||
eventRecorder.assertRefUpdatedEvents(project.get(), result2.ref,
|
||||
null, result2.revision);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createExistingTag() throws Exception {
|
||||
grantTagPermissions();
|
||||
|
||||
TagInput input = new TagInput();
|
||||
input.ref = "test";
|
||||
TagInfo result = tag(input.ref).create(input).get();
|
||||
assertThat(result.ref).isEqualTo(R_TAGS + "test");
|
||||
|
||||
input.ref = "refs/tags/test";
|
||||
exception.expect(ResourceConflictException.class);
|
||||
exception.expectMessage("tag \"" + R_TAGS + "test\" already exists");
|
||||
tag(input.ref).create(input);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createTagNotAllowed() throws Exception {
|
||||
TagInput input = new TagInput();
|
||||
input.ref = "test";
|
||||
exception.expect(AuthException.class);
|
||||
exception.expectMessage("Cannot create tag \"" + R_TAGS + "test\"");
|
||||
tag(input.ref).create(input);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createAnnotatedTagNotAllowed() throws Exception {
|
||||
block(Permission.PUSH_TAG, REGISTERED_USERS, R_TAGS + "*");
|
||||
TagInput input = new TagInput();
|
||||
input.ref = "test";
|
||||
input.message = "annotation";
|
||||
exception.expect(AuthException.class);
|
||||
exception.expectMessage(
|
||||
"Cannot create annotated tag \"" + R_TAGS + "test\"");
|
||||
tag(input.ref).create(input);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createSignedTagNotSupported() throws Exception {
|
||||
TagInput input = new TagInput();
|
||||
input.ref = "test";
|
||||
input.message = SIGNED_ANNOTATION;
|
||||
exception.expect(MethodNotAllowedException.class);
|
||||
exception.expectMessage("Cannot create signed tag \"" + R_TAGS + "test\"");
|
||||
tag(input.ref).create(input);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void mismatchedInput() throws Exception {
|
||||
TagInput input = new TagInput();
|
||||
input.ref = "test";
|
||||
|
||||
exception.expect(BadRequestException.class);
|
||||
exception.expectMessage("ref must match URL");
|
||||
tag("TEST").create(input);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void invalidTagName() throws Exception {
|
||||
grantTagPermissions();
|
||||
|
||||
TagInput input = new TagInput();
|
||||
input.ref = "refs/heads/test";
|
||||
|
||||
exception.expect(BadRequestException.class);
|
||||
exception.expectMessage("invalid tag name \"" + input.ref + "\"");
|
||||
tag(input.ref).create(input);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void invalidTagNameOnlySlashes() throws Exception {
|
||||
grantTagPermissions();
|
||||
|
||||
TagInput input = new TagInput();
|
||||
input.ref = "//";
|
||||
|
||||
exception.expect(BadRequestException.class);
|
||||
exception.expectMessage("invalid tag name \"refs/tags/\"");
|
||||
tag(input.ref).create(input);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void invalidBaseRevision() throws Exception {
|
||||
grantTagPermissions();
|
||||
|
||||
TagInput input = new TagInput();
|
||||
input.ref = "test";
|
||||
input.revision = "abcdefg";
|
||||
|
||||
exception.expect(BadRequestException.class);
|
||||
exception.expectMessage("Invalid base revision");
|
||||
tag(input.ref).create(input);
|
||||
}
|
||||
|
||||
private void assertTagList(FluentIterable<String> expected,
|
||||
@ -194,26 +324,29 @@ public class TagsIT extends AbstractDaemonTest {
|
||||
|
||||
private void createTags() throws Exception {
|
||||
grantTagPermissions();
|
||||
|
||||
String revision = pushTo("refs/heads/master").getCommit().name();
|
||||
TagInput input = new TagInput();
|
||||
input.revision = revision;
|
||||
|
||||
for (String tagname : testTags) {
|
||||
PushOneCommit.Tag tag = new PushOneCommit.Tag(tagname);
|
||||
PushOneCommit push = pushFactory.create(db, admin.getIdent(), testRepo);
|
||||
push.setTag(tag);
|
||||
PushOneCommit.Result result = push.to("refs/for/master%submit");
|
||||
result.assertOkStatus();
|
||||
TagInfo result = tag(tagname).create(input).get();
|
||||
assertThat(result.revision).isEqualTo(input.revision);
|
||||
assertThat(result.ref).isEqualTo(R_TAGS + tagname);
|
||||
}
|
||||
}
|
||||
|
||||
private void grantTagPermissions() throws Exception {
|
||||
grant(Permission.SUBMIT, project, "refs/for/refs/heads/master");
|
||||
grant(Permission.CREATE, project, R_TAGS + "*");
|
||||
grant(Permission.PUSH, project, R_TAGS + "*");
|
||||
grant(Permission.PUSH_TAG, project, R_TAGS + "*");
|
||||
grant(Permission.PUSH_SIGNED_TAG, project, R_TAGS + "*");
|
||||
}
|
||||
|
||||
private ListRefsRequest<TagInfo> getTags() throws Exception {
|
||||
return gApi.projects().name(project.get()).tags();
|
||||
}
|
||||
|
||||
private TagInfo getTag(String ref) throws Exception {
|
||||
return gApi.projects().name(project.get()).tag(ref).get();
|
||||
private TagApi tag(String tagname) throws Exception {
|
||||
return gApi.projects().name(project.get()).tag(tagname);
|
||||
}
|
||||
}
|
||||
|
@ -18,6 +18,8 @@ import com.google.gerrit.extensions.restapi.NotImplementedException;
|
||||
import com.google.gerrit.extensions.restapi.RestApiException;
|
||||
|
||||
public interface TagApi {
|
||||
TagApi create(TagInput input) throws RestApiException;
|
||||
|
||||
TagInfo get() throws RestApiException;
|
||||
|
||||
/**
|
||||
@ -25,6 +27,11 @@ public interface TagApi {
|
||||
* when adding new methods to the interface.
|
||||
**/
|
||||
class NotImplemented implements TagApi {
|
||||
@Override
|
||||
public TagApi create(TagInput input) throws RestApiException {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public TagInfo get() throws RestApiException {
|
||||
throw new NotImplementedException();
|
||||
|
@ -0,0 +1,24 @@
|
||||
// Copyright (C) 2016 The Android Open Source Project
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package com.google.gerrit.extensions.api.projects;
|
||||
|
||||
import com.google.gerrit.extensions.restapi.DefaultInput;
|
||||
|
||||
public class TagInput {
|
||||
@DefaultInput
|
||||
public String ref;
|
||||
public String revision;
|
||||
public String message;
|
||||
}
|
@ -16,8 +16,10 @@ package com.google.gerrit.server.api.projects;
|
||||
|
||||
import com.google.gerrit.extensions.api.projects.TagApi;
|
||||
import com.google.gerrit.extensions.api.projects.TagInfo;
|
||||
import com.google.gerrit.extensions.api.projects.TagInput;
|
||||
import com.google.gerrit.extensions.restapi.IdString;
|
||||
import com.google.gerrit.extensions.restapi.RestApiException;
|
||||
import com.google.gerrit.server.project.CreateTag;
|
||||
import com.google.gerrit.server.project.ListTags;
|
||||
import com.google.gerrit.server.project.ProjectResource;
|
||||
import com.google.inject.Inject;
|
||||
@ -31,18 +33,31 @@ public class TagApiImpl implements TagApi {
|
||||
}
|
||||
|
||||
private final ListTags listTags;
|
||||
private final CreateTag.Factory createTagFactory;
|
||||
private final String ref;
|
||||
private final ProjectResource project;
|
||||
|
||||
@Inject
|
||||
TagApiImpl(ListTags listTags,
|
||||
CreateTag.Factory createTagFactory,
|
||||
@Assisted ProjectResource project,
|
||||
@Assisted String ref) {
|
||||
this.listTags = listTags;
|
||||
this.createTagFactory = createTagFactory;
|
||||
this.project = project;
|
||||
this.ref = ref;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TagApi create(TagInput input) throws RestApiException {
|
||||
try {
|
||||
createTagFactory.create(ref).apply(project, input);
|
||||
return this;
|
||||
} catch (IOException e) {
|
||||
throw new RestApiException("Cannot create tag", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public TagInfo get() throws RestApiException {
|
||||
try {
|
||||
|
@ -0,0 +1,167 @@
|
||||
// Copyright (C) 2016 The Android Open Source Project
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package com.google.gerrit.server.project;
|
||||
|
||||
import static org.eclipse.jgit.lib.Constants.R_REFS;
|
||||
import static org.eclipse.jgit.lib.Constants.R_TAGS;
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.gerrit.common.ChangeHooks;
|
||||
import com.google.gerrit.common.TimeUtil;
|
||||
import com.google.gerrit.common.data.Permission;
|
||||
import com.google.gerrit.extensions.api.projects.TagInfo;
|
||||
import com.google.gerrit.extensions.api.projects.TagInput;
|
||||
import com.google.gerrit.extensions.restapi.AuthException;
|
||||
import com.google.gerrit.extensions.restapi.BadRequestException;
|
||||
import com.google.gerrit.extensions.restapi.MethodNotAllowedException;
|
||||
import com.google.gerrit.extensions.restapi.ResourceConflictException;
|
||||
import com.google.gerrit.extensions.restapi.RestApiException;
|
||||
import com.google.gerrit.extensions.restapi.RestModifyView;
|
||||
import com.google.gerrit.reviewdb.client.Branch;
|
||||
import com.google.gerrit.server.IdentifiedUser;
|
||||
import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
|
||||
import com.google.gerrit.server.git.GitRepositoryManager;
|
||||
import com.google.gerrit.server.git.TagCache;
|
||||
import com.google.gerrit.server.project.RefUtil.InvalidRevisionException;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Provider;
|
||||
import com.google.inject.assistedinject.Assisted;
|
||||
|
||||
import org.eclipse.jgit.api.Git;
|
||||
import org.eclipse.jgit.api.TagCommand;
|
||||
import org.eclipse.jgit.api.errors.GitAPIException;
|
||||
import org.eclipse.jgit.lib.Constants;
|
||||
import org.eclipse.jgit.lib.ObjectId;
|
||||
import org.eclipse.jgit.lib.Ref;
|
||||
import org.eclipse.jgit.lib.Repository;
|
||||
import org.eclipse.jgit.revwalk.RevObject;
|
||||
import org.eclipse.jgit.revwalk.RevWalk;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.TimeZone;
|
||||
|
||||
public class CreateTag implements RestModifyView<ProjectResource, TagInput> {
|
||||
private static final Logger log = LoggerFactory.getLogger(CreateTag.class);
|
||||
|
||||
public interface Factory {
|
||||
CreateTag create(String ref);
|
||||
}
|
||||
|
||||
private final Provider<IdentifiedUser> identifiedUser;
|
||||
private final GitRepositoryManager repoManager;
|
||||
private final TagCache tagCache;
|
||||
private final GitReferenceUpdated referenceUpdated;
|
||||
private final ChangeHooks hooks;
|
||||
private String ref;
|
||||
|
||||
@Inject
|
||||
CreateTag(Provider<IdentifiedUser> identifiedUser,
|
||||
GitRepositoryManager repoManager,
|
||||
TagCache tagCache,
|
||||
GitReferenceUpdated referenceUpdated,
|
||||
ChangeHooks hooks,
|
||||
@Assisted String ref) {
|
||||
this.identifiedUser = identifiedUser;
|
||||
this.repoManager = repoManager;
|
||||
this.tagCache = tagCache;
|
||||
this.referenceUpdated = referenceUpdated;
|
||||
this.hooks = hooks;
|
||||
this.ref = ref;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TagInfo apply(ProjectResource resource, TagInput input)
|
||||
throws RestApiException, IOException {
|
||||
if (input == null) {
|
||||
input = new TagInput();
|
||||
}
|
||||
if (input.ref != null && !ref.equals(input.ref)) {
|
||||
throw new BadRequestException("ref must match URL");
|
||||
}
|
||||
if (input.revision == null) {
|
||||
input.revision = Constants.HEAD;
|
||||
}
|
||||
while (ref.startsWith("/")) {
|
||||
ref = ref.substring(1);
|
||||
}
|
||||
if (ref.startsWith(R_REFS) && !ref.startsWith(R_TAGS)) {
|
||||
throw new BadRequestException("invalid tag name \"" + ref + "\"");
|
||||
}
|
||||
if (!ref.startsWith(R_TAGS)) {
|
||||
ref = R_TAGS + ref;
|
||||
}
|
||||
if (!Repository.isValidRefName(ref)) {
|
||||
throw new BadRequestException("invalid tag name \"" + ref + "\"");
|
||||
}
|
||||
|
||||
RefControl refControl = resource.getControl().controlForRef(ref);
|
||||
try (Repository repo = repoManager.openRepository(resource.getNameKey())) {
|
||||
ObjectId revid = RefUtil.parseBaseRevision(
|
||||
repo, resource.getNameKey(), input.revision);
|
||||
RevWalk rw = RefUtil.verifyConnected(repo, revid);
|
||||
RevObject object = rw.parseAny(revid);
|
||||
rw.reset();
|
||||
boolean isAnnotated = Strings.emptyToNull(input.message) != null;
|
||||
boolean isSigned = isAnnotated
|
||||
&& input.message.contains("-----BEGIN PGP SIGNATURE-----\n");
|
||||
if (isSigned) {
|
||||
throw new MethodNotAllowedException(
|
||||
"Cannot create signed tag \"" + ref + "\"");
|
||||
} else if (isAnnotated && !refControl.canPerform(Permission.PUSH_TAG)) {
|
||||
throw new AuthException("Cannot create annotated tag \"" + ref + "\"");
|
||||
} else if (!refControl.canPerform(Permission.CREATE)) {
|
||||
throw new AuthException("Cannot create tag \"" + ref + "\"");
|
||||
}
|
||||
if (repo.getRefDatabase().exactRef(ref) != null) {
|
||||
throw new ResourceConflictException(
|
||||
"tag \"" + ref + "\" already exists");
|
||||
}
|
||||
|
||||
try (Git git = new Git(repo)) {
|
||||
TagCommand tag = git.tag()
|
||||
.setObjectId(object)
|
||||
.setName(ref.substring(R_TAGS.length()))
|
||||
.setAnnotated(isAnnotated)
|
||||
.setSigned(isSigned);
|
||||
|
||||
if (isAnnotated) {
|
||||
tag.setMessage(input.message)
|
||||
.setTagger(identifiedUser.get()
|
||||
.newCommitterIdent(TimeUtil.nowTs(), TimeZone.getDefault()));
|
||||
}
|
||||
|
||||
Ref result = tag.call();
|
||||
tagCache.updateFastForward(resource.getNameKey(), ref,
|
||||
ObjectId.zeroId(), result.getObjectId());
|
||||
referenceUpdated.fire(resource.getNameKey(), ref,
|
||||
ObjectId.zeroId(), result.getObjectId(),
|
||||
identifiedUser.get().getAccount());
|
||||
hooks.doRefUpdatedHook(new Branch.NameKey(resource.getNameKey(), ref),
|
||||
ObjectId.zeroId(), result.getObjectId(),
|
||||
identifiedUser.get().getAccount());
|
||||
try (RevWalk w = new RevWalk(repo)) {
|
||||
return ListTags.createTagInfo(result, w);
|
||||
}
|
||||
}
|
||||
} catch (InvalidRevisionException e) {
|
||||
throw new BadRequestException("Invalid base revision");
|
||||
} catch (GitAPIException e) {
|
||||
log.error("Cannot create tag \"" + ref + "\"", e);
|
||||
throw new IOException(e);
|
||||
}
|
||||
}
|
||||
}
|
@ -141,22 +141,7 @@ public class ListTags implements RestReadView<ProjectResource> {
|
||||
throw new ResourceNotFoundException(id);
|
||||
}
|
||||
|
||||
private Repository getRepository(Project.NameKey project)
|
||||
throws ResourceNotFoundException, IOException {
|
||||
try {
|
||||
return repoManager.openRepository(project);
|
||||
} catch (RepositoryNotFoundException noGitRepository) {
|
||||
throw new ResourceNotFoundException();
|
||||
}
|
||||
}
|
||||
|
||||
private Map<String, Ref> visibleTags(ProjectControl control, Repository repo,
|
||||
Map<String, Ref> tags) {
|
||||
return new VisibleRefFilter(tagCache, changeNotesFactory, changeCache, repo,
|
||||
control, dbProvider.get(), false).filter(tags, true);
|
||||
}
|
||||
|
||||
private static TagInfo createTagInfo(Ref ref, RevWalk rw)
|
||||
public static TagInfo createTagInfo(Ref ref, RevWalk rw)
|
||||
throws MissingObjectException, IOException {
|
||||
RevObject object = rw.parseAny(ref.getObjectId());
|
||||
if (object instanceof RevTag) {
|
||||
@ -176,4 +161,19 @@ public class ListTags implements RestReadView<ProjectResource> {
|
||||
ref.getName(),
|
||||
ref.getObjectId().getName());
|
||||
}
|
||||
|
||||
private Repository getRepository(Project.NameKey project)
|
||||
throws ResourceNotFoundException, IOException {
|
||||
try {
|
||||
return repoManager.openRepository(project);
|
||||
} catch (RepositoryNotFoundException noGitRepository) {
|
||||
throw new ResourceNotFoundException();
|
||||
}
|
||||
}
|
||||
|
||||
private Map<String, Ref> visibleTags(ProjectControl control, Repository repo,
|
||||
Map<String, Ref> tags) {
|
||||
return new VisibleRefFilter(tagCache, changeNotesFactory, changeCache, repo,
|
||||
control, dbProvider.get(), false).filter(tags, true);
|
||||
}
|
||||
}
|
||||
|
@ -78,6 +78,8 @@ public class Module extends RestApiModule {
|
||||
|
||||
child(PROJECT_KIND, "tags").to(TagsCollection.class);
|
||||
get(TAG_KIND).to(GetTag.class);
|
||||
put(TAG_KIND).to(PutTag.class);
|
||||
factory(CreateTag.Factory.class);
|
||||
|
||||
child(PROJECT_KIND, "dashboards").to(DashboardsCollection.class);
|
||||
get(DASHBOARD_KIND).to(GetDashboard.class);
|
||||
|
@ -0,0 +1,29 @@
|
||||
// Copyright (C) 2016 The Android Open Source Project
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package com.google.gerrit.server.project;
|
||||
|
||||
import com.google.gerrit.extensions.api.projects.TagInput;
|
||||
import com.google.gerrit.extensions.restapi.ResourceConflictException;
|
||||
import com.google.gerrit.extensions.restapi.RestModifyView;
|
||||
|
||||
public class PutTag implements RestModifyView<TagResource, TagInput> {
|
||||
|
||||
@Override
|
||||
public Object apply(TagResource resource, TagInput input)
|
||||
throws ResourceConflictException {
|
||||
throw new ResourceConflictException("Tag \"" + resource.getTagInfo().ref
|
||||
+ "\" already exists");
|
||||
}
|
||||
}
|
@ -15,6 +15,7 @@
|
||||
package com.google.gerrit.server.project;
|
||||
|
||||
import com.google.gerrit.extensions.registration.DynamicMap;
|
||||
import com.google.gerrit.extensions.restapi.AcceptsCreate;
|
||||
import com.google.gerrit.extensions.restapi.ChildCollection;
|
||||
import com.google.gerrit.extensions.restapi.IdString;
|
||||
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
|
||||
@ -27,15 +28,19 @@ import java.io.IOException;
|
||||
|
||||
@Singleton
|
||||
public class TagsCollection implements
|
||||
ChildCollection<ProjectResource, TagResource> {
|
||||
ChildCollection<ProjectResource, TagResource>,
|
||||
AcceptsCreate<ProjectResource> {
|
||||
private final DynamicMap<RestView<TagResource>> views;
|
||||
private final Provider<ListTags> list;
|
||||
private final CreateTag.Factory createTagFactory;
|
||||
|
||||
@Inject
|
||||
public TagsCollection(DynamicMap<RestView<TagResource>> views,
|
||||
Provider<ListTags> list) {
|
||||
Provider<ListTags> list,
|
||||
CreateTag.Factory createTagFactory) {
|
||||
this.views = views;
|
||||
this.list = list;
|
||||
this.createTagFactory = createTagFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -53,4 +58,10 @@ public class TagsCollection implements
|
||||
public DynamicMap<RestView<TagResource>> views() {
|
||||
return views;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public CreateTag create(ProjectResource resource, IdString name) {
|
||||
return createTagFactory.create(name.get());
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user