예제 #1
0
        public override async Task <int> ExecuteAsync()
        {
            Local local = new Local(Logger);

            try
            {
                IEnumerable <DependencyDetail> dependencies = await local.GetDependenciesAsync(_options.Name);

                if (!string.IsNullOrEmpty(_options.Name))
                {
                    DependencyDetail dependency = dependencies.Where(d => d.Name.Equals(_options.Name, StringComparison.InvariantCultureIgnoreCase)).FirstOrDefault();

                    if (dependency == null)
                    {
                        throw new Exception($"A dependency with name '{_options.Name}' was not found...");
                    }

                    LogDependency(dependency);
                }

                foreach (DependencyDetail dependency in dependencies)
                {
                    LogDependency(dependency);

                    Console.WriteLine();
                }

                return(Constants.SuccessCode);
            }
            catch (Exception exc)
            {
                if (!string.IsNullOrEmpty(_options.Name))
                {
                    Logger.LogError(exc, $"Something failed while querying for local dependency '{_options.Name}'.");
                }
                else
                {
                    Logger.LogError(exc, "Something failed while querying for local dependencies.");
                }

                return(Constants.ErrorCode);
            }
        }
        public void ValidateGraphWithNoChildNodes()
        {
            DependencyDetail dependencyDetail = new DependencyDetail
            {
                Name    = "Base.Asset",
                RepoUri = "RepoX",
                Commit  = "shaX",
                Version = "1.0.0"
            };

            DependencyTestDriver.GetGraphAndCompare("DependencyGraph", async driver =>
            {
                return(await driver.GetDependencyGraph(dependencyDetail));
            },
                                                    GetExpectedDependencyGraphAsync,
                                                    dependencyDetail,
                                                    DefaultOutputFileName,
                                                    true);
        }
        public void ValidateIncompleteFullGraph()
        {
            DependencyDetail dependencyDetail = new DependencyDetail
            {
                Name    = "Base.Asset",
                RepoUri = "RepoA",
                Commit  = "shaA",
                Version = "1.0.0"
            };

            DependencyTestDriver.GetGraphAndCompare("DependencyGraph", async driver =>
            {
                return(await driver.GetDependencyGraph(dependencyDetail));
            },
                                                    GetExpectedDependencyGraphAsync,
                                                    dependencyDetail,
                                                    "output1.xml",
                                                    false);
        }
예제 #4
0
        private async Task <int> NonCoherencyUpdatesForBuildAsync(
            Build build,
            IRemote barOnlyRemote,
            List <DependencyDetail> currentDependencies,
            List <DependencyDetail> dependenciesToUpdate)
        {
            IEnumerable <AssetData> assetData = build.Assets.Select(
                a => new AssetData(a.NonShipping)
            {
                Name    = a.Name,
                Version = a.Version
            });

            string repository = build.GitHubRepository ?? build.AzureDevOpsRepository;

            // Now determine what needs to be updated.
            List <DependencyUpdate> updates = await barOnlyRemote.
                                              GetRequiredNonCoherencyUpdatesAsync(repository, build.Commit, assetData, currentDependencies);

            foreach (DependencyUpdate update in updates)
            {
                DependencyDetail from = update.From;
                DependencyDetail to   = update.To;

                // Print out what we are going to do.
                Console.WriteLine($"Updating '{from.Name}': '{from.Version}' => '{to.Version}'"
                                  + $" (from build '{build.AzureDevOpsBuildNumber}' of '{repository}')");

                // Replace in the current dependencies list so the correct data can be used in coherency updates.
                currentDependencies.Remove(from);
                currentDependencies.Add(to);

                // Final list of dependencies to update
                dependenciesToUpdate.Add(to);
            }

            return(Constants.SuccessCode);
        }
예제 #5
0
        public async Task CoherencyUpdateTests8()
        {
            // Initialize
            var    barClientMock = new Mock <IBarClient>();
            Remote remote        = new Remote(null, barClientMock.Object, NullLogger.Instance);

            // Mock the remote used by build dependency graph to gather dependency details.
            var dependencyGraphRemoteMock = new Mock <IRemote>();

            // Always return the main remote.
            var remoteFactoryMock = new Mock <IRemoteFactory>();

            remoteFactoryMock.Setup(m => m.GetRemoteAsync(It.IsAny <string>(), It.IsAny <ILogger>())).ReturnsAsync(dependencyGraphRemoteMock.Object);
            remoteFactoryMock.Setup(m => m.GetBarOnlyRemoteAsync(It.IsAny <ILogger>())).ReturnsAsync(remote);

            List <DependencyDetail> existingDetails = new List <DependencyDetail>();
            DependencyDetail        depA            = AddDependency(existingDetails, "depA", "v1", "repoA", "commit1", pinned: false);
            DependencyDetail        depB            = AddDependency(existingDetails, "depB", "v3", "repoB", "commit1", pinned: false, coherentParent: "depA");

            List <AssetData> assets = new List <AssetData>()
            {
                new AssetData(false)
                {
                    Name = "depA", Version = "v2"
                },
                new AssetData(false)
                {
                    Name = "depB", Version = "v5"
                }
            };

            BuildProducesAssets(barClientMock, "repoA", "commit2", new List <(string name, string version)>
            {
                ("depA", "v2")
            });

            List <DependencyDetail> repoADeps = new List <DependencyDetail>();

            AddDependency(repoADeps, "depC", "v10", "repoB", "commit5", pinned: false);
            RepoHasDependencies(dependencyGraphRemoteMock, "repoA", "commit2", repoADeps);

            BuildProducesAssets(barClientMock, "repoB", "commit5", new List <(string name, string version)>
            {
                ("depC", "v10")
            });

            List <DependencyUpdate> nonCoherencyUpdates =
                await remote.GetRequiredNonCoherencyUpdatesAsync("repoA", "commit2", assets, existingDetails);

            Assert.Collection(nonCoherencyUpdates,
                              u =>
            {
                Assert.Equal(depA, u.From);
                Assert.Equal("v2", u.To.Version);
            });

            // Update the current dependency details with the non coherency updates
            UpdateCurrentDependencies(existingDetails, nonCoherencyUpdates);

            await Assert.ThrowsAsync <DarcException>(() => remote.GetRequiredCoherencyUpdatesAsync(
                                                         existingDetails, remoteFactoryMock.Object));
        }
예제 #6
0
 internal bool HasDependencyOn(DependencyDetail dep)
 {
     return(HasDependencyOn(dep.RepoUri));
 }
예제 #7
0
 internal void AddDependency(DependencyDetail dep)
 {
     this.AddDependency(GetDependency(dep));
 }
예제 #8
0
 private StrippedDependency(DependencyDetail d) : this(d.RepoUri, d.Commit)
 {
 }
예제 #9
0
 internal static StrippedDependency GetDependency(DependencyDetail d)
 {
     return(GetDependency(d.RepoUri, d.Commit));
 }
예제 #10
0
        private async Task <int> CoherencyUpdatesAsync(
            IRemote barOnlyRemote,
            IRemoteFactory remoteFactory,
            List <DependencyDetail> currentDependencies,
            List <DependencyDetail> dependenciesToUpdate)
        {
            Console.WriteLine("Checking for coherency updates...");

            CoherencyMode coherencyMode = CoherencyMode.Strict;

            if (_options.LegacyCoherency)
            {
                coherencyMode = CoherencyMode.Legacy;
            }
            else
            {
                Console.WriteLine("Using 'Strict' coherency mode. If this fails, a second attempt utilizing 'Legacy' Coherency mode will be made.");
            }

            List <DependencyUpdate> coherencyUpdates = null;
            bool updateSucceeded = false;

            try
            {
                // Now run a coherency update based on the current set of dependencies updated from the previous pass.
                coherencyUpdates = await barOnlyRemote.GetRequiredCoherencyUpdatesAsync(currentDependencies, remoteFactory, coherencyMode);

                updateSucceeded = true;
            }
            catch (DarcCoherencyException e)
            {
                PrettyPrintCoherencyErrors(e);
            }

            if (coherencyMode == CoherencyMode.Strict && !updateSucceeded)
            {
                Console.WriteLine("Attempting fallback to Legacy coherency");
                try
                {
                    // Now run a coherency update based on the current set of dependencies updated from the previous pass.
                    coherencyUpdates = await barOnlyRemote.GetRequiredCoherencyUpdatesAsync(currentDependencies, remoteFactory, CoherencyMode.Legacy);

                    Console.WriteLine("... Legacy-mode fallback worked");
                    updateSucceeded = true;
                }
                catch (DarcCoherencyException e)
                {
                    PrettyPrintCoherencyErrors(e);
                }
            }
            if (!updateSucceeded)
            {
                return(Constants.ErrorCode);
            }

            foreach (DependencyUpdate dependencyUpdate in coherencyUpdates)
            {
                DependencyDetail from            = dependencyUpdate.From;
                DependencyDetail to              = dependencyUpdate.To;
                DependencyDetail coherencyParent = currentDependencies.First(d =>
                                                                             d.Name.Equals(from.CoherentParentDependencyName, StringComparison.OrdinalIgnoreCase));
                // Print out what we are going to do.
                Console.WriteLine($"Updating '{from.Name}': '{from.Version}' => '{to.Version}' " +
                                  $"to ensure coherency with {from.CoherentParentDependencyName}@{coherencyParent.Version}");

                // Final list of dependencies to update
                dependenciesToUpdate.Add(to);
            }

            return(Constants.SuccessCode);
        }
        public ScenarioTests_GitHubFlow()
        {
            using TestParameters parameters = TestParameters.GetAsync().Result;
            SetTestParameters(parameters);

            source1Assets          = GetAssetData("Foo", "1.1.0", "Bar", "2.1.0");
            source2Assets          = GetAssetData("Pizza", "3.1.0", "Hamburger", "4.1.0");
            source1AssetsUpdated   = GetAssetData("Foo", "1.17.0", "Bar", "2.17.0");
            childSourceBuildAssets = GetAssetData("Baz", "1.3.0", "Bop", "1.0");
            childSourceAssets      = GetSingleAssetData("Baz", "1.3.0");

            expectedDependenciesSource1 = new List <DependencyDetail>();
            string           sourceRepoUri = GetRepoUrl(TestRepository.TestRepo1Name);
            DependencyDetail foo           = new DependencyDetail
            {
                Name    = "Foo",
                Version = "1.1.0",
                RepoUri = sourceRepoUri,
                Commit  = TestRepository.CoherencyTestRepo1Commit,
                Type    = DependencyType.Product,
                Pinned  = false
            };

            expectedDependenciesSource1.Add(foo);

            DependencyDetail bar = new DependencyDetail
            {
                Name    = "Bar",
                Version = "2.1.0",
                RepoUri = sourceRepoUri,
                Commit  = TestRepository.CoherencyTestRepo1Commit,
                Type    = DependencyType.Product,
                Pinned  = false
            };

            expectedDependenciesSource1.Add(bar);

            expectedDependenciesSource2 = new List <DependencyDetail>();
            string           source2RepoUri = GetRepoUrl(TestRepository.TestRepo3Name);
            DependencyDetail pizza          = new DependencyDetail
            {
                Name    = "Pizza",
                Version = "3.1.0",
                RepoUri = source2RepoUri,
                Commit  = TestRepository.CoherencyTestRepo1Commit,
                Type    = DependencyType.Product,
                Pinned  = false
            };

            expectedDependenciesSource2.Add(pizza);

            DependencyDetail hamburger = new DependencyDetail
            {
                Name    = "Hamburger",
                Version = "4.1.0",
                RepoUri = source2RepoUri,
                Commit  = TestRepository.CoherencyTestRepo1Commit,
                Type    = DependencyType.Product,
                Pinned  = false
            };

            expectedDependenciesSource2.Add(hamburger);

            expectedDependenciesSource1Updated = new List <DependencyDetail>();
            DependencyDetail fooUpdated = new DependencyDetail
            {
                Name    = "Foo",
                Version = "1.1.0",
                RepoUri = sourceRepoUri,
                Commit  = TestRepository.CoherencyTestRepo1Commit,
                Type    = DependencyType.Product,
                Pinned  = false
            };

            expectedDependenciesSource1Updated.Add(fooUpdated);

            DependencyDetail barUpdated = new DependencyDetail
            {
                Name    = "Bar",
                Version = "2.1.0",
                RepoUri = sourceRepoUri,
                Commit  = TestRepository.CoherencyTestRepo1Commit,
                Type    = DependencyType.Product,
                Pinned  = false
            };

            expectedDependenciesSource1Updated.Add(barUpdated);

            expectedCoherencyDependencies = new List <DependencyDetail>();
            DependencyDetail baz = new DependencyDetail
            {
                Name    = "Baz",
                Version = "1.3.0",
                RepoUri = GetRepoUrl(TestRepository.TestRepo1Name),
                Commit  = TestRepository.CoherencyTestRepo2Commit,
                Type    = DependencyType.Product,
                Pinned  = false,
                CoherentParentDependencyName = "Foo"
            };

            DependencyDetail parentFoo = new DependencyDetail
            {
                Name    = "Foo",
                Version = "1.1.0",
                RepoUri = GetRepoUrl(TestRepository.TestRepo2Name),
                Commit  = TestRepository.CoherencyTestRepo1Commit,
                Type    = DependencyType.Product,
                Pinned  = false
            };

            DependencyDetail parentBar = new DependencyDetail
            {
                Name    = "Bar",
                Version = "2.1.0",
                RepoUri = GetRepoUrl(TestRepository.TestRepo2Name),
                Commit  = TestRepository.CoherencyTestRepo1Commit,
                Type    = DependencyType.Product,
                Pinned  = false
            };

            expectedCoherencyDependencies.Add(parentFoo);
            expectedCoherencyDependencies.Add(parentBar);
            expectedCoherencyDependencies.Add(baz);
        }
