示例#1
0
        public static async Task InstallFromSourceAsync(
            Func <Stream, Task> copyToAsync,
            VersionFolderPathContext versionFolderPathContext,
            CancellationToken token)
        {
            if (copyToAsync == null)
            {
                throw new ArgumentNullException(nameof(copyToAsync));
            }

            if (versionFolderPathContext == null)
            {
                throw new ArgumentNullException(nameof(versionFolderPathContext));
            }

            var packagePathResolver = new VersionFolderPathResolver(versionFolderPathContext.PackagesDirectory);

            var packageIdentity = versionFolderPathContext.Package;
            var logger          = versionFolderPathContext.Logger;

            var targetPath   = packagePathResolver.GetInstallPath(packageIdentity.Id, packageIdentity.Version);
            var targetNuspec = packagePathResolver.GetManifestFilePath(packageIdentity.Id, packageIdentity.Version);
            var targetNupkg  = packagePathResolver.GetPackageFilePath(packageIdentity.Id, packageIdentity.Version);
            var hashPath     = packagePathResolver.GetHashPath(packageIdentity.Id, packageIdentity.Version);

            logger.LogVerbose(
                $"Acquiring lock for the installation of {packageIdentity.Id} {packageIdentity.Version}");

            // Acquire the lock on a nukpg before we extract it to prevent the race condition when multiple
            // processes are extracting to the same destination simultaneously
            await ConcurrencyUtilities.ExecuteWithFileLockedAsync(targetNupkg,
                                                                  action : async cancellationToken =>
            {
                // If this is the first process trying to install the target nupkg, go ahead
                // After this process successfully installs the package, all other processes
                // waiting on this lock don't need to install it again.
                if (!File.Exists(hashPath))
                {
                    logger.LogVerbose(
                        $"Acquired lock for the installation of {packageIdentity.Id} {packageIdentity.Version}");

                    logger.LogMinimal(string.Format(
                                          CultureInfo.CurrentCulture,
                                          Strings.Log_InstallingPackage,
                                          packageIdentity.Id,
                                          packageIdentity.Version));

                    cancellationToken.ThrowIfCancellationRequested();

                    // We do not stop the package extraction after this point
                    // based on CancellationToken, but things can still be stopped if the process is killed.
                    if (Directory.Exists(targetPath))
                    {
                        // If we had a broken restore, clean out the files first
                        var info = new DirectoryInfo(targetPath);

                        foreach (var file in info.GetFiles())
                        {
                            file.Delete();
                        }

                        foreach (var dir in info.GetDirectories())
                        {
                            dir.Delete(true);
                        }
                    }
                    else
                    {
                        Directory.CreateDirectory(targetPath);
                    }

                    var targetTempNupkg = Path.Combine(targetPath, Path.GetRandomFileName());
                    var tempHashPath    = Path.Combine(targetPath, Path.GetRandomFileName());
                    var packageSaveMode = versionFolderPathContext.PackageSaveMode;

                    // Extract the nupkg
                    using (var nupkgStream = new FileStream(
                               targetTempNupkg,
                               FileMode.Create,
                               FileAccess.ReadWrite,
                               FileShare.ReadWrite | FileShare.Delete,
                               bufferSize: 4096,
                               useAsync: true))
                    {
                        await copyToAsync(nupkgStream);
                        nupkgStream.Seek(0, SeekOrigin.Begin);

                        using (var packageReader = new PackageArchiveReader(nupkgStream))
                        {
                            var nuspecFile = packageReader.GetNuspecFile();
                            if ((packageSaveMode & PackageSaveMode.Nuspec) == PackageSaveMode.Nuspec)
                            {
                                packageReader.ExtractFile(nuspecFile, targetNuspec, logger);
                            }

                            if ((packageSaveMode & PackageSaveMode.Files) == PackageSaveMode.Files)
                            {
                                var nupkgFileName  = Path.GetFileName(targetNupkg);
                                var nuspecFileName = Path.GetFileName(targetNuspec);
                                var hashFileName   = Path.GetFileName(hashPath);
                                var packageFiles   = packageReader.GetFiles()
                                                     .Where(file => ShouldInclude(file, hashFileName));
                                var packageFileExtractor = new PackageFileExtractor(
                                    packageFiles,
                                    versionFolderPathContext.XmlDocFileSaveMode);
                                packageReader.CopyFiles(
                                    targetPath,
                                    packageFiles,
                                    packageFileExtractor.ExtractPackageFile,
                                    logger,
                                    token);
                            }

                            string packageHash;
                            nupkgStream.Position = 0;
                            packageHash          = Convert.ToBase64String(new CryptoHashProvider("SHA512").CalculateHash(nupkgStream));

                            File.WriteAllText(tempHashPath, packageHash);
                        }
                    }

                    // Now rename the tmp file
                    if ((versionFolderPathContext.PackageSaveMode & PackageSaveMode.Nupkg) ==
                        PackageSaveMode.Nupkg)
                    {
                        File.Move(targetTempNupkg, targetNupkg);
                    }
                    else
                    {
                        try
                        {
                            File.Delete(targetTempNupkg);
                        }
                        catch (IOException ex)
                        {
                            logger.LogWarning(string.Format(
                                                  CultureInfo.CurrentCulture,
                                                  Strings.ErrorUnableToDeleteFile,
                                                  targetTempNupkg,
                                                  ex.Message));
                        }
                    }

                    // Note: PackageRepository relies on the hash file being written out as the
                    // final operation as part of a package install to assume a package was fully installed.
                    // Rename the tmp hash file
                    File.Move(tempHashPath, hashPath);

                    logger.LogVerbose($"Completed installation of {packageIdentity.Id} {packageIdentity.Version}");
                }
                else
                {
                    logger.LogVerbose("Lock not required - Package already installed "
                                      + $"{packageIdentity.Id} {packageIdentity.Version}");
                }

                return(0);
            },
                                                                  token : token);
        }
示例#2
0
        public static async Task <bool> InstallFromSourceAsync(
            IPackageDownloader packageDownloader,
            VersionFolderPathContext versionFolderPathContext,
            CancellationToken token)
        {
            if (packageDownloader == null)
            {
                throw new ArgumentNullException(nameof(packageDownloader));
            }

            if (versionFolderPathContext == null)
            {
                throw new ArgumentNullException(nameof(versionFolderPathContext));
            }

            var versionFolderPathResolver = new VersionFolderPathResolver(
                versionFolderPathContext.PackagesDirectory,
                versionFolderPathContext.IsLowercasePackagesDirectory);

            var packageIdentity = versionFolderPathContext.Package;
            var logger          = versionFolderPathContext.Logger;

            var targetPath   = versionFolderPathResolver.GetInstallPath(packageIdentity.Id, packageIdentity.Version);
            var targetNuspec = versionFolderPathResolver.GetManifestFilePath(packageIdentity.Id, packageIdentity.Version);
            var targetNupkg  = versionFolderPathResolver.GetPackageFilePath(packageIdentity.Id, packageIdentity.Version);
            var hashPath     = versionFolderPathResolver.GetHashPath(packageIdentity.Id, packageIdentity.Version);

            logger.LogVerbose(
                $"Acquiring lock for the installation of {packageIdentity.Id} {packageIdentity.Version}");

            // Acquire the lock on a nukpg before we extract it to prevent the race condition when multiple
            // processes are extracting to the same destination simultaneously
            return(await ConcurrencyUtilities.ExecuteWithFileLockedAsync(targetNupkg,
                                                                         action : async cancellationToken =>
            {
                // If this is the first process trying to install the target nupkg, go ahead
                // After this process successfully installs the package, all other processes
                // waiting on this lock don't need to install it again.
                if (!File.Exists(hashPath))
                {
                    logger.LogVerbose(
                        $"Acquired lock for the installation of {packageIdentity.Id} {packageIdentity.Version}");

                    logger.LogMinimal(string.Format(
                                          CultureInfo.CurrentCulture,
                                          Strings.Log_InstallingPackage,
                                          packageIdentity.Id,
                                          packageIdentity.Version));

                    cancellationToken.ThrowIfCancellationRequested();

                    // We do not stop the package extraction after this point
                    // based on CancellationToken, but things can still be stopped if the process is killed.
                    if (Directory.Exists(targetPath))
                    {
                        // If we had a broken restore, clean out the files first
                        var info = new DirectoryInfo(targetPath);

                        foreach (var file in info.GetFiles())
                        {
                            file.Delete();
                        }

                        foreach (var dir in info.GetDirectories())
                        {
                            dir.Delete(true);
                        }
                    }
                    else
                    {
                        Directory.CreateDirectory(targetPath);
                    }

                    var targetTempNupkg = Path.Combine(targetPath, Path.GetRandomFileName());
                    var tempHashPath = Path.Combine(targetPath, Path.GetRandomFileName());
                    var packageSaveMode = versionFolderPathContext.PackageSaveMode;

                    // Extract the nupkg
                    var copiedNupkg = await packageDownloader.CopyNupkgFileToAsync(targetTempNupkg, cancellationToken);

                    if (packageSaveMode.HasFlag(PackageSaveMode.Nuspec))
                    {
                        var nuspecFileNameFromReader = await packageDownloader.CoreReader.GetNuspecFileAsync(cancellationToken);
                        var packageFiles = new[] { nuspecFileNameFromReader };
                        var packageFileExtractor = new PackageFileExtractor(
                            packageFiles,
                            XmlDocFileSaveMode.None);
                        var packageDirectoryPath = Path.GetDirectoryName(targetNuspec);

                        var extractedNuspecFilePath = (await packageDownloader.CoreReader.CopyFilesAsync(
                                                           packageDirectoryPath,
                                                           packageFiles,
                                                           packageFileExtractor.ExtractPackageFile,
                                                           logger,
                                                           cancellationToken))
                                                      .SingleOrDefault();

                        // CopyFilesAsync(...) just extracts files to a directory.
                        // We may have to fix up the casing of the .nuspec file name.
                        if (!string.IsNullOrEmpty(extractedNuspecFilePath))
                        {
                            if (PathUtility.IsFileSystemCaseInsensitive)
                            {
                                var nuspecFileName = Path.GetFileName(targetNuspec);
                                var actualNuspecFileName = Path.GetFileName(extractedNuspecFilePath);

                                if (!string.Equals(nuspecFileName, actualNuspecFileName, StringComparison.Ordinal))
                                {
                                    var tempNuspecFilePath = Path.Combine(packageDirectoryPath, Path.GetRandomFileName());

                                    File.Move(extractedNuspecFilePath, tempNuspecFilePath);
                                    File.Move(tempNuspecFilePath, targetNuspec);
                                }
                            }
                            else if (!File.Exists(targetNuspec))
                            {
                                File.Move(extractedNuspecFilePath, targetNuspec);
                            }
                        }
                    }

                    if (packageSaveMode.HasFlag(PackageSaveMode.Files))
                    {
                        var hashFileName = Path.GetFileName(hashPath);
                        var packageFiles = (await packageDownloader.CoreReader.GetFilesAsync(cancellationToken))
                                           .Where(file => ShouldInclude(file, hashFileName));
                        var packageFileExtractor = new PackageFileExtractor(
                            packageFiles,
                            versionFolderPathContext.XmlDocFileSaveMode);
                        await packageDownloader.CoreReader.CopyFilesAsync(
                            targetPath,
                            packageFiles,
                            packageFileExtractor.ExtractPackageFile,
                            logger,
                            token);
                    }

                    var packageHash = await packageDownloader.GetPackageHashAsync("SHA512", cancellationToken);

                    File.WriteAllText(tempHashPath, packageHash);

                    // Now rename the tmp file
                    if (versionFolderPathContext.PackageSaveMode.HasFlag(PackageSaveMode.Nupkg))
                    {
                        if (copiedNupkg)
                        {
                            // Dispose of it now because it is holding a lock on the temporary .nupkg file.
                            packageDownloader.Dispose();

                            File.Move(targetTempNupkg, targetNupkg);
                        }
                    }
                    else
                    {
                        try
                        {
                            File.Delete(targetTempNupkg);
                        }
                        catch (IOException ex)
                        {
                            logger.LogWarning(string.Format(
                                                  CultureInfo.CurrentCulture,
                                                  Strings.ErrorUnableToDeleteFile,
                                                  targetTempNupkg,
                                                  ex.Message));
                        }
                    }

                    // Note: PackageRepository relies on the hash file being written out as the
                    // final operation as part of a package install to assume a package was fully installed.
                    // Rename the tmp hash file
                    File.Move(tempHashPath, hashPath);

                    logger.LogVerbose($"Completed installation of {packageIdentity.Id} {packageIdentity.Version}");
                    return true;
                }
                else
                {
                    logger.LogVerbose("Lock not required - Package already installed "
                                      + $"{packageIdentity.Id} {packageIdentity.Version}");
                    return false;
                }
            },
                                                                         token : token));
        }