Skip to content

Development: Try to fix client 404 error on testservers#13141

Open
matyasht wants to merge 1 commit into
developfrom
bugfix/development/try-fix-client-not-showing-on-testserver
Open

Development: Try to fix client 404 error on testservers#13141
matyasht wants to merge 1 commit into
developfrom
bugfix/development/try-fix-client-not-showing-on-testserver

Conversation

@matyasht

@matyasht matyasht commented Jul 5, 2026

Copy link
Copy Markdown
Contributor

Summary

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 added pre-authorization annotations according to the guidelines and checked the course groups for all new REST Calls (security).
  • I documented the Java code using JavaDoc style.

Client

  • Important: I implemented the changes with a very good performance, prevented too many (unnecessary) REST calls and made sure the UI is responsive, even with large data (e.g. using paging).
  • I strictly followed the principle of data economy for all client-server REST calls.
  • I strictly followed the client coding guidelines.
  • I strictly followed the AET UI-UX guidelines.
  • Following the theming guidelines, I specified colors only in the theming variable files and checked that the changes look consistent in both the light and the dark theme.
  • I added multiple integration tests (Vitest) related to the features (with a high test coverage), while following the test guidelines.
  • I added authorities to all new routes and checked the course groups for displaying navigation elements (links, buttons).
  • I documented the TypeScript code using JSDoc style.
  • I added multiple screenshots/screencasts of my UI changes.
  • 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).
  • I tested all changes and their related features with all corresponding user types on a test server configured with LocalVC and Jenkins.

Motivation and Context

Description

Steps for Testing

Prerequisites:

  • 1 Instructor
  • 2 Students
  • 1 Programming Exercise with Complaints enabled
  1. Log in to Artemis
  2. Navigate to Course Administration
  3. ...

Exam Mode Testing

Prerequisites:

  • 1 Instructor
  • 2 Students
  • 1 Exam with a Programming Exercise
  1. Log in to Artemis
  2. Participate in the exam as a student
  3. Make sure that the UI of the programming exercise in the exam mode stays unchanged. You can use the exam mode documentation as reference.
  4. ...

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 client changes (in particular related to REST calls and UI responsiveness) are implemented with a very good performance even for very large courses with more than 2000 students.
  • 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

Performance Tests

  • Test 1
  • Test 2

Test Coverage

No code changes detected - test coverage not required for this PR.

Last updated: 2026-07-05 18:17:55 UTC

Screenshots

Summary by CodeRabbit

  • New Features
    • Added support for optionally skipping the web app build during the production Gradle workflow.
    • When skipped, the app now uses prebuilt static assets instead of rebuilding them.
  • Bug Fixes
    • Improved the build process so generated web assets are consistently included in the final output directory.

@matyasht matyasht self-assigned this Jul 5, 2026
@matyasht matyasht requested review from a team and krusche as code owners July 5, 2026 16:20
@github-project-automation github-project-automation Bot moved this to Work In Progress in Artemis Development Jul 5, 2026
@matyasht matyasht changed the title Development: Try to fix that client 404 error on testservers Development: Try to fix client 404 error on testservers Jul 5, 2026
@coderabbitai

coderabbitai Bot commented Jul 5, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

The webapp Gradle task in gradle/profile_prod.gradle now declares an output directory and conditionally skips the production webapp build when the skipWebapp project property is set, copying prebuilt assets from build/webapp-dist instead of running webapp:prod.

Changes

Gradle Build Configuration

Layer / File(s) Summary
Conditional webapp build task
gradle/profile_prod.gradle
Added outputs.dir declaration for build/resources/main/static/; added skipWebapp conditional to copy prebuilt assets from build/webapp-dist via doLast instead of unconditionally running webapp:prod.

Estimated code review effort: 1 (Trivial) | ~5 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title matches the main goal of fixing client 404s on test servers and is specific enough to describe the change.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch bugfix/development/try-fix-client-not-showing-on-testserver

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
gradle/profile_prod.gradle (1)

29-29: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

args = ["-v"] invokes pnpm for no apparent purpose.

When skipWebapp is set, args = ["-v"] still runs pnpm -v before the real work happens in doLast. If this is only there to satisfy PnpmTask's requirement for a non-empty args, a short comment would help future readers understand why an unrelated pnpm invocation exists in the skip path.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@gradle/profile_prod.gradle` at line 29, The skipWebapp branch in the PnpmTask
configuration uses args = ["-v"] only to satisfy the task’s non-empty args
requirement, so add a brief explanatory comment near that assignment in the
profile_prod.gradle task setup. Reference the task/block where skipWebapp is
checked and doLast is used, and make it clear that the pnpm -v call is
intentional and not part of the real build work.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@gradle/profile_prod.gradle`:
- Around line 25-38: The prod task only declares the static output directory and
misses the frontend inputs that the dev profile tracks, so the task can stay
stale when webapp sources or the skipWebapp branch changes. Update the task in
profile_prod.gradle (the same task block that sets outputs.dir and args) to
declare the missing inputs for pnpm-lock.yaml, angular.json, tsconfig*.json,
src/main/webapp/, and the skipWebapp property, matching the dependency tracking
used in profile_dev.gradle.
- Around line 30-35: The webapp asset copy in the `doLast` block currently
treats a missing or empty `build/webapp-dist` as a no-op, which lets packaging
continue with an empty static output. Update the `copy` step in
`profile_prod.gradle` to explicitly verify the source exists and contains files
before copying, and fail the build with an error if it is absent or empty. Use
the `doLast`/`copy` logic around `build/webapp-dist` and
`build/resources/main/static/` as the place to add this check.

---

Nitpick comments:
In `@gradle/profile_prod.gradle`:
- Line 29: The skipWebapp branch in the PnpmTask configuration uses args =
["-v"] only to satisfy the task’s non-empty args requirement, so add a brief
explanatory comment near that assignment in the profile_prod.gradle task setup.
Reference the task/block where skipWebapp is checked and doLast is used, and
make it clear that the pnpm -v call is intentional and not part of the real
build work.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: a24e0f54-03e2-4c14-9bb6-c772f3589299

📥 Commits

Reviewing files that changed from the base of the PR and between 701470f and 821298e.

⛔ Files ignored due to path filters (1)
  • .github/workflows/ci-build.yml is excluded by !**/*.yml
📒 Files selected for processing (1)
  • gradle/profile_prod.gradle

Comment on lines +25 to +38
outputs.dir("build/resources/main/static/")
.withPropertyName("webapp-build-dir")

if (project.hasProperty("skipWebapp")) {
args = ["-v"]
doLast {
copy {
from "build/webapp-dist"
into "build/resources/main/static/"
}
}
} else {
args = ["run", "webapp:prod"]
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚀 Performance & Scalability | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== profile_prod.gradle ==\n'
nl -ba gradle/profile_prod.gradle | sed -n '1,120p'

printf '\n== references to build/resources/main/static and build/webapp-dist ==\n'
rg -n "build/resources/main/static|build/webapp-dist|skipWebapp|webapp:prod|outputs\.dir|inputs\." -S .

printf '\n== task type definition if present ==\n'
rg -n "class .*PnpmTask|PnpmTask" -S gradle . || true

printf '\n== related build files ==\n'
git ls-files | rg '(^|/)(war\.gradle|profile_prod\.gradle|build\.gradle|settings\.gradle|angular\.json|package\.json)$'

Repository: ls1intum/Artemis

Length of output: 221


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== gradle/profile_prod.gradle ==\n'
cat -n gradle/profile_prod.gradle | sed -n '1,160p'

printf '\n== references to build/resources/main/static, build/webapp-dist, skipWebapp, webapp:prod, outputs.dir, inputs. ==\n'
rg -n -S "build/resources/main/static|build/webapp-dist|skipWebapp|webapp:prod|outputs\.dir|inputs\." .

printf '\n== PnpmTask definition ==\n'
rg -n -S "class .*PnpmTask|PnpmTask" gradle . || true

printf '\n== related build files ==\n'
git ls-files | rg '(^|/)(war\.gradle|profile_prod\.gradle|build\.gradle|settings\.gradle|angular\.json|package\.json)$'

Repository: ls1intum/Artemis

Length of output: 11807


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== gradle/profile_dev.gradle (webapp task section) ==\n'
sed -n '35,80p' gradle/profile_dev.gradle | cat -n

printf '\n== PnpmTask definition / usage ==\n'
rg -n -S "PnpmTask|abstract class .*PnpmTask|class .*PnpmTask|task.*PnpmTask" build.gradle gradle . --glob '!**/build/**'

printf '\n== skipWebapp / build-webapp-dist producers ==\n'
rg -n -S "skipWebapp|build/webapp-dist|webapp-dist" . --glob '!**/build/**'

printf '\n== angular build output target ==\n'
sed -n '1,90p' angular.json | cat -n

Repository: ls1intum/Artemis

Length of output: 7969


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== skipWebapp / webapp-dist references across repo ==\n'
rg -n -S "skipWebapp|webapp-dist|build/webapp-dist" . --glob '!**/build/**' --glob '!**/dist/**'

printf '\n== build.gradle around PnpmTask wiring ==\n'
sed -n '640,720p' build.gradle | cat -n

printf '\n== profile_prod.gradle and profile_dev.gradle webapp tasks ==\n'
sed -n '18,40p' gradle/profile_prod.gradle | cat -n
printf '\n---\n'
sed -n '9,33p' gradle/profile_dev.gradle | cat -n

Repository: ls1intum/Artemis

Length of output: 6009


Add the missing webapp inputs to the prod task.
gradle/profile_prod.gradle only declares outputs.dir(...); unlike gradle/profile_dev.gradle, it doesn’t track pnpm-lock.yaml, angular.json, tsconfig*.json, src/main/webapp/, or skipWebapp. That can leave build/resources/main/static/ stale when frontend sources or the task branch changes.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@gradle/profile_prod.gradle` around lines 25 - 38, The prod task only declares
the static output directory and misses the frontend inputs that the dev profile
tracks, so the task can stay stale when webapp sources or the skipWebapp branch
changes. Update the task in profile_prod.gradle (the same task block that sets
outputs.dir and args) to declare the missing inputs for pnpm-lock.yaml,
angular.json, tsconfig*.json, src/main/webapp/, and the skipWebapp property,
matching the dependency tracking used in profile_dev.gradle.

Comment on lines +30 to +35
doLast {
copy {
from "build/webapp-dist"
into "build/resources/main/static/"
}
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the target file and nearby build logic.
git ls-files gradle/profile_prod.gradle
wc -l gradle/profile_prod.gradle
cat -n gradle/profile_prod.gradle | sed -n '1,160p'

# Look for any validation around webapp-dist or static asset packaging.
rg -n "webapp-dist|skipWebapp|resources/main/static|copy \\{" gradle . -g '!**/build/**'

Repository: ls1intum/Artemis

Length of output: 4323


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Find where this Gradle profile is applied and whether bootJar has any related inputs/outputs.
rg -n "profile_prod\.gradle|skipWebapp|bootJar|webapp-dist|build/resources/main/static" -g '!**/build/**' .

# Inspect any other Gradle files that may affect bootJar/resource packaging.
git ls-files '*.gradle' '*.gradle.kts'

Repository: ls1intum/Artemis

Length of output: 3185


🌐 Web query:

Gradle Copy task missing source directory behavior doLast bootJar copy from missing directory

💡 Result:

When a Gradle Copy task specifies a source directory that does not exist, the task typically treats the source as empty [1]. Because no files are found to copy, Gradle marks the task as "NO-SOURCE" and skips its execution without throwing an error [1][2]. If you are using a copy block inside a doLast action (e.g., within a bootJar task or a custom task), the behavior differs: 1. Copy Task Behavior: If you define a formal task of type Copy, Gradle evaluates the source paths during the configuration phase [3]. If the directory is missing, it is simply skipped [1]. 2. doLast { copy {... } } Behavior: When calling the project.copy method inside a doLast block, the copy operation is executed imperatively at runtime [3]. If the source directory specified in the from method does not exist at that moment, the copy method may fail or behave unpredictably (sometimes throwing a NullPointerException or simply doing nothing depending on the Gradle version and implementation) [4]. To handle missing source directories gracefully, you should ensure the existence of the source directory before attempting the copy, or use logic to verify the path: Example solution: task copyTask(type: Copy) { def sourceDir = file('path/to/source') if (sourceDir.exists) { from sourceDir into 'destination/dir' } else { doFirst { logger.warn("Source directory $sourceDir does not exist, skipping copy.") } } } For Spring Boot's bootJar task, you should interact with its configuration (e.g., using the bootInf method) to include files rather than relying on a doLast copy block, as this ensures your build remains compatible with Gradle's incremental build and up-to-date checks [5][6]. Using doLast for file manipulation often bypasses Gradle's task dependencies and input/output tracking, which can lead to non-deterministic build results [3][7].

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

python3 - <<'PY'
import urllib.request
url = "https://docs.gradle.org/current/dsl/org.gradle.api.tasks.Copy.html"
try:
    with urllib.request.urlopen(url, timeout=10) as r:
        text = r.read(5000).decode("utf-8", "replace")
    print(text[:2000])
except Exception as e:
    print(type(e).__name__, e)
PY

Repository: ls1intum/Artemis

Length of output: 297


🏁 Script executed:

#!/bin/bash
set -euo pipefail

python3 - <<'PY'
import urllib.request, re
urls = [
    "https://docs.gradle.org/current/userguide/more_about_tasks.html",
    "https://docs.gradle.org/current/userguide/authoring_tasks.html",
]
for url in urls:
    try:
        with urllib.request.urlopen(url, timeout=15) as r:
            txt = r.read().decode("utf-8", "replace")
        print("URL", url, "OK", len(txt))
        for needle in ["doLast", "task actions", "actions"]:
            if needle in txt:
                print(" contains", needle)
        print(txt[:500].replace("\n"," ")[:500])
    except Exception as e:
        print("URL", url, type(e).__name__, e)
PY

Repository: ls1intum/Artemis

Length of output: 575


🏁 Script executed:

#!/bin/bash
set -euo pipefail

python3 - <<'PY'
import urllib.request, re
url = "https://docs.gradle.org/current/userguide/more_about_tasks.html"
txt = urllib.request.urlopen(url, timeout=15).read().decode("utf-8", "replace")

for needle in ["doLast", "doFirst", "Task actions", "Actions are executed", "executed in order"]:
    idx = txt.find(needle)
    print(f"\n== {needle} @ {idx} ==")
    if idx != -1:
        print(re.sub(r"<[^>]+>", " ", txt[max(0, idx-300):idx+700])[:1000])
PY

Repository: ls1intum/Artemis

Length of output: 2748


🏁 Script executed:

#!/bin/bash
set -euo pipefail

python3 - <<'PY'
import urllib.request, re
url = "https://docs.gradle.org/current/userguide/working_with_files.html"
txt = urllib.request.urlopen(url, timeout=15).read().decode("utf-8", "replace")
for needle in ["copy", "missing", "nonexistent", "from(", "empty"]:
    idx = txt.lower().find(needle.lower())
    print(f"\n== {needle} @ {idx} ==")
    if idx != -1:
        print(re.sub(r"<[^>]+>", " ", txt[max(0, idx-300):idx+900])[:1200])
PY

Repository: ls1intum/Artemis

Length of output: 2748


🏁 Script executed:

#!/bin/bash
set -euo pipefail

python3 - <<'PY'
import urllib.request, re
url = "https://docs.gradle.org/current/userguide/working_with_files.html"
txt = urllib.request.urlopen(url, timeout=15).read().decode("utf-8", "replace")
needle = "If the source path of a file copy action resolves to a file that does not exist"
idx = txt.find(needle)
print("idx", idx)
print(re.sub(r"<[^>]+>", " ", txt[idx-500:idx+900]))
PY

Repository: ls1intum/Artemis

Length of output: 2748


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Compare the production profile with the dev profile to understand intended behavior.
wc -l gradle/profile_dev.gradle gradle/profile_prod.gradle
cat -n gradle/profile_dev.gradle | sed -n '1,140p'

Repository: ls1intum/Artemis

Length of output: 3268


Fail fast when build/webapp-dist is absent. copy { from "build/webapp-dist" } treats a missing source dir as empty, so webapp still succeeds and processResources packages an empty build/resources/main/static/ tree. That ships the jar without the webapp assets; throw here if the dist is missing or empty.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@gradle/profile_prod.gradle` around lines 30 - 35, The webapp asset copy in
the `doLast` block currently treats a missing or empty `build/webapp-dist` as a
no-op, which lets packaging continue with an empty static output. Update the
`copy` step in `profile_prod.gradle` to explicitly verify the source exists and
contains files before copying, and fail the build with an error if it is absent
or empty. Use the `doLast`/`copy` logic around `build/webapp-dist` and
`build/resources/main/static/` as the place to add this check.

@github-project-automation github-project-automation Bot moved this from Work In Progress to Ready For Review in Artemis Development Jul 5, 2026
@matyasht matyasht temporarily deployed to playwright-e2e-tests July 5, 2026 16:26 — with GitHub Actions Inactive
@helios-aet helios-aet Bot temporarily deployed to artemis-test5.artemis.cit.tum.de July 5, 2026 16:29 Inactive
@github-actions

github-actions Bot commented Jul 5, 2026

Copy link
Copy Markdown

End-to-End Test Results

Phase Status Details
Phase 1 (Relevant) ✅ Passed
TestsPassed ✅SkippedFailedTime ⏱
Phase 1: E2E Test Report14 ran14 passed0 skipped0 failed3m 3s
| **Phase 2** (Remaining) | ⏳ Pending... | |

Test Strategy: Two-phase execution

  • Phase 1: e2e/Login.spec.ts e2e/Logout.spec.ts e2e/SystemHealth.spec.ts
  • 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/programming/ e2e/exercise/quiz-exercise/ e2e/exercise/text/ e2e/iris/ e2e/lecture/ e2e/shared/

🔗 Workflow Run · 📊 Test Report Phase 1

@matyasht matyasht temporarily deployed to playwright-e2e-tests July 5, 2026 16:32 — with GitHub Actions Inactive
@matyasht matyasht had a problem deploying to playwright-e2e-tests July 5, 2026 17:39 — with GitHub Actions Error
@helios-aet helios-aet Bot temporarily deployed to artemis-test5.artemis.cit.tum.de July 5, 2026 17:45 Inactive
@matyasht matyasht force-pushed the bugfix/development/try-fix-client-not-showing-on-testserver branch from bbf0de0 to 821298e Compare July 5, 2026 17:51
@github-actions

github-actions Bot commented Jul 5, 2026

Copy link
Copy Markdown

@matyasht Your PR description needs attention before it can be reviewed:

Issues Found

  1. No checkboxes are checked in the PR description

How to Fix

  • Check the boxes that apply to your changes in the Checklist section

This check validates that your PR description follows the PR template. A complete description helps reviewers understand your changes and speeds up the review process.

Note: This description validation is an experimental feature. If you observe false positives, please send a DM with a link to the wrong comment to Patrick Bassner on Slack. Thank you!

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

Labels

None yet

Projects

Status: Ready For Review

Development

Successfully merging this pull request may close these issues.

1 participant