예제 #12
0
        public ScenarioTests_AzDoFlow()
        {
            source1Assets        = GetAssetData("Foo", "1.1.0", "Bar", "2.1.0");
            source2Assets        = GetAssetData("Pizza", "3.1.0", "Hamburger", "4.1.0");
            source1AssetsUpdated = GetAssetData("Foo", "1.17.0", "Bar", "2.17.0");

            expectedAzDoDependenciesSource1 = new List <DependencyDetail>();
            string           sourceRepoUri = GetAzDoRepoUrl(TestRepository.TestRepo1Name);
            DependencyDetail foo           = new DependencyDetail
            {
                Name    = "Foo",
                Version = "1.1.0",
                RepoUri = sourceRepoUri,
                Commit  = TestRepository.CoherencyTestRepo1Commit,
                Type    = DependencyType.Product,
                Pinned  = false
            };

            expectedAzDoDependenciesSource1.Add(foo);

            DependencyDetail bar = new DependencyDetail
            {
                Name    = "Bar",
                Version = "2.1.0",
                RepoUri = sourceRepoUri,
                Commit  = TestRepository.CoherencyTestRepo1Commit,
                Type    = DependencyType.Product,
                Pinned  = false
            };

            expectedAzDoDependenciesSource1.Add(bar);

            expectedAzDoDependenciesSource2 = new List <DependencyDetail>();
            string           source2RepoUri = GetAzDoRepoUrl(TestRepository.TestRepo3Name);
            DependencyDetail pizza          = new DependencyDetail
            {
                Name    = "Pizza",
                Version = "3.1.0",
                RepoUri = source2RepoUri,
                Commit  = TestRepository.CoherencyTestRepo1Commit,
                Type    = DependencyType.Product,
                Pinned  = false
            };

            expectedAzDoDependenciesSource2.Add(pizza);

            DependencyDetail hamburger = new DependencyDetail
            {
                Name    = "Hamburger",
                Version = "4.1.0",
                RepoUri = source2RepoUri,
                Commit  = TestRepository.CoherencyTestRepo1Commit,
                Type    = DependencyType.Product,
                Pinned  = false
            };

            expectedAzDoDependenciesSource2.Add(hamburger);

            expectedAzDoDependenciesSource1Updated = new List <DependencyDetail>();
            DependencyDetail fooUpdated = new DependencyDetail
            {
                Name    = "Foo",
                Version = "1.1.0",
                RepoUri = sourceRepoUri,
                Commit  = TestRepository.CoherencyTestRepo1Commit,
                Type    = DependencyType.Product,
                Pinned  = false
            };

            expectedAzDoDependenciesSource1Updated.Add(fooUpdated);

            DependencyDetail barUpdated = new DependencyDetail
            {
                Name    = "Bar",
                Version = "2.1.0",
                RepoUri = sourceRepoUri,
                Commit  = TestRepository.CoherencyTestRepo1Commit,
                Type    = DependencyType.Product,
                Pinned  = false
            };

            expectedAzDoDependenciesSource1Updated.Add(barUpdated);
        }
예제 #13
0
        public async Task Darc_AzDoFlow_FeedFlow()
        {
            TestContext.WriteLine("AzDo Dependency Feed Flow, non-batched");

            // Feed flow test strings
            string proxyFeed      = "https://some-proxy.azurewebsites.net/container/some-container/sig/somesig/se/2020-02-02/darc-int-maestro-test1-bababababab-1/index.json";
            string azdoFeed1      = "https://some_org.pkgs.visualstudio.com/_packaging/darc-int-maestro-test1-aaabaababababe-1/nuget/v3/index.json";
            string azdoFeed2      = "https://some_org.pkgs.visualstudio.com/_packaging/darc-int-maestro-test1-bbbbaababababd-1/nuget/v3/index.json";
            string azdoFeed3      = "https://some_org.pkgs.visualstudio.com/_packaging/darc-int-maestro-test1-cccbaababababf-1/nuget/v3/index.json";
            string regularFeed    = "https://dotnetfeed.blob.core.windows.net/maestro-test1/index.json";
            string buildContainer = "https://dev.azure.com/dnceng/internal/_apis/build/builds/9999999/artifacts";

            string[] expectedFeeds    = { proxyFeed, azdoFeed1, azdoFeed3 };
            string[] notExpectedFeeds = { regularFeed, azdoFeed2, buildContainer };

            IImmutableList <AssetData> feedFlowSourceAssets = ImmutableList.Create(
                GetAssetDataWithLocations(
                    "Foo",
                    "1.1.0",
                    proxyFeed,
                    LocationType.NugetFeed
                    ),
                GetAssetDataWithLocations(
                    "Bar",
                    "2.1.0",
                    azdoFeed1,
                    LocationType.NugetFeed),
                GetAssetDataWithLocations(
                    "Pizza",
                    "3.1.0",
                    azdoFeed2,
                    LocationType.NugetFeed,
                    regularFeed,
                    LocationType.NugetFeed
                    ),
                GetAssetDataWithLocations(
                    "Hamburger",
                    "4.1.0",
                    azdoFeed3,
                    LocationType.NugetFeed,
                    buildContainer,
                    LocationType.Container)
                );

            TestContext.WriteLine("Azure DevOps Internal feed flow");
            TestParameters parameters = await TestParameters.GetAsync();

            SetTestParameters(parameters);

            EndToEndFlowLogic testLogic = new EndToEndFlowLogic(parameters);

            List <DependencyDetail> expectedAzDoFeedFlowDependencies = new List <DependencyDetail>();

            DependencyDetail feedFoo = new DependencyDetail
            {
                Name      = "Foo",
                Version   = "1.1.0",
                RepoUri   = GetAzDoRepoUrl(TestRepository.TestRepo1Name),
                Commit    = TestRepository.CoherencyTestRepo1Commit,
                Type      = DependencyType.Product,
                Pinned    = false,
                Locations = new List <string> {
                    proxyFeed
                }
            };

            expectedAzDoFeedFlowDependencies.Add(feedFoo);

            DependencyDetail feedBar = new DependencyDetail
            {
                Name      = "Bar",
                Version   = "2.1.0",
                RepoUri   = GetAzDoRepoUrl(TestRepository.TestRepo1Name),
                Commit    = TestRepository.CoherencyTestRepo1Commit,
                Type      = DependencyType.Product,
                Pinned    = false,
                Locations = new List <string> {
                    azdoFeed1
                }
            };

            expectedAzDoFeedFlowDependencies.Add(feedBar);

            DependencyDetail feedPizza = new DependencyDetail
            {
                Name      = "Pizza",
                Version   = "3.1.0",
                RepoUri   = GetAzDoRepoUrl(TestRepository.TestRepo1Name),
                Commit    = TestRepository.CoherencyTestRepo1Commit,
                Type      = DependencyType.Product,
                Pinned    = false,
                Locations = new List <string> {
                    azdoFeed2, regularFeed
                }
            };

            expectedAzDoFeedFlowDependencies.Add(feedPizza);

            DependencyDetail feedHamburger = new DependencyDetail
            {
                Name      = "Hamburger",
                Version   = "4.1.0",
                RepoUri   = GetAzDoRepoUrl(TestRepository.TestRepo1Name),
                Commit    = TestRepository.CoherencyTestRepo1Commit,
                Type      = DependencyType.Product,
                Pinned    = false,
                Locations = new List <string> {
                    azdoFeed3, buildContainer
                }
            };

            expectedAzDoFeedFlowDependencies.Add(feedHamburger);
            await testLogic.NonBatchedAzDoFlowTestBase(
                $"AzDo_FeedFlowBranch_{Environment.MachineName}",
                $"AzDo_FeedFlowChannel_{Environment.MachineName}",
                feedFlowSourceAssets,
                expectedAzDoFeedFlowDependencies,
                isFeedTest : true,
                expectedFeeds : expectedFeeds,
                notExpectedFeeds : notExpectedFeeds).ConfigureAwait(false);
        }
예제 #14
0
 private void LogDependency(DependencyDetail dependency)
 {
     Console.Write(UxHelpers.DependencyToString(dependency));
 }
예제 #15
0
        public async Task CoherencyUpdateTests10(bool pinHead)
        {
            // Initialize
            Mock <IBarClient> barClientMock = new Mock <IBarClient>();
            Remote            remote        = new Remote(null, barClientMock.Object, NullLogger.Instance);

            // Mock the remote used by build dependency graph to gather dependency details.
            Mock <IRemote> dependencyGraphRemoteMock = new Mock <IRemote>();

            // Always return the main remote.
            var remoteFactoryMock = new Mock <IRemoteFactory>();

            remoteFactoryMock.Setup(m => m.GetRemoteAsync(It.IsAny <string>(), It.IsAny <ILogger>())).ReturnsAsync(dependencyGraphRemoteMock.Object);
            remoteFactoryMock.Setup(m => m.GetBarOnlyRemoteAsync(It.IsAny <ILogger>())).ReturnsAsync(remote);

            List <DependencyDetail> existingDetails = new List <DependencyDetail>();
            DependencyDetail        depA            = AddDependency(existingDetails, "depA", "v1", "repoA", "commit1", pinned: pinHead);
            DependencyDetail        depB            = AddDependency(existingDetails, "depB", "v3", "repoB", "commit2", pinned: false, coherentParent: "depA");
            // Both C and D depend on B
            DependencyDetail depC = AddDependency(existingDetails, "depC", "v0", "repoC", "commit3", pinned: false, coherentParent: "depB");
            DependencyDetail depD = AddDependency(existingDetails, "depD", "v50", "repoD", "commit5", pinned: false, coherentParent: "depB");

            BuildProducesAssets(barClientMock, "repoA", "commit1", new List <(string name, string version)>
            {
                ("depA", "v1")
            });

            List <DependencyDetail> repoADeps = new List <DependencyDetail>();

            AddDependency(repoADeps, "depY", "v42", "repoB", "commit5", pinned: false);
            RepoHasDependencies(dependencyGraphRemoteMock, "repoA", "commit1", repoADeps);

            BuildProducesAssets(barClientMock, "repoB", "commit5", new List <(string name, string version)>
            {
                ("depB", "v10"),
                ("depY", "v42"),
            });

            List <DependencyDetail> repoBDeps = new List <DependencyDetail>();

            AddDependency(repoBDeps, "depQ", "v66", "repoD", "commit35", pinned: false);
            AddDependency(repoBDeps, "depZ", "v64", "repoC", "commit7", pinned: false);
            RepoHasDependencies(dependencyGraphRemoteMock, "repoB", "commit5", repoBDeps);

            BuildProducesAssets(barClientMock, "repoC", "commit7", new List <(string name, string version)>
            {
                ("depC", "v1000"),
                ("depZ", "v64"),
            });

            BuildProducesAssets(barClientMock, "repoD", "commit35", new List <(string name, string version)>
            {
                ("depD", "v1001"),
                ("depQ", "v66"),
            });

            // This should bring B and C in line.
            List <DependencyUpdate> coherencyUpdates =
                await remote.GetRequiredCoherencyUpdatesAsync(existingDetails, remoteFactoryMock.Object);

            Assert.Collection(coherencyUpdates,
                              u =>
            {
                Assert.Equal(depC, u.From);
                Assert.Equal("v1000", u.To.Version);
                Assert.Equal("commit7", u.To.Commit);
                Assert.Equal("repoC", u.To.RepoUri);
            },
                              u =>
            {
                Assert.Equal(depB, u.From);
                Assert.Equal("v10", u.To.Version);
                Assert.Equal("commit5", u.To.Commit);
                Assert.Equal("repoB", u.To.RepoUri);
            },
                              u =>
            {
                Assert.Equal(depD, u.From);
                Assert.Equal("v1001", u.To.Version);
                Assert.Equal("commit35", u.To.Commit);
                Assert.Equal("repoD", u.To.RepoUri);
            });
        }
 private static async Task <int?> GetBuildId(DependencyDetail dep, IMaestroApi client, Dictionary <int, Client.Models.Build> buildCache,
                                             Dictionary <(string name, string version, string commit), int> assetCache, CancellationToken cancellationToken)
예제 #17
0
        /// <summary>
        /// Update local dependencies based on a specific channel.
        /// </summary>
        /// <param name="options">Command line options</param>
        /// <returns>Process exit code.</returns>
        public override async Task <int> ExecuteAsync()
        {
            try
            {
                DarcSettings darcSettings = darcSettings = LocalSettings.GetDarcSettings(_options, Logger);

                // TODO: PAT only used for pulling the arcade eng/common dir,
                // so hardcoded to GitHub PAT right now. Must be more generic in the future.
                darcSettings.GitType = GitRepoType.GitHub;
                LocalSettings localSettings = LocalSettings.LoadSettingsFile(_options);

                darcSettings.PersonalAccessToken = localSettings != null && !string.IsNullOrEmpty(localSettings.GitHubToken) ?
                                                   localSettings.GitHubToken :
                                                   _options.GitHubPat;

                Remote remote = new Remote(darcSettings, Logger);
                Local  local  = new Local(LocalHelpers.GetGitDir(Logger), Logger);
                List <DependencyDetail> dependenciesToUpdate = new List <DependencyDetail>();
                bool   someUpToDate = false;
                string finalMessage = $"Local dependencies updated from channel '{_options.Channel}'.";

                // First we need to figure out what to query for.  Load Version.Details.xml and
                // find all repository uris, optionally restricted by the input dependency parameter.
                IEnumerable <DependencyDetail> dependencies = await local.GetDependenciesAsync(_options.Name);

                if (!dependencies.Any())
                {
                    Console.WriteLine("Found no dependencies to update.");
                    return(Constants.ErrorCode);
                }

                if (!string.IsNullOrEmpty(_options.Name) && !string.IsNullOrEmpty(_options.Version))
                {
                    DependencyDetail dependency = dependencies.First();
                    dependency.Version = _options.Version;
                    dependenciesToUpdate.Add(dependency);

                    Console.WriteLine($"Updating '{dependency.Name}': '{dependency.Version}' => '{_options.Version}'");

                    finalMessage = $"Local dependency {_options.Name} updated to version '{_options.Version}'.";
                }
                else if (!string.IsNullOrEmpty(_options.PackagesFolder))
                {
                    try
                    {
                        dependenciesToUpdate.AddRange(GetDependenciesFromPackagesFolder(_options.PackagesFolder, dependencies));
                    }
                    catch (DarcException exc)
                    {
                        Logger.LogError(exc, $"Error: Failed to update dependencies based on folder '{_options.PackagesFolder}'");
                        return(Constants.ErrorCode);
                    }

                    finalMessage = $"Local dependencies updated based on packages folder {_options.PackagesFolder}.";
                }
                else
                {
                    // Start channel query.
                    var channel = remote.GetChannelAsync(_options.Channel);

                    // Limit the number of BAR queries by grabbing the repo URIs and making a hash set.
                    var repositoryUrisForQuery = dependencies.Select(dependency => dependency.RepoUri).ToHashSet();
                    ConcurrentDictionary <string, Task <Build> > getLatestBuildTaskDictionary = new ConcurrentDictionary <string, Task <Build> >();

                    Channel channelInfo = await channel;
                    if (channelInfo == null)
                    {
                        Console.WriteLine($"Could not find a channel named '{_options.Channel}'.");
                        return(Constants.ErrorCode);
                    }

                    foreach (string repoToQuery in repositoryUrisForQuery)
                    {
                        var latestBuild = remote.GetLatestBuildAsync(repoToQuery, channelInfo.Id.Value);
                        getLatestBuildTaskDictionary.TryAdd(repoToQuery, latestBuild);
                    }

                    // Now walk dependencies again and attempt the update
                    foreach (DependencyDetail dependency in dependencies)
                    {
                        Build build;
                        try
                        {
                            build = await getLatestBuildTaskDictionary[dependency.RepoUri];
                        }
                        catch (ApiErrorException e) when(e.Response.StatusCode == System.Net.HttpStatusCode.NotFound)
                        {
                            Logger.LogTrace($"No build of '{dependency.RepoUri}' found on channel '{_options.Channel}'.");
                            continue;
                        }

                        Asset buildAsset = build.Assets.Where(asset => asset.Name.Equals(dependency.Name, StringComparison.OrdinalIgnoreCase)).FirstOrDefault();
                        if (buildAsset == null)
                        {
                            Logger.LogTrace($"Dependency '{dependency.Name}' not found in latest build of '{dependency.RepoUri}' on '{_options.Channel}', skipping.");
                            continue;
                        }

                        if (buildAsset.Version == dependency.Version &&
                            buildAsset.Name == dependency.Name &&
                            build.Repository == dependency.RepoUri &&
                            build.Commit == dependency.Commit)
                        {
                            // No changes
                            someUpToDate = true;
                            continue;
                        }

                        DependencyDetail updatedDependency = new DependencyDetail
                        {
                            // TODO: Not needed, but not currently provided in Build info. Will be available on next rollout.
                            Branch = null,
                            Commit = build.Commit,
                            // If casing changes, ensure that the dependency name gets updated.
                            Name    = buildAsset.Name,
                            RepoUri = build.Repository,
                            Version = buildAsset.Version
                        };

                        dependenciesToUpdate.Add(updatedDependency);

                        // Print out what we are going to do.
                        Console.WriteLine($"Updating '{dependency.Name}': '{dependency.Version}' => '{updatedDependency.Version}'" +
                                          $" (from build '{build.BuildNumber}' of '{build.Repository}')");
                        // Notify on casing changes.
                        if (buildAsset.Name != dependency.Name)
                        {
                            Console.WriteLine($"  Dependency name normalized to '{updatedDependency.Name}'");
                        }

                        dependenciesToUpdate.Add(updatedDependency);
                    }
                }

                if (!dependenciesToUpdate.Any())
                {
                    // If we found some dependencies already up to date,
                    // then we consider this a success. Otherwise, we didn't even
                    // find matching dependencies so we should let the user know.
                    if (someUpToDate)
                    {
                        Console.WriteLine($"All dependencies are up to date.");
                        return(Constants.SuccessCode);
                    }
                    else
                    {
                        Console.WriteLine($"Found no dependencies to update.");
                        return(Constants.ErrorCode);
                    }
                }

                if (_options.DryRun)
                {
                    return(Constants.SuccessCode);
                }

                // Now call the local updater to run the update
                await local.UpdateDependenciesAsync(dependenciesToUpdate, remote);

                Console.WriteLine(finalMessage);

                return(Constants.SuccessCode);
            }
            catch (Exception e)
            {
                Logger.LogError(e, $"Error: Failed to update dependencies to channel {_options.Channel}");
                return(Constants.ErrorCode);
            }
        }
