Compare commits

...

22 Commits

Author SHA1 Message Date
yystopf d9a412c3a2 更改:tag去除前缀 2026-01-19 17:25:22 +08:00
yystopf feefd16ae1 更改:返回tag去除前缀 2026-01-19 17:13:54 +08:00
yystopf 4b32e70cdb 新增:secret和variable查询 2025-12-01 19:29:09 +08:00
yystopf 1b7a0ffb36 更改:版本号 2025-11-11 14:10:00 +08:00
yystopf 684b2733a0 新增:倉庫密鈅列表接口 2025-11-11 14:01:23 +08:00
yystopf f091079312 新增:仓库组织内代码搜索接口 2025-09-03 09:15:06 +08:00
yystopf 4c65b6777a 新增:detail_status在runs接口 2025-09-01 17:38:59 +08:00
yystopf db3a241c92 新增:detail_status在runs接口 2025-09-01 17:35:45 +08:00
xxq250 868d41e749 默认开启登录访问,与叁数RequireSignInView无关,版本号 2025-08-12 10:52:35 +08:00
xxq250 902314a413 默认开启登录访问,与叁数RequireSignInView无关 2025-08-12 10:44:56 +08:00
xxq250 7d71cba643 调整版本号,增加日期 2025-07-29 11:24:17 +08:00
xxq250 6f0092b14c fixed action run stop api 2025-07-28 15:54:07 +08:00
xxq250 31c339fd1d add action run stop api 2025-07-28 15:41:45 +08:00
xxq250 713af786c1 add action run stop api 2025-07-28 15:10:55 +08:00
yystopf 37f8da019d 更改:代码搜索版本号 2025-07-24 17:14:27 +08:00
yystopf dfe0ce8de0 新增:代码搜索接口 2025-07-24 08:30:51 +08:00
yystopf c954b4799e fix:获取冲突文件变更 2025-06-24 14:52:46 +08:00
yystopf 8910972c65 新增:重新打开pr接口 2025-06-23 19:26:16 +08:00
yystopf b015baf624 新增:wiki版本库相关接口 2025-05-12 13:12:59 +08:00
xxq250 4c2cc2a658 fixed 支持自定义仓库大小限制,bug 2025-04-28 10:47:27 +08:00
xxq250 89f6f640e1 fixed 支持自定义仓库大小限制 2025-04-18 16:09:39 +08:00
xxq250 ca5c03bfbf fixed 1、增加仓库大小限制;2、arm架构下hook事件问题;3、更新仓库限制大小 2025-04-11 14:35:39 +08:00
19 changed files with 1814 additions and 4 deletions

View File

@ -22,7 +22,7 @@ import (
)
var (
Version = "v2.7, by v1.21.0 "
Version = "v2.8.8, by v1.21.0, 20260119 "
Tags = ""
MakeVersion = ""
)

59
models/actions/secret.go Normal file
View File

@ -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
}

View File

@ -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
}

View File

@ -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,

34
modules/structs/secret.go Normal file
View File

@ -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"`
}

View File

@ -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"`
}

12
modules/util/util.go Normal file
View File

@ -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")
}

View File

@ -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() {

261
routers/hat/org/action.go Normal file
View File

@ -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)
}

View File

@ -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{}{})
}

View File

@ -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,
})
}

View File

@ -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))

View File

@ -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,
})
}

View File

@ -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)
}

View File

@ -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
}

View File

@ -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,
})
}

View File

@ -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()

105
services/actions/secrets.go Normal file
View File

@ -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)
}

View File

@ -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
}