Decompose Crossgen, remove CleanPublishOutput, replace ExtractArchive with *FileExtractToDirectory (#3927)

* Eliminate CleanPublishOutput

* Decompose Crossgen Task

* WiP

* TarGzFileExtractToDirectory

* FixModeFlags --> CHMod

Also various eliminations of dead code

* Tasks cleanup

Move all tasks to .tasks file. There is little value in keepint them in each source file as they are already being used assumptively by files that happen to get executed later.

Also eliminating uses of <Exec> for DotNet invocations

* Move to BuildTools implementation of TarGzCreateFromDirectory

* Eliminate Command.cs and helpers

* Remove dead code

* Revert TarGz from BuildTools

Latest build tools package has not picked up the task, though it is checked in.

* Disable ChMod on Windows

* Windows bug fix

* PR Feedback

* Finish changing Chmod caps
This commit is contained in:
Piotr Puszkiewicz 2016-07-26 00:29:59 -04:00 committed by GitHub
parent ee8a01b8d6
commit 5ebc6a1ceb
50 changed files with 827 additions and 1645 deletions

View file

@ -1,13 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Microsoft.DotNet.Cli.Build
{
public class DependencyVersions
{
public static readonly string CoreCLRVersion = "1.0.2";
public static readonly string JitVersion = "1.0.2";
}
}

View file

@ -1,15 +0,0 @@
using Microsoft.DotNet.Cli.Build.Framework;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Microsoft.DotNet.Cli.Build
{
public class HostArtifactNames
{
public static string DotnetHostBaseName => $"dotnet{Constants.ExeSuffix}";
public static string DotnetHostFxrBaseName => $"{Constants.DynamicLibPrefix}hostfxr{Constants.DynamicLibSuffix}";
public static string HostPolicyBaseName => $"{Constants.DynamicLibPrefix}hostpolicy{Constants.DynamicLibSuffix}";
}
}

View file

@ -1,55 +0,0 @@
using System;
using System.IO;
using System.Net.Http;
using System.Text;
using Microsoft.DotNet.Cli.Build.Framework;
using Microsoft.WindowsAzure.Storage;
using Microsoft.WindowsAzure.Storage.Blob;
using static Microsoft.DotNet.Cli.Build.Framework.BuildHelpers;
namespace Microsoft.DotNet.Cli.Build
{
public class DebRepoPublisher
{
private const string _debianRevisionNumber = "1";
private string _repoID;
private string _uploadJsonDirectory;
public DebRepoPublisher(string uploadJsonDirectory)
{
_uploadJsonDirectory = uploadJsonDirectory;
_repoID = Environment.GetEnvironmentVariable("REPO_ID");
}
public void PublishDebFileToDebianRepo(string packageName, string packageVersion, string uploadUrl)
{
var uploadJson = GenerateUploadJsonFile(packageName, packageVersion, uploadUrl);
Cmd(Path.Combine(Dirs.RepoRoot, "scripts", "publish", "repoapi_client.sh"), "-addpkg", uploadJson)
.Execute()
.EnsureSuccessful();
}
private string GenerateUploadJsonFile(string packageName, string packageVersion, string uploadUrl)
{
var uploadJson = Path.Combine(_uploadJsonDirectory, "package_upload.json");
File.Delete(uploadJson);
using (var fileStream = File.Create(uploadJson))
{
using (StreamWriter sw = new StreamWriter(fileStream))
{
sw.WriteLine("{");
sw.WriteLine($" \"name\":\"{packageName}\",");
sw.WriteLine($" \"version\":\"{packageVersion}-{_debianRevisionNumber}\",");
sw.WriteLine($" \"repositoryId\":\"{_repoID}\",");
sw.WriteLine($" \"sourceUrl\":\"{uploadUrl}\"");
sw.WriteLine("}");
}
}
return uploadJson;
}
}
}

View file

@ -8,8 +8,6 @@ using Microsoft.DotNet.Cli.Build.Framework;
using Microsoft.WindowsAzure.Storage;
using Microsoft.WindowsAzure.Storage.Blob;
using static Microsoft.DotNet.Cli.Build.Framework.BuildHelpers;
namespace Microsoft.DotNet.Cli.Build
{
public class BranchInfo

View file

@ -1,209 +0,0 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Microsoft.DotNet.Cli.Build.Framework;
using Microsoft.DotNet.InternalAbstractions;
using static Microsoft.DotNet.Cli.Build.Framework.BuildHelpers;
namespace Microsoft.DotNet.Cli.Build
{
public class Crossgen
{
private string _coreClrVersion;
private string _jitVersion;
private string _crossGenPath;
private static readonly string[] s_excludedLibraries =
{
"mscorlib.dll",
"mscorlib.ni.dll",
"System.Private.CoreLib.dll",
"System.Private.CoreLib.ni.dll"
};
// This is not always correct. The version of crossgen we need to pick up is whatever one was restored as part
// of the Microsoft.NETCore.Runtime.CoreCLR package that is part of the shared library. For now, the version hardcoded
// in CompileTargets and the one in the shared library project.json match and are updated in lock step, but long term
// we need to be able to look at the project.lock.json file and figure out what version of Microsoft.NETCore.Runtime.CoreCLR
// was used, and then select that version.
public Crossgen(string coreClrVersion, string jitVersion)
{
_coreClrVersion = coreClrVersion;
_jitVersion = jitVersion;
_crossGenPath = GetCrossgenPathForVersion();
}
private string GetCrossgenPathForVersion()
{
var crossgenPackagePath = GetCrossGenPackagePathForVersion();
if (crossgenPackagePath == null)
{
return null;
}
return Path.Combine(
crossgenPackagePath,
"tools",
$"crossgen{Constants.ExeSuffix}");
}
private string GetLibCLRJitPathForVersion()
{
var jitRid = GetCoreCLRRid();
var jitPackagePath = GetJitPackagePathForVersion();
if (jitPackagePath == null)
{
return null;
}
return Path.Combine(
jitPackagePath,
"runtimes",
jitRid,
"native",
$"{Constants.DynamicLibPrefix}clrjit{Constants.DynamicLibSuffix}");
}
private string GetJitPackagePathForVersion()
{
string jitRid = GetCoreCLRRid();
if (jitRid == null)
{
return null;
}
string packageId = $"runtime.{jitRid}.Microsoft.NETCore.Jit";
return Path.Combine(
Dirs.NuGetPackages,
packageId,
_jitVersion);
}
private string GetCoreLibsDirForVersion()
{
string coreclrRid = GetCoreCLRRid();
if (coreclrRid == null)
{
return null;
}
string packageId = $"runtime.{coreclrRid}.Microsoft.NETCore.Runtime.CoreCLR";
return Path.Combine(
Dirs.NuGetPackages,
packageId,
_coreClrVersion,
"runtimes",
coreclrRid,
"lib",
"netstandard1.0");
}
private string GetCrossGenPackagePathForVersion()
{
string coreclrRid = GetCoreCLRRid();
if (coreclrRid == null)
{
return null;
}
string packageId = $"runtime.{coreclrRid}.Microsoft.NETCore.Runtime.CoreCLR";
return Path.Combine(
Dirs.NuGetPackages,
packageId,
_coreClrVersion);
}
private string GetCoreCLRRid()
{
string rid = null;
if (CurrentPlatform.IsWindows)
{
var arch = RuntimeEnvironment.RuntimeArchitecture;
rid = $"win7-{arch}";
}
else if (CurrentPlatform.IsOSX)
{
rid = "osx.10.10-x64";
}
else if (CurrentPlatform.IsCentOS || CurrentPlatform.IsRHEL)
{
// CentOS runtime is in the runtime.rhel.7-x64... package as are all
// versions of RHEL
rid = "rhel.7-x64";
}
else if (CurrentPlatform.IsLinux)
{
rid = RuntimeEnvironment.GetRuntimeIdentifier();
}
return rid;
}
public void CrossgenDirectory(string sharedFxPath, string pathToAssemblies)
{
// Check if we need to skip crossgen
if (string.Equals(Environment.GetEnvironmentVariable("DISABLE_CROSSGEN"), "1"))
{
var originalColor = Console.ForegroundColor;
Console.ForegroundColor = ConsoleColor.Yellow;
Console.WriteLine("Skipping crossgen for because DISABLE_CROSSGEN is set to 1");
Console.ForegroundColor = originalColor;
return;
}
// HACK
// The input directory can be a portable FAT app (example the CLI itself).
// In that case there can be RID specific managed dependencies which are not right next to the app binary (example System.Diagnostics.TraceSource).
// We need those dependencies during crossgen. For now we just pass all subdirectories of the input directory as input to crossgen.
// The right fix -
// If the assembly has deps.json then parse the json file to get all the dependencies, pass these dependencies as input to crossgen.
// else pass the current directory of assembly as input to crossgen.
var coreLibsDir = GetCoreLibsDirForVersion();
var addtionalPaths = Directory.GetDirectories(pathToAssemblies, "*", SearchOption.AllDirectories).ToList();
var paths = new List<string>() { coreLibsDir, sharedFxPath, pathToAssemblies };
paths.AddRange(addtionalPaths);
var platformAssembliesPaths = string.Join(Path.PathSeparator.ToString(), paths.Distinct());
var jitPath = GetLibCLRJitPathForVersion();
var env = new Dictionary<string, string>()
{
// disable partial ngen
{ "COMPlus_PartialNGen", "0" }
};
foreach (var file in Directory.GetFiles(pathToAssemblies))
{
string fileName = Path.GetFileName(file);
if (s_excludedLibraries.Any(lib => String.Equals(lib, fileName, StringComparison.OrdinalIgnoreCase))
|| !PEUtils.HasMetadata(file))
{
continue;
}
string tempPathName = Path.ChangeExtension(file, "readytorun");
IList<string> crossgenArgs = new List<string> {
"-readytorun", "-in", file, "-out", tempPathName,
"-platform_assemblies_paths", platformAssembliesPaths
};
crossgenArgs.Add("-JITPath");
crossgenArgs.Add(jitPath);
ExecSilent(_crossGenPath, crossgenArgs, env);
File.Copy(tempPathName, file, overwrite: true);
File.Delete(tempPathName);
}
}
}
}

View file

@ -8,43 +8,10 @@ namespace Microsoft.DotNet.Cli.Build
public static class Dirs
{
public static readonly string RepoRoot = Directory.GetCurrentDirectory();
public static readonly string DebPackagingConfig = Path.Combine(Dirs.RepoRoot, "packaging", "deb");
public static readonly string Output = Path.Combine(
RepoRoot,
"artifacts",
RuntimeEnvironment.GetRuntimeIdentifier());
public static readonly string Intermediate = Path.Combine(Output, "intermediate");
public static readonly string PackagesNoRID = Path.Combine(RepoRoot, "artifacts", "packages");
public static readonly string Packages = Path.Combine(Output, "packages");
public static readonly string Stage1 = Path.Combine(Output, "stage1");
public static readonly string Stage1Compilation = Path.Combine(Output, "stage1compilation");
public static readonly string Stage1Symbols = Path.Combine(Output, "stage1symbols");
public static readonly string Stage2 = Path.Combine(Output, "stage2");
public static readonly string Stage2Compilation = Path.Combine(Output, "stage2compilation");
public static readonly string Stage2Symbols = Path.Combine(Output, "stage2symbols");
public static readonly string CorehostLatest = Path.Combine(Output, "corehost"); // Not using Path.Combine(Output, "corehost", "latest") to keep signing working.
public static readonly string CorehostLocked = Path.Combine(Output, "corehost", "locked");
public static readonly string CorehostLocalPackages = Path.Combine(Output, "corehost");
public static readonly string CorehostDummyPackages = Path.Combine(Output, "corehostdummypackages");
public static readonly string SharedFrameworkPublish = Path.Combine(Intermediate, "sharedFrameworkPublish");
public static readonly string TestOutput = Path.Combine(Output, "tests");
public static readonly string TestArtifacts = Path.Combine(TestOutput, "artifacts");
public static readonly string TestPackages = Path.Combine(TestOutput, "packages");
public static readonly string TestPackagesBuild = Path.Combine(TestOutput, "packagesBuild");
public static readonly string OSXReferenceAssembliesPath = "/Library/Frameworks/Mono.framework/Versions/Current/lib/mono/xbuild-frameworks";
public static readonly string UsrLocalReferenceAssembliesPath = "/usr/local/lib/mono/xbuild-frameworks";
public static readonly string UsrReferenceAssembliesPath = "/usr/lib/mono/xbuild-frameworks";
public static string NuGetPackages = Environment.GetEnvironmentVariable("NUGET_PACKAGES") ?? GetNuGetPackagesDir();
private static string GetNuGetPackagesDir()
{
return Path.Combine(Dirs.RepoRoot, ".nuget", "packages");
}
}
}

View file

@ -1,63 +0,0 @@
using System;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using Microsoft.DotNet.Cli.Build.Framework;
using Microsoft.DotNet.InternalAbstractions;
namespace Microsoft.DotNet.Cli.Build
{
public class DotNetCli
{
public static readonly DotNetCli Stage0 = new DotNetCli(GetStage0Path());
public static readonly DotNetCli Stage1 = new DotNetCli(Dirs.Stage1);
public static readonly DotNetCli Stage2 = new DotNetCli(Dirs.Stage2);
public string BinPath { get; }
public DotNetCli(string binPath)
{
BinPath = binPath;
}
public Command Exec(string command, params string[] args)
{
var newArgs = args.ToList();
newArgs.Insert(0, command);
if (EnvVars.Verbose)
{
newArgs.Insert(0, "-v");
}
return Command.Create(Path.Combine(BinPath, $"dotnet{Constants.ExeSuffix}"), newArgs);
}
public Command Restore(params string[] args) => Exec("restore", args);
public Command Build(params string[] args) => Exec("build", args);
public Command Pack(params string[] args) => Exec("pack", args);
public Command Test(params string[] args) => Exec("test", args);
public Command Publish(params string[] args) => Exec("publish", args);
public Command New(params string[] args) => Exec("new", args);
public string GetRuntimeId()
{
string info = Exec("", "--info").CaptureStdOut().Execute().StdOut;
string rid = Array.Find<string>(info.Split(Environment.NewLine.ToCharArray()), (e) => e.Contains("RID:"))?.Replace("RID:", "").Trim();
if (string.IsNullOrEmpty(rid))
{
throw new BuildFailureException("Could not find the Runtime ID from Stage0 --info or --version");
}
return rid;
}
private static string GetStage0Path()
{
return Path.Combine(Directory.GetCurrentDirectory(),
".dotnet_stage0",
RuntimeEnvironment.RuntimeArchitecture);
}
}
}

View file

@ -4,8 +4,6 @@ using System;
using Microsoft.DotNet.Cli.Build.Framework;
using static Microsoft.DotNet.Cli.Build.Framework.BuildHelpers;
namespace Microsoft.DotNet.Cli.Build
{
public static class FS
@ -17,90 +15,5 @@ namespace Microsoft.DotNet.Cli.Build
Directory.CreateDirectory(dir);
}
}
public static void Rm(string file)
{
if(File.Exists(file))
{
File.Delete(file);
}
}
public static void Rmdir(string dir)
{
if(Directory.Exists(dir))
{
Directory.Delete(dir, recursive: true);
}
}
public static void RmFilesInDirRecursive(string dir, string filePattern)
{
var files = Directory.EnumerateFiles(dir, filePattern, SearchOption.AllDirectories);
foreach (var file in files)
{
FS.Rm(file);
}
}
public static void Chmod(string file, string mode, bool recursive = false)
{
if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
if (recursive)
{
Command.Create("chmod", "-R", mode, file).Execute().EnsureSuccessful();
}
else
{
Command.Create("chmod", mode, file).Execute().EnsureSuccessful();
}
}
}
public static void ChmodAll(string searchDir, string pattern, string mode)
{
Exec("find", searchDir, "-type", "f", "-name", pattern, "-exec", "chmod", mode, "{}", ";");
}
public static void FixModeFlags(string dir)
{
if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
// Managed code doesn't need 'x'
ChmodAll(dir, "*.dll", "644");
ChmodAll(dir, "*.exe", "644");
// Generally, dylibs and sos have 'x' (no idea if it's required ;))
// (No need to condition this on OS since there shouldn't be any dylibs on Linux,
// but even if they are we may as well set their mode flags :))
ChmodAll(dir, "*.dylib", "755");
ChmodAll(dir, "*.so", "755");
// Executables (those without dots) are executable :)
Exec("find", dir, "-type", "f", "!", "-name", "*.*", "-exec", "chmod", "755", "{}", ";");
}
}
public static void CopyRecursive(string sourceDirectory, string destinationDirectory, bool overwrite = false)
{
Mkdirp(destinationDirectory);
foreach(var dir in Directory.EnumerateDirectories(sourceDirectory))
{
CopyRecursive(dir, Path.Combine(destinationDirectory, Path.GetFileName(dir)), overwrite);
}
foreach(var file in Directory.EnumerateFiles(sourceDirectory))
{
var dest = Path.Combine(destinationDirectory, Path.GetFileName(file));
if (!File.Exists(dest) || overwrite)
{
// We say overwrite true, because we only get here if the file didn't exist (thus it doesn't matter) or we
// wanted to overwrite :)
File.Copy(file, dest, overwrite: true);
}
}
}
}
}

