// NewSourceRepository creates a reference to a local or remote source code repository from
// a URL or path.
func NewSourceRepository(s string) (*SourceRepository, error) {
	location, err := git.ParseRepository(s)
	if err != nil {
		return nil, err
	}

	return &SourceRepository{
		location: s,
		url:      *location,
	}, nil
}
func (a *AutoLinkBuilds) Link() (map[string]gitserver.Clone, error) {
	errs := []error{}
	builders := []*buildapi.BuildConfig{}
	for _, namespace := range a.Namespaces {
		list, err := a.Client.BuildConfigs(namespace).List(labels.Everything(), fields.Everything())
		if err != nil {
			errs = append(errs, err)
			continue
		}
		for i := range list.Items {
			builders = append(builders, &list.Items[i])
		}
	}
	for _, b := range a.Builders {
		if hasItem(builders, b) {
			continue
		}
		config, err := a.Client.BuildConfigs(b.Namespace).Get(b.Name)
		if err != nil {
			errs = append(errs, err)
			continue
		}
		builders = append(builders, config)
	}

	hooks := make(map[string]string)
	if len(a.PostReceiveHook) > 0 {
		hooks["post-receive"] = a.PostReceiveHook
	}

	clones := make(map[string]gitserver.Clone)
	for _, builder := range builders {
		source := builder.Parameters.Source.Git
		if source == nil {
			continue
		}
		if builder.Annotations == nil {
			builder.Annotations = make(map[string]string)
		}

		// calculate the origin URL
		uri := source.URI
		if value, ok := builder.Annotations["git.openshift.io/origin-url"]; ok {
			uri = value
		}
		if len(uri) == 0 {
			continue
		}
		origin, err := git.ParseRepository(uri)
		if err != nil {
			errs = append(errs, err)
			continue
		}

		// calculate the local repository name and self URL
		name := builder.Name
		if a.CurrentNamespace != builder.Namespace {
			name = fmt.Sprintf("%s.%s", builder.Namespace, name)
		}
		name = fmt.Sprintf("%s.git", name)
		self := a.LinkFn(name)
		if self == nil {
			errs = append(errs, fmt.Errorf("no self URL available, can't update %s", name))
			continue
		}

		// we can't clone from ourself
		if self.Host == origin.Host {
			continue
		}

		// update the existing builder
		changed := false
		if builder.Annotations["git.openshift.io/origin-url"] != origin.String() {
			builder.Annotations["git.openshift.io/origin-url"] = origin.String()
			changed = true
		}
		if source.URI != self.String() {
			source.URI = self.String()
			changed = true
		}
		if changed {
			if _, err := a.Client.BuildConfigs(builder.Namespace).Update(builder); err != nil {
				errs = append(errs, err)
				continue
			}
		}

		clones[name] = gitserver.Clone{
			URL:   *origin,
			Hooks: hooks,
		}
	}
	return clones, errors.NewAggregate(errs)
}
// NewEnviromentConfig sets up the initial config from environment variables
func NewEnviromentConfig() (*Config, error) {
	config := NewDefaultConfig()

	home := os.Getenv("GIT_HOME")
	if len(home) == 0 {
		return nil, fmt.Errorf("GIT_HOME is required")
	}
	abs, err := filepath.Abs(home)
	if err != nil {
		return nil, fmt.Errorf("can't make %q absolute: %v", home, err)
	}
	if stat, err := os.Stat(abs); err != nil || !stat.IsDir() {
		return nil, fmt.Errorf("GIT_HOME must be an existing directory: %v", err)
	}
	config.Home = home

	if publicURL := os.Getenv("PUBLIC_URL"); len(publicURL) > 0 {
		valid, err := url.Parse(publicURL)
		if err != nil {
			return nil, fmt.Errorf("PUBLIC_URL must be a valid URL: %v", err)
		}
		config.URL = valid
	}

	gitpath := os.Getenv("GIT_PATH")
	if len(gitpath) == 0 {
		path, err := exec.LookPath("git")
		if err != nil {
			return nil, fmt.Errorf("could not find 'git' in PATH; specify GIT_PATH or set your PATH")
		}
		gitpath = path
	}
	config.GitBinary = gitpath

	config.AllowPush = os.Getenv("ALLOW_GIT_PUSH") != "no"
	config.AllowHooks = os.Getenv("ALLOW_GIT_HOOKS") != "no"
	config.AllowLazyCreate = os.Getenv("ALLOW_LAZY_CREATE") != "no"

	if hookpath := os.Getenv("HOOK_PATH"); len(hookpath) != 0 {
		path, err := filepath.Abs(hookpath)
		if err != nil {
			return nil, fmt.Errorf("HOOK_PATH was set but cannot be made absolute: %v", err)
		}
		if stat, err := os.Stat(path); err != nil || !stat.IsDir() {
			return nil, fmt.Errorf("HOOK_PATH must be an existing directory if set: %v", err)
		}
		config.HookDirectory = path
	}

	if value := os.Getenv("REQUIRE_GIT_AUTH"); len(value) > 0 {
		parts := strings.Split(value, ":")
		if len(parts) != 2 {
			return nil, fmt.Errorf("REQUIRE_GIT_AUTH must be a username and password separated by a ':'")
		}
		username, password := parts[0], parts[1]
		config.AuthenticatorFn = auth.Authenticator(func(info auth.AuthInfo) (bool, error) {
			if info.Push && !config.AllowPush {
				return false, nil
			}
			if info.Username != username || info.Password != password {
				return false, nil
			}
			return true, nil
		})
	}

	if value := os.Getenv("GIT_LISTEN"); len(value) > 0 {
		config.Listen = value
	}

	config.CleanBeforeClone = os.Getenv("GIT_FORCE_CLEAN") == "yes"

	clones := make(map[string]Clone)
	for _, env := range os.Environ() {
		if !strings.HasPrefix(env, initialClonePrefix) {
			continue
		}
		parts := strings.SplitN(env, "=", 2)
		if len(parts) != 2 {
			continue
		}
		key, value := parts[0], parts[1]
		part := key[len(initialClonePrefix):]
		if len(part) == 0 {
			continue
		}
		if len(value) == 0 {
			return nil, fmt.Errorf("%s must not have an empty value", key)
		}

		defaultName := strings.Replace(strings.ToLower(part), "_", "-", -1)
		values := strings.Split(value, ";")

		var uri, name string
		switch len(values) {
		case 1:
			uri, name = values[0], ""
		case 2:
			uri, name = values[0], values[1]
			if len(name) == 0 {
				return nil, fmt.Errorf("%s name may not be empty", key)
			}
		default:
			return nil, fmt.Errorf("%s may only have two segments (<url> or <url>;<name>)", key)
		}

		url, err := git.ParseRepository(uri)
		if err != nil {
			return nil, fmt.Errorf("%s is not a valid repository URI: %v", key, err)
		}
		switch url.Scheme {
		case "http", "https", "git", "ssh":
		default:
			return nil, fmt.Errorf("%s %q must be a http, https, git, or ssh URL", key, uri)
		}

		if len(name) == 0 {
			if n, ok := git.NameFromRepositoryURL(url); ok {
				name = n + ".git"
			}
		}
		if len(name) == 0 {
			name = defaultName + ".git"
		}

		if invalidCloneNameChars.MatchString(name) {
			return nil, fmt.Errorf("%s name %q must be only letters, numbers, dashes, or underscores", key, name)
		}
		if _, ok := reservedNames[name]; ok {
			return nil, fmt.Errorf("%s name %q is reserved (%v)", key, name, reservedNames)
		}

		clones[name] = Clone{
			URL: *url,
		}
	}
	config.InitialClones = clones

	return config, nil
}