diff --git a/frontend/awx/interfaces/LaunchConfiguration.ts b/frontend/awx/interfaces/LaunchConfiguration.ts
index c1a38551b2..9aa6960370 100644
--- a/frontend/awx/interfaces/LaunchConfiguration.ts
+++ b/frontend/awx/interfaces/LaunchConfiguration.ts
@@ -4,6 +4,7 @@ export interface LaunchConfiguration {
can_start_without_user_input: boolean;
passwords_needed_to_start: string[];
ask_scm_branch_on_launch: boolean;
+ ask_nodes_job_type_on_launch: boolean;
ask_variables_on_launch: boolean;
ask_tags_on_launch: boolean;
ask_diff_mode_on_launch: boolean;
@@ -39,6 +40,7 @@ export interface LaunchConfiguration {
limit: string;
labels: { id: number; name: string }[];
scm_branch: string;
+ nodes_job_type: 'run' | 'check';
job_tags: string;
skip_tags: string;
extra_vars: string;
diff --git a/frontend/awx/interfaces/WorkflowJobLaunch.ts b/frontend/awx/interfaces/WorkflowJobLaunch.ts
index 60ca26d4ac..97510bbcbe 100644
--- a/frontend/awx/interfaces/WorkflowJobLaunch.ts
+++ b/frontend/awx/interfaces/WorkflowJobLaunch.ts
@@ -9,6 +9,7 @@ export interface WorkflowJobLaunch {
ask_limit_on_launch: boolean;
ask_scm_branch_on_launch: boolean;
ask_skip_tags_on_launch: boolean;
+ ask_nodes_job_type_on_launch: boolean;
ask_tags_on_launch: boolean;
ask_variables_on_launch: boolean;
ask_verbosity_on_launch: boolean;
@@ -41,6 +42,7 @@ export interface WorkflowJobLaunch {
}[];
limit: string;
scm_branch: string;
+ nodes_job_type: string;
skip_tags: string;
job_type: string;
verbosity: number;
diff --git a/frontend/awx/interfaces/WorkflowJobTemplate.ts b/frontend/awx/interfaces/WorkflowJobTemplate.ts
index 975b9dabaf..2f332673e7 100644
--- a/frontend/awx/interfaces/WorkflowJobTemplate.ts
+++ b/frontend/awx/interfaces/WorkflowJobTemplate.ts
@@ -27,6 +27,7 @@ export interface WorkflowJobTemplate
type: 'workflow_job_template';
created: string;
allow_simultaneous: boolean;
+ ask_nodes_job_type_on_launch: boolean;
modified: string;
webhook_service: string;
skip_tags: string;
@@ -120,6 +121,7 @@ export interface WorkflowJobTemplateForm
| 'webhook_credential'
| 'skip_tags'
| 'job_tags'
+ | 'nodes_job_type'
| 'type'
| 'execution_environment'
| 'id'
@@ -159,6 +161,7 @@ export interface WorkflowJobTemplateCreate
| 'summary_fields'
| 'related'
| 'job_type'
+ | 'nodes_job_type'
| 'type'
| 'created'
| 'modified'
@@ -183,4 +186,5 @@ export interface WorkflowJobTemplateCreate
webhook_credential: number | null;
extra_vars: string;
organization?: number;
+ ask_nodes_job_type_on_launch: boolean;
}
diff --git a/frontend/awx/resources/templates/TemplatePage/TemplateLaunchWizard.tsx b/frontend/awx/resources/templates/TemplatePage/TemplateLaunchWizard.tsx
index 08b5fe85d4..634cbac0d9 100644
--- a/frontend/awx/resources/templates/TemplatePage/TemplateLaunchWizard.tsx
+++ b/frontend/awx/resources/templates/TemplatePage/TemplateLaunchWizard.tsx
@@ -49,6 +49,7 @@ export const formFieldToLaunchConfig = {
timeout: 'ask_timeout_on_launch',
extra_vars: 'ask_variables_on_launch',
verbosity: 'ask_verbosity_on_launch',
+ nodes_job_type: 'ask_nodes_job_type_on_launch',
};
export interface TemplateLaunch {
@@ -77,6 +78,7 @@ interface LaunchPayload {
skip_tags: string;
timeout: number;
verbosity: number;
+ nodes_job_type: string;
}
type LaunchPayloadProperty = keyof LaunchPayload;
@@ -156,6 +158,7 @@ export function LaunchTemplate({ jobType }: { jobType: string }) {
setValue('skip_tags', prompt.skip_tags?.map((tag) => tag.name).join(','));
setValue('timeout', prompt.timeout);
setValue('verbosity', prompt.verbosity);
+ setValue('nodes_job_type', prompt.nodes_job_type);
}
if (labelPayload.length > 0) {
setValue('labels', labelPayload);
@@ -244,6 +247,7 @@ export function LaunchWizard({
skip_tags: parseStringToTagArray(defaults.skip_tags),
timeout: defaults.timeout,
verbosity: defaults.verbosity,
+ nodes_job_type: defaults.nodes_job_type,
},
launch_config: config,
survey: {},
diff --git a/frontend/awx/resources/templates/TemplatePage/steps/TemplateLaunchReviewStep.tsx b/frontend/awx/resources/templates/TemplatePage/steps/TemplateLaunchReviewStep.tsx
index e76b070f5b..c50b484f39 100644
--- a/frontend/awx/resources/templates/TemplatePage/steps/TemplateLaunchReviewStep.tsx
+++ b/frontend/awx/resources/templates/TemplatePage/steps/TemplateLaunchReviewStep.tsx
@@ -197,6 +197,7 @@ export function TemplateLaunchReviewStep(props: { template: JobTemplate }) {
{prompt?.skip_tags?.map(({ name }) => )}
+ {prompt?.nodes_job_type}
);
diff --git a/frontend/awx/resources/templates/WorkflowJobTemplateForm.tsx b/frontend/awx/resources/templates/WorkflowJobTemplateForm.tsx
index af39a869aa..c9bc437fe1 100644
--- a/frontend/awx/resources/templates/WorkflowJobTemplateForm.tsx
+++ b/frontend/awx/resources/templates/WorkflowJobTemplateForm.tsx
@@ -74,6 +74,7 @@ export function EditWorkflowJobTemplate() {
ask_skip_tags_on_launch: workflowJobTemplate.ask_skip_tags_on_launch || false,
ask_tags_on_launch: workflowJobTemplate.ask_tags_on_launch || false,
ask_variables_on_launch: workflowJobTemplate.ask_variables_on_launch || false,
+ ask_nodes_job_type_on_launch: workflowJobTemplate.ask_nodes_job_type_on_launch || false,
description: workflowJobTemplate.description || '',
extra_vars: workflowJobTemplate.extra_vars || '---',
inventory: workflowJobTemplate.summary_fields.inventory || null,
diff --git a/frontend/awx/resources/templates/WorkflowJobTemplateInputs.tsx b/frontend/awx/resources/templates/WorkflowJobTemplateInputs.tsx
index bda48ca96f..ee8012dca7 100644
--- a/frontend/awx/resources/templates/WorkflowJobTemplateInputs.tsx
+++ b/frontend/awx/resources/templates/WorkflowJobTemplateInputs.tsx
@@ -125,6 +125,14 @@ export function WorkflowJobTemplateInputs(props: {
label={t('Enable concurrent jobs')}
name="allow_simultaneous"
/>
+
+ labelHelpTitle={t('Nodes job type')}
+ labelHelp={t(
+ 'If enabled, a prompt will appear during workflow launch, allowing you to set the job type for each node. This feature prevents the creation of identical workflows with only different job types. NOTE: If your workflow contains nodes with mixed job_type values, enabling this feature will override all of them with a single job_type at execution time.'
+ )}
+ label={t('Enable prompt on launch for nodes job type')}
+ name="ask_nodes_job_type_on_launch"
+ />
{isWebhookEnabled ? : null}
diff --git a/frontend/awx/resources/templates/WorkflowJobTemplatePage/WorkflowJobTemplateDetails.tsx b/frontend/awx/resources/templates/WorkflowJobTemplatePage/WorkflowJobTemplateDetails.tsx
index cabcc7d5a0..f0fd6d7d33 100644
--- a/frontend/awx/resources/templates/WorkflowJobTemplatePage/WorkflowJobTemplateDetails.tsx
+++ b/frontend/awx/resources/templates/WorkflowJobTemplatePage/WorkflowJobTemplateDetails.tsx
@@ -39,7 +39,10 @@ export function WorkflowJobTemplateDetails(props: {
const { summary_fields: summaryFields } = template;
- const showOptionsField = template.allow_simultaneous || template.webhook_service;
+ const showOptionsField =
+ template.allow_simultaneous ||
+ template.webhook_service ||
+ template.ask_nodes_job_type_on_launch;
const inventoryUrlPaths: { [key: string]: string } = {
'': 'inventory',
@@ -137,6 +140,11 @@ export function WorkflowJobTemplateDetails(props: {
{template.webhook_service && (
{t`Webhooks`}
)}
+ {template.ask_nodes_job_type_on_launch && (
+
+ {t`Prompt on launch for nodes job type`}
+
+ )}
diff --git a/frontend/awx/resources/templates/WorkflowVisualizer/types.ts b/frontend/awx/resources/templates/WorkflowVisualizer/types.ts
index ecd6e63a77..bd413ee3b4 100644
--- a/frontend/awx/resources/templates/WorkflowVisualizer/types.ts
+++ b/frontend/awx/resources/templates/WorkflowVisualizer/types.ts
@@ -136,6 +136,7 @@ export interface PromptFormValues {
labels: { name: string; id: number }[];
limit: string;
scm_branch: string;
+ nodes_job_type: string;
skip_tags: { name: string }[];
timeout: number;
verbosity: 0 | 1 | 2 | 3 | 4 | 5;
diff --git a/frontend/awx/resources/templates/WorkflowVisualizer/wizard/NodePromptsStep.tsx b/frontend/awx/resources/templates/WorkflowVisualizer/wizard/NodePromptsStep.tsx
index e1e2008a7d..e83bc23760 100644
--- a/frontend/awx/resources/templates/WorkflowVisualizer/wizard/NodePromptsStep.tsx
+++ b/frontend/awx/resources/templates/WorkflowVisualizer/wizard/NodePromptsStep.tsx
@@ -66,6 +66,7 @@ export function NodePromptsStep() {
limit: prompt?.limit ?? defaults.limit,
organization: prompt?.organization ?? organizationId,
scm_branch: prompt?.scm_branch ?? defaults.scm_branch,
+ nodes_job_type: prompt?.nodes_job_type ?? defaults.nodes_job_type,
skip_tags: prompt?.skip_tags ?? parseStringToTagArray(defaults.skip_tags),
timeout: prompt?.timeout ?? defaults.timeout,
verbosity: prompt?.verbosity ?? defaults.verbosity,
@@ -263,6 +264,23 @@ export function NodePromptsStep() {
format="yaml"
/>
+
+
+ isRequired
+ id="nodes_job_type"
+ label={t('Nodes job type')}
+ labelHelpTitle={t('Nodes job type')}
+ labelHelp={t(
+ 'Set the job type for each node. This feature prevents the creation of identical workflows with only different job types.'
+ )}
+ name="prompt.nodes_job_type"
+ options={[
+ { label: t('Check'), value: 'check' },
+ { label: t('Run'), value: 'run' },
+ ]}
+ placeholderText={t('Select nodes job type')}
+ />
+
);
}
diff --git a/frontend/awx/resources/templates/WorkflowVisualizer/wizard/helpers.ts b/frontend/awx/resources/templates/WorkflowVisualizer/wizard/helpers.ts
index f6b38f0783..240806c9ef 100644
--- a/frontend/awx/resources/templates/WorkflowVisualizer/wizard/helpers.ts
+++ b/frontend/awx/resources/templates/WorkflowVisualizer/wizard/helpers.ts
@@ -59,6 +59,7 @@ export function shouldHideOtherStep(launchData: LaunchConfiguration) {
launchData.ask_tags_on_launch ||
launchData.ask_timeout_on_launch ||
launchData.ask_variables_on_launch ||
- launchData.ask_verbosity_on_launch
+ launchData.ask_verbosity_on_launch ||
+ launchData.ask_nodes_job_type_on_launch
);
}
diff --git a/frontend/awx/resources/templates/hooks/useLaunchTemplate.tsx b/frontend/awx/resources/templates/hooks/useLaunchTemplate.tsx
index e0142f9fe9..c52c840bae 100644
--- a/frontend/awx/resources/templates/hooks/useLaunchTemplate.tsx
+++ b/frontend/awx/resources/templates/hooks/useLaunchTemplate.tsx
@@ -77,7 +77,8 @@ export function canLaunchWithoutPrompt(launchData: TemplateLaunch) {
launchData.can_start_without_user_input &&
!launchData.survey_enabled &&
(!launchData.passwords_needed_to_start || launchData.passwords_needed_to_start.length === 0) &&
- (!launchData.variables_needed_to_start || launchData.variables_needed_to_start.length === 0)
+ (!launchData.variables_needed_to_start || launchData.variables_needed_to_start.length === 0) &&
+ !launchData.ask_nodes_job_type_on_launch
);
}