예제 #18
0
        /// <summary>
        /// Update local dependencies based on a specific channel.
        /// </summary>
        /// <param name="options">Command line options</param>
        /// <returns>Process exit code.</returns>
        public override async Task <int> ExecuteAsync()
        {
            try
            {
                DarcSettings darcSettings = darcSettings = LocalSettings.GetDarcSettings(_options, Logger);

                // TODO: PAT only used for pulling the Arcade eng/common dir,
                // so hardcoded to GitHub PAT right now. Must be more generic in the future.
                darcSettings.GitType = GitRepoType.GitHub;
                LocalSettings localSettings = LocalSettings.LoadSettingsFile(_options);

                darcSettings.GitRepoPersonalAccessToken = localSettings != null && !string.IsNullOrEmpty(localSettings.GitHubToken) ?
                                                          localSettings.GitHubToken :
                                                          _options.GitHubPat;

                IRemoteFactory remoteFactory = new RemoteFactory(_options);
                IRemote        barOnlyRemote = await remoteFactory.GetBarOnlyRemoteAsync(Logger);

                Local local = new Local(Logger);
                List <DependencyDetail> dependenciesToUpdate = new List <DependencyDetail>();
                bool   someUpToDate = false;
                string finalMessage = $"Local dependencies updated from channel '{_options.Channel}'.";

                // First we need to figure out what to query for. Load Version.Details.xml and
                // find all repository uris, optionally restricted by the input dependency parameter.
                IEnumerable <DependencyDetail> localDependencies = await local.GetDependenciesAsync(_options.Name, false);

                // If the source repository was specified, filter away any local dependencies not from that
                // source repository.
                if (!string.IsNullOrEmpty(_options.SourceRepository))
                {
                    localDependencies = localDependencies.Where(
                        dependency => dependency.RepoUri.Contains(_options.SourceRepository, StringComparison.OrdinalIgnoreCase));
                }

                if (!localDependencies.Any())
                {
                    Console.WriteLine("Found no dependencies to update.");
                    return(Constants.ErrorCode);
                }

                List <DependencyDetail> currentDependencies = localDependencies.ToList();

                if (!string.IsNullOrEmpty(_options.Name) && !string.IsNullOrEmpty(_options.Version))
                {
                    DependencyDetail dependency = currentDependencies.First();
                    dependency.Version = _options.Version;
                    dependenciesToUpdate.Add(dependency);

                    Console.WriteLine($"Updating '{dependency.Name}': '{dependency.Version}' => '{_options.Version}'");

                    finalMessage = $"Local dependency {_options.Name} updated to version '{_options.Version}'.";
                }
                else if (!string.IsNullOrEmpty(_options.PackagesFolder))
                {
                    try
                    {
                        dependenciesToUpdate.AddRange(GetDependenciesFromPackagesFolder(_options.PackagesFolder, currentDependencies));
                    }
                    catch (DarcException exc)
                    {
                        Logger.LogError(exc, $"Error: Failed to update dependencies based on folder '{_options.PackagesFolder}'");
                        return(Constants.ErrorCode);
                    }

                    finalMessage = $"Local dependencies updated based on packages folder {_options.PackagesFolder}.";
                }
                else if (_options.BARBuildId > 0)
                {
                    try
                    {
                        if (!_options.CoherencyOnly)
                        {
                            Console.WriteLine($"Looking up build with BAR id {_options.BARBuildId}");
                            var specificBuild = await barOnlyRemote.GetBuildAsync(_options.BARBuildId);

                            int nonCoherencyResult = await NonCoherencyUpdatesForBuildAsync(specificBuild, barOnlyRemote, currentDependencies, dependenciesToUpdate)
                                                     .ConfigureAwait(false);

                            if (nonCoherencyResult != Constants.SuccessCode)
                            {
                                Console.WriteLine("Error: Failed to update non-coherent parent tied dependencies.");
                                return(nonCoherencyResult);
                            }

                            string sourceRepo   = specificBuild.GitHubRepository ?? specificBuild.AzureDevOpsRepository;
                            string sourceBranch = specificBuild.GitHubBranch ?? specificBuild.AzureDevOpsBranch;

                            finalMessage = $"Local dependencies updated based on build with BAR id {_options.BARBuildId} " +
                                           $"({specificBuild.AzureDevOpsBuildNumber} from {sourceRepo}@{sourceBranch})";
                        }

                        int coherencyResult = await CoherencyUpdatesAsync(barOnlyRemote, remoteFactory, currentDependencies, dependenciesToUpdate)
                                              .ConfigureAwait(false);

                        if (coherencyResult != Constants.SuccessCode)
                        {
                            Console.WriteLine("Error: Failed to update coherent parent tied dependencies.");
                            return(coherencyResult);
                        }

                        finalMessage = string.IsNullOrEmpty(finalMessage) ? "Local dependencies successfully updated." : finalMessage;
                    }
                    catch (RestApiException e) when(e.Response.Status == 404)
                    {
                        Console.WriteLine($"Could not find build with BAR id '{_options.BARBuildId}'.");
                        return(Constants.ErrorCode);
                    }
                }
                else
                {
                    if (!_options.CoherencyOnly)
                    {
                        if (string.IsNullOrEmpty(_options.Channel))
                        {
                            Console.WriteLine($"Please supply either a channel name (--channel), a packages folder (--packages-folder) " +
                                              "a BAR build id (--id), or a specific dependency name and version (--name and --version).");
                            return(Constants.ErrorCode);
                        }

                        // Start channel query.
                        Task <Channel> channel = barOnlyRemote.GetChannelAsync(_options.Channel);

                        // Limit the number of BAR queries by grabbing the repo URIs and making a hash set.
                        // We gather the latest build for any dependencies that aren't marked with coherent parent
                        // dependencies, as those will be updated based on additional queries.
                        HashSet <string> repositoryUrisForQuery = currentDependencies
                                                                  .Where(dependency => string.IsNullOrEmpty(dependency.CoherentParentDependencyName))
                                                                  .Select(dependency => dependency.RepoUri)
                                                                  .ToHashSet();

                        ConcurrentDictionary <string, Task <Build> > getLatestBuildTaskDictionary = new ConcurrentDictionary <string, Task <Build> >();

                        Channel channelInfo = await channel;
                        if (channelInfo == null)
                        {
                            Console.WriteLine($"Could not find a channel named '{_options.Channel}'.");
                            return(Constants.ErrorCode);
                        }

                        foreach (string repoToQuery in repositoryUrisForQuery)
                        {
                            Console.WriteLine($"Looking up latest build of {repoToQuery} on {_options.Channel}");
                            var latestBuild = barOnlyRemote.GetLatestBuildAsync(repoToQuery, channelInfo.Id);
                            getLatestBuildTaskDictionary.TryAdd(repoToQuery, latestBuild);
                        }

                        // For each build, first go through and determine the required updates,
                        // updating the "live" dependency information as we go.
                        // Then run a second pass where we update any assets based on coherency information.
                        foreach (KeyValuePair <string, Task <Build> > buildKvPair in getLatestBuildTaskDictionary)
                        {
                            string repoUri = buildKvPair.Key;
                            Build  build   = await buildKvPair.Value;

                            if (build == null)
                            {
                                Logger.LogTrace($"No build of '{repoUri}' found on channel '{_options.Channel}'.");
                                continue;
                            }

                            int nonCoherencyResult = await NonCoherencyUpdatesForBuildAsync(build, barOnlyRemote, currentDependencies, dependenciesToUpdate)
                                                     .ConfigureAwait(false);

                            if (nonCoherencyResult != Constants.SuccessCode)
                            {
                                Console.WriteLine("Error: Failed to update non-coherent parent tied dependencies.");
                                return(nonCoherencyResult);
                            }
                        }
                    }

                    int coherencyResult = await CoherencyUpdatesAsync(barOnlyRemote, remoteFactory, currentDependencies, dependenciesToUpdate)
                                          .ConfigureAwait(false);

                    if (coherencyResult != Constants.SuccessCode)
                    {
                        Console.WriteLine("Error: Failed to update coherent parent tied dependencies.");
                        return(coherencyResult);
                    }
                }

                if (!dependenciesToUpdate.Any())
                {
                    // If we found some dependencies already up to date,
                    // then we consider this a success. Otherwise, we didn't even
                    // find matching dependencies so we should let the user know.
                    if (someUpToDate)
                    {
                        Console.WriteLine($"All dependencies are up to date.");
                        return(Constants.SuccessCode);
                    }
                    else
                    {
                        Console.WriteLine($"Found no dependencies to update.");
                        return(Constants.ErrorCode);
                    }
                }

                if (_options.DryRun)
                {
                    return(Constants.SuccessCode);
                }

                // Now call the local updater to run the update.
                await local.UpdateDependenciesAsync(dependenciesToUpdate, remoteFactory);

                Console.WriteLine(finalMessage);

                return(Constants.SuccessCode);
            }
            catch (AuthenticationException e)
            {
                Console.WriteLine(e.Message);
                return(Constants.ErrorCode);
            }
            catch (Exception e)
            {
                Logger.LogError(e, "Error: Failed to update dependencies.");
                return(Constants.ErrorCode);
            }
        }
예제 #19
0
        /// <summary>
        /// Update local dependencies based on a specific channel.
        /// </summary>
        /// <param name="options">Command line options</param>
        /// <returns>Process exit code.</returns>
        public override async Task <int> ExecuteAsync()
        {
            try
            {
                DarcSettings darcSettings = darcSettings = LocalSettings.GetDarcSettings(_options, Logger);

                // TODO: PAT only used for pulling the arcade eng/common dir,
                // so hardcoded to GitHub PAT right now. Must be more generic in the future.
                darcSettings.GitType = GitRepoType.GitHub;
                LocalSettings localSettings = LocalSettings.LoadSettingsFile(_options);

                darcSettings.GitRepoPersonalAccessToken = localSettings != null && !string.IsNullOrEmpty(localSettings.GitHubToken) ?
                                                          localSettings.GitHubToken :
                                                          _options.GitHubPat;

                IRemoteFactory remoteFactory = new RemoteFactory(_options);
                IRemote        barOnlyRemote = await remoteFactory.GetBarOnlyRemoteAsync(Logger);

                Local local = new Local(Logger);
                List <DependencyDetail> dependenciesToUpdate = new List <DependencyDetail>();
                bool   someUpToDate = false;
                string finalMessage = $"Local dependencies updated from channel '{_options.Channel}'.";

                // First we need to figure out what to query for.  Load Version.Details.xml and
                // find all repository uris, optionally restricted by the input dependency parameter.
                IEnumerable <DependencyDetail> localDependencies = await local.GetDependenciesAsync(_options.Name, false);

                // If the source repository was specified, filter away any local dependencies not from that
                // source repository.
                if (!string.IsNullOrEmpty(_options.SourceRepository))
                {
                    localDependencies = localDependencies.Where(
                        dependency => dependency.RepoUri.Contains(_options.SourceRepository, StringComparison.OrdinalIgnoreCase));
                }

                if (!localDependencies.Any())
                {
                    Console.WriteLine("Found no dependencies to update.");
                    return(Constants.ErrorCode);
                }

                List <DependencyDetail> currentDependencies = localDependencies.ToList();

                if (!string.IsNullOrEmpty(_options.Name) && !string.IsNullOrEmpty(_options.Version))
                {
                    DependencyDetail dependency = currentDependencies.First();
                    dependency.Version = _options.Version;
                    dependenciesToUpdate.Add(dependency);

                    Console.WriteLine($"Updating '{dependency.Name}': '{dependency.Version}' => '{_options.Version}'");

                    finalMessage = $"Local dependency {_options.Name} updated to version '{_options.Version}'.";
                }
                else if (!string.IsNullOrEmpty(_options.PackagesFolder))
                {
                    try
                    {
                        dependenciesToUpdate.AddRange(GetDependenciesFromPackagesFolder(_options.PackagesFolder, currentDependencies));
                    }
                    catch (DarcException exc)
                    {
                        Logger.LogError(exc, $"Error: Failed to update dependencies based on folder '{_options.PackagesFolder}'");
                        return(Constants.ErrorCode);
                    }

                    finalMessage = $"Local dependencies updated based on packages folder {_options.PackagesFolder}.";
                }
                else
                {
                    if (!_options.CoherencyOnly)
                    {
                        if (string.IsNullOrEmpty(_options.Channel))
                        {
                            Console.WriteLine($"Please supply either a channel name (--channel), a packages folder (--packages-folder) " +
                                              $"or a specific dependency name and version (--name and --version).");
                            return(Constants.ErrorCode);
                        }

                        // Start channel query.
                        Task <Channel> channel = barOnlyRemote.GetChannelAsync(_options.Channel);

                        // Limit the number of BAR queries by grabbing the repo URIs and making a hash set.
                        // We gather the latest build for any dependencies that aren't marked with coherent parent
                        // dependencies, as those will be updated based on additional queries.
                        HashSet <string> repositoryUrisForQuery = currentDependencies
                                                                  .Where(dependency => string.IsNullOrEmpty(dependency.CoherentParentDependencyName))
                                                                  .Select(dependency => dependency.RepoUri)
                                                                  .ToHashSet();

                        ConcurrentDictionary <string, Task <Build> > getLatestBuildTaskDictionary = new ConcurrentDictionary <string, Task <Build> >();

                        Channel channelInfo = await channel;
                        if (channelInfo == null)
                        {
                            Console.WriteLine($"Could not find a channel named '{_options.Channel}'.");
                            return(Constants.ErrorCode);
                        }

                        foreach (string repoToQuery in repositoryUrisForQuery)
                        {
                            Console.WriteLine($"Looking up latest build of {repoToQuery} on {_options.Channel}");
                            var latestBuild = barOnlyRemote.GetLatestBuildAsync(repoToQuery, channelInfo.Id);
                            getLatestBuildTaskDictionary.TryAdd(repoToQuery, latestBuild);
                        }

                        // For each build, first go through and determine the required updates,
                        // updating the "live" dependency information as we go.
                        // Then run a second pass where we update any assets based on coherency information.
                        foreach (KeyValuePair <string, Task <Build> > buildKvPair in getLatestBuildTaskDictionary)
                        {
                            string repoUri = buildKvPair.Key;
                            Build  build   = await buildKvPair.Value;
                            if (build == null)
                            {
                                Logger.LogTrace($"No build of '{repoUri}' found on channel '{_options.Channel}'.");
                                continue;
                            }
                            IEnumerable <AssetData> assetData = build.Assets.Select(
                                a => new AssetData(a.NonShipping)
                            {
                                Name    = a.Name,
                                Version = a.Version
                            });

                            // Now determine what needs to be updated.
                            List <DependencyUpdate> updates = await barOnlyRemote.GetRequiredNonCoherencyUpdatesAsync(
                                repoUri, build.Commit, assetData, currentDependencies);

                            foreach (DependencyUpdate update in updates)
                            {
                                DependencyDetail from = update.From;
                                DependencyDetail to   = update.To;
                                // Print out what we are going to do.
                                Console.WriteLine($"Updating '{from.Name}': '{from.Version}' => '{to.Version}'" +
                                                  $" (from build '{build.AzureDevOpsBuildNumber}' of '{repoUri}')");

                                // Final list of dependencies to update
                                dependenciesToUpdate.Add(to);
                                // Replace in the current dependencies list so the correct data is fed into the coherency pass.
                                currentDependencies.Remove(from);
                                currentDependencies.Add(to);
                            }
                        }
                    }

                    Console.WriteLine("Checking for coherency updates...");

                    // Now run a coherency update based on the current set of dependencies updated
                    // from the previous pass.
                    List <DependencyUpdate> coherencyUpdates =
                        await barOnlyRemote.GetRequiredCoherencyUpdatesAsync(currentDependencies, remoteFactory);

                    foreach (DependencyUpdate dependencyUpdate in coherencyUpdates)
                    {
                        DependencyDetail from            = dependencyUpdate.From;
                        DependencyDetail to              = dependencyUpdate.To;
                        DependencyDetail coherencyParent = currentDependencies.First(d =>
                                                                                     d.Name.Equals(from.CoherentParentDependencyName, StringComparison.OrdinalIgnoreCase));
                        // Print out what we are going to do.
                        Console.WriteLine($"Updating '{from.Name}': '{from.Version}' => '{to.Version}' " +
                                          $"to ensure coherency with {from.CoherentParentDependencyName}@{coherencyParent.Version}");

                        // Final list of dependencies to update
                        dependenciesToUpdate.Add(to);
                    }
                }

                if (!dependenciesToUpdate.Any())
                {
                    // If we found some dependencies already up to date,
                    // then we consider this a success. Otherwise, we didn't even
                    // find matching dependencies so we should let the user know.
                    if (someUpToDate)
                    {
                        Console.WriteLine($"All dependencies are up to date.");
                        return(Constants.SuccessCode);
                    }
                    else
                    {
                        Console.WriteLine($"Found no dependencies to update.");
                        return(Constants.ErrorCode);
                    }
                }

                if (_options.DryRun)
                {
                    return(Constants.SuccessCode);
                }

                // Now call the local updater to run the update.
                await local.UpdateDependenciesAsync(dependenciesToUpdate, remoteFactory);

                Console.WriteLine(finalMessage);

                return(Constants.SuccessCode);
            }
            catch (Exception e)
            {
                Logger.LogError(e, $"Error: Failed to update dependencies to channel {_options.Channel}");
                return(Constants.ErrorCode);
            }
        }