func NewRepoContext() { zip.Verbose = false // Check if server has basic git setting. stdout, stderr, err := process.Exec("NewRepoContext(get setting)", "git", "config", "--get", "user.name") if strings.Contains(stderr, "fatal:") { log.Fatal("repo.NewRepoContext(fail to get git user.name): %s", stderr) } else if err != nil || len(strings.TrimSpace(stdout)) == 0 { if _, stderr, err = process.Exec("NewRepoContext(set email)", "git", "config", "--global", "user.email", "*****@*****.**"); err != nil { log.Fatal("repo.NewRepoContext(fail to set git user.email): %s", stderr) } else if _, stderr, err = process.Exec("NewRepoContext(set name)", "git", "config", "--global", "user.name", "Gogs"); err != nil { log.Fatal("repo.NewRepoContext(fail to set git user.name): %s", stderr) } } barePath := path.Join(setting.RepoRootPath, "git-bare.zip") if !com.IsExist(barePath) { data, err := bin.Asset("conf/content/git-bare.zip") if err != nil { log.Fatal("Fail to get asset 'git-bare.zip': %v", err) } else if err := ioutil.WriteFile(barePath, data, os.ModePerm); err != nil { log.Fatal("Fail to write asset 'git-bare.zip': %v", err) } } }
func addKey(e Engine, key *PublicKey) (err error) { // Calculate fingerprint. tmpPath := strings.Replace(path.Join(os.TempDir(), fmt.Sprintf("%d", time.Now().Nanosecond()), "id_rsa.pub"), "\\", "/", -1) os.MkdirAll(path.Dir(tmpPath), os.ModePerm) if err = ioutil.WriteFile(tmpPath, []byte(key.Content), 0644); err != nil { return err } stdout, stderr, err := process.Exec("AddPublicKey", "ssh-keygen", "-lf", tmpPath) if err != nil { return errors.New("ssh-keygen -lf: " + stderr) } else if len(stdout) < 2 { return errors.New("not enough output for calculating fingerprint: " + stdout) } key.Fingerprint = strings.Split(stdout, " ")[1] // Save SSH key. if _, err = e.Insert(key); err != nil { return err } // Don't need to rewrite this file if builtin SSH server is enabled. if setting.StartSSHServer { return nil } return saveAuthorizedKeyFile(key) }
// SSHKeyGenParsePublicKey extracts key type and length using ssh-keygen. func SSHKeyGenParsePublicKey(key string) (string, int, error) { // The ssh-keygen in Windows does not print key type, so no need go further. if setting.IsWindows { return "", 0, nil } tmpName, err := writeTmpKeyFile(key) if err != nil { return "", 0, fmt.Errorf("writeTmpKeyFile: %v", err) } defer os.Remove(tmpName) stdout, stderr, err := process.Exec("SSHKeyGenParsePublicKey", setting.SSH.KeygenPath, "-lf", tmpName) if err != nil { return "", 0, fmt.Errorf("Fail to parse public key: %s - %s", err, stderr) } if strings.Contains(stdout, "is not a public key file") { return "", 0, ErrKeyUnableVerify{stdout} } fields := strings.Split(stdout, " ") if len(fields) < 4 { return "", 0, fmt.Errorf("Invalid public key line: %s", stdout) } keyType := strings.Trim(fields[len(fields)-1], "()\r\n") return strings.ToLower(keyType), com.StrTo(fields[0]).MustInt(), nil }
func NewRepoContext() { zip.Verbose = false // Check Git installation. if _, err := exec.LookPath("git"); err != nil { log.Fatal(4, "Fail to test 'git' command: %v (forgotten install?)", err) } // Check Git version. ver, err := git.GetVersion() if err != nil { log.Fatal(4, "Fail to get Git version: %v", err) } reqVer, err := git.ParseVersion("1.7.1") if err != nil { log.Fatal(4, "Fail to parse required Git version: %v", err) } if ver.LessThan(reqVer) { log.Fatal(4, "Gogs requires Git version greater or equal to 1.7.1") } // Check if server has basic git setting and set if not. if stdout, stderr, err := process.Exec("NewRepoContext(get setting)", "git", "config", "--get", "user.name"); err != nil || strings.TrimSpace(stdout) == "" { // ExitError indicates user.name is not set if _, ok := err.(*exec.ExitError); ok || strings.TrimSpace(stdout) == "" { stndrdUserName := "******" stndrdUserEmail := "*****@*****.**" if _, stderr, gerr := process.Exec("NewRepoContext(set name)", "git", "config", "--global", "user.name", stndrdUserName); gerr != nil { log.Fatal(4, "Fail to set git user.name(%s): %s", gerr, stderr) } if _, stderr, gerr := process.Exec("NewRepoContext(set email)", "git", "config", "--global", "user.email", stndrdUserEmail); gerr != nil { log.Fatal(4, "Fail to set git user.email(%s): %s", gerr, stderr) } log.Info("Git user.name and user.email set to %s <%s>", stndrdUserName, stndrdUserEmail) } else { log.Fatal(4, "Fail to get git user.name(%s): %s", err, stderr) } } // Set git some configurations. if _, stderr, err := process.Exec("NewRepoContext(git config --global core.quotepath false)", "git", "config", "--global", "core.quotepath", "false"); err != nil { log.Fatal(4, "Fail to execute 'git config --global core.quotepath false': %s", stderr) } }
func NewRepoContext() { zip.Verbose = false // Check Git installation. if _, err := exec.LookPath("git"); err != nil { log.Fatal(4, "Fail to test 'git' command: %v (forgotten install?)", err) } // Check Git version. ver, err := git.GetVersion() if err != nil { log.Fatal(4, "Fail to get Git version: %v", err) } reqVer, err := git.ParseVersion("1.7.1") if err != nil { log.Fatal(4, "Fail to parse required Git version: %v", err) } if ver.LessThan(reqVer) { log.Fatal(4, "Gogs requires Git version greater or equal to 1.7.1") } // Git requires setting user.name and user.email in order to commit changes. for configKey, defaultValue := range map[string]string{"user.name": "Gogs", "user.email": "*****@*****.**"} { if stdout, stderr, err := process.Exec("NewRepoContext(get setting)", "git", "config", "--get", configKey); err != nil || strings.TrimSpace(stdout) == "" { // ExitError indicates this config is not set if _, ok := err.(*exec.ExitError); ok || strings.TrimSpace(stdout) == "" { if _, stderr, gerr := process.Exec("NewRepoContext(set "+configKey+")", "git", "config", "--global", configKey, defaultValue); gerr != nil { log.Fatal(4, "Fail to set git %s(%s): %s", configKey, gerr, stderr) } log.Info("Git config %s set to %s", configKey, defaultValue) } else { log.Fatal(4, "Fail to get git %s(%s): %s", configKey, err, stderr) } } } // Set git some configurations. if _, stderr, err := process.Exec("NewRepoContext(git config --global core.quotepath false)", "git", "config", "--global", "core.quotepath", "false"); err != nil { log.Fatal(4, "Fail to execute 'git config --global core.quotepath false': %s", stderr) } }
// CheckPublicKeyString checks if the given public key string is recognized by SSH. func CheckPublicKeyString(content string) (_ string, err error) { content, err = parseKeyString(content) if err != nil { return "", err } content = strings.TrimRight(content, "\n\r") if strings.ContainsAny(content, "\n\r") { return "", errors.New("only a single line with a single key please") } // write the key to a file… tmpFile, err := ioutil.TempFile(os.TempDir(), "keytest") if err != nil { return "", err } tmpPath := tmpFile.Name() defer os.Remove(tmpPath) tmpFile.WriteString(content) tmpFile.Close() // Check if ssh-keygen recognizes its contents. stdout, stderr, err := process.Exec("CheckPublicKeyString", "ssh-keygen", "-lf", tmpPath) if err != nil { return "", errors.New("ssh-keygen -lf: " + stderr) } else if len(stdout) < 2 { return "", errors.New("ssh-keygen returned not enough output to evaluate the key: " + stdout) } // The ssh-keygen in Windows does not print key type, so no need go further. if setting.IsWindows { return content, nil } sshKeygenOutput := strings.Split(stdout, " ") if len(sshKeygenOutput) < 4 { return content, ErrKeyUnableVerify{stdout} } // Check if key type and key size match. if !setting.Service.DisableMinimumKeySizeCheck { keySize := com.StrTo(sshKeygenOutput[0]).MustInt() if keySize == 0 { return "", errors.New("cannot get key size of the given key") } keyType := strings.Trim(sshKeygenOutput[len(sshKeygenOutput)-1], " ()\n") if minimumKeySize := setting.Service.MinimumKeySizes[keyType]; minimumKeySize == 0 { return "", fmt.Errorf("unrecognized public key type: %s", keyType) } else if keySize < minimumKeySize { return "", fmt.Errorf("the minimum accepted size of a public key %s is %d", keyType, minimumKeySize) } } return content, nil }
func NewRepoContext() { zip.Verbose = false // Check Git installation. if _, err := exec.LookPath("git"); err != nil { log.Fatal(4, "Fail to test 'git' command: %v (forgotten install?)", err) } // Check Git version. ver, err := git.GetVersion() if err != nil { log.Fatal(4, "Fail to get Git version: %v", err) } if ver.Major < 2 && ver.Minor < 8 { log.Fatal(4, "Gogs requires Git version greater or equal to 1.8.0") } // Check if server has basic git setting. stdout, stderr, err := process.Exec("NewRepoContext(get setting)", "git", "config", "--get", "user.name") if err != nil { log.Fatal(4, "Fail to get git user.name: %s", stderr) } else if err != nil || len(strings.TrimSpace(stdout)) == 0 { if _, stderr, err = process.Exec("NewRepoContext(set email)", "git", "config", "--global", "user.email", "*****@*****.**"); err != nil { log.Fatal(4, "Fail to set git user.email: %s", stderr) } else if _, stderr, err = process.Exec("NewRepoContext(set name)", "git", "config", "--global", "user.name", "Gogs"); err != nil { log.Fatal(4, "Fail to set git user.name: %s", stderr) } } // Set git some configurations. if _, stderr, err = process.Exec("NewRepoContext(git config --global core.quotepath false)", "git", "config", "--global", "core.quotepath", "false"); err != nil { log.Fatal(4, "Fail to execute 'git config --global core.quotepath false': %s", stderr) } }
// MigrateRepository migrates a existing repository from other project hosting. func MigrateRepository(u *User, name, desc string, private, mirror bool, url string) (*Repository, error) { repo, err := CreateRepository(u, name, desc, "", "", private, mirror, false) if err != nil { return nil, err } // Clone to temprory path and do the init commit. tmpDir := filepath.Join(os.TempDir(), fmt.Sprintf("%d", time.Now().Nanosecond())) os.MkdirAll(tmpDir, os.ModePerm) repoPath := RepoPath(u.Name, name) repo.IsBare = false if mirror { if err = MirrorRepository(repo.Id, u.Name, repo.Name, repoPath, url); err != nil { return repo, err } repo.IsMirror = true return repo, UpdateRepository(repo) } // TODO: need timeout. // Clone from local repository. _, stderr, err := process.Exec( fmt.Sprintf("MigrateRepository(git clone): %s", repoPath), "git", "clone", repoPath, tmpDir) if err != nil { return repo, errors.New("git clone: " + stderr) } // TODO: need timeout. // Pull data from source. if _, stderr, err = process.ExecDir( tmpDir, fmt.Sprintf("MigrateRepository(git pull): %s", repoPath), "git", "pull", url); err != nil { return repo, errors.New("git pull: " + stderr) } // TODO: need timeout. // Push data to local repository. if _, stderr, err = process.ExecDir( tmpDir, fmt.Sprintf("MigrateRepository(git push): %s", repoPath), "git", "push", "origin", "master"); err != nil { return repo, errors.New("git push: " + stderr) } return repo, UpdateRepository(repo) }
// CheckPublicKeyString checks if the given public key string is recognized by SSH. func CheckPublicKeyString(content string) (bool, error) { if strings.ContainsAny(content, "\n\r") { return false, errors.New("only a single line with a single key please") } // write the key to a file… tmpFile, err := ioutil.TempFile(os.TempDir(), "keytest") if err != nil { return false, err } tmpPath := tmpFile.Name() defer os.Remove(tmpPath) tmpFile.WriteString(content) tmpFile.Close() // Check if ssh-keygen recognizes its contents. stdout, stderr, err := process.Exec("CheckPublicKeyString", "ssh-keygen", "-l", "-f", tmpPath) if err != nil { return false, errors.New("ssh-keygen -l -f: " + stderr) } else if len(stdout) < 2 { return false, errors.New("ssh-keygen returned not enough output to evaluate the key: " + stdout) } // The ssh-keygen in Windows does not print key type, so no need go further. if setting.IsWindows { return true, nil } fmt.Println(stdout) sshKeygenOutput := strings.Split(stdout, " ") if len(sshKeygenOutput) < 4 { return false, ErrKeyUnableVerify } // Check if key type and key size match. keySize := com.StrTo(sshKeygenOutput[0]).MustInt() if keySize == 0 { return false, errors.New("cannot get key size of the given key") } keyType := strings.TrimSpace(sshKeygenOutput[len(sshKeygenOutput)-1]) if minimumKeySize := MinimumKeySize[keyType]; minimumKeySize == 0 { return false, errors.New("sorry, unrecognized public key type") } else if keySize < minimumKeySize { return false, fmt.Errorf("the minimum accepted size of a public key %s is %d", keyType, minimumKeySize) } return true, nil }
// MirrorRepository creates a mirror repository from source. func MirrorRepository(repoId int64, userName, repoName, repoPath, url string) error { // TODO: need timeout. _, stderr, err := process.Exec(fmt.Sprintf("MirrorRepository: %s/%s", userName, repoName), "git", "clone", "--mirror", url, repoPath) if err != nil { return errors.New("git clone --mirror: " + stderr) } if _, err = x.InsertOne(&Mirror{ RepoId: repoId, RepoName: strings.ToLower(userName + "/" + repoName), Interval: 24, NextUpdate: time.Now().Add(24 * time.Hour), }); err != nil { return err } return git.UnpackRefs(repoPath) }
// CheckPublicKeyString checks if the given public key string is recognized by SSH. func CheckPublicKeyString(content string) (bool, error) { if strings.ContainsAny(content, "\n\r") { return false, errors.New("Only a single line with a single key please") } // write the key to a file… tmpFile, err := ioutil.TempFile(os.TempDir(), "keytest") if err != nil { return false, err } tmpPath := tmpFile.Name() defer os.Remove(tmpPath) tmpFile.WriteString(content) tmpFile.Close() // … see if ssh-keygen recognizes its contents stdout, stderr, err := process.Exec("CheckPublicKeyString", "ssh-keygen", "-l", "-f", tmpPath) if err != nil { return false, errors.New("ssh-keygen -l -f: " + stderr) } else if len(stdout) < 2 { return false, errors.New("ssh-keygen returned not enough output to evaluate the key") } sshKeygenOutput := strings.Split(stdout, " ") if len(sshKeygenOutput) < 4 { return false, errors.New("Not enough fields returned by ssh-keygen -l -f") } keySize, err := com.StrTo(sshKeygenOutput[0]).Int() if err != nil { return false, errors.New("Cannot get key size of the given key") } keyType := strings.TrimSpace(sshKeygenOutput[len(sshKeygenOutput)-1]) if minimumKeySize := MinimumKeySize[keyType]; minimumKeySize == 0 { return false, errors.New("Sorry, unrecognized public key type") } else if keySize < minimumKeySize { return false, fmt.Errorf("The minimum accepted size of a public key %s is %d", keyType, minimumKeySize) } return true, nil }
// AddPublicKey adds new public key to database and authorized_keys file. func AddPublicKey(key *PublicKey) (err error) { has, err := x.Get(key) if err != nil { return err } else if has { return ErrKeyAlreadyExist } // Calculate fingerprint. tmpPath := strings.Replace(path.Join(os.TempDir(), fmt.Sprintf("%d", time.Now().Nanosecond()), "id_rsa.pub"), "\\", "/", -1) os.MkdirAll(path.Dir(tmpPath), os.ModePerm) if err = ioutil.WriteFile(tmpPath, []byte(key.Content), os.ModePerm); err != nil { return err } stdout, stderr, err := process.Exec("AddPublicKey", "ssh-keygen", "-l", "-f", tmpPath) if err != nil { return errors.New("ssh-keygen -l -f: " + stderr) } else if len(stdout) < 2 { return errors.New("not enough output for calculating fingerprint: " + stdout) } key.Fingerprint = strings.Split(stdout, " ")[1] if has, err := x.Get(&PublicKey{Fingerprint: key.Fingerprint}); err == nil && has { return ErrKeyAlreadyExist } // Save SSH key. if _, err = x.Insert(key); err != nil { return err } else if err = saveAuthorizedKeyFile(key); err != nil { // Roll back. if _, err2 := x.Delete(key); err2 != nil { return err2 } return err } return nil }
func prepareRepoCommit(repo *Repository, tmpDir, repoPath string, opts CreateRepoOptions) error { // Clone to temprory path and do the init commit. _, stderr, err := process.Exec( fmt.Sprintf("initRepository(git clone): %s", repoPath), "git", "clone", repoPath, tmpDir) if err != nil { return fmt.Errorf("git clone: %v - %s", err, stderr) } // README data, err := getRepoInitFile("readme", opts.Readme) if err != nil { return fmt.Errorf("getRepoInitFile[%s]: %v", opts.Readme, err) } cloneLink, err := repo.CloneLink() if err != nil { return fmt.Errorf("CloneLink: %v", err) } match := map[string]string{ "Name": repo.Name, "Description": repo.Description, "CloneURL.SSH": cloneLink.SSH, "CloneURL.HTTPS": cloneLink.HTTPS, } if err = ioutil.WriteFile(filepath.Join(tmpDir, "README.md"), []byte(com.Expand(string(data), match)), 0644); err != nil { return fmt.Errorf("write README.md: %v", err) } // .gitignore if len(opts.Gitignores) > 0 { var buf bytes.Buffer names := strings.Split(opts.Gitignores, ",") for _, name := range names { data, err = getRepoInitFile("gitignore", name) if err != nil { return fmt.Errorf("getRepoInitFile[%s]: %v", name, err) } buf.WriteString("# ---> " + name + "\n") buf.Write(data) buf.WriteString("\n") } if buf.Len() > 0 { if err = ioutil.WriteFile(filepath.Join(tmpDir, ".gitignore"), buf.Bytes(), 0644); err != nil { return fmt.Errorf("write .gitignore: %v", err) } } } // LICENSE if len(opts.License) > 0 { data, err = getRepoInitFile("license", opts.License) if err != nil { return fmt.Errorf("getRepoInitFile[%s]: %v", opts.License, err) } if err = ioutil.WriteFile(filepath.Join(tmpDir, "LICENSE"), data, 0644); err != nil { return fmt.Errorf("write LICENSE: %v", err) } } return nil }
// InitRepository initializes README and .gitignore if needed. func initRepository(e Engine, repoPath string, u *User, repo *Repository, initReadme bool, repoLang, license string) error { // Somehow the directory could exist. if com.IsExist(repoPath) { return fmt.Errorf("initRepository: path already exists: %s", repoPath) } // Init bare new repository. os.MkdirAll(repoPath, os.ModePerm) _, stderr, err := process.ExecDir(-1, repoPath, fmt.Sprintf("initRepository(git init --bare): %s", repoPath), "git", "init", "--bare") if err != nil { return fmt.Errorf("git init --bare: %s", err) } if err := createUpdateHook(repoPath); err != nil { return err } // Initialize repository according to user's choice. fileName := map[string]string{} if initReadme { fileName["readme"] = "README.md" } if repoLang != "" { fileName["gitign"] = ".gitignore" } if license != "" { fileName["license"] = "LICENSE" } // Clone to temprory path and do the init commit. tmpDir := filepath.Join(os.TempDir(), com.ToStr(time.Now().Nanosecond())) os.MkdirAll(tmpDir, os.ModePerm) defer os.RemoveAll(tmpDir) _, stderr, err = process.Exec( fmt.Sprintf("initRepository(git clone): %s", repoPath), "git", "clone", repoPath, tmpDir) if err != nil { return errors.New("git clone: " + stderr) } // README if initReadme { defaultReadme := repo.Name + "\n" + strings.Repeat("=", utf8.RuneCountInString(repo.Name)) + "\n\n" + repo.Description if err := ioutil.WriteFile(filepath.Join(tmpDir, fileName["readme"]), []byte(defaultReadme), 0644); err != nil { return err } } // FIXME: following two can be merged. // .gitignore // Copy custom file when available. customPath := path.Join(setting.CustomPath, "conf/gitignore", repoLang) targetPath := path.Join(tmpDir, fileName["gitign"]) if com.IsFile(customPath) { if err := com.Copy(customPath, targetPath); err != nil { return fmt.Errorf("copy gitignore: %v", err) } } else if com.IsSliceContainsStr(Gitignores, repoLang) { if err = ioutil.WriteFile(targetPath, bindata.MustAsset(path.Join("conf/gitignore", repoLang)), 0644); err != nil { return fmt.Errorf("generate gitignore: %v", err) } } else { delete(fileName, "gitign") } // LICENSE customPath = path.Join(setting.CustomPath, "conf/license", license) targetPath = path.Join(tmpDir, fileName["license"]) if com.IsFile(customPath) { if err = com.Copy(customPath, targetPath); err != nil { return fmt.Errorf("copy license: %v", err) } } else if com.IsSliceContainsStr(Licenses, license) { if err = ioutil.WriteFile(targetPath, bindata.MustAsset(path.Join("conf/license", license)), 0644); err != nil { return fmt.Errorf("generate license: %v", err) } } else { delete(fileName, "license") } if len(fileName) == 0 { // Re-fetch the repository from database before updating it (else it would // override changes that were done earlier with sql) if repo, err = getRepositoryById(e, repo.Id); err != nil { return err } repo.IsBare = true repo.DefaultBranch = "master" return updateRepository(e, repo, false) } // Apply changes and commit. return initRepoCommit(tmpDir, u.NewGitSig()) }
// InitRepository initializes README and .gitignore if needed. func initRepository(f string, u *User, repo *Repository, initReadme bool, repoLang, license string) error { repoPath := RepoPath(u.Name, repo.Name) // Create bare new repository. if err := extractGitBareZip(repoPath); err != nil { return err } // hook/post-update if err := createHookUpdate(filepath.Join(repoPath, "hooks", "update"), fmt.Sprintf(TPL_UPDATE_HOOK, setting.ScriptType, "\""+appPath+"\"")); err != nil { return err } // Initialize repository according to user's choice. fileName := map[string]string{} if initReadme { fileName["readme"] = "README.md" } if repoLang != "" { fileName["gitign"] = ".gitignore" } if license != "" { fileName["license"] = "LICENSE" } // Clone to temprory path and do the init commit. tmpDir := filepath.Join(os.TempDir(), com.ToStr(time.Now().Nanosecond())) os.MkdirAll(tmpDir, os.ModePerm) _, stderr, err := process.Exec( fmt.Sprintf("initRepository(git clone): %s", repoPath), "git", "clone", repoPath, tmpDir) if err != nil { return errors.New("initRepository(git clone): " + stderr) } // README if initReadme { defaultReadme := repo.Name + "\n" + strings.Repeat("=", utf8.RuneCountInString(repo.Name)) + "\n\n" + repo.Description if err := ioutil.WriteFile(filepath.Join(tmpDir, fileName["readme"]), []byte(defaultReadme), 0644); err != nil { return err } } // .gitignore filePath := "conf/gitignore/" + repoLang if com.IsFile(filePath) { targetPath := path.Join(tmpDir, fileName["gitign"]) if com.IsFile(filePath) { if err = com.Copy(filePath, targetPath); err != nil { return err } } else { // Check custom files. filePath = path.Join(setting.CustomPath, "conf/gitignore", repoLang) if com.IsFile(filePath) { if err := com.Copy(filePath, targetPath); err != nil { return err } } } } else { delete(fileName, "gitign") } // LICENSE filePath = "conf/license/" + license if com.IsFile(filePath) { targetPath := path.Join(tmpDir, fileName["license"]) if com.IsFile(filePath) { if err = com.Copy(filePath, targetPath); err != nil { return err } } else { // Check custom files. filePath = path.Join(setting.CustomPath, "conf/license", license) if com.IsFile(filePath) { if err := com.Copy(filePath, targetPath); err != nil { return err } } } } else { delete(fileName, "license") } if len(fileName) == 0 { repo.IsBare = true repo.DefaultBranch = "master" return UpdateRepository(repo) } // Apply changes and commit. return initRepoCommit(tmpDir, u.NewGitSig()) }