Allow stopping a running test run via the Run Tests button#29
Allow stopping a running test run via the Run Tests button#29JohnRDOrazio wants to merge 9 commits into
Conversation
The test runner button now toggles between start and stop. Clicking during a run sets a new Stopped state, halts test progression, and updates the button to a warning style. Clicking again restarts tests with proper icon and style restoration. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Adds resetTestUI() to clear card colors, icons, error tooltips, success/fail counters, and timer displays before a new test run. Ensures consistent UI when restarting after a stop or completed run. See #28 for a possible future enhancement to resume from where stopped. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
WalkthroughAdded run-token guarded WebSocket flows and a new Stopped runner state; start button can stop an in-progress run and later restart. Introduced Changes
Sequence Diagram(s)sequenceDiagram
participant UI as UI (Start/Stop Button)
participant Runner as Client Runner Logic
participant WS as WebSocket Server
UI->>Runner: click Start (Ready/JobsFinished/Stopped)
Runner->>Runner: currentRunToken = crypto.randomUUID()\nresetTestUI()\nset state = Running
Runner->>WS: sendMessage({ type: "run-start", ..., runToken })
WS-->>Runner: stream event (with runToken)
alt state == Running AND runToken matches
Runner->>UI: update cards / counters / timers
else state == Stopped OR runToken mismatches OR no token
Runner->>Runner: ignore incoming event
Runner->>UI: keep/clear spinner and show stopped label if applicable
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Poem
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@assets/js/index.js`:
- Around line 685-687: Move the early-exit guard into the very start of the
WebSocket message handler so incoming messages are ignored before any UI
mutation: in conn.onmessage check currentState against TestState.Stopped and the
current run token/id (add a per-run token generated by runTests and stored on
the module) and return immediately if the run token mismatches or currentState
=== TestState.Stopped; also update runTests()/restart logic to create and set a
fresh run token (or recreate the socket) each run so old/out-of-run responses
cannot be applied to the UI.
- Around line 799-800: In resetTestUI(), before removing DOM elements with
selector '#testSuiteAccordion .error-tooltip', iterate over tooltipMap (the map
storing Bootstrap Tooltip instances), call hide() and dispose() on each Tooltip
instance to prevent leaked listeners, then clear tooltipMap (tooltipMap.clear())
and only after that remove the DOM elements; reference the tooltipMap variable
and the resetTestUI() function and the '.error-tooltip' trigger elements when
making these changes.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
Generate a unique run token via crypto.randomUUID() each time tests start, and check it at the very top of conn.onmessage so that stale or out-of-run responses are discarded before any UI mutation occurs. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ments In resetTestUI(), iterate over tooltipMap calling hide() and dispose() on each Tooltip instance and clear the map before removing the .error-tooltip DOM elements to prevent leaked event listeners. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
assets/js/index.js (1)
550-564:⚠️ Potential issue | 🟠 MajorKeep the button enabled after
JobsFinishedso restart is reachable.Line 1312 now allows restarting from
JobsFinished, but the completion branch still disables the button at Line 559. After a full run, the user cannot click the button to restart.🐛 Proposed fix
- btnPrimary.disabled = true; + btnPrimary.disabled = false; btnPrimary.classList.remove('btn-primary'); btnPrimary.classList.add('btn-secondary');Also applies to: 1312-1312
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@assets/js/index.js` around lines 550 - 564, In the TestState.JobsFinished branch, do not disable the test-runner button — locate the TestState.JobsFinished case in assets/js/index.js and remove or change the code that sets btnPrimary.disabled = true and that swaps btn-primary to btn-secondary; instead ensure the button remains enabled (or explicitly set btnPrimary.disabled = false) so the restart path (see the restart-enabled logic around TestState handling at the other branch) can be reached; keep the icon class updates (spinIcon.classList.remove/add) and the call to setTestRunnerBtnLblTxt('Tests Complete') but do not alter the button's enabled state or primary styling.
♻️ Duplicate comments (2)
assets/js/index.js (2)
803-811:⚠️ Potential issue | 🟡 MinorDispose tooltip instances by trigger, not only by
tooltipMap.
tooltipMapis lossy: the click-away/Escape paths hide tooltips and then clear the map without disposing. If one of those hidden.error-tooltiptriggers is later removed at Line 811, its Bootstrap Tooltip instance can be left behind. Dispose each trigger’sbootstrap.Tooltip.getInstance(el)before removal.🧹 Proposed fix
- // Dispose Bootstrap Tooltip instances to prevent leaked event listeners - tooltipMap.forEach(tooltip => { - tooltip.hide(); - tooltip.dispose(); - }); - tooltipMap.clear(); + // Dispose Bootstrap Tooltip instances to prevent leaked event listeners + document.querySelectorAll('#testSuiteAccordion .error-tooltip').forEach(el => { + const tooltip = tooltipMap.get(el) || bootstrap.Tooltip.getInstance(el); + if (tooltip) { + tooltip.hide(); + tooltip.dispose(); + } + }); + tooltipMap.clear(); // Remove error tooltip DOM elements added during the previous run document.querySelectorAll('#testSuiteAccordion .error-tooltip').forEach(el => el.remove());You can verify the lossy map paths with:
#!/bin/bash # Description: Inspect tooltip lifecycle paths that hide/clear tooltipMap versus dispose instances. # Expectation: any path that clears tooltipMap without dispose means resetTestUI() should also dispose by DOM trigger. rg -n -C3 'tooltipMap\.clear\(\)|Tooltip\.getInstance|const resetTestUI|error-tooltip' assets/js/index.js🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@assets/js/index.js` around lines 803 - 811, The DOM cleanup that removes '#testSuiteAccordion .error-tooltip' elements can leak Bootstrap Tooltip instances because tooltipMap is sometimes cleared after hiding tooltips without calling dispose; update the removal loop so for each matched element you first call bootstrap.Tooltip.getInstance(el) and if an instance exists call instance.dispose(), then remove the element; ensure this change complements the existing tooltipMap disposal (tooltipMap.forEach(...); tooltipMap.clear()) so no tooltip instance remains when triggers are removed.
621-625:⚠️ Potential issue | 🟠 MajorMake the run token an actual response-correlation guard.
Line 1327 generates a token, but Line 622 only checks that some token exists. Because the token is never sent with requests or compared with incoming responses, late messages from a stopped run can still mutate the UI after the user restarts and
currentStatebecomes active again.Either have the backend echo a per-run token and reject mismatches here, or create a fresh isolated WebSocket/session per run. Also clear
currentRunTokenwhen stopping/finishing.🔧 Minimal direction
conn.onmessage = ( e ) => { if ( currentState === TestState.Stopped || currentRunToken === null ) { return; } const responseData = JSON.parse( e.data ); + if ( responseData.runToken !== currentRunToken ) { + return; + }-conn.send( JSON.stringify( { action: 'executeValidation', ...currentSourceDataChecks[ index++ ] } ) ); +conn.send( JSON.stringify( { runToken: currentRunToken, action: 'executeValidation', ...currentSourceDataChecks[ index++ ] } ) );// Stop the running test run console.log( 'Stopping test run...' ); currentState = TestState.Stopped; +currentRunToken = null;Also applies to: 1327-1328, 1345-1348
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@assets/js/index.js` around lines 621 - 625, The message handler (conn.onmessage) only checks that currentRunToken exists but never verifies it against the incoming response, so stale messages can affect a new run; update the protocol so each run generates and sends a per-run token (the token created around lines 1327-1328) and have conn.onmessage compare currentRunToken to responseData.runToken (or equivalent) before mutating UI, and also ensure currentRunToken is explicitly cleared when stopping/finishing a run (the stop/finish handlers) or when restarting to fully isolate runs; alternatively, create a fresh WebSocket/session per run and close it on stop so old messages cannot arrive.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Outside diff comments:
In `@assets/js/index.js`:
- Around line 550-564: In the TestState.JobsFinished branch, do not disable the
test-runner button — locate the TestState.JobsFinished case in
assets/js/index.js and remove or change the code that sets btnPrimary.disabled =
true and that swaps btn-primary to btn-secondary; instead ensure the button
remains enabled (or explicitly set btnPrimary.disabled = false) so the restart
path (see the restart-enabled logic around TestState handling at the other
branch) can be reached; keep the icon class updates
(spinIcon.classList.remove/add) and the call to setTestRunnerBtnLblTxt('Tests
Complete') but do not alter the button's enabled state or primary styling.
---
Duplicate comments:
In `@assets/js/index.js`:
- Around line 803-811: The DOM cleanup that removes '#testSuiteAccordion
.error-tooltip' elements can leak Bootstrap Tooltip instances because tooltipMap
is sometimes cleared after hiding tooltips without calling dispose; update the
removal loop so for each matched element you first call
bootstrap.Tooltip.getInstance(el) and if an instance exists call
instance.dispose(), then remove the element; ensure this change complements the
existing tooltipMap disposal (tooltipMap.forEach(...); tooltipMap.clear()) so no
tooltip instance remains when triggers are removed.
- Around line 621-625: The message handler (conn.onmessage) only checks that
currentRunToken exists but never verifies it against the incoming response, so
stale messages can affect a new run; update the protocol so each run generates
and sends a per-run token (the token created around lines 1327-1328) and have
conn.onmessage compare currentRunToken to responseData.runToken (or equivalent)
before mutating UI, and also ensure currentRunToken is explicitly cleared when
stopping/finishing a run (the stop/finish handlers) or when restarting to fully
isolate runs; alternatively, create a fresh WebSocket/session per run and close
it on stop so old messages cannot arrive.
Remove the code in the JobsFinished branch that disabled the button and swapped btn-primary to btn-secondary, which prevented the restart click handler from being reached. Also clear currentRunToken on finish to fully isolate completed runs from stale WebSocket messages. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Dispose any Bootstrap Tooltip instance on each .error-tooltip element via bootstrap.Tooltip.getInstance() before removing it from the DOM, covering tooltips that may not be tracked in tooltipMap. Also clear currentRunToken when stopping a test run to fully isolate runs. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add a sendMessage() helper that attaches currentRunToken to every outgoing WebSocket message. On incoming responses, verify the echoed runToken matches the current run and discard mismatched responses. This completes the client side of the run token protocol added in Liturgical-Calendar/LiturgicalCalendarAPI#533. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
@coderabbitai review |
✅ Actions performedReview triggered.
|
Port the stop/restart test run functionality from the Calendars page to the Resources page: - Add TestState.Stopped state - Add per-run token (currentRunToken) with sendMessage() helper - Add early-exit guard in onmessage to discard stale responses - Add resetTestUI() with proper Bootstrap Tooltip disposal - Update click handler to toggle between running and stopping - Keep button enabled after tests complete for easy restart Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add a multi-stage Dockerfile (PHP 8.4 built-in server on port 3003) that installs dependencies, copies application code, and generates default credentials (admin/admin) for HTTP Basic Auth. Used by the docker-compose.yml in the LiturgicalCalendarFrontend repository to bring up the full localhost development stack. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 7
♻️ Duplicate comments (1)
assets/js/index.js (1)
627-633:⚠️ Potential issue | 🟠 MajorReject missing run tokens too.
Line 632 only rejects mismatched tokens when the response includes one. Untagged late responses from a stopped run can still pass during a restart and mutate the new UI state. Require exact token equality once the backend echoes
runTokenfor every response.🔒 Proposed client-side guard once responses are tokenized
- if ( responseData.runToken && responseData.runToken !== currentRunToken ) { + if ( responseData.runToken !== currentRunToken ) { return; }Verify the WebSocket backend echoes
runTokenfor every response path:#!/bin/bash # Description: Inspect WebSocket request/response handling for runToken propagation. # Expected: every JSON response emitted for tagged validation/unit-test requests includes the same runToken. rg -n -C3 '\brunToken\b|executeValidation|validateCalendar|executeUnitTest' --glob '!node_modules/**' --glob '!dist/**' --glob '!build/**'🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@assets/js/index.js` around lines 627 - 633, The guard currently only ignores responses when a runToken exists and mismatches; update the check in assets/js/index.js around TestState/Stated handling so that any response missing responseData.runToken is also rejected when a currentRunToken exists: after parsing responseData, if currentRunToken !== null then require responseData.runToken === currentRunToken (otherwise return). Modify the condition that references responseData.runToken and currentRunToken (and keep the early return when currentState === TestState.Stopped) so untagged late responses cannot mutate new UI state.
🧹 Nitpick comments (1)
Dockerfile (1)
13-13: Install Composer from the official image (or verify installer signature).Piping the Composer installer over TLS is common, but it skips the checksum verification step recommended by Composer and exposes the build to any compromise of
getcomposer.org. Prefer copying the binary from the pinned official image, or verify the installer hash againsthttps://composer.github.io/installer.sigbefore executing.🔐 Option A — copy from the official Composer image
- curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer && \ - rm -rf /var/lib/apt/lists/* + rm -rf /var/lib/apt/lists/* + +# Pull Composer from the official pinned image +COPY --from=composer:2 /usr/bin/composer /usr/local/bin/composer🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@Dockerfile` at line 13, The Dockerfile currently installs Composer by piping the installer (curl ... | php) which skips signature verification; replace that step by either copying the official Composer binary from the pinned Composer image (use a multi-stage copy from composer:<version> and copy /usr/bin/composer to /usr/local/bin/composer) or, if you must run the installer, fetch https://composer.github.io/installer.sig and verify the installer's SHA384 signature against the downloaded installer before executing php; update the RUN step that contains "curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer" accordingly.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@assets/js/index.js`:
- Around line 468-476: The sendMessage calls inside the TestState handling are
still spreading entries from currentSourceDataChecks which may carry legacy
category values; modify both sendMessage invocations (the one before the switch
break and the one inside the TestState.ExecutingValidations branch) to
explicitly set category: 'sourceDataCheck' on the object you send (i.e.,
sendMessage({ action: 'executeValidation', category: 'sourceDataCheck',
...currentSourceDataChecks[index++] })) so that all source-data validation
WebSocket messages use the required category; update the calls that reference
sendMessage, currentSourceDataChecks, messageCounter, index and
TestState.ExecutingValidations accordingly.
- Around line 801-810: The resetTestUI function is too broad and also resets
summary/status badges; narrow the two querySelectorAll calls to target only test
result cards (e.g. change selectors from '#testSuiteAccordion .bg-success,
`#testSuiteAccordion` .bg-danger' and '#testSuiteAccordion .fa-circle-check,
`#testSuiteAccordion` .fa-circle-xmark' to selectors that scope to result cards
such as '#testSuiteAccordion .test-result-card .bg-success, `#testSuiteAccordion`
.test-result-card .bg-danger' and '#testSuiteAccordion .test-result-card
.fa-circle-check, `#testSuiteAccordion` .test-result-card .fa-circle-xmark').
Update both selector strings in resetTestUI so only result cards are reset,
leaving summary/status badges untouched.
In `@assets/js/resources.js`:
- Around line 616-624: The resetTestUI function is too broad: its selectors
remove success/failure classes and icons from all elements under
`#testSuiteAccordion` (including summary/status badges); narrow the selectors to
only target result card elements (e.g., select '#testSuiteAccordion
.card.result' or the specific result card class used) so only result cards have
their 'bg-success'/'bg-danger' and 'fa-circle-check'/'fa-circle-xmark' replaced
with 'bg-info'/'fa-circle-question'; update the querySelectorAll calls inside
resetTestUI to use the more specific result-card selector (refer to resetTestUI,
the two querySelectorAll usages) so summary/status badges remain unchanged.
In `@Dockerfile`:
- Line 35: The Dockerfile currently copies .env.example to .env.local which
bakes example placeholder config into the image; remove or stop using the COPY
./.env.example ./.env.local step and instead leave .env.local out of the image,
copy only .env.example if you want the template (e.g., keep .env.example but do
not rename it), and document that operators should supply runtime configuration
via docker run --env-file, environment variables, or mounted volume/secret;
update the Dockerfile to stop creating .env.local and update any README/compose
docs accordingly so the app reads real runtime environment values rather than
baked placeholders.
- Around line 37-38: The Dockerfile currently bakes a credentials.php with a
hardcoded admin/admin pair (AUTH_USERS) which exposes the image; replace this by
removing the build-time RUN that writes credentials.php and instead generate
credentials.php at container start (e.g., in your entrypoint) using environment
variables or secrets (AUTH_USER and AUTH_PASSWORD or AUTH_PASSWORD_HASH),
computing a bcrypt hash at runtime and failing fast if the vars are missing;
ensure includes/auth.php reads the generated credentials.php, and optionally
gate the old build-time behavior behind an explicit build ARG (disabled by
default) with clear documentation.
- Around line 41-61: The Dockerfile main stage currently runs as root and uses
PHP's built-in server; create a non-root user (e.g., app user/group) and chown
the application directory (/var/www/html) before switching to that user with a
USER directive to fix the Trivy DS-0002 issue, replace the CMD ["php","-S",...]
used in the main stage with a production-ready entrypoint (e.g., start php-fpm
and front with nginx or use php:8.4-apache/FrankenPHP) or, if this image is
dev-only, document that clearly in the image README and change the image
tag/name to indicate dev; additionally add a HEALTHCHECK instruction probing the
app (HTTP on :3003 or socket) to allow orchestrators to detect unhealthy
containers.
- Around line 46-55: The Dockerfile's runtime stage installs development
packages libicu-dev and libzip-dev which are unnecessary; replace them with the
corresponding runtime shared libraries (bookworm names libicu72 and libzip4) in
the RUN apt-get install line and remove the "-dev" packages while keeping
libyaml-0-2 and locales-all; optionally, if you don't need all locales, replace
locales-all with generating only required locales via locale-gen to reduce image
size.
---
Duplicate comments:
In `@assets/js/index.js`:
- Around line 627-633: The guard currently only ignores responses when a
runToken exists and mismatches; update the check in assets/js/index.js around
TestState/Stated handling so that any response missing responseData.runToken is
also rejected when a currentRunToken exists: after parsing responseData, if
currentRunToken !== null then require responseData.runToken === currentRunToken
(otherwise return). Modify the condition that references responseData.runToken
and currentRunToken (and keep the early return when currentState ===
TestState.Stopped) so untagged late responses cannot mutate new UI state.
---
Nitpick comments:
In `@Dockerfile`:
- Line 13: The Dockerfile currently installs Composer by piping the installer
(curl ... | php) which skips signature verification; replace that step by either
copying the official Composer binary from the pinned Composer image (use a
multi-stage copy from composer:<version> and copy /usr/bin/composer to
/usr/local/bin/composer) or, if you must run the installer, fetch
https://composer.github.io/installer.sig and verify the installer's SHA384
signature against the downloaded installer before executing php; update the RUN
step that contains "curl -sS https://getcomposer.org/installer | php --
--install-dir=/usr/local/bin --filename=composer" accordingly.
🪄 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: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: cb886ab2-c83b-4b42-a100-8112075c13f4
📒 Files selected for processing (4)
.dockerignoreDockerfileassets/js/index.jsassets/js/resources.js
✅ Files skipped from review due to trivial changes (1)
- .dockerignore
| sendMessage( { action: 'executeValidation', ...currentSourceDataChecks[ index++ ] } ); | ||
| break; | ||
| } | ||
| case TestState.ExecutingValidations: | ||
| if ( ++messageCounter === 3 ) { | ||
| console.log( 'one cycle complete, passing to next test..' ) | ||
| messageCounter = 0; | ||
| if ( index < currentSourceDataChecks.length ) { | ||
| conn.send( JSON.stringify( { action: 'executeValidation', ...currentSourceDataChecks[ index++ ] } ) ); | ||
| sendMessage( { action: 'executeValidation', ...currentSourceDataChecks[ index++ ] } ); |
There was a problem hiding this comment.
Force source-data validation messages to use sourceDataCheck.
These sends still spread currentSourceDataChecks, which includes legacy category: "universalcalendar" entries for the base checks. Override the category at the send boundary so all source-data validation WebSocket messages use the expected category.
🛠️ Proposed fix
- sendMessage( { action: 'executeValidation', ...currentSourceDataChecks[ index++ ] } );
+ sendMessage( { action: 'executeValidation', ...currentSourceDataChecks[ index++ ], category: 'sourceDataCheck' } );
...
- sendMessage( { action: 'executeValidation', ...currentSourceDataChecks[ index++ ] } );
+ sendMessage( { action: 'executeValidation', ...currentSourceDataChecks[ index++ ], category: 'sourceDataCheck' } );As per coding guidelines, assets/js/*.js: “For source data validation WebSocket messages, use category: 'sourceDataCheck' (not category-specific names like 'widerregioncalendar' or 'nationalcalendar')”.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| sendMessage( { action: 'executeValidation', ...currentSourceDataChecks[ index++ ] } ); | |
| break; | |
| } | |
| case TestState.ExecutingValidations: | |
| if ( ++messageCounter === 3 ) { | |
| console.log( 'one cycle complete, passing to next test..' ) | |
| messageCounter = 0; | |
| if ( index < currentSourceDataChecks.length ) { | |
| conn.send( JSON.stringify( { action: 'executeValidation', ...currentSourceDataChecks[ index++ ] } ) ); | |
| sendMessage( { action: 'executeValidation', ...currentSourceDataChecks[ index++ ] } ); | |
| sendMessage( { action: 'executeValidation', ...currentSourceDataChecks[ index++ ], category: 'sourceDataCheck' } ); | |
| break; | |
| } | |
| case TestState.ExecutingValidations: | |
| if ( ++messageCounter === 3 ) { | |
| console.log( 'one cycle complete, passing to next test..' ) | |
| messageCounter = 0; | |
| if ( index < currentSourceDataChecks.length ) { | |
| sendMessage( { action: 'executeValidation', ...currentSourceDataChecks[ index++ ], category: 'sourceDataCheck' } ); |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@assets/js/index.js` around lines 468 - 476, The sendMessage calls inside the
TestState handling are still spreading entries from currentSourceDataChecks
which may carry legacy category values; modify both sendMessage invocations (the
one before the switch break and the one inside the
TestState.ExecutingValidations branch) to explicitly set category:
'sourceDataCheck' on the object you send (i.e., sendMessage({ action:
'executeValidation', category: 'sourceDataCheck',
...currentSourceDataChecks[index++] })) so that all source-data validation
WebSocket messages use the required category; update the calls that reference
sendMessage, currentSourceDataChecks, messageCounter, index and
TestState.ExecutingValidations accordingly.
| const resetTestUI = () => { | ||
| // Reset all test cards (source data, calendar data, and unit tests) | ||
| document.querySelectorAll('#testSuiteAccordion .bg-success, #testSuiteAccordion .bg-danger').forEach(el => { | ||
| el.classList.remove('bg-success', 'bg-danger'); | ||
| el.classList.add('bg-info'); | ||
| }); | ||
| document.querySelectorAll('#testSuiteAccordion .fa-circle-check, #testSuiteAccordion .fa-circle-xmark').forEach(el => { | ||
| el.classList.remove('fa-circle-check', 'fa-circle-xmark'); | ||
| el.classList.add('fa-circle-question'); | ||
| }); |
There was a problem hiding this comment.
Avoid resetting status badges as test cards.
The broad #testSuiteAccordion .bg-success/.bg-danger and icon selectors also match summary/status UI, such as the specific unit test “Successful”/“Failed” badges. Restrict the reset to result cards.
🎯 Proposed selector narrowing
- document.querySelectorAll('#testSuiteAccordion .bg-success, `#testSuiteAccordion` .bg-danger').forEach(el => {
+ document.querySelectorAll('#testSuiteAccordion .card.bg-success, `#testSuiteAccordion` .card.bg-danger').forEach(el => {
el.classList.remove('bg-success', 'bg-danger');
el.classList.add('bg-info');
});
- document.querySelectorAll('#testSuiteAccordion .fa-circle-check, `#testSuiteAccordion` .fa-circle-xmark').forEach(el => {
+ document.querySelectorAll('#testSuiteAccordion .card .fa-circle-check, `#testSuiteAccordion` .card .fa-circle-xmark').forEach(el => {
el.classList.remove('fa-circle-check', 'fa-circle-xmark');
el.classList.add('fa-circle-question');
});🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@assets/js/index.js` around lines 801 - 810, The resetTestUI function is too
broad and also resets summary/status badges; narrow the two querySelectorAll
calls to target only test result cards (e.g. change selectors from
'#testSuiteAccordion .bg-success, `#testSuiteAccordion` .bg-danger' and
'#testSuiteAccordion .fa-circle-check, `#testSuiteAccordion` .fa-circle-xmark' to
selectors that scope to result cards such as '#testSuiteAccordion
.test-result-card .bg-success, `#testSuiteAccordion` .test-result-card .bg-danger'
and '#testSuiteAccordion .test-result-card .fa-circle-check, `#testSuiteAccordion`
.test-result-card .fa-circle-xmark'). Update both selector strings in
resetTestUI so only result cards are reset, leaving summary/status badges
untouched.
| const resetTestUI = () => { | ||
| document.querySelectorAll('#testSuiteAccordion .bg-success, #testSuiteAccordion .bg-danger').forEach(el => { | ||
| el.classList.remove('bg-success', 'bg-danger'); | ||
| el.classList.add('bg-info'); | ||
| }); | ||
| document.querySelectorAll('#testSuiteAccordion .fa-circle-check, #testSuiteAccordion .fa-circle-xmark').forEach(el => { | ||
| el.classList.remove('fa-circle-check', 'fa-circle-xmark'); | ||
| el.classList.add('fa-circle-question'); | ||
| }); |
There was a problem hiding this comment.
Limit reset styling to result cards only.
These selectors reset every .bg-success/.bg-danger and check/xmark icon under #testSuiteAccordion, which can also hit summary/status badges. Target .card result elements so the “Successful”/“Failed” UI chrome stays intact.
🎯 Proposed selector narrowing
- document.querySelectorAll('#testSuiteAccordion .bg-success, `#testSuiteAccordion` .bg-danger').forEach(el => {
+ document.querySelectorAll('#testSuiteAccordion .card.bg-success, `#testSuiteAccordion` .card.bg-danger').forEach(el => {
el.classList.remove('bg-success', 'bg-danger');
el.classList.add('bg-info');
});
- document.querySelectorAll('#testSuiteAccordion .fa-circle-check, `#testSuiteAccordion` .fa-circle-xmark').forEach(el => {
+ document.querySelectorAll('#testSuiteAccordion .card .fa-circle-check, `#testSuiteAccordion` .card .fa-circle-xmark').forEach(el => {
el.classList.remove('fa-circle-check', 'fa-circle-xmark');
el.classList.add('fa-circle-question');
});🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@assets/js/resources.js` around lines 616 - 624, The resetTestUI function is
too broad: its selectors remove success/failure classes and icons from all
elements under `#testSuiteAccordion` (including summary/status badges); narrow the
selectors to only target result card elements (e.g., select '#testSuiteAccordion
.card.result' or the specific result card class used) so only result cards have
their 'bg-success'/'bg-danger' and 'fa-circle-check'/'fa-circle-xmark' replaced
with 'bg-info'/'fa-circle-question'; update the querySelectorAll calls inside
resetTestUI to use the more specific result-card selector (refer to resetTestUI,
the two querySelectorAll usages) so summary/status badges remain unchanged.
| COPY ./assets ./assets | ||
| COPY ./i18n ./i18n | ||
| COPY ./*.php ./ | ||
| COPY ./.env.example ./.env.local |
There was a problem hiding this comment.
Shipping .env.example as .env.local can leak example/placeholder config into the runtime image.
.env.example is by convention a template with non-secret, non-production values. Baking it in as .env.local may cause the app to pick up placeholder values in production, and makes it harder for operators to override via mounted config/env vars. Consider leaving .env.local out of the image and providing it via a volume/secret at runtime, or via docker run --env-file.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@Dockerfile` at line 35, The Dockerfile currently copies .env.example to
.env.local which bakes example placeholder config into the image; remove or stop
using the COPY ./.env.example ./.env.local step and instead leave .env.local out
of the image, copy only .env.example if you want the template (e.g., keep
.env.example but do not rename it), and document that operators should supply
runtime configuration via docker run --env-file, environment variables, or
mounted volume/secret; update the Dockerfile to stop creating .env.local and
update any README/compose docs accordingly so the app reads real runtime
environment values rather than baked placeholders.
| # Generate default credentials file for Docker (user: admin, password: admin) | ||
| RUN php -r "file_put_contents('credentials.php', \"<?php\ndefine('AUTH_USERS', ['admin' => '\" . password_hash('admin', PASSWORD_BCRYPT) . \"']);\n\");" |
There was a problem hiding this comment.
Hardcoded default admin/admin credentials baked into the image.
Any image built from this Dockerfile ships with a publicly-known username and password. Since credentials.php is baked in at build time and AUTH_USERS is what includes/auth.php authenticates against, anyone who pulls/runs the image has admin access unless an operator knows to overwrite the file post-build. Also, bcrypt-hashing a constant string at build time provides no security benefit — the plaintext is in the Dockerfile.
Prefer injecting credentials at container start from env vars / secrets (e.g., an entrypoint that generates credentials.php from AUTH_USER/AUTH_PASSWORD_HASH), and fail fast if none are provided. At minimum, document this loudly and gate it behind a build arg that is not the default.
🔒 Example: generate credentials at runtime from env
-# Generate default credentials file for Docker (user: admin, password: admin)
-RUN php -r "file_put_contents('credentials.php', \"<?php\ndefine('AUTH_USERS', ['admin' => '\" . password_hash('admin', PASSWORD_BCRYPT) . \"']);\n\");"
+# credentials.php is generated at container start from AUTH_USER / AUTH_PASSWORD
+# (see docker-entrypoint.sh). The image ships without default credentials.And an entrypoint (in the main stage) along the lines of:
: "${AUTH_USER:?AUTH_USER must be set}"
: "${AUTH_PASSWORD:?AUTH_PASSWORD must be set}"
HASH=$(php -r "echo password_hash(getenv('AUTH_PASSWORD'), PASSWORD_BCRYPT);")
php -r "file_put_contents('/var/www/html/credentials.php', \"<?php\ndefine('AUTH_USERS', ['\" . getenv('AUTH_USER') . \"' => '$HASH']);\n\");"
exec "$@"🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@Dockerfile` around lines 37 - 38, The Dockerfile currently bakes a
credentials.php with a hardcoded admin/admin pair (AUTH_USERS) which exposes the
image; replace this by removing the build-time RUN that writes credentials.php
and instead generate credentials.php at container start (e.g., in your
entrypoint) using environment variables or secrets (AUTH_USER and AUTH_PASSWORD
or AUTH_PASSWORD_HASH), computing a bcrypt hash at runtime and failing fast if
the vars are missing; ensure includes/auth.php reads the generated
credentials.php, and optionally gate the old build-time behavior behind an
explicit build ARG (disabled by default) with clear documentation.
| FROM php:8.4-cli AS main | ||
|
|
||
| # Set the working directory | ||
| WORKDIR /var/www/html | ||
|
|
||
| # Install runtime dependencies | ||
| RUN apt-get update -y && \ | ||
| apt-get install -y --no-install-suggests --no-install-recommends \ | ||
| libyaml-0-2 libicu-dev libzip-dev locales-all && \ | ||
| rm -rf /var/lib/apt/lists/* | ||
|
|
||
| # Copy the compiled PHP extensions from the build stage | ||
| COPY --from=build /usr/local/lib/php/extensions /usr/local/lib/php/extensions | ||
| COPY --from=build /usr/local/etc/php/conf.d /usr/local/etc/php/conf.d | ||
| COPY --from=build /var/www/html /var/www/html | ||
|
|
||
| # Expose port 3003 to the host | ||
| EXPOSE 3003 | ||
|
|
||
| # Command to run PHP's built-in server | ||
| CMD ["php", "-S", "0.0.0.0:3003", "-t", "/var/www/html"] |
There was a problem hiding this comment.
Container runs as root and serves via PHP’s built-in web server.
Two production-hardening gaps in the main stage:
- No
USERdirective — the container runs as root (Trivy DS-0002). Add a non-root user andchownthe app directory. php -Sis documented by PHP as not intended for production use; it is single-threaded and lacks the robustness of a real server. If this image is intended for dev only, make that explicit in the image/README; otherwise front it with something likephp-fpm+ nginx, or usephp:8.4-apache/FrankenPHP.
A HEALTHCHECK would also be worth adding for orchestrators.
🔧 Minimal non-root user example
# Copy the built application contents
COPY --from=build /var/www/html /var/www/html
+# Run as a non-root user
+RUN groupadd --system --gid 1001 app && \
+ useradd --system --uid 1001 --gid app --home-dir /var/www/html --shell /usr/sbin/nologin app && \
+ chown -R app:app /var/www/html
+USER app
+
EXPOSE 3003
+
+HEALTHCHECK --interval=30s --timeout=5s --retries=3 \
+ CMD php -r "exit(`@file_get_contents`('http://127.0.0.1:3003/') !== false ? 0 : 1);"
CMD ["php", "-S", "0.0.0.0:3003", "-t", "/var/www/html"]📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| FROM php:8.4-cli AS main | |
| # Set the working directory | |
| WORKDIR /var/www/html | |
| # Install runtime dependencies | |
| RUN apt-get update -y && \ | |
| apt-get install -y --no-install-suggests --no-install-recommends \ | |
| libyaml-0-2 libicu-dev libzip-dev locales-all && \ | |
| rm -rf /var/lib/apt/lists/* | |
| # Copy the compiled PHP extensions from the build stage | |
| COPY --from=build /usr/local/lib/php/extensions /usr/local/lib/php/extensions | |
| COPY --from=build /usr/local/etc/php/conf.d /usr/local/etc/php/conf.d | |
| COPY --from=build /var/www/html /var/www/html | |
| # Expose port 3003 to the host | |
| EXPOSE 3003 | |
| # Command to run PHP's built-in server | |
| CMD ["php", "-S", "0.0.0.0:3003", "-t", "/var/www/html"] | |
| FROM php:8.4-cli AS main | |
| # Set the working directory | |
| WORKDIR /var/www/html | |
| # Install runtime dependencies | |
| RUN apt-get update -y && \ | |
| apt-get install -y --no-install-suggests --no-install-recommends \ | |
| libyaml-0-2 libicu-dev libzip-dev locales-all && \ | |
| rm -rf /var/lib/apt/lists/* | |
| # Copy the compiled PHP extensions from the build stage | |
| COPY --from=build /usr/local/lib/php/extensions /usr/local/lib/php/extensions | |
| COPY --from=build /usr/local/etc/php/conf.d /usr/local/etc/php/conf.d | |
| COPY --from=build /var/www/html /var/www/html | |
| # Run as a non-root user | |
| RUN groupadd --system --gid 1001 app && \ | |
| useradd --system --uid 1001 --gid app --home-dir /var/www/html --shell /usr/sbin/nologin app && \ | |
| chown -R app:app /var/www/html | |
| USER app | |
| # Expose port 3003 to the host | |
| EXPOSE 3003 | |
| # Health check | |
| HEALTHCHECK --interval=30s --timeout=5s --retries=3 \ | |
| CMD php -r "exit(`@file_get_contents`('http://127.0.0.1:3003/') !== false ? 0 : 1);" | |
| # Command to run PHP's built-in server | |
| CMD ["php", "-S", "0.0.0.0:3003", "-t", "/var/www/html"] |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@Dockerfile` around lines 41 - 61, The Dockerfile main stage currently runs as
root and uses PHP's built-in server; create a non-root user (e.g., app
user/group) and chown the application directory (/var/www/html) before switching
to that user with a USER directive to fix the Trivy DS-0002 issue, replace the
CMD ["php","-S",...] used in the main stage with a production-ready entrypoint
(e.g., start php-fpm and front with nginx or use php:8.4-apache/FrankenPHP) or,
if this image is dev-only, document that clearly in the image README and change
the image tag/name to indicate dev; additionally add a HEALTHCHECK instruction
probing the app (HTTP on :3003 or socket) to allow orchestrators to detect
unhealthy containers.
| # Install runtime dependencies | ||
| RUN apt-get update -y && \ | ||
| apt-get install -y --no-install-suggests --no-install-recommends \ | ||
| libyaml-0-2 libicu-dev libzip-dev locales-all && \ | ||
| rm -rf /var/lib/apt/lists/* | ||
|
|
||
| # Copy the compiled PHP extensions from the build stage | ||
| COPY --from=build /usr/local/lib/php/extensions /usr/local/lib/php/extensions | ||
| COPY --from=build /usr/local/etc/php/conf.d /usr/local/etc/php/conf.d | ||
| COPY --from=build /var/www/html /var/www/html |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Confirm the exact runtime package names available on the php:8.4-cli (Debian bookworm) base
# for libicu and libzip, so the runtime stage can drop the -dev packages.
docker run --rm php:8.4-cli bash -lc '
apt-get update -y >/dev/null 2>&1 &&
apt-cache search --names-only "^libicu[0-9]+$" &&
apt-cache search --names-only "^libzip[0-9]+$" &&
dpkg -l | grep -E "libicu|libzip|gettext" || true
'Repository: Liturgical-Calendar/UnitTestInterface
Length of output: 127
🏁 Script executed:
git ls-files | grep -i dockerfileRepository: Liturgical-Calendar/UnitTestInterface
Length of output: 91
🏁 Script executed:
#!/bin/bash
# Find the Dockerfile mentioned in the review (lines 46-55)
# and read the build stage to understand what extensions/dependencies are installed
fd -i dockerfile -x wc -l {} \; -exec cat {} \;Repository: Liturgical-Calendar/UnitTestInterface
Length of output: 268
🌐 Web query:
Debian bookworm php:8.4-cli libicu libzip runtime packages
💡 Result:
Debian 12 (bookworm) does not include PHP 8.4 in its official repositories; it ships PHP 8.2 by default (php8.2-cli). PHP 8.4 packages like php8.4-cli, php8.4-zip are available in Ondřej Surý's third-party repository at packages.sury.org/php for bookworm, as well as in Docker images (php:8.4-cli-bookworm) and newer Debian suites like trixie/testing. To install on Debian bookworm: 1. Add the Sury repository: apt install -y apt-transport-https lsb-release ca-certificates curl gnupg2 curl -fsSL https://packages.sury.org/php/apt.gpg | gpg --dearmor -o /etc/apt/trusted.gpg.d/sury-php.gpg echo "deb https://packages.sury.org/php/ $(lsb_release -sc) main" | tee /etc/apt/sources.list.d/sury-php.list apt update 2. Install PHP 8.4 CLI runtime: apt install -y php8.4-cli php8.4-common Runtime dependencies for php8.4-cli (analogous to php8.2-cli and Debian php8.4 in sid/trixie): - libc6, libargon2-1, libedit2, libpcre2-8-0, libsodium23, libssl3, libxml2, media-types, php8.4-common (= version), php8.4-opcache, php8.4-readline, tzdata, ucf, zlib1g For libzip (zip extension): Install php8.4-zip, which depends on libzip4t64 (runtime library for zip archives). For libicu (intl extension): Install php8.4-intl, which depends on libicu72 (International Components for Unicode runtime library, version 72.1-3+deb12u1 in bookworm). These are pulled automatically or explicitly via apt. Verify with apt show php8.4-cli php8.4-zip php8.4-intl after adding the repo. Note: PHP 8.4 is not recommended for production until stable.
Citations:
- 1: https://packages.debian.org/php8.4-common
- 2: https://packages.debian.org/php8.4-zip
- 3: https://packages.debian.org/bookworm/sh4/php/
- 4: https://wiki.crowncloud.net/?How_to_install_php_8_4_on_Debian_12=
- 5: https://github.com/docker-library/php/blob/d21ab07e/8.4-rc/bookworm/cli/Dockerfile
- 6: Installing intl fails on php:8.4-bookworm mlocati/docker-php-extension-installer#1082
- 7: http://packages.debian.org/bookworm/php8.2-cli
- 8: https://linuxcapable.com/install-php-8-4-on-debian-linux/
- 9: http://packages.debian.org/bookworm/php8.2-zip
- 10: https://packages.debian.org/source/bookworm/icu
- 11: http://mirror.greensec.de/packages.sury.org/php/dists/bookworm/main/binary-amd64/
- 12: https://packages.sury.org/php/dists/bookworm/
- 13: https://packages.debian.org/unstable/php8.4-cli
- 14: https://packages.debian.org/search?keywords=php8.4-cli
- 15: PHP 8.4 packages oerdnj/deb.sury.org#2176
- 16: https://packages.debian.org/php8.4-cli
- 17: https://packages.debian.org/en/forky/php8.4-cli
🏁 Script executed:
cat DockerfileRepository: Liturgical-Calendar/UnitTestInterface
Length of output: 2276
🌐 Web query:
PHP gettext extension runtime requirements Debian bookworm
💡 Result:
The PHP gettext extension is a core extension compiled into PHP by default, with no separate runtime package required in Debian Bookworm (Debian 12). No php8.2-gettext or php-gettext package exists; searches for it return errors. The core php8.2-common package (version 8.2.30-1~deb12u1) provides the base runtime including standard extensions like gettext. Runtime requirements are satisfied by installing php8.2-common (which depends on libc6, libffi8, libssl3, php-common, ucf). Gettext functions (gettext, dgettext, etc.) are available once PHP 8.2 is installed. Note: php-php-gettext is a separate PEAR package emulating gettext in pure PHP (depends on php-mbstring/php8.2-mbstring, php-pear), used only if avoiding the native extension or system gettext libs, but not the native extension itself.
Citations:
- 1: https://packages.debian.org/bookworm/php-php-gettext
- 2: https://packages.debian.org/source/bookworm/php-gettext
- 3: https://packages.debian.org/bookworm/php-gettext
- 4: http://packages.debian.org/bookworm/php8.2-intl
- 5: https://packages.debian.org/bookworm/source/php8.2
- 6: http://packages.debian.org/bookworm/php-php-gettext
- 7: https://packages.debian.org/bookworm/php8.2-common
- 8: https://packages.debian.org/bookworm/php-intl
🌐 Web query:
libzip4t64 libzip4 Debian bookworm difference
💡 Result:
In Debian bookworm (version 12, released June 2023), the libzip package provides libzip4 (version 1.7.3-1+b1) as the runtime library for reading, creating, and modifying zip archives. This is the standard package available across all architectures like amd64, arm64, armhf, i386, etc. libzip4t64 is not available in bookworm. It appears only in later suites: sid/unstable (for ia64 debports port) at version 1.7.3-1.1+b1, and during the transition in experimental. The libzip source in bookworm builds only libzip4 and libzip-dev, not libzip4t64. The key difference is that libzip4t64 is the renamed runtime library package (libzip4 -> libzip4t64) for the Debian 64-bit time_t transition (to address the Year 2038 problem on 32-bit architectures like armhf). This transition involved NMUs (non-maintainer uploads) starting March 2024 (e.g., libzip 1.7.3-1.1 NMU, bug #1062743), after bookworm's freeze and release. Libraries exposing time_t in their ABI were renamed with "t64" suffix to enable co-installability of old and new ABI versions during the transition. Since bookworm predates this transition (glibc in bookworm uses 32-bit time_t on 32-bit arches), it uses the pre-transition libzip4 without ABI changes for 64-bit time_t.
Citations:
- 1: https://packages.debian.org/bookworm/libzip4
- 2: https://packages.debian.org/libzip4
- 3: https://packages.debian.org/bookworm/ppc64el/libzip4
- 4: https://packages.debian.org/source/bookworm/libzip
- 5: https://packages.debian.org/bookworm/source/libzip
- 6: https://packages.debian.org/libzip4t64
- 7: https://packages.debian.org/sid/libzip4t64
- 8: https://wiki.debian.org/ReleaseGoals/64bit-time
- 9: https://sources.debian.org/src/libzip/1.11.3-2/debian/changelog
Replace -dev packages with runtime libraries in runtime stage.
libicu-dev and libzip-dev are development headers not needed at runtime. The intl and zip extensions (compiled in the build stage) only require the runtime shared libraries: libicu72 and libzip4 on bookworm. Removing the -dev packages significantly reduces the final image size. The libyaml-0-2 and locales-all packages are correct.
Note: gettext is built into PHP core and requires no separate runtime package.
🧰 Suggested change
-RUN apt-get update -y && \
- apt-get install -y --no-install-suggests --no-install-recommends \
- libyaml-0-2 libicu-dev libzip-dev locales-all && \
- rm -rf /var/lib/apt/lists/*
+RUN apt-get update -y && \
+ apt-get install -y --no-install-suggests --no-install-recommends \
+ libyaml-0-2 libicu72 libzip4 locales-all && \
+ rm -rf /var/lib/apt/lists/*If locales-all (~200MB) is not required, consider generating only the locales the app needs via locale-gen.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| # Install runtime dependencies | |
| RUN apt-get update -y && \ | |
| apt-get install -y --no-install-suggests --no-install-recommends \ | |
| libyaml-0-2 libicu-dev libzip-dev locales-all && \ | |
| rm -rf /var/lib/apt/lists/* | |
| # Copy the compiled PHP extensions from the build stage | |
| COPY --from=build /usr/local/lib/php/extensions /usr/local/lib/php/extensions | |
| COPY --from=build /usr/local/etc/php/conf.d /usr/local/etc/php/conf.d | |
| COPY --from=build /var/www/html /var/www/html | |
| # Install runtime dependencies | |
| RUN apt-get update -y && \ | |
| apt-get install -y --no-install-suggests --no-install-recommends \ | |
| libyaml-0-2 libicu72 libzip4 locales-all && \ | |
| rm -rf /var/lib/apt/lists/* | |
| # Copy the compiled PHP extensions from the build stage | |
| COPY --from=build /usr/local/lib/php/extensions /usr/local/lib/php/extensions | |
| COPY --from=build /usr/local/etc/php/conf.d /usr/local/etc/php/conf.d | |
| COPY --from=build /var/www/html /var/www/html |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@Dockerfile` around lines 46 - 55, The Dockerfile's runtime stage installs
development packages libicu-dev and libzip-dev which are unnecessary; replace
them with the corresponding runtime shared libraries (bookworm names libicu72
and libzip4) in the RUN apt-get install line and remove the "-dev" packages
while keeping libyaml-0-2 and locales-all; optionally, if you don't need all
locales, replace locales-all with generating only required locales via
locale-gen to reduce image size.
Summary
TestState.Stoppedstate that halts test progression and ignores incoming WebSocket responseselsebranch that previously just logged a warningTest plan
See #28 for a future enhancement to support resuming from where stopped.
🤖 Generated with Claude Code
Summary by CodeRabbit
New Features
Bug Fixes