forked from gitea/gitea
feat(API): add route and implementation for creating/updating repository secret (#26766)
spec: https://docs.github.com/en/rest/actions/secrets?apiVersion=2022-11-28#create-or-update-a-repository-secret - Add a new route for creating or updating a secret value in a repository - Create a new file `routers/api/v1/repo/action.go` with the implementation of the `CreateOrUpdateSecret` function - Update the Swagger documentation for the `updateRepoSecret` operation in the `v1_json.tmpl` template file --------- Signed-off-by: Bo-Yi Wu <appleboy.tw@gmail.com> Co-authored-by: Giteabot <teabot@gitea.io>
This commit is contained in:
parent
2d9249b6d9
commit
b91057b172
|
@ -160,3 +160,31 @@ func DeleteSecret(ctx context.Context, orgID, repoID int64, name string) error {
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CreateOrUpdateSecret creates or updates a secret and returns true if it was created
|
||||||
|
func CreateOrUpdateSecret(ctx context.Context, orgID, repoID int64, name, data string) (bool, error) {
|
||||||
|
sc := new(Secret)
|
||||||
|
name = strings.ToUpper(name)
|
||||||
|
has, err := db.GetEngine(ctx).
|
||||||
|
Where("owner_id=?", orgID).
|
||||||
|
And("repo_id=?", repoID).
|
||||||
|
And("name=?", name).
|
||||||
|
Get(sc)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if !has {
|
||||||
|
_, err = InsertEncryptedSecret(ctx, orgID, repoID, name, data)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := UpdateSecret(ctx, orgID, repoID, name, data); err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
|
@ -933,6 +933,10 @@ func Routes() *web.Route {
|
||||||
m.Post("/accept", repo.AcceptTransfer)
|
m.Post("/accept", repo.AcceptTransfer)
|
||||||
m.Post("/reject", repo.RejectTransfer)
|
m.Post("/reject", repo.RejectTransfer)
|
||||||
}, reqToken())
|
}, reqToken())
|
||||||
|
m.Group("/actions/secrets", func() {
|
||||||
|
m.Combo("/{secretname}").
|
||||||
|
Put(reqToken(), reqOwner(), bind(api.CreateOrUpdateSecretOption{}), repo.CreateOrUpdateSecret)
|
||||||
|
})
|
||||||
m.Group("/hooks/git", func() {
|
m.Group("/hooks/git", func() {
|
||||||
m.Combo("").Get(repo.ListGitHooks)
|
m.Combo("").Get(repo.ListGitHooks)
|
||||||
m.Group("/{id}", func() {
|
m.Group("/{id}", func() {
|
||||||
|
@ -1301,7 +1305,7 @@ func Routes() *web.Route {
|
||||||
m.Group("/actions/secrets", func() {
|
m.Group("/actions/secrets", func() {
|
||||||
m.Get("", reqToken(), reqOrgOwnership(), org.ListActionsSecrets)
|
m.Get("", reqToken(), reqOrgOwnership(), org.ListActionsSecrets)
|
||||||
m.Combo("/{secretname}").
|
m.Combo("/{secretname}").
|
||||||
Put(reqToken(), reqOrgOwnership(), bind(api.CreateOrUpdateSecretOption{}), org.CreateOrUpdateOrgSecret).
|
Put(reqToken(), reqOrgOwnership(), bind(api.CreateOrUpdateSecretOption{}), org.CreateOrUpdateSecret).
|
||||||
Delete(reqToken(), reqOrgOwnership(), org.DeleteOrgSecret)
|
Delete(reqToken(), reqOrgOwnership(), org.DeleteOrgSecret)
|
||||||
})
|
})
|
||||||
m.Group("/public_members", func() {
|
m.Group("/public_members", func() {
|
||||||
|
|
|
@ -74,7 +74,7 @@ func listActionsSecrets(ctx *context.APIContext) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// create or update one secret of the organization
|
// create or update one secret of the organization
|
||||||
func CreateOrUpdateOrgSecret(ctx *context.APIContext) {
|
func CreateOrUpdateSecret(ctx *context.APIContext) {
|
||||||
// swagger:operation PUT /orgs/{org}/actions/secrets/{secretname} organization updateOrgSecret
|
// swagger:operation PUT /orgs/{org}/actions/secrets/{secretname} organization updateOrgSecret
|
||||||
// ---
|
// ---
|
||||||
// summary: Create or Update a secret value in an organization
|
// summary: Create or Update a secret value in an organization
|
||||||
|
@ -108,26 +108,17 @@ func CreateOrUpdateOrgSecret(ctx *context.APIContext) {
|
||||||
// "$ref": "#/responses/forbidden"
|
// "$ref": "#/responses/forbidden"
|
||||||
secretName := ctx.Params(":secretname")
|
secretName := ctx.Params(":secretname")
|
||||||
if err := actions.NameRegexMatch(secretName); err != nil {
|
if err := actions.NameRegexMatch(secretName); err != nil {
|
||||||
ctx.Error(http.StatusBadRequest, "CreateOrUpdateOrgSecret", err)
|
ctx.Error(http.StatusBadRequest, "CreateOrUpdateSecret", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
opt := web.GetForm(ctx).(*api.CreateOrUpdateSecretOption)
|
opt := web.GetForm(ctx).(*api.CreateOrUpdateSecretOption)
|
||||||
err := secret_model.UpdateSecret(
|
isCreated, err := secret_model.CreateOrUpdateSecret(ctx, ctx.Org.Organization.ID, 0, secretName, opt.Data)
|
||||||
ctx, ctx.Org.Organization.ID, 0, secretName, opt.Data,
|
if err != nil {
|
||||||
)
|
ctx.Error(http.StatusInternalServerError, "CreateOrUpdateSecret", err)
|
||||||
if secret_model.IsErrSecretNotFound(err) {
|
|
||||||
_, err := secret_model.InsertEncryptedSecret(
|
|
||||||
ctx, ctx.Org.Organization.ID, 0, secretName, actions.ReserveLineBreakForTextarea(opt.Data),
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
ctx.Error(http.StatusInternalServerError, "InsertEncryptedSecret", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
ctx.Status(http.StatusCreated)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if err != nil {
|
if isCreated {
|
||||||
ctx.Error(http.StatusInternalServerError, "UpdateSecret", err)
|
ctx.Status(http.StatusCreated)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,75 @@
|
||||||
|
// Copyright 2023 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package repo
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
secret_model "code.gitea.io/gitea/models/secret"
|
||||||
|
"code.gitea.io/gitea/modules/context"
|
||||||
|
api "code.gitea.io/gitea/modules/structs"
|
||||||
|
"code.gitea.io/gitea/modules/web"
|
||||||
|
"code.gitea.io/gitea/routers/web/shared/actions"
|
||||||
|
)
|
||||||
|
|
||||||
|
// create or update one secret of the repository
|
||||||
|
func CreateOrUpdateSecret(ctx *context.APIContext) {
|
||||||
|
// swagger:operation PUT /repos/{owner}/{repo}/actions/secrets/{secretname} repository updateRepoSecret
|
||||||
|
// ---
|
||||||
|
// summary: Create or Update a secret value in a repository
|
||||||
|
// consumes:
|
||||||
|
// - application/json
|
||||||
|
// produces:
|
||||||
|
// - application/json
|
||||||
|
// parameters:
|
||||||
|
// - name: owner
|
||||||
|
// in: path
|
||||||
|
// description: owner of the repository
|
||||||
|
// type: string
|
||||||
|
// required: true
|
||||||
|
// - name: repo
|
||||||
|
// in: path
|
||||||
|
// description: name of the repository
|
||||||
|
// type: string
|
||||||
|
// required: true
|
||||||
|
// - name: secretname
|
||||||
|
// in: path
|
||||||
|
// description: name of the secret
|
||||||
|
// type: string
|
||||||
|
// required: true
|
||||||
|
// - name: body
|
||||||
|
// in: body
|
||||||
|
// schema:
|
||||||
|
// "$ref": "#/definitions/CreateOrUpdateSecretOption"
|
||||||
|
// responses:
|
||||||
|
// "201":
|
||||||
|
// description: response when creating a secret
|
||||||
|
// "204":
|
||||||
|
// description: response when updating a secret
|
||||||
|
// "400":
|
||||||
|
// "$ref": "#/responses/error"
|
||||||
|
// "403":
|
||||||
|
// "$ref": "#/responses/forbidden"
|
||||||
|
|
||||||
|
owner := ctx.Repo.Owner
|
||||||
|
repo := ctx.Repo.Repository
|
||||||
|
|
||||||
|
secretName := ctx.Params(":secretname")
|
||||||
|
if err := actions.NameRegexMatch(secretName); err != nil {
|
||||||
|
ctx.Error(http.StatusBadRequest, "CreateOrUpdateSecret", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
opt := web.GetForm(ctx).(*api.CreateOrUpdateSecretOption)
|
||||||
|
isCreated, err := secret_model.CreateOrUpdateSecret(ctx, owner.ID, repo.ID, secretName, opt.Data)
|
||||||
|
if err != nil {
|
||||||
|
ctx.Error(http.StatusInternalServerError, "CreateOrUpdateSecret", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if isCreated {
|
||||||
|
ctx.Status(http.StatusCreated)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.Status(http.StatusNoContent)
|
||||||
|
}
|
|
@ -3230,6 +3230,65 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"/repos/{owner}/{repo}/actions/secrets/{secretname}": {
|
||||||
|
"put": {
|
||||||
|
"consumes": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"repository"
|
||||||
|
],
|
||||||
|
"summary": "Create or Update a secret value in a repository",
|
||||||
|
"operationId": "updateRepoSecret",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"description": "owner of the repository",
|
||||||
|
"name": "owner",
|
||||||
|
"in": "path",
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"description": "name of the repository",
|
||||||
|
"name": "repo",
|
||||||
|
"in": "path",
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"description": "name of the secret",
|
||||||
|
"name": "secretname",
|
||||||
|
"in": "path",
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "body",
|
||||||
|
"in": "body",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/CreateOrUpdateSecretOption"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"201": {
|
||||||
|
"description": "response when creating a secret"
|
||||||
|
},
|
||||||
|
"204": {
|
||||||
|
"description": "response when updating a secret"
|
||||||
|
},
|
||||||
|
"400": {
|
||||||
|
"$ref": "#/responses/error"
|
||||||
|
},
|
||||||
|
"403": {
|
||||||
|
"$ref": "#/responses/forbidden"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"/repos/{owner}/{repo}/activities/feeds": {
|
"/repos/{owner}/{repo}/activities/feeds": {
|
||||||
"get": {
|
"get": {
|
||||||
"produces": [
|
"produces": [
|
||||||
|
|
Loading…
Reference in New Issue