Skip to content

Programming exercises: Add the server foundation for multiple build containers per build plan - #13560

Open
LukasGreinwald wants to merge 2 commits into
feature/localci/build-plan-editor-pagefrom
feature/localci/multi-container-foundation
Open

Programming exercises: Add the server foundation for multiple build containers per build plan#13560
LukasGreinwald wants to merge 2 commits into
feature/localci/build-plan-editor-pagefrom
feature/localci/multi-container-foundation

Conversation

@LukasGreinwald

@LukasGreinwald LukasGreinwald commented Aug 24, 2026

Copy link
Copy Markdown

Summary

A build plan is currently a flat list of build phases that all run in one Docker container, with one image and one checkout of every repository the exercise defines. That single container is what forces the instructor's tests and student-authored code to share an environment. This PR teaches the server to represent, validate and persist a build plan as several named containers, each with its own image, its own phases, and its own list of repositories to check out. Execution is deliberately unchanged: scheduling still builds a single build job from the first container, and the orchestration that runs one build job per container follows in a separate PR. Exercises written before this change keep working untouched, without a data migration.

Checklist

General

Server

  • Important: I implemented the changes with a very good performance and prevented too many (unnecessary) and too complex database calls.
  • I strictly followed the principle of data economy for all database calls.
  • I strictly followed the server coding and design guidelines and the REST API guidelines.
  • I added multiple integration tests (Spring) related to the features (with a high test coverage).
  • I documented the Java code using JavaDoc style.

Client

  • I translated all newly inserted strings into English and German.

Changes affecting Programming Exercises

  • High priority: I tested all changes and their related features with all corresponding user types on a test server configured with the integrated lifecycle setup (LocalVC and LocalCI).

Motivation and Context

#12165 (Multiple Docker Containers per Programming Exercise) proposes letting an instructor split a build plan into several named containers, each with its own image, its own build phases, and its own selection of repositories. Scoping the repositories per container is what makes the isolation real: a container that runs student-authored tests can be provisioned with the assignment repository only, so the instructor's test files are never copied into it.

That is a large change, touching the stored configuration format, the validation, the editor and the build orchestration at once. This PR does the first part only, so that the model and its validation can be reviewed on their own, before the orchestration that schedules one build job per container is added on top.

It builds on #13284, which extracted the build plan out of the exercise form, and is opened against that branch so the diff shows only the multi-container work. #12165 lists that extraction as a prerequisite, because every per-container option multiplies with the number of containers and the exercise form has no room for it. This PR should be merged after #13284.

Description

Server

BuildContainerDTO is the new unit of a build plan: a name that is unique within the plan, an optional Docker image, an optional list of repositories to check out, and the ordered build phases that run inside it. BuildContainerRepositoryDTO names one repository by type, plus the repository name for auxiliary repositories. Docker flags (network, CPU, memory, environment variables) stay configured per exercise and apply to every container.

BuildPlanPhasesDTO gains a containers field alongside the existing phases and dockerImage. Configurations written before this change carry only the latter two, so effectiveContainers() normalizes them into a single container named default that scopes no repositories, which is exactly the behaviour a build plan without containers has today. Every caller can therefore work with containers without knowing which format a configuration was stored in, and no data migration is needed. allPhases() flattens the phases of every container for callers that ask a question about the plan as a whole. Both formats keep serializing as before, since @JsonInclude(NON_EMPTY) omits whichever fields the configuration does not use.

BuildPlanConfigurationValidator replaces the phase validation that previously lived in ProgrammingExerciseValidationService. Both save paths, the full exercise update and the build plan editor endpoint, now validate through it, so the same misconfiguration is rejected with the same error and key on both. It checks that a plan defines at least one container, that container names are unique and well formed, that every container has at least one phase, and that phase names are well formed, not reserved, and unique within their container. Phase names only have to be unique per container because containers execute independently of each other. Since a plan can now have several containers, a violation names the container and, where applicable, the phase it occurred in, so an instructor can tell which part of the plan to fix.

LocalCITriggerService reads the plan through effectiveContainers() and rejects a plan with more than one container with a LocalCIException before the build job is queued. Collapsing several containers into the single script and image of one build job would defeat the isolation they exist for, so executing them is left to the orchestration follow-up. Single-container and legacy plans resolve their phases and image exactly as before, including the fallback to the exercise's language defaults. AutomaticAfterDueDateService uses allPhases(), so an after-due-date phase is found regardless of which container it sits in, and the rebuild is still scheduled.

Deliberately not part of this PR, and coming with the orchestration follow-up: executing more than one container, provisioning the scoped repositories (BuildContainerRepositoryDTO is persisted, validated and round-trip tested here, but nothing consumes it yet), and the editor UI for adding and editing containers.

Client

No client code changes. The new validation errors are added to the English and German error.json, replacing the previous flat keys with parameterized ones that name the offending container and phase.

Steps for Testing

This PR changes the server only, and nothing in the client can create a multi-container plan yet, so there is no user-visible change to exercise. The steps below verify that normalizing a legacy configuration into one container changed nothing.

