├── .gitignore ├── LICENSE ├── README.md ├── demo.gif ├── licenses.py ├── logo.png ├── nlt_gb.py ├── setup.py └── user_profile.py /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Byte-compiled / optimized / DLL files 3 | __pycache__/ 4 | *.py[cod] 5 | *$py.class 6 | 7 | # C extensions 8 | *.so 9 | 10 | # Distribution / packaging 11 | .Python 12 | build/ 13 | develop-eggs/ 14 | dist/ 15 | downloads/ 16 | eggs/ 17 | .eggs/ 18 | lib/ 19 | lib64/ 20 | parts/ 21 | sdist/ 22 | var/ 23 | wheels/ 24 | *.egg-info/ 25 | .installed.cfg 26 | *.egg 27 | MANIFEST 28 | 29 | # PyInstaller 30 | # Usually these files are written by a python script from a template 31 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 32 | *.manifest 33 | *.spec 34 | 35 | # Installer logs 36 | pip-log.txt 37 | pip-delete-this-directory.txt 38 | 39 | # Unit test / coverage reports 40 | htmlcov/ 41 | .tox/ 42 | .coverage 43 | .coverage.* 44 | .cache 45 | nosetests.xml 46 | coverage.xml 47 | *.cover 48 | .hypothesis/ 49 | .pytest_cache/ 50 | 51 | # Translations 52 | *.mo 53 | *.pot 54 | 55 | # Django stuff: 56 | *.log 57 | local_settings.py 58 | db.sqlite3 59 | 60 | # Flask stuff: 61 | instance/ 62 | .webassets-cache 63 | 64 | # Scrapy stuff: 65 | .scrapy 66 | 67 | # Sphinx documentation 68 | docs/_build/ 69 | 70 | # PyBuilder 71 | target/ 72 | 73 | # Jupyter Notebook 74 | .ipynb_checkpoints 75 | 76 | # pyenv 77 | .python-version 78 | 79 | # celery beat schedule file 80 | celerybeat-schedule 81 | 82 | # SageMath parsed files 83 | *.sage.py 84 | 85 | # Environments 86 | .env 87 | .venv 88 | env/ 89 | venv/ 90 | ENV/ 91 | env.bak/ 92 | venv.bak/ 93 | 94 | # Spyder project settings 95 | .spyderproject 96 | .spyproject 97 | 98 | # Rope project settings 99 | .ropeproject 100 | 101 | # mkdocs documentation 102 | /site 103 | 104 | # mypy 105 | .mypy_cache/ -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Dibya Ranjan Jena 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 | Briefly-logo 4 |

5 | 6 |

nlt-github