View file

@ -1,27 +0,0 @@
using static Microsoft.DotNet.Cli.Build.Framework.BuildHelpers;
namespace Microsoft.DotNet.Cli.Build
{
public static class GitUtils
{
public static int GetCommitCount()
{
return int.Parse(ExecuteGitCommand("rev-list", "--count", "HEAD"));
}
public static string GetCommitHash()
{
return ExecuteGitCommand("rev-parse", "HEAD");
}
private static string ExecuteGitCommand(params string[] args)
{
var gitResult = Cmd("git", args)
.CaptureStdOut()
.Execute();
gitResult.EnsureSuccessful();
return gitResult.StdOut.Trim();
}
}
}

View file

@ -1,48 +0,0 @@
using System.Collections.Generic;
namespace Microsoft.DotNet.Cli.Build
{
public class HostVersion : Version
{
// ------------------------------------------HOST-VERSIONING-------------------------------------------
//
// Host versions are independent of CLI versions. Moreover, these version numbers
// are baked into the binary and is used to look up a serviced binary replacement.
//
//
// Latest hosts for production of nupkgs.
//
// Version constants without suffix
public override int Major => 1;
public override int Minor => 0;
public override int Patch => 1;
public override string ReleaseSuffix => "rc3";
public string LatestHostVersionNoSuffix => $"{Major}.{Minor}.{Patch}";
public string LatestHostFxrVersionNoSuffix => $"{Major}.{Minor}.{Patch}";
public string LatestHostPolicyVersionNoSuffix => $"{Major}.{Minor}.{Patch}";
public string LatestHostPrerelease => ReleaseSuffix;
public string LatestHostBuildMajor => $"{CommitCountString}";
public string LatestHostBuildMinor => "00";
public string LatestHostSuffix => $"{ReleaseSuffix}-{LatestHostBuildMajor}-{LatestHostBuildMinor}";
// Full versions and package information.
public string LatestHostVersion => $"{LatestHostVersionNoSuffix}-{LatestHostSuffix}";
public string LatestHostFxrVersion => $"{LatestHostFxrVersionNoSuffix}-{LatestHostSuffix}";
public string LatestHostPolicyVersion => $"{LatestHostPolicyVersionNoSuffix}-{LatestHostSuffix}";
public Dictionary<string, string> LatestHostPackages => new Dictionary<string, string>()
{
{ "Microsoft.NETCore.DotNetHost", LatestHostVersion },
{ "Microsoft.NETCore.DotNetHostResolver", LatestHostFxrVersion },
{ "Microsoft.NETCore.DotNetHostPolicy", LatestHostPolicyVersion }
};
//
// Locked muxer for consumption in CLI.
//
public bool IsLocked = false; // Set this variable to toggle muxer locking.
public string LockedHostFxrVersion => IsLocked ? "1.0.1-rc2-002468-00" : LatestHostFxrVersion;
public string LockedHostVersion => IsLocked ? "1.0.1-rc2-002468-00" : LatestHostVersion;
}
}

