Unverified Commit 0aac4109 authored by Greg Messner's avatar Greg Messner Committed by GitHub
Browse files

Add Releases API support. (#490)

parent 16187dba
......@@ -53,7 +53,7 @@ To utilize GitLab4J™ API in your Java project, simply add the following de
```java
dependencies {
...
compile group: 'org.gitlab4j', name: 'gitlab4j-api', version: '4.14.7'
compile group: 'org.gitlab4j', name: 'gitlab4j-api', version: '4.14.8'
}
```
......@@ -64,7 +64,7 @@ dependencies {
<dependency>
<groupId>org.gitlab4j</groupId>
<artifactId>gitlab4j-api</artifactId>
<version>4.14.7</version>
<version>4.14.8</version>
</dependency>
```
......@@ -284,6 +284,7 @@ The following is a list of the available sub APIs along with a sample use of eac
&nbsp;&nbsp;[PipelineApi](#pipelineapi)<br/>
&nbsp;&nbsp;[ProjectApi](#projectapi)<br/>
&nbsp;&nbsp;[ProtectedBranchesApi](#protectedbranchesapi)<br/>
&nbsp;&nbsp;[ReleasesApi](#releasesapi)<br/>
&nbsp;&nbsp;[RepositoryApi](#repositoryapi)<br/>
&nbsp;&nbsp;[RepositoryFileApi](#repositoryfileapi)<br/>
&nbsp;&nbsp;[RunnersApi](#runnersapi)<br/>
......@@ -489,6 +490,12 @@ Project newProject = gitLabApi.getProjectApi().createProject(projectSpec);
List<ProtectedBranch> branches = gitLabApi.getProtectedBranchesApi().getProtectedBranches(project.getId());
```
#### ReleasesApi
```java
// Get a list of releases for the specified project
List<Release> releases = gitLabApi.getReleasesApi().getReleases(projectId);
```
#### RepositoryApi
```java
// Get a list of repository branches from a project, sorted by name alphabetically
......
......@@ -460,6 +460,24 @@ public abstract class AbstractApi implements Constants {
}
}
/**
* Perform an HTTP PUT call with the specified payload object and path objects, returning
* a ClientResponse instance with the data returned from the endpoint.
*
* @param expectedStatus the HTTP status that should be returned from the server
* @param payload the object instance that will be serialized to JSON and used as the PUT data
* @param pathArgs variable list of arguments used to build the URI
* @return a ClientResponse instance with the data returned from the endpoint
* @throws GitLabApiException if any exception occurs during execution
*/
protected Response put(Response.Status expectedStatus, Object payload, Object... pathArgs) throws GitLabApiException {
try {
return validate(getApiClient().put(payload, pathArgs), expectedStatus);
} catch (Exception e) {
throw handle(e);
}
}
/**
* Perform an HTTP PUT call with the specified form data and path objects, returning
* a ClientResponse instance with the data returned from the endpoint.
......
......@@ -80,6 +80,7 @@ public class GitLabApi {
private PipelineApi pipelineApi;
private ProjectApi projectApi;
private ProtectedBranchesApi protectedBranchesApi;
private ReleasesApi releasesApi;
private RepositoryApi repositoryApi;
private RepositoryFileApi repositoryFileApi;
private RunnersApi runnersApi;
......@@ -1448,6 +1449,25 @@ public class GitLabApi {
return (this.protectedBranchesApi);
}
/**
* Gets the ReleasesApi instance owned by this GitLabApi instance. The ReleasesApi is used
* to perform all release related API calls.
*
* @return the ReleasesApi instance owned by this GitLabApi instance
*/
public ReleasesApi getReleasesApi() {
if (releasesApi == null) {
synchronized (this) {
if (releasesApi == null) {
releasesApi = new ReleasesApi(this);
}
}
}
return (releasesApi);
}
/**
* Gets the RepositoryApi instance owned by this GitLabApi instance. The RepositoryApi is used
* to perform all repository related API calls.
......
......@@ -694,6 +694,21 @@ public class GitLabApiClient {
return (invocation(url, null).put(Entity.entity(formData, MediaType.APPLICATION_FORM_URLENCODED_TYPE)));
}
/**
* Perform an HTTP PUT call with the specified payload object and URL, returning
* a ClientResponse instance with the data returned from the endpoint.
*
* @param payload the object instance that will be serialized to JSON and used as the PUT data
* @param pathArgs variable list of arguments used to build the URI
* @return a ClientResponse instance with the data returned from the endpoint
* @throws IOException if an error occurs while constructing the URL
*/
protected Response put(Object payload, Object... pathArgs) throws IOException {
URL url = getApiUrl(pathArgs);
Entity<?> entity = Entity.entity(payload, MediaType.APPLICATION_JSON);
return (invocation(url, null).put(entity));
}
/**
* Perform an HTTP DELETE call with the specified form data and path objects, returning
* a Response instance with the data returned from the endpoint.
......
package org.gitlab4j.api;
import java.util.List;
import java.util.Optional;
import java.util.stream.Stream;
import javax.ws.rs.core.Response;
import org.gitlab4j.api.models.Release;
import org.gitlab4j.api.models.ReleaseParams;
/**
* This class provides an entry point to all the GitLab Releases API calls.
* @see <a href="https://docs.gitlab.com/ce/api/releases">Releases API at GitLab</a>
*/
public class ReleasesApi extends AbstractApi {
public ReleasesApi(GitLabApi gitLabApi) {
super(gitLabApi);
}
/**
* Get a list of releases for a project, sorted by release date.
*
* <pre><code>GitLab Endpoint: GET /projects/:id/releases</code></pre>
*
* @param projectIdOrPath id, path of the project, or a Project instance holding the project ID or path
* @return the list of releases for the specified project
* @throws GitLabApiException if any exception occurs
*/
public List<Release> getReleases(Object projectIdOrPath) throws GitLabApiException {
return (getReleases(projectIdOrPath, getDefaultPerPage()).all());
}
/**
* Get a Pager of releases for a project, sorted by release date.
*
* <pre><code>GitLab Endpoint: GET /projects/:id/releases</code></pre>
*
* @param projectIdOrPath id, path of the project, or a Project instance holding the project ID or path
* @param itemsPerPage the number of Release instances that will be fetched per page
* @return the Pager of Release instances for the specified project ID
* @throws GitLabApiException if any exception occurs
*/
public Pager<Release> getReleases(Object projectIdOrPath, int itemsPerPage) throws GitLabApiException {
return (new Pager<Release>(this, Release.class, itemsPerPage, null, "projects", getProjectIdOrPath(projectIdOrPath), "releases"));
}
/**
* Get a Stream of releases for a project, sorted by release date.
*
* <pre><code>GitLab Endpoint: GET /projects/:id/releases</code></pre>
*
* @param projectIdOrPath id, path of the project, or a Project instance holding the project ID or path
* @return a Stream of Release instances for the specified project ID
* @throws GitLabApiException if any exception occurs
*/
public Stream<Release> getReleasesStream(Object projectIdOrPath) throws GitLabApiException {
return (getReleases(projectIdOrPath, getDefaultPerPage()).stream());
}
/**
* Get a Release for the given tag name.
*
* <pre><code>GitLab Endpoint: GET /projects/:id/releases/:tagName</code></pre>
*
* @param projectIdOrPath id, path of the project, or a Project instance holding the project ID or path
* @param tagName the name of the tag to fetch the Release for
* @return a Releases instance with info on the specified tag
* @throws GitLabApiException if any exception occurs
*/
public Release getRelease(Object projectIdOrPath, String tagName) throws GitLabApiException {
Response response = get(Response.Status.OK, null, "projects", getProjectIdOrPath(projectIdOrPath), "releases", urlEncode(tagName));
return (response.readEntity(Release.class));
}
/**
* Get an Optional instance holding a Release instance for the specific tag name.
*
* <pre><code>GitLab Endpoint: GET /projects/:id/releases/:tagName</code></pre>
*
* @param projectIdOrPath id, path of the project, or a Project instance holding the project ID or path
* @param tagName the name of the tag to fetch the Release for
* @return an Optional instance with the specified Release as the value
* @throws GitLabApiException if any exception occurs
*/
public Optional<Release> getOptionalRelease(Object projectIdOrPath, String tagName) throws GitLabApiException {
try {
return (Optional.ofNullable(getRelease(projectIdOrPath, tagName)));
} catch (GitLabApiException glae) {
return (GitLabApi.createOptionalFromException(glae));
}
}
/**
* Create a Release. You need push access to the repository to create a Release.
*
* <pre><code>GitLab Endpoint: POST /projects/:id/releases</code></pre>
*
* @param projectIdOrPath id, path of the project, or a Project instance holding the project ID or path
* @param params a ReleaseParams instance holding the parameters for the release
* @return a Release instance containing the newly created Release info
* @throws GitLabApiException if any exception occurs
*/
public Release createRelease(Object projectIdOrPath, ReleaseParams params) throws GitLabApiException {
Response response = post(Response.Status.CREATED, params,
"projects", getProjectIdOrPath(projectIdOrPath), "releases");
return (response.readEntity(Release.class));
}
/**
* Updates the release notes of a given release.
*
* <pre><code>GitLab Endpoint: PUT /projects/:id/releases/:tag_name</code></pre>
*
* @param projectIdOrPath id, path of the project, or a Project instance holding the project ID or path
* @param params a ReleaseParams instance holding the parameters for the release
* @return a Release instance containing info on the updated Release
* @throws GitLabApiException if any exception occurs
*/
public Release updateRelease(Object projectIdOrPath, ReleaseParams params) throws GitLabApiException {
String tagName = params.getTagName();
if (tagName == null || tagName.trim().isEmpty()) {
throw new RuntimeException("params.tagName cannot be null or empty");
}
Response response = put(Response.Status.OK, params,
"projects", getProjectIdOrPath(projectIdOrPath), "releases", tagName);
return (response.readEntity(Release.class));
}
/**
* Delete a Release. Deleting a Release will not delete the associated tag.
*
* <pre><code>GitLab Endpoint: DELETE /projects/:id/releases/:tag_name</code></pre>
*
* @param projectIdOrPath the project in the form of an Integer(ID), String(path), or Project instance
* @param tagName the tag name that the release was created from
* @throws GitLabApiException if any exception occurs
*/
public void deleteRelease(Object projectIdOrPath, String tagName) throws GitLabApiException {
delete(Response.Status.OK, null, "projects", getProjectIdOrPath(projectIdOrPath), "releases", tagName);
}
}
package org.gitlab4j.api.models;
import java.util.List;
import org.gitlab4j.api.Constants.ArchiveFormat;
import org.gitlab4j.api.utils.JacksonJson;
/**
* This class is part of the Release class model.
*/
public class Assets {
public static class Source {
private ArchiveFormat format;
private String url;
public ArchiveFormat getFormat() {
return format;
}
public void setFormat(ArchiveFormat format) {
this.format = format;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
@Override
public String toString() {
return (JacksonJson.toJsonString(this));
}
}
public static class Link {
private Integer id;
private String name;
private String url;
private Boolean external;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public Boolean getExternal() {
return external;
}
public void setExternal(Boolean external) {
this.external = external;
}
@Override
public String toString() {
return (JacksonJson.toJsonString(this));
}
}
private Integer count;
private List<Source> sources;
private List<Link> links;
private String evidenceFilePath;
public Integer getCount() {
return count;
}
public void setCount(Integer count) {
this.count = count;
}
public List<Source> getSources() {
return sources;
}
public void setSources(List<Source> sources) {
this.sources = sources;
}
public List<Link> getLinks() {
return links;
}
public void setLinks(List<Link> links) {
this.links = links;
}
public String getEvidenceFilePath() {
return evidenceFilePath;
}
public void setEvidenceFilePath(String evidenceFilePath) {
this.evidenceFilePath = evidenceFilePath;
}
@Override
public String toString() {
return (JacksonJson.toJsonString(this));
}
}
package org.gitlab4j.api.models;
import java.util.Date;
import java.util.List;
import org.gitlab4j.api.utils.JacksonJson;
public class Release {
private String name;
private String tagName;
private String description;
private String descriptionHtml;
private Date createdAt;
private Date releasedAt;
private Author author;
private Commit commit;
private List<Milestone> milestones;
private String commitPath;
private String tagPath;
private String evidenceSha;
private Assets assets;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getTagName() {
return tagName;
......@@ -23,6 +45,86 @@ public class Release {
this.description = description;
}
public String getDescriptionHtml() {
return descriptionHtml;
}
public void setDescriptionHtml(String descriptionHtml) {
this.descriptionHtml = descriptionHtml;
}
public Date getCreatedAt() {
return createdAt;
}
public void setCreatedAt(Date createdAt) {
this.createdAt = createdAt;
}
public Date getReleasedAt() {
return releasedAt;
}
public void setReleasedAt(Date releasedAt) {
this.releasedAt = releasedAt;
}
public Author getAuthor() {
return author;
}
public void setAuthor(Author author) {
this.author = author;
}
public Commit getCommit() {
return commit;
}
public void setCommit(Commit commit) {
this.commit = commit;
}
public List<Milestone> getMilestones() {
return milestones;
}
public void setMilestones(List<Milestone> milestones) {
this.milestones = milestones;
}
public String getCommitPath() {
return commitPath;
}
public void setCommitPath(String commitPath) {
this.commitPath = commitPath;
}
public String getTagPath() {
return tagPath;
}
public void setTagPath(String tagPath) {
this.tagPath = tagPath;
}
public String getEvidenceSha() {
return evidenceSha;
}
public void setEvidenceSha(String evidenceSha) {
this.evidenceSha = evidenceSha;
}
public Assets getAssets() {
return assets;
}
public void setAssets(Assets assets) {
this.assets = assets;
}
@Override
public String toString() {
return (JacksonJson.toJsonString(this));
......
package org.gitlab4j.api.models;
import java.util.Date;
import java.util.List;
import org.gitlab4j.api.utils.JacksonJson;
public class ReleaseParams {
private String name;
private String tagName;
private String description;
private String ref;
private List<Milestone> milestones;
private Assets assets;
private Date releasedAt;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public ReleaseParams withName(String name) {
this.name = name;
return (this);
}
public String getTagName() {
return tagName;
}
public void setTagName(String tagName) {
this.tagName = tagName;
}
public ReleaseParams withTagName(String tagName) {
this.tagName = tagName;
return (this);
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public ReleaseParams withDescription(String description) {
this.description = description;
return (this);
}
public List<Milestone> getMilestones() {
return milestones;
}
public void setMilestones(List<Milestone> milestones) {
this.milestones = milestones;
}
public ReleaseParams withMilestones(List<Milestone> milestones) {
this.milestones = milestones;
return (this);
}
public String getRef() {
return ref;
}
public void setRef(String ref) {
this.ref = ref;
}
public ReleaseParams withRef(String ref) {
this.ref = ref;
return (this);
}
public Assets getAssets() {
return assets;
}
public void setAssets(Assets assets) {
this.assets = assets;
}
public ReleaseParams withAssets(Assets assets) {
this.assets = assets;
return (this);
}
public Date getReleasedAt() {
return releasedAt;
}
public void setReleasedAt(Date releasedAt) {
this.releasedAt = releasedAt;
}
public ReleaseParams withReleasedAt(Date releasedAt) {
this.releasedAt = releasedAt;
return (this);
}
@Override
public String toString() {
return (JacksonJson.toJsonString(this));
}
}
......@@ -88,6 +88,7 @@ import org.gitlab4j.api.models.ProtectedBranch;
import org.gitlab4j.api.models.ProtectedTag;
import org.gitlab4j.api.models.PushRules;
import org.gitlab4j.api.models.RegistryRepository;
import org.gitlab4j.api.models.Release;
import org.gitlab4j.api.models.RepositoryFile;
import org.gitlab4j.api.models.Runner;
import org.gitlab4j.api.models.RunnerDetail;
......@@ -438,6 +439,12 @@ public class TestGitLabApiBeans {
assertTrue(compareJson(repos, "registry-repositories.json"));
}
@Test
public void testReleases() throws Exception {
List<Release> releases = unmarshalResourceList(Release.class, "releases.json");
assertTrue(compareJson(releases, "releases.json"));
}
@Test
public void testRepositoryFile() throws Exception {
RepositoryFile file = unmarshalResource(RepositoryFile.class, "repository-file.json");
......
package org.gitlab4j.api;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assume.assumeTrue;
import java.util.List;
import java.util.Optional;
import org.gitlab4j.api.models.Project;
import org.gitlab4j.api.models.Release;
import org.gitlab4j.api.models.ReleaseParams;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.experimental.categories.Category;
@Category(IntegrationTest.class)
public class TestReleasesApi extends AbstractIntegrationTest {
private static final String TEST_TAG_NAME = "test-release-tag";
private static final String TEST_RELEASE_NAME = "Test Release";
private static final String TEST_RELEASE_DESCRIPTION = "Test releases API release.";
private static final String TEST_UPDATED_RELEASE_DESCRIPTION = "UPDATED: Test releases API release.";
private static GitLabApi gitLabApi;
private static Project testProject;
public TestReleasesApi() {
super();
}
@BeforeClass
public static void testSetup() {
// Must setup the connection to the GitLab test server and get the test Project instance
gitLabApi = baseTestSetup();
testProject = getTestProject();
deleteTestResources();
}
@AfterClass
public static void tearDown() {
deleteTestResources();
}
private static final void deleteTestResources() {
if (testProject != null) {
try {
gitLabApi.getReleasesApi().deleteRelease(testProject, TEST_TAG_NAME);
} catch (Exception ignore) {}
try {
gitLabApi.getTagsApi().deleteTag(testProject, TEST_TAG_NAME);
} catch (Exception ignore) {}
}
}
@Before
public void beforeMethod() {
assumeTrue(testProject != null);
}
@Test
public void testCreateAndDeleteRelease() throws GitLabApiException {
ReleaseParams params = new ReleaseParams()
.withName(TEST_RELEASE_NAME)
.withTagName(TEST_TAG_NAME)
.withDescription(TEST_RELEASE_DESCRIPTION)
.withRef("master");
Release testRelease = gitLabApi.getReleasesApi().createRelease(testProject, params);
assertNotNull(testRelease);
assertEquals(TEST_RELEASE_NAME, testRelease.getName());
assertEquals(TEST_TAG_NAME, testRelease.getTagName());
List<Release> releases = gitLabApi.getReleasesApi().getReleases(testProject);
assertTrue(releases.stream().map(Release::getTagName).anyMatch(s -> TEST_TAG_NAME.equals(s)));
gitLabApi.getReleasesApi().deleteRelease(testProject, TEST_TAG_NAME);
Optional<Release> release = gitLabApi.getReleasesApi().getOptionalRelease(testProject, TEST_TAG_NAME);
assertFalse(release.isPresent());
releases = gitLabApi.getReleasesApi().getReleases(testProject);
assertFalse(releases.stream().map(Release::getTagName).anyMatch(s -> TEST_TAG_NAME.equals(s)));
}
@Test
public void testCreateAndUpdateRelease() throws GitLabApiException {
ReleaseParams params = new ReleaseParams()
.withName(TEST_RELEASE_NAME)
.withTagName(TEST_TAG_NAME)
.withDescription(TEST_RELEASE_DESCRIPTION)
.withRef("master");
Release testRelease = gitLabApi.getReleasesApi().createRelease(testProject, params);
assertNotNull(testRelease);
assertEquals(TEST_RELEASE_NAME, testRelease.getName());
assertEquals(TEST_TAG_NAME, testRelease.getTagName());
Optional<Release> release = gitLabApi.getReleasesApi().getOptionalRelease(testProject, TEST_TAG_NAME);
assertTrue(release.isPresent());
params = new ReleaseParams()
.withTagName(TEST_TAG_NAME)
.withDescription(TEST_UPDATED_RELEASE_DESCRIPTION);
testRelease = gitLabApi.getReleasesApi().updateRelease(testProject, params);
assertNotNull(testRelease);
assertEquals(TEST_RELEASE_NAME, testRelease.getName());
assertEquals(TEST_TAG_NAME, testRelease.getTagName());
assertEquals(TEST_UPDATED_RELEASE_DESCRIPTION, testRelease.getDescription());
release = gitLabApi.getReleasesApi().getOptionalRelease(testProject, TEST_TAG_NAME);
assertTrue(release.isPresent());
testRelease = release.get();
assertEquals(TEST_RELEASE_NAME, testRelease.getName());
assertEquals(TEST_TAG_NAME, testRelease.getTagName());
assertEquals(TEST_UPDATED_RELEASE_DESCRIPTION, testRelease.getDescription());
gitLabApi.getReleasesApi().deleteRelease(testProject, TEST_TAG_NAME);
release = gitLabApi.getReleasesApi().getOptionalRelease(testProject, TEST_TAG_NAME);
assertFalse(release.isPresent());
}
}
[
{
"tag_name":"v0.2",
"description":"## CHANGELOG\r\n\r\n- Escape label and milestone titles to prevent XSS in GFM autocomplete. !2740\r\n- Prevent private snippets from being embeddable.\r\n- Add subresources removal to member destroy service.",
"name":"Awesome app v0.2 beta",
"description_html":"\u003ch2 dir=\"auto\"\u003e\n\u003ca id=\"user-content-changelog\" class=\"anchor\" href=\"#changelog\" aria-hidden=\"true\"\u003e\u003c/a\u003eCHANGELOG\u003c/h2\u003e\n\u003cul dir=\"auto\"\u003e\n\u003cli\u003eEscape label and milestone titles to prevent XSS in GFM autocomplete. !2740\u003c/li\u003e\n\u003cli\u003ePrevent private snippets from being embeddable.\u003c/li\u003e\n\u003cli\u003eAdd subresources removal to member destroy service.\u003c/li\u003e\n\u003c/ul\u003e",
"created_at":"2019-01-03T01:56:19.539Z",
"released_at":"2019-01-03T01:56:19.539Z",
"author":{
"id":1,
"name":"Administrator",
"username":"root",
"state":"active",
"avatar_url":"https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80\u0026d=identicon",
"web_url":"https://gitlab.example.com/root"
},
"commit":{
"id":"079e90101242458910cccd35eab0e211dfc359c0",
"short_id":"079e9010",
"title":"Update README.md",
"created_at":"2019-01-03T01:55:38Z",
"parent_ids":[
"f8d3d94cbd347e924aa7b715845e439d00e80ca4"
],
"message":"Update README.md",
"author_name":"Administrator",
"author_email":"admin@example.com",
"authored_date":"2019-01-03T01:55:38Z",
"committer_name":"Administrator",
"committer_email":"admin@example.com",
"committed_date":"2019-01-03T01:55:38Z"
},
"milestones": [
{
"id":51,
"iid":1,
"project_id":24,
"title":"v1.0-rc",
"description":"Voluptate fugiat possimus quis quod aliquam expedita.",
"state":"closed",
"created_at":"2019-07-12T19:45:44.256Z",
"updated_at":"2019-07-12T19:45:44.256Z",
"due_date":"2019-08-16T11:00:00.256Z",
"start_date":"2019-07-30T12:00:00.256Z"
},
{
"id":52,
"iid":2,
"project_id":24,
"title":"v1.0",
"description":"Voluptate fugiat possimus quis quod aliquam expedita.",
"state":"closed",
"created_at":"2019-07-16T14:00:12.256Z",
"updated_at":"2019-07-16T14:00:12.256Z",
"due_date":"2019-08-16T11:00:00.256Z",
"start_date":"2019-07-30T12:00:00.256Z"
}
],
"commit_path":"/root/awesome-app/commit/588440f66559714280628a4f9799f0c4eb880a4a",
"tag_path":"/root/awesome-app/-/tags/v0.11.1",
"evidence_sha":"760d6cdfb0879c3ffedec13af470e0f71cf52c6cde4d",
"assets":{
"count":6,
"sources":[
{
"format":"zip",
"url":"https://gitlab.example.com/root/awesome-app/-/archive/v0.2/awesome-app-v0.2.zip"
},
{
"format":"tar.gz",
"url":"https://gitlab.example.com/root/awesome-app/-/archive/v0.2/awesome-app-v0.2.tar.gz"
},
{
"format":"tar.bz2",
"url":"https://gitlab.example.com/root/awesome-app/-/archive/v0.2/awesome-app-v0.2.tar.bz2"
},
{
"format":"tar",
"url":"https://gitlab.example.com/root/awesome-app/-/archive/v0.2/awesome-app-v0.2.tar"
}
],
"links":[
{
"id":2,
"name":"awesome-v0.2.msi",
"url":"http://192.168.10.15:3000/msi",
"external":true
},
{
"id":1,
"name":"awesome-v0.2.dmg",
"url":"http://192.168.10.15:3000",
"external":true
}
],
"evidence_file_path":"https://gitlab.example.com/root/awesome-app/-/releases/v0.2/evidence.json"
}
},
{
"tag_name":"v0.1",
"description":"## CHANGELOG\r\n\r\n-Remove limit of 100 when searching repository code. !8671\r\n- Show error message when attempting to reopen an MR and there is an open MR for the same branch. !16447 (Akos Gyimesi)\r\n- Fix a bug where internal email pattern wasn't respected. !22516",
"name":"Awesome app v0.1 alpha",
"description_html":"\u003ch2 dir=\"auto\"\u003e\n\u003ca id=\"user-content-changelog\" class=\"anchor\" href=\"#changelog\" aria-hidden=\"true\"\u003e\u003c/a\u003eCHANGELOG\u003c/h2\u003e\n\u003cul dir=\"auto\"\u003e\n\u003cli\u003eRemove limit of 100 when searching repository code. !8671\u003c/li\u003e\n\u003cli\u003eShow error message when attempting to reopen an MR and there is an open MR for the same branch. !16447 (Akos Gyimesi)\u003c/li\u003e\n\u003cli\u003eFix a bug where internal email pattern wasn't respected. !22516\u003c/li\u003e\n\u003c/ul\u003e",
"created_at":"2019-01-03T01:55:18.203Z",
"released_at":"2019-01-03T01:55:18.203Z",
"author":{
"id":1,
"name":"Administrator",
"username":"root",
"state":"active",
"avatar_url":"https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80\u0026d=identicon",
"web_url":"https://gitlab.example.com/root"
},
"commit":{
"id":"f8d3d94cbd347e924aa7b715845e439d00e80ca4",
"short_id":"f8d3d94c",
"title":"Initial commit",
"created_at":"2019-01-03T01:53:28Z",
"parent_ids":[
],
"message":"Initial commit",
"author_name":"Administrator",
"author_email":"admin@example.com",
"authored_date":"2019-01-03T01:53:28Z",
"committer_name":"Administrator",
"committer_email":"admin@example.com",
"committed_date":"2019-01-03T01:53:28Z"
},
"evidence_sha":"760d6cdfb0879c3ffedec13af470e0f71cf52c6cde4d",
"assets":{
"count":4,
"sources":[
{
"format":"zip",
"url":"https://gitlab.example.com/root/awesome-app/-/archive/v0.1/awesome-app-v0.1.zip"
},
{
"format":"tar.gz",
"url":"https://gitlab.example.com/root/awesome-app/-/archive/v0.1/awesome-app-v0.1.tar.gz"
},
{
"format":"tar.bz2",
"url":"https://gitlab.example.com/root/awesome-app/-/archive/v0.1/awesome-app-v0.1.tar.bz2"
},
{
"format":"tar",
"url":"https://gitlab.example.com/root/awesome-app/-/archive/v0.1/awesome-app-v0.1.tar"
}
],
"links":[
],
"evidence_file_path":"https://gitlab.example.com/root/awesome-app/-/releases/v0.1/evidence.json"
}
}
]
\ No newline at end of file
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment