Skip to content

feat(hooks): invoke customActions from deploy.*.hooks.before/after#10067

Draft
bogdannazarenko wants to merge 3 commits intoGoogleContainerTools:mainfrom
unburdenedio:feat/lifecycle-hooks-run-custom-actions
Draft

feat(hooks): invoke customActions from deploy.*.hooks.before/after#10067
bogdannazarenko wants to merge 3 commits intoGoogleContainerTools:mainfrom
unburdenedio:feat/lifecycle-hooks-run-custom-actions

Conversation

@bogdannazarenko
Copy link
Copy Markdown
Contributor

Phase 3: Cloud Deploy custom-target parity — action hooks

Final phase of the three-part series started in #10065 and continued in #10066. Neither earlier PR is required to land first; this branch is cut from main.

What's in

  • New action: hook union member on DeployHookItem:

    deploy:
      kubectl:
        hooks:
          before:
            - action: { name: db-migrate }
          after:
            - action: { name: smoke-test }
    customActions:
      - name: db-migrate
        containers: [{ image: myorg/migrator, ... }]

    Reuses the existing customActions runtime — a single action definition is runnable standalone via skaffold exec or wrapped as a deploy hook, so users aren't forced to choose between the two shapes.

  • ActionInvoker interface in pkg/skaffold/hooks. A tiny SetDefaultActionInvoker setter is wired once in runner.New right after GetActionsRunner succeeds. The hooks package stays free of any pkg/skaffold/actions import, preserving the existing dependency tree.

  • actionsRunnerInvoker adapter in pkg/skaffold/runner satisfies hooks.ActionInvoker by delegating to the runner's Exec with nil artifact slices — hooks are not themselves build producers.

  • Load-time validation (validateActionHookRefs): unknown action references fail at config parse rather than deep inside a deploy. Missing name fields are flagged too. Covers kubectl, helm, and kpt deployers.

  • Unit tests (pkg/skaffold/hooks/action_test.go): ordered dispatch, first-failure aborts remaining hooks, missing-invoker sentinel.

  • Docs + runnable example: new 'Action hooks' section in docs-v2/docs/lifecycle-hooks.md; examples/hooks-action/ (mirrored under integration/examples/) exercises the schema end-to-end with pre-deploy + post-deploy busybox actions.

What's deliberately out of scope

  • CloudRunDeployHooks still accepts []HostHook only. Wiring action: support into the CloudRun deployer would require promoting its hook list to a union type; saving that for a dedicated follow-up to keep this diff focused.
  • Pre-delete / post-delete hooks (PLAN's Phase 4).
  • Render / build ActionHook support — not requested.
  • No schema version bump: this PR keeps latest at skaffold/v4beta14 per the maintainer-driven hack/new-version.sh workflow. If you'd prefer the freeze done here, happy to add a commit.

Test results

  • go build ./... — clean
  • go test -short -race ./pkg/skaffold/hooks/... ./pkg/skaffold/schema/validation/... ./pkg/skaffold/runner/... ./pkg/skaffold/deploy/... ./cmd/... — green
    (the pre-existing TestNewEnvClient/DOCKER_HOST_not_set,_output_invalid_host and TestNewRunner failures reproduce on main and are unrelated)
  • ./hack/check-samples.sh — passes

@bogdannazarenko bogdannazarenko requested a review from a team as a code owner April 24, 2026 17:03
Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces 'Action hooks,' allowing users to reference custom actions as deployment lifecycle hooks. The implementation includes the ActionInvoker interface to prevent package cycles, updated validation to ensure action names exist, and new documentation and examples. A review comment points out that adding fields to the v4beta14 schema might violate versioning policies if that version is already frozen, suggesting a move to a new schema version.

Comment thread pkg/skaffold/schema/latest/config.go
@bogdannazarenko bogdannazarenko force-pushed the feat/lifecycle-hooks-run-custom-actions branch from 6453e17 to c8d0251 Compare April 24, 2026 17:15
@bogdannazarenko
Copy link
Copy Markdown
Contributor Author

bogdannazarenko commented Apr 24, 2026

Waiting on maintainers to cut new schema version on main branch

Adds a new ActionHook to the deploy lifecycle-hook union so users can
reference an existing customActions entry from
deploy.*.hooks.before / .after instead of re-implementing the
containerized task as a host hook with ad-hoc docker run arguments:

    deploy:
      kubectl: { ... }
      hooks:
        before:
          - action: { name: pre-deploy-check }
        after:
          - action: { name: migrate-db }
    customActions:
      - name: migrate-db
        containers: [{ image: myorg/migrator:latest, ... }]

Implementation
  * schema: ActionHook{Name} joins HostHook and ContainerHook as a
    third oneOf=deploy_hook union member on DeployHookItem.
  * hooks: new package-level ActionInvoker interface plus a
    SetDefaultActionInvoker setter so deploy hook runners can dispatch
    without an import cycle back into pkg/skaffold/actions. The
    invoker is registered once in runner.New immediately after
    GetActionsRunner succeeds, mirroring how
    SetupStaticEnvOptions is wired.
  * runner: small actionsRunnerInvoker adapter satisfies the new
    interface by delegating to ActionsRunner.Exec with nil artifact
    slices (hooks do not themselves build).
  * deploy.go: the run() dispatch loop recognises ActionHook and
    calls runActionHook, which returns a helpful error if no invoker
    is wired (e.g. from tests that stub hooks out).

CloudRunDeployHooks still accepts only []HostHook and is therefore
unaffected; wiring ActionHook into CloudRun is a follow-up once the
CloudRun deployer grows its own union type.
Unit coverage for the new ActionHook dispatch path:
  - happy path fires pre then post hooks in declaration order
  - first failure aborts the remaining hooks and surfaces both the
    phase and the action name in the wrapped error
  - a config with ActionHook but no registered invoker returns the
    'no custom-actions runner available' sentinel

Also wires a new validateActionHookRefs pass into
ProcessWithRunContext so configs that reference an unknown custom
action fail at load time with a targeted message instead of deep
inside the deploy. Covers the three kubernetes-family deployers
(LegacyHelmDeploy, KubectlDeploy, KptDeploy); CloudRunDeployHooks
is intentionally still HostHook-only in this PR.
Adds a new 'Action hooks' section to docs-v2/docs/lifecycle-hooks.md
describing the action: union member, how it relates to customActions,
which deployers support it (kubectl, helm, kpt), and that it shares a
runtime with skaffold exec (so a single action definition is runnable
standalone or wrapped as a deploy hook).

Ships a runnable examples/hooks-action/ fixture (mirrored under
integration/examples/) with pre-deploy-check + post-deploy-smoke
actions and a minimal busybox Pod manifest so maintainers can smoke
test the new schema end-to-end.
@bogdannazarenko bogdannazarenko force-pushed the feat/lifecycle-hooks-run-custom-actions branch from c8d0251 to d7e5025 Compare April 24, 2026 17:37
@bogdannazarenko bogdannazarenko marked this pull request as draft April 24, 2026 20:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant