Extend documentation
This commit is contained in:
parent
0e555d9e8a
commit
f356b1e65f
9 changed files with 160 additions and 98 deletions
63
doc/notes.md
63
doc/notes.md
|
@ -1,63 +0,0 @@
|
||||||
# Programmieren [1,2] Gitlab
|
|
||||||
|
|
||||||
- https://docs.gitlab.com/omnibus/README.html
|
|
||||||
|
|
||||||
## Authentication
|
|
||||||
|
|
||||||
- use GITZ LDAP for login
|
|
||||||
- not allow "create new repo"
|
|
||||||
|
|
||||||
|
|
||||||
## Structure
|
|
||||||
|
|
||||||
- main repo
|
|
||||||
+ publish example solutions
|
|
||||||
+ CI config for checkstyle
|
|
||||||
+ Protected Runner for JPlag
|
|
||||||
+ restrict access to branches with example solutions
|
|
||||||
|
|
||||||
- student repos
|
|
||||||
+ forked from main repo
|
|
||||||
+ one repo per student
|
|
||||||
+ student has *Developer* Access
|
|
||||||
+ *tutors* group has *Master* access
|
|
||||||
+ students can request access (Abgabepartner)
|
|
||||||
+ *tutors* can grant access
|
|
||||||
|
|
||||||
## Checkstyle
|
|
||||||
|
|
||||||
- GitLab CI
|
|
||||||
- [Docker](https://docs.gitlab.com/omnibus/docker/README.html)container
|
|
||||||
- [Shared Runner](https://docs.gitlab.com/ce/ci/runners/README.html)
|
|
||||||
- restrict Container to [checkstyle](http://checkstyle.sourceforge.net/)
|
|
||||||
- disable internet access for container
|
|
||||||
|
|
||||||
## JPlag
|
|
||||||
|
|
||||||
- Deadline [at,cron]job or schedule via gitlab
|
|
||||||
- triggers [Protected Runner](https://docs.gitlab.com/ee/ci/runners/README.html#protected-runners)
|
|
||||||
- creates automatic protected TAG in each repo
|
|
||||||
- checks out TAG from all repos into /tmp and runs [JPlag](https://jplag.ipd.kit.edu/)
|
|
||||||
- replace with MOSS? https://github.com/soachishti/moss.py
|
|
||||||
- deploy key in each repo
|
|
||||||
|
|
||||||
## (optional) sync script
|
|
||||||
|
|
||||||
- (one-way) sync students and groups from [Stud.IP REST API](http://docs.studip.de/develop/Entwickler/RESTAPI) to [Gitlab REST API](https://docs.gitlab.com/ce/api/)
|
|
||||||
|
|
||||||
# Replicate (TODO: ansible playbook)
|
|
||||||
|
|
||||||
- install gitlab
|
|
||||||
- install docker
|
|
||||||
- copy gitlab.rb
|
|
||||||
- partially protected
|
|
||||||
- default project limit = 0
|
|
||||||
- shared runner for checkstyle
|
|
||||||
|
|
||||||
- protected runner for
|
|
||||||
|
|
||||||
+ setting protected tags
|
|
||||||
+ running jplag
|
|
||||||
|
|
||||||
- script for creating repos and groups
|
|
||||||
- SSH deploy key
|
|
|
@ -4,9 +4,8 @@
|
||||||
# You can set these variables from the command line.
|
# You can set these variables from the command line.
|
||||||
SPHINXOPTS =
|
SPHINXOPTS =
|
||||||
SPHINXBUILD = sphinx-build
|
SPHINXBUILD = sphinx-build
|
||||||
SPHINXPROJ = abgabesystem
|
SOURCEDIR = source
|
||||||
SOURCEDIR = .
|
BUILDDIR = build
|
||||||
BUILDDIR = _build
|
|
||||||
|
|
||||||
# Put it first so that "make" without argument is like "make help".
|
# Put it first so that "make" without argument is like "make help".
|
||||||
help:
|
help:
|
|
@ -7,9 +7,8 @@ REM Command file for Sphinx documentation
|
||||||
if "%SPHINXBUILD%" == "" (
|
if "%SPHINXBUILD%" == "" (
|
||||||
set SPHINXBUILD=sphinx-build
|
set SPHINXBUILD=sphinx-build
|
||||||
)
|
)
|
||||||
set SOURCEDIR=.
|
set SOURCEDIR=source
|
||||||
set BUILDDIR=_build
|
set BUILDDIR=build
|
||||||
set SPHINXPROJ=abgabesystem
|
|
||||||
|
|
||||||
if "%1" == "" goto help
|
if "%1" == "" goto help
|
||||||
|
|
|
@ -12,9 +12,9 @@
|
||||||
# add these directories to sys.path here. If the directory is relative to the
|
# add these directories to sys.path here. If the directory is relative to the
|
||||||
# documentation root, use os.path.abspath to make it absolute, like shown here.
|
# documentation root, use os.path.abspath to make it absolute, like shown here.
|
||||||
#
|
#
|
||||||
import os
|
# import os
|
||||||
import sys
|
# import sys
|
||||||
sys.path.insert(0, os.path.abspath('../src'))
|
# sys.path.insert(0, os.path.abspath('.'))
|
||||||
|
|
||||||
|
|
||||||
# -- Project information -----------------------------------------------------
|
# -- Project information -----------------------------------------------------
|
||||||
|
@ -26,7 +26,7 @@ author = 'Tim Schubert'
|
||||||
# The short X.Y version
|
# The short X.Y version
|
||||||
version = ''
|
version = ''
|
||||||
# The full version, including alpha/beta/rc tags
|
# The full version, including alpha/beta/rc tags
|
||||||
release = ''
|
release = '1.0'
|
||||||
|
|
||||||
|
|
||||||
# -- General configuration ---------------------------------------------------
|
# -- General configuration ---------------------------------------------------
|
||||||
|
@ -42,7 +42,10 @@ extensions = [
|
||||||
'sphinx.ext.autodoc',
|
'sphinx.ext.autodoc',
|
||||||
'sphinx.ext.doctest',
|
'sphinx.ext.doctest',
|
||||||
'sphinx.ext.intersphinx',
|
'sphinx.ext.intersphinx',
|
||||||
|
'sphinx.ext.todo',
|
||||||
'sphinx.ext.coverage',
|
'sphinx.ext.coverage',
|
||||||
|
'sphinx.ext.viewcode',
|
||||||
|
'sphinx.ext.githubpages',
|
||||||
]
|
]
|
||||||
|
|
||||||
# Add any paths that contain templates here, relative to this directory.
|
# Add any paths that contain templates here, relative to this directory.
|
||||||
|
@ -66,11 +69,11 @@ language = None
|
||||||
|
|
||||||
# List of patterns, relative to source directory, that match files and
|
# List of patterns, relative to source directory, that match files and
|
||||||
# directories to ignore when looking for source files.
|
# directories to ignore when looking for source files.
|
||||||
# This pattern also affects html_static_path and html_extra_path .
|
# This pattern also affects html_static_path and html_extra_path.
|
||||||
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
|
exclude_patterns = []
|
||||||
|
|
||||||
# The name of the Pygments (syntax highlighting) style to use.
|
# The name of the Pygments (syntax highlighting) style to use.
|
||||||
pygments_style = 'sphinx'
|
pygments_style = None
|
||||||
|
|
||||||
|
|
||||||
# -- Options for HTML output -------------------------------------------------
|
# -- Options for HTML output -------------------------------------------------
|
||||||
|
@ -159,9 +162,32 @@ texinfo_documents = [
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
# -- Options for Epub output -------------------------------------------------
|
||||||
|
|
||||||
|
# Bibliographic Dublin Core info.
|
||||||
|
epub_title = project
|
||||||
|
|
||||||
|
# The unique identifier of the text. This can be a ISBN number
|
||||||
|
# or the project homepage.
|
||||||
|
#
|
||||||
|
# epub_identifier = ''
|
||||||
|
|
||||||
|
# A unique identification for the text.
|
||||||
|
#
|
||||||
|
# epub_uid = ''
|
||||||
|
|
||||||
|
# A list of files that should not be packed into the epub file.
|
||||||
|
epub_exclude_files = ['search.html']
|
||||||
|
|
||||||
|
|
||||||
# -- Extension configuration -------------------------------------------------
|
# -- Extension configuration -------------------------------------------------
|
||||||
|
|
||||||
# -- Options for intersphinx extension ---------------------------------------
|
# -- Options for intersphinx extension ---------------------------------------
|
||||||
|
|
||||||
# Example configuration for intersphinx: refer to the Python standard library.
|
# Example configuration for intersphinx: refer to the Python standard library.
|
||||||
intersphinx_mapping = {'https://docs.python.org/': None}
|
intersphinx_mapping = {'https://docs.python.org/': None}
|
||||||
|
|
||||||
|
# -- Options for todo extension ----------------------------------------------
|
||||||
|
|
||||||
|
# If true, `todo` and `todoList` produce output, else they produce nothing.
|
||||||
|
todo_include_todos = True
|
|
@ -1,5 +1,5 @@
|
||||||
.. abgabesystem documentation master file, created by
|
.. abgabesystem documentation master file, created by
|
||||||
sphinx-quickstart on Fri Jun 1 13:35:35 2018.
|
sphinx-quickstart on Fri Sep 28 14:59:39 2018.
|
||||||
You can adapt this file completely to your liking, but it should at least
|
You can adapt this file completely to your liking, but it should at least
|
||||||
contain the root `toctree` directive.
|
contain the root `toctree` directive.
|
||||||
|
|
||||||
|
@ -18,18 +18,3 @@ Indices and tables
|
||||||
* :ref:`genindex`
|
* :ref:`genindex`
|
||||||
* :ref:`modindex`
|
* :ref:`modindex`
|
||||||
* :ref:`search`
|
* :ref:`search`
|
||||||
|
|
||||||
.. automodule:: abgabesystem
|
|
||||||
:members:
|
|
||||||
|
|
||||||
.. automodule:: abgabesystem.projects
|
|
||||||
:members:
|
|
||||||
|
|
||||||
.. automodule:: abgabesystem.students
|
|
||||||
:members:
|
|
||||||
|
|
||||||
.. automodule:: abgabesystem.commands
|
|
||||||
:members:
|
|
||||||
|
|
||||||
.. autoclass:: abgabesystem.students.Student
|
|
||||||
:members:
|
|
|
@ -9,6 +9,10 @@ from gitlab.exceptions import GitlabCreateError, GitlabGetError
|
||||||
|
|
||||||
def enroll_students(gl, args):
|
def enroll_students(gl, args):
|
||||||
"""Creates Gitlab users from exported students list
|
"""Creates Gitlab users from exported students list
|
||||||
|
|
||||||
|
Args:
|
||||||
|
gl: API
|
||||||
|
args: command line arguments
|
||||||
"""
|
"""
|
||||||
|
|
||||||
student_group = get_student_group(gl, args.course)
|
student_group = get_student_group(gl, args.course)
|
||||||
|
@ -25,6 +29,10 @@ def enroll_students(gl, args):
|
||||||
|
|
||||||
def projects(gl, args):
|
def projects(gl, args):
|
||||||
"""Creates the projects for all course participants
|
"""Creates the projects for all course participants
|
||||||
|
|
||||||
|
Args:
|
||||||
|
gl: API
|
||||||
|
args: command line arguments
|
||||||
"""
|
"""
|
||||||
course = None
|
course = None
|
||||||
for g in gl.groups.list(search=args.course):
|
for g in gl.groups.list(search=args.course):
|
||||||
|
@ -39,7 +47,12 @@ def projects(gl, args):
|
||||||
|
|
||||||
|
|
||||||
def deadline(gl, args):
|
def deadline(gl, args):
|
||||||
"""Checks deadlines for course and triggers deadline if it is reached"""
|
"""Checks deadlines for course and triggers deadline if it is reached
|
||||||
|
|
||||||
|
Args:
|
||||||
|
gl: API
|
||||||
|
args: command line arguments
|
||||||
|
"""
|
||||||
|
|
||||||
deadline_name = args.tag_name
|
deadline_name = args.tag_name
|
||||||
try:
|
try:
|
||||||
|
@ -63,6 +76,10 @@ def deadline(gl, args):
|
||||||
|
|
||||||
def plagiates(gl, args):
|
def plagiates(gl, args):
|
||||||
"""Runs the plagiarism checker (JPlag) for the solutions with a certain tag
|
"""Runs the plagiarism checker (JPlag) for the solutions with a certain tag
|
||||||
|
|
||||||
|
Args:
|
||||||
|
gl: API
|
||||||
|
args: command line arguments
|
||||||
"""
|
"""
|
||||||
|
|
||||||
solutions_dir = 'input'
|
solutions_dir = 'input'
|
||||||
|
@ -90,6 +107,10 @@ def plagiates(gl, args):
|
||||||
|
|
||||||
def course(gl, args):
|
def course(gl, args):
|
||||||
"""Creates the group for the course
|
"""Creates the group for the course
|
||||||
|
|
||||||
|
Args:
|
||||||
|
gl: API
|
||||||
|
args: command line arguments
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
gl.groups.create({
|
gl.groups.create({
|
||||||
|
|
|
@ -2,10 +2,21 @@ import logging as log
|
||||||
|
|
||||||
|
|
||||||
class InvalidCourse(Exception):
|
class InvalidCourse(Exception):
|
||||||
|
"""Raised if the selected course is invalid.
|
||||||
|
"""
|
||||||
|
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
def create_subgroup(gl, name, parent_group):
|
def create_subgroup(gl, name, parent_group):
|
||||||
|
"""Creates a group with `parent_group` as its parent.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
gl: gitlab API object
|
||||||
|
name: name of the group to be created
|
||||||
|
parent_group: parent group of the created group
|
||||||
|
"""
|
||||||
|
|
||||||
log.info("Creating subgroup %s in group %s" % (name, parent_group.name))
|
log.info("Creating subgroup %s in group %s" % (name, parent_group.name))
|
||||||
return gl.groups.create({
|
return gl.groups.create({
|
||||||
"name": name,
|
"name": name,
|
||||||
|
@ -23,6 +34,15 @@ def create_solutions_group(gl, parent_group):
|
||||||
|
|
||||||
|
|
||||||
def create_course(gl, course_name):
|
def create_course(gl, course_name):
|
||||||
|
"""Creates a complete course as required by the `abgabesystem` including
|
||||||
|
the students and solutions groups.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
gl: gitlab API object
|
||||||
|
course_name: name of the course, may contain any characters from
|
||||||
|
[0-9,a-z,A-Z,_, ]
|
||||||
|
"""
|
||||||
|
|
||||||
group = gl.groups.create({
|
group = gl.groups.create({
|
||||||
"name": course_name,
|
"name": course_name,
|
||||||
"path": course_name.lower().replace(" ", "_"),
|
"path": course_name.lower().replace(" ", "_"),
|
||||||
|
|
|
@ -10,7 +10,12 @@ def create_tag(project, tag, ref):
|
||||||
"""Creates protected tag on ref
|
"""Creates protected tag on ref
|
||||||
|
|
||||||
The tag is used by the abgabesystem to mark the state of a solution at the
|
The tag is used by the abgabesystem to mark the state of a solution at the
|
||||||
deadline
|
deadline.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
project: GIT repository to create the tag in
|
||||||
|
tag: name of the tag to be created
|
||||||
|
ref: name of the red (branch / commit) to create the new tag on
|
||||||
"""
|
"""
|
||||||
|
|
||||||
print('Project %s. Creating tag %s' % (project.path, tag))
|
print('Project %s. Creating tag %s' % (project.path, tag))
|
||||||
|
@ -21,9 +26,17 @@ def create_tag(project, tag, ref):
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def fork_reference(gl, reference, namespace, deploy_key):
|
def fork_reference(gl, reference, namespace, deploy_key):
|
||||||
"""Create fork of solutions for student.
|
"""Create fork of solutions for student.
|
||||||
|
|
||||||
|
Returns the created project.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
gl: gitlab API object
|
||||||
|
reference: project to fork from
|
||||||
|
namespace: namespace to place the created project into
|
||||||
|
deploy_key: will be used by the abgabesystem to access the created
|
||||||
|
project
|
||||||
"""
|
"""
|
||||||
|
|
||||||
fork = reference.forks.create({
|
fork = reference.forks.create({
|
||||||
|
@ -46,6 +59,14 @@ def fork_reference(gl, reference, namespace, deploy_key):
|
||||||
def create_project(gl, group, user, reference, deploy_key):
|
def create_project(gl, group, user, reference, deploy_key):
|
||||||
"""Creates a namespace (subgroup) and forks the project with
|
"""Creates a namespace (subgroup) and forks the project with
|
||||||
the reference solutions into that namespace
|
the reference solutions into that namespace
|
||||||
|
|
||||||
|
Args:
|
||||||
|
gl: Gitlab API object
|
||||||
|
group: project will be created in the namespace of this group
|
||||||
|
user: user to add to the project as a developer
|
||||||
|
reference: project to fork the new project from
|
||||||
|
deploy_key: deploy key used by the `abgabesystem` to access the new
|
||||||
|
project
|
||||||
"""
|
"""
|
||||||
|
|
||||||
subgroup = None
|
subgroup = None
|
||||||
|
@ -79,6 +100,14 @@ def create_project(gl, group, user, reference, deploy_key):
|
||||||
|
|
||||||
|
|
||||||
def create_reference_solution(gl, namespace):
|
def create_reference_solution(gl, namespace):
|
||||||
|
"""Creates a new project for the reference solutions.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
gl: gitlab API object
|
||||||
|
namespace: namespace to create the project in (that of the solutions for the course)
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
reference_project = gl.projects.create({
|
reference_project = gl.projects.create({
|
||||||
'name': 'solutions',
|
'name': 'solutions',
|
||||||
'namespace_id': namespace,
|
'namespace_id': namespace,
|
||||||
|
@ -100,7 +129,12 @@ def create_reference_solution(gl, namespace):
|
||||||
|
|
||||||
|
|
||||||
def setup_projects(gl, course, deploy_key):
|
def setup_projects(gl, course, deploy_key):
|
||||||
"""Sets up the internal structure for the group for use with the course
|
"""Sets up the internal structure for the group for use with the course.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
gl: gitlab API object
|
||||||
|
course: course to set up projects for
|
||||||
|
deploy_key: will be used to access the solutions from the abgabesystem
|
||||||
"""
|
"""
|
||||||
|
|
||||||
solutions = None
|
solutions = None
|
||||||
|
|
|
@ -5,10 +5,17 @@ from gitlab import GUEST_ACCESS
|
||||||
|
|
||||||
|
|
||||||
class MissingStudentsGroup(Exception):
|
class MissingStudentsGroup(Exception):
|
||||||
|
"""Raised if a the group for the students has not already been created
|
||||||
|
inside the course.
|
||||||
|
"""
|
||||||
|
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class MissingCourseGroup(Exception):
|
class MissingCourseGroup(Exception):
|
||||||
|
"""Raised if the group for the course is missing.
|
||||||
|
"""
|
||||||
|
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
@ -18,6 +25,12 @@ class Student():
|
||||||
Students are read from the CSV file that was exported from Stud.IP.
|
Students are read from the CSV file that was exported from Stud.IP.
|
||||||
For each user, a dummy LDAP user is created in Gitlab.
|
For each user, a dummy LDAP user is created in Gitlab.
|
||||||
Upon the first login Gitlab fetches the complete user using LDAP.
|
Upon the first login Gitlab fetches the complete user using LDAP.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
user: user name
|
||||||
|
mail: mail address of the user
|
||||||
|
name: full name of the user
|
||||||
|
group: tutorial group of the user
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, user, mail, name, group):
|
def __init__(self, user, mail, name, group):
|
||||||
|
@ -27,7 +40,11 @@ class Student():
|
||||||
self.group = group
|
self.group = group
|
||||||
|
|
||||||
def from_csv(csvfile):
|
def from_csv(csvfile):
|
||||||
"""Creates an iterable containing the users"""
|
"""Creates an iterable containing the users
|
||||||
|
|
||||||
|
Args:
|
||||||
|
csvfile: CSV file from Stud.IP (latin-1)
|
||||||
|
"""
|
||||||
reader = csv.DictReader(csvfile, delimiter=';', quotechar='"')
|
reader = csv.DictReader(csvfile, delimiter=';', quotechar='"')
|
||||||
|
|
||||||
for line in reader:
|
for line in reader:
|
||||||
|
@ -36,7 +53,12 @@ class Student():
|
||||||
|
|
||||||
|
|
||||||
def get_students_csv(gl, students_csv):
|
def get_students_csv(gl, students_csv):
|
||||||
"""Returns already existing GitLab users for students from provided CSV file that have an account.
|
"""Returns already existing GitLab users for students from provided CSV
|
||||||
|
file that have an account.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
gl: Gitlab API object
|
||||||
|
students_csv: CSV file from Stud.IP
|
||||||
"""
|
"""
|
||||||
|
|
||||||
for student in Student.from_csv(students_csv):
|
for student in Student.from_csv(students_csv):
|
||||||
|
@ -47,6 +69,10 @@ def get_students_csv(gl, students_csv):
|
||||||
|
|
||||||
def enrolled_students(gl, course):
|
def enrolled_students(gl, course):
|
||||||
"""Returns the students enrolled in the course
|
"""Returns the students enrolled in the course
|
||||||
|
|
||||||
|
Args:
|
||||||
|
gl: Gitlab API object
|
||||||
|
course: course the students are enrolled in
|
||||||
"""
|
"""
|
||||||
|
|
||||||
students = None
|
students = None
|
||||||
|
@ -66,6 +92,12 @@ def enrolled_students(gl, course):
|
||||||
def create_user(gl, student, ldap_base, ldap_provider):
|
def create_user(gl, student, ldap_base, ldap_provider):
|
||||||
"""Creates a GitLab user account student.
|
"""Creates a GitLab user account student.
|
||||||
Requires admin privileges.
|
Requires admin privileges.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
gl: Gitlab API object
|
||||||
|
student: student to create user for
|
||||||
|
ldap_base: the search base string for the LDAP query
|
||||||
|
ldap_provider: LDAP provider configured for Gitlab (usually `main`)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
user = gl.users.create({
|
user = gl.users.create({
|
||||||
|
@ -84,6 +116,10 @@ def create_user(gl, student, ldap_base, ldap_provider):
|
||||||
|
|
||||||
def get_student_group(gl, course_name):
|
def get_student_group(gl, course_name):
|
||||||
"""Gets the `students` subgroup for the course
|
"""Gets the `students` subgroup for the course
|
||||||
|
|
||||||
|
Args:
|
||||||
|
gl: Gitlab API objects
|
||||||
|
course_name: name of the course
|
||||||
"""
|
"""
|
||||||
|
|
||||||
course = None
|
course = None
|
||||||
|
@ -108,6 +144,11 @@ def get_student_group(gl, course_name):
|
||||||
|
|
||||||
def enroll_student(gl, user, subgroup):
|
def enroll_student(gl, user, subgroup):
|
||||||
"""Adds a student to the course
|
"""Adds a student to the course
|
||||||
|
|
||||||
|
Args:
|
||||||
|
gl: Gitlab API object
|
||||||
|
user: user to add to the course
|
||||||
|
subgroup: student will become member of this group
|
||||||
"""
|
"""
|
||||||
|
|
||||||
subgroup.members.create({
|
subgroup.members.create({
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue