Problems using NuGet.Core API to Install/Update/Delete packages

Apr 13, 2011 at 6:33 PM
Edited Apr 13, 2011 at 6:37 PM

Hi there.

 

I'm attempting to write a short command-line utility to help me with my package management in a CI scenario

For this purpose I've created the following class (based on a code example found after watching self-updating-site with NuGet)

 

1. remoteSource is set to a local NuGet feed (like C:\NuGet)

2. soulutionRoot is set to the folder containing the solution that is using NuGet, "packages" folder path is  solutionRoot/packages

 

public class TaskProjectManager
    {
        public TaskProjectManager(string remoteSource, string solutionRoot)
        {
            string localSource = Path.Combine(solutionRoot, "packages");

            IPackageRepository sourceRepository = PackageRepositoryFactory.Default.CreateRepository(new PackageSource(remoteSource));
            IPackageRepository localRepository = PackageRepositoryFactory.Default.CreateRepository(new PackageSource(localSource));
            IPackagePathResolver pathResolver = new DefaultPackagePathResolver(localSource);
            IProjectSystem project = new TaskProjectSystem(solutionRoot);
            ProjectManager = new ProjectManager(sourceRepository, pathResolver, project, localRepository);
        }

        #region Get-Package(s)
        public IQueryable<IPackage> GetInstalledPackages(string searchTerms)
        {
            return GetPackages(this.LocalRepository, searchTerms);
        }

        public static IQueryable<IPackage> GetPackages(IQueryable<IPackage> packages, string searchTerm)
        {
            if (!string.IsNullOrEmpty(searchTerm))
            {
                searchTerm = searchTerm.Trim();
                packages = packages.Find(searchTerm.Split(new char[0]));
            }

            return packages;
        }

        public static IQueryable<IPackage> GetPackages(IPackageRepository repository, string searchTerm)
        {
            return GetPackages(repository.GetPackages(), searchTerm);
        }

        public IEnumerable<IPackage> GetPackagesRequiringLicenseAcceptance(IPackage package)
        {
            IPackageRepository localRepository = this.LocalRepository;
            IPackageRepository sourceRepository = this.SourceRepository;
            return GetPackagesRequiringLicenseAcceptance(package, localRepository, sourceRepository);
        }

        private static IEnumerable<IPackage> GetPackageDependencies(IPackage package, IPackageRepository localRepository, IPackageRepository sourceRepository)
        {
            IPackageRepository repository = localRepository;
            IPackageRepository repository2 = sourceRepository;
            ILogger instance = NullLogger.Instance;
            bool ignoreDependencies = false;
            InstallWalker walker = new InstallWalker(repository, repository2, instance, ignoreDependencies);
            return walker.ResolveOperations(package).Where<PackageOperation>(delegate(PackageOperation operation)
            {
                return (operation.Action == PackageAction.Install);
            }).Select<PackageOperation, IPackage>(delegate(PackageOperation operation)
            {
                return operation.Package;
            });
        }

        public static IEnumerable<IPackage> GetPackagesRequiringLicenseAcceptance(IPackage package, IPackageRepository localRepository, IPackageRepository sourceRepository)
        {
            return GetPackageDependencies(package, localRepository, sourceRepository).Where<IPackage>(delegate(IPackage p)
            {
                return p.RequireLicenseAcceptance;
            });
        }

        public IQueryable<IPackage> GetPackagesWithUpdates(string searchTerms)
        {
            return GetPackages(this.SourceRepository.GetUpdates(this.LocalRepository.GetPackages()).AsQueryable<IPackage>(), searchTerms);
        }

        public IQueryable<IPackage> GetRemotePackages(string searchTerms)
        {
            return GetPackages(this.SourceRepository, searchTerms);
        }
        #endregion


        public IPackage GetUpdate(IPackage package)
        {
            return this.SourceRepository.GetUpdates(new IPackage[] { package }).SingleOrDefault<IPackage>();
        }

        public IEnumerable<string> InstallPackage(IPackage package)
        {
            return this.PerformLoggedAction(() => {            
                bool ignoreDependencies = false;
                this.ProjectManager.AddPackageReference(package.Id, package.Version, ignoreDependencies);
            });
        }

        public IEnumerable<string> UpdatePackage(IPackage package)
        {
            return this.PerformLoggedAction(() => {
                bool updateDependencies = true;                
                this.ProjectManager.UpdatePackageReference(package.Id, package.Version, updateDependencies);
            });
        }

        public IEnumerable<string> UninstallPackage(IPackage package, bool removeDependencies)
        {
            return this.PerformLoggedAction(() => {            
                bool forceRemove = false;
                bool flag1 = removeDependencies;
                this.ProjectManager.RemovePackageReference(package.Id, forceRemove, flag1);
            });
        }

        private IEnumerable<string> PerformLoggedAction(Action action)
        {
            ErrorLogger logger = new ErrorLogger();
            this.ProjectManager.Logger = logger;
            try
            {
                action();
            }
            finally
            {
                this.ProjectManager.Logger = null;
            }
            return logger.Errors;
        }

        IPackageRepository SourceRepository { get { return this.ProjectManager.SourceRepository; } }
        IPackageRepository LocalRepository { get { return this.ProjectManager.LocalRepository; } }
        IProjectManager ProjectManager;

        private class ErrorLogger : ILogger
        {
            private readonly IList<string> _errors = new List<string>();

            public void Log(MessageLevel level, string message, params object[] args)
            {
                if (level == MessageLevel.Warning)
                {
                    this._errors.Add(string.Format(CultureInfo.CurrentCulture, message, args));
                }
            }

            public IEnumerable<string> Errors
            {
                get
                {
                    return this._errors;
                }
            }
        }
    }