Prerequisites:

  • 1 Instructor
  • 1 Student
  • 1 Course with a Programming Exercise (Java)
  • Test server with the integrated code lifecycle setup (LocalVC and LocalCI)
  1. Log in as an instructor and open an existing programming exercise.
  2. Open the build plan editor, verify that the build plan still loads, change a build phase script and save.
  3. Verify that the template and solution builds are triggered and succeed, i.e. the plan still executes as before.
  4. Create a new programming exercise and verify that it receives a default build plan and that its builds succeed.
  5. As a student, submit to the exercise and verify that the build runs and the result is reported.
  6. As an instructor, set a due date and add a build phase with the condition "after due date", then save and verify that the "Run Tests after Due Date" date is scheduled.
  7. Enter an invalid build plan (a duplicate phase name, or a reserved phase name such as main) and verify that saving is rejected with a message that names both the phase and the container it is in (default for a plan normalized from a legacy configuration).

Exam Mode Testing

Prerequisites:

  • 1 Instructor
  • 1 Exam with a Programming Exercise
  1. Open the exam programming exercise and edit its build plan.
  2. Save a change and verify that it persists and that the builds are triggered.
  3. Add a build phase with the condition "after due date" and verify that the rebuild date is derived from the exam end date, as before.

Testserver States

You can manage test servers using Helios. Check environment statuses in the environment list. To deploy to a test server, go to the CI/CD page, find your PR or branch, and trigger the deployment.

Review Progress

Performance Review

  • I (as a reviewer) confirm that the server changes (in particular related to database calls) are implemented with a very good performance even for very large courses with more than 2000 students.

Code Review

  • Code Review 1
  • Code Review 2

Manual Tests

  • Test 1
  • Test 2

Exam Mode Test

  • Test 1
  • Test 2

Test Coverage

Class/File Line Coverage Confirmation (assert/expect)
BuildPlanPhasesDTO.java
BuildContainerDTO.java
BuildContainerRepositoryDTO.java
BuildPlanConfigurationValidator.java
ProgrammingExerciseValidationService.java
LocalCITriggerService.java
AutomaticAfterDueDateService.java

New and extended server tests: BuildPlanPhasesDTOTest (legacy normalization, containers taking precedence, scoped repositories surviving a round trip, allPhases() for both formats, serialization unchanged for legacy plans), BuildPlanConfigurationValidatorTest (every rule, the same phase name allowed in different containers, the error naming the offending container and phase), ProgrammingExerciseBuildConfigResourceIntegrationTest (saving a multi-container plan and the rejections it produces), AutomaticAfterDueDateServiceTest (an after-due-date phase in the second container), and LocalVCLocalCIIntegrationTest (scheduling for a single-container and a legacy plan is unchanged).

@LukasGreinwald
LukasGreinwald requested review from a team and krusche as code owners August 24, 2026 14:49
@github-project-automation github-project-automation Bot moved this to Work In Progress in Artemis Development Aug 24, 2026
@github-actions github-actions Bot added tests server Pull requests that update Java code. (Added Automatically!) client Pull requests that update TypeScript code. (Added Automatically!) programming Pull requests that affect the corresponding module labels Aug 24, 2026
@LukasGreinwald
LukasGreinwald temporarily deployed to playwright-e2e-tests August 24, 2026 15:02 — with GitHub Actions Inactive
@github-actions

github-actions Bot commented Aug 24, 2026

Copy link
Copy Markdown

End-to-End Test Results

Phase Status Details
Phase 1 (Relevant) ❌ Failed
TestsPassed ☑️Skipped ⚠️Failed ❌️Time ⏱
Phase 1: E2E Test Report47 ran45 passed1 skipped1 failed16m 25s
Phase 2 (Remaining) ⏭ Skipped (no remaining tests)
❌ Failed Tests (Phase 1)
  • Programming exercise basic submissions › Shows the build result in the course build overview › Renders the result-only badge in the finished build jobs table (11m 42s)

Test Strategy: Two-phase execution

  • Phase 1: e2e/Login.spec.ts e2e/Logout.spec.ts e2e/SystemHealth.spec.ts e2e/exercise/programming/
  • Phase 2: e2e/Passkey.spec.ts e2e/PasskeyReminderPersistence.spec.ts e2e/admin/ e2e/atlas/ e2e/course/ e2e/exam/ExamAssessment.spec.ts e2e/exam/ExamChecklists.spec.ts e2e/exam/ExamCreationDeletion.spec.ts e2e/exam/ExamDateVerification.spec.ts e2e/exam/ExamManagement.spec.ts e2e/exam/ExamParticipation.spec.ts e2e/exam/ExamResults.spec.ts e2e/exam/ExamTestRun.spec.ts e2e/exam/test-exam/ e2e/exercise/ExerciseImport.spec.ts e2e/exercise/file-upload/ e2e/exercise/modeling/ e2e/exercise/quiz-exercise/ e2e/exercise/text/ e2e/iris/ e2e/lecture/ e2e/shared/

Overall: ❌ E2E: real (non-flaky) test failure

🔗 Workflow Run · 📊 Test Report Phase 1

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

client Pull requests that update TypeScript code. (Added Automatically!) programming Pull requests that affect the corresponding module server Pull requests that update Java code. (Added Automatically!) tests

Projects

Status: Work In Progress

Development

Successfully merging this pull request may close these issues.

1 participant