Walkthrough: A Maven Assignment

Set up the smallest graded Maven assignment, from divekit init to a report with Surefire and PMD results.

Tested with divekit v2.49.1 and the maven plugin suite (builder v1.0.3, maven/pmd v2.1.0, maven/surefire v2.1.0).

This walkthrough builds one assignment with one Java class, hidden tests, and a PMD check. At the end, every student has a work repository to push to, and every push produces a report page with the test and PMD results. You do not write a .gitlab-ci.yml yourself; Divekit generates both pipelines.

You need a GitLab group for the course, its numeric group ID, and a host alias for that GitLab instance (see Installation). The examples use the group ID 5551, a distribution named greeting, and the students user12 and user13.

1. Create The Origin

mkdir greeting && cd greeting
git init
divekit init

Answer the prompts:

PromptAnswer
Distribution Namegreeting
Enable Evaluation Pipeline?Yes
Main Group ID5551
Set Member Permissionsdeveloper, so students can push
Select Remote Hostthe host alias of your GitLab
Evaluation Pipeline Group IDleave empty

divekit init writes .divekit/distributions/greeting/config.json with the evaluation pipeline enabled.

2. Add The Assignment Content

The origin holds both what students see and what only the evaluation sees. File and directory suffixes decide where each path ends up:

greeting/
├── TASK.md
├── pom.xml
├── pmd-ruleset.xml
└── src/
    ├── main/java/de/example_overwrite_to_eval/
    │   └── Greeter.java
    └── test/java/de/example/
        └── GreeterTest_evalrepo.java
  • TASK.md, pom.xml, and pmd-ruleset.xml go to both repositories.
  • de/example_overwrite_to_eval/ becomes de/example/ in the work repository. On every evaluation run, the student’s version replaces the one in the eval repository.
  • GreeterTest_evalrepo.java becomes GreeterTest.java and exists only in the eval repository, so students never see the tests or the expected value.

The class students complete:

package de.example;

public class Greeter {

    public String greet() {
        return null;
    }
}

The hidden test carries the solution value:

package de.example;

import static org.junit.jupiter.api.Assertions.assertEquals;

import org.junit.jupiter.api.Test;

class GreeterTest {

    @Test
    void greetReturnsTheExpectedGreeting() {
        assertEquals("Hello, Divekit!", new Greeter().greet());
    }
}

The pom.xml declares JUnit, Surefire, and the PMD plugin with the course ruleset. The PMD plugin of the suite runs mvn pmd:pmd, so the rules in the pom.xml decide what is checked:

<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>de.example</groupId>
    <artifactId>greeting</artifactId>
    <version>1.0.0</version>

    <properties>
        <maven.compiler.release>21</maven.compiler.release>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter</artifactId>
            <version>5.11.4</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>3.5.2</version>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-pmd-plugin</artifactId>
                <version>3.26.0</version>
                <configuration>
                    <rulesets>
                        <ruleset>pmd-ruleset.xml</ruleset>
                    </rulesets>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

pmd-ruleset.xml lists the rules of the course:

<?xml version="1.0"?>
<ruleset name="course"
    xmlns="http://pmd.sourceforge.net/ruleset/2.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://pmd.sourceforge.net/ruleset/2.0.0 https://pmd.sourceforge.io/ruleset_2_0_0.xsd">
    <description>Rules checked for this assignment.</description>
    <rule ref="category/java/bestpractices.xml/UnusedLocalVariable"/>
    <rule ref="category/java/codestyle.xml/MethodNamingConventions"/>
    <rule ref="category/java/errorprone.xml/EmptyCatchBlock"/>
</ruleset>

3. Add The Students

printf 'user12\nuser13\n' > members.txt
divekit members -d greeting --members-file members.txt

Each line becomes one repository pair. Divekit stores the members file under ~/.divekit/members/ and points members.path in config.json at it.

4. Enable The Maven Suite

divekit plugin install
divekit plugin add maven
divekit plugin disable maven/jacoco-pit

plugin install writes the pipeline templates into .divekit/eval-pipeline/. Run it before plugin add: the suite’s jobs are composed into those templates, and without them the evaluation repositories run no plugin jobs. plugin add maven locks the suite’s builder and plugins to exact releases and image digests in .divekit/eval-pipeline/plugins.json. This assignment needs no coverage or mutation testing, so JaCoCo/PIT is switched off.

5. Check Before You Distribute

divekit plugin list
Plugin suites

  maven               enabled    plugin-suite
    maven/jacoco-pit  disabled    v2.1.1 · sha256:3ef259f0a9cc
    maven/pmd         enabled    v2.1.0 · sha256:3db3b2d112f8
    maven/surefire    enabled    v2.1.0 · sha256:374b3088d8a3

The composed evaluation pipeline must contain the build job and one job per enabled plugin:

grep -nE "^(build|'maven:[a-z-]+'):" .divekit/eval-pipeline/eval/.gitlab-ci.yml
195:build:
238:'maven:pmd':
258:'maven:surefire':

If maven:pmd and maven:surefire are missing, run divekit plugin install again. Then preview the rollout:

divekit distribute -d greeting --dry-run

Commit the origin, including .divekit/, so the course can be rebuilt from it.

6. Distribute

divekit distribute -d greeting

Divekit creates one subgroup per distribution with a work and an eval group below it:

<course group>/greeting/
├── work/
│   ├── greeting-<uuid-1>     (user12, developer)
│   └── greeting-<uuid-2>     (user13, developer)
└── eval/
    ├── greeting-<uuid-1>
    └── greeting-<uuid-2>

A work repository contains TASK.md, pom.xml, pmd-ruleset.xml, src/main/java/de/example/Greeter.java, and the generated .gitlab-ci.yml with .gitlab/ci/divekit/eval-trigger.yml. Its only job triggers the matching eval pipeline.

An eval repository additionally contains src/test/java/de/example/GreeterTest.java, the generated .gitlab-ci.yml, and .divekit/materialization.json, which tells the eval pipeline to copy src/main/java/de/example from the work repository before each run. Students have no access to it. Divekit links each pair with CI variables and tokens.

distribute also writes remotes.json and individuals.json next to config.json; commit them too, since follow-up commands such as divekit patch read them.

7. Read The Report

Each push to a work repository starts its eval pipeline:

prepare-test-environment → build → maven:pmd, maven:surefire → generate_report → pages

The report is published on the eval repository’s GitLab Page. For the unchanged starter code it shows the five PMD principles as passed and greetReturnsTheExpectedGreeting as failed, because greet() still returns null. After a student changes it to return "Hello, Divekit!" and pushes, the next report shows the test as passed.

The same results are available as public/export.json in the generate_report job artifacts.

When You Need More

  • Settings for a suite plugin, such as coverage thresholds: divekit plugin configure maven, see Plugin Suites.
  • Per-student variants of the task: Individualization.
  • Changing CI or plugin versions after distribution: divekit plugin update followed by divekit patch ci -d greeting.
  • More copy and overwrite rules: Work-To-Eval Materialization.