diff --git a/Makefile b/Makefile index 01e4a13f15f3..9a4dbf6ba063 100644 --- a/Makefile +++ b/Makefile @@ -420,3 +420,7 @@ generate-images: $(TMPDIR)/images/64.png $(TMPDIR)/images/128.png \ $(PWD)/public/img/favicon.ico rm -rf $(TMPDIR)/images + +.PHONY: pr +pr: + $(GO) run contrib/pr/checkout.go $(PR) diff --git a/contrib/pr/checkout.go b/contrib/pr/checkout.go new file mode 100644 index 000000000000..903a94454cd9 --- /dev/null +++ b/contrib/pr/checkout.go @@ -0,0 +1,256 @@ +package main + +/* +Checkout a PR and load the tests data into sqlite database +*/ + +import ( + "flag" + "fmt" + "io/ioutil" + "log" + "net/http" + "net/url" + "os" + "os/exec" + "os/user" + "path" + "path/filepath" + "runtime" + "time" + + "code.gitea.io/gitea/modules/markup/external" + "code.gitea.io/gitea/routers" + "code.gitea.io/gitea/routers/routes" + "github.com/Unknwon/com" + "github.com/go-xorm/xorm" + context2 "github.com/gorilla/context" + "gopkg.in/src-d/go-git.v4" + "gopkg.in/src-d/go-git.v4/config" + "gopkg.in/src-d/go-git.v4/plumbing" + "gopkg.in/testfixtures.v2" + + "code.gitea.io/gitea/models" + "code.gitea.io/gitea/modules/setting" +) + +var codeFilePath = "contrib/pr/checkout.go" + +func runPR() { + log.Printf("[PR] Starting gitea ...\n") + curDir, err := os.Getwd() + if err != nil { + log.Fatal(err) + } + setting.NewContext() + + setting.RepoRootPath, err = ioutil.TempDir(os.TempDir(), "repos") + if err != nil { + log.Fatalf("TempDir: %v\n", err) + } + setting.AppDataPath, err = ioutil.TempDir(os.TempDir(), "appdata") + if err != nil { + log.Fatalf("TempDir: %v\n", err) + } + setting.AppWorkPath = curDir + setting.StaticRootPath = curDir + setting.GravatarSourceURL, err = url.Parse("https://secure.gravatar.com/avatar/") + if err != nil { + log.Fatalf("url.Parse: %v\n", err) + } + + setting.AppURL = "http://localhost:8080/" + setting.HTTPPort = "8080" + setting.SSH.Domain = "localhost" + setting.SSH.Port = 3000 + setting.InstallLock = true + setting.SecretKey = "9pCviYTWSb" + setting.InternalToken = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYmYiOjE0OTI3OTU5ODN9.OQkH5UmzID2XBdwQ9TAI6Jj2t1X-wElVTjbE7aoN4I8" + curUser, err := user.Current() + if err != nil { + log.Fatal(err) + } + setting.RunUser = curUser.Username + + log.Printf("[PR] Loading fixtures data ...\n") + setting.CheckLFSVersion() + //models.LoadConfigs() + /* + models.DbCfg.Type = "sqlite3" + models.DbCfg.Path = ":memory:" + models.DbCfg.Timeout = 500 + */ + db := setting.Cfg.Section("database") + db.NewKey("DB_TYPE", "sqlite3") + db.NewKey("PATH", ":memory:") + setting.LogSQL = true + models.LoadConfigs() + routers.NewServices() + //x, err = xorm.NewEngine("sqlite3", "file::memory:?cache=shared") + + var helper testfixtures.Helper + helper = &testfixtures.SQLite{} + models.NewEngine(func(_ *xorm.Engine) error { + return nil + }) + models.HasEngine = true + //x.ShowSQL(true) + err = models.InitFixtures( + helper, + path.Join(curDir, "models/fixtures/"), + ) + if err != nil { + fmt.Printf("Error initializing test database: %v\n", err) + os.Exit(1) + } + models.LoadFixtures() + os.RemoveAll(setting.RepoRootPath) + os.RemoveAll(models.LocalCopyPath()) + os.RemoveAll(models.LocalWikiPath()) + com.CopyDir(path.Join(curDir, "integrations/gitea-repositories-meta"), setting.RepoRootPath) + + log.Printf("[PR] Setting up router\n") + //routers.GlobalInit() + external.RegisterParsers() + m := routes.NewMacaron() + routes.RegisterRoutes(m) + + log.Printf("[PR] Ready for testing !\n") + log.Printf("[PR] Login with user1, user2, user3, ... with pass: password\n") + /* + log.Info("Listen: %v://%s%s", setting.Protocol, listenAddr, setting.AppSubURL) + + if setting.LFS.StartServer { + log.Info("LFS server enabled") + } + + if setting.EnablePprof { + go func() { + log.Info("Starting pprof server on localhost:6060") + log.Info("%v", http.ListenAndServe("localhost:6060", nil)) + }() + } + */ + + //Start the server + http.ListenAndServe(":8080", context2.ClearHandler(m)) + + log.Printf("[PR] Cleaning up ...\n") + /* + if err = os.RemoveAll(setting.Indexer.IssuePath); err != nil { + fmt.Printf("os.RemoveAll: %v\n", err) + os.Exit(1) + } + if err = os.RemoveAll(setting.Indexer.RepoPath); err != nil { + fmt.Printf("Unable to remove repo indexer: %v\n", err) + os.Exit(1) + } + */ + if err = os.RemoveAll(setting.RepoRootPath); err != nil { + log.Fatalf("os.RemoveAll: %v\n", err) + } + if err = os.RemoveAll(setting.AppDataPath); err != nil { + log.Fatalf("os.RemoveAll: %v\n", err) + } +} + +func main() { + var runPRFlag = flag.Bool("run", false, "Run the PR code") + flag.Parse() + if *runPRFlag { + runPR() + return + } + + //Otherwise checkout PR + if len(os.Args) != 2 { + log.Fatal("Need only one arg: the PR number") + } + pr := os.Args[1] + + codeFilePath = filepath.FromSlash(codeFilePath) //Convert to running OS + + //Copy this file if it will not exist in the PR branch + dat, err := ioutil.ReadFile(codeFilePath) + if err != nil { + log.Fatalf("Failed to cache this code file : %v", err) + } + + repo, err := git.PlainOpen(".") + if err != nil { + log.Fatalf("Failed to open the repo : %v", err) + } + + //Find remote upstream + remotes, err := repo.Remotes() + if err != nil { + log.Fatalf("Failed to list remotes of repo : %v", err) + } + remoteUpstream := "origin" //Default + for _, r := range remotes { + if r.Config().URLs[0] == "https://github.com/go-gitea/gitea" || r.Config().URLs[0] == "git@github.com:go-gitea/gitea.git" { //fetch at index 0 + remoteUpstream = r.Config().Name + break + } + } + + branch := fmt.Sprintf("pr-%s-%d", pr, time.Now().Unix()) + branchRef := plumbing.NewBranchReferenceName(branch) + + log.Printf("Fetching PR #%s in %s\n", pr, branch) + if runtime.GOOS == "windows" { + //Use git cli command for windows + runCmd("git", "fetch", "origin", fmt.Sprintf("pull/%s/head:%s", pr, branch)) + } else { + ref := fmt.Sprintf("refs/pull/%s/head:%s", pr, branchRef) + err = repo.Fetch(&git.FetchOptions{ + RemoteName: remoteUpstream, + RefSpecs: []config.RefSpec{ + config.RefSpec(ref), + }, + }) + if err != nil { + log.Fatalf("Failed to fetch %s from %s : %v", ref, remoteUpstream, err) + } + } + + tree, err := repo.Worktree() + if err != nil { + log.Fatalf("Failed to parse git tree : %v", err) + } + log.Printf("Checkout PR #%s in %s\n", pr, branch) + err = tree.Checkout(&git.CheckoutOptions{ + Branch: branchRef, + //Force: runtime.GOOS == "windows", + }) + if err != nil { + log.Fatalf("Failed to checkout %s : %v", branch, err) + } + + //Copy this file if not exist + if _, err := os.Stat(codeFilePath); os.IsNotExist(err) { + err = os.MkdirAll(filepath.Dir(codeFilePath), 0755) + if err != nil { + log.Fatalf("Failed to duplicate this code file in PR : %v", err) + } + err = ioutil.WriteFile(codeFilePath, dat, 0644) + if err != nil { + log.Fatalf("Failed to duplicate this code file in PR : %v", err) + } + } + time.Sleep(5 * time.Second) + //Start with integration test + runCmd("go", "run", "-tags", "sqlite sqlite_unlock_notify", codeFilePath, "-run") +} +func runCmd(cmd ...string) { + log.Printf("Executing : %s ...\n", cmd) + c := exec.Command(cmd[0], cmd[1:]...) + c.Stdout = os.Stdout + c.Stderr = os.Stderr + if err := c.Start(); err != nil { + log.Panicln(err) + } + if err := c.Wait(); err != nil { + log.Panicln(err) + } +}