├── .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 |
4 |
5 |
6 | nlt-github
7 |
8 |
9 |
10 | 
11 | [](https://gitter.im/kwoc_19/nlt-github?utm_source=share-link&utm_medium=link&utm_campaign=share-link)
12 | [](./LICENSE)
13 | [](https://kwoc.kossiitkgp.org/)
14 | [](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 |
--------------------------------------------------------------------------------