7 | 8 |
9 | 10 | ![](https://img.shields.io/badge/python-3-red.svg?style=for-the-badge&logo=python) 11 | [![Gitter](https://img.shields.io/gitter/room/:user/:repo.svg?style=for-the-badge)](https://gitter.im/kwoc_19/nlt-github?utm_source=share-link&utm_medium=link&utm_campaign=share-link) 12 | [![](https://img.shields.io/badge/LICENSE-MIT-orange.svg?style=for-the-badge)](./LICENSE) 13 | [![KWoC 2018](https://img.shields.io/badge/KWoC-2018-0078D6.svg?style=for-the-badge&longCache=true&logo=data:image/png;base64,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)](https://kwoc.kossiitkgp.org/) 14 | [![contribution](https://img.shields.io/badge/contibutions-welcome-green.svg?style=for-the-badge)](https://github.com/dibyasonu/nlt-github/pulls) 15 | 16 | 17 |

nlt-github is an open source project which makes sure that you don't have to leave your beloved terminal to upload a new project in Github

18 |
19 | 20 | ------------------------------------------ 21 | ### Demo 22 |

23 | 24 |

25 | 26 | ## Installation 27 | `$ git clone https://github.com/dibyasonu/nlt-github.git` 28 | 29 | `$ pip install --editable .` 30 | 31 | ## Steps 32 | 1. **First we need to create a user. Creating a user creates a personal access token and stores the data to access the token 33 | locally** 34 | 35 | `$ nlt config --adduser` 36 | 37 | 2. **We add files like gitignore, LICENSE and README. (Optional)** 38 | 39 | `$ nlt add --gitignore` shows templates list for different languages, the user can select any no of languages and accordingly 40 | gitignore is addded to your project folder. 41 | 42 | `$ nlt add --license` shows templates list for different LICENSE, the user can select any one and 43 | LICENSE file is generated and accordingly addded to your project folder. 44 | 45 | `$ nlt add --readme` adds a README.md to your project. 46 | 47 | 3. **We create a Repo in your github account. We upload the project from the terminal.** 48 | 49 | `$ nlt create-remote ` 50 | Prompts for the **username** and the details like **repo name** and **Description** of the repo. It creates a repo 51 | according to given details, and adds its remote address to the origin referrence to the local project. 52 | 53 | `$ nlt create-remote --privy` 54 | It does the above task but the repo created is a **private** repo. 55 | 56 | Now you can push the project simply by `git push --all` 57 | 58 | 59 | ## Usage 60 | 61 | `$ nlt --help` 62 | ``` 63 | Usage: nlt [OPTIONS] COMMAND [ARGS]... 64 | 65 | Options: 66 | --help Show this message and exit. 67 | 68 | Commands: 69 | add Add required files 70 | config Configure Users 71 | create-remote create a new repo in Github and add remote origin to the 72 | local project. 73 | list-repos view list of repositories belonging to the user. 74 | pr list pull requests of current repository 75 | view-profile view basic info of any particular user. 76 | ``` 77 | 78 | #### Add required files 79 | 80 | `$ nlt add --help` 81 | 82 | ``` 83 | Usage: nlt add [OPTIONS] 84 | 85 | Add required files 86 | 87 | Options: 88 | --license Add license templates from the list to your project. 89 | --gitignore Add gitignore template from the list to your Project. 90 | --readme Addd README to your project. 91 | ``` 92 | 93 | #### Configure Users 94 | 95 | `$ nlt config --help` 96 | 97 | ``` 98 | Usage: nlt config [OPTIONS] 99 | 100 | Configure Users 101 | 102 | Options: 103 | --adduser Creates a personal access token in github and stores them locally. 104 | --deluser Remove created personal access token from github and locally. 105 | --showusers Show added users. 106 | ``` 107 | 108 | #### Create repo 109 | 110 | `$ nlt create-remote --help` 111 | 112 | ``` 113 | Usage: nlt create-remote [OPTIONS] 114 | 115 | Options: 116 | --username TEXT provide username in whose account the new repo is to be created. 117 | --privy create a private repository if used. 118 | ``` 119 | #### List repo 120 | 121 | `$ nlt list-repos --help` 122 | 123 | ``` 124 | Usage: nlt list-repos [OPTIONS] 125 | 126 | view list of repositories belonging to the user, private repositories can 127 | also be listed if user is configured. 128 | 129 | Options: 130 | --username TEXT provide username in whose repos are to be listed. 131 | --all specify if private repos are needed,in that case username 132 | must be configured. 133 | --help Show this message and exit. 134 | ``` 135 | #### Handle Pull Request 136 | 137 | `$ nlt pr --help` 138 | 139 | ``` 140 | Usage: nlt pr [OPTIONS] 141 | 142 | list open pull requests at remote of current git repository test them, 143 | merge them or comment on the thread 144 | 145 | Options: 146 | --help Show this message and exit. 147 | ``` 148 | #### View Profile 149 | 150 | `$ nlt view-profile --help` 151 | 152 | ``` 153 | Usage: nlt view-profile [OPTIONS] 154 | 155 | view basic profile information of a particular user 156 | 157 | Options: 158 | --username TEXT provide username in whose info is needed. 159 | --all specify if private repos count is needed,in that case 160 | username must be configured. 161 | --help Show this message and exit. 162 | ``` 163 | ------------------------------------------ 164 | ### Contributing 165 | 166 | * We're are open to `enhancements` & `bug-fixes` :smile:. 167 | * Feel free to add issues and submit patches 168 | 169 | ------------------------------------------ 170 | ### Author 171 | Dibya Ranjan Jena - [dibyasonu](https://github.com/dibyasonu) 172 | 173 | See also the list of [contributors](https://github.com/dibyasonu/nlt-github/graphs/contributors) who participated in this project. 174 | 175 | ------------------------------------------ 176 | ### License 177 | This project is licensed under the MIT - see the [LICENSE](./LICENSE) file for details 178 | 179 | 180 | -------------------------------------------------------------------------------- /demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dibyasonu/nlt-github/fe2f94d82edb7c5a71bb70c857ba3f9036652e57/demo.gif -------------------------------------------------------------------------------- /licenses.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function, unicode_literals 2 | import os 3 | import click 4 | import datetime 5 | import requests 6 | import click 7 | import json 8 | import sys 9 | from subprocess import call, STDOUT 10 | 11 | 12 | def getRequestsAsJSON(fromURL): 13 | r = requests.get(fromURL) 14 | if r.status_code==200: 15 | return r.json() 16 | else: 17 | click.secho("Internal error occured.", bold=True, fg='red') 18 | sys.exit(0) 19 | 20 | def getLicenseKey(lics, choice): 21 | 22 | return [k for k, v in lics.items() if v == choice][0] 23 | 24 | 25 | def replacePlaceholders(licenseText, licenseIdentifier, authorName, licenseYear): 26 | 27 | if licenseIdentifier == 'gpl-3.0' or licenseIdentifier == 'agpl-3.0': 28 | programName = click.prompt("\nEnter program name") 29 | desc = click.prompt("\nEnter one line to give a brief idea of what the program does") 30 | return licenseText.replace('', str(licenseYear)).replace('', authorName).replace('', programName).replace("", (programName + ' ' + desc)) 31 | 32 | elif licenseIdentifier == 'apache-2.0': 33 | return licenseText.replace('[yyyy]', str(licenseYear)).replace('[name of copyright owner]', authorName) 34 | 35 | elif licenseIdentifier == 'gpl-2.0': 36 | return licenseText.replace('Copyright (C) year name of author', ('Copyright (C) ' + str(licenseYear) + ' ' + authorName)) 37 | 38 | elif licenseIdentifier == 'lgpl-2.1': 39 | libraryName = click.prompt("\nEnter library name") 40 | desc = click.prompt("\nEnter one line to give a brief idea of what the library does") 41 | return licenseText.replace('', str(licenseYear)).replace('', authorName).replace("", (libraryName + ' ' + desc)) 42 | 43 | else: 44 | return licenseText.replace('[year]', str(licenseYear)).replace('[fullname]', authorName) 45 | 46 | 47 | 48 | def createLicense(srcURL, lKey): 49 | 50 | licenseBody = getRequestsAsJSON(srcURL + '/' + lKey)['body'] 51 | name = click.prompt("\nEnter your name") 52 | year = click.prompt("\nEnter the year(Press enter to use the current year)", show_default = False, default = datetime.datetime.now().year) 53 | licenseBody = replacePlaceholders(licenseBody, lKey, name, year) 54 | licenseFile = open('LICENSE', 'w') 55 | licenseFile.write(licenseBody) 56 | 57 | 58 | def generateLicense(licenseURL, licenses, chosen): 59 | 60 | click.clear() 61 | chosenLicenseKey = getLicenseKey(licenses, chosen) 62 | createLicense(licenseURL, chosenLicenseKey) 63 | click.secho("\nCreated LICENSE file based on the %s in the current directory.\n" %chosen, fg = "green", bold = True) -------------------------------------------------------------------------------- /logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dibyasonu/nlt-github/fe2f94d82edb7c5a71bb70c857ba3f9036652e57/logo.png -------------------------------------------------------------------------------- /nlt_gb.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import getpass 4 | import subprocess 5 | import requests 6 | import click 7 | import json 8 | import colorama 9 | import user_profile 10 | from cryptography.fernet import Fernet 11 | import licenses 12 | from pick import pick 13 | from pick import Picker 14 | from git import Repo 15 | 16 | 17 | @click.group() 18 | def cli(): 19 | pass 20 | 21 | def encrypt(data): 22 | cipher_key = Fernet.generate_key() 23 | cipher = Fernet(cipher_key) 24 | interim = json.dumps(data) 25 | end_string = str.encode(interim) 26 | encrypted_text = cipher.encrypt(end_string) 27 | return encrypted_text+cipher_key 28 | 29 | 30 | def decrypt(data): 31 | cipher_key = data[-44:] 32 | data = data[:-44] 33 | cipher = Fernet(cipher_key) 34 | decrypted_text = cipher.decrypt(data) 35 | interim = decrypted_text.decode() 36 | end_string = json.loads(interim) 37 | return end_string 38 | 39 | def file_handler(*argv): 40 | osuser = getpass.getuser() 41 | if os.name == 'nt': 42 | pat = os.path.join("C:", os.sep, "Users", osuser) 43 | elif sys.platform == 'darwin': 44 | pat = os.path.join("/","Users",osuser) 45 | else: 46 | pat = os.path.join("/", "home", osuser) 47 | nltpath = os.path.join(pat,'.nlt') 48 | if not os.path.isfile(nltpath): 49 | with open(nltpath, 'wb')as file: 50 | if(not len(argv)): 51 | data = {} 52 | else: 53 | data = argv[0] 54 | data = encrypt(data) 55 | file.write(data) 56 | data = decrypt(data) 57 | else: 58 | if(not len(argv)): 59 | with open(nltpath, 'rb') as file: 60 | data = file.read() 61 | data = decrypt(data) 62 | else: 63 | with open(nltpath,'wb') as file: 64 | data = argv[0] 65 | data = encrypt(data) 66 | file.write(data) 67 | data = decrypt(data) 68 | 69 | return data 70 | 71 | 72 | def execute(com): 73 | proc=subprocess.Popen(com,shell=True,stdout=subprocess.PIPE,stderr=subprocess.STDOUT) 74 | stdout_value = proc.communicate()[0] 75 | print(stdout_value.decode("utf-8")) 76 | 77 | 78 | def go_back(picker): 79 | return None, -1 80 | 81 | def get_languages(cwd,options,language_ext,list_of_lang=[]): 82 | for element in os.listdir(cwd): 83 | path = os.path.join(cwd,element) 84 | if os.path.isfile(path): 85 | ext = '.'+element.split('.')[-1] 86 | if ext in language_ext.keys(): 87 | lang = language_ext[ext] 88 | ignore = lang+'.gitignore' 89 | if ignore in options and ignore not in list_of_lang: 90 | list_of_lang.append(ignore) 91 | elif os.path.isdir(path): 92 | list_of_lang = get_languages(path,options,language_ext,list_of_lang) 93 | return list_of_lang 94 | 95 | 96 | @cli.command('create-remote',short_help='create a new repo in Github and add remote origin to the local project.') 97 | @click.option('--username',default='default',prompt=True,help='provide username in whose account the new repo is to be created.') 98 | @click.option('--privy',is_flag=bool,default=False,help="create a private repository if used.") 99 | def push_remote(username,privy): 100 | data=file_handler() 101 | if username in data.keys(): 102 | proname=click.prompt('Please enter the Project name') 103 | desc=click.prompt('A short description of the repository.') 104 | headers={"Authorization": "token "+data[username][0]} 105 | proname = proname.strip().replace(' ', '-') #sanitization 106 | 107 | payload={"name": proname,"description": desc,"private": privy,"has_issues": True,"has_projects": True,"has_wiki": True} 108 | 109 | response=requests.post('https://api.github.com/user/repos', headers=headers, data=json.dumps(payload)) 110 | 111 | if response.status_code == 201: 112 | repo_url = response.json()['clone_url'] 113 | click.secho('Repo created succesfully\n',bold=True,fg='green') 114 | click.secho(f'\nYour repository name is {proname}',bold=True,fg='green') 115 | click.secho(f'\nand it is at {repo_url}\n',bold=True,fg='green') 116 | command="git remote add origin "+repo_url 117 | execute(command) 118 | click.secho('Remote added succesfully',bold=True,fg='green') 119 | 120 | else: 121 | click.secho(str(response.json()),bold=True,fg='red') 122 | else: 123 | click.secho('user not found',bold=True,fg='red') 124 | click.secho('\nAdd a user using "nlt config --adduser"\n',bold=True,fg='green') 125 | 126 | 127 | @cli.command('config',help="Configure Users") 128 | # @click.option('--admin',is_flag=bool,default=False,help="Add a default global user for your machine") 129 | @click.option('--adduser',is_flag=bool,default=False,help="Creates a personal access token in github and stores them locally.") 130 | @click.option('--deluser',is_flag=bool,default=False,help="Remove created personal access token from github and locally.") 131 | @click.option('--showusers',is_flag=bool,default=False,help="Show added users.") 132 | @click.option('--setdefault',is_flag=bool,default=False,help="add default users.") 133 | def user_config(adduser,deluser,showusers,setdefault): 134 | data=file_handler() 135 | # if admin: 136 | 137 | # if bool(data): 138 | # pass 139 | # #work to do 140 | # else: 141 | # click.secho('No users added. Add users by running "nlt config --adduser"',bold=True,fg='red') 142 | 143 | if adduser: 144 | user_name=click.prompt('Please enter your Github user name') 145 | password=click.prompt('Enter the password',hide_input=True) 146 | if user_name in data.keys(): 147 | click.secho('user exists',bold=True,fg='red') 148 | #checks whether token status is ok output exists(enhancment) 149 | #token status is not ok ask to delete and create a new token;addusr(enhancment) 150 | 151 | else: 152 | payload='{"scopes": ["admin:public_key", "admin:repo_hook", "delete_repo", "repo", "user"], "note": "NLT"}' 153 | response=requests.post('https://api.github.com/authorizations',data=payload,auth=(user_name, password)) 154 | 155 | if response.status_code==201: 156 | data[user_name]=[response.json()['token'],response.json()['url']] 157 | if click.confirm('Do you want to add this account as default account ?'): 158 | data['default']=[response.json()['token'],response.json()['url'],user_name] 159 | 160 | file_handler(data) 161 | click.secho('user added succesfully',bold=True,fg='green') 162 | 163 | # ADD CONDITION TO CHECK IF TOKEN EXISTS 164 | #checks if user don't exist locally but token is in github import it and add user(enhancment) 165 | #so delete it and add another token 166 | 167 | if deluser: 168 | user_name=click.prompt('Please enter your Github user name') 169 | password=click.prompt('Enter the password',hide_input=True) 170 | 171 | if user_name in data.keys(): 172 | response=requests.delete(data[user_name][1], auth=(user_name, password)) 173 | 174 | if response.status_code==204: 175 | if 'default' in data and data['default'][2]==user_name: 176 | data.pop('default') 177 | data.pop(user_name) 178 | file_handler(data) 179 | click.secho('user deleted succesfully',bold=True,fg='green') 180 | else: 181 | click.secho('Enter the right credentials',bold=True,fg='red') 182 | else: 183 | click.secho('user not found',bold=True,fg='red') 184 | 185 | if showusers: 186 | users=[x for x in data if x != 'default'] 187 | if 'default' in data: 188 | default = data['default'][2] 189 | else: 190 | default = None 191 | if len(users): 192 | for i in users: 193 | if i!=default: 194 | click.secho(i,bold=True,fg='blue') 195 | else: 196 | click.secho(i+" *",bold=True,fg='blue') 197 | else: 198 | click.secho('No users added. Add users by running "nlt config --adduser"',bold=True,fg='red') 199 | #checks users as well as their status and generate the status 200 | 201 | if setdefault: 202 | user_name=click.prompt('Please enter your Github user name') 203 | if user_name in data.keys(): 204 | data['default'] = data[user_name]+[user_name] 205 | file_handler(data) 206 | click.secho('default user added',bold=True,fg='yellow') 207 | else: 208 | click.secho('user not found',bold=True,fg='red') 209 | 210 | @cli.command('add',help="Add required files") 211 | @click.option('--license',is_flag=bool,default=False,help="Add license templates from the list to your project.") 212 | @click.option('--gitignore',is_flag=bool,default=False,help="Add gitignore template from the list to your Project.") 213 | @click.option('--readme',is_flag=bool,default=False,help="Addd README to your project.") 214 | def add(license, gitignore, readme): 215 | 216 | if license: 217 | click.clear() 218 | licenseURL = 'https://api.github.com/licenses' 219 | GETResponse = licenses.getRequestsAsJSON(licenseURL) 220 | licensesDict = {} 221 | 222 | for i in GETResponse: 223 | licensesDict[i['key']] = i['name'] 224 | promptMessage = 'Choose a license or press s to stop' 225 | title = promptMessage 226 | options = list(licensesDict.values()) 227 | picker = Picker(options, title, indicator = '=>', default_index = 0) 228 | picker.register_custom_handler(ord('s'), go_back) 229 | chosenLicense, index = picker.start() 230 | # user selection is stored in chosenLicense, which is the index'th element in options = licenses 231 | if index != -1: 232 | licenses.generateLicense(licenseURL, licensesDict, chosenLicense) 233 | else: 234 | sys.exit(0) 235 | 236 | if gitignore: 237 | url = "https://api.github.com/repos/github/gitignore/contents/" 238 | r = requests.get(url) 239 | 240 | if r.status_code==200: 241 | x = r.json() 242 | else: 243 | click.secho("Internal error occured.", bold=True, fg='red') 244 | sys.exit(0) 245 | ignores = [{"name" : item['name'], "url" : item['download_url']} for item in x if item['type']=='file' and ".gitignore" in item['name']] 246 | promptMessage = 'Choose a gitignore \n(press SPACE to mark, ENTER to continue, s to stop):' 247 | title = promptMessage 248 | options = [item['name'] for item in ignores] 249 | language_ext = requests.get("https://raw.githubusercontent.com/fristonio/Resources/master/lang-ext.json").json() 250 | selected_ext = get_languages(os.getcwd(),options,language_ext) 251 | for item in selected_ext: 252 | index = options.index(item) 253 | options.remove(item) 254 | ignore = ignores[index] 255 | ignores.remove(ignore) 256 | options = [item]+options 257 | ignores = [ignore]+ignores 258 | inp = click.prompt("do you want smart gitignore(Y) or manually select(N)?") 259 | if inp[0].lower() == 'n': 260 | picker = Picker(options, title, multi_select=True, min_selection_count=1) 261 | picker.register_custom_handler(ord('s'), go_back) 262 | selected = picker.start() 263 | elif inp[0].lower() == 'y': 264 | selected = [[item,options.index(item)] for item in selected_ext] 265 | if type(selected) == list: 266 | d_urls = [ignores[item[1]]['url'] for item in selected] 267 | else: 268 | sys.exit(0) 269 | sep = "\n"+("#" * 40)+"\n" 270 | str_write = ''.join(["".join(sep+requests.get(item).text+sep) for item in d_urls]) 271 | with open('.gitignore', 'a+') as file: 272 | file.write(str_write) 273 | click.secho("gitignore templates added succesfully.\n", fg = "green", bold = True) 274 | 275 | if readme: 276 | with open('README.md', 'w+') as file: 277 | pass 278 | 279 | @cli.command('list-repos',short_help='view list of repositories belonging to the user.') 280 | @click.option('--username',default='default',prompt=True,help='provide username in whose repos are to be listed.') 281 | @click.option('--all',is_flag=bool,default=False,help='specify if private repos are needed,in that case username must be configured.') 282 | def list_repos(username,all): 283 | ''' 284 | view list of repositories belonging to the user, private repositories can also be listed if user is configured. 285 | ''' 286 | data = file_handler() 287 | user_profile.display_repo(data,username,all) 288 | 289 | 290 | @cli.command('view-profile',short_help='view basic info of any particular user.') 291 | @click.option('--username',default='default',prompt=True,help='provide username in whose info is needed.') 292 | @click.option('--all',is_flag=bool,default=False,help='specify if private repos count is needed,in that case username must be configured.') 293 | def list_repos(username,all): 294 | ''' 295 | view basic profile information of a particular user 296 | ''' 297 | data = file_handler() 298 | user_profile.display_profile(data,username,all) 299 | 300 | @cli.command('pr',short_help='list pull requests of current repository') 301 | def list_pr(): 302 | ''' 303 | list open pull requests at remote of current git repository test them, merge them or comment on the thread 304 | ''' 305 | repo = Repo(os.getcwd()) 306 | assert not repo.bare 307 | url = repo.remotes.origin.url 308 | repo_name = url.replace('https://github.com','').replace('.git','') 309 | params = {'state':'open'} 310 | data = file_handler() 311 | url = "https://api.github.com/repos"+repo_name+"/pulls" 312 | r = requests.get(url,params=params) 313 | if r.status_code == 200: 314 | pulls = r.json() 315 | message = "pull requests for "+ repo_name[1:] +"\npress s to quit and enter to select" 316 | options = [ '#'+pull['url'].split('/')[-1]+' '+pull['title']+' : '+pull['user']['login'] for pull in pulls ] 317 | picker = Picker(options, message , indicator = '=>', default_index = 0) 318 | picker.register_custom_handler(ord('s'), go_back) 319 | chosenpr, index = picker.start() 320 | pull = pulls[index] 321 | event_number = pull['url'].split('/')[-1] 322 | from_to = pull['head']['label']+' to '+pull['base']['label'] 323 | click.clear() 324 | click.secho('#'+event_number+': from ',nl=False) 325 | click.secho(from_to,fg='cyan') 326 | click.secho("title: ",nl=False) 327 | click.secho(pull['title'],fg='yellow') 328 | click.secho("made by: ",nl=False) 329 | click.secho(pull['user']['login'],fg='blue') 330 | if(pull['body']!=""): 331 | click.secho("body:\n"+pull['body'],fg='yellow') 332 | click.secho("created at: "+pull['created_at']) 333 | comment_url = pull['comments_url'] 334 | r = requests.get(comment_url) 335 | if r.status_code == 200: 336 | comments = r.json() 337 | if comments != []: 338 | click.secho('comments:') 339 | for comment in comments: 340 | click.secho(comment['user']['login']+":"+comment['body']) 341 | click.secho('=================================================') 342 | inp = click.prompt("enter n to check the pull request in a new branch\n m to merge the pull request\n c to comment on the pull request \n any other key to exit\n") 343 | if inp == 'n': 344 | click.secho('creating a new brach and checking out to the branch') 345 | fetch = 'git fetch origin pull/'+event_number+'/head:pr#'+event_number 346 | os.system(fetch) 347 | checkout = 'git checkout pr#'+event_number 348 | os.system(checkout) 349 | elif inp == 'm': 350 | click.confirm('Are you sure you want to merge this pull request ?',abort=True) 351 | username = click.prompt("username") 352 | headers = {"Authorization": "token "+data[username][0]} 353 | url = "https://api.github.com/repos"+repo_name+"/pulls/"+event_number+"/merge" 354 | r = requests.put(url,headers=headers) 355 | click.secho(r.json()['message']) 356 | elif inp == 'c': 357 | inp = click.prompt("Enter the comment that you want to make") 358 | username = click.prompt("username") 359 | headers = {"Authorization": "token "+data[username][0]} 360 | url = "https://api.github.com/repos"+repo_name+"/issues/"+event_number+"/comments" 361 | payload = {"body":inp} 362 | r = requests.post(url,data=json.dumps(payload),headers=headers) 363 | if r.status_code == 201: 364 | click.secho("comment published") 365 | else: 366 | click.secho("Internal Error",fg='red') 367 | else: 368 | click.secho("Internal error"+r.status_code, fg='red') 369 | else: 370 | click.secho("Internal error", fg='red') 371 | 372 | if __name__ == '__main__': 373 | cli() 374 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup, find_packages 2 | 3 | def readme(): 4 | try: 5 | with open('README.md') as f: 6 | return f.read() 7 | except: 8 | pass 9 | 10 | setup( 11 | name ='NLT', 12 | version ='1.0', 13 | description = 'Upload your projects to Github without leaving the terminal', 14 | author = 'Dibya Ranjan Jena', 15 | author_email = 'dibyajena917@gmail.com', 16 | 17 | long_description=readme(), 18 | long_description_content_type='text/markdown', 19 | 20 | packages = find_packages(), 21 | include_package_data = True, 22 | install_requires=[ 23 | 'requests', 24 | 'click', 25 | 'colorama', 26 | 'windows-curses;platform_system=="Windows"', 27 | 'pick', 28 | 'cryptography', 29 | 'prettytable', 30 | 'gitpython' 31 | ], 32 | entry_points=''' 33 | [console_scripts] 34 | nlt=nlt_gb:cli 35 | 36 | ''', 37 | ) -------------------------------------------------------------------------------- /user_profile.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import os 3 | import click 4 | import datetime 5 | import requests 6 | import click 7 | import json 8 | from prettytable import PrettyTable 9 | 10 | def display_repo(data,username,all): 11 | if all: 12 | if username in data.keys(): 13 | payload = {'type':'owner','per_page':100} 14 | url='https://api.github.com/user/repos' 15 | headers={"Authorization": "token "+data[username][0]} 16 | response=requests.get(url,headers=headers,params=payload) 17 | else: 18 | click.secho('User not configured can not view private repos. Add users by running "nlt config --adduser".\n', fg = "red", bold = True) 19 | sys.exit(0) 20 | else: 21 | payload = {'per_page':100} 22 | url='https://api.github.com/users/'+username+'/repos' 23 | response=requests.get(url,payload) 24 | if response.status_code == 200: 25 | response=response.json() 26 | t = PrettyTable(['Repo','url','* star']) 27 | i=1 28 | for repo in response: 29 | t.add_row([repo["name"],repo["html_url"],repo["stargazers_count"]]) 30 | 31 | click.secho(t.get_string(sortby="* star",reversesort=True)) 32 | else: 33 | click.secho("Internal error occured.", bold=True, fg='red') 34 | sys.exit(0) 35 | 36 | def display_profile(data,username,all): 37 | if all: 38 | if username in data.keys(): 39 | url='https://api.github.com/user' 40 | headers={"Authorization": "token "+data[username][0]} 41 | response=requests.get(url,headers=headers) 42 | else: 43 | click.secho('User not configured can not view private repos. Add users by running "nlt config --adduser".\n', fg = "red", bold = True) 44 | sys.exit(0) 45 | else: 46 | url='https://api.github.com/users/'+username 47 | response=requests.get(url) 48 | if response.status_code == 200: 49 | response=response.json() 50 | click.secho(response['name']+" ("+username+")", bold=True, fg='yellow') 51 | if(response['email']): 52 | click.secho("Location: "+str(response['email'])) 53 | if(response['company']): 54 | click.secho("Company: "+str(response['company'])) 55 | if(response['location']): 56 | click.secho("Location: "+str(response['location'])) 57 | if(response['bio']): 58 | click.secho("Bio: "+response['bio']) 59 | click.secho("Public repos: "+str(response['public_repos'])) 60 | if all: 61 | click.secho("Private repos: "+str(response['total_private_repos'])) 62 | click.secho("Followers: "+str(response['followers'])+" || Following: "+str(response['following'])) 63 | url='https://api.github.com/users/'+username+'/events/public' 64 | response=requests.get(url) 65 | if response.status_code == 200: 66 | response=response.json() 67 | if len(response)>0: 68 | click.secho('last public activity: '+response[0]['created_at']) 69 | option=click.prompt("Press l to view repos else anything else to stop") 70 | if(option.lower()=='l'): 71 | if all: 72 | display_repo(data,username,True) 73 | else: 74 | display_repo(data,username,False) 75 | else: 76 | sys.exit(0) 77 | 78 | else: 79 | click.secho("Internal error occured.", bold=True, fg='red') 80 | sys.exit(0) 81 | --------------------------------------------------------------------------------