Dilawar's Blog

watch -n 1 /dev/null

15 Jul 2021

Insert credentials from environment into remote url when `git push`

You are on a remote server. You don’t want to store your credentials on that machine. You can temporary export your credentials as environment variable e.g. export GITHUB_USER=dilawar and export GITHUB_PASSWORD=ladidadadad.

Here is a small script that acts as a wrapper around git push. It modifies the remote url and insert username and password (read from env). No need to store ssh keys on remote server or git clone with https credentials.

For github.com, it looks for GITHUB_USER first, then for GIT_USER, and then USER environment variable. Similarly it looks for GITHUB_TOKEN, GIT_TOKEN for tokens. For gitlab.com, it looks for GITLAB_USER or GIT_USER or USER for username, and GITLAB_TOKEN, GIT_TOKEN for password.

If the script can’t determine the username and password, it throws an exception.

On success, it calls git push with new url (that has username and password). For example git push https://github.com/dilawar/Scripts will become git push https://dilawar:mytoken@github.com/dilawar/Scritps.

You can find the script here.

#!/usr/bin/env python3
# pip3 install typer --user  # only dependency

import sys
import typing as T
import subprocess
from urllib.parse import urlparse
import os

import typer

app = typer.Typer()

git = "git"


def env(key:str, default=None) -> T.Optional[str]:
    return os.environ.get(key, default)

def find_remote() -> str:
    remote = subprocess.check_output([git, "remote", "-v"], text=True)
    remote = [x for x in remote.split("\n") if "(push)" in x.strip()][0]
    remote = remote.split()[1]
    return remote


def get_user(domain) -> T.Optional[str]:
    if domain == "github.com":
        return env("GITHUB_USER", env("GIT_USER", None))
    if domain == "gitlab.com":
        return env("GITLAB_USER", env("GIT_USER", None))
    return env('GIT_USER', env('USER', None))

def get_token(domain) -> T.Optional[str]:
    if domain == "github.com":
        return env("GITHUB_TOKEN", env("GIT_TOKEN", None))
    if domain == "gitlab.com":
        return env("GITLAB_TOKEN", env("GIT_TOKEN", None))
    return env("GIT_TOKEN", None)


def find_branch() -> str:
    branches = (
        subprocess.check_output([git, "branch", "-a"], text=True).strip().split("\n")
    )
    branch = [x.strip() for x in branches if x.strip()[0] == "*"][0]
    return branch[1:].strip()  # remote leading *


def rewrite_remote(remote: str) -> str:
    o = urlparse(remote)
    assert o.scheme in ["https", "http"]
    U, P = get_user(o.netloc), get_token(o.netloc)
    assert U is not None, "Could not determine user"
    assert P is not None, "Could not determine token"
    return o.geturl().replace(f"{o.scheme}://", f"{o.scheme}://{U}:{P}@")

def git_push(url, branch):
    cmd = f"git push {url} {branch}"
    subprocess.run(cmd.split())

@app.command()
def main(remote: T.Optional[str] = None, branch: T.Optional[str] = None):
    # get the remote
    remote = find_remote() if remote is None else remote
    branch = find_branch() if branch is None else branch
    url = rewrite_remote(remote)
    git_push(url, branch)


if __name__ == "__main__":
    app()

Usage

Save it as gh_push.py somewhere in $PATH and make it executable. Export GITHUB_USER and GITHUB_TOKEN. And instead of git push. do

$ gh_push.py

Categories

Tags