View file

@ -1,28 +0,0 @@
using System;
using System.IO;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace Microsoft.DotNet.Cli.Build
{
public static class JsonUtils
{
public static JObject ReadProject(string projectJsonPath)
{
using (TextReader projectFileReader = File.OpenText(projectJsonPath))
{
var projectJsonReader = new JsonTextReader(projectFileReader);
var serializer = new JsonSerializer();
return serializer.Deserialize<JObject>(projectJsonReader);
}
}
public static void WriteProject(JObject projectRoot, string projectJsonPath)
{
string projectJson = JsonConvert.SerializeObject(projectRoot, Formatting.Indented);
File.WriteAllText(projectJsonPath, projectJson + Environment.NewLine);
}
}
}

View file

@ -9,12 +9,6 @@ namespace Microsoft.DotNet.Cli.Build
{
public class Monikers
{
public const string SharedFrameworkName = "Microsoft.NETCore.App";
public const string CLISdkBrandName = "Microsoft .NET Core 1.0.0 - SDK Preview 2";
public const string SharedFxBrandName = "Microsoft .NET Core 1.0.0 - Runtime";
public const string SharedHostBrandName = "Microsoft .NET Core 1.0.0 - Host";
public const string HostFxrBrandName = "Microsoft .NET Core 1.0.0 - Host FX Resolver";
public static string GetBadgeMoniker()
{
switch (RuntimeEnvironment.GetRuntimeIdentifier())
@ -29,16 +23,6 @@ namespace Microsoft.DotNet.Cli.Build
return $"{CurrentPlatform.Current}_{CurrentArchitecture.Current}";
}
public static string GetDebianHostFxrPackageName(string hostFxrNugetVersion)
{
return $"dotnet-hostfxr-{hostFxrNugetVersion}".ToLower();
}
public static string GetDebianSharedFrameworkPackageName(string sharedFrameworkNugetVersion)
{
return $"dotnet-sharedframework-{SharedFrameworkName}-{sharedFrameworkNugetVersion}".ToLower();
}
public static string GetOSShortName()
{

View file

@ -1,27 +0,0 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection.PortableExecutable;
namespace Microsoft.DotNet.Cli.Build
{
public static class PEUtils
{
public static bool HasMetadata(string pathToFile)
{
try
{
using (var inStream = File.OpenRead(pathToFile))
{
using (var peReader = new PEReader(inStream))
{
return peReader.HasMetadata;
}
}
}
catch (BadImageFormatException) { }
return false;
}
}
}

View file

@ -11,27 +11,6 @@ namespace Microsoft.DotNet.Cli.Build
{
public class PublishMutationUtilties
{
public static void CleanPublishOutput(
string path,
string name,
bool deleteRuntimeConfigJson=false,
bool deleteDepsJson=false)
{
File.Delete(Path.Combine(path, $"{name}{Constants.ExeSuffix}"));
File.Delete(Path.Combine(path, $"{name}.dll"));
File.Delete(Path.Combine(path, $"{name}.pdb"));
if (deleteRuntimeConfigJson)
{
File.Delete(Path.Combine(path, $"{name}.runtimeconfig.json"));
}
if (deleteDepsJson)
{
File.Delete(Path.Combine(path, $"{name}.deps.json"));
}
}
public static void ChangeEntryPointLibraryName(string depsFile, string newName)
{
JToken deps;

View file

@ -9,83 +9,6 @@ namespace Microsoft.DotNet.Cli.Build
{
public static class Utils
{
public static void CleanNuGetTempCache()
{
// Clean NuGet Temp Cache on Linux (seeing some issues on Linux)
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux) && Directory.Exists("/tmp/NuGet"))
{
Directory.Delete("/tmp/NuGet", recursive: true);
}
}
public static string GetOSName()
{
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
return "win";
}
else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
{
return "osx";
}
else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
{
throw new NotImplementedException();
}
else
{
throw new PlatformNotSupportedException();
}
}
public static void DeleteDirectory(string path)
{
if (Directory.Exists(path))
{
string[] files = Directory.GetFiles(path, "*", SearchOption.AllDirectories);
foreach (string file in files)
{
File.SetAttributes(file, FileAttributes.Normal);
File.Delete(file);
}
var retry = 5;
while (retry >= 0)
{
try
{
Directory.Delete(path, true);
return;
}
catch (IOException)
{
if (retry == 0)
{
throw;
}
System.Threading.Thread.Sleep(200);
retry--;
}
}
}
}
public static void CopyDirectoryRecursively(string path, string destination, bool keepParentDir = false)
{
if (keepParentDir)
{
path = path.TrimEnd(Path.DirectorySeparatorChar);
destination = Path.Combine(destination, Path.GetFileName(path));
Directory.CreateDirectory(destination);
}
foreach (var file in Directory.GetFiles(path, "*", SearchOption.AllDirectories))
{
string destFile = file.Replace(path, destination);
Directory.CreateDirectory(Path.GetDirectoryName(destFile));
File.Copy(file, destFile, true);
}
}
public static string GetVersionFileContent(string commitHash, string version)
{
return $@"{commitHash}{Environment.NewLine}{version}{Environment.NewLine}";