Commit 0b01150a authored by Alan Marchiori's avatar Alan Marchiori
Browse files

added ignore

parent b23a7310
......@@ -3,8 +3,10 @@ import os
import os.path
import config
import config.validation as validation
from config.echo import echo, error, warn
import shutil
from config.echo import *
from utils.gitlab import GitLab
import courses
@click.command()
@click.argument('course', callback=validation.course)
......@@ -15,6 +17,11 @@ def init(course, semester, section):
and on gitlab for a given COURSE, SECTION, and SEMESTER. Example: init CSCI206 S20 61.
"""
coursedef = courses.all[config.course_objf_fmt.format(
course=course,
semester=semester,
section=section)]
coursename = config.course_name_fmt.format(
course=course,
semester=semester,
......@@ -25,17 +32,25 @@ def init(course, semester, section):
semester=semester,
section=section))
if click.confirm ("Initialize {} into {}?".format(coursename, lp)):
if confirm("Initialize {} into {}?".format(coursename, lp)):
labdir = os.path.join(lp, config.labs_subdir)
if os.path.exists(lp):
# prompt to wipe it.
warn("The path already exists!")
if warn_confirm("Would you like me to WIPE {} and re-create (this will DELETE ALL files)".format(lp)):
shutil.rmtree(lp)
success("Deleted {}".format(lp))
if not os.path.exists(lp):
# make the root folder, plus the labs subfolder
# ensure permissions are not globally readable
for d in [lp, labdir]:
os.makedirs(d)
os.chmod(d,mode=0o0770)
success("Created {}".format(lp))
else:
warn("The path already exists, skipping directory creation.")
warn("Skipping directory creation.")
with config.UserConfig() as uc:
uc.add_course(
......@@ -44,7 +59,7 @@ def init(course, semester, section):
gl = GitLab()
while not 'api_token' in uc.cfg or not gl.check_token():
api_token = click.prompt("Got to {} and create a personal access token with the \"api scope\" checked. Copy-paste the value here".format(
api_token = prompt("Got to {} and create a personal access token with the \"api scope\" checked. Copy-paste the value here".format(
config.gitlab_api_token_url
))
uc.add_string(
......@@ -53,3 +68,57 @@ def init(course, semester, section):
)
# needed to refresh the new token cached in the GitLab module
gl = GitLab()
# check if it already exists
p = gl.search_for_project(coursename)
if len(p) > 0:
for prj in p:
if warn_confirm(
"Gitlab repo \"{}\" already exists--should I DELETE it?".format(
prj['name_with_namespace'])):
if gl.remove_project(prj['id']):
success("Removed \"{}\"".format(prj['name_with_namespace']))
else:
error("Failed to remove \"{}\"".format(prj['name_with_namespace']))
# try again
p = gl.search_for_project(coursename)
if len(p) == 0:
repo = gl.create_project(coursename)
if 'id' in repo:
success("Created gitlab repo \"{}\" at {}".format(
repo['name_with_namespace'],
repo['web_url']))
# try again (2nd time)
p = gl.search_for_project(coursename)
if len(p) == 1:
repo = p[0]
# add members
for name in coursedef['instructors'] + coursedef['tas']:
uobj = gl.get_users(username=name)
if len(uobj)==0:
warn('No gitlab user "{}" found!.'.format(name))
elif len(uobj)==1:
mem = gl.add_member_to_project(
repo['id'],
uobj[0]['id'],
40)
if mem:
success("Added {}, {} ({}) to \"{}\"".format(
mem['name'],
mem['username'],
mem['id'],
repo['name_with_namespace']
))
else:
warn("Adding member {}, {} ({}) to \"{}\" failed.".format(
uobj[0]['name'],
uobj[0]['username'],
uobj[0]['id'],
repo['name_with_namespace']
))
else: warn('Cannot add member -- multiple gitlab accounts for {}: {}'.format(
name, uobj))
else:
warn("Skipped repo creation and member addition.")
from config.user import UserConfig
local_conf = "~/.labtool/config.json"
# course name for user's path and git repo
course_name_fmt = "{course}-{semester}-{section}"
# course name in course object (ignores section assuming shared sections)
course_objf_fmt = "{course}-{semester}"
local_home = "~/" + course_name_fmt
labs_subdir = "labs"
# define valid course strings
courses = ['CSCI206']
# shown to the user when asking for a Gitlab API token
gitlab_api_token_url = "https://gitlab.bucknell.edu/profile/personal_access_tokens"
gitlab_url = "https://gitlab.bucknell.edu/"
......
"""Various helpers to handle icons and colors
FUTURE: create style files.
"""
import click
def echo(*args, **kwargs):
click.echo(*args, **kwargs)
return click.echo(
#\uf0d0 = magic
click.style("\uf0d0 ", bold=True) + \
" ".join(map(str, args)), **kwargs)
def confirm(*args, **kwargs):
return click.confirm(
#\u2754 = question mark
click.style("\u2753 ", bold=True) + \
" ".join(map(str, args)), **kwargs)
def prompt(*args, **kwargs):
return click.prompt(*args, **kwargs)
def success(*args, **kwargs):
return click.echo(
#\u2713 = checkmark
click.style("\u2713 ", fg='green', bold=True) + \
" ".join(map(str, args)), **kwargs)
def error(*args, **kwargs):
click.echo(
click.style("ERROR", fg='red', bold=True) + \
": " + args[0], *args[1:], **kwargs)
return click.echo(
#\u274c = cross mark
click.style("\u274c ", fg='red', bold=True) + \
" ".join(map(str, args)), **kwargs)
def error_confirm(*args, **kwargs):
return click.confirm(
#\u274c = cross mark
click.style("\u274c ", fg='red', bold=True) + \
" ".join(map(str, args)), **kwargs)
def warn(*args, **kwargs):
click.echo(
click.style("WARNING", fg='yellow', bold=True) + \
": " + args[0], *args[1:], **kwargs)
return click.echo(
#\u26A0 = warning mark
click.style("\u26a0 ", fg='yellow', bold=True) + \
" ".join(map(str, args)), **kwargs)
def warn_confirm (*args, **kwargs):
return click.confirm(
#\u26A0 = warning mark
click.style("\u26a0 ", fg='yellow', bold=True) + \
" ".join(map(str, args)), **kwargs)
import click
import config
import courses
def course(c, param, value):
"ensure a valid course defined in config.__init__.py"
if value not in config.courses:
if value not in courses.courses:
raise click.BadParameter("Course must be one of {}.".format(
config.courses
courses.courses
))
return value
def section(c, param, value):
......
def load_rubrics(path):
"return a dict of rubric docs"
# TODO: this.
pass
# define valid course strings
courses = ['CSCI206']
# dictionary of all course info
all = {
'CSCI206':
{'S20':
{
'instructors': ['amm042','xmeng','jvs008']
'CSCI206-S20': {
'instructors': ['amm042','xmeng','jvs008'],
'tas': ['zp002'],
'rubrics': load_rubrics('labs/CSCI206/')
} # S20
} #CSCI206
} # END COURSE CSCI206-S20
}
__all__ = [all]
__all__ = [all, courses]
import requests
import requests as req
from pprint import pprint
import os.path
import platform
......@@ -13,41 +13,68 @@ class GitLab:
self.token = {'private_token': config.UserConfig.getConfig()['api_token']}
self.url = config.gitlab_url + config.gitlab_api
# wrap requests to always send token and check status_code
def get(self, url, **kwargs):
p = dict(self.token, **kwargs)
r = req.get(url, params=p)
if r.status_code > 299:
error("GET {} [{}]: {}.".format(
url, r.status_code, r.text))
return r
def post(self, url, **kwargs):
p = dict(self.token, **kwargs)
r = req.post(url, params=p)
if r.status_code > 299:
error("POST {} [{}]: {}.".format(
url, r.status_code, r.text))
return r
def delete(self, url, **kwargs):
p = dict(self.token, **kwargs)
r = req.delete(url, params=p)
if r.status_code > 299:
error("DELETE {} [{}]: {}.".format(
url, r.status_code, r.text))
return r
def get_user(self):
"get user account details"
r = requests.get(self.url+'user', params=self.token)
return r.json(), r.status_code
"get user account details (logged in user)"
r = self.get(self.url+'user')
if r.status_code > 299:
return None
return r.json()
def get_users(self, **kwargs):
"get user account details for OTHER users (not logged in user)"
r = self.get(self.url+'users', **kwargs)
if r.status_code > 299:
return None
return r.json()
def get_keys(self):
"get all ssh keys on gitlab"
r = requests.get(self.url+'user/keys', params=self.token)
return r.json(), r.status_code
r = self.get(self.url+'user/keys')
if r.status_code != 200:
return None
return r.json()
def check_token(self):
"Make a gitlab API call and check the return code"
u, rc = self.get_user()
if rc != 200:
warn("GitLab returned {}: {}. Check your private token.".format(rc, u))
return rc == 200
return self.get_user() != None
def put_key(self, name, key):
"put a new key on gitlab"
r = requests.post(
r = self.post(
self.url+'user/keys',
params=self.token,
data={
'title': name,
'key': key})
echo("result: {}".format(r.status_code))
if r.status_code != 201:
error(r.json()['error'])
return r.status_code
def ensure_key(self):
"ensure the local ssh key is on gitlab"
mykey = utils.ensure_id_rsa()
found = False
for key, rc in self.get_keys():
for key in self.get_keys():
if mykey==key['key']:
found = True
break
......@@ -57,11 +84,59 @@ class GitLab:
self.put_key(
name="{}@{}".format(
os.environ['USER'],
platform.node),
platform.node()),
key=mykey)
def search_for_project(self, name, owned=True):
"search gitlab for the project <name>"
r = self.get(
self.url+'projects',
owned=owned,
search=name)
if r.status_code != 200:
return []
else:
return r.json()
if __name__=='__main__':
with config.UserConfig() as uc:
gl = GitLab()
gl.ensure_key()
def create_project(self, name, **kwargs):
"""create a project on gitalb
docs: https://docs.gitlab.com/ee/api/projects.html#create-project
"""
r = self.post(
self.url+'projects',
name=name,
**kwargs)
# returns the project resource or error codes
return r.json()
def remove_project(self, project_id, **kwargs):
"""remove (DELETE) a project on gitalb
returns True on success
"""
r = self.delete(
self.url+'projects/'+str(project_id),
**kwargs)
if r.status_code != 202:
return False
return True
def list_members_of_project(self, project_id):
"return members of project"
r = self.get(self.url+'projects/'+str(project_id)+'/members')
if r.status_code != 200:
return []
else:
return r.json()
def add_member_to_project(self, project_id, user_id, access=10):
"""add user_id to project_id with access
10 => Guest access
20 => Reporter access
30 => Developer access
40 => Maintainer access
"""
r = self.post(
self.url+'projects/'+str(project_id)+'/members',
user_id=user_id,
access_level=access)
if r.status_code > 299:
return None
else:
return r.json()
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment