Пример #1
0
        public IPackageMetadata ResolveSource(PackageRequestRef request)
        {
            string prefix, archiveType, packageType;

            if (request.Uri.StartsWith("local-lzma://"))
            {
                prefix      = "local-lzma://";
                archiveType = PackageManager.ARCHIVE_FORMAT_TAR_LZMA;
                packageType = PackageManager.PACKAGE_TYPE_LIBRARY;
            }
            else if (request.Uri.StartsWith("local-gzip://"))
            {
                prefix      = "local-gzip://";
                archiveType = PackageManager.ARCHIVE_FORMAT_TAR_GZIP;
                packageType = PackageManager.PACKAGE_TYPE_LIBRARY;
            }
            else if (request.Uri.StartsWith("local-tool-lzma://"))
            {
                prefix      = "local-tool-lzma://";
                archiveType = PackageManager.ARCHIVE_FORMAT_TAR_LZMA;
                packageType = PackageManager.PACKAGE_TYPE_GLOBAL_TOOL;
            }
            else if (request.Uri.StartsWith("local-tool-gzip://"))
            {
                prefix      = "local-tool-gzip://";
                archiveType = PackageManager.ARCHIVE_FORMAT_TAR_GZIP;
                packageType = PackageManager.PACKAGE_TYPE_GLOBAL_TOOL;
            }
            else
            {
                throw new InvalidOperationException("Unexpected prefix for local package URL " + request.Uri);
            }

            var localPackagePath = request.Uri.Substring(prefix.Length);

            if (!File.Exists(localPackagePath))
            {
                throw new InvalidOperationException("Unable to find local package file " + localPackagePath + " on disk.");
            }

            return(new ProtobuildPackageMetadata(
                       null,
                       packageType,
                       null,
                       request.Platform,
                       request.GitRef,
                       archiveType,
                       localPackagePath,
                       (metadata, folder, name, upgrade, source) =>
            {
                _binaryPackageResolve.Resolve(metadata, folder, name, upgrade);
            },
                       _binaryPackageResolve.GetProtobuildPackageBinary));
        }
 public IPackageMetadata ResolveSource(string workingDirectory, PackageRequestRef request)
 {
     return(new TransformedPackageMetadata(
                NormalizeScheme(request.Uri),
                PackageManager.PACKAGE_TYPE_LIBRARY,
                request.Platform,
                request.GitRef,
                _transformer,
                (workingDirectoryAlt, metadata, folder, name, upgrade, preferSource) =>
     {
         _binaryPackageResolve.Resolve(workingDirectoryAlt, metadata, folder, name, upgrade);
     },
                _binaryPackageResolve.GetProtobuildPackageBinary));
 }
        public IPackageMetadata ResolveSource(string workingDirectory, PackageRequestRef request)
        {
            var baseUri = new Uri(request.Uri);

            var     apiUri  = new Uri(baseUri.ToString().TrimEnd('/') + "/api");
            dynamic apiData = null;

            var performOnlineLookup = true;

            if (!request.ForceUpgrade && request.IsStaticReference)
            {
                performOnlineLookup = false;
                if (_packageRequestCache.IsCached(request.Uri))
                {
                    try
                    {
                        apiData = _packageRequestCache.GetCachedJsonObject(request.Uri);
                    }
                    catch (ExecEnvironment.SelfInvokeExitException)
                    {
                        throw;
                    }
                    catch
                    {
                        performOnlineLookup = true;
                    }
                }
                else
                {
                    performOnlineLookup = true;
                }
            }

            if (performOnlineLookup)
            {
                try
                {
                    string jsonString;
                    apiData = this.GetJSON(apiUri, out jsonString);
                    if (apiData.has_error)
                    {
                        throw new InvalidOperationException((string)apiData.error);
                    }
                    try
                    {
                        _packageRequestCache.StoreCachedData(request.Uri, jsonString);
                    }
                    catch (IOException)
                    {
                        RedirectableConsole.WriteLine("WARNING: Unable to save cached result of request.");
                    }
                }
                catch (Exception)
                {
                    // Attempt to retrieve it from the lookup cache.
                    if (_packageRequestCache.IsCached(request.Uri))
                    {
                        var shouldThrow = false;
                        try
                        {
                            apiData = _packageRequestCache.GetCachedJsonObject(request.Uri);
                        }
                        catch (ExecEnvironment.SelfInvokeExitException)
                        {
                            throw;
                        }
                        catch
                        {
                            shouldThrow = true;
                        }
                        if (shouldThrow)
                        {
                            throw;
                        }
                    }
                    else
                    {
                        throw;
                    }
                }
            }

            if (apiData == null)
            {
                throw new InvalidOperationException("apiData is null");
            }

            var sourceUri = (string)apiData.result.package.gitUrl;
            var type      = (string)apiData.result.package.type;

            if (!string.IsNullOrWhiteSpace(sourceUri))
            {
                try
                {
                    new Uri(sourceUri);
                }
                catch (ExecEnvironment.SelfInvokeExitException)
                {
                    throw;
                }
                catch
                {
                    throw new InvalidOperationException(
                              "Received invalid Git URL when loading package from " + apiUri);
                }
            }
            else
            {
                RedirectableConsole.WriteLine("WARNING: This package does not have a source repository set.");
            }

            var downloadMap    = new Dictionary <string, string>();
            var archiveTypeMap = new Dictionary <string, string>();
            var resolvedHash   = new Dictionary <string, string>();

            foreach (var ver in apiData.result.versions)
            {
                if (ver.platformName != request.Platform)
                {
                    continue;
                }
                if (!downloadMap.ContainsKey(ver.versionName))
                {
                    downloadMap.Add(ver.versionName, ver.downloadUrl);
                    archiveTypeMap.Add(ver.versionName, ver.archiveType);
                    resolvedHash.Add(ver.versionName, ver.versionName);
                }
            }
            foreach (var branch in apiData.result.branches)
            {
                if (!downloadMap.ContainsKey(branch.versionName))
                {
                    continue;
                }
                if (!downloadMap.ContainsKey(branch.branchName))
                {
                    downloadMap.Add(branch.branchName, downloadMap[branch.versionName]);
                    archiveTypeMap.Add(branch.branchName, archiveTypeMap[branch.versionName]);
                    resolvedHash.Add(branch.branchName, branch.versionName);
                }
            }

            // Resolve Git reference to Git commit hash.
            var gitCommit = resolvedHash.ContainsKey(request.GitRef) ? resolvedHash[request.GitRef] : request.GitRef;

            if (string.IsNullOrWhiteSpace(sourceUri))
            {
                // Normalize source URI value.
                sourceUri = null;
            }

            string fileUri, archiveType;

            if (!downloadMap.ContainsKey(gitCommit))
            {
                if (string.IsNullOrWhiteSpace(sourceUri))
                {
                    throw new InvalidOperationException("Unable to resolve binary package for version \"" +
                                                        request.GitRef + "\" and platform \"" + request.Platform +
                                                        "\" and this package does not have a source repository");
                }
                else
                {
                    RedirectableConsole.WriteLine("Unable to resolve binary package for version \"" + request.GitRef +
                                                  "\" and platform \"" + request.Platform + "\", falling back to source version");
                    fileUri     = null;
                    archiveType = null;
                }
            }
            else
            {
                fileUri     = downloadMap[gitCommit];
                archiveType = archiveTypeMap[gitCommit];
            }

            return(new ProtobuildPackageMetadata(
                       request.Uri,
                       type,
                       sourceUri,
                       request.Platform,
                       gitCommit,
                       archiveType,
                       fileUri,
                       (workingDirectoryAlt, metadata, folder, name, upgrade, source) =>
            {
                if (source == true)
                {
                    _sourcePackageResolve.Resolve(workingDirectoryAlt, metadata, folder, name, upgrade);
                }
                else
                {
                    _binaryPackageResolve.Resolve(workingDirectoryAlt, metadata, folder, name, upgrade);
                }
            },
                       _binaryPackageResolve.GetProtobuildPackageBinary));
        }
Пример #4
0
        public IPackageMetadata ResolveSource(string workingDirectory, PackageRequestRef request)
        {
            var path = request.Uri.Substring("local-nuget-v3://".Length);

            string packageName;
            string packageType;
            string sourceCodeUrl;
            string version;
            string binaryFormat;
            string binaryUri;
            string commitHashForSourceResolve;

            // Figure out the package type by looking at the tags inside
            // the package's .nuspec file.
            using (var storer = ZipStorer.Open(path, FileAccess.Read))
            {
                var entries = storer.ReadCentralDir();

                var nuspecEntries = entries.Where(
                    x => x.FilenameInZip.EndsWith(".nuspec") &&
                    !x.FilenameInZip.Contains("/") &&
                    !x.FilenameInZip.Contains("\\")).ToList();

                if (nuspecEntries.Count != 0)
                {
                    using (var stream = new MemoryStream())
                    {
                        storer.ExtractFile(nuspecEntries[0], stream);
                        stream.Seek(0, SeekOrigin.Begin);

                        var document = new XmlDocument();
                        document.Load(stream);

                        var ns = new XmlNamespaceManager(document.NameTable);
                        ns.AddNamespace("x", "http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd");

                        packageName = document.SelectSingleNode("//x:id", ns)?.InnerText;
                        version     = document.SelectSingleNode("//x:version", ns)?.InnerText;
                        var tags = document.SelectSingleNode("//x:tags", ns)?.InnerText?.Split(new[] { ' ' }) ?? new string[0];

                        packageType = PackageManager.PACKAGE_TYPE_LIBRARY;
                        commitHashForSourceResolve = null;
                        sourceCodeUrl = null;

                        foreach (var tag in tags)
                        {
                            if (!string.IsNullOrWhiteSpace(tag))
                            {
                                if (tag == "type=global-tool")
                                {
                                    packageType = PackageManager.PACKAGE_TYPE_GLOBAL_TOOL;
                                }
                                else if (tag == "type=template")
                                {
                                    packageType = PackageManager.PACKAGE_TYPE_TEMPLATE;
                                }

                                if (tag.StartsWith("commit="))
                                {
                                    commitHashForSourceResolve = tag.Substring("commit=".Length);
                                }

                                if (tag.StartsWith("git="))
                                {
                                    sourceCodeUrl = tag.Substring("git=".Length);
                                }
                            }
                        }

                        binaryUri    = path;
                        binaryFormat = PackageManager.ARCHIVE_FORMAT_NUGET_ZIP;
                    }
                }
                else
                {
                    throw new InvalidOperationException("NuGet package is missing nuspec file!");
                }
            }

            return(new NuGet3PackageMetadata(
                       null,
                       packageName,
                       packageType,
                       sourceCodeUrl,
                       request.Platform,
                       version,
                       binaryFormat,
                       binaryUri,
                       commitHashForSourceResolve,
                       (workingDirectoryAlt, metadata, folder, name, upgrade, source) =>
            {
                if (source == true)
                {
                    _sourcePackageResolve.Resolve(workingDirectoryAlt, metadata, folder, name, upgrade);
                }
                else
                {
                    _binaryPackageResolve.Resolve(workingDirectoryAlt, metadata, folder, name, upgrade);
                }
            }));
        }
Пример #5
0
        public IPackageMetadata ResolveSource(string workingDirectory, PackageRequestRef request)
        {
            var components = request.Uri.Split(new[] { '|' }, 2);

            var repository  = NormalizeScheme(components[0]);
            var packageName = components[1];
            var version     = request.GitRef;

            var semVerRegex  = new Regex("^[0-9]\\.[0-9]\\.[0-9](\\-.*)?$");
            var gitHashRegex = new Regex("^[0-9a-fA-F]{40}$");

            var shouldQuerySourceRepository = false;

            if (semVerRegex.IsMatch(version))
            {
                // This is a semantic version; leave as-is.
            }
            else if (gitHashRegex.IsMatch(version))
            {
                // This is a Git hash, convert it to NuGet's semantic version.
                version = NuGetVersionHelper.CreateNuGetPackageVersion(version, request.Platform);
            }
            else
            {
                // This is a branch, or other kind of source reference.  We need
                // to query the source repository to resolve it to a Git hash.
                shouldQuerySourceRepository = true;
            }

            string serviceJson;
            var    serviceIndex = _packageRequestCache.TryGetOptionallyCachedJsonObject(
                repository,
                !request.ForceUpgrade,
                out serviceJson,
                id => GetData(new Uri(id)));

            string registrationsBaseUrl = null;

            foreach (var entry in serviceIndex.resources)
            {
                var entryType = (string)entry["@type"];
                if (entryType == "RegistrationsBaseUrl")
                {
                    registrationsBaseUrl = (string)entry["@id"];
                }
            }

            if (registrationsBaseUrl == null)
            {
                throw new InvalidOperationException("Unable to locate RegistrationsBaseUrl service.");
            }

            var packageMetadataUrl =
                $"{registrationsBaseUrl.TrimEnd(new[] {'/'})}/{packageName.ToLowerInvariant()}/index.json";

            string packageMetadataJson;
            var    packageMetadata = _packageRequestCache.TryGetOptionallyCachedJsonObject(
                packageMetadataUrl,
                !request.ForceUpgrade && request.IsStaticReference,
                out packageMetadataJson,
                id => GetData(new Uri(id)));

            string latestPackageVersion = null;

            foreach (var item in packageMetadata.items)
            {
                if (latestPackageVersion == null || string.CompareOrdinal((string)item.upper, latestPackageVersion) > 0)
                {
                    latestPackageVersion = item.upper;
                }
            }

            var packagesByVersionLock = new object();
            var packagesByVersion     = new Dictionary <string, dynamic>();

            if (_hostPlatformDetector.DetectPlatform() == "Windows")
            {
                // Do this in parallel as we may need to make multiple HTTPS requests.
                Parallel.ForEach((IEnumerable <object>)packageMetadata.items,
                                 item => PopulatePackagesByVersion(packagesByVersionLock, packagesByVersion, (dynamic)item, request));
            }
            else
            {
                // Parallelisation is not safe on this platform, do it sequentually.
                foreach (var item in packageMetadata.items)
                {
                    PopulatePackagesByVersion(packagesByVersionLock, packagesByVersion, item, request);
                }
            }

            string packageType = PackageManager.PACKAGE_TYPE_LIBRARY;

            string sourceCodeUrl = null;
            string commitHashForSourceResolve = null;

            if (shouldQuerySourceRepository)
            {
                var lookupVersion = latestPackageVersion;
                if (!packagesByVersion.ContainsKey(lookupVersion) && packagesByVersion.ContainsKey(lookupVersion + "+git.unspecified"))
                {
                    lookupVersion += "+git.unspecified";
                }
                sourceCodeUrl = ExtractSourceRepository(packagesByVersion[lookupVersion]);
                packageType   = ExtractPackageType(packagesByVersion[lookupVersion]);

                if (!string.IsNullOrWhiteSpace(sourceCodeUrl))
                {
                    if (sourceCodeUrl.StartsWith("git="))
                    {
                        sourceCodeUrl = sourceCodeUrl.Substring("git=".Length);

                        var performGitLsRemote = true;
                        if (sourceCodeUrl.StartsWith("https://github.com/"))
                        {
                            try
                            {
                                // This is a GitHub repository.  During the installation of Protobuild Manager (hosted on GitHub), we need
                                // to resolve the latest version on NuGet, but we can't do this because Git may not be in the PATH or may
                                // not be available.  We still want developers to be able to install Protobuild Manager without Git on
                                // their PATH (as they may have dedicated shells to use it), so we attempt to use the GitHub API to resolve
                                // the commit hash first.
                                string gitHubJsonInfo;
                                var    gitHubComponents =
                                    sourceCodeUrl.Substring("https://github.com/".Length).Split('/');
                                var gitHubOwner  = gitHubComponents[0];
                                var gitHubRepo   = gitHubComponents[1];
                                var gitHubApiUrl = "https://api.github.com/repos/" + gitHubOwner +
                                                   "/" +
                                                   gitHubRepo + "/branches/" + version;
                                var gitHubJson = _packageRequestCache.TryGetOptionallyCachedJsonObject(
                                    gitHubApiUrl,
                                    !request.ForceUpgrade && request.IsStaticReference,
                                    out packageMetadataJson,
                                    id =>
                                {
                                    using (var client = new RetryableWebClient())
                                    {
                                        client.SilentOnError = true;
                                        client.SetHeader("User-Agent", "Protobuild NuGet Lookup/v1.0");
                                        client.SetHeader("Accept", "application/vnd.github.v3+json");

                                        return(client.DownloadString(gitHubApiUrl));
                                    }
                                });
                                var commitHash = gitHubJson.commit.sha;

                                if (!string.IsNullOrWhiteSpace(commitHash))
                                {
                                    // This is a match and we've found our Git hash to use.
                                    version =
                                        NuGetVersionHelper.CreateNuGetPackageVersion(commitHash.Trim(),
                                                                                     request.Platform);
                                    commitHashForSourceResolve = commitHash.Trim();
                                    performGitLsRemote         = false;
                                }
                            }
                            catch (Exception)
                            {
                                Console.WriteLine("NOTICE: Unable to lookup version information via GitHub API; falling back to 'git ls-remote'");
                            }
                        }

                        if (performGitLsRemote)
                        {
                            var heads = _packageRequestCache.TryGetOptionallyCachedData(
                                "git:" + sourceCodeUrl,
                                !request.ForceUpgrade && request.IsStaticReference,
                                id => GitUtils.RunGitAndCapture(
                                    workingDirectory,
                                    null,
                                    "ls-remote --heads " + new Uri(sourceCodeUrl)));

                            var lines = heads.Split(new string[] { "\r\n", "\n", "\r" },
                                                    StringSplitOptions.RemoveEmptyEntries);

                            foreach (var line in lines)
                            {
                                var sourceEntryComponents = line.Split('\t');
                                if (sourceEntryComponents.Length >= 2)
                                {
                                    var branchName = sourceEntryComponents[1].Trim();

                                    if (branchName.StartsWith("refs/heads/"))
                                    {
                                        branchName = branchName.Substring("refs/heads/".Length);
                                    }
                                    else
                                    {
                                        continue;
                                    }

                                    if (string.Equals(version, branchName, StringComparison.InvariantCulture))
                                    {
                                        // This is a match and we've found our Git hash to use.
                                        version =
                                            NuGetVersionHelper.CreateNuGetPackageVersion(
                                                sourceEntryComponents[0].Trim(),
                                                request.Platform);
                                        commitHashForSourceResolve = sourceEntryComponents[0].Trim();
                                        break;
                                    }
                                }
                            }
                        }
                    }
                    else
                    {
                        throw new InvalidOperationException("Unknown source code repository type '" + sourceCodeUrl + "'");
                    }

                    // If we fall out of this loop, we'll hit the next if statement and most likely fail.
                }
            }

            string binaryUri    = null;
            string binaryFormat = null;

            if (!packagesByVersion.ContainsKey(version))
            {
                if (string.IsNullOrWhiteSpace(sourceCodeUrl))
                {
                    throw new InvalidOperationException(
                              "Unable to resolve binary package for version \"" +
                              version + "\" and platform \"" + request.Platform +
                              "\" and this package does not have a source repository");
                }
                else
                {
                    RedirectableConsole.WriteLine("Unable to resolve binary package for version \"" + version +
                                                  "\" and platform \"" + request.Platform + "\", falling back to source version");
                }
            }
            else
            {
                sourceCodeUrl = ExtractSourceRepository(packagesByVersion[version]);
                packageType   = ExtractPackageType(packagesByVersion[version]);

                if (!string.IsNullOrWhiteSpace(sourceCodeUrl))
                {
                    if (sourceCodeUrl.StartsWith("git="))
                    {
                        sourceCodeUrl = sourceCodeUrl.Substring("git=".Length);
                    }
                    else
                    {
                        throw new InvalidOperationException("Unknown source code repository type '" + sourceCodeUrl + "'");
                    }
                }

                if (commitHashForSourceResolve == null)
                {
                    commitHashForSourceResolve = ExtractCommitHash(packagesByVersion[version]);
                }

                // packageContent may not be under catalogEntry; our best guess is that NuGet
                // moved it from the root to catalogEntry at some point in the past, but not
                // all of the registrations are updated with it under catalogEntry, e.g. RestSharp
                try
                {
                    binaryUri = packagesByVersion[version].catalogEntry.packageContent;
                }
                catch
                {
                    binaryUri = packagesByVersion[version].packageContent;
                }

                binaryFormat = PackageManager.ARCHIVE_FORMAT_NUGET_ZIP;
            }

            return(new NuGet3PackageMetadata(
                       repository,
                       packageName,
                       packageType,
                       sourceCodeUrl,
                       request.Platform,
                       version,
                       binaryFormat,
                       binaryUri,
                       commitHashForSourceResolve,
                       (workingDirectoryAlt, metadata, folder, name, upgrade, source) =>
            {
                if (source == true)
                {
                    _sourcePackageResolve.Resolve(workingDirectoryAlt, metadata, folder, name, upgrade);
                }
                else
                {
                    _binaryPackageResolve.Resolve(workingDirectoryAlt, metadata, folder, name, upgrade);
                }
            }));
        }