Newer
Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
#!/usr/bin/python3
import time
import argparse,getpass,re
import sys,subprocess,os
import json,urllib.request
import gitlab
# Parse command-line arguments.
parser = argparse.ArgumentParser(description="This script is used to create student repositories.")
parser.add_argument('group_name', help="The name of the Gitlab group to create projects in.")
parser.add_argument('--token-file', default="/dev/stdin",
help="Path to file containing your Gitlab private token. Default is to read from standard input.")
parser.add_argument('--add-students', action='store_true',
help="By default, students will not be added to their repos. Set this option to add them, which will email them too.")
students_arg_group = parser.add_mutually_exclusive_group()
students_arg_group.add_argument('--classlist', nargs=1, help="Path to your course's .classlist file on the student.cs Linux servers.")
students_arg_group.add_argument('--students', help="A comma separated list of student Quest IDs. Create repositories for these students only.")
args = parser.parse_args()
# save command line argument inputs in variables
group_name = args.group_name
token_file = args.token_file
add_students = args.add_students
# Read private token from keyboard or from file
gitlab.set_private_token(token_file)
# If adding students, read gitlab session cookie
if add_students:
print("You want students to be added to their project/repository.")
print("This script adds students by interfacing with the git.uwaterloo.ca website directly.")
print("Please login to https://git.uwaterloo.ca and enter your _gitlab_session cookie from git.uwaterloo.ca below.")
gitlab_session_cookie = getpass.getpass("git.uwaterloo.ca _gitlab_session cookie value:")
print(gitlab_session_cookie)
# Students we will create repositories for
students = []
if args.students:
students = list(map(lambda s:s.strip(), args.students.split(',')))
students = list(filter(lambda s: s and not s.isspace(), students))
elif args.classlist:
classlist_regex = re.compile('^[0-9]{8}:([a-z0-9]+):')
classlist_file = args.classlist[0]
for line in open(classlist_file, 'r'):
match = classlist_regex.match(line)
if match != None:
userid = match.group(1)
userid = userid[0:8]
students.append(userid)
# Create a hash mapping student usernames to the id of their project/repo
# This should be empty. If not, it means some projects have already
# been created.
group_id = gitlab.get_group_id(group_name)
group_data = gitlab.request("groups/%d" % group_id)
projects_data = group_data['projects']
project_ids = {}
for project in projects_data:
username = project['ssh_url_to_repo'].rsplit('/',1)[-1][:-4]
project_ids[username] = project['id']
# Begin processing students
print("Processing %d total students." % len(students))
for student in students:
print(os.linesep)
print('-' * 60)
print("> Processing %s" % student)
# Create project/repo for students who do not have one yet.
if student not in project_ids:
# Student doesn't have a project/repo yet. Create it
print("> %s doesn't have a project/repo yet. Creating it now." % student)
new_project = gitlab.request('projects', post_hash={'name':student, 'namespace_id':group_id, 'visibility_level':0})
project_ids[student] = new_project['id']
print("> Created new project with id %d" % new_project['id'])
else:
print("> %s already has a project (id %d). Not creating it again." % (student, project_ids[student]))
# Create master branch if it doesn't exist yet
existing_branches = gitlab.request('projects/%d/repository/branches' % project_ids[student])
master_branch_exists = False
for branch in existing_branches:
if branch['name'] == 'master':
master_branch_exists = True
if not master_branch_exists:
print("> master branch doesn't exist for %s. Creating it." % student)
gitlab.request('projects/%d/repository/files' % project_ids[student],
post_hash={'file_path':".gitignore", 'branch_name':"master", 'content':"#\n", 'commit_message':"Initial commit to create master branch"})
# Wait for master branch to become protected. Gitlab seems to have a delay on protecting the
# master branch when it's created.
while True:
master_branch_info = gitlab.request('/projects/%d/repository/branches/master' % project_ids[student], quit_on_error=False)
if master_branch_info and master_branch_info['protected']:
print("> Newly created master branch has become protected.")
break
print("> Waiting for Gitlab to make newly created master branch protected.")
time.sleep(1) # Don't spam Gitlab website
else:
print("> master branch already exists for %s. Not creating it." % student)
# Turn off master branch protection (on by default). At this point
# in the code, we have created master branch if it doesn't exist.
# So master branch should exist. Also, if master is already unprotected,
# then this operation does nothing (it's idempotent).
print("> Unprotecting master branch.")
gitlab.request('/projects/%d/repository/branches/master/unprotect' % project_ids[student], http_method='PUT')
# The repo is now set up with an unprotected master branch.
# Do email invitation if user wants to do that.
if add_students:
print("> Adding student to project/repository.")
# Step 1: Go to project_members web page and get authenticity token.
print("> Getting authenticity token from project_members page.")
authenticity_token = None
req = urllib.request.Request("https://git.uwaterloo.ca/%s/%s/project_members" % (group_name, student),
headers={'Cookie': "_gitlab_session=%s"%gitlab_session_cookie})
with urllib.request.urlopen(req) as f:
project_members_html = f.read().decode('utf-8')
for line in project_members_html.splitlines():
match = re.search(r'<input name="authenticity_token" type="hidden" value="([^"]+)" />', line)
if match:
authenticity_token = match.group(1)
break
# Step 2: Make the post request to invite by email
if authenticity_token:
student_email = "%s@uwaterloo.ca" % student
print("> Got authenticity token.")
print("> Sending invitation email to %s" % student_email)
post_data = urllib.parse.urlencode({'authenticity_token':authenticity_token,'user_ids':student_email,'access_level':30}).encode('ascii')
add_student_post = urllib.request.Request("https://git.uwaterloo.ca/%s/%s/project_members" % (group_name, student),
headers={'Cookie': "_gitlab_session=%s"%gitlab_session_cookie},
data=post_data, method='POST')
urllib.request.urlopen(add_student_post)
else:
print("> Could not get add student %s to repo!" % student)
print("> Done processing %s." % student)
time.sleep(3) # Put in a bit of a delay so that git.uwaterloo.ca isn't hammered