public TrackingConfig PrepareDirectory( IExecutionContext executionContext, ServiceEndpoint endpoint, ISourceProvider sourceProvider) { // Validate parameters. Trace.Entering(); ArgUtil.NotNull(executionContext, nameof(executionContext)); ArgUtil.NotNull(executionContext.Variables, nameof(executionContext.Variables)); ArgUtil.NotNull(endpoint, nameof(endpoint)); ArgUtil.NotNull(sourceProvider, nameof(sourceProvider)); var trackingManager = HostContext.GetService <ITrackingManager>(); // Defer to the source provider to calculate the hash key. Trace.Verbose("Calculating build directory hash key."); string hashKey = sourceProvider.GetBuildDirectoryHashKey(executionContext, endpoint); Trace.Verbose($"Hash key: {hashKey}"); // Load the existing tracking file if one already exists. string trackingFile = Path.Combine( IOUtil.GetWorkPath(HostContext), Constants.Build.Path.SourceRootMappingDirectory, executionContext.Variables.System_CollectionId, executionContext.Variables.System_DefinitionId, Constants.Build.Path.TrackingConfigFile); Trace.Verbose($"Loading tracking config if exists: {trackingFile}"); TrackingConfigBase existingConfig = trackingManager.LoadIfExists(executionContext, trackingFile); // Check if the build needs to be garbage collected. If the hash key // has changed, then the existing build directory cannot be reused. TrackingConfigBase garbageConfig = null; if (existingConfig != null && !string.Equals(existingConfig.HashKey, hashKey, StringComparison.OrdinalIgnoreCase)) { // Just store a reference to the config for now. It can safely be // marked for garbage collection only after the new build directory // config has been created. Trace.Verbose($"Hash key from existing tracking config does not match. Existing key: {existingConfig.HashKey}"); garbageConfig = existingConfig; existingConfig = null; } // Create a new tracking config if required. TrackingConfig newConfig; if (existingConfig == null) { Trace.Verbose("Creating a new tracking config file."); newConfig = trackingManager.Create( executionContext, endpoint, hashKey, trackingFile, sourceProvider.TestOverrideBuildDirectory()); ArgUtil.NotNull(newConfig, nameof(newConfig)); } else { // Convert legacy format to the new format if required. newConfig = ConvertToNewFormat(executionContext, endpoint, existingConfig); // Fill out repository type if it's not there. // repository type is a new property introduced for maintenance job if (string.IsNullOrEmpty(newConfig.RepositoryType)) { newConfig.RepositoryType = endpoint.Type; } // For existing tracking config files, update the job run properties. Trace.Verbose("Updating job run properties."); trackingManager.UpdateJobRunProperties(executionContext, newConfig, trackingFile); } // Mark the old configuration for garbage collection. if (garbageConfig != null) { Trace.Verbose("Marking existing config for garbage collection."); trackingManager.MarkForGarbageCollection(executionContext, garbageConfig); } // Prepare the build directory. // There are 2 ways to provide build directory clean policy. // 1> set definition variable build.clean or agent.clean.buildDirectory. (on-prem user need to use this, since there is no Web UI in TFS 2016) // 2> select source clean option in definition repository tab. (VSTS will have this option in definition designer UI) BuildCleanOption cleanOption = GetBuildDirectoryCleanOption(executionContext, endpoint); CreateDirectory( executionContext, description: "build directory", path: Path.Combine(IOUtil.GetWorkPath(HostContext), newConfig.BuildDirectory), deleteExisting: cleanOption == BuildCleanOption.All); CreateDirectory( executionContext, description: "artifacts directory", path: Path.Combine(IOUtil.GetWorkPath(HostContext), newConfig.ArtifactsDirectory), deleteExisting: true); CreateDirectory( executionContext, description: "test results directory", path: Path.Combine(IOUtil.GetWorkPath(HostContext), newConfig.TestResultsDirectory), deleteExisting: true); CreateDirectory( executionContext, description: "binaries directory", path: Path.Combine(IOUtil.GetWorkPath(HostContext), newConfig.BuildDirectory, Constants.Build.Path.BinariesDirectory), deleteExisting: cleanOption == BuildCleanOption.Binary); CreateDirectory( executionContext, description: "source directory", path: Path.Combine(IOUtil.GetWorkPath(HostContext), newConfig.BuildDirectory, Constants.Build.Path.SourcesDirectory), deleteExisting: cleanOption == BuildCleanOption.Source); return(newConfig); }
// TODO: Updates legacy config. private TestHostContext Setup( [CallerMemberName] string name = "", BuildCleanOption? cleanOption = null, ExistingConfigKind existingConfigKind = ExistingConfigKind.None) { // Setup the host context. TestHostContext hc = new TestHostContext(this, name); // Create a random work path. var configStore = new Mock<IConfigurationStore>(); _workFolder = Path.Combine( Path.GetDirectoryName(Assembly.GetEntryAssembly().Location), $"_work_{Path.GetRandomFileName()}"); var settings = new AgentSettings() { WorkFolder = _workFolder }; configStore.Setup(x => x.GetSettings()).Returns(settings); hc.SetSingleton<IConfigurationStore>(configStore.Object); // Setup the execution context. _ec = new Mock<IExecutionContext>(); List<string> warnings; _variables = new Variables(hc, new Dictionary<string, string>(), new List<MaskHint>(), out warnings); _variables.Set(Constants.Variables.System.CollectionId, CollectionId); _variables.Set(Constants.Variables.System.DefinitionId, DefinitionId); _variables.Set(Constants.Variables.Build.Clean, $"{cleanOption}"); _ec.Setup(x => x.Variables).Returns(_variables); // Store the expected tracking file path. _trackingFile = Path.Combine( _workFolder, Constants.Build.Path.SourceRootMappingDirectory, _ec.Object.Variables.System_CollectionId, _ec.Object.Variables.System_DefinitionId, Constants.Build.Path.TrackingConfigFile); // Setup the endpoint. _endpoint = new ServiceEndpoint() { Name = "Some endpoint name", Url = new Uri("http://contoso.visualstudio.com"), }; // Setup the source provider. _sourceProvider = new Mock<ISourceProvider>(); _sourceProvider .Setup(x => x.GetBuildDirectoryHashKey(_ec.Object, _endpoint)) .Returns(HashKey); hc.SetSingleton<ISourceProvider>(_sourceProvider.Object); // Store the existing config object. switch (existingConfigKind) { case ExistingConfigKind.Matching: _existingConfig = new TrackingConfig(_ec.Object, _endpoint, 1, HashKey); Assert.Equal("1", _existingConfig.BuildDirectory); break; case ExistingConfigKind.Nonmatching: _existingConfig = new TrackingConfig(_ec.Object, _endpoint, 2, NonmatchingHashKey); Assert.Equal("2", _existingConfig.BuildDirectory); break; case ExistingConfigKind.None: break; default: throw new NotSupportedException(); } // Store the new config object. if (existingConfigKind == ExistingConfigKind.Matching) { _newConfig = _existingConfig; } else { _newConfig = new TrackingConfig(_ec.Object, _endpoint, 3, HashKey); Assert.Equal("3", _newConfig.BuildDirectory); } // Setup the tracking manager. _trackingManager = new Mock<ITrackingManager>(); _trackingManager .Setup(x => x.LoadIfExists(_ec.Object, _trackingFile)) .Returns(_existingConfig); if (existingConfigKind == ExistingConfigKind.None || existingConfigKind == ExistingConfigKind.Nonmatching) { _trackingManager .Setup(x => x.Create(_ec.Object, _endpoint, HashKey, _trackingFile)) .Returns(_newConfig); if (existingConfigKind == ExistingConfigKind.Nonmatching) { _trackingManager .Setup(x => x.MarkForGarbageCollection(_ec.Object, _existingConfig)); } } else if (existingConfigKind == ExistingConfigKind.Matching) { _trackingManager .Setup(x => x.UpdateJobRunProperties(_ec.Object, _existingConfig, _trackingFile)); } else { throw new NotSupportedException(); } hc.SetSingleton<ITrackingManager>(_trackingManager.Object); // Setup the build directory manager. _buildDirectoryManager = new BuildDirectoryManager(); _buildDirectoryManager.Initialize(hc); return hc; }
public TrackingConfig PrepareDirectory( IExecutionContext executionContext, IList <RepositoryResource> repositories, WorkspaceOptions workspace) { // Validate parameters. Trace.Entering(); ArgUtil.NotNull(executionContext, nameof(executionContext)); ArgUtil.NotNull(executionContext.Variables, nameof(executionContext.Variables)); ArgUtil.NotNull(repositories, nameof(repositories)); var trackingManager = HostContext.GetService <ITrackingManager>(); // Create the tracking config for this execution of the pipeline var agentSettings = HostContext.GetService <IConfigurationStore>().GetSettings(); var newConfig = trackingManager.Create(executionContext, repositories, ShouldOverrideBuildDirectory(repositories, agentSettings)); // Load the tracking config from the last execution of the pipeline var existingConfig = trackingManager.LoadExistingTrackingConfig(executionContext); // If there aren't any major changes, merge the configurations and use the same workspace if (trackingManager.AreTrackingConfigsCompatible(executionContext, newConfig, existingConfig)) { newConfig = trackingManager.MergeTrackingConfigs(executionContext, newConfig, existingConfig); } else if (existingConfig != null) { // If the previous config had different repos, get a new workspace folder and mark the old one for clean up trackingManager.MarkForGarbageCollection(executionContext, existingConfig); // If the config file was updated to a new config, we need to delete the legacy artifact/staging directories. // DeleteDirectory will check for the existence of the folders first. DeleteDirectory( executionContext, description: "legacy artifacts directory", path: Path.Combine(existingConfig.BuildDirectory, Constants.Build.Path.LegacyArtifactsDirectory)); DeleteDirectory( executionContext, description: "legacy staging directory", path: Path.Combine(existingConfig.BuildDirectory, Constants.Build.Path.LegacyStagingDirectory)); } // Save any changes to the config file trackingManager.UpdateTrackingConfig(executionContext, newConfig); // Prepare the build directory. // There are 2 ways to provide build directory clean policy. // 1> set definition variable build.clean or agent.clean.buildDirectory. (on-prem user need to use this, since there is no Web UI in TFS 2016) // 2> select source clean option in definition repository tab. (VSTS will have this option in definition designer UI) BuildCleanOption cleanOption = GetBuildDirectoryCleanOption(executionContext, workspace); CreateDirectory( executionContext, description: "build directory", path: Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Work), newConfig.BuildDirectory), deleteExisting: cleanOption == BuildCleanOption.All); CreateDirectory( executionContext, description: "artifacts directory", path: Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Work), newConfig.ArtifactsDirectory), deleteExisting: true); CreateDirectory( executionContext, description: "test results directory", path: Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Work), newConfig.TestResultsDirectory), deleteExisting: true); CreateDirectory( executionContext, description: "binaries directory", path: Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Work), newConfig.BuildDirectory, Constants.Build.Path.BinariesDirectory), deleteExisting: cleanOption == BuildCleanOption.Binary); CreateDirectory( executionContext, description: "source directory", path: Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Work), newConfig.SourcesDirectory), deleteExisting: cleanOption == BuildCleanOption.Source); // Set the default clone path for each repository (the Checkout task may override this later) foreach (var repository in repositories) { var repoPath = GetDefaultRepositoryPath(executionContext, repository, newConfig.SourcesDirectory); Trace.Info($"Set repository path for repository {repository.Alias} to '{repoPath}'"); repository.Properties.Set <string>(RepositoryPropertyNames.Path, repoPath); } return(newConfig); }