Exemple #1
0
        private bool ValidateUrls(HosterRepository repo)
        {
            bool result = true;

            var validator = new UrlHelper();

            if (!validator.UrlIsValid(repo.CloneUrl))
            {
                return(false);
            }

            if (repo.HasWiki)
            {
                if (!validator.UrlIsValid(repo.WikiUrl))
                {
                    return(false);
                }
            }

            if (repo.HasIssues)
            {
                if (!validator.UrlIsValid(repo.IssueUrl))
                {
                    return(false);
                }
            }

            return(result);
        }
Exemple #2
0
        public void IsPrivateWorks()
        {
            var sut = new HosterRepository("foo", "foo", "http://clone", ScmType.Git);

            sut.SetPrivate(true);

            Assert.True(sut.IsPrivate);
        }
Exemple #3
0
        public void SetIssuesWorks()
        {
            var sut = new HosterRepository("foo", "foo", "http://clone", ScmType.Git);

            sut.SetIssues(true, "url");

            Assert.True(sut.HasIssues);
            Assert.Equal("url", sut.IssueUrl);
        }
Exemple #4
0
        public void SetWikiWorks()
        {
            var sut = new HosterRepository("foo", "foo", "http://clone", ScmType.Git);

            sut.SetWiki(true, "url");

            Assert.True(sut.HasWiki);
            Assert.Equal("url", sut.WikiUrl);
        }
Exemple #5
0
        public void InvalidCharsInRepoNameAreReplaced(string inputName, string savedName)
        {
            var sut = new HosterRepository(inputName, "name", "http://clone", ScmType.Git);

            Assert.Equal(savedName, sut.FullName);

            sut = new HosterRepository(inputName, "name", "http://clone", ScmType.Git, false, "", false, "");
            Assert.Equal(savedName, sut.FullName);
        }
Exemple #6
0
        public void ThrowsWhenConfigSourceIsNull()
        {
            var factory = new FakeHosterFactory(new FakeHoster());
            var repo    = new HosterRepository("foo", "foo", "http://clone", ScmType.Git);

            var sut = new HosterBackupMaker(factory);

            Assert.Throws <ArgumentNullException>(() => sut.MakeBackup(null, repo, "foo"));
        }
Exemple #7
0
        public void MakeBackup(ConfigSource source, HosterRepository repo, string repoFolder)
        {
            if (source == null)
            {
                throw new ArgumentNullException(Resource.ConfigSourceIsNull);
            }

            var hoster = factory.Create(source.Hoster);

            hoster.Backup.MakeBackup(source, repo, repoFolder);
        }
Exemple #8
0
        public void BackupBaseExecutesAllSubMethods()
        {
            var repo = new HosterRepository("foo", "foo", "http://clone", ScmType.Git);

            repo.SetWiki(true, "http://wiki");
            repo.SetIssues(true, "http://issues");

            var sut = new FakeHosterBackup();

            sut.MakeBackup(new ConfigSource(), repo, @"c:\foo");

            Assert.True(sut.WasExecuted);
        }
Exemple #9
0
        public void MakeBackupCallsUnderlyingMethod()
        {
            var hoster = new FakeHoster();

            var factory = new FakeHosterFactory(hoster);
            var repo    = new HosterRepository("foo", "foo", "http://clone", ScmType.Git);

            repo.SetWiki(true, "http://wiki");
            repo.SetIssues(true, "http://issues");

            var reader = new FakeConfigReader();

            reader.SetDefaultFakeConfig();
            var config = reader.ReadConfig();
            var source = config.Sources.First();

            var sut = new HosterBackupMaker(factory);

            sut.MakeBackup(source, repo, "foo");

            Assert.True(hoster.FakeBackup.WasExecuted);
        }
        public List <HosterRepository> GetRepositoryList(ConfigSource source)
        {
            var    list      = new List <HosterRepository>();
            string className = this.GetType().Name;

            request.SetBaseUrl("https://api.bitbucket.org");

            if (source.IsAuthenticated)
            {
                request.AddBasicAuthHeader(source.AuthName, source.Password);
            }

            string url = "/2.0/repositories/" + source.Name;

            while (url != null)
            {
                var result = request.Execute(url).Result;

                if (result.IsSuccessStatusCode)
                {
                    var apiResponse = JsonConvert.DeserializeObject <BitbucketApiResponse>(result.Content);

                    // #60: 2 months after Bitbucket's HG deprecation, their API still returns HG repos but cloning/pulling them fails -> ignore them
                    foreach (var apiRepo in apiResponse.values.Where(x => x.scm.ToLower() != "hg"))
                    {
                        ScmType type;
                        switch (apiRepo.scm.ToLower())
                        {
                        case "git":
                            type = ScmType.Git;
                            break;

                        default:
                            throw new InvalidOperationException(string.Format(Resource.ApiInvalidScmType, apiRepo.full_name));
                        }

                        var    clone    = apiRepo.links.clone.Where(r => r.name == "https").First();
                        string cloneurl = clone.href;

                        var repo = new HosterRepository(apiRepo.full_name, apiRepo.slug, cloneurl, type);

                        repo.SetPrivate(apiRepo.is_private);

                        if (apiRepo.has_wiki)
                        {
                            string wikiUrl = cloneurl + "/wiki";
                            repo.SetWiki(true, wikiUrl.ToString());
                        }

                        // TODO: Issues

                        list.Add(repo);
                    }

                    url = apiResponse.next;
                }
                else
                {
                    switch (result.Status)
                    {
                    case HttpStatusCode.Unauthorized:
                        throw new AuthenticationException(string.Format(Resource.ApiAuthenticationFailed, source.AuthName));

                    case HttpStatusCode.Forbidden:
                        throw new SecurityException(Resource.ApiMissingPermissions);

                    case HttpStatusCode.NotFound:
                        throw new InvalidOperationException(string.Format(Resource.ApiInvalidUsername, source.Name));
                    }
                }
            }

            return(list);
        }
