Skip to content

chore: lifecycle-stage template method and logger/resolution dedups#232

Open
dan2k3k4 wants to merge 10 commits into
devfrom
chore/lifecycle-template-and-dedups
Open

chore: lifecycle-stage template method and logger/resolution dedups#232
dan2k3k4 wants to merge 10 commits into
devfrom
chore/lifecycle-template-and-dedups

Conversation

@dan2k3k4

@dan2k3k4 dan2k3k4 commented Jul 17, 2026

Copy link
Copy Markdown
Member

What

PR 6 of 6 — the closer of the advisory plan set: plans 009 + 010, the "less code" refactors. Net −221 lines of app code (+232 lines of new tests).

Plan 009 — runLifecyclePhase() template method (17 of 19 traits)

Every lifecycle stage repeated the same ~18-line skeleton (log context, four validation booleans, the 8-line validate call, *_RUNNING save, try/catch → *_FAILED, *_COMPLETED save) with hand-typed status triples. Now one template method on Generic\PolydockApp carries the skeleton; each stage supplies its status quadruple, validation flags, and a body closure (which may short-circuit after setting its own terminal status — Deploy/Remove Lagoon-error paths preserved exactly).

  • Migrated cluster-by-cluster across 8 commits, full suite green after every commit: Remove → Create → Deploy → Upgrade → Claim → AmazeeClaw + DependencyTrack.
  • Behavior preservation: log/status message strings byte-identical; adopted-instance guards stay ahead of the template in all three Remove traits; AmazeeClaw's injected-credentials hook order unchanged.
  • 2 justified exceptions (within the plan's allowance, recorded in cluster commits): PollDeployProgressAppInstanceTrait (polling flow — no running/completed shape) and UpgradeAppInstanceTrait (sets UPGRADE_RUNNING without saving; the template's save would newly fire a status event — behavior change, so left).
  • New RunLifecyclePhaseTest pins the template's three exit paths (complete / body short-circuit / exception → failed).

Plan 010 — logger trait + small dedups

  • Both service providers hand-rolled the 6 logger methods PolydockAppLoggerTrait supplies — and their setLogger lacked the trait's cache-flush, silently dropping buffered startup logs (latent bug, now fixed by reuse). Trait docblocks corrected to @return $this.
  • LagoonProjectPurgeService::makeWithDefaults() eager-duplicated its own lazy client() resolution — dropped.
  • The API's triple-implemented group_id/group_slug XOR-validation + lookup → one resolveExistingGroupFromRequest() helper; validation messages byte-identical (API tests pass unchanged).

Testing

  • php artisan test — 454 passed (3 new)
  • ./vendor/bin/phpstan analyse — no errors; Pint clean
  • Reviewer note: executed per the pre-written implementation plans; every hunk traced to a plan step during review.

Greptile Summary

This PR closes two refactoring plans: a runLifecyclePhase() template method on Generic\PolydockApp that absorbs the ~18-line boilerplate skeleton shared by 17 lifecycle stage traits, and a set of smaller deduplications covering the logger trait, group resolution, and the Lagoon purge service factory.

  • Plan 009: Each lifecycle trait's method now calls runLifecyclePhase() with a status quadruple (expected/running/completed/failed) and a body closure; the skeleton (log start, validate, save RUNNING, try/catch → FAILED, save COMPLETED) lives in one place. Two justified exceptions remain: the poll-progress trait (no running/completed shape) and UpgradeAppInstanceTrait (intentionally skips saving UPGRADE_RUNNING to avoid a new status event).
  • Plan 010: Both service providers now use PolydockAppLoggerTrait instead of six hand-rolled methods — fixing a latent bug where their setLogger omitted the CacheLogger buffer flush, silently dropping startup logs. LagoonProjectPurgeService::makeWithDefaults() is simplified to rely on the already-present lazy client() resolver, and the three copies of group-ID/slug XOR-validation in the API controller are consolidated into one resolveExistingGroupFromRequest() helper.
  • Three new tests pin the template's exit paths (null body → COMPLETED, thrown exception → FAILED, non-null return → short-circuit).

Confidence Score: 5/5

Safe to merge — all lifecycle stage migrations are behavior-preserving, the latent CacheLogger-flush bug is correctly fixed by trait adoption, and the purge-service factory simplification is backed by the already-present lazy resolver.

The template method is a straightforward extraction: status transitions, log strings, and validation flags match the originals, the two documented exceptions are recorded in commit messages, and 454 tests plus PHPStan confirm nothing regressed. No behavioral change introduces incorrect state or data loss.

No files require special attention. The Remove trait adopted-instance logging shift and the resolveTargetGroup XOR-validation addition were covered in prior review rounds.

Important Files Changed

