diff --git a/Scalar.Service/RepoRegistry.cs b/Scalar.Service/RepoRegistry.cs index ac21f8c905..8461f78236 100644 --- a/Scalar.Service/RepoRegistry.cs +++ b/Scalar.Service/RepoRegistry.cs @@ -174,22 +174,59 @@ public void RunMaintenanceTaskForRepos(MaintenanceTasks.Task task, string userId } else { - metadata.Add(TracingConstants.MessageKey.InfoMessage, "Calling maintenance verb"); + string rootPath; + string errorMessage; foreach (RepoRegistration repo in activeRepos) { + rootPath = Path.GetPathRoot(repo.EnlistmentRoot); + metadata[nameof(repo.EnlistmentRoot)] = repo.EnlistmentRoot; metadata[nameof(task)] = task; - this.tracer.RelatedEvent( - EventLevel.Informational, - $"{nameof(this.RunMaintenanceTaskForRepos)}_CallingMaintenance", - metadata); + metadata[nameof(rootPath)] = rootPath; + metadata.Remove(nameof(errorMessage)); - if (!this.scalarVerb.CallMaintenance(task, repo.EnlistmentRoot, sessionId)) + if (!string.IsNullOrWhiteSpace(rootPath) && !this.fileSystem.DirectoryExists(rootPath)) { - // TODO: #111 - If the maintenance verb failed because the repo is no longer - // on disk, it should be removed from the registry + // If the volume does not exist we'll assume the drive was removed or is encrypted, + // and we'll leave the repo in the registry (but we won't run maintenance on it). + this.tracer.RelatedEvent( + EventLevel.Informational, + $"{nameof(this.RunMaintenanceTaskForRepos)}_SkippedRepoWithMissingVolume", + metadata); + + continue; } + + if (!this.fileSystem.DirectoryExists(repo.EnlistmentRoot)) + { + // The repo is no longer on disk (but its volume is present) + // Unregister the repo + if (this.TryRemoveRepo(repo.EnlistmentRoot, out errorMessage)) + { + this.tracer.RelatedEvent( + EventLevel.Informational, + $"{nameof(this.RunMaintenanceTaskForRepos)}_RemovedMissingRepo", + metadata); + } + else + { + metadata[nameof(errorMessage)] = errorMessage; + this.tracer.RelatedEvent( + EventLevel.Informational, + $"{nameof(this.RunMaintenanceTaskForRepos)}_FailedToRemoveRepo", + metadata); + } + + continue; + } + + this.tracer.RelatedEvent( + EventLevel.Informational, + $"{nameof(this.RunMaintenanceTaskForRepos)}_CallingMaintenance", + metadata); + + this.scalarVerb.CallMaintenance(task, repo.EnlistmentRoot, sessionId); } } } diff --git a/Scalar.UnitTests/Service/Mac/MacServiceTests.cs b/Scalar.UnitTests/Service/Mac/MacServiceTests.cs index 22fb996414..cebad8993f 100644 --- a/Scalar.UnitTests/Service/Mac/MacServiceTests.cs +++ b/Scalar.UnitTests/Service/Mac/MacServiceTests.cs @@ -3,6 +3,7 @@ using Scalar.Common; using Scalar.Common.Maintenance; using Scalar.Service; +using Scalar.Tests.Should; using Scalar.UnitTests.Mock.Common; using Scalar.UnitTests.Mock.FileSystem; using System.IO; @@ -30,11 +31,35 @@ public void SetUp() this.scalarPlatform.MockCurrentUser = ExpectedActiveUserId.ToString(); } + [TestCase] + public void RepoRegistryRemovesRegisteredRepoIfMissingFromDisk() + { + Mock repoMounterMock = new Mock(MockBehavior.Strict); + + this.fileSystem.DirectoryExists(ExpectedActiveRepoPath).ShouldBeFalse($"{ExpectedActiveRepoPath} should not exist"); + + MaintenanceTasks.Task task = MaintenanceTasks.Task.FetchCommitsAndTrees; + + this.CreateTestRepos(ServiceDataLocation); + RepoRegistry repoRegistry = new RepoRegistry( + this.tracer, + this.fileSystem, + ServiceDataLocation, + repoMounterMock.Object); + + repoRegistry.RunMaintenanceTaskForRepos(task, ExpectedActiveUserId.ToString(), ExpectedSessionId); + repoMounterMock.VerifyAll(); + + repoRegistry.ReadRegistry().ShouldNotContain(entry => entry.Key.Equals(ExpectedActiveRepoPath)); + } + [TestCase] public void RepoRegistryCallsMaintenanceVerbOnlyForRegisteredRepos() { Mock repoMounterMock = new Mock(MockBehavior.Strict); + this.fileSystem.CreateDirectory(ExpectedActiveRepoPath); + MaintenanceTasks.Task task = MaintenanceTasks.Task.FetchCommitsAndTrees; repoMounterMock.Setup(mp => mp.CallMaintenance(task, ExpectedActiveRepoPath, ExpectedActiveUserId)).Returns(true);