Exemple #11
0
        public List <HosterRepository> GetRepositoryList(ConfigSource config)
        {
            var repos = new List <HosterRepository>();

            this.req.SetBaseUrl("https://gitlab.com");
            this.req.AddHeader("Accept", "application/json");

            if (config.IsAuthenticated)
            {
                this.req.AddHeader("Private-Token", config.Password);
            }

            string type = "users";
            string args = string.Empty;

            if (config.Type.ToLower() != "user")
            {
                type = "groups";
                args = "?include_subgroups=true";
            }

            var scm = this.factory.Create(ScmType.Git);

            string url = string.Format("/api/v4/{0}/{1}/projects{2}", type, config.Name, args);

            while (url != null)
            {
                var result = req.Execute(url).Result;
                url = null;
                if (result.IsSuccessStatusCode)
                {
                    var response = JsonConvert.DeserializeObject <List <GitlabApiRepo> >(result.Content);

                    foreach (var apiRepo in response)
                    {
                        string cloneUrl = apiRepo.http_url_to_repo;

                        var repo = new HosterRepository(apiRepo.path_with_namespace, apiRepo.name, cloneUrl, ScmType.Git);

                        repo.SetPrivate(apiRepo.visibility == "private");

                        // wiki: the API only returns if it's enabled, but not if it actually contains pages
                        // The Git repo always exists, even when the wiki has no pages.
                        // So we can't check for the existence of the Git repo -> we need to make another API call to check if it has at least one page
                        if (apiRepo.wiki_enabled && cloneUrl.EndsWith(".git"))
                        {
                            // Rate limit of 10 requests per second per IP address (https://docs.gitlab.com/ee/user/gitlab_com/index.html#gitlabcom-specific-rate-limits)
                            // --> block long enough so that 10 requests will take longer than a second
                            Task.Delay(110).Wait();

                            string wikiUrl    = string.Format("/api/v4/projects/{0}/wikis", apiRepo.id);
                            var    wikiResult = req.Execute(wikiUrl).Result;
                            if (wikiResult.IsSuccessStatusCode)
                            {
                                var wikis = JsonConvert.DeserializeObject <List <GitlabApiWiki> >(wikiResult.Content);
                                if (wikis.Any())
                                {
                                    repo.SetWiki(true, cloneUrl.Substring(0, cloneUrl.Length - ".git".Length) + ".wiki.git");
                                }
                            }
                        }

                        // TODO: Issues
                        // _links -> issues ??
                        // issues_access_level ??

                        repos.Add(repo);
                    }

                    if (result.Headers.Contains("Link"))
                    {
                        // There are multiple links, but all in one header value
                        // https://docs.gitlab.com/ee/api/README.html#pagination-link-header
                        string links = result.Headers.GetValues("Link").First();

                        // The API returns something like this and we need the link named "next":
                        // <https://gitlab.com/api/foo>; rel="next", <https://gitlab.com/api/bar>; rel="first", <https://gitlab.com/api/baz>; rel="last"
                        foreach (var link in links.Split(','))
                        {
                            var items = link.Split(';');
                            if (items[1].Contains("next"))
                            {
                                url = items[0].Trim('<', '>', ' ');
                                break;
                            }
                        }
                    }
                }
                else
                {
                    switch (result.Status)
                    {
                    case HttpStatusCode.Unauthorized:
                        throw new AuthenticationException(string.Format(Resource.ApiAuthenticationFailed, config.AuthName));

                    case HttpStatusCode.Forbidden:
                        throw new SecurityException(Resource.ApiMissingPermissions);

                    case HttpStatusCode.NotFound:
                        throw new InvalidOperationException(string.Format(Resource.ApiInvalidUsername, config.Name));
                    }
                }
            }

            return(repos);
        }
