Description
Fallout can only emit untyped (string) workflow_dispatch inputs. GitHubActionsWorkflowDispatchTrigger.Write (src/Fallout.Common/CI/GitHubActions/Configuration/GitHubActionsWorkflowDispatchTrigger.cs:21-32) writes only description: and required: per input, never type:, default:, or options:. The attribute surface is correspondingly string-only: GitHubActionsAttribute.cs:57-58 exposes only OnWorkflowDispatchOptionalInputs / OnWorkflowDispatchRequiredInputs (plain string-name arrays), mapped onto the trigger at :280-288. There is no way to declare a boolean, number, choice, or environment input, set a per-input default:, or supply the options: list a choice input requires.
GitHub Actions supports five workflow_dispatch input types (string, boolean, number, choice, environment), each with description/required/default, and choice additionally with options. Without typing, a "publish?" flag is a free-text box that arrives as the strings "true"/"false", a "channel" input can't be a validated dropdown, and a target-environment input can't be the environment picker.
This needs a different design from "add more arrays." A per-type pair of Optional*/Required* string arrays cannot express options: (choice) or a per-input default:, and would grow to roughly ten arrays. Model each input instead as a first-class, compile-checked declaration via a repeatable attribute:
public enum GitHubActionsInputType { String, Boolean, Number, Choice, Environment }
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
public class GitHubActionsInputAttribute : Attribute
{
public GitHubActionsInputAttribute(string name) => Name = name;
public string Name { get; }
public GitHubActionsInputType Type { get; set; } = GitHubActionsInputType.String;
public bool Required { get; set; }
public string? Default { get; set; } // emitted verbatim (e.g. "false", "3", "beta")
public string[] Options { get; set; } = []; // required iff Type == Choice
public string? Description { get; set; } // defaults to the humanized Name
public string[] Workflows { get; set; } = []; // optional: scope to one or more [GitHubActions] by name; empty = all dispatch workflows on the class
}
Generator changes:
- Collect all
GitHubActionsInputAttributes on the build class. For each [GitHubActions] workflow, take the inputs whose Workflows is empty (applies to every dispatch workflow) or contains that workflow's name, so a single input can be shared across any subset of workflows.
- In the dispatch trigger, emit
description:, required:, type: (omit for string so existing output stays byte-identical), default: when set, and an options: list for choice.
- Feed
GetImports() so every declared input is still importable as env: NAME: ${{ github.event.inputs.NAME }}, exactly as today.
- Validate at generation time:
choice requires non-empty Options (and Default must be one of them if set); a number Default must parse as a number; a boolean Default must be true/false; and every name in Workflows must match a declared [GitHubActions] workflow. A bad declaration — including a Workflows typo that matches no workflow — fails the build instead of silently emitting invalid YAML or dropping the input.
Backward compatibility: the existing OnWorkflowDispatchOptionalInputs / OnWorkflowDispatchRequiredInputs arrays keep working unchanged (each is equivalent to a string input via the new attribute), so this is purely additive. It also supersedes the boolean-only arrays first sketched in the early draft of this proposal — boolean is now just Type = Boolean.
Usage Example
The Workflows list lets each input target one or more specific workflows, so inputs can be mixed and matched across a class that declares several [GitHubActions] workflows:
[GitHubActions(
"release",
GitHubActionsImage.UbuntuLatest,
On = [GitHubActionsTrigger.WorkflowDispatch],
InvokedTargets = [nameof(Pack)])]
[GitHubActions(
"nightly",
GitHubActionsImage.UbuntuLatest,
On = [GitHubActionsTrigger.WorkflowDispatch],
InvokedTargets = [nameof(Test)])]
// Workflows unset -> emitted to BOTH release and nightly:
[GitHubActionsInput("Verbose", Type = Boolean, Default = "false")]
// shared by a chosen subset:
[GitHubActionsInput("Retries", Type = Number, Default = "3", Workflows = ["release", "nightly"])]
// release only (string / choice / environment):
[GitHubActionsInput("Version", Required = true, Workflows = ["release"])]
[GitHubActionsInput("Channel", Type = Choice, Options = ["alpha", "beta", "stable"], Default = "beta", Workflows = ["release"])]
[GitHubActionsInput("DeployEnvironment", Type = Environment, Required = true, Workflows = ["release"])]
public partial class Build : NukeBuild { }
Resulting YAML — .github/workflows/release.yml gets the full set:
on:
workflow_dispatch:
inputs:
Verbose:
description: "Verbose"
required: false
type: boolean
default: false
Retries:
description: "Retries"
required: false
type: number
default: 3
Version:
description: "Version"
required: true
Channel:
description: "Channel"
required: false
type: choice
options:
- alpha
- beta
- stable
default: beta
DeployEnvironment:
description: "Deploy Environment"
required: true
type: environment
…while .github/workflows/nightly.yml gets only the inputs scoped to it:
on:
workflow_dispatch:
inputs:
Verbose:
description: "Verbose"
required: false
type: boolean
default: false
Retries:
description: "Retries"
required: false
type: number
default: 3
Scoping note: because a free-standing attribute is declared at class level, each input carries an optional Workflows list to target one or more [GitHubActions] workflows by name; leaving it empty applies the input to every dispatch-triggered workflow on the class. The names are validated against the declared workflows at generation time, so a typo fails the build rather than silently dropping the input. The alternative considered, string-encoded input specs on [GitHubActions] itself (e.g. "Channel:choice:alpha|beta|stable:default=beta"), would scope naturally but is stringly-typed and unvalidated, so we recommend the typed attribute instead.
Alternative
Declare every input as a plain string via OnWorkflowDispatchOptionalInputs / OnWorkflowDispatchRequiredInputs: the manual-run UI shows free-text boxes, conditions must string-compare (if: ${{ github.event.inputs.PublishToNuget == 'true' }}), there are no validated choice dropdowns or environment pickers, and no per-input defaults. The only way to get typed inputs today is hand-editing the generated YAML (overwritten on the next regeneration) or forking the generator. Our POC over NUKE added booleans through extra arrays (GitHubActionsWorkflowExtendedDispatchTrigger), but that array approach does not extend to choice/number/environment, which is exactly why this issue proposes the typed attribute.
Could you help with a pull-request?
Yes.
Description
Fallout can only emit untyped (string)
workflow_dispatchinputs.GitHubActionsWorkflowDispatchTrigger.Write(src/Fallout.Common/CI/GitHubActions/Configuration/GitHubActionsWorkflowDispatchTrigger.cs:21-32) writes onlydescription:andrequired:per input, nevertype:,default:, oroptions:. The attribute surface is correspondingly string-only:GitHubActionsAttribute.cs:57-58exposes onlyOnWorkflowDispatchOptionalInputs/OnWorkflowDispatchRequiredInputs(plain string-name arrays), mapped onto the trigger at:280-288. There is no way to declare aboolean,number,choice, orenvironmentinput, set a per-inputdefault:, or supply theoptions:list achoiceinput requires.GitHub Actions supports five
workflow_dispatchinput types (string,boolean,number,choice,environment), each withdescription/required/default, andchoiceadditionally withoptions. Without typing, a "publish?" flag is a free-text box that arrives as the strings"true"/"false", a "channel" input can't be a validated dropdown, and a target-environment input can't be the environment picker.This needs a different design from "add more arrays." A per-type pair of
Optional*/Required*string arrays cannot expressoptions:(choice) or a per-inputdefault:, and would grow to roughly ten arrays. Model each input instead as a first-class, compile-checked declaration via a repeatable attribute:Generator changes:
GitHubActionsInputAttributes on the build class. For each[GitHubActions]workflow, take the inputs whoseWorkflowsis empty (applies to every dispatch workflow) or contains that workflow's name, so a single input can be shared across any subset of workflows.description:,required:,type:(omit forstringso existing output stays byte-identical),default:when set, and anoptions:list forchoice.GetImports()so every declared input is still importable asenv: NAME: ${{ github.event.inputs.NAME }}, exactly as today.choicerequires non-emptyOptions(andDefaultmust be one of them if set); anumberDefaultmust parse as a number; abooleanDefaultmust betrue/false; and every name inWorkflowsmust match a declared[GitHubActions]workflow. A bad declaration — including aWorkflowstypo that matches no workflow — fails the build instead of silently emitting invalid YAML or dropping the input.Backward compatibility: the existing
OnWorkflowDispatchOptionalInputs/OnWorkflowDispatchRequiredInputsarrays keep working unchanged (each is equivalent to astringinput via the new attribute), so this is purely additive. It also supersedes the boolean-only arrays first sketched in the early draft of this proposal — boolean is now justType = Boolean.Usage Example
The
Workflowslist lets each input target one or more specific workflows, so inputs can be mixed and matched across a class that declares several[GitHubActions]workflows:Resulting YAML —
.github/workflows/release.ymlgets the full set:…while
.github/workflows/nightly.ymlgets only the inputs scoped to it:Scoping note: because a free-standing attribute is declared at class level, each input carries an optional
Workflowslist to target one or more[GitHubActions]workflows by name; leaving it empty applies the input to every dispatch-triggered workflow on the class. The names are validated against the declared workflows at generation time, so a typo fails the build rather than silently dropping the input. The alternative considered, string-encoded input specs on[GitHubActions]itself (e.g."Channel:choice:alpha|beta|stable:default=beta"), would scope naturally but is stringly-typed and unvalidated, so we recommend the typed attribute instead.Alternative
Declare every input as a plain string via
OnWorkflowDispatchOptionalInputs/OnWorkflowDispatchRequiredInputs: the manual-run UI shows free-text boxes, conditions must string-compare (if: ${{ github.event.inputs.PublishToNuget == 'true' }}), there are no validatedchoicedropdowns or environment pickers, and no per-input defaults. The only way to get typed inputs today is hand-editing the generated YAML (overwritten on the next regeneration) or forking the generator. Our POC over NUKE added booleans through extra arrays (GitHubActionsWorkflowExtendedDispatchTrigger), but that array approach does not extend tochoice/number/environment, which is exactly why this issue proposes the typed attribute.Could you help with a pull-request?
Yes.