Programmatically delete local repository with LibGit2Sharp - c#

I would like to delete the local repo folder that I cloned from remote repository using LibGit2Sharp.
I read here here that I have to Dispose() the Repository before I can delete it, but it still not works fine.
using (var repo = new LibGit2Sharp.Repository(path))
{
repo.Dispose();
}
Directory.DeleteFolder(path);
And I still have an exception:
Access to the path 'c16566a7-202a-4c8a-84de-3e3caadd5af9' is denied.
The content of the 'path' variable is the following:
C:\Users\USERNAME\AppData\Local\dftmp\Resources\c16566a7-202a-4c8a-84de-3e3caadd5af9\directory\UserRepos\github.com\domonkosgabor\testrepo
This folder was created by a worker role to a local storage.
What should I do to delete the whole folder (including .git)?

For the benefit of anyone else having this problem:
I had the same problem, but I was still getting an UnauthorizedAccessException even though I was running as administrator, and I was disposing the repository object correctly. It turns out that some of the files in the .git folder are marked as ReadOnly, so I had to loop through each file and remove the ReadOnly attribute before deleting. I wrote a custom method to do this:
/// <summary>
/// Recursively deletes a directory as well as any subdirectories and files. If the files are read-only, they are flagged as normal and then deleted.
/// </summary>
/// <param name="directory">The name of the directory to remove.</param>
public static void DeleteReadOnlyDirectory(string directory)
{
foreach (var subdirectory in Directory.EnumerateDirectories(directory))
{
DeleteReadOnlyDirectory(subdirectory);
}
foreach (var fileName in Directory.EnumerateFiles(directory))
{
var fileInfo = new FileInfo(fileName);
fileInfo.Attributes = FileAttributes.Normal;
fileInfo.Delete();
}
Directory.Delete(directory);
}

I would like to delete the local repo folder that I cloned from remote repository using LibGit2Sharp. I read here here that I have to Dispose() the Repository before I can delete it.
LibGit2Sharp keeps hold on some files in the .git folder (mainly the packfiles for performance reasons). Calling Dispose() will release those handles and deallocate the non managed memory.
As such, it's indeed a strong recommendation to rely on the using statement (or, at the very least to Dispose() the Repository instance when you're done with it).
If you don't do this, those handles will eventually be released through finalizers when your AppDomain has unloaded, but you will have no real control regarding "when" that's going to happen.
Edit: Reading your code once again, I overlooked something. The recommended pattern is either
using (var repo = new LibGit2Sharp.Repository(path))
{
// Do amazing stuff
}
or
var repo = new LibGit2Sharp.Repository(path);
// Do amazing stuff
repo.Dispose();
Indeed, the using statement will automatically issue a call to Dispose() once the code reach the end of the scope.
Access to the path 'c16566a7-202a-4c8a-84de-3e3caadd5af9' is denied.
Regarding this point, I think this has nothing to do with LibGit2Sharp.
Is the process (trying to delete the folder named after a guid) running under an identity granted with enough rights to do so?

Related

How to output a file from a roslyn code analyzer?

I'm using the roslyn API to write a DiagnosticAnalyzer and CodeFix.
After I have collected all strings and string-interpolations, I want to write all of them to a file but I am not sure how to do this the best way.
Of course I can always simply do a File.WriteAllText(...) but I'd like to expose more control to the user.
I'm also not sure about how to best trigger the generation of this file, so my questions are:
I do not want to hard-code the filename, what would be the best way to expose this setting to the user of the code-analyzer? A config file? If so, how would I access that? ie: How do I know the directory?
If one string is missing from the file, I'd like to to suggest a code fix like "Project contains changed or new strings, regenerate string file". Is this the best way to do this? Or is it possible to add a button or something to visual studio?
I'm calling the devenv.com executable from the commandline to trigger builds, is there a way to force my code-fix to run either while building, or before/after? Or would I have to "manually" load the solution with roslyn and execute my codefix?
I've just completed a project on this. There are a few things that you will need to do / know.
You will probably need to switch you're portable class library to a class library. otherwise you will have trouble calling the File.WriteAllText()
You can see how to Convert a portable class library to a regular here
This will potentially not appropriately work for when trying to apply all changes to document/project/solution. When Calling from a document/project/solution, the changes are precalcuated and applied in a preview window. If you cancel, an undo action is triggered to undo all changes, if you write to a file during this time, and do not register an undo action you will not undo the changes to the file.
I've opened a bug with roslyn but you can handle instances by override the preview you can see how to do so here
And one more final thing you may need to know is how to access the Solution from the analyzer which, Currently there is a hack I've written to do so here
As Tamas said you can use additional files you can see how to do so here
You can use additional files, but I know on the version I'm using resource files, are not marked as additional files by default they are embeddedResources.
So, for my users to not have to manually mark the resource as additonalFiles I wrote a function to get out the Designer.cs files associated with resource files from the csproj file using xDoc you can use it as an example if you choose to parse the csproj file:
protected List<string> GetEmbeddedResourceResxDocumentPaths(Project project)
{
XDocument xmldoc = XDocument.Load(project.FilePath);
XNamespace msbuild = "http://schemas.microsoft.com/developer/msbuild/2003";
var resxFiles = new List<string>();
foreach (var resource in xmldoc.Descendants(msbuild + "EmbeddedResource"))
{
string includePath = resource.Attribute("Include").Value;
var includeExtension = Path.GetExtension(includePath);
if (0 == string.Compare(includeExtension, RESX_FILE_EXTENSION, StringComparison.OrdinalIgnoreCase))
{
var outputTag = resource.Elements(msbuild + LAST_GENERATED_TAG).FirstOrDefault();
if (null != outputTag)
{
resxFiles.Add(outputTag.Value);
}
}
}
return resxFiles;
}
For config files you can use the AdditionalFiles msbuild property, which is passed to the analyzers through the context. See here.

Dynamically compiled project losing resources

I need to compile source code of big project dynamically and output type can be Windows Application or Class Library.
Code is nicely executed and its possible to make .dll or .exe files, but problem is that, when I'm trying to make .exe file - it's losing resources like project icon. Result file doesn't include assembly information to.
Any way to solve this? (Expected result should be the same, that manual Build function on project file in Visual Studio 2015).
Thank you!
var workspace = MSBuildWorkspace.Create();
//Locating project file that is WindowsApplication
var project = workspace.OpenProjectAsync(#"C:\RoslynTestProjectExe\RoslynTestProjectExe.csproj").Result;
var metadataReferences = project.MetadataReferences;
// removing all references
foreach (var reference in metadataReferences)
{
project = project.RemoveMetadataReference(reference);
}
//getting new path of dlls location and adding them to project
var param = CreateParamString(); //my own function that returns list of references
foreach (var par in param)
{
project = project.AddMetadataReference(MetadataReference.CreateFromFile(par));
}
//compiling
var projectCompilation = project.GetCompilationAsync().Result;
using (var stream = new MemoryStream())
{
var result = projectCompilation.Emit(stream);
if (result.Success)
{
/// Getting result
//writing exe file
using (var file = File.Create(Path.Combine(_buildPath, fileName)))
{
stream.Seek(0, SeekOrigin.Begin);
stream.CopyTo(file);
}
}
}
We never really designed the workspace API to include all the information you need to emit like this; in particular when you're calling Emit there's an EmitOptions you can pass that includes, amongst other things, resource information. But we don't expose that information since this scenario wasn't hugely considered. We've done some of the work in the past to enable this but ultimately never merged it. You might wish to consider filing a bug so we officially have the request somewhere.
So what can you do? I think there's a few options. You might consider not using Roslyn at all but rather modifying the project file and building that with the MSBuild APIs. Unfortunately I don't know what you're ultimately trying to achieve here (it would help if you mentioned it), but there's a lot more than just the compiler invocation that is involved in building a project. Changing references potentially changes other things too.
It'd also be possible, of course, to update MSBuildWorkspace yourself to pass this through. If you were to modify the Roslyn code, you'll see we implement a series of interfaces named "ICscHostObject#" (where # is a number) and we get passed the information from MSBuild to that. It looks like we already stash that in the command line arguments, so you might be able to pass that to our command line parser and get the data back you need that way.

Do I need to check if directory/file exist or not?

Which one code is better?
Code1:
if (!Directory.Exists("DirectoryPathHere"))
Directory.CreateDirectory("DirectoryPathHere");
Code2:
Directory.CreateDirectory("DirectoryPathHere");
I think Code2 because as I saw it not gives any error and its not making new folder when the folder already exists, so I though that checking for folder existence is useless. Right?
You don't need to check if the directory already exists, the method does it for you. If you check on MSDN :
Any and all directories specified in path are created, unless they
already exist or unless some part of path is invalid. The path
parameter specifies a directory path, not a file path. If the
directory already exists, this method does not create a new directory,
but it returns a DirectoryInfo object for the existing directory.
I would use a DirectoryInfo class, check if it exists, and maybe also if it does exist, check the permissions on the directory in case my current run-time permissions are not sufficient to access the contents or update the directory.
You should apply exception handling to whichever method you go with; what if, for instance, a file exists with the name of the directory?
The key thing is the CreateDirectory method implicitly checks if the Directory Exists before attempting to create it.
For code readability it is better to use the explicit method Directory.Exists first.
I also strongly agree with #SimonWhitehead on the defensive programming front. Showing you're aware of the pit falls... and defending actively against them explicitly it in your code is a good thing:)
I think we can all see the fact that the second method does the same,
but, is it cheaper in terms of being more readable? No.
Anyone who knows the framework will probably disagree, and I can too. But:
Always code as if the person who ends up maintaining your code is a
violent psychopath who knows where you live.
http://www.codinghorror.com/blog/2008/06/coding-for-violent-psychopaths.html
Edit 2: I have a funny feeling that the compiler does this. The assembly programmers would be able to detect it before producing the IL..
Here is a simple code from http://msdn.microsoft.com/en-us/library/54a0at6s.aspx
using System;
using System.IO;
class Test
{
public static void Main()
{
// Specify the directory you want to manipulate.
string path = #"c:\MyDir";
try
{
// Determine whether the directory exists.
if (Directory.Exists(path))
{
Console.WriteLine("That path exists already.");
return;
}
// Try to create the directory.
DirectoryInfo di = Directory.CreateDirectory(path);
Console.WriteLine("The directory was created successfully at {0}.", Directory.GetCreationTime(path));
// Delete the directory.
di.Delete();
Console.WriteLine("The directory was deleted successfully.");
}
catch (Exception e)
{
Console.WriteLine("The process failed: {0}", e.ToString());
}
finally {}
}
}
You don't need to check it, but because there are many problems that occur when handling files and folder, its better that include a try-catch statement so that any potential problems be handled:
try {
Directory.CreateDirectory("DirectoryPathHere");
}
catch (Exception ex)
{
MessageBox.Show("Error: "+ex.Message);
}
you could also add finally if its needed.

Assembly Location Changes in Runtime

I'm running coded ui automation and defined a method attribute called [ExternalDataSource()] to read a document (csv, xml...) and parse the data into some dictionaries. I'll copy it here so you can have a better insight:
[System.AttributeUsage(System.AttributeTargets.Method)]
public class ExternalDataSource : System.Attribute
{
public ExternalDataSource(string filename)
{
DirectoryInfo di = new DirectoryInfo(Assembly.GetExecutingAssembly().Location);
string file = Path.Combine(Path.GetDirectoryName(di.FullName), filename);
try
{
code
}
catch (Exception)
{
throw new UITestException("Cannot load data source document");
}
}
}
In it I try to access Assembly.GetExecutingAssembly().Location to get a file that is copied to the TestResult/Out folder. I assigned this attribute to only one TestMethod() in the whole application and while debugging, I found out that the application enters the attribute's c'tor twice. Both times the Location is different. Once it's from the bin/Debug folder, the other time it's from the TestResults/Out folder. Two questions:
Why does the debugger enter that attribute twice if I call it only once in my application?
Why does the location of the same assembly change?
Well it seems nobody had an answer, but while debugging a run from the command line using mstest.exe with the vs2012 JIT Debugger i found out a strange thing:
When putting a System.Diagnostics.Debugger.Break() in the class where this attribute is the jitter was called from MSTest.exe but when this breakpoint was in the testmethod decorated with this attribute, QTAgent32.exe was called. I had implemented a singleton class to handle my parameters, and while it was populated in ExternalDataSource in this attribute by MSTest, when entering QTAgent32 (the test) it was empty.
The solution that worked for me was just to initialize that Singleton with the data on [TestInitialize()].
Hope this helps somebody.

C# - Sharing resources file between project

I would like two how to do to share Resources files between 2 (or more) projects?
So, to resume, I've three project :
the development project (CF.NET) that include the resource file (with all definition).
I've two other projects that are empty BUT linking to the development projects, it's just a different build each time, so when I modify the development project, all three projects are updated too. (Modification of the csproj file.)
Question is, what about Resources files? When I try to access from the development project I get all resources but when I try from the 2 others, it throws an "MissingManifestResourceException".
Any idea how to solve this issue?
Thanks.
[EDIT]
Here is what I've done :
Create a project named "RealProject" which contains all code (including resources files)
Create a project named "LinkedProject" which contains nothing (I deleted all files into it and modify the csproj file as the following :
<ItemGroup>
<Compile Include="..\RealProject\**\*.cs" />
</ItemGroup>
So in LinkedProject directory I've only :
[Directory] bin
[Directory] obj
[File ] LinkedProject.csproj
The whole LinkedProject uses the RealProject files, it's just a different configuration build (see here to know why : C# - Code compiler for .NET & CF.NET )
Once in that configuration, I've no access to the resources files from the RealProject ...
If you need screens or more detailed explanation, just ask.
[EDIT]
With this code, it works, Resource manager isn't loaded on the good Assembly name, but it should exists a better solution !!!
Assembly ass = Assembly.ReflectionOnlyLoadFrom(#"..\..\..\RealProject\bin\Debug\RealProject.dll");
ResourceManager manager = new ResourceManager("RealProject.Properties.Resources", ass);
[Solution]
Things to check :
The LinkedProject as the same
namespace as the RealProject
Add Resources as links
Clean up all your solution
Rebuild it
Test !
Try to add the resource file as a link to the other two projects and make sure the namespaces as defined in the project file is the same.
Try adding existing file in other projects as a link.
The problem with sharing resources files between different projects is that the root namespace has to be the same in all the projects you use the same file in.
Or not.
You can get the root namespace at runtime in the *Resources.designer.cs file. Note the links in the comments to related answers. Make sure you commit this and keep an eye on it, the code-generator has a habit of overwriting it which would break its universality. I used the xml doc comments to remind me what's going on, if the code-gen obliterates it I'll see it in the commit diff.
/// <summary> Returns the cached ResourceManager instance used by this class. </summary>
/// <remarks> DO NOT commit any version of this file the tool overwrites, it will break it for other projects</remarks>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
public static global::System.Resources.ResourceManager ResourceManager {
get {
if (object.ReferenceEquals(resourceMan, null))
{
// https://stackoverflow.com/a/1329631/492 https://stackoverflow.com/a/51978052/492
Assembly thisAssembly = typeof(/*thisClassName*/).Assembly;
// you need a class called "App", or just change the name on the next line to one you have in the root namespace
var apps = thisAssembly?.GetTypes().Where(t => t.Name == "App");
if (apps.Count() != 1)
throw new InvalidOperationException($"Too Many App classes. Count: {apps.Count()}, Apps: {apps}");
Type appType = apps.FirstOrDefault();
string ns = appType.Namespace;
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager($"{ns}.OtherNames.Spaces.thisClassName", typeof(thisClassName).Assembly);
resourceMan = temp;
}
return resourceMan;
}
}

Categories