Filename Overview
app/Polydock/Apps/Generic/PolydockApp.php Adds runLifecyclePhase() template method; skeleton is correct — validates, saves RUNNING, calls body, catches \Exception, saves COMPLETED/FAILED. Named-parameter defaults preserve all per-trait flag combinations.
app/Http/Controllers/Api/AuthenticatedApiController.php Triple-duplicated group-resolution logic consolidated into resolveExistingGroupFromRequest(); XOR validation now present on all three callers including the previously-unchecked resolveTargetGroup path (behavioral shift noted in prior comments).
app/PolydockServiceProviders/PolydockServiceProviderAmazeeAiBackend.php Six hand-rolled logger methods replaced by use PolydockAppLoggerTrait;; fixes latent bug where setLogger didn't flush the CacheLogger buffer before switching to a real logger.
app/PolydockServiceProviders/PolydockServiceProviderFTLagoon.php Same logger-trait adoption as AmazeeAiBackend; same latent CacheLogger-flush bug now fixed.
app/Services/LagoonProjectPurgeService.php makeWithDefaults() now defers client resolution to the existing lazy client() method; constructor's second param is already ?Client $client = null so no arity mismatch.
tests/Feature/Polydock/RunLifecyclePhaseTest.php New feature test covers all three template exit paths (null body → COMPLETED, exception → FAILED, non-null return → short-circuit). Validation is correctly stubbed out.
app/Polydock/Apps/Generic/Traits/Deploy/DeployAppInstanceTrait.php Correctly passes DEPLOY_RUNNING as both $runningStatus and $completedStatus; the double-RUNNING save matches the original behavior and a comment documents the intentional design.
app/Polydock/Apps/Generic/Traits/Remove/RemoveAppInstanceTrait.php Adopted-instance guard preserved ahead of the template call; non-adopted path correctly delegates to runLifecyclePhase.
app/Polydock/Apps/AmazeeClaw/Traits/Claim/ClaimAppInstanceTrait.php Business logic faithfully moved into the body closure; AI-credential hook ordering and claim-failure short-circuit path both preserved.
app/Polydock/Core/Traits/PolydockAppLoggerTrait.php Docblock @return annotations corrected from explicit class union to $this; no functional changes to logger methods.

Comments Outside Diff (1)

  1. app/Http/Controllers/Api/AuthenticatedApiController.php, line 88-97 (link)

    P2 resolveTargetGroup silently gains XOR validation it didn't have before

    The old resolveTargetGroup (used by create/register flows) had no mutual-exclusion check — it just returned the first matching field. By delegating to resolveExistingGroupFromRequest, this method now throws a ValidationException if both group_id and group_slug are present in the request. Any API client or test that previously relied on "group_id wins when both are sent" through this code path will start seeing a 422 instead. The change is logically correct, but it is an undocumented behavior shift for callers of resolveTargetGroup.

Reviews (2): Last reviewed commit: "chore: restore the starting log line for..." | Re-trigger Greptile

dan2k3k4 added 9 commits July 17, 2026 18:33
PollDeployProgressAppInstanceTrait is left unmigrated: it is a polling
trait with no running/completed transition of its own — it never sets a
*_RUNNING status, derives the instance status from a Lagoon deployment
state mapping, and early-returns without a terminal status, which does
not fit the validate -> running -> body -> completed template.
UpgradeAppInstanceTrait is left unmigrated: it sets UPGRADE_RUNNING
without persisting it (no ->save()), while the template always saves the
running status. Saving would fire the UPGRADE_RUNNING status-changed
event and its listener dispatch, changing behavior.
Implements plan 010 from the advisory audit:

- Both Polydock service providers hand-rolled the six logger methods that
  PolydockAppLoggerTrait already supplies — and their setLogger copies
  lacked the trait's cache-logger flush, silently dropping startup log
  lines buffered before a real logger attached. Both now use the trait
  (~100 lines removed); the trait's over-specific @return docblocks
  (hardcoding its two previous consumers) become @return $this.
- LagoonProjectPurgeService::makeWithDefaults() duplicated the exact
  client resolution its lazy client() method performs — dropped the eager
  copy.
- The authenticated API implemented the mutually-exclusive
  group_id/group_slug validation + lookup three times (getInstances,
  assignInstanceToGroup, resolveTargetGroup). One shared
  resolveExistingGroupFromRequest() helper now serves all three, with
  byte-identical validation messages; resolveTargetGroup keeps its
  group_name/personal-group creation paths.
Comment thread app/Polydock/Apps/Generic/Traits/Remove/RemoveAppInstanceTrait.php
Review feedback: before the template-method refactor the ': starting'
info line fired unconditionally ahead of the adopted guard, so adopted
detaches always produced it. The refactor deferred it into
runLifecyclePhase, which only the non-adopted path reaches — restore the
line inside the adopted guard in all three Remove traits so log
correlation is unchanged.
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.

1 participant