| name | scaffold-action |
| description | Generate a new jhelm action class and matching test file from a name |
| argument-hint | <ActionName> |
| allowed-tools | Bash(./mvnw *) |
Scaffold a new jhelm Action class
$ARGUMENTS is the action name in PascalCase without the Action suffix.
Example: /scaffold-action Lint → creates LintAction.java + LintActionTest.java.
Package: org.alexmond.jhelm.core
Source root: jhelm-core/src/main/java/org/alexmond/jhelm/core/
Test root: jhelm-core/src/test/java/org/alexmond/jhelm/core/
Step 1: Determine dependencies
Ask (or infer from context) whether the action needs:
Engine engine — only if it renders templates (install, upgrade, template)
KubeService kubeService — only if it talks to Kubernetes (install, upgrade, uninstall, rollback, status, list, history)
Use only the fields actually required.
Step 2: Create the action class
File: jhelm-core/src/main/java/org/alexmond/jhelm/core/$ARGUMENTS${"Action"}.java
package org.alexmond.jhelm.core;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@RequiredArgsConstructor
public class ${ARGUMENTS}Action {
private final Engine engine;
private final KubeService kubeService;
public <ReturnType> <methodName>(<params>) throws Exception {
}
}
Rules:
@RequiredArgsConstructor — never write a constructor by hand
@Slf4j — use log.debug/info/warn/error, never System.out.println
- Throw descriptive
RuntimeException (with cause) for error cases
- For dry-run actions, skip
kubeService calls when dryRun == true
Step 3: Create the test class
File: jhelm-core/src/test/java/org/alexmond/jhelm/core/${ARGUMENTS}ActionTest.java
package org.alexmond.jhelm.core;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
class ${ARGUMENTS}ActionTest {
@Mock
private Engine engine;
@Mock
private KubeService kubeService;
private ${ARGUMENTS}Action ${argumentsCamel}Action;
@BeforeEach
void setUp() {
MockitoAnnotations.openMocks(this);
${argumentsCamel}Action = new ${ARGUMENTS}Action();
}
@Test
void testSuccess() throws Exception {
}
@Test
void testThrowsWhenNotFound() throws Exception {
}
@Test
void testDryRunSkipsKube() throws Exception {
}
}
Test rules:
- One
@BeforeEach setUp() that calls MockitoAnnotations.openMocks(this)
- Use
doNothing().when(mock).voidMethod(...) not when(...).thenReturn(null) for void methods
- Use
ArgumentCaptor when you need to assert on what was passed to a mock
- Always test: success path, error/not-found path, and dry-run (if applicable)
- Prefer real data (manifest strings, release builders) over excessive mocking
Step 4: Run precommit check
./mvnw spring-javaformat:apply -pl jhelm-core && ./mvnw test -pl jhelm-core 2>&1 | tail -10