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
5package integrations
6
7import (
8	"context"
9	"fmt"
10	"net"
11	"net/http"
12	"net/url"
13	"os"
14	"path"
15	"path/filepath"
16	"strconv"
17	"strings"
18	"testing"
19	"time"
20
21	"code.gitea.io/gitea/modules/git"
22	"code.gitea.io/gitea/modules/setting"
23	"code.gitea.io/gitea/modules/ssh"
24	"code.gitea.io/gitea/modules/util"
25
26	"github.com/stretchr/testify/assert"
27)
28
29func withKeyFile(t *testing.T, keyname string, callback func(string)) {
30
31	tmpDir, err := os.MkdirTemp("", "key-file")
32	assert.NoError(t, err)
33	defer util.RemoveAll(tmpDir)
34
35	err = os.Chmod(tmpDir, 0700)
36	assert.NoError(t, err)
37
38	keyFile := filepath.Join(tmpDir, keyname)
39	err = ssh.GenKeyPair(keyFile)
40	assert.NoError(t, err)
41
42	err = os.WriteFile(path.Join(tmpDir, "ssh"), []byte("#!/bin/bash\n"+
43		"ssh -o \"UserKnownHostsFile=/dev/null\" -o \"StrictHostKeyChecking=no\" -o \"IdentitiesOnly=yes\" -i \""+keyFile+"\" \"$@\""), 0700)
44	assert.NoError(t, err)
45
46	//Setup ssh wrapper
47	os.Setenv("GIT_SSH", path.Join(tmpDir, "ssh"))
48	os.Setenv("GIT_SSH_COMMAND",
49		"ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -o IdentitiesOnly=yes -i \""+keyFile+"\"")
50	os.Setenv("GIT_SSH_VARIANT", "ssh")
51
52	callback(keyFile)
53}
54
55func createSSHUrl(gitPath string, u *url.URL) *url.URL {
56	u2 := *u
57	u2.Scheme = "ssh"
58	u2.User = url.User("git")
59	u2.Host = net.JoinHostPort(setting.SSH.ListenHost, strconv.Itoa(setting.SSH.ListenPort))
60	u2.Path = gitPath
61	return &u2
62}
63
64func allowLFSFilters() []string {
65	// Now here we should explicitly allow lfs filters to run
66	filteredLFSGlobalArgs := make([]string, len(git.GlobalCommandArgs))
67	j := 0
68	for _, arg := range git.GlobalCommandArgs {
69		if strings.Contains(arg, "lfs") {
70			j--
71		} else {
72			filteredLFSGlobalArgs[j] = arg
73			j++
74		}
75	}
76	return filteredLFSGlobalArgs[:j]
77}
78
79func onGiteaRunTB(t testing.TB, callback func(testing.TB, *url.URL), prepare ...bool) {
80	if len(prepare) == 0 || prepare[0] {
81		defer prepareTestEnv(t, 1)()
82	}
83	s := http.Server{
84		Handler: c,
85	}
86
87	u, err := url.Parse(setting.AppURL)
88	assert.NoError(t, err)
89	listener, err := net.Listen("tcp", u.Host)
90	i := 0
91	for err != nil && i <= 10 {
92		time.Sleep(100 * time.Millisecond)
93		listener, err = net.Listen("tcp", u.Host)
94		i++
95	}
96	assert.NoError(t, err)
97	u.Host = listener.Addr().String()
98
99	defer func() {
100		ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute)
101		s.Shutdown(ctx)
102		cancel()
103	}()
104
105	go s.Serve(listener)
106	//Started by config go ssh.Listen(setting.SSH.ListenHost, setting.SSH.ListenPort, setting.SSH.ServerCiphers, setting.SSH.ServerKeyExchanges, setting.SSH.ServerMACs)
107
108	callback(t, u)
109}
110
111func onGiteaRun(t *testing.T, callback func(*testing.T, *url.URL), prepare ...bool) {
112	onGiteaRunTB(t, func(t testing.TB, u *url.URL) {
113		callback(t.(*testing.T), u)
114	}, prepare...)
115}
116
117func doGitClone(dstLocalPath string, u *url.URL) func(*testing.T) {
118	return func(t *testing.T) {
119		assert.NoError(t, git.CloneWithArgs(context.Background(), u.String(), dstLocalPath, allowLFSFilters(), git.CloneRepoOptions{}))
120		exist, err := util.IsExist(filepath.Join(dstLocalPath, "README.md"))
121		assert.NoError(t, err)
122		assert.True(t, exist)
123	}
124}
125
126func doPartialGitClone(dstLocalPath string, u *url.URL) func(*testing.T) {
127	return func(t *testing.T) {
128		assert.NoError(t, git.CloneWithArgs(context.Background(), u.String(), dstLocalPath, allowLFSFilters(), git.CloneRepoOptions{
129			Filter: "blob:none",
130		}))
131		exist, err := util.IsExist(filepath.Join(dstLocalPath, "README.md"))
132		assert.NoError(t, err)
133		assert.True(t, exist)
134	}
135}
136
137func doGitCloneFail(u *url.URL) func(*testing.T) {
138	return func(t *testing.T) {
139		tmpDir, err := os.MkdirTemp("", "doGitCloneFail")
140		assert.NoError(t, err)
141		defer util.RemoveAll(tmpDir)
142		assert.Error(t, git.Clone(u.String(), tmpDir, git.CloneRepoOptions{}))
143		exist, err := util.IsExist(filepath.Join(tmpDir, "README.md"))
144		assert.NoError(t, err)
145		assert.False(t, exist)
146	}
147}
148
149func doGitInitTestRepository(dstPath string) func(*testing.T) {
150	return func(t *testing.T) {
151		// Init repository in dstPath
152		assert.NoError(t, git.InitRepository(dstPath, false))
153		// forcibly set default branch to master
154		_, err := git.NewCommand("symbolic-ref", "HEAD", git.BranchPrefix+"master").RunInDir(dstPath)
155		assert.NoError(t, err)
156		assert.NoError(t, os.WriteFile(filepath.Join(dstPath, "README.md"), []byte(fmt.Sprintf("# Testing Repository\n\nOriginally created in: %s", dstPath)), 0644))
157		assert.NoError(t, git.AddChanges(dstPath, true))
158		signature := git.Signature{
159			Email: "test@example.com",
160			Name:  "test",
161			When:  time.Now(),
162		}
163		assert.NoError(t, git.CommitChanges(dstPath, git.CommitChangesOptions{
164			Committer: &signature,
165			Author:    &signature,
166			Message:   "Initial Commit",
167		}))
168	}
169}
170
171func doGitAddRemote(dstPath, remoteName string, u *url.URL) func(*testing.T) {
172	return func(t *testing.T) {
173		_, err := git.NewCommand("remote", "add", remoteName, u.String()).RunInDir(dstPath)
174		assert.NoError(t, err)
175	}
176}
177
178func doGitPushTestRepository(dstPath string, args ...string) func(*testing.T) {
179	return func(t *testing.T) {
180		_, err := git.NewCommand(append([]string{"push", "-u"}, args...)...).RunInDir(dstPath)
181		assert.NoError(t, err)
182	}
183}
184
185func doGitPushTestRepositoryFail(dstPath string, args ...string) func(*testing.T) {
186	return func(t *testing.T) {
187		_, err := git.NewCommand(append([]string{"push"}, args...)...).RunInDir(dstPath)
188		assert.Error(t, err)
189	}
190}
191
192func doGitCreateBranch(dstPath, branch string) func(*testing.T) {
193	return func(t *testing.T) {
194		_, err := git.NewCommand("checkout", "-b", branch).RunInDir(dstPath)
195		assert.NoError(t, err)
196	}
197}
198
199func doGitCheckoutBranch(dstPath string, args ...string) func(*testing.T) {
200	return func(t *testing.T) {
201		_, err := git.NewCommandNoGlobals(append(append(allowLFSFilters(), "checkout"), args...)...).RunInDir(dstPath)
202		assert.NoError(t, err)
203	}
204}
205
206func doGitMerge(dstPath string, args ...string) func(*testing.T) {
207	return func(t *testing.T) {
208		_, err := git.NewCommand(append([]string{"merge"}, args...)...).RunInDir(dstPath)
209		assert.NoError(t, err)
210	}
211}
212
213func doGitPull(dstPath string, args ...string) func(*testing.T) {
214	return func(t *testing.T) {
215		_, err := git.NewCommandNoGlobals(append(append(allowLFSFilters(), "pull"), args...)...).RunInDir(dstPath)
216		assert.NoError(t, err)
217	}
218}
219