diff --git a/models/attachment.go b/models/attachment.go
index acb1f0716c..ffe2eea80c 100644
--- a/models/attachment.go
+++ b/models/attachment.go
@@ -11,10 +11,12 @@ import (
 	"os"
 	"path"
 
-	gouuid "github.com/satori/go.uuid"
-
 	"code.gitea.io/gitea/modules/setting"
 	"code.gitea.io/gitea/modules/util"
+	api "code.gitea.io/sdk/gitea"
+
+	"github.com/go-xorm/xorm"
+	gouuid "github.com/satori/go.uuid"
 )
 
 // Attachment represent a attachment of issue/comment/release.
@@ -39,6 +41,20 @@ func (a *Attachment) IncreaseDownloadCount() error {
 	return nil
 }
 
+// APIFormat converts models.Attachment to api.Attachment
+func (a *Attachment) APIFormat() *api.Attachment {
+	size, _ := a.Size()
+	return &api.Attachment{
+		ID:            a.ID,
+		Name:          a.Name,
+		Created:       a.CreatedUnix.AsTime(),
+		DownloadCount: a.DownloadCount,
+		Size:          size,
+		UUID:          a.UUID,
+		DownloadURL:   a.DownloadURL(),
+	}
+}
+
 // AttachmentLocalPath returns where attachment is stored in local file
 // system based on given UUID.
 func AttachmentLocalPath(uuid string) string {
@@ -50,6 +66,20 @@ func (a *Attachment) LocalPath() string {
 	return AttachmentLocalPath(a.UUID)
 }
 
+// Size returns the file's size of the attachment
+func (a *Attachment) Size() (int64, error) {
+	fi, err := os.Stat(a.LocalPath())
+	if err != nil {
+		return 0, err
+	}
+	return fi.Size(), nil
+}
+
+// DownloadURL returns the download url of the attached file
+func (a *Attachment) DownloadURL() string {
+	return fmt.Sprintf("%sattachments/%s", setting.AppURL, a.UUID)
+}
+
 // NewAttachment creates a new attachment object.
 func NewAttachment(name string, buf []byte, file multipart.File) (_ *Attachment, err error) {
 	attach := &Attachment{
@@ -81,6 +111,22 @@ func NewAttachment(name string, buf []byte, file multipart.File) (_ *Attachment,
 	return attach, nil
 }
 
+// GetAttachmentByID returns attachment by given id
+func GetAttachmentByID(id int64) (*Attachment, error) {
+	return getAttachmentByID(x, id)
+}
+
+func getAttachmentByID(e Engine, id int64) (*Attachment, error) {
+	attach := &Attachment{ID: id}
+
+	if has, err := e.Get(attach); err != nil {
+		return nil, err
+	} else if !has {
+		return nil, ErrAttachmentNotExist{ID: id, UUID: ""}
+	}
+	return attach, nil
+}
+
 func getAttachmentByUUID(e Engine, uuid string) (*Attachment, error) {
 	attach := &Attachment{UUID: uuid}
 	has, err := e.Get(attach)
@@ -180,3 +226,20 @@ func DeleteAttachmentsByComment(commentID int64, remove bool) (int, error) {
 
 	return DeleteAttachments(attachments, remove)
 }
+
+// UpdateAttachment updates the given attachment in database
+func UpdateAttachment(atta *Attachment) error {
+	return updateAttachment(x, atta)
+}
+
+func updateAttachment(e Engine, atta *Attachment) error {
+	var sess *xorm.Session
+	if atta.ID != 0 && atta.UUID == "" {
+		sess = e.ID(atta.ID)
+	} else {
+		// Use uuid only if id is not set and uuid is set
+		sess = e.Where("uuid = ?", atta.UUID)
+	}
+	_, err := sess.Cols("name", "issue_id", "release_id", "comment_id", "download_count").Update(atta)
+	return err
+}
diff --git a/models/attachment_test.go b/models/attachment_test.go
index d568e39431..be4baf3055 100644
--- a/models/attachment_test.go
+++ b/models/attachment_test.go
@@ -58,3 +58,32 @@ func TestDeleteAttachments(t *testing.T) {
 	assert.True(t, IsErrAttachmentNotExist(err))
 	assert.Nil(t, attachment)
 }
+
+func TestGetAttachmentByID(t *testing.T) {
+	assert.NoError(t, PrepareTestDatabase())
+
+	attach, err := GetAttachmentByID(1)
+	assert.NoError(t, err)
+	assert.Equal(t, "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11", attach.UUID)
+}
+
+func TestAttachment_DownloadURL(t *testing.T) {
+	attach := &Attachment{
+		UUID: "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11",
+		ID:   1,
+	}
+	assert.Equal(t, "https://try.gitea.io/attachments/a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11", attach.DownloadURL())
+}
+
+func TestUpdateAttachment(t *testing.T) {
+	assert.NoError(t, PrepareTestDatabase())
+
+	attach, err := GetAttachmentByID(1)
+	assert.NoError(t, err)
+	assert.Equal(t, "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11", attach.UUID)
+
+	attach.Name = "new_name"
+	assert.NoError(t, UpdateAttachment(attach))
+
+	AssertExistsAndLoadBean(t, &Attachment{Name: "new_name"})
+}
diff --git a/models/release.go b/models/release.go
index 66202615d2..586f494e7d 100644
--- a/models/release.go
+++ b/models/release.go
@@ -53,7 +53,7 @@ func (r *Release) loadAttributes(e Engine) error {
 			return err
 		}
 	}
-	return nil
+	return GetReleaseAttachments(r)
 }
 
 // LoadAttributes load repo and publisher attributes for a release
@@ -79,6 +79,10 @@ func (r *Release) TarURL() string {
 
 // APIFormat convert a Release to api.Release
 func (r *Release) APIFormat() *api.Release {
+	assets := make([]*api.Attachment, 0)
+	for _, att := range r.Attachments {
+		assets = append(assets, att.APIFormat())
+	}
 	return &api.Release{
 		ID:           r.ID,
 		TagName:      r.TagName,
@@ -92,6 +96,7 @@ func (r *Release) APIFormat() *api.Release {
 		CreatedAt:    r.CreatedUnix.AsTime(),
 		PublishedAt:  r.CreatedUnix.AsTime(),
 		Publisher:    r.Publisher.APIFormat(),
+		Attachments:  assets,
 	}
 }
 
diff --git a/package.json b/package.json
index 8251e50264..3410bb1f35 100644
--- a/package.json
+++ b/package.json
@@ -4,4 +4,4 @@
         "less": "^2.7.2",
         "less-plugin-clean-css": "^1.5.1"
     }
-}
\ No newline at end of file
+}
diff --git a/public/swagger.v1.json b/public/swagger.v1.json
index cc918e015a..ad1b91ad81 100644
--- a/public/swagger.v1.json
+++ b/public/swagger.v1.json
@@ -3225,6 +3225,37 @@
     },
     "/repos/{owner}/{repo}/releases": {
       "get": {
+        "produces": [
+          "application/json"
+        ],
+        "tags": [
+          "repository"
+        ],
+        "summary": "List a repo's releases",
+        "operationId": "repoListReleases",
+        "parameters": [
+          {
+            "type": "string",
+            "description": "owner of the repo",
+            "name": "owner",
+            "in": "path",
+            "required": true
+          },
+          {
+            "type": "string",
+            "description": "name of the repo",
+            "name": "repo",
+            "in": "path",
+            "required": true
+          }
+        ],
+        "responses": {
+          "200": {
+            "$ref": "#/responses/ReleaseList"
+          }
+        }
+      },
+      "post": {
         "consumes": [
           "application/json"
         ],
@@ -3267,6 +3298,44 @@
       }
     },
     "/repos/{owner}/{repo}/releases/{id}": {
+      "get": {
+        "produces": [
+          "application/json"
+        ],
+        "tags": [
+          "repository"
+        ],
+        "summary": "Get a release",
+        "operationId": "repoGetRelease",
+        "parameters": [
+          {
+            "type": "string",
+            "description": "owner of the repo",
+            "name": "owner",
+            "in": "path",
+            "required": true
+          },
+          {
+            "type": "string",
+            "description": "name of the repo",
+            "name": "repo",
+            "in": "path",
+            "required": true
+          },
+          {
+            "type": "integer",
+            "description": "id of the release to get",
+            "name": "id",
+            "in": "path",
+            "required": true
+          }
+        ],
+        "responses": {
+          "200": {
+            "$ref": "#/responses/Release"
+          }
+        }
+      },
       "delete": {
         "tags": [
           "repository"
@@ -3351,6 +3420,247 @@
         }
       }
     },
+    "/repos/{owner}/{repo}/releases/{id}/assets": {
+      "get": {
+        "produces": [
+          "application/json"
+        ],
+        "tags": [
+          "repository"
+        ],
+        "summary": "List release's attachments",
+        "operationId": "repoListReleaseAttachments",
+        "parameters": [
+          {
+            "type": "string",
+            "description": "owner of the repo",
+            "name": "owner",
+            "in": "path",
+            "required": true
+          },
+          {
+            "type": "string",
+            "description": "name of the repo",
+            "name": "repo",
+            "in": "path",
+            "required": true
+          },
+          {
+            "type": "integer",
+            "description": "id of the release",
+            "name": "id",
+            "in": "path",
+            "required": true
+          }
+        ],
+        "responses": {
+          "200": {
+            "$ref": "#/responses/AttachmentList"
+          }
+        }
+      },
+      "post": {
+        "consumes": [
+          "multipart/form-data"
+        ],
+        "produces": [
+          "application/json"
+        ],
+        "tags": [
+          "repository"
+        ],
+        "summary": "Create a release attachment",
+        "operationId": "repoCreateReleaseAttachment",
+        "parameters": [
+          {
+            "type": "string",
+            "description": "owner of the repo",
+            "name": "owner",
+            "in": "path",
+            "required": true
+          },
+          {
+            "type": "string",
+            "description": "name of the repo",
+            "name": "repo",
+            "in": "path",
+            "required": true
+          },
+          {
+            "type": "integer",
+            "description": "id of the release",
+            "name": "id",
+            "in": "path",
+            "required": true
+          },
+          {
+            "type": "string",
+            "description": "name of the attachment",
+            "name": "name",
+            "in": "query"
+          },
+          {
+            "type": "file",
+            "description": "attachment to upload",
+            "name": "attachment",
+            "in": "formData",
+            "required": true
+          }
+        ],
+        "responses": {
+          "201": {
+            "$ref": "#/responses/Attachment"
+          }
+        }
+      }
+    },
+    "/repos/{owner}/{repo}/releases/{id}/assets/{attachment_id}": {
+      "get": {
+        "produces": [
+          "application/json"
+        ],
+        "tags": [
+          "repository"
+        ],
+        "summary": "Get a release attachment",
+        "operationId": "repoGetReleaseAttachment",
+        "parameters": [
+          {
+            "type": "string",
+            "description": "owner of the repo",
+            "name": "owner",
+            "in": "path",
+            "required": true
+          },
+          {
+            "type": "string",
+            "description": "name of the repo",
+            "name": "repo",
+            "in": "path",
+            "required": true
+          },
+          {
+            "type": "integer",
+            "description": "id of the release",
+            "name": "id",
+            "in": "path",
+            "required": true
+          },
+          {
+            "type": "integer",
+            "description": "id of the attachment to get",
+            "name": "attachment_id",
+            "in": "path",
+            "required": true
+          }
+        ],
+        "responses": {
+          "200": {
+            "$ref": "#/responses/Attachment"
+          }
+        }
+      },
+      "delete": {
+        "produces": [
+          "application/json"
+        ],
+        "tags": [
+          "repository"
+        ],
+        "summary": "Delete a release attachment",
+        "operationId": "repoDeleteReleaseAttachment",
+        "parameters": [
+          {
+            "type": "string",
+            "description": "owner of the repo",
+            "name": "owner",
+            "in": "path",
+            "required": true
+          },
+          {
+            "type": "string",
+            "description": "name of the repo",
+            "name": "repo",
+            "in": "path",
+            "required": true
+          },
+          {
+            "type": "integer",
+            "description": "id of the release",
+            "name": "id",
+            "in": "path",
+            "required": true
+          },
+          {
+            "type": "integer",
+            "description": "id of the attachment to delete",
+            "name": "attachment_id",
+            "in": "path",
+            "required": true
+          }
+        ],
+        "responses": {
+          "204": {
+            "$ref": "#/responses/empty"
+          }
+        }
+      },
+      "patch": {
+        "consumes": [
+          "application/json"
+        ],
+        "produces": [
+          "application/json"
+        ],
+        "tags": [
+          "repository"
+        ],
+        "summary": "Edit a release attachment",
+        "operationId": "repoEditReleaseAttachment",
+        "parameters": [
+          {
+            "type": "string",
+            "description": "owner of the repo",
+            "name": "owner",
+            "in": "path",
+            "required": true
+          },
+          {
+            "type": "string",
+            "description": "name of the repo",
+            "name": "repo",
+            "in": "path",
+            "required": true
+          },
+          {
+            "type": "integer",
+            "description": "id of the release",
+            "name": "id",
+            "in": "path",
+            "required": true
+          },
+          {
+            "type": "integer",
+            "description": "id of the attachment to edit",
+            "name": "attachment_id",
+            "in": "path",
+            "required": true
+          },
+          {
+            "name": "body",
+            "in": "body",
+            "schema": {
+              "$ref": "#/definitions/EditAttachmentOptions"
+            }
+          }
+        ],
+        "responses": {
+          "201": {
+            "$ref": "#/responses/Attachment"
+          }
+        }
+      }
+    },
     "/repos/{owner}/{repo}/stargazers": {
       "get": {
         "produces": [
@@ -4994,6 +5304,45 @@
       },
       "x-go-package": "code.gitea.io/gitea/vendor/code.gitea.io/sdk/gitea"
     },
+    "Attachment": {
+      "description": "Attachment a generic attachment",
+      "type": "object",
+      "properties": {
+        "browser_download_url": {
+          "type": "string",
+          "x-go-name": "DownloadURL"
+        },
+        "created_at": {
+          "type": "string",
+          "format": "date-time",
+          "x-go-name": "Created"
+        },
+        "download_count": {
+          "type": "integer",
+          "format": "int64",
+          "x-go-name": "DownloadCount"
+        },
+        "id": {
+          "type": "integer",
+          "format": "int64",
+          "x-go-name": "ID"
+        },
+        "name": {
+          "type": "string",
+          "x-go-name": "Name"
+        },
+        "size": {
+          "type": "integer",
+          "format": "int64",
+          "x-go-name": "Size"
+        },
+        "uuid": {
+          "type": "string",
+          "x-go-name": "UUID"
+        }
+      },
+      "x-go-package": "code.gitea.io/gitea/vendor/code.gitea.io/sdk/gitea"
+    },
     "Branch": {
       "description": "Branch represents a repository branch",
       "type": "object",
@@ -5202,6 +5551,11 @@
           "uniqueItems": true,
           "x-go-name": "Key"
         },
+        "read_only": {
+          "description": "Describe if the key has only read access or read/write",
+          "type": "boolean",
+          "x-go-name": "ReadOnly"
+        },
         "title": {
           "description": "Title of the key to add",
           "type": "string",
@@ -5540,6 +5894,17 @@
       },
       "x-go-package": "code.gitea.io/gitea/vendor/code.gitea.io/sdk/gitea"
     },
+    "EditAttachmentOptions": {
+      "description": "EditAttachmentOptions options for editing attachments",
+      "type": "object",
+      "properties": {
+        "name": {
+          "type": "string",
+          "x-go-name": "Name"
+        }
+      },
+      "x-go-package": "code.gitea.io/gitea/vendor/code.gitea.io/sdk/gitea"
+    },
     "EditHookOption": {
       "description": "EditHookOption options when modify one hook",
       "type": "object",
@@ -6459,6 +6824,13 @@
       "description": "Release represents a repository release",
       "type": "object",
       "properties": {
+        "assets": {
+          "type": "array",
+          "items": {
+            "$ref": "#/definitions/Attachment"
+          },
+          "x-go-name": "Attachments"
+        },
         "author": {
           "$ref": "#/definitions/User"
         },
@@ -6848,6 +7220,19 @@
     "AccessTokenList": {
       "description": "AccessTokenList represents a list of API access token."
     },
+    "Attachment": {
+      "schema": {
+        "$ref": "#/definitions/Attachment"
+      }
+    },
+    "AttachmentList": {
+      "schema": {
+        "type": "array",
+        "items": {
+          "$ref": "#/definitions/Attachment"
+        }
+      }
+    },
     "Branch": {
       "schema": {
         "$ref": "#/definitions/Branch"
@@ -7131,7 +7516,7 @@
     },
     "parameterBodies": {
       "schema": {
-        "$ref": "#/definitions/MigrateRepoForm"
+        "$ref": "#/definitions/EditAttachmentOptions"
       },
       "headers": {
         "AddCollaboratorOption": {},
@@ -7152,6 +7537,7 @@
         "CreateTeamOption": {},
         "CreateUserOption": {},
         "DeleteEmailOption": {},
+        "EditAttachmentOptions": {},
         "EditHookOption": {},
         "EditIssueCommentOption": {},
         "EditIssueOption": {},
diff --git a/routers/api/v1/api.go b/routers/api/v1/api.go
index 240bb2fc4d..4c454cb6a0 100644
--- a/routers/api/v1/api.go
+++ b/routers/api/v1/api.go
@@ -469,9 +469,18 @@ func RegisterRoutes(m *macaron.Macaron) {
 				m.Group("/releases", func() {
 					m.Combo("").Get(repo.ListReleases).
 						Post(reqToken(), reqRepoWriter(), context.ReferencesGitRepo(), bind(api.CreateReleaseOption{}), repo.CreateRelease)
-					m.Combo("/:id").Get(repo.GetRelease).
-						Patch(reqToken(), reqRepoWriter(), context.ReferencesGitRepo(), bind(api.EditReleaseOption{}), repo.EditRelease).
-						Delete(reqToken(), reqRepoWriter(), repo.DeleteRelease)
+					m.Group("/:id", func() {
+						m.Combo("").Get(repo.GetRelease).
+							Patch(reqToken(), reqRepoWriter(), context.ReferencesGitRepo(), bind(api.EditReleaseOption{}), repo.EditRelease).
+							Delete(reqToken(), reqRepoWriter(), repo.DeleteRelease)
+						m.Group("/assets", func() {
+							m.Combo("").Get(repo.ListReleaseAttachments).
+								Post(reqToken(), reqRepoWriter(), repo.CreateReleaseAttachment)
+							m.Combo("/:asset").Get(repo.GetReleaseAttachment).
+								Patch(reqToken(), reqRepoWriter(), bind(api.EditAttachmentOptions{}), repo.EditReleaseAttachment).
+								Delete(reqToken(), reqRepoWriter(), repo.DeleteReleaseAttachment)
+						})
+					})
 				})
 				m.Post("/mirror-sync", reqToken(), reqRepoWriter(), repo.MirrorSync)
 				m.Get("/editorconfig/:filename", context.RepoRef(), repo.GetEditorconfig)
diff --git a/routers/api/v1/repo/release.go b/routers/api/v1/repo/release.go
index 41efa482dd..629fb2ea57 100644
--- a/routers/api/v1/repo/release.go
+++ b/routers/api/v1/repo/release.go
@@ -13,7 +13,7 @@ import (
 
 // GetRelease get a single release of a repository
 func GetRelease(ctx *context.APIContext) {
-	// swagger:operation GET /repos/{owner}/{repo}/releases repository repoGetRelease
+	// swagger:operation GET /repos/{owner}/{repo}/releases/{id} repository repoGetRelease
 	// ---
 	// summary: Get a release
 	// produces:
@@ -29,7 +29,7 @@ func GetRelease(ctx *context.APIContext) {
 	//   description: name of the repo
 	//   type: string
 	//   required: true
-	// - name: repo
+	// - name: id
 	//   in: path
 	//   description: id of the release to get
 	//   type: integer
diff --git a/routers/api/v1/repo/release_attachment.go b/routers/api/v1/repo/release_attachment.go
new file mode 100644
index 0000000000..80b2064df0
--- /dev/null
+++ b/routers/api/v1/repo/release_attachment.go
@@ -0,0 +1,322 @@
+// Copyright 2018 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package repo
+
+import (
+	"code.gitea.io/gitea/models"
+	"code.gitea.io/gitea/modules/context"
+	"code.gitea.io/gitea/modules/setting"
+	api "code.gitea.io/sdk/gitea"
+	"errors"
+	"net/http"
+	"strings"
+)
+
+// GetReleaseAttachment gets a single attachment of the release
+func GetReleaseAttachment(ctx *context.APIContext) {
+	// swagger:operation GET /repos/{owner}/{repo}/releases/{id}/assets/{attachment_id} repository repoGetReleaseAttachment
+	// ---
+	// summary: Get a release attachment
+	// produces:
+	// - application/json
+	// parameters:
+	// - name: owner
+	//   in: path
+	//   description: owner of the repo
+	//   type: string
+	//   required: true
+	// - name: repo
+	//   in: path
+	//   description: name of the repo
+	//   type: string
+	//   required: true
+	// - name: id
+	//   in: path
+	//   description: id of the release
+	//   type: integer
+	//   required: true
+	// - name: attachment_id
+	//   in: path
+	//   description: id of the attachment to get
+	//   type: integer
+	//   required: true
+	// responses:
+	//   "200":
+	//     "$ref": "#/responses/Attachment"
+	releaseID := ctx.ParamsInt64(":id")
+	attachID := ctx.ParamsInt64(":asset")
+	attach, err := models.GetAttachmentByID(attachID)
+	if err != nil {
+		ctx.Error(500, "GetAttachmentByID", err)
+		return
+	}
+	if attach.ReleaseID != releaseID {
+		ctx.Status(404)
+		return
+	}
+	// FIXME Should prove the existence of the given repo, but results in unnecessary database requests
+	ctx.JSON(200, attach.APIFormat())
+}
+
+// ListReleaseAttachments lists all attachments of the release
+func ListReleaseAttachments(ctx *context.APIContext) {
+	// swagger:operation GET /repos/{owner}/{repo}/releases/{id}/assets repository repoListReleaseAttachments
+	// ---
+	// summary: List release's attachments
+	// produces:
+	// - application/json
+	// parameters:
+	// - name: owner
+	//   in: path
+	//   description: owner of the repo
+	//   type: string
+	//   required: true
+	// - name: repo
+	//   in: path
+	//   description: name of the repo
+	//   type: string
+	//   required: true
+	// - name: id
+	//   in: path
+	//   description: id of the release
+	//   type: integer
+	//   required: true
+	// responses:
+	//   "200":
+	//     "$ref": "#/responses/AttachmentList"
+	releaseID := ctx.ParamsInt64(":id")
+	release, err := models.GetReleaseByID(releaseID)
+	if err != nil {
+		ctx.Error(500, "GetReleaseByID", err)
+		return
+	}
+	if release.RepoID != ctx.Repo.Repository.ID {
+		ctx.Status(404)
+		return
+	}
+	if err := release.LoadAttributes(); err != nil {
+		ctx.Error(500, "LoadAttributes", err)
+		return
+	}
+	ctx.JSON(200, release.APIFormat().Attachments)
+}
+
+// CreateReleaseAttachment creates an attachment and saves the given file
+func CreateReleaseAttachment(ctx *context.APIContext) {
+	// swagger:operation POST /repos/{owner}/{repo}/releases/{id}/assets repository repoCreateReleaseAttachment
+	// ---
+	// summary: Create a release attachment
+	// produces:
+	// - application/json
+	// consumes:
+	// - multipart/form-data
+	// parameters:
+	// - name: owner
+	//   in: path
+	//   description: owner of the repo
+	//   type: string
+	//   required: true
+	// - name: repo
+	//   in: path
+	//   description: name of the repo
+	//   type: string
+	//   required: true
+	// - name: id
+	//   in: path
+	//   description: id of the release
+	//   type: integer
+	//   required: true
+	// - name: name
+	//   in: query
+	//   description: name of the attachment
+	//   type: string
+	//   required: false
+	// - name: attachment
+	//   in: formData
+	//   description: attachment to upload
+	//   type: file
+	//   required: true
+	// responses:
+	//   "201":
+	//     "$ref": "#/responses/Attachment"
+
+	// Check if attachments are enabled
+	if !setting.AttachmentEnabled {
+		ctx.Error(404, "AttachmentEnabled", errors.New("attachment is not enabled"))
+		return
+	}
+
+	// Check if release exists an load release
+	releaseID := ctx.ParamsInt64(":id")
+	release, err := models.GetReleaseByID(releaseID)
+	if err != nil {
+		ctx.Error(500, "GetReleaseByID", err)
+		return
+	}
+
+	// Get uploaded file from request
+	file, header, err := ctx.GetFile("attachment")
+	if err != nil {
+		ctx.Error(500, "GetFile", err)
+		return
+	}
+	defer file.Close()
+
+	buf := make([]byte, 1024)
+	n, _ := file.Read(buf)
+	if n > 0 {
+		buf = buf[:n]
+	}
+
+	// Check if the filetype is allowed by the settings
+	fileType := http.DetectContentType(buf)
+
+	allowedTypes := strings.Split(setting.AttachmentAllowedTypes, ",")
+	allowed := false
+	for _, t := range allowedTypes {
+		t := strings.Trim(t, " ")
+		if t == "*/*" || t == fileType {
+			allowed = true
+			break
+		}
+	}
+
+	if !allowed {
+		ctx.Error(400, "DetectContentType", errors.New("File type is not allowed"))
+		return
+	}
+
+	var filename = header.Filename
+	if query := ctx.Query("name"); query != "" {
+		filename = query
+	}
+
+	// Create a new attachment and save the file
+	attach, err := models.NewAttachment(filename, buf, file)
+	if err != nil {
+		ctx.Error(500, "NewAttachment", err)
+		return
+	}
+	attach.ReleaseID = release.ID
+	if err := models.UpdateAttachment(attach); err != nil {
+		ctx.Error(500, "UpdateAttachment", err)
+		return
+	}
+	ctx.JSON(201, attach.APIFormat())
+}
+
+// EditReleaseAttachment updates the given attachment
+func EditReleaseAttachment(ctx *context.APIContext, form api.EditAttachmentOptions) {
+	// swagger:operation PATCH /repos/{owner}/{repo}/releases/{id}/assets/{attachment_id} repository repoEditReleaseAttachment
+	// ---
+	// summary: Edit a release attachment
+	// produces:
+	// - application/json
+	// consumes:
+	// - application/json
+	// parameters:
+	// - name: owner
+	//   in: path
+	//   description: owner of the repo
+	//   type: string
+	//   required: true
+	// - name: repo
+	//   in: path
+	//   description: name of the repo
+	//   type: string
+	//   required: true
+	// - name: id
+	//   in: path
+	//   description: id of the release
+	//   type: integer
+	//   required: true
+	// - name: attachment_id
+	//   in: path
+	//   description: id of the attachment to edit
+	//   type: integer
+	//   required: true
+	// - name: body
+	//   in: body
+	//   schema:
+	//     "$ref": "#/definitions/EditAttachmentOptions"
+	// responses:
+	//   "201":
+	//     "$ref": "#/responses/Attachment"
+
+	// Check if release exists an load release
+	releaseID := ctx.ParamsInt64(":id")
+	attachID := ctx.ParamsInt64(":attachment")
+	attach, err := models.GetAttachmentByID(attachID)
+	if err != nil {
+		ctx.Error(500, "GetAttachmentByID", err)
+		return
+	}
+	if attach.ReleaseID != releaseID {
+		ctx.Status(404)
+		return
+	}
+	// FIXME Should prove the existence of the given repo, but results in unnecessary database requests
+	if form.Name != "" {
+		attach.Name = form.Name
+	}
+
+	if err := models.UpdateAttachment(attach); err != nil {
+		ctx.Error(500, "UpdateAttachment", attach)
+	}
+	ctx.JSON(201, attach.APIFormat())
+}
+
+// DeleteReleaseAttachment delete a given attachment
+func DeleteReleaseAttachment(ctx *context.APIContext) {
+	// swagger:operation DELETE /repos/{owner}/{repo}/releases/{id}/assets/{attachment_id} repository repoDeleteReleaseAttachment
+	// ---
+	// summary: Delete a release attachment
+	// produces:
+	// - application/json
+	// parameters:
+	// - name: owner
+	//   in: path
+	//   description: owner of the repo
+	//   type: string
+	//   required: true
+	// - name: repo
+	//   in: path
+	//   description: name of the repo
+	//   type: string
+	//   required: true
+	// - name: id
+	//   in: path
+	//   description: id of the release
+	//   type: integer
+	//   required: true
+	// - name: attachment_id
+	//   in: path
+	//   description: id of the attachment to delete
+	//   type: integer
+	//   required: true
+	// responses:
+	//   "204":
+	//     "$ref": "#/responses/empty"
+
+	// Check if release exists an load release
+	releaseID := ctx.ParamsInt64(":id")
+	attachID := ctx.ParamsInt64(":attachment")
+	attach, err := models.GetAttachmentByID(attachID)
+	if err != nil {
+		ctx.Error(500, "GetAttachmentByID", err)
+		return
+	}
+	if attach.ReleaseID != releaseID {
+		ctx.Status(404)
+		return
+	}
+	// FIXME Should prove the existence of the given repo, but results in unnecessary database requests
+
+	if err := models.DeleteAttachment(attach, true); err != nil {
+		ctx.Error(500, "DeleteAttachment", err)
+		return
+	}
+	ctx.Status(204)
+}
diff --git a/routers/api/v1/swagger/options.go b/routers/api/v1/swagger/options.go
index 31251eb3e2..3ea324186d 100644
--- a/routers/api/v1/swagger/options.go
+++ b/routers/api/v1/swagger/options.go
@@ -63,4 +63,6 @@ type swaggerParameterBodies struct {
 	EditUserOption   api.EditUserOption
 
 	MigrateRepoForm auth.MigrateRepoForm
+
+	EditAttachmentOptions api.EditAttachmentOptions
 }
diff --git a/routers/api/v1/swagger/repo.go b/routers/api/v1/swagger/repo.go
index 703f7d18dd..1f25691105 100644
--- a/routers/api/v1/swagger/repo.go
+++ b/routers/api/v1/swagger/repo.go
@@ -90,3 +90,15 @@ type swaggerResponseWatchInfo struct {
 type swaggerResponseSearchResults struct {
 	Body api.SearchResults `json:"body"`
 }
+
+// swagger:response AttachmentList
+type swaggerResponseAttachmentList struct {
+	//in: body
+	Body []api.Attachment `json:"body"`
+}
+
+// swagger:response Attachment
+type swaggerResponseAttachment struct {
+	//in: body
+	Body api.Attachment `json:"body"`
+}
diff --git a/vendor/code.gitea.io/sdk/gitea/admin_user.go b/vendor/code.gitea.io/sdk/gitea/admin_user.go
index 6d736d3462..bc0f36994d 100644
--- a/vendor/code.gitea.io/sdk/gitea/admin_user.go
+++ b/vendor/code.gitea.io/sdk/gitea/admin_user.go
@@ -12,7 +12,7 @@ import (
 
 // CreateUserOption create user options
 type CreateUserOption struct {
-	SourceID int64 `json:"source_id"`
+	SourceID  int64  `json:"source_id"`
 	LoginName string `json:"login_name"`
 	// required: true
 	Username string `json:"username" binding:"Required;AlphaDashDot;MaxSize(35)"`
@@ -21,8 +21,8 @@ type CreateUserOption struct {
 	// swagger:strfmt email
 	Email string `json:"email" binding:"Required;Email;MaxSize(254)"`
 	// required: true
-	Password string `json:"password" binding:"Required;MaxSize(255)"`
-	SendNotify bool `json:"send_notify"`
+	Password   string `json:"password" binding:"Required;MaxSize(255)"`
+	SendNotify bool   `json:"send_notify"`
 }
 
 // AdminCreateUser create a user
@@ -37,20 +37,20 @@ func (c *Client) AdminCreateUser(opt CreateUserOption) (*User, error) {
 
 // EditUserOption edit user options
 type EditUserOption struct {
-	SourceID int64 `json:"source_id"`
+	SourceID  int64  `json:"source_id"`
 	LoginName string `json:"login_name"`
-	FullName string `json:"full_name" binding:"MaxSize(100)"`
+	FullName  string `json:"full_name" binding:"MaxSize(100)"`
 	// required: true
 	// swagger:strfmt email
-	Email string `json:"email" binding:"Required;Email;MaxSize(254)"`
-	Password string `json:"password" binding:"MaxSize(255)"`
-	Website string `json:"website" binding:"MaxSize(50)"`
-	Location string `json:"location" binding:"MaxSize(50)"`
-	Active *bool `json:"active"`
-	Admin *bool `json:"admin"`
-	AllowGitHook *bool `json:"allow_git_hook"`
-	AllowImportLocal *bool `json:"allow_import_local"`
-	MaxRepoCreation *int `json:"max_repo_creation"`
+	Email            string `json:"email" binding:"Required;Email;MaxSize(254)"`
+	Password         string `json:"password" binding:"MaxSize(255)"`
+	Website          string `json:"website" binding:"MaxSize(50)"`
+	Location         string `json:"location" binding:"MaxSize(50)"`
+	Active           *bool  `json:"active"`
+	Admin            *bool  `json:"admin"`
+	AllowGitHook     *bool  `json:"allow_git_hook"`
+	AllowImportLocal *bool  `json:"allow_import_local"`
+	MaxRepoCreation  *int   `json:"max_repo_creation"`
 }
 
 // AdminEditUser modify user informations
diff --git a/vendor/code.gitea.io/sdk/gitea/attachment.go b/vendor/code.gitea.io/sdk/gitea/attachment.go
index 10e7a1d56a..e90a7114d9 100644
--- a/vendor/code.gitea.io/sdk/gitea/attachment.go
+++ b/vendor/code.gitea.io/sdk/gitea/attachment.go
@@ -3,15 +3,90 @@
 // license that can be found in the LICENSE file.
 
 package gitea // import "code.gitea.io/sdk/gitea"
-import "time"
+import (
+	"bytes"
+	"encoding/json"
+	"fmt"
+	"io"
+	"mime/multipart"
+	"net/http"
+	"time"
+)
 
 // Attachment a generic attachment
+// swagger:model
 type Attachment struct {
-	ID            int64     `json:"id"`
-	Name          string    `json:"name"`
-	Size          int64     `json:"size"`
-	DownloadCount int64     `json:"download_count"`
-	Created       time.Time `json:"created_at"`
-	UUID          string    `json:"uuid"`
-	DownloadURL   string    `json:"browser_download_url"`
+	ID            int64  `json:"id"`
+	Name          string `json:"name"`
+	Size          int64  `json:"size"`
+	DownloadCount int64  `json:"download_count"`
+	// swagger:strfmt date-time
+	Created     time.Time `json:"created_at"`
+	UUID        string    `json:"uuid"`
+	DownloadURL string    `json:"browser_download_url"`
+}
+
+// ListReleaseAttachments list release's attachments
+func (c *Client) ListReleaseAttachments(user, repo string, release int64) ([]*Attachment, error) {
+	attachments := make([]*Attachment, 0, 10)
+	err := c.getParsedResponse("GET",
+		fmt.Sprintf("/repos/%s/%s/releases/%d/assets", user, repo, release),
+		nil, nil, &attachments)
+	return attachments, err
+}
+
+// ListReleaseAttachments list release's attachments
+func (c *Client) GetReleaseAttachment(user, repo string, release int64, id int64) (*Attachment, error) {
+	a := new(Attachment)
+	err := c.getParsedResponse("GET",
+		fmt.Sprintf("/repos/%s/%s/releases/%d/assets/%d", user, repo, release, id),
+		nil, nil, &a)
+	return a, err
+}
+
+// CreateReleaseAttachment creates an attachment for the given release
+func (c *Client) CreateReleaseAttachment(user, repo string, release int64, file io.Reader, filename string) (*Attachment, error) {
+	// Write file to body
+	body := new(bytes.Buffer)
+	writer := multipart.NewWriter(body)
+	part, err := writer.CreateFormFile("attachment", filename)
+	if err != nil {
+		return nil, err
+	}
+
+	if _, err = io.Copy(part, file); err != nil {
+		return nil, err
+	}
+	if err = writer.Close(); err != nil {
+		return nil, err
+	}
+
+	// Send request
+	attachment := new(Attachment)
+	err = c.getParsedResponse("POST",
+		fmt.Sprintf("/repos/%s/%s/releases/%d/assets", user, repo, release),
+		http.Header{"Content-Type": {writer.FormDataContentType()}}, body, &attachment)
+	return attachment, err
+}
+
+// EditReleaseAttachment updates the given attachment with the given options
+func (c *Client) EditReleaseAttachment(user, repo string, release int64, attachment int64, form EditAttachmentOptions) (*Attachment, error) {
+	body, err := json.Marshal(&form)
+	if err != nil {
+		return nil, err
+	}
+	attach := new(Attachment)
+	return attach, c.getParsedResponse("PATCH", fmt.Sprintf("/repos/%s/%s/releases/%d/assets/%d", user, repo, release, attachment), jsonHeader, bytes.NewReader(body), attach)
+}
+
+// DeleteReleaseAttachment deletes the given attachment including the uploaded file
+func (c *Client) DeleteReleaseAttachment(user, repo string, release int64, id int64) error {
+	_, err := c.getResponse("DELETE", fmt.Sprintf("/repos/%s/%s/releases/%d/assets/%d", user, repo, release, id), nil, nil)
+	return err
+}
+
+// EditAttachmentOptions options for editing attachments
+// swagger:model
+type EditAttachmentOptions struct {
+	Name string `json:"name"`
 }
diff --git a/vendor/code.gitea.io/sdk/gitea/gitea.go b/vendor/code.gitea.io/sdk/gitea/gitea.go
index 20cb44feed..374a3faa96 100644
--- a/vendor/code.gitea.io/sdk/gitea/gitea.go
+++ b/vendor/code.gitea.io/sdk/gitea/gitea.go
@@ -7,6 +7,7 @@ package gitea
 import (
 	"encoding/json"
 	"errors"
+	"fmt"
 	"io"
 	"io/ioutil"
 	"net/http"
@@ -69,6 +70,8 @@ func (c *Client) getResponse(method, path string, header http.Header, body io.Re
 		return nil, errors.New("403 Forbidden")
 	case 404:
 		return nil, errors.New("404 Not Found")
+	case 422:
+		return nil, fmt.Errorf("422 Unprocessable Entity: %s", string(data))
 	}
 
 	if resp.StatusCode/100 != 2 {
diff --git a/vendor/code.gitea.io/sdk/gitea/hook.go b/vendor/code.gitea.io/sdk/gitea/hook.go
index b7109482f8..a9b0bdbd06 100644
--- a/vendor/code.gitea.io/sdk/gitea/hook.go
+++ b/vendor/code.gitea.io/sdk/gitea/hook.go
@@ -21,16 +21,16 @@ var (
 
 // Hook a hook is a web hook when one repository changed
 type Hook struct {
-	ID      int64             `json:"id"`
-	Type    string            `json:"type"`
-	URL     string            `json:"-"`
-	Config  map[string]string `json:"config"`
-	Events  []string          `json:"events"`
-	Active  bool              `json:"active"`
+	ID     int64             `json:"id"`
+	Type   string            `json:"type"`
+	URL    string            `json:"-"`
+	Config map[string]string `json:"config"`
+	Events []string          `json:"events"`
+	Active bool              `json:"active"`
 	// swagger:strfmt date-time
-	Updated time.Time         `json:"updated_at"`
+	Updated time.Time `json:"updated_at"`
 	// swagger:strfmt date-time
-	Created time.Time         `json:"created_at"`
+	Created time.Time `json:"created_at"`
 }
 
 // HookList represents a list of API hook.
@@ -67,7 +67,7 @@ type CreateHookOption struct {
 	Type string `json:"type" binding:"Required"`
 	// required: true
 	Config map[string]string `json:"config" binding:"Required"`
-	Events []string `json:"events"`
+	Events []string          `json:"events"`
 	// default: false
 	Active bool `json:"active"`
 }
@@ -95,8 +95,8 @@ func (c *Client) CreateRepoHook(user, repo string, opt CreateHookOption) (*Hook,
 // EditHookOption options when modify one hook
 type EditHookOption struct {
 	Config map[string]string `json:"config"`
-	Events []string `json:"events"`
-	Active *bool `json:"active"`
+	Events []string          `json:"events"`
+	Active *bool             `json:"active"`
 }
 
 // EditOrgHook modify one hook of an organization, with hook id and options
@@ -140,7 +140,7 @@ type Payloader interface {
 // PayloadUser represents the author or committer of a commit
 type PayloadUser struct {
 	// Full name of the commit author
-	Name     string `json:"name"`
+	Name string `json:"name"`
 	// swagger:strfmt email
 	Email    string `json:"email"`
 	UserName string `json:"username"`
@@ -159,7 +159,7 @@ type PayloadCommit struct {
 	Committer    *PayloadUser               `json:"committer"`
 	Verification *PayloadCommitVerification `json:"verification"`
 	// swagger:strfmt date-time
-	Timestamp    time.Time                  `json:"timestamp"`
+	Timestamp time.Time `json:"timestamp"`
 }
 
 // PayloadCommitVerification represents the GPG verification of a commit
diff --git a/vendor/code.gitea.io/sdk/gitea/issue.go b/vendor/code.gitea.io/sdk/gitea/issue.go
index 720f54caae..206c52a59e 100644
--- a/vendor/code.gitea.io/sdk/gitea/issue.go
+++ b/vendor/code.gitea.io/sdk/gitea/issue.go
@@ -43,12 +43,12 @@ type Issue struct {
 	//
 	// type: string
 	// enum: open,closed
-	State     StateType  `json:"state"`
-	Comments  int        `json:"comments"`
+	State    StateType `json:"state"`
+	Comments int       `json:"comments"`
 	// swagger:strfmt date-time
-	Created   time.Time  `json:"created_at"`
+	Created time.Time `json:"created_at"`
 	// swagger:strfmt date-time
-	Updated   time.Time  `json:"updated_at"`
+	Updated time.Time `json:"updated_at"`
 
 	PullRequest *PullRequestMeta `json:"pull_request"`
 }
@@ -86,15 +86,15 @@ func (c *Client) GetIssue(owner, repo string, index int64) (*Issue, error) {
 // CreateIssueOption options to create one issue
 type CreateIssueOption struct {
 	// required:true
-	Title     string  `json:"title" binding:"Required"`
-	Body      string  `json:"body"`
+	Title string `json:"title" binding:"Required"`
+	Body  string `json:"body"`
 	// username of assignee
-	Assignee  string  `json:"assignee"`
+	Assignee string `json:"assignee"`
 	// milestone id
-	Milestone int64   `json:"milestone"`
+	Milestone int64 `json:"milestone"`
 	// list of label ids
-	Labels    []int64 `json:"labels"`
-	Closed    bool    `json:"closed"`
+	Labels []int64 `json:"labels"`
+	Closed bool    `json:"closed"`
 }
 
 // CreateIssue create a new issue for a given repository
diff --git a/vendor/code.gitea.io/sdk/gitea/issue_comment.go b/vendor/code.gitea.io/sdk/gitea/issue_comment.go
index f146c1fe2d..2c8127c609 100644
--- a/vendor/code.gitea.io/sdk/gitea/issue_comment.go
+++ b/vendor/code.gitea.io/sdk/gitea/issue_comment.go
@@ -13,16 +13,16 @@ import (
 
 // Comment represents a comment on a commit or issue
 type Comment struct {
-	ID       int64     `json:"id"`
-	HTMLURL  string    `json:"html_url"`
-	PRURL    string    `json:"pull_request_url"`
-	IssueURL string    `json:"issue_url"`
-	Poster   *User     `json:"user"`
-	Body     string    `json:"body"`
+	ID       int64  `json:"id"`
+	HTMLURL  string `json:"html_url"`
+	PRURL    string `json:"pull_request_url"`
+	IssueURL string `json:"issue_url"`
+	Poster   *User  `json:"user"`
+	Body     string `json:"body"`
 	// swagger:strfmt date-time
-	Created  time.Time `json:"created_at"`
+	Created time.Time `json:"created_at"`
 	// swagger:strfmt date-time
-	Updated  time.Time `json:"updated_at"`
+	Updated time.Time `json:"updated_at"`
 }
 
 // ListIssueComments list comments on an issue.
diff --git a/vendor/code.gitea.io/sdk/gitea/issue_label.go b/vendor/code.gitea.io/sdk/gitea/issue_label.go
index 3acf1440b4..47d1b8221e 100644
--- a/vendor/code.gitea.io/sdk/gitea/issue_label.go
+++ b/vendor/code.gitea.io/sdk/gitea/issue_label.go
@@ -13,8 +13,8 @@ import (
 // Label a label to an issue or a pr
 // swagger:model
 type Label struct {
-	ID    int64  `json:"id"`
-	Name  string `json:"name"`
+	ID   int64  `json:"id"`
+	Name string `json:"name"`
 	// example: 00aabb
 	Color string `json:"color"`
 	URL   string `json:"url"`
@@ -36,7 +36,7 @@ func (c *Client) GetRepoLabel(owner, repo string, id int64) (*Label, error) {
 // CreateLabelOption options for creating a label
 type CreateLabelOption struct {
 	// required:true
-	Name  string `json:"name" binding:"Required"`
+	Name string `json:"name" binding:"Required"`
 	// required:true
 	// example: #00aabb
 	Color string `json:"color" binding:"Required;Size(7)"`
diff --git a/vendor/code.gitea.io/sdk/gitea/issue_milestone.go b/vendor/code.gitea.io/sdk/gitea/issue_milestone.go
index d820067422..775a6a9117 100644
--- a/vendor/code.gitea.io/sdk/gitea/issue_milestone.go
+++ b/vendor/code.gitea.io/sdk/gitea/issue_milestone.go
@@ -13,16 +13,16 @@ import (
 
 // Milestone milestone is a collection of issues on one repository
 type Milestone struct {
-	ID           int64      `json:"id"`
-	Title        string     `json:"title"`
-	Description  string     `json:"description"`
-	State        StateType  `json:"state"`
-	OpenIssues   int        `json:"open_issues"`
-	ClosedIssues int        `json:"closed_issues"`
+	ID           int64     `json:"id"`
+	Title        string    `json:"title"`
+	Description  string    `json:"description"`
+	State        StateType `json:"state"`
+	OpenIssues   int       `json:"open_issues"`
+	ClosedIssues int       `json:"closed_issues"`
 	// swagger:strfmt date-time
-	Closed       *time.Time `json:"closed_at"`
+	Closed *time.Time `json:"closed_at"`
 	// swagger:strfmt date-time
-	Deadline     *time.Time `json:"due_on"`
+	Deadline *time.Time `json:"due_on"`
 }
 
 // ListRepoMilestones list all the milestones of one repository
@@ -39,10 +39,10 @@ func (c *Client) GetMilestone(owner, repo string, id int64) (*Milestone, error)
 
 // CreateMilestoneOption options for creating a milestone
 type CreateMilestoneOption struct {
-	Title       string     `json:"title"`
-	Description string     `json:"description"`
+	Title       string `json:"title"`
+	Description string `json:"description"`
 	// swagger:strfmt date-time
-	Deadline    *time.Time `json:"due_on"`
+	Deadline *time.Time `json:"due_on"`
 }
 
 // CreateMilestone create one milestone with options
diff --git a/vendor/code.gitea.io/sdk/gitea/issue_tracked_time.go b/vendor/code.gitea.io/sdk/gitea/issue_tracked_time.go
index dcdecbb2a2..7f4b64cbb3 100644
--- a/vendor/code.gitea.io/sdk/gitea/issue_tracked_time.go
+++ b/vendor/code.gitea.io/sdk/gitea/issue_tracked_time.go
@@ -13,7 +13,7 @@ import (
 
 // TrackedTime worked time for an issue / pr
 type TrackedTime struct {
-	ID      int64     `json:"id"`
+	ID int64 `json:"id"`
 	// swagger:strfmt date-time
 	Created time.Time `json:"created"`
 	// Time in seconds
diff --git a/vendor/code.gitea.io/sdk/gitea/org.go b/vendor/code.gitea.io/sdk/gitea/org.go
index d67d707e7b..19238d1028 100644
--- a/vendor/code.gitea.io/sdk/gitea/org.go
+++ b/vendor/code.gitea.io/sdk/gitea/org.go
@@ -42,11 +42,11 @@ func (c *Client) GetOrg(orgname string) (*Organization, error) {
 // CreateOrgOption options for creating an organization
 type CreateOrgOption struct {
 	// required: true
-	UserName string `json:"username" binding:"Required"`
-	FullName string `json:"full_name"`
+	UserName    string `json:"username" binding:"Required"`
+	FullName    string `json:"full_name"`
 	Description string `json:"description"`
-	Website string `json:"website"`
-	Location string `json:"location"`
+	Website     string `json:"website"`
+	Location    string `json:"location"`
 }
 
 // EditOrgOption options for editing an organization
diff --git a/vendor/code.gitea.io/sdk/gitea/org_team.go b/vendor/code.gitea.io/sdk/gitea/org_team.go
index 0c9a740350..2fc6796d43 100644
--- a/vendor/code.gitea.io/sdk/gitea/org_team.go
+++ b/vendor/code.gitea.io/sdk/gitea/org_team.go
@@ -10,7 +10,7 @@ type Team struct {
 	Name        string `json:"name"`
 	Description string `json:"description"`
 	// enum: none,read,write,admin,owner
-	Permission  string `json:"permission"`
+	Permission string `json:"permission"`
 }
 
 // CreateTeamOption options for creating a team
@@ -19,7 +19,7 @@ type CreateTeamOption struct {
 	Name        string `json:"name" binding:"Required;AlphaDashDot;MaxSize(30)"`
 	Description string `json:"description" binding:"MaxSize(255)"`
 	// enum: read,write,admin
-	Permission  string `json:"permission"`
+	Permission string `json:"permission"`
 }
 
 // EditTeamOption options for editing a team
@@ -28,5 +28,5 @@ type EditTeamOption struct {
 	Name        string `json:"name" binding:"Required;AlphaDashDot;MaxSize(30)"`
 	Description string `json:"description" binding:"MaxSize(255)"`
 	// enum: read,write,admin
-	Permission  string `json:"permission"`
+	Permission string `json:"permission"`
 }
diff --git a/vendor/code.gitea.io/sdk/gitea/pull.go b/vendor/code.gitea.io/sdk/gitea/pull.go
index 0aa0ea8cc1..ee3fe116de 100644
--- a/vendor/code.gitea.io/sdk/gitea/pull.go
+++ b/vendor/code.gitea.io/sdk/gitea/pull.go
@@ -29,8 +29,8 @@ type PullRequest struct {
 	DiffURL  string `json:"diff_url"`
 	PatchURL string `json:"patch_url"`
 
-	Mergeable      bool       `json:"mergeable"`
-	HasMerged      bool       `json:"merged"`
+	Mergeable bool `json:"mergeable"`
+	HasMerged bool `json:"merged"`
 	// swagger:strfmt date-time
 	Merged         *time.Time `json:"merged_at"`
 	MergedCommitID *string    `json:"merge_commit_sha"`
diff --git a/vendor/code.gitea.io/sdk/gitea/release.go b/vendor/code.gitea.io/sdk/gitea/release.go
index 01e41e35a6..396251dcac 100644
--- a/vendor/code.gitea.io/sdk/gitea/release.go
+++ b/vendor/code.gitea.io/sdk/gitea/release.go
@@ -13,21 +13,22 @@ import (
 
 // Release represents a repository release
 type Release struct {
-	ID           int64     `json:"id"`
-	TagName      string    `json:"tag_name"`
-	Target       string    `json:"target_commitish"`
-	Title        string    `json:"name"`
-	Note         string    `json:"body"`
-	URL          string    `json:"url"`
-	TarURL       string    `json:"tarball_url"`
-	ZipURL       string    `json:"zipball_url"`
-	IsDraft      bool      `json:"draft"`
-	IsPrerelease bool      `json:"prerelease"`
+	ID           int64  `json:"id"`
+	TagName      string `json:"tag_name"`
+	Target       string `json:"target_commitish"`
+	Title        string `json:"name"`
+	Note         string `json:"body"`
+	URL          string `json:"url"`
+	TarURL       string `json:"tarball_url"`
+	ZipURL       string `json:"zipball_url"`
+	IsDraft      bool   `json:"draft"`
+	IsPrerelease bool   `json:"prerelease"`
 	// swagger:strfmt date-time
-	CreatedAt    time.Time `json:"created_at"`
+	CreatedAt time.Time `json:"created_at"`
 	// swagger:strfmt date-time
-	PublishedAt  time.Time `json:"published_at"`
-	Publisher    *User     `json:"author"`
+	PublishedAt time.Time     `json:"published_at"`
+	Publisher   *User         `json:"author"`
+	Attachments []*Attachment `json:"assets"`
 }
 
 // ListReleases list releases of a repository
diff --git a/vendor/code.gitea.io/sdk/gitea/repo.go b/vendor/code.gitea.io/sdk/gitea/repo.go
index 3def6e26ce..339cbd33de 100644
--- a/vendor/code.gitea.io/sdk/gitea/repo.go
+++ b/vendor/code.gitea.io/sdk/gitea/repo.go
@@ -41,10 +41,10 @@ type Repository struct {
 	OpenIssues    int         `json:"open_issues_count"`
 	DefaultBranch string      `json:"default_branch"`
 	// swagger:strfmt date-time
-	Created       time.Time   `json:"created_at"`
+	Created time.Time `json:"created_at"`
 	// swagger:strfmt date-time
-	Updated       time.Time   `json:"updated_at"`
-	Permissions   *Permission `json:"permissions,omitempty"`
+	Updated     time.Time   `json:"updated_at"`
+	Permissions *Permission `json:"permissions,omitempty"`
 }
 
 // ListMyRepos lists all repositories for the authenticated user that has access to.
@@ -122,15 +122,15 @@ func (c *Client) DeleteRepo(owner, repo string) error {
 // MigrateRepoOption options for migrating a repository from an external service
 type MigrateRepoOption struct {
 	// required: true
-	CloneAddr string `json:"clone_addr" binding:"Required"`
+	CloneAddr    string `json:"clone_addr" binding:"Required"`
 	AuthUsername string `json:"auth_username"`
 	AuthPassword string `json:"auth_password"`
 	// required: true
 	UID int `json:"uid" binding:"Required"`
 	// required: true
-	RepoName string `json:"repo_name" binding:"Required"`
-	Mirror bool `json:"mirror"`
-	Private bool `json:"private"`
+	RepoName    string `json:"repo_name" binding:"Required"`
+	Mirror      bool   `json:"mirror"`
+	Private     bool   `json:"private"`
 	Description string `json:"description"`
 }
 
diff --git a/vendor/code.gitea.io/sdk/gitea/repo_key.go b/vendor/code.gitea.io/sdk/gitea/repo_key.go
index f2f1038aa3..ec53311bda 100644
--- a/vendor/code.gitea.io/sdk/gitea/repo_key.go
+++ b/vendor/code.gitea.io/sdk/gitea/repo_key.go
@@ -13,10 +13,10 @@ import (
 
 // DeployKey a deploy key
 type DeployKey struct {
-	ID       int64     `json:"id"`
-	Key      string    `json:"key"`
-	URL      string    `json:"url"`
-	Title    string    `json:"title"`
+	ID    int64  `json:"id"`
+	Key   string `json:"key"`
+	URL   string `json:"url"`
+	Title string `json:"title"`
 	// swagger:strfmt date-time
 	Created  time.Time `json:"created_at"`
 	ReadOnly bool      `json:"read_only"`
diff --git a/vendor/code.gitea.io/sdk/gitea/status.go b/vendor/code.gitea.io/sdk/gitea/status.go
index 5ce25321af..3060ab1b2b 100644
--- a/vendor/code.gitea.io/sdk/gitea/status.go
+++ b/vendor/code.gitea.io/sdk/gitea/status.go
@@ -38,9 +38,9 @@ type Status struct {
 	Context     string      `json:"context"`
 	Creator     *User       `json:"creator"`
 	// swagger:strfmt date-time
-	Created     time.Time   `json:"created_at"`
+	Created time.Time `json:"created_at"`
 	// swagger:strfmt date-time
-	Updated     time.Time   `json:"updated_at"`
+	Updated time.Time `json:"updated_at"`
 }
 
 // CombinedStatus holds the combined state of several statuses for a single commit
diff --git a/vendor/code.gitea.io/sdk/gitea/user.go b/vendor/code.gitea.io/sdk/gitea/user.go
index d104cc9933..f6b687e979 100644
--- a/vendor/code.gitea.io/sdk/gitea/user.go
+++ b/vendor/code.gitea.io/sdk/gitea/user.go
@@ -13,13 +13,13 @@ import (
 // swagger:model
 type User struct {
 	// the user's id
-	ID        int64  `json:"id"`
+	ID int64 `json:"id"`
 	// the user's username
-	UserName  string `json:"login"`
+	UserName string `json:"login"`
 	// the user's full name
-	FullName  string `json:"full_name"`
+	FullName string `json:"full_name"`
 	// swagger:strfmt email
-	Email     string `json:"email"`
+	Email string `json:"email"`
 	// URL to the user's avatar
 	AvatarURL string `json:"avatar_url"`
 }
diff --git a/vendor/code.gitea.io/sdk/gitea/user_gpgkey.go b/vendor/code.gitea.io/sdk/gitea/user_gpgkey.go
index ae5dcdcb10..0817d89461 100644
--- a/vendor/code.gitea.io/sdk/gitea/user_gpgkey.go
+++ b/vendor/code.gitea.io/sdk/gitea/user_gpgkey.go
@@ -24,9 +24,9 @@ type GPGKey struct {
 	CanEncryptStorage bool           `json:"can_encrypt_storage"`
 	CanCertify        bool           `json:"can_certify"`
 	// swagger:strfmt date-time
-	Created           time.Time      `json:"created_at,omitempty"`
+	Created time.Time `json:"created_at,omitempty"`
 	// swagger:strfmt date-time
-	Expires           time.Time      `json:"expires_at,omitempty"`
+	Expires time.Time `json:"expires_at,omitempty"`
 }
 
 // GPGKeyEmail an email attached to a GPGKey
diff --git a/vendor/code.gitea.io/sdk/gitea/user_search.go b/vendor/code.gitea.io/sdk/gitea/user_search.go
new file mode 100644
index 0000000000..65ab980d66
--- /dev/null
+++ b/vendor/code.gitea.io/sdk/gitea/user_search.go
@@ -0,0 +1,14 @@
+package gitea
+
+import "fmt"
+
+type searchUsersResponse struct {
+	Users []*User `json:"data"`
+}
+
+// SearchUsers finds users by query
+func (c *Client) SearchUsers(query string, limit int) ([]*User, error) {
+	resp := new(searchUsersResponse)
+	err := c.getParsedResponse("GET", fmt.Sprintf("/users/search?q=%s&limit=%d", query, limit), nil, nil, &resp)
+	return resp.Users, err
+}
diff --git a/vendor/vendor.json b/vendor/vendor.json
index c6a10a585b..967a297b27 100644
--- a/vendor/vendor.json
+++ b/vendor/vendor.json
@@ -9,10 +9,10 @@
 			"revisionTime": "2018-02-10T03:05:43Z"
 		},
 		{
-			"checksumSHA1": "Qtq0kW+BnpYMOriaoCjMa86WGG8=",
+			"checksumSHA1": "PWaIU7g1YSkETxka2DIS1EYsPK0=",
 			"path": "code.gitea.io/sdk/gitea",
-			"revision": "79eee8f12c7fc1cc5b802c5cdc5b494ef3733866",
-			"revisionTime": "2017-12-20T06:57:50Z"
+			"revision": "cdbef997666132599cc92dc22aa94de3db04adeb",
+			"revisionTime": "2018-03-02T14:48:43Z"
 		},
 		{
 			"checksumSHA1": "bOODD4Gbw3GfcuQPU2dI40crxxk=",