private async Task CreateProjectAsync(AzureDevOpsRepositoryTarget source, AzureDevOpsRepository repo, CancellationToken cancellationToken) { var targetProjects = await GetProjectsAsync(cancellationToken); var matched = targetProjects.FirstOrDefault(p => p.Name == repo.Project); if (matched != null) { return; } // get project from source again to also mirror its visibility & description var sourceProjects = await source.GetProjectsAsync(cancellationToken); var toClone = sourceProjects.Single(p => p.Name == repo.Project); // properties are stored separate // first get project to get id (not set in list) var response = await source.HttpClient.GetAsync($"https://dev.azure.com/{source.DevOpsOrganization}/_apis/projects/{toClone.Name}?api-version=5.1", cancellationToken); response.EnsureSuccessStatusCode(); var sourceProject = await JsonSerializer.DeserializeAsync <Project>(await response.Content.ReadAsStreamAsync(), JsonSettings.Default, cancellationToken); // with project id we can query project properties // api is eternally in preview var properties = await source.GetCollectionAsync <NameValue>($"https://dev.azure.com/{source.DevOpsOrganization}/_apis/projects/{sourceProject.Id}/properties?api-version=5.1-preview.1", cancellationToken); // fallback to hardcoded default project template id var correctProcess = properties.FirstOrDefault(p => p.Name == "System.ProcessTemplateType")?.Value.GetString() ?? "b8a3a935-7e91-48b8-a94c-606d37c3e9f2"; var json = JsonSerializer.Serialize(new { name = toClone.Name, description = toClone.Description, visibility = toClone.Visibility, capabilities = new { versioncontrol = new { sourceControlType = "Git" }, processTemplate = new { templateTypeId = correctProcess } } }, JsonSettings.Default); response = await HttpClient.PostAsync($"https://dev.azure.com/{DevOpsOrganization}/_apis/projects?api-version=5.1", new StringContent(json, Encoding.UTF8, "application/json"), cancellationToken); response.EnsureSuccessStatusCode(); // should wait for project creation as it takes some time. if we don't then git repo creation fails // don't bother because function runs every 5 mins -> repo will be created soon after again until it succeeds }
public ContainerBuilder SetAzureDevopsRepository(AzureDevOpsRepository repository) { container.Setup(p => p.GetService(typeof(AzureDevOpsRepository))).Returns(repository); return(this); }
private async Task <IEnumerable <Item> > GetReadmeItemsFromRepository(Repository repository, AzureDevOpsRepository azureDevOpsRepo, AzureDevOpsMetadata metadata) { List <Item> readmeItems = new List <Item>(); try { HttpClient client = GetAzureDevopsHttpClient(metadata); var responseBody = await client.GetStringAsync( $"https://dev.azure.com/{repository.Name}/{azureDevOpsRepo.project.name}/_apis/git/repositories/{azureDevOpsRepo.name}/items?recursionLevel=full&includeContentMetadata=true&latestProcessedChange=true&includeLinks=true&api-version=5.1").ConfigureAwait(false); var itemsResponse = JsonConvert.DeserializeObject <ItemsResponse>(responseBody); foreach (var item in itemsResponse.Items) { if (item.path.EndsWith("readme.md", StringComparison.CurrentCultureIgnoreCase)) { readmeItems.Add(item); } } } catch (Exception ex) { //TODO: handle exceptions Console.WriteLine(ex); } return(readmeItems); }
public async Task <Mirror[]> GetExistingMirrorsAsync(CancellationToken cancellationToken) { await EnsureAccessToken(cancellationToken); var mirrors = new List <Mirror>(); var builds = await GetBuildsAsync(cancellationToken); var mirrorBuilds = builds.Where(b => b.Name.StartsWith(_config.BuildNamePrefix)).ToArray(); _log.LogInformation($"Looking for existing mirrors in {builds.Length} builds ({mirrorBuilds.Length} of those are mirror builds).."); foreach (var build in mirrorBuilds) { if (!_buildToCloneId.HasValue && _config.BuildToClone.Equals(build.Name, StringComparison.OrdinalIgnoreCase)) { _buildToCloneId = build.Id; } var repoName = build.Name.Substring(_config.BuildNamePrefix.Length); var repo = new Repository { Name = repoName }; if (_repositorySource.Type == "dev.azure.com" && repoName.Contains(" - ")) { var ado = (AzureDevOpsRepositoryTarget)_repositorySource; // ADO to ADO clone uses "Project - Repo" in build name when repo is in different repo than the build var parts = repoName.Split(" - "); repo = new AzureDevOpsRepository { Name = parts[1], GitUrl = $"https://dev.azure.com/{ado.DevOpsOrganization}/{parts[0]}/_git/{parts[1]}" }; } var expectedRepoUrls = _repositorySource.GetRepositoryUrls(repo); // repo property isn't loaded in overall list, so get definition // to ensure build is using correct source repo var buildDefinition = await GetBuildDefinitionAsync(build.Id, cancellationToken); var buildWithRepo = JsonSerializer.Deserialize <Build>(buildDefinition.GetRawText(), JsonSettings.Default); // normalize because github/gitlab allow both urls ending in .git and without // and Azure DevOps builds can also use both types of urls as source for builds var foundUrl = Normalize(buildWithRepo.Repository.Url); expectedRepoUrls = expectedRepoUrls.Select(Normalize).ToArray(); if (!expectedRepoUrls.Contains(foundUrl)) { throw new NotSupportedException($"Expected build '{build.Name}' to use one of these repos: '{string.Join(", ", expectedRepoUrls)}' as source but it uses '{buildWithRepo.Repository.Url}'"); } mirrors.Add(new Mirror { BuildName = build.Name, Id = build.Id, Repository = repo.Name }); } if (!_buildToCloneId.HasValue) { throw new InvalidOperationException($"Could not find the build to clone '{_config.BuildToClone}' while scanning builds in Azure DevOps"); } return(mirrors.ToArray()); }