This guide covers more complex workflows and best practices.
GitLab Flow uses two main branches:
- develop: Feature development (students work here)
- main: Production code (released version)
mpd:
blatt02:
startercode:
url: git@gitlab.lrz.de:mpd/starter.git
fromBranch: startercode
toBranch: main
template: true
templateMessage: Initial Startercode
branches:
- name: main
protect: true # main is locked
- name: develop
default: true
release:
mergeRequest:
source: develop # PR from this branch
target: main # Into this branch
pipeline: true # Require passing CI
dockerImages:
- myapp/service- Students work on
developbranch - When ready for submission: create merge request
develop→main - If
pipeline: true, CI/CD must pass first - Merge to
main - Docker images automatically built if configured
glabs urls mpd blatt02 | xargs open # Open all repos
# Then navigate to Merge Requests tab in eachEnable container registry for automated builds:
release:
dockerImages:
- myapp/backend
- myapp/frontend- Creates container registry entries
- Enables Docker build configurations
- Sets up image namespaces
- Push Dockerfile(s) to repository
- Commit to
mainbranch - GitLab CI/CD builds images automatically
- Images available in project container registry
FROM python:3.11-slim
WORKDIR /app
COPY . /app
RUN pip install -r requirements.txt
CMD ["python", "app.py"]Use seeder when startercode alone isn't enough.
config/mpd.yaml:
seeder:
cmd: python3
args:
- /path/to/generate_assignment.py
- "%s"
name: Course Bot
email: bot@hm.edu
toBranch: maingenerate_assignment.py:
#!/usr/bin/env python3
import os
import sys
import subprocess
repo_path = sys.argv[1]
# Create directory structure
os.makedirs(f"{repo_path}/src", exist_ok=True)
os.makedirs(f"{repo_path}/tests", exist_ok=True)
# Generate files
with open(f"{repo_path}/README.md", "w") as f:
f.write("# Assignment\n\n")
with open(f"{repo_path}/src/main.py", "w") as f:
f.write("# TODO: Implement your solution here\n")
# Commit changes
os.chdir(repo_path)
subprocess.run(["git", "add", "."], check=True)
subprocess.run([
"git", "commit", "-m", "Initial commit from seeder",
"--author", "Course Bot <bot@hm.edu>"
], check=True)For verified commits, use GPG signing:
seeder:
cmd: python3
args:
- /path/to/seeder.py
- "%s"
name: Course Bot
email: bot@hm.edu
signKey: |
-----BEGIN PGP PRIVATE KEY BLOCK-----
[base64-encoded GPG key]
-----END PGP PRIVATE KEY BLOCK-----When running generate, you'll be prompted:
Passphrase for signing key is required. Please enter it now:
Create GPG key for signing:
# Generate key
gpg --full-generate-key
# Export key (needed for config)
gpg --armor --export-secret-key bot@hm.edu > private-key.asc
# View content for config file
cat private-key.ascStudents will see:
✓ Signed commit (verified badge in GitLab UI)
Manage multiple courses with shared configuration.
~/.glabs.yaml # Main config
~/courses/
mpd.yaml # Course 1
vss.yaml # Course 2
shared.yaml # Shared definitions (not in courses list)
gitlab:
host: https://gitlab.lrz.de
token: glpat-XXXXX
coursesfilepath: ~/courses
courses:
- mpd
- vssshared.yaml (NOT in courses list):
shared_students:
- alice@hm.edu
- bob@hm.edumpd.yaml:
mpd:
coursepath: teaching/mpd
students: ${shared_students} # Reference doesn't workWorkaround: Define in both course files or use course-level override per assignment.
Use glabs with shell scripts for complex workflows.
#!/bin/bash
course=$1
shift
assignments=$@
for assignment in $assignments; do
echo "Generating $course $assignment..."
glabs generate $course $assignment || echo "Failed: $assignment"
doneRun:
./batch_generate.sh mpd blatt01 blatt02 blatt03#!/bin/bash
course=$1
assignment=$2
# Clone all
glabs clone $course $assignment -f -p /tmp/work
# Run analysis
for repo in /tmp/work/*; do
student=$(basename "$repo")
commits=$(cd "$repo" && git rev-list --count HEAD)
lines=$(cd "$repo" && find . -name "*.py" | xargs wc -l | tail -1)
echo "$student: $commits commits, $lines lines"
done
# Generate report
glabs report $course $assignment --html > report.html1. Batch generate by group
glabs generate mpd blatt01 'team-a' # One group
glabs generate mpd blatt01 'team-b' # Another group2. Skip verbose logging
glabs generate mpd blatt01 # Default (no -v flag)3. Filter before operations
glabs clone mpd blatt01 'a.*' # Only names starting with 'a'Run during off-peak hours:
# Late night: generate all
glabs generate mpd blatt01
# Next morning: protect and report
glabs protect mpd blatt01
glabs report mpd blatt01 --htmlRun glabs operations on schedule.
crontab -e:
# Generate reports daily at 6 AM
0 6 * * * cd /home/instructor && glabs report mpd blatt01 --html > reports/blatt01-$(date +\%Y\%m\%d).htmlcrontab -e:
# Generate repos on day 1
0 8 1 * * glabs generate mpd blatt01
0 9 1 * * glabs generate mpd blatt02
# Protect branches on submission day
0 18 3 * * glabs protect mpd blatt01
0 18 10 * * glabs protect mpd blatt02
# Generate reports daily during submission period
0 23 * * * glabs report mpd blatt01 --htmlUse glabs output in GitLab CI/CD pipelines.
# .gitlab-ci.yml
deploy_stage:
script:
# Get list of student repos
- glabs urls mpd blatt01 > repo_urls.txt
# Deploy to each
- |
while read url; do
git clone $url && cd submission && ./deploy.sh
done < repo_urls.txttest_submissions:
script:
# Clone all submissions
- glabs clone mpd blatt01 -f -p /tmp/submissions
# Run tests
- |
for repo in /tmp/submissions/*; do
cd $repo && ./run_tests.sh || echo "Failed: $repo"
doneglabs check <course> # Before any operation
glabs show <course> <assignment> # Before risky operationsCreate test students/groups first:
students:
- test1@hm.edu
- test2@hm.edu
- alice@hm.edu # Real studentsThen test with one first:
glabs generate mpd blatt01 test1# Save current state
glabs report mpd blatt01 --json > backup.json
# Then run operation
glabs update mpd blatt01coursepath: teaching/2024ss/mpd # Include year/semester
assignmentpath: blatt-01-datastructures # Describe contentstartercode:
toBranch: main
protectToBranch: true # Always lock production# mpd.yaml
# Course: Algorithms and Data Structures
# Semester: 2024 Summer Semester
# Instructor: Prof. Mueller
# GitLab Group: teaching/2024ss/mpd
mpd:
coursepath: teaching/2024ss/mpd
# ... rest of configgit init ~/courses
git add mpd.yaml vss.yaml
git commit -m "Course config 2024ss"
git remote add origin git@gitlab.lrz.de:teaching/configs.git
git pushIf seeder script fails with merge conflicts:
- Check if students have existing work
- Use
glabs deleteand regenerate if needed - Or resolve conflicts manually in each repo
If glabs becomes slow:
- Check network (GitLab API latency)
- Use verbose mode to identify bottlenecks:
glabs -v generate ... - Filter operations:
glabs protect mpd blatt01 'alice'(one student) - Consider chunking large operations
With 100+ students, you may hit rate limits:
- Spread operations over time
- Use different API token
- Contact GitLab admin for higher limits
- Batch operations: generate morning, protect afternoon
- Full help:
glabs --helpandglabs <command> --help - See actual config:
glabs show <course> <assignment> - Debug mode:
glabs -v <command> ... - Report issues: Check the GitHub repository