protected override Task <object> RemoteExecuteAsync(IRemoteOperationExecutionContext context) { var sourcePath = context.ResolvePath(this.SourceDirectory); this.LogInformation($"Finding matching files in {sourcePath}..."); if (!DirectoryEx.Exists(sourcePath)) { this.LogError($"Directory {sourcePath} does not exist."); return(Complete); } var mask = new MaskingContext(this.Includes, this.Excludes); var matches = DirectoryEx.GetFileSystemInfos(sourcePath, mask) .OfType <SlimFileInfo>() .Where(f => f.FullName.EndsWith(".sql", StringComparison.OrdinalIgnoreCase)) .ToList(); if (matches.Count == 0) { this.LogError($"No matching .sql files were found in {sourcePath}."); return(Complete); } var outputFileName = context.ResolvePath(this.OutputFile); DirectoryEx.Create(PathEx.GetDirectoryName(outputFileName)); using (var buffer = new TemporaryStream()) { using (var zip = new ZipArchive(buffer, ZipArchiveMode.Create, true)) { foreach (var f in matches) { var entryName = getEntryName(f.FullName); this.LogDebug($"Adding {entryName}..."); zip.CreateEntryFromFile(f.FullName, entryName, CompressionLevel.Optimal); } } buffer.Position = 0; using (var outputStream = FileEx.Open(outputFileName, FileMode.Create, FileAccess.Write, FileShare.None, FileOptions.SequentialScan)) { using (var inedoSqlStream = typeof(BundleSqlScriptsOperation).Assembly.GetManifestResourceStream("Inedo.Extensions.SqlServer.Operations.inedosql.exe")) { inedoSqlStream.CopyTo(outputStream); } buffer.CopyTo(outputStream); } } this.LogInformation($"{outputFileName} created."); return(Complete); string getEntryName(string fullName) => fullName.Substring(0, sourcePath.Length).TrimStart('\\', '/').Replace('\\', '/'); }
public override async Task ExecuteAsync(IOperationExecutionContext context) { var sourceDirectory = context.ResolvePath(this.SourceDirectory); this.LogDebug($"Deleting files from {sourceDirectory}..."); var fileOps = await context.Agent.GetServiceAsync <IFileOperationsExecuter>().ConfigureAwait(false); if (!await fileOps.DirectoryExistsAsync(sourceDirectory).ConfigureAwait(false)) { this.LogInformation(sourceDirectory + " does not exist."); return; } var mask = new MaskingContext(this.Includes, this.Excludes); // Optimization for clearing a folder if (mask.Includes.FirstOrDefault() == "**" && !mask.Excludes.Any() && !this.VerboseLogging) { this.LogDebug("Mask indicates that files and directories should be deleted recursively, clearing directory..."); await fileOps.ClearDirectoryAsync(sourceDirectory).ConfigureAwait(false); this.LogInformation("Directory cleared."); return; } // Get the matches and make sure the root is not included // Sort by the number of directory separators in descending order so subdirs are deleted first var files = (await fileOps.GetFileSystemInfosAsync(sourceDirectory, mask).ConfigureAwait(false)) .OrderByDescending(m => m.FullName.Where(c => c == '/' || c == '\\').Count()) .ToList(); var filesToDelete = files .OfType <SlimFileInfo>() .Select(e => e.FullName) .ToArray(); var directoriesToDelete = files .OfType <SlimDirectoryInfo>() .Select(e => e.FullName) .ToArray(); if (this.VerboseLogging) { foreach (var fileName in filesToDelete) { this.LogDebug($"Deleting file: {fileName}"); } } await fileOps.DeleteFilesAsync(filesToDelete).ConfigureAwait(false); if (this.VerboseLogging) { foreach (var dirName in directoriesToDelete) { this.LogDebug($"Deleting directory: {dirName}"); } } await fileOps.DeleteDirectoriesAsync(directoriesToDelete).ConfigureAwait(false); this.LogInformation($"Deleted {filesToDelete.Length} files and {directoriesToDelete.Length} directories."); }
public override async Task ExecuteAsync(IOperationExecutionContext context) { var sourcePath = context.ResolvePath(this.SourceDirectory); var maskingContext = new MaskingContext(this.Includes, this.Excludes); this.LogDebug($"Searching for files matching {maskingContext.ToString().Replace(Environment.NewLine, ", ")} in {sourcePath}..."); var fileOps = await context.Agent.GetServiceAsync <IFileOperationsExecuter>(); var matches = (await fileOps.GetFileSystemInfosAsync(context.ResolvePath(this.SourceDirectory), new MaskingContext(this.Includes, this.Excludes))) .OfType <SlimFileInfo>() .ToList(); if (matches.Count == 0) { this.LogWarning("No matching files found."); return; } this.LogDebug($"Found {matches.Count} matching files."); foreach (var projectFile in matches) { try { this.LogDebug($"Reading {projectFile.FullName}..."); XDocument xdoc; using (var stream = await fileOps.OpenFileAsync(projectFile.FullName, FileMode.Open, FileAccess.Read)) { xdoc = XDocument.Load(stream); } this.LogDebug($"{projectFile.FullName} loaded."); if (xdoc.Root.Name.LocalName != "Project") { this.LogError($"{projectFile.FullName} is not a valid project file; root element is \"{xdoc.Root.Name.LocalName}\", expected \"Project\"."); continue; } this.LogInformation($"Setting Version in {projectFile.FullName} to {this.Version}..."); UpdateOrAdd(xdoc, "Version", this.Version); if (!string.IsNullOrWhiteSpace(this.AssemblyVersion)) { this.LogInformation($"Setting AssemblyVersion in {projectFile.FullName} to {this.AssemblyVersion}..."); UpdateOrAdd(xdoc, "AssemblyVersion", this.AssemblyVersion); } if (!string.IsNullOrWhiteSpace(this.FileVersion)) { this.LogInformation($"Setting FileVersion in {projectFile.FullName} to {this.FileVersion}..."); UpdateOrAdd(xdoc, "FileVersion", this.FileVersion); } if (!string.IsNullOrWhiteSpace(this.PackageVersion)) { this.LogInformation($"Setting PackageVersion in {projectFile.FullName} to {this.PackageVersion}..."); UpdateOrAdd(xdoc, "PackageVersion", this.PackageVersion); } this.LogDebug($"Writing {projectFile.FullName}..."); using (var stream = await fileOps.OpenFileAsync(projectFile.FullName, FileMode.Create, FileAccess.Write)) { xdoc.Save(stream); } this.LogDebug($"{projectFile.FullName} saved."); } catch (Exception ex) { this.LogError($"Unable to update {projectFile.FullName}: {ex.Message}"); } } }
public override async Task <PersistedConfiguration> CollectAsync(IOperationCollectionContext context) { var fileOps = context.Agent.GetService <IFileOperationsExecuter>(); var client = new ProGetClient(this.Template.FeedUrl, this.Template.FeedName, this.Template.UserName, this.Template.Password, this); try { var packageId = PackageName.Parse(this.Template.PackageName); var packageInfo = await client.GetPackageInfoAsync(packageId).ConfigureAwait(false); var version = new ProGetPackageVersionSpecifier(this.Template.PackageVersion).GetBestMatch(packageInfo.versions); if (version == null) { this.LogError($"Package {this.Template.PackageName} does not have a version {this.Template.PackageVersion}."); return(null); } this.LogInformation($"Resolved package version is {version}."); if (!await fileOps.DirectoryExistsAsync(this.Template.TargetDirectory).ConfigureAwait(false)) { this.LogInformation(this.Template.TargetDirectory + " does not exist."); return(new ProGetPackageConfiguration { TargetDirectory = this.Template.TargetDirectory }); } var mask = new MaskingContext(this.Template.Includes, this.Template.Excludes); this.LogInformation(this.Template.TargetDirectory + " exists; getting remote file list..."); var remoteFileList = await fileOps.GetFileSystemInfosAsync(this.Template.TargetDirectory, mask).ConfigureAwait(false); var remoteFiles = new Dictionary <string, SlimFileSystemInfo>(remoteFileList.Count, StringComparer.OrdinalIgnoreCase); foreach (var file in remoteFileList) { var relativeName = file.FullName.Substring(this.Template.TargetDirectory.Length).Replace('\\', '/').Trim('/'); if (file is SlimDirectoryInfo) { relativeName += "/"; } remoteFiles.Add(relativeName, file); } remoteFileList = null; // async GC optimization this.LogDebug($"{this.Template.TargetDirectory} contains {remoteFiles.Count} file system entries."); this.LogInformation($"Connecting to {this.Template.FeedUrl} to get metadata for {this.Template.PackageName}:{version}..."); var versionInfo = await client.GetPackageVersionInfoAsync(packageId, version).ConfigureAwait(false); if (versionInfo.fileList == null) { this.LogError("File list is unavailable for this package; it may be an orphaned entry."); return(null); } this.LogDebug($"Package contains {versionInfo.fileList.Length} file system entries."); foreach (var entry in versionInfo.fileList) { var relativeName = entry.name; if (!mask.IsMatch(relativeName)) { continue; } var file = remoteFiles.GetValueOrDefault(relativeName); if (file == null) { this.LogInformation($"Entry {relativeName} is not present in {this.Template.TargetDirectory}."); return(new ProGetPackageConfiguration { TargetDirectory = this.Template.TargetDirectory }); } if (!entry.name.EndsWith("/")) { var fileInfo = (SlimFileInfo)file; if (entry.size != fileInfo.Size || entry.date != fileInfo.LastWriteTimeUtc) { this.LogInformation($"File {relativeName} in {this.Template.TargetDirectory} is different from file in package."); this.LogDebug($"Source info: {entry.size} bytes, {entry.date} timestamp"); this.LogDebug($"Target info: {fileInfo.Size} bytes, {fileInfo.LastWriteTimeUtc} timestamp"); return(new ProGetPackageConfiguration { TargetDirectory = this.Template.TargetDirectory }); } } } if (this.Template.DeleteExtra) { foreach (var name in remoteFiles.Keys) { if (!versionInfo.fileList.Any(entry => entry.name == name)) { this.LogInformation($"File {name} in {this.Template.TargetDirectory} does not exist in package."); return(new ProGetPackageConfiguration { TargetDirectory = this.Template.TargetDirectory }); } } } this.LogInformation($"All package files and directories are present in {this.Template.TargetDirectory}."); return(new ProGetPackageConfiguration { Current = true, TargetDirectory = this.Template.TargetDirectory }); } catch (ProGetException ex) { this.LogError(ex.FullMessage); return(null); } }
protected override Task <object> RemoteExecuteAsync(IRemoteOperationExecutionContext context) { if (this.ReadOnly ?? this.Hidden ?? this.System == null) { this.LogWarning("No file attributes have been specified."); return(Complete); } var mask = new MaskingContext(this.Includes, this.Excludes); var sourcePath = context.ResolvePath(this.SourceDirectory); this.LogInformation($"Getting list of files in {sourcePath} matching {mask}..."); var matches = DirectoryEx.GetFileSystemInfos(sourcePath, mask) .OfType <SlimFileInfo>() .ToList(); if (matches.Count == 0) { this.LogWarning("No files match the specified mask."); return(Complete); } FileAttributes attributesToChange = 0; if (this.ReadOnly.HasValue) { attributesToChange |= FileAttributes.ReadOnly; } if (this.Hidden.HasValue) { attributesToChange |= FileAttributes.Hidden; } if (this.System.HasValue) { attributesToChange |= FileAttributes.System; } FileAttributes attributeValues = 0; if (this.ReadOnly.GetValueOrDefault()) { attributeValues |= FileAttributes.ReadOnly; } if (this.Hidden.GetValueOrDefault()) { attributeValues |= FileAttributes.Hidden; } if (this.System.GetValueOrDefault()) { attributeValues |= FileAttributes.System; } if (this.VerboseLogging) { this.LogDebug("Attributes to change: " + attributesToChange); this.LogDebug("Attribute values: " + attributeValues); } this.LogDebug($"Found {matches.Count} matching files."); this.LogInformation("Applying attributes..."); foreach (var file in matches) { context.CancellationToken.ThrowIfCancellationRequested(); var attributes = file.Attributes; if (((attributes & attributesToChange) ^ attributeValues) != 0) { attributes &= ~attributesToChange; attributes |= attributeValues; if (this.VerboseLogging) { this.LogDebug("Changing " + file.FullName + "..."); } FileEx.SetAttributes(file.FullName, attributes); } } this.LogInformation("Attributes applied."); return(Complete); }