A delivery team runs Java, Python, and documentation builds through one shared pipeline. The commands differ, and some builds skip compilation or deployment, but source checkout must happen first and workspace cleanup must happen at the end—even after a failure.
The project already has a working BuildRunner. Your job is to implement only the classes that express the Template Method pattern:
BuildPipeline, the abstract base class that owns the build sequence and cleanup guarantee.JavaBuildPipeline, PythonBuildPipeline, and DocsBuildPipeline, which provide language-specific stages and hook choices.Do not reimplement BuildRunner; it is included in the starter code and connects your pipelines to the tests. The tests use its public API:
BuildRunner() creates a runner that has run nothing.String[] run(String kind, String failingStep) runs the "java", "python" or "docs" pipeline and returns the steps it carried out, in order. An unrecognised kind or an unrecognised stage name returns ["UNKNOWN"] and runs nothing.boolean compiles(String kind) returns whether that pipeline runs a compile stage. An unrecognised kind returns false.boolean deploys(String kind) returns whether that pipeline runs a deploy stage. An unrecognised kind returns false.int runCount() returns how many builds have run.int failureCount() returns how many of those builds aborted.The sequence is checkout, compile, test, package, deploy, cleanup. Compile and deploy are optional, and the other four always run.
"Fetching sources from the repository"."Cleaning the workspace", and it runs whether the build finished or aborted."Compiling sources with javac", tests "Running JUnit tests", packages "Packaging as a JAR" and deploys "Deploying the JAR to Nexus"."Running pylint, nothing to compile", tests "Running pytest tests", packages "Packaging as a Python wheel" and does not deploy."Checking links in the docs", packages "Packaging the docs site" and deploys "Publishing the docs site".failingStep is one of "none", "checkout", "compile", "test", "package" or "deploy". When the named stage is reached it produces "FAILED at <stage>" in place of its usual step, every stage after it is skipped, and cleanup still runs. Naming a stage that this pipeline does not run changes nothing, and the build finishes normally.
Keep the stage order, optional compile/deploy decisions, failure handling, and cleanup guarantee in the template owned by BuildPipeline. Concrete pipelines should supply stage details and override hooks; the base class should never branch on a pipeline name.
Input:
Output:
Explanation: The java pipeline runs every stage, deploys because it opted in, and finishes with the cleanup the skeleton always appends.
Input:
Output:
Explanation: The python pipeline fails at the test stage. Packaging and deployment never run, and cleanup still appears at the end, which is where implementations that return early from the abort diverge.
kind and failingStep are lower-case words of at most 20 letters.100 calls in total are made across all methods.Full marks when the stage order and the guarantee that cleanup runs after an abort both live in one base-class method the pipelines cannot replace. Lose points heavily when each pipeline repeats the sequence, or when the abort path returns before cleanup is appended.
Full marks when compile and deploy are guarded by overridable methods carrying base-class defaults, so the docs pipeline skips compiling and the python pipeline skips deploying without either being named in the skeleton. Lose points heavily when the skeleton checks the pipeline name, or when a skipped stage still contributes an entry to the transcript.
Full marks when a failing stage produces `"FAILED at <stage>"` and stops every stage after it, naming a stage the pipeline skips leaves the build successful, and an unrecognised kind or stage name returns `["UNKNOWN"]` without counting. Lose points for printing to stdout.
Passing every test is not enough on its own. A submission is accepted only when the design also clears the bar.
| Call | Returns |
|---|---|
| new BuildRunner() | null |
| run("java", "none") | ["Fetching sources from the repository","Compiling sources with javac","Running JUnit tests","Packaging as a JAR","Deploying the JAR to Nexus","Cleaning the workspace"] |
| runCount() | 1 |
| failureCount() | 0 |
The java pipeline runs every stage, deploys because it opted in, and finishes with the cleanup the skeleton always appends.
Run checks these cases. Submit also runs a larger hidden set.