Exemple #12
0
        public List <HosterRepository> GetRepositoryList(ConfigSource source)
        {
            var    list      = new List <HosterRepository>();
            string className = this.GetType().Name;

            var product = new ProductHeaderValue(this.context.UserAgent, this.context.VersionNumberString);
            var client  = new GitHubClient(product);

            if (source.IsAuthenticated)
            {
                var basicAuth = new Credentials(source.AuthName, source.Password);
                client.Credentials = basicAuth;
            }

            IReadOnlyList <Repository> repos = null;

            try
            {
                switch (source.Type.ToLower())
                {
                case "user":

                    if (source.IsAuthenticated)
                    {
                        // If authenticated, lists ALL repos for the user, include private ones (https://developer.github.com/v3/repos/#list-your-repositories)
                        repos = client.Repository.GetAllForCurrent().Result;
                    }
                    else
                    {
                        // GetAllForCurrent REQUIRES authentication, so we must use GetAllForUser when not authenticated to list public repos (https://developer.github.com/v3/repos/#list-user-repositories)
                        repos = client.Repository.GetAllForUser(source.Name).Result;
                    }

                    break;

                case "org":

                    repos = client.Repository.GetAllForOrg(source.Name).Result;
                    break;
                }
            }
            catch (Exception e)
            {
                string message = e.Message;

                if (e.InnerException is AuthorizationException)
                {
                    message = string.Format(Resource.ApiAuthenticationFailed, source.AuthName);
                }
                else if (e.InnerException is ForbiddenException)
                {
                    message = Resource.ApiMissingPermissions;
                }
                else if (e.InnerException is NotFoundException)
                {
                    message = string.Format(Resource.ApiInvalidUsername, source.Name);
                }

                throw new ApiException(message, e);
            }

            // Check the right scope:
            // #29: when authenticated, the personal access token must at least have the "repo" scope, otherwise the API doesn't return private repos
            // There are no tests for this, because this would require everybody to create a second token with insufficient permissions in order to run the integration tests.
            // -> so we just throw an exception here, which means that most of the integration tests will fail
            if (source.IsAuthenticated)
            {
                var info = client.GetLastApiInfo();
                if (!info.OauthScopes.Contains("repo"))
                {
                    throw new SecurityException(string.Format(Resource.ApiGithubNotEnoughScope, source.Title));
                }
            }

            var scm = this.factory.Create(ScmType.Git);

            if (repos != null)
            {
                // #29: GetAllForCurrent (see above) returns ALL repos the user has access to (for example, the repos of orgs where the user is a member).
                // We only want the repos under the user:
                var userRepos = repos.Where(r => r.Owner.Login == source.Name);

                foreach (var apiRepo in userRepos)
                {
                    var repo = new HosterRepository(apiRepo.FullName, apiRepo.Name, apiRepo.CloneUrl, ScmType.Git);

                    repo.SetPrivate(apiRepo.Private);

                    if (apiRepo.HasWiki && apiRepo.CloneUrl.EndsWith(".git"))
                    {
                        // build wiki clone URL, because API doesn't return it
                        string wikiUrl = apiRepo.CloneUrl.Substring(0, apiRepo.CloneUrl.Length - ".git".Length) + ".wiki.git";

                        // issue #13: the GitHub API only returns whether it's *possible* to create a wiki, but not if the repo actually *has* a wiki.
                        // So we need to skip the wiki when the URL (which we just built manually) is not a valid repository.
                        if (scm.RemoteRepositoryExists(wikiUrl))
                        {
                            repo.SetWiki(true, wikiUrl);
                        }
                    }

                    if (apiRepo.HasIssues)
                    {
                        // The API has only a URL for one issue (with a placeholder at the end), but this URL isn't in Octokit.
                        // So we have to build it manually:
                        repo.SetIssues(true, apiRepo.Url + "/issues/");
                    }

                    list.Add(repo);
                }
            }

            return(list);
        }
Exemple #13
0
        public List <HosterRepository> GetRepositoryList(ConfigSource source)
        {
            var    list      = new List <HosterRepository>();
            string className = this.GetType().Name;

            var product = new ProductHeaderValue(this.context.UserAgent, this.context.VersionNumberString);
            var client  = new GitHubClient(product);

            if (source.IsAuthenticated)
            {
                var basicAuth = new Credentials(source.AuthName, source.Password);
                client.Credentials = basicAuth;
            }

            IReadOnlyList <Repository> repos = null;

            try
            {
                switch (source.Type.ToLower())
                {
                case "user":

                    repos = client.Repository.GetAllForUser(source.Name).Result;
                    break;

                case "org":

                    repos = client.Repository.GetAllForOrg(source.Name).Result;
                    break;
                }
            }
            catch (Exception e)
            {
                string message = e.Message;

                if (e.InnerException is AuthorizationException)
                {
                    message = string.Format(Resource.ApiAuthenticationFailed, source.AuthName);
                }
                else if (e.InnerException is ForbiddenException)
                {
                    message = Resource.ApiMissingPermissions;
                }
                else if (e.InnerException is NotFoundException)
                {
                    message = string.Format(Resource.ApiInvalidUsername, source.Name);
                }

                throw new ApiException(message, e);
            }

            var scm = this.factory.Create(ScmType.Git);

            if (repos != null)
            {
                foreach (var apiRepo in repos)
                {
                    var repo = new HosterRepository(apiRepo.FullName, apiRepo.Name, apiRepo.CloneUrl, ScmType.Git);

                    repo.SetPrivate(apiRepo.Private);

                    if (apiRepo.HasWiki && apiRepo.CloneUrl.EndsWith(".git"))
                    {
                        // build wiki clone URL, because API doesn't return it
                        string wikiUrl = apiRepo.CloneUrl.Substring(0, apiRepo.CloneUrl.Length - ".git".Length) + ".wiki.git";

                        // issue #13: the GitHub API only returns whether it's *possible* to create a wiki, but not if the repo actually *has* a wiki.
                        // So we need to skip the wiki when the URL (which we just built manually) is not a valid repository.
                        if (scm.RemoteRepositoryExists(wikiUrl))
                        {
                            repo.SetWiki(true, wikiUrl);
                        }
                    }

                    if (apiRepo.HasIssues)
                    {
                        // The API has only a URL for one issue (with a placeholder at the end), but this URL isn't in Octokit.
                        // So we have to build it manually:
                        repo.SetIssues(true, apiRepo.Url + "/issues/");
                    }

                    list.Add(repo);
                }
            }

            return(list);
        }
Exemple #14
0
        public List <HosterRepository> GetRepositoryList(ConfigSource source)
        {
            var    list      = new List <HosterRepository>();
            string className = this.GetType().Name;

            request.SetBaseUrl("https://api.bitbucket.org");

            if (source.IsAuthenticated)
            {
                request.AddBasicAuthHeader(source.AuthName, source.Password);
            }

            string url         = string.Empty;
            string apiUsername = null;

            // Issue #32: from Apr 29 2019, usernames (not team names) must be replaced by UUIDs
            if (source.Type.ToLower() == "user")
            {
                url = "/2.0/users/" + source.Name;

                var result = request.Execute(url).Result;

                if (result.IsSuccessStatusCode)
                {
                    var apiResponse = JsonConvert.DeserializeObject <BitbucketApiUserResponse>(result.Content);
                    if (apiResponse != null)
                    {
                        apiUsername = Uri.EscapeUriString(apiResponse.uuid);
                    }
                }

                if (string.IsNullOrWhiteSpace(apiUsername))
                {
                    throw new InvalidOperationException(string.Format(Resource.ApiBitbucketCantGetUuid, source.Name));
                }
            }
            else
            {
                apiUsername = source.Name;
            }


            url = "/2.0/repositories/" + apiUsername;

            while (url != null)
            {
                var result = request.Execute(url).Result;

                if (result.IsSuccessStatusCode)
                {
                    var apiResponse = JsonConvert.DeserializeObject <BitbucketApiResponse>(result.Content);

                    foreach (var apiRepo in apiResponse.values)
                    {
                        ScmType type;
                        switch (apiRepo.scm.ToLower())
                        {
                        case "hg":
                            type = ScmType.Mercurial;
                            break;

                        case "git":
                            type = ScmType.Git;
                            break;

                        default:
                            throw new InvalidOperationException(string.Format(Resource.ApiInvalidScmType, apiRepo.full_name));
                        }

                        var    clone    = apiRepo.links.clone.Where(r => r.name == "https").First();
                        string cloneurl = clone.href;

                        var repo = new HosterRepository(apiRepo.full_name, apiRepo.slug, cloneurl, type);

                        repo.SetPrivate(apiRepo.is_private);

                        if (apiRepo.has_wiki)
                        {
                            string wikiUrl = cloneurl + "/wiki";
                            repo.SetWiki(true, wikiUrl.ToString());
                        }

                        // TODO: Issues

                        list.Add(repo);
                    }

                    url = apiResponse.next;
                }
                else
                {
                    switch (result.Status)
                    {
                    case HttpStatusCode.Unauthorized:
                        throw new AuthenticationException(string.Format(Resource.ApiAuthenticationFailed, source.AuthName));

                    case HttpStatusCode.Forbidden:
                        throw new SecurityException(Resource.ApiMissingPermissions);

                    case HttpStatusCode.NotFound:
                        throw new InvalidOperationException(string.Format(Resource.ApiInvalidUsername, source.Name));
                    }
                }
            }

            return(list);
        }