Add JSON progress and retry support to task runners - #8672
Merged
Merged
Conversation
gonzaloriestra
added this pull request to stack #8673
September 25, 2026 12:29
gonzaloriestra
marked this pull request as ready for review
September 28, 2026 08:19
gonzaloriestra
force-pushed
the
gonzalo/json-render-tasks
branch
from
September 28, 2026 08:20
4f39011 to
2a52c56
Compare
gonzaloriestra
force-pushed
the
gonzalo/json-render-tasks
branch
from
September 28, 2026 10:25
2a52c56 to
1a9d580
Compare
fredericoo
reviewed
Sep 28, 2026
fredericoo
left a comment
Contributor
There was a problem hiding this comment.
one more thing: since this changes the behaviour of a public cli-kit API for every plugin (not just theme commands), could the changeset in #8682 get a line for @shopify/cli-kit along the lines of "renderTasks runs without Ink and emits JSON progress events in JSON mode"? Right now it only mentions theme commands, so plugin authors wouldn't know.
gonzaloriestra
force-pushed
the
gonzalo/json-render-tasks
branch
from
September 29, 2026 10:28
1a9d580 to
7db949b
Compare
isaacroldan
reviewed
Sep 29, 2026
gonzaloriestra
force-pushed
the
gonzalo/json-render-tasks
branch
from
September 29, 2026 13:44
7db949b to
2321422
Compare
isaacroldan
reviewed
Sep 29, 2026
isaacroldan
approved these changes
Sep 29, 2026
isaacroldan
left a comment
Contributor
There was a problem hiding this comment.
Just a minor comment about the onComplete
gonzaloriestra
force-pushed
the
gonzalo/json-render-tasks
branch
from
September 29, 2026 14:18
2321422 to
58d1f09
Compare
Contributor
Differences in type declarationsWe detected differences in the type declarations generated by Typescript for this branch compared to the baseline ('main' branch). Please, review them to ensure they are backward-compatible. Here are some important things to keep in mind:
New type declarationspackages/cli-kit/dist/private/node/ui/tasks.d.tsimport { TokenizedString } from '../../../public/node/output.js';
export interface Task<TContext = unknown> {
title: string | TokenizedString;
task: (ctx: TContext, task: Task<TContext>) => Promise<void | Task<TContext>[]>;
retry?: number;
retryCount?: number;
errors?: Error[];
skip?: (ctx: TContext) => boolean;
}
export declare function runTasks<TContext>(tasks: Task<TContext>[], onTask?: (task: Task<TContext>) => void): Promise<TContext>;
Existing type declarationspackages/cli-kit/dist/public/common/command-events.d.ts@@ -23,14 +23,14 @@ export declare const commandDiagnosticEventSchema: z.ZodObject<{
export declare const commandProgressEventSchema: z.ZodObject<{
type: z.ZodLiteral<"progress">;
timestamp: z.ZodString;
- status: z.ZodEnum<["started", "updated", "completed"]>;
+ status: z.ZodEnum<["started", "updated", "retrying", "completed", "failed"]>;
operation: z.ZodString;
message: z.ZodOptional<z.ZodString>;
current: z.ZodOptional<z.ZodNumber>;
total: z.ZodOptional<z.ZodNumber>;
}, "strict", z.ZodTypeAny, {
type: "progress";
- status: "started" | "updated" | "completed";
+ status: "started" | "updated" | "retrying" | "completed" | "failed";
timestamp: string;
operation: string;
message?: string | undefined;
@@ -38,7 +38,7 @@ export declare const commandProgressEventSchema: z.ZodObject<{
total?: number | undefined;
}, {
type: "progress";
- status: "started" | "updated" | "completed";
+ status: "started" | "updated" | "retrying" | "completed" | "failed";
timestamp: string;
operation: string;
message?: string | undefined;
@@ -67,14 +67,14 @@ export declare const commandEventSchema: z.ZodDiscriminatedUnion<"type", [z.ZodO
}>, z.ZodObject<{
type: z.ZodLiteral<"progress">;
timestamp: z.ZodString;
- status: z.ZodEnum<["started", "updated", "completed"]>;
+ status: z.ZodEnum<["started", "updated", "retrying", "completed", "failed"]>;
operation: z.ZodString;
message: z.ZodOptional<z.ZodString>;
current: z.ZodOptional<z.ZodNumber>;
total: z.ZodOptional<z.ZodNumber>;
}, "strict", z.ZodTypeAny, {
type: "progress";
- status: "started" | "updated" | "completed";
+ status: "started" | "updated" | "retrying" | "completed" | "failed";
timestamp: string;
operation: string;
message?: string | undefined;
@@ -82,7 +82,7 @@ export declare const commandEventSchema: z.ZodDiscriminatedUnion<"type", [z.ZodO
total?: number | undefined;
}, {
type: "progress";
- status: "started" | "updated" | "completed";
+ status: "started" | "updated" | "retrying" | "completed" | "failed";
timestamp: string;
operation: string;
message?: string | undefined;
packages/cli-kit/dist/public/node/command-events.d.ts@@ -29,14 +29,14 @@ export declare const commandEventOutputSchema: import("./json-output-schema.js")
}>, import("zod").ZodObject<{
type: import("zod").ZodLiteral<"progress">;
timestamp: import("zod").ZodString;
- status: import("zod").ZodEnum<["started", "updated", "completed"]>;
+ status: import("zod").ZodEnum<["started", "updated", "retrying", "completed", "failed"]>;
operation: import("zod").ZodString;
message: import("zod").ZodOptional<import("zod").ZodString>;
current: import("zod").ZodOptional<import("zod").ZodNumber>;
total: import("zod").ZodOptional<import("zod").ZodNumber>;
}, "strict", import("zod").ZodTypeAny, {
type: "progress";
- status: "started" | "updated" | "completed";
+ status: "started" | "updated" | "retrying" | "completed" | "failed";
timestamp: string;
operation: string;
message?: string | undefined;
@@ -44,7 +44,7 @@ export declare const commandEventOutputSchema: import("./json-output-schema.js")
total?: number | undefined;
}, {
type: "progress";
- status: "started" | "updated" | "completed";
+ status: "started" | "updated" | "retrying" | "completed" | "failed";
timestamp: string;
operation: string;
message?: string | undefined;
packages/cli-kit/dist/public/node/ui.d.ts@@ -318,6 +318,8 @@ export declare function renderTasks<TContext>(tasks: Task<TContext>[], { renderO
export interface RenderSingleTaskOptions<T> {
title: TokenizedString;
task: (updateStatus: (status: TokenizedString) => void) => Promise<T>;
+ /** The number of additional attempts after a failure. Defaults to zero. */
+ retry?: number;
onAbort?: () => void;
renderOptions?: RenderOptions;
}
@@ -326,12 +328,13 @@ export interface RenderSingleTaskOptions<T> {
* @param options - Configuration object
* @param options.title - The initial title to display with the loading bar
* @param options.task - The async task to execute. Receives an updateStatus callback to change the displayed title.
+ * @param options.retry - The number of additional attempts after a failure. Defaults to zero.
* @param options.renderOptions - Optional render configuration
* @returns The result of the task
* @example
* Loading app ...
*/
-export declare function renderSingleTask<T>({ title, task, onAbort, renderOptions, }: RenderSingleTaskOptions<T>): Promise<T>;
+export declare function renderSingleTask<T>({ title, task, retry, onAbort, renderOptions, }: RenderSingleTaskOptions<T>): Promise<T>;
export interface RenderTextPromptOptions extends Omit<TextPromptProps, 'onSubmit'> {
renderOptions?: RenderOptions;
}
packages/cli-kit/dist/private/node/ui/hooks/use-async-and-unmount.d.ts@@ -1,6 +1,6 @@
-interface Options {
- onFulfilled?: () => unknown;
+interface Options<T> {
+ onFulfilled?: (result: T) => unknown;
onRejected?: (error: Error) => void;
}
-export default function useAsyncAndUnmount(asyncFunction: () => Promise<unknown>, { onFulfilled, onRejected }?: Options): void;
+export default function useAsyncAndUnmount<T>(asyncFunction: () => Promise<T>, { onFulfilled, onRejected }?: Options<T>): void;
export {};
\ No newline at end of file
packages/cli-kit/dist/private/node/ui/components/Tasks.d.ts@@ -1,14 +1,7 @@
import { AbortSignal } from '../../../../public/node/abort.js';
-import { TokenizedString } from '../../../../public/node/output.js';
+import { Task } from '../tasks.js';
import React from 'react';
-export interface Task<TContext = unknown> {
- title: string | TokenizedString;
- task: (ctx: TContext, task: Task<TContext>) => Promise<void | Task<TContext>[]>;
- retry?: number;
- retryCount?: number;
- errors?: Error[];
- skip?: (ctx: TContext) => boolean;
-}
+export type { Task } from '../tasks.js';
interface TasksProps<TContext> {
tasks: Task<TContext>[];
silent?: boolean;
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
WHY are these changes introduced?
Task lists need to run without terminal UI in JSON mode, and progress consumers need to see retries and final failures.
WHAT is this pull request doing?
Run
renderTaskswithout Ink in JSON mode while preserving shared context, subtasks, retries, and skipped tasks. Both task runners emit retry and failure events, andrenderSingleTaskgains an optional retry count that defaults to zero.Theme commands adopt this support higher in the stack.
Checklist