Compare commits
22 Commits
| Author | SHA1 | Date |
|---|---|---|
|
|
d9a412c3a2 | |
|
|
feefd16ae1 | |
|
|
4b32e70cdb | |
|
|
1b7a0ffb36 | |
|
|
684b2733a0 | |
|
|
f091079312 | |
|
|
4c65b6777a | |
|
|
db3a241c92 | |
|
|
868d41e749 | |
|
|
902314a413 | |
|
|
7d71cba643 | |
|
|
6f0092b14c | |
|
|
31c339fd1d | |
|
|
713af786c1 | |
|
|
37f8da019d | |
|
|
dfe0ce8de0 | |
|
|
c954b4799e | |
|
|
8910972c65 | |
|
|
b015baf624 | |
|
|
4c2cc2a658 | |
|
|
89f6f640e1 | |
|
|
ca5c03bfbf |
2
main.go
2
main.go
|
|
@ -22,7 +22,7 @@ import (
|
|||
)
|
||||
|
||||
var (
|
||||
Version = "v2.7, by v1.21.0 "
|
||||
Version = "v2.8.8, by v1.21.0, 20260119 "
|
||||
Tags = ""
|
||||
MakeVersion = ""
|
||||
)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,59 @@
|
|||
package actions
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
|
||||
"code.gitea.io/gitea/models/db"
|
||||
secret_model "code.gitea.io/gitea/models/secret"
|
||||
"xorm.io/builder"
|
||||
)
|
||||
|
||||
type FindSecretsOptions struct {
|
||||
db.ListOptions
|
||||
OwnerID int64
|
||||
RepoID int64
|
||||
SecretID int64
|
||||
Name string
|
||||
}
|
||||
|
||||
func (opts *FindSecretsOptions) toConds() builder.Cond {
|
||||
cond := builder.NewCond()
|
||||
if opts.OwnerID > 0 {
|
||||
cond = cond.And(builder.Eq{"owner_id": opts.OwnerID})
|
||||
}
|
||||
if opts.RepoID > 0 {
|
||||
cond = cond.And(builder.Eq{"repo_id": opts.RepoID})
|
||||
}
|
||||
if opts.SecretID != 0 {
|
||||
cond = cond.And(builder.Eq{"id": opts.SecretID})
|
||||
}
|
||||
if opts.Name != "" {
|
||||
cond = cond.And(builder.Eq{"name": strings.ToUpper(opts.Name)})
|
||||
}
|
||||
|
||||
return cond
|
||||
}
|
||||
|
||||
func FindSecrets(ctx context.Context, opts FindSecretsOptions) ([]*secret_model.Secret, error) {
|
||||
var secrets []*secret_model.Secret
|
||||
sess := db.GetEngine(ctx)
|
||||
if opts.PageSize != 0 {
|
||||
sess = db.SetSessionPagination(sess, &opts.ListOptions)
|
||||
}
|
||||
return secrets, sess.
|
||||
Where(opts.toConds()).
|
||||
OrderBy("created_unix desc").
|
||||
Find(&secrets)
|
||||
}
|
||||
|
||||
func CountSecrets(ctx context.Context, opts *FindSecretsOptions) (int64, error) {
|
||||
return db.GetEngine(ctx).Where(opts.toConds()).Count(new(secret_model.Secret))
|
||||
}
|
||||
|
||||
func DeleteSecret(ctx context.Context, secret *secret_model.Secret) error {
|
||||
if _, err := db.DeleteByID(ctx, secret.ID, secret); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
@ -0,0 +1,65 @@
|
|||
package actions
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
|
||||
gitea_actions "code.gitea.io/gitea/models/actions"
|
||||
"code.gitea.io/gitea/models/db"
|
||||
"xorm.io/builder"
|
||||
)
|
||||
|
||||
type FindVariablesOpts struct {
|
||||
db.ListOptions
|
||||
IDs []int64
|
||||
RepoID int64
|
||||
OwnerID int64 // it will be ignored if RepoID is set
|
||||
Name string
|
||||
}
|
||||
|
||||
func (opts FindVariablesOpts) ToConds() builder.Cond {
|
||||
cond := builder.NewCond()
|
||||
if len(opts.IDs) > 0 {
|
||||
if len(opts.IDs) == 1 {
|
||||
cond = cond.And(builder.Eq{"id": opts.IDs[0]})
|
||||
} else {
|
||||
cond = cond.And(builder.In("id", opts.IDs))
|
||||
}
|
||||
}
|
||||
|
||||
// Since we now support instance-level variables,
|
||||
// there is no need to check for null values for `owner_id` and `repo_id`
|
||||
cond = cond.And(builder.Eq{"repo_id": opts.RepoID})
|
||||
if opts.RepoID != 0 { // if RepoID is set
|
||||
// ignore OwnerID and treat it as 0
|
||||
cond = cond.And(builder.Eq{"owner_id": 0})
|
||||
} else {
|
||||
cond = cond.And(builder.Eq{"owner_id": opts.OwnerID})
|
||||
}
|
||||
|
||||
if opts.Name != "" {
|
||||
cond = cond.And(builder.Eq{"name": strings.ToUpper(opts.Name)})
|
||||
}
|
||||
return cond
|
||||
}
|
||||
|
||||
func FindVariables(ctx context.Context, opts FindVariablesOpts) ([]*gitea_actions.ActionVariable, error) {
|
||||
var variables []*gitea_actions.ActionVariable
|
||||
sess := db.GetEngine(ctx)
|
||||
if opts.PageSize != 0 {
|
||||
sess = db.SetSessionPagination(sess, &opts.ListOptions)
|
||||
}
|
||||
return variables, sess.Where(opts.ToConds()).OrderBy("created_unix desc").Find(&variables)
|
||||
}
|
||||
|
||||
func CountVariables(ctx context.Context, opts FindVariablesOpts) (int64, error) {
|
||||
sess := db.GetEngine(ctx)
|
||||
return sess.Where(opts.ToConds()).Count(new(gitea_actions.ActionVariable))
|
||||
}
|
||||
|
||||
func DeleteVariable(ctx context.Context, variable *gitea_actions.ActionVariable) error {
|
||||
if _, err := db.DeleteByID(ctx, variable.ID, variable); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
@ -23,7 +23,7 @@ func ToTag(repo *repo.Repository, gitRepo *git.Repository, t *git.Tag) (tag *api
|
|||
return &api.Tag{}, err
|
||||
}
|
||||
return &api.Tag{
|
||||
Name: t.Name,
|
||||
Name: strings.TrimPrefix(t.Name, "tags/"),
|
||||
Message: strings.TrimSpace(t.Message),
|
||||
ID: t.ID.String(),
|
||||
Commit: tagCommit,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,34 @@
|
|||
package structs
|
||||
|
||||
import "time"
|
||||
|
||||
// CreateVariableOption the option when creating variable
|
||||
// swagger:model
|
||||
type CreateSecretOption struct {
|
||||
// Value of the variable to create
|
||||
//
|
||||
// required: true
|
||||
Data string `json:"data" binding:"Required"`
|
||||
}
|
||||
|
||||
// UpdateVariableOption the option when updating variable
|
||||
// swagger:model
|
||||
type UpdateSecretOption struct {
|
||||
// New name for the variable. If the field is empty, the variable name won't be updated.
|
||||
Name string `json:"name"`
|
||||
// Value of the variable to update
|
||||
//
|
||||
// required: true
|
||||
Data string `json:"data" binding:"Required"`
|
||||
|
||||
// Description of the variable to update
|
||||
}
|
||||
|
||||
// Secret represents a secret
|
||||
// swagger:model
|
||||
type Secret struct {
|
||||
// the secret's name
|
||||
Name string `json:"name"`
|
||||
// swagger:strfmt date-time
|
||||
Created time.Time `json:"created_at"`
|
||||
}
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
package structs
|
||||
|
||||
// CreateVariableOption the option when creating variable
|
||||
// swagger:model
|
||||
type CreateVariableOption struct {
|
||||
// Value of the variable to create
|
||||
//
|
||||
// required: true
|
||||
Value string `json:"value" binding:"Required"`
|
||||
}
|
||||
|
||||
// UpdateVariableOption the option when updating variable
|
||||
// swagger:model
|
||||
type UpdateVariableOption struct {
|
||||
// New name for the variable. If the field is empty, the variable name won't be updated.
|
||||
Name string `json:"name"`
|
||||
// Value of the variable to update
|
||||
//
|
||||
// required: true
|
||||
Value string `json:"value" binding:"Required"`
|
||||
|
||||
// Description of the variable to update
|
||||
}
|
||||
|
||||
// ActionVariable return value of the query API
|
||||
// swagger:model
|
||||
type ActionVariable struct {
|
||||
// the owner to which the variable belongs
|
||||
OwnerID int64 `json:"owner_id"`
|
||||
// the repository to which the variable belongs
|
||||
RepoID int64 `json:"repo_id"`
|
||||
// the name of the variable
|
||||
Name string `json:"name"`
|
||||
// the value of the variable
|
||||
Data string `json:"data"`
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
package util
|
||||
|
||||
import "strings"
|
||||
|
||||
func ReserveLineBreakForTextarea(input string) string {
|
||||
// Since the content is from a form which is a textarea, the line endings are \r\n.
|
||||
// It's a standard behavior of HTML.
|
||||
// But we want to store them as \n like what GitHub does.
|
||||
// And users are unlikely to really need to keep the \r.
|
||||
// Other than this, we should respect the original content, even leading or trailing spaces.
|
||||
return strings.ReplaceAll(input, "\r\n", "\n")
|
||||
}
|
||||
|
|
@ -30,6 +30,7 @@ import (
|
|||
"code.gitlink.org.cn/Gitlink/gitea_hat.git/routers/hat/org"
|
||||
"code.gitlink.org.cn/Gitlink/gitea_hat.git/routers/hat/repo"
|
||||
"code.gitlink.org.cn/Gitlink/gitea_hat.git/routers/hat/repo/actions"
|
||||
"code.gitlink.org.cn/Gitlink/gitea_hat.git/routers/hat/repo/explore"
|
||||
"code.gitlink.org.cn/Gitlink/gitea_hat.git/routers/hat/user"
|
||||
"github.com/go-chi/cors"
|
||||
)
|
||||
|
|
@ -121,10 +122,32 @@ func Routers() *web.Route {
|
|||
m.Group("", func() {
|
||||
m.Get("/version", misc.Version)
|
||||
m.Post("/create_pr_version", bind(gitea_api.PullRequestPayload{}), repo.CreatePrVersion)
|
||||
m.Post("/create_repo_size_limit_hook", bind(gitea_api.RepositoryPayload{}), repo.CreateRepoSizeLimitHook)
|
||||
m.Post("/update_hook_arm", bind(gitea_api.RepositoryPayload{}), repo.UpdateHookArm)
|
||||
m.Post("/update_repo_size_limit_hook", bind(gitea_api.RepositoryPayload{}), repo.UpdateRepoSizeLimitHook)
|
||||
m.Group("/repos", func() {
|
||||
if setting.Indexer.RepoIndexerEnabled {
|
||||
m.Get("/explore/code", explore.Code)
|
||||
}
|
||||
m.Group("/{username}/{reponame}", func() {
|
||||
m.Get("/code", context.ReferencesGitRepo(), reqRepoReader(unit.TypeCode), repo.Code)
|
||||
m.Combo("").Delete(reqToken(), reqOwner(), repo.Delete)
|
||||
m.Group("/actions", func() {
|
||||
m.Group("/secrets", func() {
|
||||
m.Get("", reqToken(), reqAdmin(), actions.ListActionsSecrets)
|
||||
m.Combo("/{secretname}").
|
||||
Delete(reqToken(), reqAdmin(), actions.DeleteActionsSecret).
|
||||
Post(reqToken(), reqAdmin(), bind(hat_api.CreateSecretOption{}), actions.CreateActionsSecret).
|
||||
Put(reqToken(), reqAdmin(), bind(hat_api.UpdateSecretOption{}), actions.UpdateActionsSecret)
|
||||
})
|
||||
m.Group("/variables", func() {
|
||||
m.Get("", reqToken(), reqAdmin(), actions.ListVariables)
|
||||
m.Combo("/{variablename}").
|
||||
Get(reqToken(), reqAdmin(), actions.GetVariable).
|
||||
Delete(reqToken(), reqAdmin(), actions.DeleteVariable).
|
||||
Post(reqToken(), reqAdmin(), bind(hat_api.CreateVariableOption{}), actions.CreateVariable).
|
||||
Put(reqToken(), reqAdmin(), bind(hat_api.UpdateVariableOption{}), actions.UpdateVariable)
|
||||
})
|
||||
m.Get("", context.ReferencesGitRepo(), actions.ListActions)
|
||||
m.Post("/disable", reqAdmin(), actions.DisableWorkflowFile)
|
||||
m.Post("/enable", reqAdmin(), actions.EnableWorkflowFile)
|
||||
|
|
@ -132,6 +155,7 @@ func Routers() *web.Route {
|
|||
m.Post("/runs/{run}/jobs/{job}/rerun", reqRepoWriter(unit_model.TypeActions), actions.Rerun)
|
||||
m.Get("/runs/{run}/jobs/{job}/logs", actions.Logs)
|
||||
m.Post("/runs/{run}/rerun", reqRepoWriter(unit_model.TypeActions), actions.Rerun)
|
||||
m.Post("/runs/{run}/cancel", reqRepoWriter(unit_model.TypeActions), actions.Cancel)
|
||||
m.Post("/runs", context.ReferencesGitRepo(), reqRepoWriter(unit_model.TypeActions), actions.Run)
|
||||
}, reqRepoReader(unit_model.TypeActions))
|
||||
m.Post("/transfer", reqOwner(), bind(gitea_api.TransferRepoOption{}), repo.Transfer)
|
||||
|
|
@ -156,6 +180,7 @@ func Routers() *web.Route {
|
|||
}, reqRepoReader(unit_model.TypeCode), context.ReferencesGitRepo(true))
|
||||
m.Group("/wiki", func() {
|
||||
m.Get("/page_names", repo.ListWikiPageNames)
|
||||
m.Get("/revisions/{pageName}/{revision}", repo.ShowRevision)
|
||||
})
|
||||
m.Group("/readme", func() {
|
||||
m.Get("", repo.GetReadmeContents)
|
||||
|
|
@ -184,6 +209,7 @@ func Routers() *web.Route {
|
|||
m.Get("", repo.ListPullRequestVersions)
|
||||
m.Get("/{versionId}/diff", context.ReferencesGitRepo(), repo.GetPullRequestVersionDiff)
|
||||
})
|
||||
m.Get("/change_status", repo.ChangePullRequestCloseStatus)
|
||||
})
|
||||
}, mustAllowPulls, reqRepoReader(unit_model.TypeCode), context.ReferencesGitRepo())
|
||||
m.Group("/releases", func() {
|
||||
|
|
@ -235,11 +261,29 @@ func Routers() *web.Route {
|
|||
if setting.Service.EnableUserHeatmap {
|
||||
m.Get("/heatmap", user.GetUserHeatmapData)
|
||||
}
|
||||
m.Get("/code", user.Code)
|
||||
}, context_service.UserAssignmentAPI())
|
||||
})
|
||||
m.Post("/orgs", reqToken(), bind(gitea_api.CreateOrgOption{}), org.Create)
|
||||
m.Group("/orgs/{org}", func() {
|
||||
m.Combo("").Patch(reqToken(), reqOrgOwnership(), bind(hat_api.EditOrgOption{}), org.Edit)
|
||||
m.Group("/actions", func() {
|
||||
m.Group("/secrets", func() {
|
||||
m.Get("", reqToken(), reqAdmin(), org.ListActionsSecrets)
|
||||
m.Combo("/{secretname}").
|
||||
Delete(reqToken(), reqAdmin(), org.DeleteActionsSecret).
|
||||
Post(reqToken(), reqAdmin(), bind(hat_api.CreateSecretOption{}), org.CreateActionsSecret).
|
||||
Put(reqToken(), reqAdmin(), bind(hat_api.UpdateSecretOption{}), org.UpdateActionsSecret)
|
||||
})
|
||||
m.Group("/variables", func() {
|
||||
m.Get("", reqToken(), reqOrgOwnership(), org.ListVariables)
|
||||
m.Combo("/{variablename}").
|
||||
Get(reqToken(), reqOrgOwnership(), org.GetVariable).
|
||||
Delete(reqToken(), reqOrgOwnership(), org.DeleteVariable).
|
||||
Post(reqToken(), reqOrgOwnership(), bind(hat_api.CreateVariableOption{}), org.CreateVariable).
|
||||
Put(reqToken(), reqOrgOwnership(), bind(hat_api.UpdateVariableOption{}), org.UpdateVariable)
|
||||
})
|
||||
})
|
||||
}, orgAssignment(true))
|
||||
|
||||
m.Group("/teams/{teamid}", func() {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,261 @@
|
|||
package org
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
|
||||
"code.gitea.io/gitea/modules/context"
|
||||
api "code.gitea.io/gitea/modules/structs"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
"code.gitea.io/gitea/modules/web"
|
||||
"code.gitea.io/gitea/routers/api/v1/utils"
|
||||
|
||||
hat_actions_model "code.gitlink.org.cn/Gitlink/gitea_hat.git/models/actions"
|
||||
hat_api "code.gitlink.org.cn/Gitlink/gitea_hat.git/modules/structs"
|
||||
hat_actions_service "code.gitlink.org.cn/Gitlink/gitea_hat.git/services/actions"
|
||||
)
|
||||
|
||||
func DeleteActionsSecret(ctx *context.APIContext) {
|
||||
if err := hat_actions_service.DeleteSecretByName(ctx, ctx.Org.Organization.ID, 0, ctx.Params("secretname")); err != nil {
|
||||
if errors.Is(err, util.ErrInvalidArgument) {
|
||||
ctx.Error(http.StatusBadRequest, "DeleteSecretByName", err)
|
||||
} else if errors.Is(err, util.ErrNotExist) {
|
||||
ctx.NotFound(err)
|
||||
} else {
|
||||
ctx.InternalServerError(err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
ctx.Status(http.StatusNoContent)
|
||||
}
|
||||
|
||||
func CreateActionsSecret(ctx *context.APIContext) {
|
||||
opt := web.GetForm(ctx).(*hat_api.CreateSecretOption)
|
||||
ownerID := ctx.Org.Organization.ID
|
||||
secretName := ctx.Params("secretname")
|
||||
|
||||
v, err := hat_actions_service.GetSecret(ctx, hat_actions_model.FindSecretsOptions{
|
||||
OwnerID: ownerID,
|
||||
Name: secretName,
|
||||
})
|
||||
if err != nil && !errors.Is(err, util.ErrNotExist) {
|
||||
ctx.InternalServerError(err)
|
||||
return
|
||||
}
|
||||
if v != nil && v.ID > 0 {
|
||||
ctx.Error(http.StatusConflict, "", util.NewAlreadyExistErrorf("secret name %s already exists", secretName))
|
||||
return
|
||||
}
|
||||
if _, err := hat_actions_service.CreateSecret(ctx, ownerID, 0, secretName, opt.Data); err != nil {
|
||||
if errors.Is(err, util.ErrInvalidArgument) {
|
||||
ctx.Error(http.StatusBadRequest, "CreateSecret", err)
|
||||
} else {
|
||||
ctx.InternalServerError(err)
|
||||
}
|
||||
return
|
||||
}
|
||||
ctx.Status(http.StatusCreated)
|
||||
}
|
||||
|
||||
func UpdateActionsSecret(ctx *context.APIContext) {
|
||||
opt := web.GetForm(ctx).(*hat_api.UpdateSecretOption)
|
||||
|
||||
_, err := hat_actions_service.GetSecret(ctx, hat_actions_model.FindSecretsOptions{
|
||||
OwnerID: ctx.Org.Organization.ID,
|
||||
Name: ctx.Params("secretname"),
|
||||
})
|
||||
if err != nil {
|
||||
if errors.Is(err, util.ErrNotExist) {
|
||||
ctx.Error(http.StatusNotFound, "GetSecret", err)
|
||||
} else {
|
||||
ctx.InternalServerError(err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if opt.Name == "" {
|
||||
opt.Name = ctx.Params("secretname")
|
||||
}
|
||||
|
||||
_, err = hat_actions_service.UpdateSecret(ctx, ctx.Org.Organization.ID, 0, opt.Name, opt.Data)
|
||||
if err != nil {
|
||||
if errors.Is(err, util.ErrInvalidArgument) {
|
||||
ctx.Error(http.StatusBadRequest, "UpdateSecret", err)
|
||||
} else {
|
||||
ctx.InternalServerError(err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
ctx.Status(http.StatusNoContent)
|
||||
|
||||
}
|
||||
|
||||
func ListActionsSecrets(ctx *context.APIContext) {
|
||||
opts := &hat_actions_model.FindSecretsOptions{
|
||||
OwnerID: ctx.Org.Organization.ID,
|
||||
ListOptions: utils.GetListOptions(ctx),
|
||||
}
|
||||
|
||||
count, err := hat_actions_model.CountSecrets(ctx, opts)
|
||||
if err != nil {
|
||||
ctx.InternalServerError(err)
|
||||
return
|
||||
}
|
||||
|
||||
secrets, err := hat_actions_model.FindSecrets(ctx, *opts)
|
||||
if err != nil {
|
||||
ctx.InternalServerError(err)
|
||||
return
|
||||
}
|
||||
|
||||
apiSecrets := make([]*api.Secret, len(secrets))
|
||||
for k, v := range secrets {
|
||||
apiSecrets[k] = &api.Secret{
|
||||
Name: v.Name,
|
||||
Created: v.CreatedUnix.AsTime(),
|
||||
}
|
||||
}
|
||||
|
||||
ctx.SetTotalCountHeader(count)
|
||||
ctx.JSON(http.StatusOK, apiSecrets)
|
||||
}
|
||||
|
||||
func GetVariable(ctx *context.APIContext) {
|
||||
v, err := hat_actions_service.GetVariable(ctx, hat_actions_model.FindVariablesOpts{
|
||||
OwnerID: ctx.Org.Organization.ID,
|
||||
Name: ctx.Params("variablename"),
|
||||
})
|
||||
if err != nil {
|
||||
if errors.Is(err, util.ErrNotExist) {
|
||||
ctx.NotFound(err)
|
||||
} else {
|
||||
ctx.InternalServerError(err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
variable := &hat_api.ActionVariable{
|
||||
OwnerID: v.OwnerID,
|
||||
RepoID: v.RepoID,
|
||||
Name: v.Name,
|
||||
Data: v.Data,
|
||||
}
|
||||
|
||||
ctx.JSON(http.StatusOK, variable)
|
||||
}
|
||||
|
||||
func DeleteVariable(ctx *context.APIContext) {
|
||||
if err := hat_actions_service.DeleteVariableByName(ctx, ctx.Org.Organization.ID, 0, ctx.Params("variablename")); err != nil {
|
||||
if errors.Is(err, util.ErrInvalidArgument) {
|
||||
ctx.Error(http.StatusBadRequest, "DeleteVariableByName", err)
|
||||
} else if errors.Is(err, util.ErrNotExist) {
|
||||
ctx.NotFound(err)
|
||||
} else {
|
||||
ctx.InternalServerError(err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
ctx.Status(http.StatusNoContent)
|
||||
}
|
||||
|
||||
func CreateVariable(ctx *context.APIContext) {
|
||||
opt := web.GetForm(ctx).(*hat_api.CreateVariableOption)
|
||||
ownerID := ctx.Org.Organization.ID
|
||||
variableName := ctx.Params("variablename")
|
||||
|
||||
v, err := hat_actions_service.GetVariable(ctx, hat_actions_model.FindVariablesOpts{
|
||||
OwnerID: ownerID,
|
||||
Name: variableName,
|
||||
})
|
||||
if err != nil && !errors.Is(err, util.ErrNotExist) {
|
||||
ctx.InternalServerError(err)
|
||||
return
|
||||
}
|
||||
if v != nil && v.ID > 0 {
|
||||
ctx.Error(http.StatusConflict, "", util.NewAlreadyExistErrorf("variable name %s already exists", variableName))
|
||||
return
|
||||
}
|
||||
|
||||
if _, err := hat_actions_service.CreateVariable(ctx, ownerID, 0, variableName, opt.Value); err != nil {
|
||||
if errors.Is(err, util.ErrInvalidArgument) {
|
||||
ctx.Error(http.StatusBadRequest, "CreateVariable", err)
|
||||
} else {
|
||||
ctx.InternalServerError(err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
ctx.Status(http.StatusCreated)
|
||||
}
|
||||
|
||||
func UpdateVariable(ctx *context.APIContext) {
|
||||
opt := web.GetForm(ctx).(*hat_api.UpdateVariableOption)
|
||||
|
||||
v, err := hat_actions_service.GetVariable(ctx, hat_actions_model.FindVariablesOpts{
|
||||
OwnerID: ctx.Org.Organization.ID,
|
||||
Name: ctx.Params("variablename"),
|
||||
})
|
||||
if err != nil {
|
||||
if errors.Is(err, util.ErrNotExist) {
|
||||
ctx.Error(http.StatusNotFound, "GetVariable", err)
|
||||
} else {
|
||||
ctx.InternalServerError(err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if opt.Name == "" {
|
||||
opt.Name = ctx.Params("variablename")
|
||||
}
|
||||
|
||||
v.Name = opt.Name
|
||||
v.Data = opt.Value
|
||||
|
||||
if _, err := hat_actions_service.UpdateVariableNameData(ctx, v); err != nil {
|
||||
if errors.Is(err, util.ErrInvalidArgument) {
|
||||
ctx.Error(http.StatusBadRequest, "UpdateVariableNameData", err)
|
||||
} else {
|
||||
ctx.InternalServerError(err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
ctx.Status(http.StatusNoContent)
|
||||
}
|
||||
|
||||
func ListVariables(ctx *context.APIContext) {
|
||||
opts := hat_actions_model.FindVariablesOpts{
|
||||
OwnerID: ctx.Org.Organization.ID,
|
||||
ListOptions: utils.GetListOptions(ctx),
|
||||
}
|
||||
|
||||
vars, err := hat_actions_model.FindVariables(ctx, opts)
|
||||
|
||||
if err != nil {
|
||||
ctx.InternalServerError(err)
|
||||
return
|
||||
}
|
||||
|
||||
count, err := hat_actions_model.CountVariables(ctx, opts)
|
||||
|
||||
if err != nil {
|
||||
ctx.InternalServerError(err)
|
||||
return
|
||||
}
|
||||
|
||||
variables := make([]*hat_api.ActionVariable, len(vars))
|
||||
for i, v := range vars {
|
||||
variables[i] = &hat_api.ActionVariable{
|
||||
OwnerID: v.OwnerID,
|
||||
RepoID: v.RepoID,
|
||||
Name: v.Name,
|
||||
Data: v.Data,
|
||||
}
|
||||
}
|
||||
|
||||
ctx.SetTotalCountHeader(count)
|
||||
ctx.JSON(http.StatusOK, variables)
|
||||
}
|
||||
|
|
@ -8,6 +8,8 @@ import (
|
|||
"strings"
|
||||
"time"
|
||||
|
||||
"code.gitea.io/gitea/modules/timeutil"
|
||||
|
||||
stdCtx "context"
|
||||
|
||||
actions_model "code.gitea.io/gitea/models/actions"
|
||||
|
|
@ -21,17 +23,265 @@ import (
|
|||
"code.gitea.io/gitea/modules/container"
|
||||
"code.gitea.io/gitea/modules/context"
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
api "code.gitea.io/gitea/modules/structs"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
"code.gitea.io/gitea/modules/web"
|
||||
"code.gitea.io/gitea/routers/api/v1/utils"
|
||||
"code.gitea.io/gitea/routers/web/repo"
|
||||
actions_service "code.gitea.io/gitea/services/actions"
|
||||
"code.gitea.io/gitea/services/convert"
|
||||
hat_actions_model "code.gitlink.org.cn/Gitlink/gitea_hat.git/models/actions"
|
||||
hat_api "code.gitlink.org.cn/Gitlink/gitea_hat.git/modules/structs"
|
||||
hat_actions_service "code.gitlink.org.cn/Gitlink/gitea_hat.git/services/actions"
|
||||
jobparser "github.com/nektos/act/pkg/jobparser"
|
||||
"github.com/nektos/act/pkg/model"
|
||||
"xorm.io/builder"
|
||||
)
|
||||
|
||||
func DeleteActionsSecret(ctx *context.APIContext) {
|
||||
if err := hat_actions_service.DeleteSecretByName(ctx, 0, ctx.Repo.Repository.ID, ctx.Params("secretname")); err != nil {
|
||||
if errors.Is(err, util.ErrInvalidArgument) {
|
||||
ctx.Error(http.StatusBadRequest, "DeleteSecretByName", err)
|
||||
} else if errors.Is(err, util.ErrNotExist) {
|
||||
ctx.NotFound(err)
|
||||
} else {
|
||||
ctx.InternalServerError(err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
ctx.Status(http.StatusNoContent)
|
||||
}
|
||||
|
||||
func CreateActionsSecret(ctx *context.APIContext) {
|
||||
opt := web.GetForm(ctx).(*hat_api.CreateSecretOption)
|
||||
repoID := ctx.Repo.Repository.ID
|
||||
secretName := ctx.Params("secretname")
|
||||
|
||||
v, err := hat_actions_service.GetSecret(ctx, hat_actions_model.FindSecretsOptions{
|
||||
RepoID: repoID,
|
||||
Name: secretName,
|
||||
})
|
||||
if err != nil && !errors.Is(err, util.ErrNotExist) {
|
||||
ctx.InternalServerError(err)
|
||||
return
|
||||
}
|
||||
if v != nil && v.ID > 0 {
|
||||
ctx.Error(http.StatusConflict, "", util.NewAlreadyExistErrorf("secret name %s already exists", secretName))
|
||||
return
|
||||
}
|
||||
if _, err := hat_actions_service.CreateSecret(ctx, 0, repoID, secretName, opt.Data); err != nil {
|
||||
if errors.Is(err, util.ErrInvalidArgument) {
|
||||
ctx.Error(http.StatusBadRequest, "CreateSecret", err)
|
||||
} else {
|
||||
ctx.InternalServerError(err)
|
||||
}
|
||||
return
|
||||
}
|
||||
ctx.Status(http.StatusCreated)
|
||||
}
|
||||
|
||||
func UpdateActionsSecret(ctx *context.APIContext) {
|
||||
opt := web.GetForm(ctx).(*hat_api.UpdateSecretOption)
|
||||
|
||||
_, err := hat_actions_service.GetSecret(ctx, hat_actions_model.FindSecretsOptions{
|
||||
RepoID: ctx.Repo.Repository.ID,
|
||||
Name: ctx.Params("secretname"),
|
||||
})
|
||||
if err != nil {
|
||||
if errors.Is(err, util.ErrNotExist) {
|
||||
ctx.Error(http.StatusNotFound, "GetSecret", err)
|
||||
} else {
|
||||
ctx.InternalServerError(err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if opt.Name == "" {
|
||||
opt.Name = ctx.Params("secretname")
|
||||
}
|
||||
|
||||
_, err = hat_actions_service.UpdateSecret(ctx, 0, ctx.Repo.Repository.ID, opt.Name, opt.Data)
|
||||
if err != nil {
|
||||
if errors.Is(err, util.ErrInvalidArgument) {
|
||||
ctx.Error(http.StatusBadRequest, "UpdateSecret", err)
|
||||
} else {
|
||||
ctx.InternalServerError(err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
ctx.Status(http.StatusNoContent)
|
||||
|
||||
}
|
||||
|
||||
func ListActionsSecrets(ctx *context.APIContext) {
|
||||
opts := &hat_actions_model.FindSecretsOptions{
|
||||
RepoID: ctx.Repo.Repository.ID,
|
||||
ListOptions: utils.GetListOptions(ctx),
|
||||
}
|
||||
|
||||
count, err := hat_actions_model.CountSecrets(ctx, opts)
|
||||
if err != nil {
|
||||
ctx.InternalServerError(err)
|
||||
return
|
||||
}
|
||||
|
||||
secrets, err := hat_actions_model.FindSecrets(ctx, *opts)
|
||||
if err != nil {
|
||||
ctx.InternalServerError(err)
|
||||
return
|
||||
}
|
||||
|
||||
apiSecrets := make([]*api.Secret, len(secrets))
|
||||
for k, v := range secrets {
|
||||
apiSecrets[k] = &api.Secret{
|
||||
Name: v.Name,
|
||||
Created: v.CreatedUnix.AsTime(),
|
||||
}
|
||||
}
|
||||
|
||||
ctx.SetTotalCountHeader(count)
|
||||
ctx.JSON(http.StatusOK, apiSecrets)
|
||||
}
|
||||
|
||||
func GetVariable(ctx *context.APIContext) {
|
||||
v, err := hat_actions_service.GetVariable(ctx, hat_actions_model.FindVariablesOpts{
|
||||
RepoID: ctx.Repo.Repository.ID,
|
||||
Name: ctx.Params("variablename"),
|
||||
})
|
||||
if err != nil {
|
||||
if errors.Is(err, util.ErrNotExist) {
|
||||
ctx.NotFound(err)
|
||||
} else {
|
||||
ctx.InternalServerError(err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
variable := &hat_api.ActionVariable{
|
||||
OwnerID: v.OwnerID,
|
||||
RepoID: v.RepoID,
|
||||
Name: v.Name,
|
||||
Data: v.Data,
|
||||
}
|
||||
|
||||
ctx.JSON(http.StatusOK, variable)
|
||||
}
|
||||
|
||||
func DeleteVariable(ctx *context.APIContext) {
|
||||
if err := hat_actions_service.DeleteVariableByName(ctx, 0, ctx.Repo.Repository.ID, ctx.Params("variablename")); err != nil {
|
||||
if errors.Is(err, util.ErrInvalidArgument) {
|
||||
ctx.Error(http.StatusBadRequest, "DeleteVariableByName", err)
|
||||
} else if errors.Is(err, util.ErrNotExist) {
|
||||
ctx.NotFound(err)
|
||||
} else {
|
||||
ctx.InternalServerError(err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
ctx.Status(http.StatusNoContent)
|
||||
}
|
||||
|
||||
func CreateVariable(ctx *context.APIContext) {
|
||||
opt := web.GetForm(ctx).(*hat_api.CreateVariableOption)
|
||||
repoID := ctx.Repo.Repository.ID
|
||||
variableName := ctx.Params("variablename")
|
||||
|
||||
v, err := hat_actions_service.GetVariable(ctx, hat_actions_model.FindVariablesOpts{
|
||||
RepoID: repoID,
|
||||
Name: variableName,
|
||||
})
|
||||
if err != nil && !errors.Is(err, util.ErrNotExist) {
|
||||
ctx.InternalServerError(err)
|
||||
return
|
||||
}
|
||||
if v != nil && v.ID > 0 {
|
||||
ctx.Error(http.StatusConflict, "", util.NewAlreadyExistErrorf("variable name %s already exists", variableName))
|
||||
return
|
||||
}
|
||||
|
||||
if _, err := hat_actions_service.CreateVariable(ctx, 0, repoID, variableName, opt.Value); err != nil {
|
||||
if errors.Is(err, util.ErrInvalidArgument) {
|
||||
ctx.Error(http.StatusBadRequest, "CreateVariable", err)
|
||||
} else {
|
||||
ctx.InternalServerError(err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
ctx.Status(http.StatusCreated)
|
||||
}
|
||||
|
||||
func UpdateVariable(ctx *context.APIContext) {
|
||||
opt := web.GetForm(ctx).(*hat_api.UpdateVariableOption)
|
||||
|
||||
v, err := hat_actions_service.GetVariable(ctx, hat_actions_model.FindVariablesOpts{
|
||||
RepoID: ctx.Repo.Repository.ID,
|
||||
Name: ctx.Params("variablename"),
|
||||
})
|
||||
if err != nil {
|
||||
if errors.Is(err, util.ErrNotExist) {
|
||||
ctx.Error(http.StatusNotFound, "GetVariable", err)
|
||||
} else {
|
||||
ctx.InternalServerError(err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if opt.Name == "" {
|
||||
opt.Name = ctx.Params("variablename")
|
||||
}
|
||||
|
||||
v.Name = opt.Name
|
||||
v.Data = opt.Value
|
||||
|
||||
if _, err := hat_actions_service.UpdateVariableNameData(ctx, v); err != nil {
|
||||
if errors.Is(err, util.ErrInvalidArgument) {
|
||||
ctx.Error(http.StatusBadRequest, "UpdateVariableNameData", err)
|
||||
} else {
|
||||
ctx.InternalServerError(err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
ctx.Status(http.StatusNoContent)
|
||||
}
|
||||
|
||||
func ListVariables(ctx *context.APIContext) {
|
||||
opts := hat_actions_model.FindVariablesOpts{
|
||||
RepoID: ctx.Repo.Repository.ID,
|
||||
ListOptions: utils.GetListOptions(ctx),
|
||||
}
|
||||
vars, err := hat_actions_model.FindVariables(ctx, opts)
|
||||
|
||||
if err != nil {
|
||||
ctx.InternalServerError(err)
|
||||
return
|
||||
}
|
||||
|
||||
count, err := hat_actions_model.CountVariables(ctx, opts)
|
||||
|
||||
if err != nil {
|
||||
ctx.InternalServerError(err)
|
||||
return
|
||||
}
|
||||
|
||||
variables := make([]*hat_api.ActionVariable, len(vars))
|
||||
for i, v := range vars {
|
||||
variables[i] = &hat_api.ActionVariable{
|
||||
OwnerID: v.OwnerID,
|
||||
RepoID: v.RepoID,
|
||||
Name: v.Name,
|
||||
Data: v.Data,
|
||||
}
|
||||
}
|
||||
|
||||
ctx.SetTotalCountHeader(count)
|
||||
ctx.JSON(http.StatusOK, variables)
|
||||
}
|
||||
|
||||
func Run(ctx *context.APIContext) {
|
||||
workflow := ctx.FormString("workflow")
|
||||
ref := ctx.FormString("ref")
|
||||
|
|
@ -131,6 +381,11 @@ type Workflow struct {
|
|||
ErrMsg string
|
||||
}
|
||||
|
||||
type DetailStatus struct {
|
||||
RunID int64
|
||||
Status []actions_model.Status
|
||||
}
|
||||
|
||||
type ResponseAction struct {
|
||||
Workflows []Workflow
|
||||
CurWorkflow string
|
||||
|
|
@ -143,6 +398,7 @@ type ResponseAction struct {
|
|||
Runs actions_model.RunList
|
||||
Actors []*user.User
|
||||
StatusInfoList []actions_model.StatusInfo
|
||||
DetailStatusList []DetailStatus
|
||||
}
|
||||
|
||||
func ListActions(ctx *context.APIContext) {
|
||||
|
|
@ -261,7 +517,20 @@ func ListActions(ctx *context.APIContext) {
|
|||
}
|
||||
|
||||
for _, run := range runs {
|
||||
var detailStatuses []actions_model.Status
|
||||
jobs, err := actions_model.GetRunJobsByRunID(ctx, run.ID)
|
||||
if err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "actions_model.GetRunJobsByRunID", err.Error())
|
||||
return
|
||||
}
|
||||
for _, j := range jobs {
|
||||
detailStatuses = append(detailStatuses, j.Status)
|
||||
}
|
||||
run.Repo = ctx.Repo.Repository
|
||||
responseAction.DetailStatusList = append(responseAction.DetailStatusList, DetailStatus{
|
||||
RunID: run.ID,
|
||||
Status: detailStatuses,
|
||||
})
|
||||
}
|
||||
|
||||
if err := runs.LoadTriggerUser(ctx); err != nil {
|
||||
|
|
@ -684,3 +953,44 @@ func disableOrEnableWorkflowFile(ctx *context.APIContext, isEnable bool) {
|
|||
|
||||
ctx.Status(http.StatusNoContent)
|
||||
}
|
||||
|
||||
func Cancel(ctx *context.APIContext) {
|
||||
runIndex := ctx.ParamsInt64("run")
|
||||
|
||||
_, jobs := getRunJobs(ctx, runIndex, -1)
|
||||
if ctx.Written() {
|
||||
return
|
||||
}
|
||||
|
||||
if err := db.WithTx(ctx, func(ctx stdCtx.Context) error {
|
||||
for _, job := range jobs {
|
||||
status := job.Status
|
||||
if status.IsDone() {
|
||||
continue
|
||||
}
|
||||
if job.TaskID == 0 {
|
||||
job.Status = actions_model.StatusCancelled
|
||||
job.Stopped = timeutil.TimeStampNow()
|
||||
n, err := actions_model.UpdateRunJob(ctx, job, builder.Eq{"task_id": 0}, "status", "stopped")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if n == 0 {
|
||||
return fmt.Errorf("job has changed, try again")
|
||||
}
|
||||
continue
|
||||
}
|
||||
if err := actions_model.StopTask(ctx, job.TaskID, actions_model.StatusCancelled); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}); err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "runs Cancel", err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
actions_service.CreateCommitStatus(ctx, jobs...)
|
||||
|
||||
ctx.JSON(http.StatusOK, struct{}{})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,119 @@
|
|||
package explore
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
"code.gitea.io/gitea/modules/context"
|
||||
code_indexer "code.gitea.io/gitea/modules/indexer/code"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
)
|
||||
|
||||
func Code(ctx *context.APIContext) {
|
||||
language := ctx.FormString("language")
|
||||
keyword := ctx.FormString("keyword")
|
||||
queryType := ctx.FormString("query_type")
|
||||
isMatch := queryType == "match"
|
||||
|
||||
page := ctx.FormInt("page")
|
||||
if page <= 0 {
|
||||
page = 1
|
||||
}
|
||||
|
||||
var (
|
||||
repoIDs []int64
|
||||
err error
|
||||
isAdmin bool
|
||||
)
|
||||
|
||||
if ctx.Doer != nil {
|
||||
isAdmin = ctx.Doer.IsAdmin
|
||||
}
|
||||
|
||||
if ctx.Doer == nil || !isAdmin {
|
||||
repoIDs, err = repo_model.FindUserCodeAccessibleRepoIDs(ctx, ctx.Doer)
|
||||
if err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "FindUserCodeAccessibleRepoIDs", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
total int
|
||||
searchResults []*code_indexer.Result
|
||||
searchResultLanguages []*code_indexer.SearchResultLanguages
|
||||
)
|
||||
|
||||
var codeIndexerUnavailable bool
|
||||
var repoMaps map[int64]*repo_model.Repository
|
||||
if (len(repoIDs) > 0) || isAdmin {
|
||||
total, searchResults, searchResultLanguages, err = code_indexer.PerformSearch(ctx, repoIDs, language, keyword, page, setting.UI.RepoSearchPagingNum, isMatch)
|
||||
if err != nil {
|
||||
if code_indexer.IsAvailable(ctx) {
|
||||
ctx.Error(http.StatusInternalServerError, "SearchResults", err)
|
||||
return
|
||||
}
|
||||
codeIndexerUnavailable = true
|
||||
} else {
|
||||
codeIndexerUnavailable = !code_indexer.IsAvailable(ctx)
|
||||
}
|
||||
|
||||
loadRepoIDs := make([]int64, 0, len(searchResults))
|
||||
for _, result := range searchResults {
|
||||
var find bool
|
||||
for _, id := range loadRepoIDs {
|
||||
if id == result.RepoID {
|
||||
find = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !find {
|
||||
loadRepoIDs = append(loadRepoIDs, result.RepoID)
|
||||
}
|
||||
}
|
||||
|
||||
repoMaps, err := repo_model.GetRepositoriesMapByIDs(loadRepoIDs)
|
||||
if err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "GetRepositoriesMapByIDs", err)
|
||||
return
|
||||
}
|
||||
|
||||
repoMaps = repoMaps
|
||||
|
||||
if len(loadRepoIDs) != len(repoMaps) {
|
||||
// Remove deleted repos from search results
|
||||
cleanedSearchResults := make([]*code_indexer.Result, 0, len(repoMaps))
|
||||
for _, sr := range searchResults {
|
||||
if _, found := repoMaps[sr.RepoID]; found {
|
||||
cleanedSearchResults = append(cleanedSearchResults, sr)
|
||||
}
|
||||
}
|
||||
|
||||
searchResults = cleanedSearchResults
|
||||
}
|
||||
}
|
||||
|
||||
pageCount := int(math.Ceil(float64(total) / float64(setting.UI.RepoSearchPagingNum)))
|
||||
ctx.Resp.Header().Set("X-Page", strconv.Itoa(page))
|
||||
ctx.Resp.Header().Set("X-PerPage", strconv.Itoa(setting.UI.RepoSearchPagingNum))
|
||||
ctx.Resp.Header().Set("X-Total", strconv.FormatInt(int64(total), 10))
|
||||
ctx.Resp.Header().Set("X-PageCount", strconv.Itoa(pageCount))
|
||||
ctx.Resp.Header().Set("X-HasMore", strconv.FormatBool(page < pageCount))
|
||||
|
||||
ctx.SetLinkHeader(int(total), setting.UI.RepoSearchPagingNum)
|
||||
ctx.Resp.Header().Set("X-Total-Count", fmt.Sprintf("%d", total))
|
||||
ctx.JSON(http.StatusOK, struct {
|
||||
CodeIndexerUnavailable bool
|
||||
RepoMaps map[int64]*repo_model.Repository
|
||||
SearchResults []*code_indexer.Result
|
||||
SearchResultLanguages []*code_indexer.SearchResultLanguages
|
||||
}{
|
||||
CodeIndexerUnavailable: codeIndexerUnavailable,
|
||||
RepoMaps: repoMaps,
|
||||
SearchResults: searchResults,
|
||||
SearchResultLanguages: searchResultLanguages,
|
||||
})
|
||||
}
|
||||
|
|
@ -35,6 +35,41 @@ import (
|
|||
hat_pull_service "code.gitlink.org.cn/Gitlink/gitea_hat.git/services/pull"
|
||||
)
|
||||
|
||||
func ChangePullRequestCloseStatus(ctx *context.APIContext) {
|
||||
pr, err := issues_model.GetPullRequestByIndex(ctx, ctx.Repo.Repository.ID, ctx.ParamsInt64(":index"))
|
||||
if err != nil {
|
||||
if issues_model.IsErrPullRequestNotExist(err) {
|
||||
ctx.NotFound()
|
||||
} else {
|
||||
ctx.Error(http.StatusInternalServerError, "GetPullRequestByIndex", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
changeStatus := ctx.FormBool("is_closed")
|
||||
_, err = issues_model.ChangeIssueStatus(ctx, pr.Issue, ctx.Doer, changeStatus)
|
||||
if err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "ChangeIssueStatus", err)
|
||||
}
|
||||
|
||||
if err = pull_service.TestPatch(pr); err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "TestPatch", err)
|
||||
}
|
||||
|
||||
// Update Commit Divergence
|
||||
divergence, err := pull_service.GetDiverging(ctx, pr)
|
||||
if err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "GetDiverging", err)
|
||||
}
|
||||
pr.CommitsAhead = divergence.Ahead
|
||||
pr.CommitsBehind = divergence.Behind
|
||||
|
||||
if err := pr.UpdateColsIfNotMerged(ctx, "merge_base", "status", "conflicted_files", "changed_protected_files", "base_branch", "commits_ahead", "commits_behind"); err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "UpdateColsIfNotMerged", err)
|
||||
}
|
||||
|
||||
ctx.Status(http.StatusNoContent)
|
||||
}
|
||||
|
||||
func CreatePrVersion(ctx *context.APIContext) {
|
||||
form := web.GetForm(ctx).(*gitea_api.PullRequestPayload)
|
||||
hat_pull_service.AddToTaskQueue(&issues_model.PullRequest{ID: form.PullRequest.ID}, string(form.Action))
|
||||
|
|
|
|||
|
|
@ -0,0 +1,68 @@
|
|||
package repo
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
"code.gitea.io/gitea/modules/context"
|
||||
code_indexer "code.gitea.io/gitea/modules/indexer/code"
|
||||
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
)
|
||||
|
||||
func Code(ctx *context.APIContext) {
|
||||
language := ctx.FormString("language")
|
||||
keyword := ctx.FormString("keyword")
|
||||
queryType := ctx.FormString("query_type")
|
||||
isMatch := queryType == "match"
|
||||
|
||||
page := ctx.FormInt("page")
|
||||
if page <= 0 {
|
||||
page = 1
|
||||
}
|
||||
|
||||
var (
|
||||
total int
|
||||
searchResults []*code_indexer.Result
|
||||
searchResultLanguages []*code_indexer.SearchResultLanguages
|
||||
)
|
||||
|
||||
var codeIndexerUnavailable bool
|
||||
var repoMaps map[int64]*repo_model.Repository
|
||||
|
||||
total, searchResults, searchResultLanguages, err := code_indexer.PerformSearch(ctx, []int64{ctx.Repo.Repository.ID},
|
||||
language, keyword, page, setting.UI.RepoSearchPagingNum, isMatch)
|
||||
if err != nil {
|
||||
if code_indexer.IsAvailable(ctx) {
|
||||
ctx.Error(http.StatusInternalServerError, "SearchResults", err)
|
||||
return
|
||||
}
|
||||
codeIndexerUnavailable = true
|
||||
} else {
|
||||
codeIndexerUnavailable = !code_indexer.IsAvailable(ctx)
|
||||
}
|
||||
|
||||
pageCount := int(math.Ceil(float64(total) / float64(setting.UI.RepoSearchPagingNum)))
|
||||
ctx.Resp.Header().Set("X-Page", strconv.Itoa(page))
|
||||
ctx.Resp.Header().Set("X-PerPage", strconv.Itoa(setting.UI.RepoSearchPagingNum))
|
||||
ctx.Resp.Header().Set("X-Total", strconv.FormatInt(int64(total), 10))
|
||||
ctx.Resp.Header().Set("X-PageCount", strconv.Itoa(pageCount))
|
||||
ctx.Resp.Header().Set("X-HasMore", strconv.FormatBool(page < pageCount))
|
||||
|
||||
ctx.SetLinkHeader(int(total), setting.UI.RepoSearchPagingNum)
|
||||
ctx.Resp.Header().Set("X-Total-Count", fmt.Sprintf("%d", total))
|
||||
ctx.JSON(http.StatusOK, struct {
|
||||
CodeIndexerUnavailable bool
|
||||
RepoMaps map[int64]*repo_model.Repository
|
||||
SearchResults []*code_indexer.Result
|
||||
SearchResultLanguages []*code_indexer.SearchResultLanguages
|
||||
}{
|
||||
CodeIndexerUnavailable: codeIndexerUnavailable,
|
||||
RepoMaps: repoMaps,
|
||||
SearchResults: searchResults,
|
||||
SearchResultLanguages: searchResultLanguages,
|
||||
})
|
||||
}
|
||||
|
|
@ -0,0 +1,385 @@
|
|||
package repo
|
||||
|
||||
import (
|
||||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/context"
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
gitea_api "code.gitea.io/gitea/modules/structs"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
"code.gitea.io/gitea/modules/web"
|
||||
"fmt"
|
||||
"github.com/unknwon/com"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
const (
|
||||
SIZE_LIMIT_SCRIPT_NAME = "size_limit"
|
||||
)
|
||||
|
||||
type UpdateRepoSizeLimit struct {
|
||||
OwnerName string `json:"owner_name"`
|
||||
RepoName string `json:"repo_name"`
|
||||
LimitSize int `json:"limit_size"`
|
||||
}
|
||||
|
||||
func CreateRepoSizeLimitHook(ctx *context.APIContext) {
|
||||
form := web.GetForm(ctx).(*gitea_api.RepositoryPayload)
|
||||
limitMb := ctx.FormInt("limit_size")
|
||||
if limitMb < 30 {
|
||||
limitMb = 512
|
||||
}
|
||||
eventName := string(form.Action)
|
||||
if eventName == "deleted" {
|
||||
ctx.Status(http.StatusOK)
|
||||
return
|
||||
}
|
||||
getAssignmentRepo(ctx, form.Repository.ID)
|
||||
hookDir := filepath.Join(ctx.Repo.Repository.RepoPath(), "hooks")
|
||||
hookName := "pre-receive"
|
||||
//results := make([]string, 0, 10)
|
||||
sizeLimitTpls := []string{
|
||||
fmt.Sprintf("#!/usr/bin/env %s\n\n\nset -o pipefail\n\nreadonly DEFAULT_FILE_MAXSIZE_MB=\"30\" \nreadonly CONFIG_NAME=\"hooks.maxfilesize\"\nreadonly NULLSHA=\"0000000000000000000000000000000000000000\"\nreadonly EXIT_SUCCESS=0\nreadonly EXIT_FAILURE=1\nreadonly DEFAULT_REPO_MAXSIZE_MB=\"1024\" \nreadonly CHECK_FLAG_ON=1\n\n\nPUSH_SIZE_CHECK_FLAG=1\nREPO_MAX_FILE_SIZE=1024\nREPO_MAX_SIZE=%d\nREPO_CURRENT_SIZE=$(du -sk \"${PWD}\" | cut -f1)\nsize_in_mb=$(($REPO_CURRENT_SIZE / 1024))\nstatus=\"$EXIT_SUCCESS\"\n\n# skip this hook entirely if shell check is not open\ncheck_flag=${PUSH_SIZE_CHECK_FLAG}\nif [[ $check_flag != $CHECK_FLAG_ON ]]; then\nexit $EXIT_SUCCESS\nfi\n\n\n#######################################\n# check the file max size limit\n#######################################\n\n# get maximum filesize (from repository-specific config)\nmaxsize_mb=\"${REPO_MAX_FILE_SIZE}\"\n\nif [[ \"$?\" != $EXIT_SUCCESS ]]; then\necho \"failed to get ${CONFIG_NAME} from config\"\nexit \"$EXIT_FAILURE\"\nfi\n\npush_size=\"0\"\n# read lines from stdin (format: \"<oldref> <newref> <refname>\\n\")\nwhile read oldref newref refname; do\n# skip branch deletions\nif [[ \"$newref\" == \"$NULLSHA\" ]]; then\n continue\nfi\n\n# find large objects\n# check all objects from $oldref (possible $NULLSHA) to $newref, but\n# skip all objects that have already been accepted (i.e. are referenced by\n# another branch or tag).\n\nnew_branch_flag=0\nif [[ \"$oldref\" == \"$NULLSHA\" ]]; then\n target=\"$newref\"\n new_branch_flag=1\n echo \"You are creating a new remote branch,openI will check all files in commit history to find oversize files\"\nelse\n target=\"${oldref}..${newref}\"\nfi\nmaxsize=`expr $maxsize_mb \\* 1048576` \n\n# find objects in this push_size\n# print like:\n# 08da8e2ab9ae4095bf94dd71ac913132b880b463 commit 214\n# 43e993b768ede5740e8c65de2ed6edec25053ea1 tree 185\n# 4476971d76569039df7569af1b8d03c288f6b193 blob 20167318 b0417e6593a1.zip\nfiles=\"$(git rev-list --objects \"$target\" | \\\n git cat-file $'--batch-check=%%(objectname) %%(objecttype) %%(objectsize) %%(rest)' | \\\n awk -F ' ' -v maxbytes=\"$maxsize\" 'BEGIN {totalIn=0} {if( $3 > maxbytes && $2 == \"blob\") { totalIn+=$3; print $4} else { totalIn+=$3}} END { printf (\"totalIn=\\t%%s\",totalIn)}' )\"\n \nif [[ \"$?\" != $EXIT_SUCCESS ]]; then\n echo \"failed to check for large files in ref ${refname}\"\n continue\nfi\n\nIFS=$'\\n'\n# rewrite IFS to seperate line in $files\nfor file in $files; do\n # if don't unset IFS,temp_array=(${file}) will get error answer\n \n if [[ ${file} == totalIn=* ]]; then\n\tIFS=$'\\t'\n\ttemp_array=(${file})\n\tpush_size=${temp_array[1]}\n\tcontinue\n fi\n\tunset IFS\n if [[ \"$status\" == $EXIT_SUCCESS ]]; then\n\t\techo -e \"Error: Your push was rejected because it contains files larger than $(numfmt --to=iec \"$maxsize_mb\") Mb\"\n\t\techo \"help document -- ...\"\n\t\techo \"oversize files:\"\n\t\tstatus=\"$EXIT_FAILURE\"\t\n fi\n echo -e \"\\033[31m- ${file}\\033[0m \"\ndone\n\nif [[ \"$status\" != $EXIT_SUCCESS ]]; then\n\texit \"$status\"\nfi\n\ndone\n\n#######################################\n# check the repo max size limit\n#######################################\nif [[ $push_size -eq \"0\" ]]; then\n\texit $EXIT_SUCCESS\nfi\n\n# if create new branch or tag,use count-objects -v to get pack size\nif [[ $new_branch_flag -eq 1 ]]; then\n size_kb=`git count-objects -v | grep 'size-pack' | sed 's/.*\\(size-pack:\\).//'`\n size_pack_kb=`git count-objects -v | grep 'size:' | sed 's/.*\\(size:\\).//'`\n\ttotal_kb=`expr $size_kb + $size_pack_kb`\n\tlet push_size=$total_kb*1024\nfi\n\nsizelimit_mb=\"${REPO_MAX_SIZE}\"\nlet sizelimit_b=$sizelimit_mb*1024*1024\n\n# repo size at here means the size of repo directory in server \nreposize_b=${REPO_CURRENT_SIZE}\n\nlet total=$REPO_CURRENT_SIZE*1024\n\nif [ $total -gt $sizelimit_b ]; then\n echo \"Error: Your push was rejected because the repository size is large than $sizelimit_mb Mb\"\n echo \"Error: 推送被拒绝,文件或目录大小大于 $sizelimit_mb Mb\"\n exit $EXIT_FAILURE\nfi\n\n\nexit $EXIT_SUCCESS", setting.ScriptType, limitMb),
|
||||
fmt.Sprintf(""),
|
||||
fmt.Sprintf(""),
|
||||
}
|
||||
|
||||
oldHookPath := filepath.Join(hookDir, hookName+".d", SIZE_LIMIT_SCRIPT_NAME)
|
||||
if !com.IsExist(oldHookPath) {
|
||||
//if err := checkHookFile(generateHookScriptPath(hookDir, hookName, SIZE_LIMIT_SCRIPT_NAME), sizeLimitTpls[0], results); err != nil {
|
||||
// ctx.Error(http.StatusInternalServerError, "CreateSizeLimitHook checkHookFile", err)
|
||||
// ctx.NotFound()
|
||||
// return
|
||||
//}
|
||||
|
||||
if err := writeHookTpl(generateHookScriptPath(hookDir, hookName, SIZE_LIMIT_SCRIPT_NAME), sizeLimitTpls[0]); err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "CreateSizeLimitHook checkHookFile", err)
|
||||
ctx.NotFound()
|
||||
return
|
||||
}
|
||||
}
|
||||
ctx.Status(http.StatusOK)
|
||||
}
|
||||
|
||||
// 更新arm架构中事件失效问题
|
||||
func UpdateHookArm(ctx *context.APIContext) {
|
||||
form := web.GetForm(ctx).(*gitea_api.RepositoryPayload)
|
||||
eventName := string(form.Action)
|
||||
if eventName == "deleted" {
|
||||
ctx.Status(http.StatusOK)
|
||||
return
|
||||
}
|
||||
getAssignmentRepo(ctx, form.Repository.ID)
|
||||
if err := UpdateDelegateHooks(ctx.Repo.Repository.RepoPath()); err != nil {
|
||||
fmt.Errorf("Unable to recreate delegate hooks for %-v. ERROR: %w", ctx.Repo, err)
|
||||
ctx.Error(http.StatusInternalServerError, "", "Unable to recreate delegate hooks.")
|
||||
return
|
||||
}
|
||||
ctx.Status(http.StatusOK)
|
||||
}
|
||||
|
||||
// 更新仓库限制大小
|
||||
func UpdateRepoSizeLimitHook(ctx *context.APIContext) {
|
||||
form := web.GetForm(ctx).(*UpdateRepoSizeLimit)
|
||||
ownerName := string(form.OwnerName)
|
||||
repoName := string(form.RepoName)
|
||||
LimitSize := form.LimitSize
|
||||
|
||||
getAssignmentRepoByName(ctx, ownerName, repoName)
|
||||
|
||||
hookDir := filepath.Join(ctx.Repo.Repository.RepoPath(), "hooks")
|
||||
hookName := "pre-receive"
|
||||
//results := make([]string, 0, 10)
|
||||
sizeLimitTpls := []string{
|
||||
//fmt.Sprintf("#!/usr/bin/env %s\n\n\nset -o pipefail\n\nreadonly DEFAULT_FILE_MAXSIZE_MB=\"30\" \nreadonly CONFIG_NAME=\"hooks.maxfilesize\"\nreadonly NULLSHA=\"0000000000000000000000000000000000000000\"\nreadonly EXIT_SUCCESS=0\nreadonly EXIT_FAILURE=1\nreadonly DEFAULT_REPO_MAXSIZE_MB=\"1024\" \nreadonly CHECK_FLAG_ON=1\n\n\nstatus=\"$EXIT_SUCCESS\"\n\n# skip this hook entirely if shell check is not open\ncheck_flag=${PUSH_SIZE_CHECK_FLAG}\nif [[ $check_flag != $CHECK_FLAG_ON ]]; then\nexit $EXIT_SUCCESS\nfi\n\n\n#######################################\n# check the file max size limit\n#######################################\n\n# get maximum filesize (from repository-specific config)\nmaxsize_mb=\"${REPO_MAX_FILE_SIZE}\"\n\nif [[ \"$?\" != $EXIT_SUCCESS ]]; then\necho \"failed to get ${CONFIG_NAME} from config\"\nexit \"$EXIT_FAILURE\"\nfi\n\npush_size=\"0\"\n# read lines from stdin (format: \"<oldref> <newref> <refname>\\n\")\nwhile read oldref newref refname; do\n# skip branch deletions\nif [[ \"$newref\" == \"$NULLSHA\" ]]; then\n continue\nfi\n\n# find large objects\n# check all objects from $oldref (possible $NULLSHA) to $newref, but\n# skip all objects that have already been accepted (i.e. are referenced by\n# another branch or tag).\n\nnew_branch_flag=0\nif [[ \"$oldref\" == \"$NULLSHA\" ]]; then\n target=\"$newref\"\n new_branch_flag=1\n echo \"You are creating a new remote branch,openI will check all files in commit history to find oversize files\"\nelse\n target=\"${oldref}..${newref}\"\nfi\nmaxsize=`expr $maxsize_mb \\* 1048576` \n\n# find objects in this push_size\n# print like:\n# 08da8e2ab9ae4095bf94dd71ac913132b880b463 commit 214\n# 43e993b768ede5740e8c65de2ed6edec25053ea1 tree 185\n# 4476971d76569039df7569af1b8d03c288f6b193 blob 20167318 b0417e6593a1.zip\nfiles=\"$(git rev-list --objects \"$target\" | \\\n git cat-file $'--batch-check=%%(objectname) %%(objecttype) %%(objectsize) %%(rest)' | \\\n awk -F ' ' -v maxbytes=\"$maxsize\" 'BEGIN {totalIn=0} {if( $3 > maxbytes && $2 == \"blob\") { totalIn+=$3; print $4} else { totalIn+=$3}} END { printf (\"totalIn=\\t%%s\",totalIn)}' )\"\n \nif [[ \"$?\" != $EXIT_SUCCESS ]]; then\n echo \"failed to check for large files in ref ${refname}\"\n continue\nfi\n\nIFS=$'\\n'\n# rewrite IFS to seperate line in $files\nfor file in $files; do\n # if don't unset IFS,temp_array=(${file}) will get error answer\n \n if [[ ${file} == totalIn=* ]]; then\n\tIFS=$'\\t'\n\ttemp_array=(${file})\n\tpush_size=${temp_array[1]}\n\tcontinue\n fi\n\tunset IFS\n if [[ \"$status\" == $EXIT_SUCCESS ]]; then\n\t\techo -e \"Error: Your push was rejected because it contains files larger than $(numfmt --to=iec \"$maxsize_mb\") Mb\"\n\t\techo \"help document -- https://openi.pcl.ac.cn/zeizei/OpenI_Learning/src/branch/master/docs/git/repository_capacity_help.md\"\n\t\techo \"oversize files:\"\n\t\tstatus=\"$EXIT_FAILURE\"\t\n fi\n echo -e \"\\033[31m- ${file}\\033[0m \"\ndone\n\nif [[ \"$status\" != $EXIT_SUCCESS ]]; then\n\texit \"$status\"\nfi\n\ndone\n\n#######################################\n# check the repo max size limit\n#######################################\nif [[ $push_size -eq \"0\" ]]; then\n\texit $EXIT_SUCCESS\nfi\n\n# if create new branch or tag,use count-objects -v to get pack size\nif [[ $new_branch_flag -eq 1 ]]; then\n size_kb=`git count-objects -v | grep 'size-pack' | sed 's/.*\\(size-pack:\\).//'`\n size_pack_kb=`git count-objects -v | grep 'size:' | sed 's/.*\\(size:\\).//'`\n\ttotal_kb=`expr $size_kb + $size_pack_kb`\n\tlet push_size=$total_kb*1024\nfi\n\nsizelimit_mb=\"${REPO_MAX_SIZE}\"\nlet sizelimit_b=$sizelimit_mb*1024*1024\n\n# repo size at here means the size of repo directory in server \nreposize_b=${REPO_CURRENT_SIZE}\n\ntotal=`expr $push_size + $reposize_b`\n\nif [ $total -gt $sizelimit_b ]; then\n echo \"Error: Your push was rejected because the repository size is large than $sizelimit_mb Mb\"\n echo \"see the help document--https://openi.pcl.ac.cn/zeizei/OpenI_Learning/src/branch/master/docs/git/repository_capacity_help.md\"\n exit $EXIT_FAILURE\nfi\n\n\nexit $EXIT_SUCCESS", setting.ScriptType, LimitSize),
|
||||
fmt.Sprintf("#!/usr/bin/env %s\n\n\nset -o pipefail\n\nreadonly DEFAULT_FILE_MAXSIZE_MB=\"30\" \nreadonly CONFIG_NAME=\"hooks.maxfilesize\"\nreadonly NULLSHA=\"0000000000000000000000000000000000000000\"\nreadonly EXIT_SUCCESS=0\nreadonly EXIT_FAILURE=1\nreadonly DEFAULT_REPO_MAXSIZE_MB=\"1024\" \nreadonly CHECK_FLAG_ON=1\n\n\nPUSH_SIZE_CHECK_FLAG=1\nREPO_MAX_FILE_SIZE=1024\nREPO_MAX_SIZE=%d\nREPO_CURRENT_SIZE=$(du -sk \"${PWD}\" | cut -f1)\nsize_in_mb=$(($REPO_CURRENT_SIZE / 1024))\nstatus=\"$EXIT_SUCCESS\"\n\n# skip this hook entirely if shell check is not open\ncheck_flag=${PUSH_SIZE_CHECK_FLAG}\nif [[ $check_flag != $CHECK_FLAG_ON ]]; then\nexit $EXIT_SUCCESS\nfi\n\n\n#######################################\n# check the file max size limit\n#######################################\n\n# get maximum filesize (from repository-specific config)\nmaxsize_mb=\"${REPO_MAX_FILE_SIZE}\"\n\nif [[ \"$?\" != $EXIT_SUCCESS ]]; then\necho \"failed to get ${CONFIG_NAME} from config\"\nexit \"$EXIT_FAILURE\"\nfi\n\npush_size=\"0\"\n# read lines from stdin (format: \"<oldref> <newref> <refname>\\n\")\nwhile read oldref newref refname; do\n# skip branch deletions\nif [[ \"$newref\" == \"$NULLSHA\" ]]; then\n continue\nfi\n\n# find large objects\n# check all objects from $oldref (possible $NULLSHA) to $newref, but\n# skip all objects that have already been accepted (i.e. are referenced by\n# another branch or tag).\n\nnew_branch_flag=0\nif [[ \"$oldref\" == \"$NULLSHA\" ]]; then\n target=\"$newref\"\n new_branch_flag=1\n echo \"You are creating a new remote branch,openI will check all files in commit history to find oversize files\"\nelse\n target=\"${oldref}..${newref}\"\nfi\nmaxsize=`expr $maxsize_mb \\* 1048576` \n\n# find objects in this push_size\n# print like:\n# 08da8e2ab9ae4095bf94dd71ac913132b880b463 commit 214\n# 43e993b768ede5740e8c65de2ed6edec25053ea1 tree 185\n# 4476971d76569039df7569af1b8d03c288f6b193 blob 20167318 b0417e6593a1.zip\nfiles=\"$(git rev-list --objects \"$target\" | \\\n git cat-file $'--batch-check=%%(objectname) %%(objecttype) %%(objectsize) %%(rest)' | \\\n awk -F ' ' -v maxbytes=\"$maxsize\" 'BEGIN {totalIn=0} {if( $3 > maxbytes && $2 == \"blob\") { totalIn+=$3; print $4} else { totalIn+=$3}} END { printf (\"totalIn=\\t%%s\",totalIn)}' )\"\n \nif [[ \"$?\" != $EXIT_SUCCESS ]]; then\n echo \"failed to check for large files in ref ${refname}\"\n continue\nfi\n\nIFS=$'\\n'\n# rewrite IFS to seperate line in $files\nfor file in $files; do\n # if don't unset IFS,temp_array=(${file}) will get error answer\n \n if [[ ${file} == totalIn=* ]]; then\n\tIFS=$'\\t'\n\ttemp_array=(${file})\n\tpush_size=${temp_array[1]}\n\tcontinue\n fi\n\tunset IFS\n if [[ \"$status\" == $EXIT_SUCCESS ]]; then\n\t\techo -e \"Error: Your push was rejected because it contains files larger than $(numfmt --to=iec \"$maxsize_mb\") Mb\"\n\t\techo \"help document -- ...\"\n\t\techo \"oversize files:\"\n\t\tstatus=\"$EXIT_FAILURE\"\t\n fi\n echo -e \"\\033[31m- ${file}\\033[0m \"\ndone\n\nif [[ \"$status\" != $EXIT_SUCCESS ]]; then\n\texit \"$status\"\nfi\n\ndone\n\n#######################################\n# check the repo max size limit\n#######################################\nif [[ $push_size -eq \"0\" ]]; then\n\texit $EXIT_SUCCESS\nfi\n\n# if create new branch or tag,use count-objects -v to get pack size\nif [[ $new_branch_flag -eq 1 ]]; then\n size_kb=`git count-objects -v | grep 'size-pack' | sed 's/.*\\(size-pack:\\).//'`\n size_pack_kb=`git count-objects -v | grep 'size:' | sed 's/.*\\(size:\\).//'`\n\ttotal_kb=`expr $size_kb + $size_pack_kb`\n\tlet push_size=$total_kb*1024\nfi\n\nsizelimit_mb=\"${REPO_MAX_SIZE}\"\nlet sizelimit_b=$sizelimit_mb*1024*1024\n\n# repo size at here means the size of repo directory in server \nreposize_b=${REPO_CURRENT_SIZE}\n\nlet total=$REPO_CURRENT_SIZE*1024\n\nif [ $total -gt $sizelimit_b ]; then\n echo \"Error: Your push was rejected because the repository size is large than $sizelimit_mb Mb\"\n echo \"Error: 推送被拒绝,文件或目录大小大于 $sizelimit_mb Mb\"\n exit $EXIT_FAILURE\nfi\n\n\nexit $EXIT_SUCCESS", setting.ScriptType, LimitSize),
|
||||
|
||||
fmt.Sprintf(""),
|
||||
fmt.Sprintf(""),
|
||||
}
|
||||
|
||||
oldHookPath := filepath.Join(hookDir, hookName+".d", SIZE_LIMIT_SCRIPT_NAME)
|
||||
if !com.IsExist(oldHookPath) {
|
||||
//if err := checkHookFile(generateHookScriptPath(hookDir, hookName, SIZE_LIMIT_SCRIPT_NAME), sizeLimitTpls[0], results); err != nil {
|
||||
// ctx.Error(http.StatusInternalServerError, "CreateSizeLimitHook checkHookFile", err)
|
||||
// ctx.NotFound()
|
||||
// return
|
||||
//}
|
||||
|
||||
if err := writeHookTpl(generateHookScriptPath(hookDir, hookName, SIZE_LIMIT_SCRIPT_NAME), sizeLimitTpls[0]); err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "CreateSizeLimitHook checkHookFile", err)
|
||||
ctx.NotFound()
|
||||
return
|
||||
}
|
||||
}
|
||||
ctx.Status(http.StatusOK)
|
||||
}
|
||||
|
||||
func resetRepoSizeLimitHook(ctx *context.APIContext) {
|
||||
form := web.GetForm(ctx).(*gitea_api.RepositoryPayload)
|
||||
eventName := string(form.Action)
|
||||
if eventName == "deleted" {
|
||||
ctx.Status(http.StatusOK)
|
||||
return
|
||||
}
|
||||
getAssignmentRepo(ctx, form.Repository.ID)
|
||||
hookDir := filepath.Join(ctx.Repo.Repository.RepoPath(), "hooks")
|
||||
hookName := "pre-receive"
|
||||
//results := make([]string, 0, 10)
|
||||
sizeLimitTpls := []string{
|
||||
fmt.Sprintf("#!/usr/bin/env %s\n\n\nset -o pipefail\n\nreadonly DEFAULT_FILE_MAXSIZE_MB=\"30\" \nreadonly CONFIG_NAME=\"hooks.maxfilesize\"\nreadonly NULLSHA=\"0000000000000000000000000000000000000000\"\nreadonly EXIT_SUCCESS=0\nreadonly EXIT_FAILURE=1\nreadonly DEFAULT_REPO_MAXSIZE_MB=\"1024\" \nreadonly CHECK_FLAG_ON=1\n\n\nstatus=\"$EXIT_SUCCESS\"\n\n# skip this hook entirely if shell check is not open\ncheck_flag=${PUSH_SIZE_CHECK_FLAG}\nif [[ $check_flag != $CHECK_FLAG_ON ]]; then\nexit $EXIT_SUCCESS\nfi\n\n\n#######################################\n# check the file max size limit\n#######################################\n\n# get maximum filesize (from repository-specific config)\nmaxsize_mb=\"${REPO_MAX_FILE_SIZE}\"\n\nif [[ \"$?\" != $EXIT_SUCCESS ]]; then\necho \"failed to get ${CONFIG_NAME} from config\"\nexit \"$EXIT_FAILURE\"\nfi\n\npush_size=\"0\"\n# read lines from stdin (format: \"<oldref> <newref> <refname>\\n\")\nwhile read oldref newref refname; do\n# skip branch deletions\nif [[ \"$newref\" == \"$NULLSHA\" ]]; then\n continue\nfi\n\n# find large objects\n# check all objects from $oldref (possible $NULLSHA) to $newref, but\n# skip all objects that have already been accepted (i.e. are referenced by\n# another branch or tag).\n\nnew_branch_flag=0\nif [[ \"$oldref\" == \"$NULLSHA\" ]]; then\n target=\"$newref\"\n new_branch_flag=1\n echo \"You are creating a new remote branch,openI will check all files in commit history to find oversize files\"\nelse\n target=\"${oldref}..${newref}\"\nfi\nmaxsize=`expr $maxsize_mb \\* 1048576` \n\n# find objects in this push_size\n# print like:\n# 08da8e2ab9ae4095bf94dd71ac913132b880b463 commit 214\n# 43e993b768ede5740e8c65de2ed6edec25053ea1 tree 185\n# 4476971d76569039df7569af1b8d03c288f6b193 blob 20167318 b0417e6593a1.zip\nfiles=\"$(git rev-list --objects \"$target\" | \\\n git cat-file $'--batch-check=%%(objectname) %%(objecttype) %%(objectsize) %%(rest)' | \\\n awk -F ' ' -v maxbytes=\"$maxsize\" 'BEGIN {totalIn=0} {if( $3 > maxbytes && $2 == \"blob\") { totalIn+=$3; print $4} else { totalIn+=$3}} END { printf (\"totalIn=\\t%%s\",totalIn)}' )\"\n \nif [[ \"$?\" != $EXIT_SUCCESS ]]; then\n echo \"failed to check for large files in ref ${refname}\"\n continue\nfi\n\nIFS=$'\\n'\n# rewrite IFS to seperate line in $files\nfor file in $files; do\n # if don't unset IFS,temp_array=(${file}) will get error answer\n \n if [[ ${file} == totalIn=* ]]; then\n\tIFS=$'\\t'\n\ttemp_array=(${file})\n\tpush_size=${temp_array[1]}\n\tcontinue\n fi\n\tunset IFS\n if [[ \"$status\" == $EXIT_SUCCESS ]]; then\n\t\techo -e \"Error: Your push was rejected because it contains files larger than $(numfmt --to=iec \"$maxsize_mb\") Mb\"\n\t\techo \"help document -- https://openi.pcl.ac.cn/zeizei/OpenI_Learning/src/branch/master/docs/git/repository_capacity_help.md\"\n\t\techo \"oversize files:\"\n\t\tstatus=\"$EXIT_FAILURE\"\t\n fi\n echo -e \"\\033[31m- ${file}\\033[0m \"\ndone\n\nif [[ \"$status\" != $EXIT_SUCCESS ]]; then\n\texit \"$status\"\nfi\n\ndone\n\n#######################################\n# check the repo max size limit\n#######################################\nif [[ $push_size -eq \"0\" ]]; then\n\texit $EXIT_SUCCESS\nfi\n\n# if create new branch or tag,use count-objects -v to get pack size\nif [[ $new_branch_flag -eq 1 ]]; then\n size_kb=`git count-objects -v | grep 'size-pack' | sed 's/.*\\(size-pack:\\).//'`\n size_pack_kb=`git count-objects -v | grep 'size:' | sed 's/.*\\(size:\\).//'`\n\ttotal_kb=`expr $size_kb + $size_pack_kb`\n\tlet push_size=$total_kb*1024\nfi\n\nsizelimit_mb=\"${REPO_MAX_SIZE}\"\nlet sizelimit_b=$sizelimit_mb*1024*1024\n\n# repo size at here means the size of repo directory in server \nreposize_b=${REPO_CURRENT_SIZE}\n\ntotal=`expr $push_size + $reposize_b`\n\nif [ $total -gt $sizelimit_b ]; then\n echo \"Error: Your push was rejected because the repository size is large than $sizelimit_mb Mb\"\n echo \"see the help document--https://openi.pcl.ac.cn/zeizei/OpenI_Learning/src/branch/master/docs/git/repository_capacity_help.md\"\n exit $EXIT_FAILURE\nfi\n\n\nexit $EXIT_SUCCESS", setting.ScriptType),
|
||||
fmt.Sprintf(""),
|
||||
fmt.Sprintf(""),
|
||||
}
|
||||
|
||||
oldHookPath := filepath.Join(hookDir, hookName+".d", SIZE_LIMIT_SCRIPT_NAME)
|
||||
if !com.IsExist(oldHookPath) {
|
||||
//if err := checkHookFile(generateHookScriptPath(hookDir, hookName, SIZE_LIMIT_SCRIPT_NAME), sizeLimitTpls[0], results); err != nil {
|
||||
// ctx.Error(http.StatusInternalServerError, "CreateSizeLimitHook checkHookFile", err)
|
||||
// ctx.NotFound()
|
||||
// return
|
||||
//}
|
||||
|
||||
if err := writeHookTpl(generateHookScriptPath(hookDir, hookName, SIZE_LIMIT_SCRIPT_NAME), sizeLimitTpls[0]); err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "CreateSizeLimitHook checkHookFile", err)
|
||||
ctx.NotFound()
|
||||
return
|
||||
}
|
||||
}
|
||||
ctx.Status(http.StatusOK)
|
||||
}
|
||||
|
||||
func getAssignmentRepo(ctx *context.APIContext, id int64) {
|
||||
// Get repository.
|
||||
repo, err := repo_model.GetRepositoryByID(ctx, id)
|
||||
if err != nil {
|
||||
if repo_model.IsErrRepoNotExist(err) {
|
||||
redirectRepoID, err := repo_model.LookupRedirect(repo.OwnerID, repo.Name)
|
||||
if err == nil {
|
||||
context.RedirectToRepo(ctx.Base, redirectRepoID)
|
||||
} else if repo_model.IsErrRedirectNotExist(err) {
|
||||
ctx.NotFound(ctx.Tr("repo.RepoNotFound", repo.OwnerID))
|
||||
} else {
|
||||
ctx.Error(http.StatusInternalServerError, "LookupRepoRedirect", err)
|
||||
}
|
||||
} else {
|
||||
ctx.Error(http.StatusInternalServerError, "GetRepositoryByID", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
ctx.Repo.Repository = repo
|
||||
}
|
||||
|
||||
func getAssignmentRepoByName(ctx *context.APIContext, ownerName string, repoName string) {
|
||||
// Get repository.
|
||||
owner, err := user_model.GetUserByName(ctx, ownerName)
|
||||
repo, err := repo_model.GetRepositoryByName(owner.ID, repoName)
|
||||
if err != nil {
|
||||
if repo_model.IsErrRepoNotExist(err) {
|
||||
redirectRepoID, err := repo_model.LookupRedirect(repo.OwnerID, repo.Name)
|
||||
if err == nil {
|
||||
context.RedirectToRepo(ctx.Base, redirectRepoID)
|
||||
} else if repo_model.IsErrRedirectNotExist(err) {
|
||||
ctx.NotFound(ctx.Tr("repo.RepoNotFound", repo.OwnerID))
|
||||
} else {
|
||||
ctx.Error(http.StatusInternalServerError, "LookupRepoRedirect", err)
|
||||
}
|
||||
} else {
|
||||
ctx.Error(http.StatusInternalServerError, "GetRepositoryByID", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
ctx.Repo.Repository = repo
|
||||
}
|
||||
|
||||
// UpdateDelegateHooks creates all the hooks scripts for the repo
|
||||
func UpdateDelegateHooks(repoPath string) (err error) {
|
||||
hookNames, hookTpls, giteaHookTpls := getHookTemplates()
|
||||
hookDir := filepath.Join(repoPath, "hooks")
|
||||
|
||||
for i, hookName := range hookNames {
|
||||
oldHookPath := filepath.Join(hookDir, hookName)
|
||||
newHookPath := filepath.Join(hookDir, hookName+".d", "gitea")
|
||||
|
||||
if err := os.MkdirAll(filepath.Join(hookDir, hookName+".d"), os.ModePerm); err != nil {
|
||||
return fmt.Errorf("create hooks dir '%s': %w", filepath.Join(hookDir, hookName+".d"), err)
|
||||
}
|
||||
|
||||
// WARNING: This will override all old server-side hooks
|
||||
if err = util.Remove(oldHookPath); err != nil && !os.IsNotExist(err) {
|
||||
return fmt.Errorf("unable to pre-remove old hook file '%s' prior to rewriting: %w ", oldHookPath, err)
|
||||
}
|
||||
if err = os.WriteFile(oldHookPath, []byte(hookTpls[i]), 0o777); err != nil {
|
||||
return fmt.Errorf("write old hook file '%s': %w", oldHookPath, err)
|
||||
}
|
||||
|
||||
if err = ensureExecutable(oldHookPath); err != nil {
|
||||
return fmt.Errorf("Unable to set %s executable. Error %w", oldHookPath, err)
|
||||
}
|
||||
|
||||
if err = util.Remove(newHookPath); err != nil && !os.IsNotExist(err) {
|
||||
return fmt.Errorf("unable to pre-remove new hook file '%s' prior to rewriting: %w", newHookPath, err)
|
||||
}
|
||||
if err = os.WriteFile(newHookPath, []byte(giteaHookTpls[i]), 0o777); err != nil {
|
||||
return fmt.Errorf("write new hook file '%s': %w", newHookPath, err)
|
||||
}
|
||||
|
||||
if err = ensureExecutable(newHookPath); err != nil {
|
||||
return fmt.Errorf("Unable to set %s executable. Error %w", oldHookPath, err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func getHookTemplates() (hookNames, hookTpls, giteaHookTpls []string) {
|
||||
hookNames = []string{"pre-receive", "update", "post-receive"}
|
||||
hookTpls = []string{
|
||||
// for pre-receive
|
||||
fmt.Sprintf(`#!/usr/bin/env %s
|
||||
# AUTO GENERATED BY GITEA, DO NOT MODIFY
|
||||
data=$(cat)
|
||||
exitcodes=""
|
||||
hookname=$(basename $0)
|
||||
GIT_DIR=${GIT_DIR:-$(dirname $0)/..}
|
||||
|
||||
for hook in ${GIT_DIR}/hooks/${hookname}.d/*; do
|
||||
test -x "${hook}" && test -f "${hook}"
|
||||
echo "${data}" | "${hook}"
|
||||
exitcodes="${exitcodes} $?"
|
||||
done
|
||||
|
||||
for i in ${exitcodes}; do
|
||||
[ ${i} -eq 0 ] || exit ${i}
|
||||
done
|
||||
`, setting.ScriptType),
|
||||
|
||||
// for update
|
||||
fmt.Sprintf(`#!/usr/bin/env %s
|
||||
# AUTO GENERATED BY GITEA, DO NOT MODIFY
|
||||
exitcodes=""
|
||||
hookname=$(basename $0)
|
||||
GIT_DIR=${GIT_DIR:-$(dirname $0/..)}
|
||||
|
||||
for hook in ${GIT_DIR}/hooks/${hookname}.d/*; do
|
||||
test -x "${hook}" && test -f "${hook}"
|
||||
"${hook}" $1 $2 $3
|
||||
exitcodes="${exitcodes} $?"
|
||||
done
|
||||
|
||||
for i in ${exitcodes}; do
|
||||
[ ${i} -eq 0 ] || exit ${i}
|
||||
done
|
||||
`, setting.ScriptType),
|
||||
|
||||
// for post-receive
|
||||
fmt.Sprintf(`#!/usr/bin/env %s
|
||||
# AUTO GENERATED BY GITEA, DO NOT MODIFY
|
||||
data=$(cat)
|
||||
exitcodes=""
|
||||
hookname=$(basename $0)
|
||||
GIT_DIR=${GIT_DIR:-$(dirname $0)/..}
|
||||
|
||||
for hook in ${GIT_DIR}/hooks/${hookname}.d/*; do
|
||||
test -x "${hook}" && test -f "${hook}"
|
||||
echo "${data}" | "${hook}"
|
||||
exitcodes="${exitcodes} $?"
|
||||
done
|
||||
|
||||
for i in ${exitcodes}; do
|
||||
[ ${i} -eq 0 ] || exit ${i}
|
||||
done
|
||||
`, setting.ScriptType),
|
||||
}
|
||||
|
||||
giteaHookTpls = []string{
|
||||
// for pre-receive
|
||||
fmt.Sprintf(`#!/usr/bin/env %s
|
||||
# AUTO GENERATED BY GITEA, DO NOT MODIFY
|
||||
%s hook --config=%s pre-receive
|
||||
`, setting.ScriptType, util.ShellEscape(setting.AppPath), util.ShellEscape(setting.CustomConf)),
|
||||
|
||||
// for update
|
||||
fmt.Sprintf(`#!/usr/bin/env %s
|
||||
# AUTO GENERATED BY GITEA, DO NOT MODIFY
|
||||
%s hook --config=%s update $1 $2 $3
|
||||
`, setting.ScriptType, util.ShellEscape(setting.AppPath), util.ShellEscape(setting.CustomConf)),
|
||||
|
||||
// for post-receive
|
||||
fmt.Sprintf(`#!/usr/bin/env %s
|
||||
# AUTO GENERATED BY GITEA, DO NOT MODIFY
|
||||
%s hook --config=%s post-receive
|
||||
`, setting.ScriptType, util.ShellEscape(setting.AppPath), util.ShellEscape(setting.CustomConf)),
|
||||
}
|
||||
|
||||
if git.SupportProcReceive {
|
||||
hookNames = append(hookNames, "proc-receive")
|
||||
hookTpls = append(hookTpls,
|
||||
fmt.Sprintf(`#!/usr/bin/env %s
|
||||
# AUTO GENERATED BY GITEA, DO NOT MODIFY
|
||||
%s hook --config=%s proc-receive
|
||||
`, setting.ScriptType, util.ShellEscape(setting.AppPath), util.ShellEscape(setting.CustomConf)))
|
||||
giteaHookTpls = append(giteaHookTpls, "")
|
||||
}
|
||||
|
||||
return hookNames, hookTpls, giteaHookTpls
|
||||
}
|
||||
|
||||
func generateHookScriptPath(hookDir, hookName, fileName string) string {
|
||||
return filepath.Join(hookDir, hookName+".d", fileName)
|
||||
}
|
||||
|
||||
func checkHookFile(filePath, tpl string, results []string) error {
|
||||
if tpl == "" {
|
||||
return nil
|
||||
}
|
||||
contents, err := ioutil.ReadFile(filePath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if string(contents) != tpl {
|
||||
results = append(results, fmt.Sprintf("old hook file %s is out of date", filePath))
|
||||
}
|
||||
if !checkExecutable(filePath) {
|
||||
results = append(results, fmt.Sprintf("old hook file %s is not executable", filePath))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func writeHookTpl(hookPath, content string) error {
|
||||
if content == "" {
|
||||
return nil
|
||||
}
|
||||
if err := ioutil.WriteFile(hookPath, []byte(content), 0777); err != nil {
|
||||
return fmt.Errorf("write new hook file '%s': %v", hookPath, err)
|
||||
}
|
||||
|
||||
if err := ensureExecutable(hookPath); err != nil {
|
||||
return fmt.Errorf("Unable to set %s executable. Error %v", hookPath, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func checkExecutable(filename string) bool {
|
||||
fileInfo, err := os.Stat(filename)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return (fileInfo.Mode() & 0100) > 0
|
||||
}
|
||||
|
||||
func ensureExecutable(filename string) error {
|
||||
fileInfo, err := os.Stat(filename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (fileInfo.Mode() & 0100) > 0 {
|
||||
return nil
|
||||
}
|
||||
mode := fileInfo.Mode() | 0100
|
||||
return os.Chmod(filename, mode)
|
||||
}
|
||||
|
|
@ -2,12 +2,15 @@ package repo
|
|||
|
||||
import (
|
||||
"net/http"
|
||||
"net/url"
|
||||
|
||||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
"code.gitea.io/gitea/modules/context"
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
api "code.gitea.io/gitea/modules/structs"
|
||||
util "code.gitea.io/gitea/modules/util"
|
||||
"code.gitea.io/gitea/services/convert"
|
||||
wiki_service "code.gitea.io/gitea/services/wiki"
|
||||
)
|
||||
|
||||
|
|
@ -73,3 +76,110 @@ func ListWikiPageNames(ctx *context.APIContext) {
|
|||
ctx.SetTotalCountHeader(int64(len(entries)))
|
||||
ctx.JSON(http.StatusOK, pages)
|
||||
}
|
||||
|
||||
func ShowRevision(ctx *context.APIContext) {
|
||||
wikiRepo, err := git.OpenRepository(ctx, ctx.Repo.Repository.WikiPath())
|
||||
if err != nil {
|
||||
if git.IsErrNotExist(err) || err.Error() == "no such file or directory" {
|
||||
ctx.NotFound(err)
|
||||
} else {
|
||||
ctx.Error(http.StatusInternalServerError, "OpenRepository", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if wikiRepo != nil {
|
||||
|
||||
defer wikiRepo.Close()
|
||||
}
|
||||
|
||||
// get requested pagename
|
||||
pageName := wiki_service.WebPathFromRequest(ctx.PathParamRaw(":pageName"))
|
||||
if len(pageName) == 0 {
|
||||
pageName = "Home"
|
||||
}
|
||||
|
||||
revision := ctx.PathParamRaw(":revision")
|
||||
|
||||
commit, err := wikiRepo.GetCommit(revision)
|
||||
if err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "GetCommit", err)
|
||||
return
|
||||
}
|
||||
|
||||
content, pageFilename := wikiContentsByName(ctx, commit, pageName, false)
|
||||
|
||||
sidebarContent, _ := wikiContentsByName(ctx, commit, "_Sidebar", true)
|
||||
|
||||
footerContent, _ := wikiContentsByName(ctx, commit, "_Footer", true)
|
||||
|
||||
// get commit count - wiki revisions
|
||||
commitsCount, _ := wikiRepo.FileCommitsCount("master", pageFilename)
|
||||
|
||||
// Get last change information.
|
||||
lastCommit, err := wikiRepo.GetCommitByPath(pageFilename)
|
||||
if err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "GetCommitByPath", err)
|
||||
return
|
||||
}
|
||||
|
||||
ctx.JSON(http.StatusOK, &api.WikiPage{
|
||||
WikiPageMetaData: convert.ToWikiPageMetaData(pageName, lastCommit, ctx.Repo.Repository),
|
||||
ContentBase64: content,
|
||||
CommitCount: commitsCount,
|
||||
Sidebar: sidebarContent,
|
||||
Footer: footerContent,
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
// findEntryForFile finds the tree entry for a target filepath.
|
||||
func findEntryForFile(commit *git.Commit, target string) (*git.TreeEntry, error) {
|
||||
entry, err := commit.GetTreeEntryByPath(target)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if entry != nil {
|
||||
return entry, nil
|
||||
}
|
||||
|
||||
// Then the unescaped, shortest alternative
|
||||
var unescapedTarget string
|
||||
if unescapedTarget, err = url.QueryUnescape(target); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return commit.GetTreeEntryByPath(unescapedTarget)
|
||||
}
|
||||
|
||||
// wikiContentsByEntry returns the contents of the wiki page referenced by the
|
||||
// given tree entry, encoded with base64. Writes to ctx if an error occurs.
|
||||
func wikiContentsByEntry(ctx *context.APIContext, entry *git.TreeEntry) string {
|
||||
blob := entry.Blob()
|
||||
if blob.Size() > setting.API.DefaultMaxBlobSize {
|
||||
return ""
|
||||
}
|
||||
content, err := blob.GetBlobContentBase64()
|
||||
if err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "GetBlobContentBase64", err)
|
||||
return ""
|
||||
}
|
||||
return content
|
||||
}
|
||||
|
||||
// wikiContentsByName returns the contents of a wiki page, along with a boolean
|
||||
// indicating whether the page exists. Writes to ctx if an error occurs.
|
||||
func wikiContentsByName(ctx *context.APIContext, commit *git.Commit, wikiName wiki_service.WebPath, isSidebarOrFooter bool) (string, string) {
|
||||
gitFilename := wiki_service.WebPathToGitPath(wikiName)
|
||||
entry, err := findEntryForFile(commit, gitFilename)
|
||||
if err != nil {
|
||||
if git.IsErrNotExist(err) {
|
||||
if !isSidebarOrFooter {
|
||||
ctx.NotFound()
|
||||
}
|
||||
} else {
|
||||
ctx.ServerError("findEntryForFile", err)
|
||||
}
|
||||
return "", ""
|
||||
}
|
||||
return wikiContentsByEntry(ctx, entry), gitFilename
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,18 @@
|
|||
package user
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
|
||||
"code.gitea.io/gitea/modules/context"
|
||||
code_indexer "code.gitea.io/gitea/modules/indexer/code"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/timeutil"
|
||||
|
||||
hat_activities_models "code.gitlink.org.cn/Gitlink/gitea_hat.git/models/activities"
|
||||
)
|
||||
|
||||
|
|
@ -25,3 +33,91 @@ func GetUserHeatmapData(ctx *context.APIContext) {
|
|||
ctx.JSON(http.StatusOK, heatmap)
|
||||
|
||||
}
|
||||
|
||||
func Code(ctx *context.APIContext) {
|
||||
language := ctx.FormString("language")
|
||||
keyword := ctx.FormString("keyword")
|
||||
queryType := ctx.FormString("query_type")
|
||||
isMatch := queryType == "match"
|
||||
|
||||
page := ctx.FormInt("page")
|
||||
if page <= 0 {
|
||||
page = 1
|
||||
}
|
||||
|
||||
var (
|
||||
repoIDs []int64
|
||||
err error
|
||||
)
|
||||
|
||||
repoIDs, err = repo_model.FindUserCodeAccessibleOwnerRepoIDs(ctx, ctx.ContextUser.ID, ctx.Doer)
|
||||
if err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "FindUserCodeAccessibleOwnerRepoIDs", err)
|
||||
return
|
||||
}
|
||||
|
||||
var (
|
||||
total int
|
||||
searchResults []*code_indexer.Result
|
||||
searchResultLanguages []*code_indexer.SearchResultLanguages
|
||||
)
|
||||
|
||||
var codeIndexerUnavailable bool
|
||||
var repoMaps map[int64]*repo_model.Repository
|
||||
|
||||
if len(repoIDs) > 0 {
|
||||
total, searchResults, searchResultLanguages, err = code_indexer.PerformSearch(ctx, repoIDs, language, keyword, page, setting.UI.RepoSearchPagingNum, isMatch)
|
||||
if err != nil {
|
||||
if code_indexer.IsAvailable(ctx) {
|
||||
ctx.Error(http.StatusInternalServerError, "SearchResults", err)
|
||||
return
|
||||
}
|
||||
codeIndexerUnavailable = true
|
||||
} else {
|
||||
codeIndexerUnavailable = !code_indexer.IsAvailable(ctx)
|
||||
}
|
||||
|
||||
loadRepoIDs := make([]int64, 0, len(searchResults))
|
||||
for _, result := range searchResults {
|
||||
var find bool
|
||||
for _, id := range loadRepoIDs {
|
||||
if id == result.RepoID {
|
||||
find = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !find {
|
||||
loadRepoIDs = append(loadRepoIDs, result.RepoID)
|
||||
}
|
||||
}
|
||||
|
||||
repoMaps, err := repo_model.GetRepositoriesMapByIDs(loadRepoIDs)
|
||||
if err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "GetRepositoriesMapByIDs", err)
|
||||
return
|
||||
}
|
||||
|
||||
repoMaps = repoMaps
|
||||
}
|
||||
|
||||
pageCount := int(math.Ceil(float64(total) / float64(setting.UI.RepoSearchPagingNum)))
|
||||
ctx.Resp.Header().Set("X-Page", strconv.Itoa(page))
|
||||
ctx.Resp.Header().Set("X-PerPage", strconv.Itoa(setting.UI.RepoSearchPagingNum))
|
||||
ctx.Resp.Header().Set("X-Total", strconv.FormatInt(int64(total), 10))
|
||||
ctx.Resp.Header().Set("X-PageCount", strconv.Itoa(pageCount))
|
||||
ctx.Resp.Header().Set("X-HasMore", strconv.FormatBool(page < pageCount))
|
||||
|
||||
ctx.SetLinkHeader(int(total), setting.UI.RepoSearchPagingNum)
|
||||
ctx.Resp.Header().Set("X-Total-Count", fmt.Sprintf("%d", total))
|
||||
ctx.JSON(http.StatusOK, struct {
|
||||
CodeIndexerUnavailable bool
|
||||
RepoMaps map[int64]*repo_model.Repository
|
||||
SearchResults []*code_indexer.Result
|
||||
SearchResultLanguages []*code_indexer.SearchResultLanguages
|
||||
}{
|
||||
CodeIndexerUnavailable: codeIndexerUnavailable,
|
||||
RepoMaps: repoMaps,
|
||||
SearchResults: searchResults,
|
||||
SearchResultLanguages: searchResultLanguages,
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -282,8 +282,10 @@ func registerRoutes(m *web.Route) {
|
|||
reqSignIn := verifyAuthWithOptions(&common.VerifyOptions{SignInRequired: true})
|
||||
reqSignOut := verifyAuthWithOptions(&common.VerifyOptions{SignOutRequired: true})
|
||||
// TODO: rename them to "optSignIn", which means that the "sign-in" could be optional, depends on the VerifyOptions (RequireSignInView)
|
||||
ignSignIn := verifyAuthWithOptions(&common.VerifyOptions{SignInRequired: setting.Service.RequireSignInView})
|
||||
ignExploreSignIn := verifyAuthWithOptions(&common.VerifyOptions{SignInRequired: setting.Service.RequireSignInView || setting.Service.Explore.RequireSigninView})
|
||||
//ignSignIn := verifyAuthWithOptions(&common.VerifyOptions{SignInRequired: setting.Service.RequireSignInView})
|
||||
ignSignIn := verifyAuthWithOptions(&common.VerifyOptions{SignInRequired: true})
|
||||
//ignExploreSignIn := verifyAuthWithOptions(&common.VerifyOptions{SignInRequired: setting.Service.RequireSignInView || setting.Service.Explore.RequireSigninView})
|
||||
ignExploreSignIn := verifyAuthWithOptions(&common.VerifyOptions{SignInRequired: true})
|
||||
|
||||
validation.AddBindingRules()
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,105 @@
|
|||
package actions
|
||||
|
||||
import (
|
||||
"context"
|
||||
"regexp"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
secret_model "code.gitea.io/gitea/models/secret"
|
||||
hat_actions_model "code.gitlink.org.cn/Gitlink/gitea_hat.git/models/actions"
|
||||
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
)
|
||||
|
||||
var globalVars = sync.OnceValue(func() (ret struct {
|
||||
namePattern, forbiddenPrefixPattern *regexp.Regexp
|
||||
},
|
||||
) {
|
||||
ret.namePattern = regexp.MustCompile("(?i)^[A-Z_][A-Z0-9_]*$")
|
||||
ret.forbiddenPrefixPattern = regexp.MustCompile("(?i)^GIT(EA|HUB)_")
|
||||
return ret
|
||||
})
|
||||
|
||||
func ValidateName(name string) error {
|
||||
vars := globalVars()
|
||||
if !vars.namePattern.MatchString(name) ||
|
||||
vars.forbiddenPrefixPattern.MatchString(name) ||
|
||||
strings.EqualFold(name, "CI") /* CI is always set to true in GitHub Actions*/ {
|
||||
return util.NewInvalidArgumentErrorf("invalid variable or secret name")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func CreateSecret(ctx context.Context, ownerID, repoID int64, name, data string) (*secret_model.Secret, error) {
|
||||
if err := ValidateName(name); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
s, err := secret_model.InsertEncryptedSecret(ctx, ownerID, repoID, name, data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return s, nil
|
||||
}
|
||||
|
||||
func UpdateSecret(ctx context.Context, ownerID, repoID int64, name, data string) (bool, error) {
|
||||
if err := ValidateName(name); err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
s, err := GetSecret(ctx, hat_actions_model.FindSecretsOptions{
|
||||
OwnerID: ownerID,
|
||||
RepoID: repoID,
|
||||
Name: name,
|
||||
})
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
if s == nil {
|
||||
return false, util.NewNotExistErrorf("secret not found")
|
||||
}
|
||||
|
||||
err = secret_model.UpdateSecret(ctx, s.ID, data)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func GetSecret(ctx context.Context, opts hat_actions_model.FindSecretsOptions) (*secret_model.Secret, error) {
|
||||
vars, err := hat_actions_model.FindSecrets(ctx, opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(vars) != 1 {
|
||||
return nil, util.NewNotExistErrorf("variable not found")
|
||||
}
|
||||
return vars[0], nil
|
||||
}
|
||||
|
||||
func DeleteSecretByName(ctx context.Context, ownerID, repoID int64, name string) error {
|
||||
s, err := GetSecret(ctx, hat_actions_model.FindSecretsOptions{
|
||||
OwnerID: ownerID,
|
||||
RepoID: repoID,
|
||||
Name: name,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return hat_actions_model.DeleteSecret(ctx, s)
|
||||
}
|
||||
|
||||
func DeleteSecretByID(ctx context.Context, secretID int64) error {
|
||||
s, err := GetSecret(ctx, hat_actions_model.FindSecretsOptions{
|
||||
SecretID: secretID,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return hat_actions_model.DeleteSecret(ctx, s)
|
||||
}
|
||||
|
|
@ -0,0 +1,69 @@
|
|||
package actions
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
actions_model "code.gitea.io/gitea/models/actions"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
|
||||
secret_service "code.gitea.io/gitea/services/secrets"
|
||||
hat_actions_model "code.gitlink.org.cn/Gitlink/gitea_hat.git/models/actions"
|
||||
hat_util "code.gitlink.org.cn/Gitlink/gitea_hat.git/modules/util"
|
||||
)
|
||||
|
||||
func CreateVariable(ctx context.Context, ownerID, repoID int64, name, data string) (*actions_model.ActionVariable, error) {
|
||||
if err := secret_service.ValidateName(name); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
v, err := actions_model.InsertVariable(ctx, ownerID, repoID, name, hat_util.ReserveLineBreakForTextarea(data))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return v, nil
|
||||
}
|
||||
|
||||
func UpdateVariableNameData(ctx context.Context, variable *actions_model.ActionVariable) (bool, error) {
|
||||
if err := secret_service.ValidateName(variable.Name); err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
variable.Data = hat_util.ReserveLineBreakForTextarea(variable.Data)
|
||||
|
||||
return actions_model.UpdateVariable(ctx, variable)
|
||||
}
|
||||
|
||||
func DeleteVariableByID(ctx context.Context, variableID int64) error {
|
||||
v, err := GetVariable(ctx, hat_actions_model.FindVariablesOpts{
|
||||
IDs: []int64{variableID},
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return hat_actions_model.DeleteVariable(ctx, v)
|
||||
}
|
||||
|
||||
func DeleteVariableByName(ctx context.Context, ownerID, repoID int64, name string) error {
|
||||
v, err := GetVariable(ctx, hat_actions_model.FindVariablesOpts{
|
||||
OwnerID: ownerID,
|
||||
RepoID: repoID,
|
||||
Name: name,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return hat_actions_model.DeleteVariable(ctx, v)
|
||||
}
|
||||
|
||||
func GetVariable(ctx context.Context, opts hat_actions_model.FindVariablesOpts) (*actions_model.ActionVariable, error) {
|
||||
vars, err := hat_actions_model.FindVariables(ctx, opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(vars) != 1 {
|
||||
return nil, util.NewNotExistErrorf("variable not found")
|
||||
}
|
||||
return vars[0], nil
|
||||
}
|
||||
Loading…
Reference in New Issue