Skip to content

feat(stack): stack action list/run/log for custom actions (#155) - #165

Merged
omattsson merged 2 commits into
mainfrom
feat/155-stack-actions
Oct 19, 2026
Merged

omattsson merged 2 commits into
mainfrom
feat/155-stack-actions

Conversation

@omattsson

Copy link
Copy Markdown
Owner

Summary

Commands for the custom actions of the hooks config (hooks.actions):

  • stack action list <stack>: the actions with label, CAN RUN, JOB LOG and the parameters (type, enum options, required, default). -q prints the names. -o json|yaml prints the full response.
  • stack action run <stack> <action> [--param key=value ...] [--yes] [--wait] [--timeout 60m]:
    • It reads the action list first. An unknown action or can_invoke: false stops before the request.
    • It types the declared parameters (bool, enum), fills declared defaults, passes undeclared parameters as strings, and refuses a missing required parameter.
    • It asks with the confirmation text of the action. --yes skips the question.
    • A subscriber status of 400 or higher (in the 200 envelope) gives exit code 1, after the result is printed.
  • stack action log <stack> <action> <job-id> [--offset N] [--follow]: one log chunk (log text to stdout, status to stderr), or the full log with --follow.

--wait and --follow, as the web UI does:

  • They write the job ID to stderr before the poll starts.
  • They poll every 3 s, and at once again after a truncated chunk.
  • On 429, 502, 503, 504 and network errors they write a note to stderr and wait: Retry-After (at most 5 minutes), else a doubling delay from 3 s up to 30 s.
  • They stop on done, on other errors (401, 403, 404, a response that is not JSON) and at --timeout. A final status outside the success set (succeeded, success, completed, done, ok, ...) gives exit code 1.
  • With -o json|yaml, the log goes to stderr and the result has job_status.

Other changes

  • client.SanitizeLogText: log text keeps line feeds and tabs. Carriage returns and bidi controls are removed, other control characters become a space.
  • APIError.RetryAfter().
  • Fix a race in renewIfExpiring (pkg/client/auth.go): read the current token before the token file. Before, a parallel renewal between the two reads gave a second refresh, and TestRenew_ConcurrentGoroutinesOneClient failed now and then under load.
  • Test helper resetCmdFlags resets slice and array flags with Replace.

Tests

  • go test ./... passes. go test -shuffle=on ./cmd passes. go test -race ./pkg/client -count=5 passes. TestRenew_ConcurrentGoroutinesOneClient -count=100 passes.
  • New tests: cli/cmd/stack_action_test.go (parameters, confirmation, refusal, --wait with backoff, timeouts, truncated chunks, JSON output, log --follow) and client tests in cli/pkg/client/stack_inspect_test.go.

Closes #155

🤖 Generated with Claude Code

- stack action list: the custom actions of the hooks config with label,
  CAN RUN, JOB LOG and the parameters (type, options, required, default).
- stack action run <stack> <action> [--param k=v] [--yes] [--wait]:
  types the declared parameters (bool, enum), fills defaults, passes
  undeclared ones as strings and refuses a missing required parameter
  before the request. Asks with the confirmation text unless --yes. A
  subscriber status >= 400 gives exit code 1.
- --wait and stack action log --follow poll the job log every 3 s, with
  backoff on 429, 502, 503, 504 and network errors (Retry-After, else
  doubling up to 30 s). They stop on done, on other errors and at
  --timeout (default 60m). A failed final status gives exit code 1. The
  job ID goes to stderr before the poll starts.
- client.SanitizeLogText: log text keeps line feeds and tabs; other
  control characters are removed or replaced.
- Fix a race in renewIfExpiring: read the current token before the token
  file. TestRenew_ConcurrentGoroutinesOneClient failed now and then under
  load (a second refresh).

Closes #155

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Oct 19, 2026 •

Copy link
Copy Markdown

Important

  • 🔍 Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

⚙️ Run configuration
  • Configuration used: Repository: omattsson/stackctl/.coderabbit.yaml
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: d62a8c16-17f1-4d70-9214-46e336ec00b0
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

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.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@omattsson
omattsson merged commit a250257 into main Oct 19, 2026
8 checks passed
@omattsson
omattsson deleted the feat/155-stack-actions branch October 19, 2026 07:34
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.

Custom instance actions: list, run and job log (/stack-instances/:id/actions)

1 participant