Skip to content

Commit e84326a

Browse files
jolheisertechknowlogick
authored andcommitted
Add git hooks and webhooks to template repositories; move to services (#8926)
* Add git hooks and webhooks to template options Signed-off-by: jolheiser <john.olheiser@gmail.com> * Update models/repo.go Co-Authored-By: guillep2k <18600385+guillep2k@users.noreply.github.com> * Add tooltip if the user can't edit git hooks Signed-off-by: jolheiser <john.olheiser@gmail.com> * Close repositories after copying git hooks Signed-off-by: jolheiser <john.olheiser@gmail.com> * Wording Co-Authored-By: guillep2k <18600385+guillep2k@users.noreply.github.com> * Restructure for services Signed-off-by: jolheiser <john.olheiser@gmail.com> * Return errors Signed-off-by: jolheiser <john.olheiser@gmail.com> * Move GenerateRepository to using a DBContext Signed-off-by: jolheiser <john.olheiser@gmail.com> * Wrap with models.WithTx Signed-off-by: jolheiser <john.olheiser@gmail.com> * Remove debug print Signed-off-by: jolheiser <john.olheiser@gmail.com> * Move if-error-delete-repo outside WithTx Signed-off-by: jolheiser <john.olheiser@gmail.com> * Return nil if no repo generated Signed-off-by: jolheiser <john.olheiser@gmail.com>
1 parent f25fd5c commit e84326a

File tree

8 files changed

+244
-115
lines changed

8 files changed

+244
-115
lines changed

models/repo.go

+4-100
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,6 @@ import (
4242
"github.com/unknwon/com"
4343
ini "gopkg.in/ini.v1"
4444
"xorm.io/builder"
45-
"xorm.io/xorm"
4645
)
4746

4847
var repoWorkingPool = sync.NewExclusivePool()
@@ -1265,11 +1264,13 @@ type GenerateRepoOptions struct {
12651264
Private bool
12661265
GitContent bool
12671266
Topics bool
1267+
GitHooks bool
1268+
Webhooks bool
12681269
}
12691270

12701271
// IsValid checks whether at least one option is chosen for generation
12711272
func (gro GenerateRepoOptions) IsValid() bool {
1272-
return gro.GitContent || gro.Topics // or other items as they are added
1273+
return gro.GitContent || gro.Topics || gro.GitHooks || gro.Webhooks // or other items as they are added
12731274
}
12741275

12751276
func getRepoInitFile(tp, name string) ([]byte, error) {
@@ -1483,37 +1484,6 @@ func initRepository(e Engine, repoPath string, u *User, repo *Repository, opts C
14831484
return nil
14841485
}
14851486

1486-
// generateRepository initializes repository from template
1487-
func generateRepository(e Engine, repo, templateRepo *Repository) (err error) {
1488-
tmpDir := filepath.Join(os.TempDir(), "gitea-"+repo.Name+"-"+com.ToStr(time.Now().Nanosecond()))
1489-
1490-
if err := os.MkdirAll(tmpDir, os.ModePerm); err != nil {
1491-
return fmt.Errorf("Failed to create dir %s: %v", tmpDir, err)
1492-
}
1493-
1494-
defer func() {
1495-
if err := os.RemoveAll(tmpDir); err != nil {
1496-
log.Error("RemoveAll: %v", err)
1497-
}
1498-
}()
1499-
1500-
if err = generateRepoCommit(e, repo, templateRepo, tmpDir); err != nil {
1501-
return fmt.Errorf("generateRepoCommit: %v", err)
1502-
}
1503-
1504-
// re-fetch repo
1505-
if repo, err = getRepositoryByID(e, repo.ID); err != nil {
1506-
return fmt.Errorf("getRepositoryByID: %v", err)
1507-
}
1508-
1509-
repo.DefaultBranch = "master"
1510-
if err = updateRepository(e, repo, false); err != nil {
1511-
return fmt.Errorf("updateRepository: %v", err)
1512-
}
1513-
1514-
return nil
1515-
}
1516-
15171487
var (
15181488
reservedRepoNames = []string{".", ".."}
15191489
reservedRepoPatterns = []string{"*.git", "*.wiki"}
@@ -1524,7 +1494,7 @@ func IsUsableRepoName(name string) error {
15241494
return isUsableName(reservedRepoNames, reservedRepoPatterns, name)
15251495
}
15261496

1527-
func createRepository(e *xorm.Session, doer, u *User, repo *Repository) (err error) {
1497+
func createRepository(e Engine, doer, u *User, repo *Repository) (err error) {
15281498
if err = IsUsableRepoName(repo.Name); err != nil {
15291499
return err
15301500
}
@@ -2771,72 +2741,6 @@ func ForkRepository(doer, owner *User, oldRepo *Repository, name, desc string) (
27712741
return repo, CopyLFS(repo, oldRepo)
27722742
}
27732743

2774-
// GenerateRepository generates a repository from a template
2775-
func GenerateRepository(doer, owner *User, templateRepo *Repository, opts GenerateRepoOptions) (_ *Repository, err error) {
2776-
repo := &Repository{
2777-
OwnerID: owner.ID,
2778-
Owner: owner,
2779-
Name: opts.Name,
2780-
LowerName: strings.ToLower(opts.Name),
2781-
Description: opts.Description,
2782-
IsPrivate: opts.Private,
2783-
IsEmpty: !opts.GitContent || templateRepo.IsEmpty,
2784-
IsFsckEnabled: templateRepo.IsFsckEnabled,
2785-
TemplateID: templateRepo.ID,
2786-
}
2787-
2788-
createSess := x.NewSession()
2789-
defer createSess.Close()
2790-
if err = createSess.Begin(); err != nil {
2791-
return nil, err
2792-
}
2793-
2794-
if err = createRepository(createSess, doer, owner, repo); err != nil {
2795-
return nil, err
2796-
}
2797-
2798-
//Commit repo to get created repo ID
2799-
err = createSess.Commit()
2800-
if err != nil {
2801-
return nil, err
2802-
}
2803-
2804-
sess := x.NewSession()
2805-
defer sess.Close()
2806-
if err = sess.Begin(); err != nil {
2807-
return repo, err
2808-
}
2809-
2810-
repoPath := RepoPath(owner.Name, repo.Name)
2811-
if err = checkInitRepository(repoPath); err != nil {
2812-
return repo, err
2813-
}
2814-
2815-
if opts.GitContent && !templateRepo.IsEmpty {
2816-
if err = generateRepository(sess, repo, templateRepo); err != nil {
2817-
return repo, err
2818-
}
2819-
2820-
if err = repo.updateSize(sess); err != nil {
2821-
return repo, fmt.Errorf("failed to update size for repository: %v", err)
2822-
}
2823-
2824-
if err = copyLFS(sess, repo, templateRepo); err != nil {
2825-
return repo, fmt.Errorf("failed to copy LFS: %v", err)
2826-
}
2827-
}
2828-
2829-
if opts.Topics {
2830-
for _, topic := range templateRepo.Topics {
2831-
if _, err = addTopicByNameToRepo(sess, repo.ID, topic); err != nil {
2832-
return repo, err
2833-
}
2834-
}
2835-
}
2836-
2837-
return repo, sess.Commit()
2838-
}
2839-
28402744
// GetForks returns all the forks of the repository
28412745
func (repo *Repository) GetForks() ([]*Repository, error) {
28422746
forks := make([]*Repository, 0, repo.NumForks)

models/repo_generate.go

+162
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
// Copyright 2019 The Gitea Authors. All rights reserved.
2+
// Use of this source code is governed by a MIT-style
3+
// license that can be found in the LICENSE file.
4+
5+
package models
6+
7+
import (
8+
"fmt"
9+
"os"
10+
"path/filepath"
11+
"strings"
12+
"time"
13+
14+
"code.gitea.io/gitea/modules/git"
15+
"code.gitea.io/gitea/modules/log"
16+
17+
"github.com/unknwon/com"
18+
)
19+
20+
// generateRepository initializes repository from template
21+
func generateRepository(e Engine, repo, templateRepo *Repository) (err error) {
22+
tmpDir := filepath.Join(os.TempDir(), "gitea-"+repo.Name+"-"+com.ToStr(time.Now().Nanosecond()))
23+
24+
if err := os.MkdirAll(tmpDir, os.ModePerm); err != nil {
25+
return fmt.Errorf("Failed to create dir %s: %v", tmpDir, err)
26+
}
27+
28+
defer func() {
29+
if err := os.RemoveAll(tmpDir); err != nil {
30+
log.Error("RemoveAll: %v", err)
31+
}
32+
}()
33+
34+
if err = generateRepoCommit(e, repo, templateRepo, tmpDir); err != nil {
35+
return fmt.Errorf("generateRepoCommit: %v", err)
36+
}
37+
38+
// re-fetch repo
39+
if repo, err = getRepositoryByID(e, repo.ID); err != nil {
40+
return fmt.Errorf("getRepositoryByID: %v", err)
41+
}
42+
43+
repo.DefaultBranch = "master"
44+
if err = updateRepository(e, repo, false); err != nil {
45+
return fmt.Errorf("updateRepository: %v", err)
46+
}
47+
48+
return nil
49+
}
50+
51+
// GenerateRepository generates a repository from a template
52+
func GenerateRepository(ctx DBContext, doer, owner *User, templateRepo *Repository, opts GenerateRepoOptions) (_ *Repository, err error) {
53+
generateRepo := &Repository{
54+
OwnerID: owner.ID,
55+
Owner: owner,
56+
Name: opts.Name,
57+
LowerName: strings.ToLower(opts.Name),
58+
Description: opts.Description,
59+
IsPrivate: opts.Private,
60+
IsEmpty: !opts.GitContent || templateRepo.IsEmpty,
61+
IsFsckEnabled: templateRepo.IsFsckEnabled,
62+
TemplateID: templateRepo.ID,
63+
}
64+
65+
if err = createRepository(ctx.e, doer, owner, generateRepo); err != nil {
66+
return nil, err
67+
}
68+
69+
repoPath := RepoPath(owner.Name, generateRepo.Name)
70+
if err = checkInitRepository(repoPath); err != nil {
71+
return generateRepo, err
72+
}
73+
74+
return generateRepo, nil
75+
}
76+
77+
// GenerateGitContent generates git content from a template repository
78+
func GenerateGitContent(ctx DBContext, templateRepo, generateRepo *Repository) error {
79+
if err := generateRepository(ctx.e, generateRepo, templateRepo); err != nil {
80+
return err
81+
}
82+
83+
if err := generateRepo.updateSize(ctx.e); err != nil {
84+
return fmt.Errorf("failed to update size for repository: %v", err)
85+
}
86+
87+
if err := copyLFS(ctx.e, generateRepo, templateRepo); err != nil {
88+
return fmt.Errorf("failed to copy LFS: %v", err)
89+
}
90+
return nil
91+
}
92+
93+
// GenerateTopics generates topics from a template repository
94+
func GenerateTopics(ctx DBContext, templateRepo, generateRepo *Repository) error {
95+
for _, topic := range templateRepo.Topics {
96+
if _, err := addTopicByNameToRepo(ctx.e, generateRepo.ID, topic); err != nil {
97+
return err
98+
}
99+
}
100+
return nil
101+
}
102+
103+
// GenerateGitHooks generates git hooks from a template repository
104+
func GenerateGitHooks(ctx DBContext, templateRepo, generateRepo *Repository) error {
105+
generateGitRepo, err := git.OpenRepository(generateRepo.repoPath(ctx.e))
106+
if err != nil {
107+
return err
108+
}
109+
defer generateGitRepo.Close()
110+
111+
templateGitRepo, err := git.OpenRepository(templateRepo.repoPath(ctx.e))
112+
if err != nil {
113+
return err
114+
}
115+
defer templateGitRepo.Close()
116+
117+
templateHooks, err := templateGitRepo.Hooks()
118+
if err != nil {
119+
return err
120+
}
121+
122+
for _, templateHook := range templateHooks {
123+
generateHook, err := generateGitRepo.GetHook(templateHook.Name())
124+
if err != nil {
125+
return err
126+
}
127+
128+
generateHook.Content = templateHook.Content
129+
if err := generateHook.Update(); err != nil {
130+
return err
131+
}
132+
}
133+
return nil
134+
}
135+
136+
// GenerateWebhooks generates webhooks from a template repository
137+
func GenerateWebhooks(ctx DBContext, templateRepo, generateRepo *Repository) error {
138+
templateWebhooks, err := GetWebhooksByRepoID(templateRepo.ID)
139+
if err != nil {
140+
return err
141+
}
142+
143+
for _, templateWebhook := range templateWebhooks {
144+
generateWebhook := &Webhook{
145+
RepoID: generateRepo.ID,
146+
URL: templateWebhook.URL,
147+
HTTPMethod: templateWebhook.HTTPMethod,
148+
ContentType: templateWebhook.ContentType,
149+
Secret: templateWebhook.Secret,
150+
HookEvent: templateWebhook.HookEvent,
151+
IsActive: templateWebhook.IsActive,
152+
HookTaskType: templateWebhook.HookTaskType,
153+
OrgID: templateWebhook.OrgID,
154+
Events: templateWebhook.Events,
155+
Meta: templateWebhook.Meta,
156+
}
157+
if err := createWebhook(ctx.e, generateWebhook); err != nil {
158+
return err
159+
}
160+
}
161+
return nil
162+
}

modules/auth/repo_form.go

+2
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ type CreateRepoForm struct {
4040
RepoTemplate int64
4141
GitContent bool
4242
Topics bool
43+
GitHooks bool
44+
Webhooks bool
4345
}
4446

4547
// Validate validates the fields

options/locale/locale_en-US.ini

+3
Original file line numberDiff line numberDiff line change
@@ -637,6 +637,9 @@ reactions_more = and %d more
637637

638638
template.items = Template Items
639639
template.git_content = Git Content (Default Branch)
640+
template.git_hooks = Git Hooks
641+
template.git_hooks_tooltip = You are currently unable to modify or remove git hooks once added. Select this only if you trust the template repository.
642+
template.webhooks = Webhooks
640643
template.topics = Topics
641644
template.one_item = Must select at least one template item
642645
template.invalid = Must select a template repository

routers/repo/repo.go

+2
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,8 @@ func CreatePost(ctx *context.Context, form auth.CreateRepoForm) {
188188
Private: form.Private,
189189
GitContent: form.GitContent,
190190
Topics: form.Topics,
191+
GitHooks: form.GitHooks,
192+
Webhooks: form.Webhooks,
191193
}
192194

193195
if !opts.IsValid() {

services/repository/generate.go

+63
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
// Copyright 2019 The Gitea Authors. All rights reserved.
2+
// Use of this source code is governed by a MIT-style
3+
// license that can be found in the LICENSE file.
4+
5+
package repository
6+
7+
import (
8+
"code.gitea.io/gitea/models"
9+
"code.gitea.io/gitea/modules/log"
10+
"code.gitea.io/gitea/modules/notification"
11+
)
12+
13+
// GenerateRepository generates a repository from a template
14+
func GenerateRepository(doer, owner *models.User, templateRepo *models.Repository, opts models.GenerateRepoOptions) (_ *models.Repository, err error) {
15+
var generateRepo *models.Repository
16+
if err = models.WithTx(func(ctx models.DBContext) error {
17+
generateRepo, err = models.GenerateRepository(ctx, doer, owner, templateRepo, opts)
18+
if err != nil {
19+
return err
20+
}
21+
22+
// Git Content
23+
if opts.GitContent && !templateRepo.IsEmpty {
24+
if err = models.GenerateGitContent(ctx, templateRepo, generateRepo); err != nil {
25+
return err
26+
}
27+
}
28+
29+
// Topics
30+
if opts.Topics {
31+
if err = models.GenerateTopics(ctx, templateRepo, generateRepo); err != nil {
32+
return err
33+
}
34+
}
35+
36+
// Git Hooks
37+
if opts.GitHooks {
38+
if err = models.GenerateGitHooks(ctx, templateRepo, generateRepo); err != nil {
39+
return err
40+
}
41+
}
42+
43+
// Webhooks
44+
if opts.Webhooks {
45+
if err = models.GenerateWebhooks(ctx, templateRepo, generateRepo); err != nil {
46+
return err
47+
}
48+
}
49+
50+
return nil
51+
}); err != nil {
52+
if generateRepo != nil {
53+
if errDelete := models.DeleteRepository(doer, owner.ID, generateRepo.ID); errDelete != nil {
54+
log.Error("Rollback deleteRepository: %v", errDelete)
55+
}
56+
}
57+
return nil, err
58+
}
59+
60+
notification.NotifyCreateRepository(doer, owner, generateRepo)
61+
62+
return generateRepo, nil
63+
}

0 commit comments

Comments
 (0)