Skip to content

Post update hook - #134

Open
githubkusi wants to merge 8 commits into
dkorecko:mainfrom
githubkusi:post-update-hook
Open

githubkusi wants to merge 8 commits into
dkorecko:mainfrom
githubkusi:post-update-hook

Conversation

@githubkusi

@githubkusi githubkusi commented Mar 31, 2026

Copy link
Copy Markdown

This PR implements a post update hook, which allows to run a script after a successful update. The script is run within the PatchPanda container, but has access to the host filesystem. Sample script:

  #!/bin/bash      
  apt-get update
  apt-get install -y git

  # needed since script is run as root
  git config --global --add safe.directory $PP_PROJECT_DIR

  git config --global user.email "none@none"
  git config --global user.name "PatchPanda Updater"
  
  cd $PP_PROJECT_DIR
  git add docker-compose.yml
  git commit -m "Update $PP_OLD_VERSION -> $PP_NEW_VERSION"

A typical use case includes an automated git commit of the docker-compose.yml. Unfortunately, the current image mcr.microsoft.com/dotnet/aspnet doesn't include git, hence it need to be installed from within the script. In order to simplify post update scripts for the git use-case, the installation of git could be done within the Dockerfile. What do you think?

This PR was created with significant help of Github Copilot

fixes #89

Summary by CodeRabbit

  • New Features

    • Added post-update hook support to run an optional user script after a successful update.
    • Hooks are read from the Docker label patchpanda.hook.post_update and stored per container; if the hook or compose file is missing/blank, the hook is skipped.
    • The hook runs with PP_PROJECT_DIR, PP_NAME, PP_OLD_VERSION, PP_NEW_VERSION, and PP_UPDATE_TIME. Any hook execution errors are logged without failing the update.
  • Documentation

    • Added a “Post-hooks” section with configuration details, environment variables, and an example workflow.

@coderabbitai

coderabbitai Bot commented Mar 31, 2026

Copy link
Copy Markdown

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

This PR adds a nullable post-update hook field, stores it in EF Core, loads it from Docker labels, runs configured scripts after successful updates, and documents the hook contract and environment variables.

Changes

Post-update hook feature

Layer / File(s) Summary
Hook persistence
PatchPanda.Web/Entities/Container.cs, PatchPanda.Web/Migrations/*
Adds PostUpdateHook to Container and maps it through the migration and EF Core model snapshot as a nullable text column.
Hook execution service
PatchPanda.Web/Services/HookServices.cs
Adds HookService to validate hook paths, launch platform-specific scripts, set update environment variables, capture output, and report failures.
Update-flow integration and support
PatchPanda.Web/Services/DockerService.cs, PatchPanda.Web/Services/UpdateService.cs, PatchPanda.Web/Program.cs, PatchPanda.Units/*, README.md
Reads the Docker hook label, registers and injects HookService, runs hooks after successful updates, updates test construction, and documents usage and variables.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Possibly related PRs

Poem

🐰 Hooks now hop when updates land,
With old and new versions close at hand.
Scripts receive their vars in flight,
Then logs report if all went right.
A carrot cheer for post-update fun!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 25.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title is concise and accurately reflects the main addition of a post-update hook.
Linked Issues check ✅ Passed The PR adds a post-update hook with script execution and update variables, matching the requested hook-based extension in #89.
Out of Scope Changes check ✅ Passed The changes are focused on the hook feature and supporting wiring, with no clear unrelated additions.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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

Choose a reason for hiding this comment

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

Actionable comments posted: 8

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@PatchPanda.Web/Services/DockerService.cs`:
- Line 18: Remove the unused HookService dependency from DockerService: delete
the private readonly field _hookService, remove the HookService parameter from
the DockerService constructor and its assignment, and update any constructor
call sites to stop passing a HookService; ensure no other references to
_hookService remain in DockerService (the hook execution belongs in
UpdateService), and run a build to verify compilation.
- Around line 20-27: The DockerService constructor now requires a HookService
parameter, so update the Mock<DockerService> usages in UpdateServiceTests to
supply a HookService mock: create a Mock<HookService> (e.g., hookServiceMock)
and pass hookServiceMock.Object as the final argument to each
Mock<DockerService> constructor call that currently constructs DockerService
with logger, IDbContextFactory<DataContext>, IVersionService, IPortainerService,
and IFileService; ensure the symbol names match (DockerService constructor and
HookService) and the new mock is used where the other service mocks are
provided.

In `@PatchPanda.Web/Services/HookServices.cs`:
- Around line 59-62: The hook runner (ExecuteHookAsync) currently only logs
non-zero exit codes, so callers like UpdateService can't react via their
existing exception handling; change the non-zero path in ExecuteHookAsync to
throw an exception (e.g., InvalidOperationException or a specific
HookExecutionException) that includes the scriptPath and process.ExitCode in the
message, while optionally keeping the _logger.LogError call for diagnostics, so
UpdateService's catch block will be triggered on hook failures.
- Around line 44-52: Ensure the process output streams are started reading
immediately after process.Start() and before awaiting process termination: call
process.StandardOutput.ReadToEndAsync() and
process.StandardError.ReadToEndAsync() right after process.Start(), store the
resulting stdoutTask and stderrTask, then await process.WaitForExitAsync() and
finally await stdoutTask/stderrTask to collect outputs; reference the Start(),
StandardOutput.ReadToEndAsync(), StandardError.ReadToEndAsync(),
WaitForExitAsync(), stdoutTask and stderrTask symbols when making the change.
- Around line 27-34: The ProcessStartInfo code interpolates scriptPath into a
bash command (FileName="/bin/bash", Arguments=$"-c \"{scriptPath}\"") which
enables command injection and breaks on Windows; change it to avoid shell
interpolation by launching the script directly (use ProcessStartInfo.FileName =
scriptPath or use ProcessStartInfo.ArgumentList to pass the script as an
argument) and add OS branching using RuntimeInformation.IsOSPlatform to choose a
shell only when necessary (e.g., use /bin/bash on Linux when running shell
commands, use pwsh or cmd on Windows), and validate/normalize scriptPath (ensure
it is an absolute path, exists, and contains no unsafe characters) before
starting the process; update the code in HookServices.cs around the
ProcessStartInfo creation (references: ProcessStartInfo, FileName, Arguments,
scriptPath) accordingly.

In `@PatchPanda.Web/Services/UpdateService.cs`:
- Around line 778-803: The current post-update hook invocation uses
stack.ConfigFile (possibly null) as the PP_PROJECT_DIR arg in
_hookService.ExecuteHookAsync, which causes hooks that do "cd $PP_PROJECT_DIR"
to fail for Portainer-managed stacks; update the block that checks
targetApp.PostUpdateHook to first detect stack.ConfigFile == null and in that
case either (preferred) skip executing the hook and call _logger.LogWarning with
context (targetApp.Name and that it's a Portainer-managed stack), or
(alternative) supply a safe fallback path (e.g., a dedicated runtime directory)
instead of string.Empty; ensure the chosen behavior is applied where
_hookService.ExecuteHookAsync is invoked so hooks never receive an empty project
dir.

In `@README.md`:
- Around line 233-244: The README example post-update hook is missing
surrounding blank lines for the heading and uses unquoted shell variables which
break on paths with spaces; update the script under the "Example post-update
hook script" to add a blank line before and after the fenced bash block, and
quote variables by changing uses of PP_PROJECT_DIR to "$PP_PROJECT_DIR" and
interpolate version variables in the commit message as "${PP_OLD_VERSION}" and
"${PP_NEW_VERSION}" so the git commit -m line reads a safe, properly expanded
string.
- Around line 214-223: The "Post-hooks" section in README.md needs proper
Markdown spacing for linting: add a blank line above and below the "##
Post-hooks" heading and ensure there is a blank line before the fenced code
block and after its closing ```; also ensure the fenced block specifies the
language (```yaml) and the code block content remains unchanged — update the
block around the "myservice" labels example so the heading and code fence have
blank lines on both sides to satisfy the linter.
🪄 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: Repository UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 7d97f459-2cc0-4d19-afcb-6957c1049dfe

📥 Commits

Reviewing files that changed from the base of the PR and between 7afcb22 and bcdf892.

📒 Files selected for processing (9)
  • PatchPanda.Web/Entities/Container.cs
  • PatchPanda.Web/Migrations/20260323223243_AddPostUpdateHook.Designer.cs
  • PatchPanda.Web/Migrations/20260323223243_AddPostUpdateHook.cs
  • PatchPanda.Web/Migrations/DataContextModelSnapshot.cs
  • PatchPanda.Web/Program.cs
  • PatchPanda.Web/Services/DockerService.cs
  • PatchPanda.Web/Services/HookServices.cs
  • PatchPanda.Web/Services/UpdateService.cs
  • README.md

Comment thread PatchPanda.Web/Services/DockerService.cs Outdated
Comment thread PatchPanda.Web/Services/DockerService.cs
Comment thread PatchPanda.Web/Services/HookServices.cs
Comment thread PatchPanda.Web/Services/HookServices.cs Outdated
Comment thread PatchPanda.Web/Services/HookServices.cs
Comment thread PatchPanda.Web/Services/UpdateService.cs
Comment thread README.md
Comment thread README.md
@benssson

benssson commented Apr 2, 2026

Copy link
Copy Markdown

It would be great to get a pre-update hook as well. My use case is to create a zfs snapshot of my data directory before an update to make rollbacks trivial.

@dkorecko

dkorecko commented Apr 9, 2026

Copy link
Copy Markdown
Owner

Hey @githubkusi, thank you for the PR! Could you please address coderabbit's comments before it's merged? And for the pre-update @benssson, since we already have most of the logic here once this is merged then should be easy to implement.

@githubkusi

Copy link
Copy Markdown
Author

Hey @dkorecko, yes, sure, I'll address coderabbits comments, I just wanted to make sure you're happy with the general direction of this PR before I invest time.

@dkorecko

dkorecko commented Apr 9, 2026

Copy link
Copy Markdown
Owner

Hey @dkorecko, yes, sure, I'll address coderabbits comments, I just wanted to make sure you're happy with the general direction of this PR before I invest time.

The direction looks good to me!

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
PatchPanda.Web/Services/HookServices.cs (1)

64-71: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

No timeout on hook process execution.

If the hook script hangs (e.g., waiting for input, network stall, infinite loop), WaitForExitAsync blocks indefinitely. This stalls the update background service, leaves the job in "processing" state, and prevents future updates. Consider adding a configurable timeout with process termination on expiry.

🔒 Proposed fix: add timeout with process kill
 using var process = new Process { StartInfo = startInfo };
 
 process.Start();
 
 var stdoutTask = process.StandardOutput.ReadToEndAsync();
 var stderrTask = process.StandardError.ReadToEndAsync();
 
-await process.WaitForExitAsync();
+var timeout = TimeSpan.FromMinutes(5);
+if (!await process.WaitForExitAsync(timeout))
+{
+    process.Kill(entireProcessTree: true);
+    throw new TimeoutException($"Hook script '{scriptPath}' timed out after {timeout.TotalMinutes} minutes.");
+}
 
 var stdout = await stdoutTask;
 var stderr = await stderrTask;
🤖 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 `@PatchPanda.Web/Services/HookServices.cs` around lines 64 - 71, The hook
execution in HookServices’s process flow can block forever because
WaitForExitAsync has no timeout. Add a configurable timeout around the process
execution in the same method that starts the Process and reads stdout/stderr,
then terminate the hook process if it exceeds the limit and handle that failure
path cleanly so the background update service can continue.
PatchPanda.Web/Services/DockerService.cs (1)

170-175: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

PostUpdateHook is not synced for existing containers in ResetComposeStacks.

The new PostUpdateHook field is populated when creating a new Container (line 170-175), but when an existing container is updated during reset (lines 289-300), PostUpdateHook is not copied from the running container to the existing database record. If a user adds, changes, or removes the patchpanda.hook.post_update Docker label, the stale value persists in the database after a reset.

🐛 Proposed fix: sync PostUpdateHook for existing containers
                    existingContainer.Uptime = runningContainer.Uptime;
                    existingContainer.CurrentSha = runningContainer.CurrentSha;
                    existingContainer.GitHubRepo = runningContainer.GitHubRepo;
                    existingContainer.SecondaryGitHubRepos = runningContainer.SecondaryGitHubRepos;
                    existingContainer.Version = runningContainer.Version;
                    existingContainer.TargetImage = runningContainer.TargetImage;
                    existingContainer.Regex = runningContainer.Regex;
+                   existingContainer.PostUpdateHook = runningContainer.PostUpdateHook;
🤖 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 `@PatchPanda.Web/Services/DockerService.cs` around lines 170 - 175, The
`PostUpdateHook` value is being set when creating a new container in
`DockerService`, but `ResetComposeStacks` does not copy that field onto an
existing container record during updates, leaving stale data in the database.
Update the existing-container sync path in `ResetComposeStacks` to read the
current `patchpanda.hook.post_update` label and assign it to `PostUpdateHook` on
the persisted container, matching the logic used in the container creation flow.
PatchPanda.Web/Services/UpdateService.cs (1)

2-2: 📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Move PatchPanda.Services to GlobalUsings.cs
UpdateService.cs should not declare this namespace locally; add global using PatchPanda.Services; to PatchPanda.Web/GlobalUsings.cs and remove the file-level import here.

🤖 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 `@PatchPanda.Web/Services/UpdateService.cs` at line 2, The file-level `using
PatchPanda.Services;` in `UpdateService.cs` should be removed and replaced with
a `global using PatchPanda.Services;` in `GlobalUsings.cs` so the namespace is
shared across the project. Update the `UpdateService` file to rely on the global
import and keep the namespace declaration centralized in `GlobalUsings.cs`.

Source: Coding guidelines

🤖 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 `@PatchPanda.Web/Services/HookServices.cs`:
- Line 42: Remove the explanatory inline comment in HookServices, since the code
should be self-documenting instead of relying on narration. Update the relevant
logic around the script-path argument handling in the HookServices code path,
and keep the implementation clear enough that the comment is no longer needed.

---

Outside diff comments:
In `@PatchPanda.Web/Services/DockerService.cs`:
- Around line 170-175: The `PostUpdateHook` value is being set when creating a
new container in `DockerService`, but `ResetComposeStacks` does not copy that
field onto an existing container record during updates, leaving stale data in
the database. Update the existing-container sync path in `ResetComposeStacks` to
read the current `patchpanda.hook.post_update` label and assign it to
`PostUpdateHook` on the persisted container, matching the logic used in the
container creation flow.

In `@PatchPanda.Web/Services/HookServices.cs`:
- Around line 64-71: The hook execution in HookServices’s process flow can block
forever because WaitForExitAsync has no timeout. Add a configurable timeout
around the process execution in the same method that starts the Process and
reads stdout/stderr, then terminate the hook process if it exceeds the limit and
handle that failure path cleanly so the background update service can continue.

In `@PatchPanda.Web/Services/UpdateService.cs`:
- Line 2: The file-level `using PatchPanda.Services;` in `UpdateService.cs`
should be removed and replaced with a `global using PatchPanda.Services;` in
`GlobalUsings.cs` so the namespace is shared across the project. Update the
`UpdateService` file to rely on the global import and keep the namespace
declaration centralized in `GlobalUsings.cs`.
🪄 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: Repository UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: a9739ec2-fef1-414d-ac85-88720c6d5e26

📥 Commits

Reviewing files that changed from the base of the PR and between bcdf892 and 7972b8a.

📒 Files selected for processing (4)
  • PatchPanda.Web/Services/DockerService.cs
  • PatchPanda.Web/Services/HookServices.cs
  • PatchPanda.Web/Services/UpdateService.cs
  • README.md

Comment thread PatchPanda.Web/Services/HookServices.cs Outdated
coderabbitai[bot]
coderabbitai Bot previously approved these changes Jul 9, 2026
kusi added 4 commits July 9, 2026 16:50
Hook service after an update has been performed
Author: Copilot

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
PatchPanda.Web/Services/DockerService.cs (1)

295-315: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Sync PostUpdateHook from running containers to existing containers.

PostUpdateHook is loaded from the Docker label (lines 176-181) but is missing from the existing-container update block. This means the hook only works for containers discovered for the first time — any container already in the database will never pick up a new or changed patchpanda.hook.post_update label.

🐛 Proposed fix: add PostUpdateHook to the sync block
 if (existingContainer is not null)
 {
     existingContainer.Uptime = runningContainer.Uptime;
     existingContainer.CurrentSha = runningContainer.CurrentSha;
     existingContainer.GitHubRepo = runningContainer.GitHubRepo;
     existingContainer.SecondaryGitHubRepos = runningContainer.SecondaryGitHubRepos;
     existingContainer.Version = runningContainer.Version;
     existingContainer.TargetImage = runningContainer.TargetImage;
     existingContainer.Regex = runningContainer.Regex;
+    existingContainer.PostUpdateHook = runningContainer.PostUpdateHook;
     if (
🤖 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 `@PatchPanda.Web/Services/DockerService.cs` around lines 295 - 315, The
existing-container merge in DockerService should also sync the PostUpdateHook
value from runningContainer to existingContainer, since it is already populated
from the Docker label when containers are discovered. Update the sync block in
the existingContainer branch to copy PostUpdateHook alongside the other fields,
using the same pattern as GitHubRepo, Version, and TargetImage so
database-backed containers pick up hook changes.
🤖 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 `@PatchPanda.Web/Program.cs`:
- Line 3: Move the PatchPanda.Services import out of Program and into
GlobalUsings, since the coding guideline is to keep shared namespace imports
centralized there. Update Program.cs to remove the per-file using and add the
same import to PatchPanda.Web/GlobalUsings.cs so any references to symbols from
PatchPanda.Services continue to resolve through the shared global using.

In `@PatchPanda.Web/Services/DockerService.cs`:
- Line 247: The DbContext disposal in the async flow should use asynchronous
disposal instead of synchronous disposal. Update the database access in the
DockerService method that creates the context with
_dbContextFactory.CreateDbContext() to use await using so the DbContext is
disposed correctly in the async method.

In `@PatchPanda.Web/Services/HookServices.cs`:
- Line 1: Move the System.Diagnostics import out of HookServices and into
GlobalUsings so the namespace is available project-wide. Remove the per-file
using statement from HookServices and add the equivalent global using in
GlobalUsings.cs, following the existing global import pattern used by the
project.

In `@PatchPanda.Web/Services/UpdateService.cs`:
- Line 2: Move the PatchPanda.Services namespace import out of UpdateService and
into PatchPanda.Web/GlobalUsings, since this project expects shared imports to
live in the global usings file instead of per-file using statements. Remove the
using from UpdateService and add the corresponding global import in GlobalUsings
so any types referenced there still resolve through the shared namespace setup.

---

Outside diff comments:
In `@PatchPanda.Web/Services/DockerService.cs`:
- Around line 295-315: The existing-container merge in DockerService should also
sync the PostUpdateHook value from runningContainer to existingContainer, since
it is already populated from the Docker label when containers are discovered.
Update the sync block in the existingContainer branch to copy PostUpdateHook
alongside the other fields, using the same pattern as GitHubRepo, Version, and
TargetImage so database-backed containers pick up hook 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

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: ed152f4a-95fd-455d-8ebc-04b9704bfe7b

📥 Commits

Reviewing files that changed from the base of the PR and between 0439e30 and d65e15c.

📒 Files selected for processing (9)
  • PatchPanda.Web/Entities/Container.cs
  • PatchPanda.Web/Migrations/20260323223243_AddPostUpdateHook.Designer.cs
  • PatchPanda.Web/Migrations/20260323223243_AddPostUpdateHook.cs
  • PatchPanda.Web/Migrations/DataContextModelSnapshot.cs
  • PatchPanda.Web/Program.cs
  • PatchPanda.Web/Services/DockerService.cs
  • PatchPanda.Web/Services/HookServices.cs
  • PatchPanda.Web/Services/UpdateService.cs
  • README.md

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Caution

Inline review comments failed to post. This is likely due to GitHub's internal server error or limits when posting large numbers of comments. If you are seeing this consistently it is likely a permissions issue. Please check "Moderation" -> "Code review limits" under your organization settings.

Actionable comments posted: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
PatchPanda.Web/Services/DockerService.cs (1)

295-315: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Sync PostUpdateHook from running containers to existing containers.

PostUpdateHook is loaded from the Docker label (lines 176-181) but is missing from the existing-container update block. This means the hook only works for containers discovered for the first time — any container already in the database will never pick up a new or changed patchpanda.hook.post_update label.

🐛 Proposed fix: add PostUpdateHook to the sync block
 if (existingContainer is not null)
 {
     existingContainer.Uptime = runningContainer.Uptime;
     existingContainer.CurrentSha = runningContainer.CurrentSha;
     existingContainer.GitHubRepo = runningContainer.GitHubRepo;
     existingContainer.SecondaryGitHubRepos = runningContainer.SecondaryGitHubRepos;
     existingContainer.Version = runningContainer.Version;
     existingContainer.TargetImage = runningContainer.TargetImage;
     existingContainer.Regex = runningContainer.Regex;
+    existingContainer.PostUpdateHook = runningContainer.PostUpdateHook;
     if (
🤖 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 `@PatchPanda.Web/Services/DockerService.cs` around lines 295 - 315, The
existing-container merge in DockerService should also sync the PostUpdateHook
value from runningContainer to existingContainer, since it is already populated
from the Docker label when containers are discovered. Update the sync block in
the existingContainer branch to copy PostUpdateHook alongside the other fields,
using the same pattern as GitHubRepo, Version, and TargetImage so
database-backed containers pick up hook changes.
🤖 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 `@PatchPanda.Web/Program.cs`:
- Line 3: Move the PatchPanda.Services import out of Program and into
GlobalUsings, since the coding guideline is to keep shared namespace imports
centralized there. Update Program.cs to remove the per-file using and add the
same import to PatchPanda.Web/GlobalUsings.cs so any references to symbols from
PatchPanda.Services continue to resolve through the shared global using.

In `@PatchPanda.Web/Services/DockerService.cs`:
- Line 247: The DbContext disposal in the async flow should use asynchronous
disposal instead of synchronous disposal. Update the database access in the
DockerService method that creates the context with
_dbContextFactory.CreateDbContext() to use await using so the DbContext is
disposed correctly in the async method.

In `@PatchPanda.Web/Services/HookServices.cs`:
- Line 1: Move the System.Diagnostics import out of HookServices and into
GlobalUsings so the namespace is available project-wide. Remove the per-file
using statement from HookServices and add the equivalent global using in
GlobalUsings.cs, following the existing global import pattern used by the
project.

In `@PatchPanda.Web/Services/UpdateService.cs`:
- Line 2: Move the PatchPanda.Services namespace import out of UpdateService and
into PatchPanda.Web/GlobalUsings, since this project expects shared imports to
live in the global usings file instead of per-file using statements. Remove the
using from UpdateService and add the corresponding global import in GlobalUsings
so any types referenced there still resolve through the shared namespace setup.

---

Outside diff comments:
In `@PatchPanda.Web/Services/DockerService.cs`:
- Around line 295-315: The existing-container merge in DockerService should also
sync the PostUpdateHook value from runningContainer to existingContainer, since
it is already populated from the Docker label when containers are discovered.
Update the sync block in the existingContainer branch to copy PostUpdateHook
alongside the other fields, using the same pattern as GitHubRepo, Version, and
TargetImage so database-backed containers pick up hook 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

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: ed152f4a-95fd-455d-8ebc-04b9704bfe7b

📥 Commits

Reviewing files that changed from the base of the PR and between 0439e30 and d65e15c.

📒 Files selected for processing (9)
  • PatchPanda.Web/Entities/Container.cs
  • PatchPanda.Web/Migrations/20260323223243_AddPostUpdateHook.Designer.cs
  • PatchPanda.Web/Migrations/20260323223243_AddPostUpdateHook.cs
  • PatchPanda.Web/Migrations/DataContextModelSnapshot.cs
  • PatchPanda.Web/Program.cs
  • PatchPanda.Web/Services/DockerService.cs
  • PatchPanda.Web/Services/HookServices.cs
  • PatchPanda.Web/Services/UpdateService.cs
  • README.md
🛑 Comments failed to post (4)
PatchPanda.Web/Program.cs (1)

3-3: 📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Move using PatchPanda.Services; to GlobalUsings.cs.

As per coding guidelines, namespace imports should be added to PatchPanda.Web/GlobalUsings.cs instead of per-file using statements.

🤖 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 `@PatchPanda.Web/Program.cs` at line 3, Move the PatchPanda.Services import out
of Program and into GlobalUsings, since the coding guideline is to keep shared
namespace imports centralized there. Update Program.cs to remove the per-file
using and add the same import to PatchPanda.Web/GlobalUsings.cs so any
references to symbols from PatchPanda.Services continue to resolve through the
shared global using.

Source: Coding guidelines

PatchPanda.Web/Services/DockerService.cs (1)

247-247: 📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Use await using for DbContext in async method.

As per coding guidelines, when accessing the database in async methods, use await using var db = _dbContextFactory.CreateDbContext() instead of using.

♻️ Proposed fix
-        using var db = _dbContextFactory.CreateDbContext();
+        await using var db = _dbContextFactory.CreateDbContext();
📝 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.

        await using var db = _dbContextFactory.CreateDbContext();
🤖 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 `@PatchPanda.Web/Services/DockerService.cs` at line 247, The DbContext disposal
in the async flow should use asynchronous disposal instead of synchronous
disposal. Update the database access in the DockerService method that creates
the context with _dbContextFactory.CreateDbContext() to use await using so the
DbContext is disposed correctly in the async method.

Source: Coding guidelines

PatchPanda.Web/Services/HookServices.cs (1)

1-1: 📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Move using System.Diagnostics; to GlobalUsings.cs.

As per coding guidelines, namespace imports should be added to PatchPanda.Web/GlobalUsings.cs instead of per-file using statements.

🤖 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 `@PatchPanda.Web/Services/HookServices.cs` at line 1, Move the
System.Diagnostics import out of HookServices and into GlobalUsings so the
namespace is available project-wide. Remove the per-file using statement from
HookServices and add the equivalent global using in GlobalUsings.cs, following
the existing global import pattern used by the project.

Source: Coding guidelines

PatchPanda.Web/Services/UpdateService.cs (1)

2-2: 📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Move using PatchPanda.Services; to GlobalUsings.cs.

Per the project's coding guidelines, namespace imports should be added to PatchPanda.Web/GlobalUsings.cs instead of per-file using statements. This new import should be placed there rather than in this source file.

As per coding guidelines: "Add namespace imports to PatchPanda.Web/GlobalUsings.cs instead of per-file using statements."

♻️ Proposed change

Remove from UpdateService.cs:

-using PatchPanda.Services;

Add to PatchPanda.Web/GlobalUsings.cs:

+global using PatchPanda.Services;
📝 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.


🤖 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 `@PatchPanda.Web/Services/UpdateService.cs` at line 2, Move the
PatchPanda.Services namespace import out of UpdateService and into
PatchPanda.Web/GlobalUsings, since this project expects shared imports to live
in the global usings file instead of per-file using statements. Remove the using
from UpdateService and add the corresponding global import in GlobalUsings so
any types referenced there still resolve through the shared namespace setup.

Source: Coding guidelines

@githubkusi

Copy link
Copy Markdown
Author

I believe I've addressed all coderabbit complaints. Example script from the readme works fine for me. Looks good to you @dkorecko ?

Comment thread PatchPanda.Web/Services/UpdateService.cs Outdated
@dkorecko

Copy link
Copy Markdown
Owner

I believe I've addressed all coderabbit complaints. Example script from the readme works fine for me. Looks good to you @dkorecko ?

Only one comment, but looks good other than that! Once that's addressed and if workflows pass, we'll be good to merge and get this released 👍

@dkorecko

Copy link
Copy Markdown
Owner

Also, the app does not build as you can see 😄

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
PatchPanda.Web/Services/UpdateService.cs (2)

820-826: 🩺 Stability & Availability | 🟠 Major | 🏗️ Heavy lift

Bound post-update hook execution.

ExecuteHookAsync starts a child process and waits without a cancellation token or timeout. A hanging hook can leave Update awaiting indefinitely after the database has recorded the update as successful. Add bounded cancellation/timeout handling in HookService and pass the update token here.

🤖 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 `@PatchPanda.Web/Services/UpdateService.cs` around lines 820 - 826, Update
HookService.ExecuteHookAsync to accept and enforce a cancellation token with a
bounded timeout while awaiting the child process, then pass the existing update
cancellation token from the Update flow at the shown call site. Preserve
successful hook execution while ensuring a hanging or cancelled post-update hook
cannot leave Update awaiting indefinitely.

2-2: 📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Move this namespace import to PatchPanda.Web/GlobalUsings.cs.

UpdateService.cs should not add a per-file namespace import for HookService; centralize it in the Web project’s global usings file.

As per coding guidelines, **/*.cs imports must be added to PatchPanda.Web/GlobalUsings.cs instead of per-file using statements.

🤖 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 `@PatchPanda.Web/Services/UpdateService.cs` at line 2, Remove the
PatchPanda.Services using directive from UpdateService.cs and add the
corresponding global using to PatchPanda.Web/GlobalUsings.cs, keeping the file’s
HookService references compiling through the centralized import.

Source: Coding guidelines

🤖 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 `@PatchPanda.Units/Services/UpdateServiceTests.cs`:
- Around line 53-55: Add behavioral tests around the test factory’s HookService
wiring, covering successful PostUpdateHook execution, hook failure handling, and
the Portainer skip branch. Prefer injecting a deterministic hook abstraction
into the relevant UpdateService tests, and assert each path’s observable
behavior without changing unrelated service setup.

---

Outside diff comments:
In `@PatchPanda.Web/Services/UpdateService.cs`:
- Around line 820-826: Update HookService.ExecuteHookAsync to accept and enforce
a cancellation token with a bounded timeout while awaiting the child process,
then pass the existing update cancellation token from the Update flow at the
shown call site. Preserve successful hook execution while ensuring a hanging or
cancelled post-update hook cannot leave Update awaiting indefinitely.
- Line 2: Remove the PatchPanda.Services using directive from UpdateService.cs
and add the corresponding global using to PatchPanda.Web/GlobalUsings.cs,
keeping the file’s HookService references compiling through the centralized
import.
🪄 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: Repository UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 23476420-1f3e-49d3-bbb7-a295269b5560

📥 Commits

Reviewing files that changed from the base of the PR and between d65e15c and 71ec35b.

📒 Files selected for processing (3)
  • PatchPanda.Units/GlobalUsings.cs
  • PatchPanda.Units/Services/UpdateServiceTests.cs
  • PatchPanda.Web/Services/UpdateService.cs

Comment on lines +53 to 55
_notificationService.Object,
new HookService(new Mock<ILogger<HookService>>().Object)
);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🔵 Trivial | 🏗️ Heavy lift

Add behavioral coverage for post-update hooks.

The test factory now wires HookService, but no test configures PostUpdateHook or verifies execution, failure handling, or the Portainer skip branch. Add deterministic tests for these paths, preferably through an injectable hook abstraction.

🤖 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 `@PatchPanda.Units/Services/UpdateServiceTests.cs` around lines 53 - 55, Add
behavioral tests around the test factory’s HookService wiring, covering
successful PostUpdateHook execution, hook failure handling, and the Portainer
skip branch. Prefer injecting a deterministic hook abstraction into the relevant
UpdateService tests, and assert each path’s observable behavior without changing
unrelated service setup.

@githubkusi

Copy link
Copy Markdown
Author

Now, I guess coderabbit complains about not having tests for the post-update hook functionality. You want tests for theses as well?

@dkorecko

dkorecko commented Aug 3, 2026

Copy link
Copy Markdown
Owner

@githubkusi looks good, thanks! So you've been using this for some time now with no issues? Would you say it's good to be released?

@githubkusi

Copy link
Copy Markdown
Author

@dkorecko I just tested it locally so far, let me also run it for a while on my productive system. I will let you know

kusi added 2 commits August 4, 2026 11:40
Previously ResetComposeStacks only set PostUpdateHook when creating a
new Container entity, so changing the patchpanda.hook.post_update
label on an already-tracked container was never picked up on
subsequent rescans. Now it's synced like the other container fields.
@githubkusi

githubkusi commented Aug 9, 2026

Copy link
Copy Markdown
Author

I'm using it on my production system now, all fine. I added a small fix which avoids the need to restart PatchPanda after a label change. And updated the readme. Other than that it's ready to go I guess @dkorecko

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Pre/post hooks (or git integration?)

3 participants