Пример #1
0
        /// <summary>
        /// Whether the driver support the specified uri.
        /// </summary>
        /// <param name="io">The input/output instance.</param>
        /// <param name="config">The config instance.</param>
        /// <param name="uri">Thr specified uri.</param>
        /// <param name="deep">Whether is deep checker.</param>
        /// <returns>True if the driver is supported.</returns>
        public static bool IsSupport(IIO io, Config config, string uri, bool deep = false)
        {
            if (Regex.IsMatch(uri, @"(^git://|\.git/?$|git(?:olite)?@|//git\.|//github\.com/|//gitlib\.com/)", RegexOptions.IgnoreCase))
            {
                return(true);
            }

            bool ProcessCheck(string command, string cwd)
            {
                var process = new BucketProcessExecutor(io);

                return(process.Execute(command, cwd) == 0);
            }

            if (FileSystemLocal.IsLocalPath(uri))
            {
                uri = FileSystemLocal.GetPlatformPath(uri);
                if (!Directory.Exists(uri))
                {
                    return(false);
                }

                return(ProcessCheck("git tag", uri));
            }

            if (!deep)
            {
                return(false);
            }

            return(ProcessCheck($"git ls-remote --heads {ProcessExecutor.Escape(uri)}", null));
        }
Пример #2
0
        /// <summary>
        /// Fetch the specified reference or synchronize the git repository.
        /// </summary>
        /// <param name="uri">The specified git repository. can use ssh.</param>
        /// <param name="reference">The specified git reference.</param>
        /// <param name="path">The specified saved path. this path will be based on the file system.</param>
        /// <returns>True if fetched. false is sync mirror.</returns>
        public bool FetchReferenceOrSyncMirror(string uri, string reference, string path = null)
        {
            var parsedPath = string.IsNullOrEmpty(path) ? GetRepositoryNameFromUri(uri) : path;

            if (fileSystem is IReportPath report)
            {
                parsedPath = report.ApplyRootPath(parsedPath);
            }

            if (string.IsNullOrEmpty(parsedPath))
            {
                throw new FileSystemException("Clone path is invalid, cannot be empty.");
            }

            if (fileSystem.Exists(parsedPath, FileSystemOptions.Directory) &&
                process.Execute("git rev-parse --git-dir", out string output, parsedPath) == 0 &&
                output.Trim() == ".")
            {
                var escapedReference = ProcessExecutor.Escape($"{reference}^{{commit}}");
                if (process.Execute($"git rev-parse --quiet --verify {escapedReference}", parsedPath) == 0)
                {
                    return(true);
                }
            }

            SyncMirror(uri, path);

            return(false);
        }
Пример #3
0
        /// <summary>
        /// Generate compatibility files under unix.
        /// </summary>
        /// <returns>Returns the compatible content.</returns>
        protected virtual string GenerateUnixProxyCode(string binPath, string link)
        {
            binPath = BaseFileSystem.GetRelativePath(link, binPath);
            var binDirPath = ProcessExecutor.Escape(Path.GetDirectoryName(binPath));
            var binFile = Path.GetFileName(binPath);

            var proxyCode =
@"#!/usr/bin/env sh

dir=$(cd ""${0%[/\\]*}"" > /dev/null; cd %binDirPath% && pwd)

if [ -d /proc/cygdrive ]; then
    case $(which dotnet) in
        $(readlink -n /proc/cygdrive)/*)
            dir=$(cygpath -m ""$dir"");
            ;;
    esac
fi

""${dir}/%binFile%"" ""$@""
";

            proxyCode = proxyCode.Replace("%binDirPath%", binDirPath);
            proxyCode = proxyCode.Replace("%binFile%", binFile);
            proxyCode = proxyCode.Replace("\r\n", "\n");

            return proxyCode;
        }
Пример #4
0
        /// <summary>
        /// Generate compatibility files under windows.
        /// </summary>
        /// <param name="binPath">The package's bin file.</param>
        /// <param name="link">The link file.</param>
        /// <returns>Returns the compatible content.</returns>
        protected virtual string GenerateWindowsProxyCode(string binPath, string link)
        {
            var caller = DetermineBinaryCaller(binPath, fileSystem);
            binPath = BaseFileSystem.GetRelativePath(link, binPath);

            return "@ECHO OFF\r\n" +
            "setlocal DISABLEDELAYEDEXPANSION\r\n" +
            $"SET BIN_TARGET=%~dp0/{ProcessExecutor.Escape(binPath).Trim('\'', '"')}\r\n" +
            $"{caller} \"%BIN_TARGET%\" %*\r\n";
        }
Пример #5
0
        /// <inheritdoc />
        protected override string GetCommitLogs(string fromReference, string toReference, string cwd)
        {
            cwd = NormalizePath(cwd);
            var escapedFrom = ProcessExecutor.Escape(fromReference);
            var escapedTo   = ProcessExecutor.Escape(toReference);

            var command = $"git log {escapedFrom}..{escapedTo} --pretty=format:\"%h - %an: %s\"";

            if (Process.Execute(command, out string stdout, out string stderr, cwd) != 0)
            {
                throw new RuntimeException($"Failed to execute {command}{Environment.NewLine}{Environment.NewLine}{stderr}");
            }

            return(stdout.Trim());
        }
Пример #6
0
        /// <summary>
        /// Synchronize the specified git repository to the specified directory.
        /// </summary>
        /// <param name="uri">The specified git repository. can use ssh.</param>
        /// <param name="path">The specified saved path. this path will be based on the file system.</param>
        /// <returns>True if successful to sync mirror.</returns>
        public bool SyncMirror(string uri, string path = null)
        {
            path = string.IsNullOrEmpty(path) ? GetRepositoryNameFromUri(uri) : path;

            if (fileSystem is IReportPath report)
            {
                path = report.ApplyRootPath(path);
            }

            if (string.IsNullOrEmpty(path))
            {
                throw new FileSystemException("Clone path is invalid, cannot be empty.");
            }

            if (fileSystem.Exists(path, FileSystemOptions.Directory) &&
                process.Execute("git rev-parse --git-dir", out string output, path) == 0 &&
                output.Trim() == ".")
            {
                string Upgrate(string protoUri)
                {
                    return($"git remote set-url origin {protoUri} && git remote update --prune origin");
                }

                try
                {
                    ExecuteCommand(Upgrate, uri, path);
                    return(true);
                }
#pragma warning disable CA1031
                catch (System.Exception)
#pragma warning restore CA1031
                {
                    return(false);
                }
            }

            fileSystem.Delete(path);

            string Cloneable(string protoUri)
            {
                return($"git clone --mirror {ProcessExecutor.Escape(protoUri)} {ProcessExecutor.Escape(path)}");
            }

            ExecuteCommand(Cloneable, uri, path, true);

            return(true);
        }
Пример #7
0
        /// <inheritdoc />
        protected override void DoUpdate(IPackage initial, IPackage target, string cwd, string uri)
        {
            Git.CleanEnvironment();
            cwd = NormalizePath(cwd);

            if (!HasMetadataRepository(cwd))
            {
                throw new RuntimeException($"The .git directory is missing from \"{cwd}\"");
            }

            var updateOriginUri = false;

            if (Process.Execute("git remote -v", out string stdout, cwd) == 0)
            {
                var originMatch = Regex.Match(stdout, @"^origin\s+(?<uri>\S+?)\r?", RegexOptions.Multiline);
                var bucketMatch = Regex.Match(stdout, @"^bucket\s+(?<uri>\S+?)\r?", RegexOptions.Multiline);

                if (originMatch.Success && bucketMatch.Success &&
                    originMatch.Groups["uri"].Value == bucketMatch.Groups["uri"].Value &&
                    bucketMatch.Groups["uri"].Value != target.GetSourceUri())
                {
                    updateOriginUri = true;
                }
            }

            var reference = target.GetSourceReference();

            IO.WriteError($" Checking out {GetShortHash(reference)}");

            string CommandCallable(string authUri)
            {
                var escapedAuthUri   = ProcessExecutor.Escape(authUri);
                var escapedReference = ProcessExecutor.Escape($"{reference}^{{commit}}");

                return($"git remote set-url bucket {escapedAuthUri} && git rev-parse --quiet --verify {escapedReference} || (git fetch bucket && git fetch --tags bucket)");
            }

            git.ExecuteCommand(CommandCallable, uri, cwd);

            ExecuteUpdate(cwd, reference, target);

            if (updateOriginUri)
            {
                UpdateOriginUri(target.GetSourceUri(), cwd);
            }
        }
Пример #8
0
        /// <summary>
        /// Set the origin push uri.
        /// </summary>
        protected virtual void SetPushUri(string uri, string cwd)
        {
            // set push url for github projects.
            var match = Regex.Match(uri, $"^(?:https?|git)://{Git.GetGithubDomainsRegex(Config)}/(?<username>[^/]+)/(?<repository>[^/]+?)(?:\\.git)?$");

            if (!match.Success)
            {
                return;
            }

            string[] protocols = Config.Get(Settings.GithubProtocols);
            var      pushUri   = $"git@{match.Groups["domain"].Value}:{match.Groups["username"].Value}/{match.Groups["repository"].Value}.git";

            if (!Array.Exists(protocols, protocol => protocol == "ssh"))
            {
                pushUri = $"https://{match.Groups["domain"].Value}/{match.Groups["username"].Value}/{match.Groups["repository"].Value}.git";
            }

            Process.Execute($"git remote set-url --push origin {ProcessExecutor.Escape(pushUri)}", cwd);
        }
Пример #9
0
        private DateTime?GetPackageTimeFromSource(IPackage package)
        {
            var      installedPath = Path.Combine(Environment.CurrentDirectory, installationManager.GetInstalledPath(package));
            var      sourceType    = package.GetSourceType();
            DateTime?ret           = null;

            if (string.IsNullOrEmpty(installedPath) || !Array.Exists(new[] { "git" }, (item) => item == sourceType))
            {
                return(null);
            }

            var sourceReference = package.GetSourceReference() ?? package.GetDistReference();

            DateTime?GetGitDateTime()
            {
                Git.CleanEnvironment();
                if (process.Execute(
                        $"git log -n1 --pretty=%aD {ProcessExecutor.Escape(sourceReference)}",
                        out string output, installedPath) == 0 &&
                    DateTime.TryParse(output.Trim(), out DateTime dateTime))
                {
                    return(dateTime);
                }

                return(null);
            }

            switch (sourceReference)
            {
            case "git":
                ret = GetGitDateTime();
                break;

            default:
                break;
            }

            return(ret);
        }
Пример #10
0
        /// <summary>
        /// extract <paramref name="file"/> to <paramref name="extractPath"/> with unzip command.
        /// </summary>
        protected internal virtual void ExtractWithUnzipCommand(string file, string extractPath, bool isFallback = false)
        {
            // When called after a ZipArchive failed, perhaps
            // there is some files to overwrite
            var overwrite = isFallback ? "-o " : string.Empty;
            var command   = $"unzip -qq {overwrite}{ProcessExecutor.Escape(file)} -d {ProcessExecutor.Escape(extractPath)}";

            FileSystemLocal.EnsureDirectory(extractPath);

            SException processException;

            try
            {
                if (process.Execute(command, out _, out string stderr) == 0)
                {
                    return;
                }

                throw new RuntimeException(
                          $"Failed to execute {command} {Environment.NewLine}{Environment.NewLine} {stderr}");
            }
#pragma warning disable CA1031
            catch (SException ex)
#pragma warning restore CA1031
            {
                processException = ex;
            }

            if (isFallback)
            {
                ExceptionDispatchInfo.Capture(processException).Throw();
            }

            io.WriteError($"    {processException.Message}");
            io.WriteError($"    The archive may contain identical file names with different capitalization (which fails on case insensitive filesystems)");
            io.WriteError($"    Unzip with unzip command failed, falling back to {nameof(ZipArchive)}.");

            ExtractWithZipArchive(file, extractPath, true);
        }
Пример #11
0
        /// <inheritdoc />
        public override void Delete(string path = null)
        {
            AssertReadonly();

            var location = ApplyRootPath(path);

            void DeleteFile(string fileLocation)
            {
                if (!File.Exists(fileLocation))
                {
                    return;
                }

                File.SetAttributes(fileLocation, FileAttributes.Normal);
                File.Delete(fileLocation);
            }

            void DeleteDirectory(string directoryLocation)
            {
                if (!Directory.Exists(directoryLocation))
                {
                    return;
                }

                var files       = Directory.GetFiles(directoryLocation);
                var directories = Directory.GetDirectories(directoryLocation);

                Array.ForEach(files, DeleteFile);
                Array.ForEach(directories, DeleteDirectory);
                Directory.Delete(directoryLocation, false);
            }

            try
            {
                DeleteFile(location);
                DeleteDirectory(location);
            }
#pragma warning disable CA1031
            catch (SException)
#pragma warning restore CA1031
            {
                // Retry after a bit on windows since it tends
                // to be touchy with mass removals.
                if (Platform.IsWindows)
                {
                    Thread.Sleep(350);
                }

                try
                {
                    DeleteFile(location);
                    DeleteDirectory(location);
                }
                catch (SException)
                {
                    string command;
                    if (Platform.IsWindows)
                    {
                        command = $"rmdir /S /Q {ProcessExecutor.Escape(location)}";
                    }
                    else
                    {
                        command = $"rm -rf {ProcessExecutor.Escape(location)}";
                    }

                    if (process.Execute(command) != 0)
                    {
                        throw;
                    }
                }
            }
        }
Пример #12
0
 /// <summary>
 /// Update the origin uri.
 /// </summary>
 protected virtual void UpdateOriginUri(string uri, string cwd)
 {
     Process.Execute($"git remote set-url origin {ProcessExecutor.Escape(uri)}", cwd);
     SetPushUri(uri, cwd);
 }
Пример #13
0
        /// <summary>
        /// Updates the given path to the given commit ref.
        /// </summary>
        /// <param name="cwd">The given path.</param>
        /// <param name="reference">Checkout to specified reference(commit ref, tag, branch).</param>
        /// <param name="branch">The name of the branch to use when checking out.</param>
        /// <returns>If a string is returned, it is the commit reference that was checked out if the original could not be found.</returns>
        protected virtual string UpdateToCommit(string cwd, string reference, string branch, DateTime?releaseDate)
        {
            Process.Execute("git branch -r", out string branches, cwd);

            bool IsGitHash(string hash)
            {
                return(Regex.IsMatch(hash, "^[a-f0-9]{40}$"));
            }

            bool IsBranchesHasRemote(string remote)
            {
                return(Regex.IsMatch(branches, $"^\\s+bucket/{Regex.Escape(remote)}\r?$", RegexOptions.Multiline));
            }

            string command;
            var    force = hasDiscardedChanges || hasStashedChanges ? "-f " : string.Empty;

            branch = Regex.Replace(branch ?? string.Empty, @"(?:^dev-|(?:\.x)?-dev$)", string.Empty, RegexOptions.IgnoreCase);

            // check whether non-commitish are branches or tags, and fetch branches
            // with the remote name. Use branch(in the context may be the version)
            // as the new branch name.
            if (!IsGitHash(reference) &&
                !string.IsNullOrEmpty(branches) &&
                IsBranchesHasRemote(reference))
            {
                var escapedBranch    = ProcessExecutor.Escape(branch);
                var escapedReference = ProcessExecutor.Escape($"bucket/{reference}");
                command = $"git checkout {force}-B {escapedBranch} {escapedReference} -- && git reset --hard {escapedReference} --";
                if (Process.Execute(command, cwd) == 0)
                {
                    return(null);
                }
            }

            // try to checkout branch by name and then reset it so it's on the proper branch name.
            if (IsGitHash(reference))
            {
                // add 'v' in front of the branch if it was stripped when generating the pretty name.
                if (!IsBranchesHasRemote(branch) && IsBranchesHasRemote($"v{branch}"))
                {
                    branch = $"v{branch}";
                }

                var escapedBranch = ProcessExecutor.Escape(branch);
                command = $"git checkout {escapedBranch} --";
                var fallbackCommand = $"git checkout {force}-B {escapedBranch} {ProcessExecutor.Escape($"bucket/{branch}")} --";
                if (Process.Execute(command, cwd) == 0 || Process.Execute(fallbackCommand, cwd) == 0)
                {
                    command = $"git reset --hard {ProcessExecutor.Escape(reference)} --";
                    if (Process.Execute(command, cwd) == 0)
                    {
                        return(null);
                    }
                }
            }

            // This uses the "--" sequence to separate branch from file parameters.
            //
            // Otherwise git tries the branch name as well as file name.
            // If the non-existent branch is actually the name of a file, the file
            // is checked out.
            var escapedGitReference = ProcessExecutor.Escape(reference);

            command = $"git checkout {force}{escapedGitReference} -- && git reset --hard {escapedGitReference} --";
            if (Process.Execute(command, out _, out string stderr, cwd) == 0)
            {
                return(null);
            }

            // reference was not found (prints "fatal: reference is not a tree: $ref").
            if (stderr.Contains(reference))
            {
                IO.WriteError($"    <warning>{reference} is gone (history was rewritten?)</warning>");
            }

            stderr = BucketProcessExecutor.FilterSensitive($"Failed to execute \"{command}\" {Environment.NewLine}{Environment.NewLine}{stderr}");
            throw new RuntimeException(stderr);
        }
Пример #14
0
        /// <inheritdoc />
        protected override void DoInstall(IPackage package, string cwd, string uri)
        {
            Git.CleanEnvironment();
            cwd = NormalizePath(cwd);
            var cachePath = Config.Get(Settings.CacheVcsDir) + $"/{CacheFileSystem.FormatCacheFolder(uri)}/";
            var reference = package.GetSourceReference();
            var flag      = Platform.IsWindows ? "/D " : string.Empty;

            // --dissociate option is only available since git 2.3.0-rc0
            var gitVersion = git.GetVersion();
            var message    = $"Cloning {GetShortHash(reference)}";

            var command = $"git clone --no-checkout %uri% %path% && cd {flag}%path% && git remote add bucket %uri% && git fetch bucket";

            if (!string.IsNullOrEmpty(gitVersion) &&
                Comparator.GreaterThanOrEqual(gitVersion, "2.3.0-rc0") &&
                CacheFileSystem.IsUsable(cachePath))
            {
                IO.WriteError(string.Empty, true, Verbosities.Debug);
                IO.WriteError($"    Cloning to cache at {ProcessExecutor.Escape(cachePath)}", true, Verbosities.Debug);

                try
                {
                    git.FetchReferenceOrSyncMirror(uri, reference, cachePath);
                    if (FileSystem.Exists(cachePath, FileSystemOptions.Directory))
                    {
                        command = "git clone --no-checkout %cachePath% %path% --dissociate --reference %cachePath%"
                                  + $" && cd {flag}%path%"
                                  + " && git remote set-url origin %uri% && git remote add bucket %uri%";
                        message = $"Cloning {GetShortHash(reference)} from cache.";
                    }
                }
                catch (RuntimeException)
                {
                    // ignore runtime exception because this is an optimization solution.
                }
            }

            IO.WriteError(message);

            string CommandCallable(string authUri)
            {
                var template = command;

                template = template.Replace("%uri%", ProcessExecutor.Escape(authUri));
                template = template.Replace("%path%", ProcessExecutor.Escape(cwd));
                template = template.Replace("%cachePath%", ProcessExecutor.Escape(cachePath));
                return(template);
            }

            git.ExecuteCommand(CommandCallable, uri, cwd, true);

            if (uri != package.GetSourceUri())
            {
                UpdateOriginUri(package.GetSourceUri(), cwd);
            }
            else
            {
                SetPushUri(uri, cwd);
            }

            ExecuteUpdate(cwd, reference, package);
        }