And the FileSystem class :

 public class TaskProjectSystem : PhysicalFileSystem, IFileSystem, IProjectSystem
    {
        public TaskProjectSystem(string root)
            : base(root)
        { }

        protected virtual string GetReferencePath(string name)
        {
            return Path.Combine("bin", name);
        }

        public object GetPropertyValue(string propertyName)
        {
            if ((propertyName != null) && propertyName.Equals("RootNamespace", StringComparison.OrdinalIgnoreCase))
            {
                return string.Empty;
            }
            return null;
        }

        #region IProjectSystem Members
        void IProjectSystem.AddReference(string referencePath, System.IO.Stream stream)
        {
            string fileName = Path.GetFileName(referencePath);
            string fullPath = this.GetFullPath(this.GetReferencePath(fileName));
            this.AddFile(fullPath, stream);
        }        

        dynamic IProjectSystem.GetPropertyValue(string propertyName)
        {
            throw new System.NotImplementedException();
        }

        bool IProjectSystem.IsSupportedFile(string path)
        {
            throw new System.NotImplementedException();
        }

        string IProjectSystem.ProjectName
        {
            get { throw new System.NotImplementedException(); }
        }

        bool IProjectSystem.ReferenceExists(string name)
        {
            string referencePath = this.GetReferencePath(name);
            return this.FileExists(referencePath);
        }

        void IProjectSystem.RemoveReference(string name)
        {
            this.DeleteFile(this.GetReferencePath(name));
            if (!this.GetFiles("bin").Any<string>())
            {
                this.DeleteDirectory("bin");
            }
        }

        System.Runtime.Versioning.FrameworkName IProjectSystem.TargetFramework
        {
            get { return VersionUtility.DefaultTargetFramework; }
        }
        #endregion


        public void AddFrameworkReference(string name)
        {
            throw new NotImplementedException();
        }
    }

 

 

I have encountered severe problems while attempting to do pretty much anything.

My Scenario :

Create a package with a single assembly to be referenced from that package. Add it to your local NuGet feed

Create a solution, Install-Package the forementioned package.

Create the next version of your package and add it you your local NuGet feed

Using the code & settings above, try to update your package or uninstall/install the package

Issues encountered :

1. UninstallPackage fails because it is trying to remove the packages/MyPackage.OLD_VERSION directory which is not emtpy, after the *.nupkg file is deleted.

The subfolder lib/MyAssembly.dll is left and so are the PS1 scripts in the tools folder. There's a try { } catch {} code segment that attempts to execute an action 3 times until it gives up and returns . That's where it all fails. 3 strikes, you're out.

FileSystemExtensions.cs L164 , Attempt is where "3 strikes" occurs

 

private static void DoSafeAction(Action action, ILogger logger) {
            try {
                Attempt(action);
            }
            catch (Exception e) {
                logger.Log(MessageLevel.Warning, e.Message);
            }
        }

 

2. UpdatePackage is not working because it is attempting to uninstall the previous package first and fails for the same reason

3. InstallPackage also failed, but I was to wasted to try and see what failed.

 

This is pretty much my 1st attempt at doing something with NuGet.Core API, I have no idea what did I manage to mess up, so your input will be highly apreciated.

Developer
Apr 13, 2011 at 7:01 PM

Thats alot of code you wrote :) but I wouldn't base it on the self updating website (that's a bad example to go off of). If you want to see what we do inside of VS take a look at:

http://nuget.codeplex.com/SourceControl/changeset/view/1179c56a3770#src%2fVisualStudio%2fVSPackageManager.cs

What you're trying to do It's pretty non trivial so expect some pain :). We don't have any support for doing this yet outside of VS so you're the first to do it. Hopefully the above code helps.

Apr 13, 2011 at 7:43 PM

Hi there, thanks for replying so quickly,

I'll see what I can come up with, I'll definitely post back when I run into something. If possible, please let me know what sort of problems should I be expecting ?

 

Developer
Apr 13, 2011 at 8:41 PM

VS locking files (you've already seen this). If you're using TFS files will be marked as read only. There's probably a bunch more

May 3, 2013 at 3:06 PM
Hi, i have similar issues. Im actually setting upp an self updating service for an web application.

I started out with the autoupgrade nuget package. Did not work since my package (compiled directly from my csproj file, got a lot of dependencies and some was to new for the nuget.core version). So i removed that and built my own WebProjectManager from scratch with dll inspection to work with the nuget.core 2.5.

Got a weird "No entry found" exception. Seamed that the Microsoft.AspNet.WebPages.Administration wich is only compatible with NuGet.Core (≥ 1.6.2 && < 1.7) and i now run Nuget.Core 2.5. Built a new WebProjectSystem from scratch by code inspection. Now i can manage to do an update.

However this is where i got my problem. When i get to the part where my library files is added: WebProjectSystem.AddReference(string referencePath, Stream stream)
Where my streams by some wierd reason is set to 0 in size (just like i got a empty stream) so im basicly adding new empty files for my new dll files of the package im installing. I can extract my files from the nuget package and apply them manually and it works so no problems there, but my streams is empty.

Any help on that one ?
May 16, 2013 at 11:01 AM
Azreal wrote:
Hi, i have similar issues. Im actually setting upp an self updating service for an web application.

I started out with the autoupgrade nuget package. Did not work since my package (compiled directly from my csproj file, got a lot of dependencies and some was to new for the nuget.core version). So i removed that and built my own WebProjectManager from scratch with dll inspection to work with the nuget.core 2.5.

Got a weird "No entry found" exception. Seamed that the Microsoft.AspNet.WebPages.Administration wich is only compatible with NuGet.Core (≥ 1.6.2 && < 1.7) and i now run Nuget.Core 2.5. Built a new WebProjectSystem from scratch by code inspection. Now i can manage to do an update.

However this is where i got my problem. When i get to the part where my library files is added: WebProjectSystem.AddReference(string referencePath, Stream stream)
Where my streams by some wierd reason is set to 0 in size (just like i got a empty stream) so im basicly adding new empty files for my new dll files of the package im installing. I can extract my files from the nuget package and apply them manually and it works so no problems there, but my streams is empty.

Any help on that one ?
Hi Azreal


is it possible for you to share updated WebProjectSystem against NuGet 2.5.0

Thanks in Advance
Amit
May 16, 2013 at 1:10 PM
Here is my WebProjectSystem class.
public class WebProjectSystem : PhysicalFileSystem, IProjectSystem, IFileSystem, IPropertyProvider
    {
        readonly ILog Log = LogManager.GetLogger(typeof(WebProjectSystem));
        private const string BinDir = "bin";
        private const string AppCodeFolder = "App_Code";
        private static readonly string[] _generatedFilesFolder = new string[]
        {
            "Generated___Files"
        };
        private static readonly string[] _sourceFileExtensions = new string[]
        {
            ".cs",
            ".vb"
        };
        private static readonly string[] _knownPublicKeys = new string[]
        {
            "b03f5f7f11d50a3a",
            "b77a5c561934e089",
            "31bf3856ad364e35"
        };
        /// <summary>This type/member supports the .NET Framework infrastructure and is not intended to be used directly from your code.</summary>
        public string ProjectName
        {
            get
            {
                return base.Root;
            }
        }
        /// <summary>This type/member supports the .NET Framework infrastructure and is not intended to be used directly from your code.</summary>
        public FrameworkName TargetFramework
        {
            get
            {
                return VersionUtility.DefaultTargetFramework;
            }
        }
        /// <summary>This type/member supports the .NET Framework infrastructure and is not intended to be used directly from your code.</summary>
        public WebProjectSystem(string root)
            : base(root)
        {
        }
        /// <summary>This type/member supports the .NET Framework infrastructure and is not intended to be used directly from your code.</summary>
        public void AddReference(string referencePath, Stream stream)
        {
            string fileName = Path.GetFileName(referencePath);
            string fullPath = this.GetFullPath(this.GetReferencePath(fileName));
            this.AddFile(fullPath, stream);
        }
        /// <summary>This type/member supports the .NET Framework infrastructure and is not intended to be used directly from your code.</summary>
        //[return: Dynamic]
        public dynamic GetPropertyValue(string propertyName)
        {
            if (propertyName == null)
            {
                return null;
            }
            if (propertyName.Equals("RootNamespace", StringComparison.OrdinalIgnoreCase))
            {
                return string.Empty;
            }
            return null;
        }
        /// <summary>This type/member supports the .NET Framework infrastructure and is not intended to be used directly from your code.</summary>
        public bool IsSupportedFile(string path)
        {
            return !Path.GetFileName(path).Equals("app.config", StringComparison.OrdinalIgnoreCase);
        }
        /// <summary>This type/member supports the .NET Framework infrastructure and is not intended to be used directly from your code.</summary>
        public bool ReferenceExists(string name)
        {
            string referencePath = this.GetReferencePath(name);
            return this.FileExists(referencePath);
        }
        /// <summary>This type/member supports the .NET Framework infrastructure and is not intended to be used directly from your code.</summary>
        public void RemoveReference(string name)
        {
            this.DeleteFile(this.GetReferencePath(name));
            if (!this.GetFiles("bin", false).Any<string>())
            {
                this.DeleteDirectory("bin");
            }
        }
        /// <summary>This type/member supports the .NET Framework infrastructure and is not intended to be used directly from your code.</summary>
        public void AddFrameworkReference(string name)
        {
            string text = WebProjectSystem.ResolvePartialAssemblyName(name);
            if (text == null)
            {
                //throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, PackageManagerResources.UnknownFrameworkReference, new object[]
                //{
                //    name
                //})); 
                throw new InvalidOperationException("Unknown framework reference");
            }
            WebProjectSystem.AddReferencesToConfig(this, text);
        }
        /// <summary>This type/member supports the .NET Framework infrastructure and is not intended to be used directly from your code.</summary>
        public override IEnumerable<string> GetDirectories(string path)
        {
            if (WebProjectSystem.IsUnderAppCode(path))
            {
                return base.GetDirectories(path).Except(WebProjectSystem._generatedFilesFolder, StringComparer.OrdinalIgnoreCase);
            }
            return base.GetDirectories(path);
        }
        /// <summary>This type/member supports the .NET Framework infrastructure and is not intended to be used directly from your code.</summary>
        public string ResolvePath(string path)
        {
            if (WebProjectSystem.RequiresAppCodeRemapping(path))
            {
                path = Path.Combine("App_Code", path);
            }
            return path;
        }
        /// <summary>This type/member supports the .NET Framework infrastructure and is not intended to be used directly from your code.</summary>
        protected virtual string GetReferencePath(string name)
        {
            return Path.Combine("bin", name);
        }
        internal static string ResolvePartialAssemblyName(string name)
        {
            string[] knownPublicKeys = WebProjectSystem._knownPublicKeys;
            for (int i = 0; i < knownPublicKeys.Length; i++)
            {
                string text = knownPublicKeys[i];
                string text2 = string.Format(CultureInfo.InvariantCulture, "{0}, Version={1}, Culture=neutral, PublicKeyToken={2}", new object[]
                {
                    name,
                    VersionUtility.DefaultTargetFrameworkVersion,
                    text
                });
                try
                {
                    Assembly.Load(text2);
                    return text2;
                }
                catch
                {
                }
            }
            return null;
        }
        internal static void AddReferencesToConfig(IFileSystem fileSystem, string references)
        {
            string text = Path.Combine(fileSystem.Root, "web.config");
            XDocument xDocument;
            if (fileSystem.FileExists(text))
            {
                using (Stream stream = fileSystem.OpenFile(text))
                {
                    xDocument = XDocument.Load(stream, LoadOptions.PreserveWhitespace);
                    goto IL_68;
                }
            }
            xDocument = new XDocument(new object[]
            {
                new XElement("configuration")
            });
        IL_68:
            XElement orCreateChild = WebProjectSystem.GetOrCreateChild(xDocument.Root, "system.web/compilation/assemblies");
            if (!(
                from item in orCreateChild.Elements()
                where !string.IsNullOrEmpty(item.GetOptionalAttributeValue("assembly", null))
                let assemblyName = new AssemblyName(item.Attribute("assembly").Value).Name
                where string.Equals(assemblyName, references, StringComparison.OrdinalIgnoreCase)
                select item).Any<XElement>())
            {
                orCreateChild.Add(new XElement("add", new XAttribute("assembly", references)));
                WebProjectSystem.SaveDocument(fileSystem, text, xDocument);
            }
        }
        private static void SaveDocument(IFileSystem fileSystem, string webConfigPath, XDocument document)
        {
            using (MemoryStream memoryStream = new MemoryStream())
            {
                document.Save(memoryStream);
                memoryStream.Seek(0L, SeekOrigin.Begin);
                fileSystem.AddFile(webConfigPath, memoryStream);
            }
        }
        private static XElement GetOrCreateChild(XElement element, string childName)
        {
            string[] array = childName.Split(new char[]
            {
                '/'
            });
            for (int i = 0; i < array.Length; i++)
            {
                string expandedName = array[i];
                XElement xElement = element.Element(expandedName);
                if (xElement == null)
                {
                    xElement = new XElement(expandedName);
                    element.Add(xElement);
                }
                element = xElement;
            }
            return element;
        }
        private static bool RequiresAppCodeRemapping(string path)
        {
            return !WebProjectSystem.IsUnderAppCode(path) && WebProjectSystem.IsSourceFile(path);
        }
        private static bool IsUnderAppCode(string path)
        {
            return path.StartsWith("App_Code" + Path.DirectorySeparatorChar, StringComparison.OrdinalIgnoreCase);
        }
        private static bool IsSourceFile(string path)
        {
            return WebProjectSystem._sourceFileExtensions.Contains(Path.GetExtension(path), StringComparer.OrdinalIgnoreCase);
        }


        public void AddImport(string targetPath, ProjectImportLocation location)
        {
            throw new NotImplementedException();
        }

        public bool FileExistsInProject(string path)
        {
            Log.DebugFormat("FileExistsInProject()... : {0}", path);
            return base.FileExists(path);
        }

        public bool IsBindingRedirectSupported
        {
            get { return false; }
        }

        public void RemoveImport(string targetPath)
        {
            throw new NotImplementedException();
        }
    }
In the end i took the code from nuget.core and found the issue to my problem.

In class Project manager method ExtractPackageFilesToProject, they use the method AddReference call to my WebProjectSystem. However i found this there:
// The current implementation of all ProjectSystem does not use the Stream parameter at all.
                    // We can't change the API now, so just pass in a null stream.
                    Project.AddReference(relativeReferencePath, Stream.Null);
i changed Stream.Null to assemblyReference.GetStream() and that solved everything. However i had to add a lot of classes manually from the nuget source to my project.
May 20, 2013 at 4:46 PM
Thank you so much guys for all your posts. It is very helpful to me especially that I am new to this.

tulum wedding photography
May 20, 2013 at 5:10 PM
Thanks Azreal


i m all set with update manger for my asp.net 2.o website n service with rollback to previous version.

thanks a lot :)
Feb 11, 2014 at 4:06 PM
Hi, I'm trying to do something similar but it doesn't add the library file to the bin directory. I have used your WebProjectSystem class and tried the following:
var sourceRepository = PackageRepositoryFactory.Default.CreateRepository(@"C:\Packages");
var localRepository = PackageRepositoryFactory.Default.CreateRepository(Server.MapPath("~/App_Data/Packages"));

var projectManager = new ProjectManager(sourceRepository, new DefaultPackagePathResolver(Server.MapPath("~/App_Data/Packages")), new WebProjectSystem(Server.MapPath("~/")), localRepository);

var package = packages.Single(p => p.Id == "MyPackage");

projectManager.AddPackageReference(package.Id, package.Version, true, false);
I'd appreciate it if someone could show me what I've done wrong. Thanks
Feb 12, 2014 at 9:59 AM
Edited Feb 13, 2014 at 9:30 AM
Problem solved, my library file was targeting a different version of the .Net framework. I also had the same issue Azreal was having. Instead of copying all the NuGet files to my project. I changed the AddReference method of the WebProjectSystem class to:

public void AddReference(string referencePath, Stream stream) {
    if (stream == Stream.Null) {
        var tempPath = Path.Combine(Path.GetTempPath(), "nuget");
        var libraryPath = referencePath.Substring(referencePath.IndexOf(@"\lib\"));
        var filePath = Directory.GetFiles(tempPath, "*" + Path.GetFileName(referencePath), SearchOption.AllDirectories).LastOrDefault(f => f.EndsWith(libraryPath));

        if (string.IsNullOrEmpty(filePath))
            throw new Exception("Reference not found");

        stream = new FileStream(filePath, FileMode.Open);
    }

    string fileName = Path.GetFileName(referencePath);
    string fullPath = this.GetFullPath(this.GetReferencePath(fileName));
    this.AddFile(fullPath, stream);

    stream.Dispose();
}
Edit

It actually looks like someone came up with a better solution. See https://nuget.codeplex.com/discussions/479191. I've also posted this as a bug. See https://nuget.codeplex.com/workitem/4029 for more information.