Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Resolve hostname set in ssh config #87

Closed
guruor opened this issue Nov 12, 2023 · 18 comments
Closed

Resolve hostname set in ssh config #87

guruor opened this issue Nov 12, 2023 · 18 comments

Comments

@guruor
Copy link

guruor commented Nov 12, 2023

Original issue in upstream repo: ruifm/gitlinker.nvim#53

Working PR for the same in upstream repo: ruifm/gitlinker.nvim#52 (Pending review)

@linrongbin16
Copy link
Owner

hi, @G0V1NDS, thanks for mention it, I'm looking into it.

@linrongbin16
Copy link
Owner

linrongbin16 commented Nov 12, 2023

would you please help me with some details:

  1. when mention alias, can you give a specific case? if it's not data-sensitive. for example it's xyz-git.
  2. you propose using ssh -G to resolve host, I need to check how does the command work, if it's a better way to resolve all git remotes, or just for your use case?
  3. is it running ssh -G xyz-git, or running something? I am confused about the description.

@guruor
Copy link
Author

guruor commented Nov 13, 2023

@linrongbin16
I just referred open issue from upstream repo. Upstream issue and PR were created by @joaodubas

I will explain how I am using ssh config. SSH allows you to configure alias for the hosts.
For example I have this alias in my ~/.ssh/config:

Host github-personal
    HostName github.com
    User git
    IdentityFile ~/.ssh/id_rsa_personal
    Compression no

I can use this alias in git remote like this:

# ❯ git remotes
origin  https://github.com/G0V1NDS/dotfiles-open.git (fetch)
origin  https://github.com/G0V1NDS/dotfiles-open.git (push)
personal        git@github-personal:G0V1NDS/dotfiles-open.git (fetch)
personal        git@github-personal:G0V1NDS/dotfiles-open.git (push)

Here origin remote is configured from regular https URL provided when cloning the repo. I configured another origin named personal using github-personal alias.
Now when I push to personal remote, git internally resolves this alias and understands that github-personal is pointing to github.com and it has to use ~/.ssh/id_rsa_personal key.

We can get the same info from ssh -G by checking the config for the configured host.
Like this:

# ❯ ssh -G github-personal | grep ^hostname
hostname github.com

Not sure ssh -G is the best approach available, got to know about this from original issue only, but I can see that is will solve a generic problem and not limited to my use case.

@linrongbin16
Copy link
Owner

linrongbin16 commented Nov 13, 2023

hi @G0V1NDS , really thanks for your detailed example and education.

