Add python package to setup SSH keys

Supports setting SSH keys for the following services:

* GitHub
* GitLab
* BitBucket Cloud
* Gogs
This commit is contained in:
Kenneth Benzie 2018-01-04 16:50:39 +00:00
parent 930e540f01
commit b84d91eb54
3 changed files with 209 additions and 0 deletions

13
README.md Normal file
View File

@ -0,0 +1,13 @@
# bootstrap
Bootstrap an OS instance with bare essentials.
## Package
Install as a pip package to set SSH keys on any of GitHub, GitLab, BitBucket
Cloud, or Gogs servers:
```
pip install git+https://code.infektor.net/config/bootstrap.git
python -c 'import bootstrap; bootstrap.set_ssh_keys()'
```

185
bootstrap/__init__.py Normal file
View File

@ -0,0 +1,185 @@
"""Interactively set SSH keys on remote Git servers."""
from __future__ import print_function
from getpass import getpass
from os import environ
from os.path import join
from platform import node, system
from requests import ConnectionError, get, post
from requests.auth import HTTPBasicAuth
from requests.compat import urlparse
try:
input = raw_input
except NameError:
pass
class BootstrapError(Exception):
"""Bootstrap Exception."""
pass
def bootstrap_error(response):
"""Create a BootstrapError from a Response."""
return BootstrapError('%s %s' % (response.status_code, response.reason))
def agree(question, default='Y'):
"""Prompt user to answer a yes/no question."""
valid = {
'y': True,
'Y': True,
'yes': True,
'n': False,
'N': False,
'no': False,
'': {'Y': True,
'N': False}[default]
}
answer = input('%s [%s]? ' % (question, {'Y': 'Y/n', 'N': 'y/N'}[default]))
try:
return valid[answer]
except KeyError:
print('invalid input: %s' % answer)
return agree(question)
def get_url(service, default):
"""Get URL."""
url = input('%s URL%s: ' % (service, ' (%s)' % default if default else ''))
if url == '':
if default:
return default
else:
print('invalid input: %s' % url)
return get_url(service, default)
return url
def get_username_password(service):
"""Get username/password."""
username = input('%s username: ' % service)
password = getpass('%s password: ' % service)
return (username, password)
def get_local_key():
"""Get local SSH key."""
if system() == 'Windows':
home = environ['userprofile']
else:
home = environ['HOME']
with open(join(home, '.ssh', 'id_rsa.pub'), 'r') as key_file:
return key_file.read().rstrip()
def key_exists(keys, local_key):
"""Check if local SSH key is already set."""
for key in keys:
if local_key.startswith(key['key']):
return True
return False
def set_github_ssh_key():
"""Set GitHub SSH key."""
url = urlparse(get_url('GitHub', 'https://github.com'))
keys_url = '%s://api.%s/user/keys' % (url.scheme, url.netloc)
username, password = get_username_password('GitHub')
auth = HTTPBasicAuth(username, password)
response = get(keys_url, auth=auth)
if response.status_code != 200:
raise bootstrap_error(response)
keys = response.json()
local_key = get_local_key()
if not key_exists(keys, local_key):
response = post(
keys_url, auth=auth, json={'title': node(),
'key': local_key})
if response.status_code != 201:
raise bootstrap_error(response)
def set_gitlab_ssh_key():
"""Set GitLab SSH key."""
api_url = '%s/api/v4' % get_url('GitLab', 'https://gitlab.com')
session_url = '%s/session' % api_url
keys_url = '%s/user/keys' % api_url
username, password = get_username_password('GitLab')
response = post(session_url, {'login': username, 'password': password})
if response.status_code != 201:
raise bootstrap_error(response)
auth = {'Private-Token': response.json()['private_token']}
response = get(keys_url, headers=auth)
if response.status_code != 200:
raise bootstrap_error(response)
keys = response.json()
local_key = get_local_key()
if not key_exists(keys, local_key):
response = post(
keys_url, headers=auth, json={'title': node(),
'key': local_key})
if response.status_code != 201:
raise bootstrap_error(response)
def set_bitbucket_cloud_ssh_key():
"""Set BitBucket Cloud SSH key."""
username, password = get_username_password('BitBucket Cloud')
keys_url = 'https://api.bitbucket.org/1.0/users/%s/ssh-keys' % username
auth = HTTPBasicAuth(username, password)
response = get(keys_url, auth=auth)
if response.status_code != 200:
raise bootstrap_error(response)
keys = response.json()
local_key = get_local_key()
if not key_exists(keys, local_key):
response = post(
keys_url, auth=auth, data={'label': node(),
'key': local_key})
if response.status_code != 200:
raise bootstrap_error(response)
def set_gogs_ssh_key():
"""Set Gogs SSH key."""
keys_url = '%s/api/v1/user/keys' % get_url('Gogs', None)
username, password = get_username_password('Gogs')
auth = HTTPBasicAuth(username, password)
response = get(keys_url, auth=auth)
if response.status_code != 200:
raise bootstrap_error(response)
keys = response.json()
local_key = get_local_key()
if not key_exists(keys, local_key):
response = post(
keys_url, auth=auth, json={'title': node(),
'key': local_key})
if response.status_code != 201:
raise bootstrap_error(response)
def set_ssh_keys():
"""Interactively set SSH keys on remote Git servers."""
try:
for service in ['GitHub', 'GitLab', 'BitBucket Cloud', 'Gogs']:
question = 'Set %s SSH key' % service
default = 'Y'
while agree(question, default):
try:
{
'GitHub': set_github_ssh_key,
'GitLab': set_gitlab_ssh_key,
'BitBucket Cloud': set_bitbucket_cloud_ssh_key,
'Gogs': set_gogs_ssh_key,
}[service]()
if service == 'BitBucket Cloud':
break
question = 'Set another %s SSH key' % service
default = 'N'
except (BootstrapError, ConnectionError) as error:
print('error: %s' % error.message)
except KeyboardInterrupt:
exit(130)

11
setup.py Normal file
View File

@ -0,0 +1,11 @@
"""Setup bootstrap package."""
from setuptools import find_packages, setup
setup(
name='bootstrap',
version='0.1.0',
packages=find_packages(),
install_requires=[
'requests',
])