Programming exercises: Add the server foundation for multiple build containers per build plan - #13560
Open
LukasGreinwald wants to merge 2 commits into
Conversation
LukasGreinwald
temporarily deployed
to
playwright-e2e-tests
August 24, 2026 15:02 — with
GitHub Actions
Inactive
End-to-End Test Results
❌ Failed Tests (Phase 1)
Test Strategy: Two-phase execution
Overall: ❌ E2E: real (non-flaky) test failure |
… fix the DTO test package
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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
Client
Changes affecting Programming Exercises
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
BuildContainerDTOis 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.BuildContainerRepositoryDTOnames 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.BuildPlanPhasesDTOgains acontainersfield alongside the existingphasesanddockerImage. Configurations written before this change carry only the latter two, soeffectiveContainers()normalizes them into a single container nameddefaultthat 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.BuildPlanConfigurationValidatorreplaces the phase validation that previously lived inProgrammingExerciseValidationService. 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.LocalCITriggerServicereads the plan througheffectiveContainers()and rejects a plan with more than one container with aLocalCIExceptionbefore 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.AutomaticAfterDueDateServiceusesallPhases(), 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 (
BuildContainerRepositoryDTOis 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:
main) and verify that saving is rejected with a message that names both the phase and the container it is in (defaultfor a plan normalized from a legacy configuration).Exam Mode Testing
Prerequisites:
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
Code Review
Manual Tests
Exam Mode Test
Test Coverage
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), andLocalVCLocalCIIntegrationTest(scheduling for a single-container and a legacy plan is unchanged).