I will first do some investigation into this (since I'm not expert in git alias), make sure the solution is good for most users.

@linrongbin16
Copy link
Owner

linrongbin16 commented Nov 13, 2023

Hi @G0V1NDS , @joaodubas,

Please use below config to map github-personal.com to github.com:

require("gitlinker").setup({
    custom_rules = function(remote_url)
        local rules = {
            {
                "^git@github-personal%.([_%.%-%w]+):([%.%-%w]+)/([_%.%-%w]+)%.git$",
                "https://github.%1/%2/%3/blob/",
            },
            {
                "^git@github-personal%.([_%.%-%w]+):([%.%-%w]+)/([_%.%-%w]+)$",
                "https://github.%1/%2/%3/blob/",
            },
        }
        for _, rule in ipairs(rules) do
            local pattern = rule[1]
            local replace = rule[2]
            if string.match(remote_url, pattern) then
                local result = string.gsub(remote_url, pattern, replace)
                return result
            end
        end
        return nil
    end,
})

update: since #99 , you can simply config:

require("gitlinker").setup({
  override_rules = {
    {
      "^git@github-personal%.([_%.%-%w]+):([%.%-%w]+)/([_%.%-%w]+)%.git$",
      "https://github.%1/%2/%3/blob/",
    },
    {
      "^git@github-personal%.([_%.%-%w]+):([%.%-%w]+)/([_%.%-%w]+)$",
      "https://github.%1/%2/%3/blob/",
    },
  }
})

@guruor
Copy link
Author

guruor commented Nov 14, 2023

@linrongbin16 Thanks for a quick solution, really appreciate it.
I was able to achieve the expected result from the override_rules with some tweaks on your suggestion.

    override_rules = {
      {
        "^git@github%-personal:([%.%-%w]+)/([_%.%-%w]+)$",
        "https://github.com/%1/%2/blob/",
      },
      {
        "^git@bitbucket%-work:([%.%-%w]+)/([_%.%-%w]+)$",
        "https://bitbucket.org/%1/%2/src/",
      },
      {
        "^git@gitlab%-work:([%.%-%w]+)/([_%.%-%w]+)$",
        "https://gitlab.com/%1/%2/blob/",
      },
    },

Tweaks made:

  • My ssh alias wasn't having TLD suffix, so I had to hardcode it in substitute pattern.
  • Needed to escape the hyphen in the pattern %-personal to ensure proper functionality.

I would still prefer a solution which utilises the ssh config so user won't need to define a override rule as the information is already available in ssh config.
Will see how fugitive is achieving the host identification for ssh remotes.

@guruor
Copy link
Author

guruor commented Nov 14, 2023

@linrongbin16 A quick look at fugitive code tells that it is achieving the same by reading the ssh config file to read information ('user', 'hostname' and 'port') which helps derive the remote url.

@linrongbin16
Copy link
Owner

hi @G0V1NDS , thanks for point that out, I will check

@joaodubas
Copy link

@linrongbin16 the solution I implemented in a PR upstream parses the result ssh -G <alias>, as can be seen in lua/gitlinker/ssh.lua.

I can open the same PR here if it makes sense.

@linrongbin16
Copy link
Owner

linrongbin16 commented Nov 14, 2023

yeah, @joaodubas thank you!


seems using ssh is the correct way to get the domain.

at first I am only thinking I can use a regex thing (lua pattern) to map host to remote, but now I found it's much more complicated.

a permanent git url contains multiple components ( for example: https://github.com/neovim/neovim/blob/2e156a3b7d7e25e56b03683cc6228c531f4c91ef/src/nvim/main.c#L137-L156):

  1. domain: https://github.com, this part should be resolved by ssh -G.
  2. user/repo: /neovim/neovim, for this plugin it's linrongbin16/gitlinker.nvim.
  3. a route: /blob ( there's another requirement want to generate the /blame url, it's also another route).
  4. a git commit: /2e156a3b7d7e25e56b03683cc6228c531f4c91ef.
  5. the file path: /src/nvim/main.c.
  6. the line ranges: #L137-L156. and I just found that github even support columns, it looks like #L137C1-L156C10.

I am thinking how to make this thing real powerful and customizable.

@linrongbin16
Copy link
Owner

@joaodubas , oh, I forgot why this lua/gitlinker/ssh.lua not in my fork (maybe at first I didn't know what this ssh is for, so I simply delete it?)

I will patch this part back to my fork. thanks for point that!

@joaodubas
Copy link

joaodubas commented Nov 15, 2023

@linrongbin16 this is the module I created to solve the issue with aliases in ssh configuration. It belongs to the PR @G0V1NDS referenced on the first message (ruifm#52)

@linrongbin16
Copy link
Owner

linrongbin16 commented Nov 15, 2023

hi @joaodubas , @G0V1NDS ,

I'm trying to add ssh -G host in this PR: #110.

But I have some issue when testing it in my local machine. I guess I don't have a correct environment (alias git host) to test this feature, would you please help test if it's working?

Or if this feature could be tested without an alias git host environment, would you please help my testing steps (not sure if my steps have any issues):

  1. set my ~/.ssh/config:

    Host github-personal.com
        HostName github.com
        User git
        IdentityFile ~/.ssh/id_rsa_personal
        Compression no
    
  2. set my ~/github/linrongbin16/gitlinker.nvim/.git/config:

    [core]
         repositoryformatversion = 0
         filemode = true
         bare = false
         logallrefupdates = true
         ignorecase = true
         precomposeunicode = true
     ; [remote "origin"]
     ;   url = https://github.com/linrongbin16/gitlinker.nvim.git
     ;   fetch = +refs/heads/*:refs/remotes/origin/*
     [branch "master"]
         remote = origin
         merge = refs/heads/master
     [lfs]
         repositoryformatversion = 0
     [user]
         name = linrongbin16
         email = [email protected]
     [branch "feat-routers"]
         remote = origin
         merge = refs/heads/feat-routers
     [remote "personal"]
         url = [email protected]:linrongbin16/gitlinker.nvim.git
         fetch = +refs/heads/*:refs/remotes/personal/*
    

    note: the last [remote "personal"] is the test target, and I commented the [remote "origin"].

  3. open the file ~/github/linrongbin16/gitlinker.nvim/lua/gitlinker.lua, and press <leader>gl, there's an error:

    image
  4. Here's the debug logging:

    gitlinker.log

  5. looks like the error is throwed from the command:

    git rev-parse personal

    it's trying to search a git commit in current repo.

  6. all below commands are failed to search the correct git commit:

    git rev-parse @{u}
    git rev-parse personal
  7. this command works:

    git rev-parse HEAD
  8. but it's been filtered by the check function _is_rev_in_remote (it looks like trying to verify if the git commit exists in remote host, I guess if in a correct environment that has correct alias host, this filter should be correct?):

    git branch --remotes --contains HEAD
    git branch --remotes --contains HEAD~1
    git branch --remotes --contains HEAD~2
    git branch --remotes --contains HEAD~3
    ...

    The outputs is origin/feat-routes, which cannot match the personal, the check is here:

    if _is_rev_in_remote("HEAD", remote) then
    local head_rev = _get_rev("HEAD")
    if head_rev then
    return head_rev
    end
    end
    -- try last 50 parent commits
    for i = 1, 50 do
    local revspec = "HEAD~" .. i
    if _is_rev_in_remote(revspec, remote) then
    local rev = _get_rev(revspec)
    if rev then
    return rev
    end
    end
    end

@guruor
Copy link
Author

guruor commented Nov 15, 2023

@linrongbin16 your ssh and git config looks good to me.
I tried testing the code on local and faced multiple issues except the rev-parse issue, added the fix for those in PR comments.

@linrongbin16
Copy link
Owner

linrongbin16 commented Nov 15, 2023

@G0V1NDS , really thanks for your contribute! I have fixed all the comments in:

would you please take a look again?

@guruor
Copy link
Author

guruor commented Nov 15, 2023

@linrongbin16 All the issues are resolved, thanks for quick update.
Tested for github mainly, everything looks good.
Though BitBucket is not officially supported, still tried the same on a BitBucket repo. URL format was almost same, I just replaced blob with src in URL and it worked for BitBucket too.

Were you able to fix your local issue regarding rev-parse?

@linrongbin16
Copy link
Owner

No, I still failed to generate the git link in my local environment, but since you helped testing, I will merge into master branch now.

really thanks to your contribute!

@linrongbin16
Copy link
Owner

solved

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants