Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions .github/workflows/mutation-cargo.yml
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,17 @@ on:
pipefail`; empty means no extra setup.
type: string
default: ""
submodules:
description: >-
How to check out the caller repository's git submodules,
forwarded verbatim to actions/checkout's `submodules` input:
`false` (default, no submodules), `true` (top-level only) or
`recursive`. Set this for callers that vendor a build-graph
dependency as a git submodule (e.g. a path dependency under
`third_party/`), whose directory must exist for the unmutated
baseline to build.
type: string
default: "false"
Comment on lines +69 to +79

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n## Workflow input definition\n'
sed -n '1,140p' .github/workflows/mutation-cargo.yml

printf '\n## Docs excerpt\n'
sed -n '70,120p' docs/mutation-cargo-workflow.md

Repository: leynos/shared-actions

Length of output: 8160


🌐 Web query:

GitHub Actions reusable workflow inputs type string boolean YAML with: true quoted string workflow_call

💡 Result:

In GitHub Actions reusable workflows, the inputs context stores boolean values as actual booleans, unlike workflow_dispatch events which often treat them as strings [1][2]. Because of this, it is critical to use boolean literals (without quotes) when evaluating these inputs in conditions [1][3]. Key behaviors for boolean inputs in reusable workflows: 1. Usage in Conditions: Use the literal true or false rather than the string 'true' or 'false' [1][3]. For example: if: ${{ inputs.my-boolean == true }} or simply: if: ${{ inputs.my-boolean }} 2. Comparison Inconsistency: A common pitfall is that comparing a reusable workflow's boolean input against a string (e.g., inputs.my-boolean == 'true') will evaluate to false [3]. This contrasts with workflow_dispatch inputs, which are strings and require comparison against 'true' [1][3]. 3. Script Usage: When using a boolean input inside a run command, it may not behave as expected if directly interpolated. It is often safest to map the input to an environment variable, which will handle the conversion [4]: env: MY_BOOL: ${{ inputs.my-boolean }} run: echo $MY_BOOL 4. Defining Inputs: When defining inputs in on.workflow_call, specify the type: boolean [2]. If no default is provided, it defaults to false [2]. If you find yourself needing to handle both string and boolean types (e.g., if you are passing inputs from a source that is inconsistently typed), you can use a compound check: if: ${{ inputs.my-boolean == true || inputs.my-boolean == 'true' }} [5]

Citations:


🌐 Web query:

GitHub Actions workflow_call input type string boolean coercion reusable workflow caller passes true to string input

💡 Result:

In GitHub Actions, the handling of input types in reusable workflows (workflow_call) is strict and distinct from other event types like workflow_dispatch [1][2]. When defining an input with type: boolean in a reusable workflow, GitHub treats the value as an actual boolean type, not a string [1][2]. If a caller workflow attempts to pass a string "true" or "false" to an input defined as type: boolean in the reusable workflow, it will fail because the data types must strictly match [3][4]. Key considerations include: 1. Strict Typing: Reusable workflow inputs are strictly typed [3]. If you define an input as type: boolean, the caller must pass a boolean value [3]. If you define an input as type: string, the caller must pass a string value [3]. 2. Incompatibility with String Inputs: If your reusable workflow defines an input as type: string, it will not automatically coerce the string "true" into a boolean true [1]. You must treat it as a string within the workflow (e.g., by comparing it against the string literal 'true' in expressions: if: inputs.my-input == 'true') [5][1]. 3. Comparison Issues: There is a known inconsistency where comparing boolean inputs in workflow_call requires using the boolean literal true (e.g., if: inputs.my-bool == true), whereas workflow_dispatch often behaves as if inputs are strings [5][1]. If you are experiencing issues where a boolean input is not being evaluated correctly, ensure you are comparing it against the boolean primitive true or false rather than the string equivalents [5][1]. 4. Best Practice for Scripts: If you need to use a boolean input inside a run step, you may encounter issues where the boolean cannot be directly interpreted as a shell string [6]. A common workaround is to map the input to an environment variable in the job definition [6]: yaml jobs: my-job: runs-on: ubuntu-latest steps: - name: My step env: MY_BOOL: ${{ inputs.my-boolean-input }} run: echo "The value is $MY_BOOL" If you must pass a value that might vary in type, it is generally safer to define the input as type: string in your reusable workflow and perform manual validation or string comparisons within the called workflow [1].

Citations:


🌐 Web query:

site:docs.github.com reusable workflow input type string boolean caller YAML boolean string workflow_call

💡 Result:

In GitHub Actions, reusable workflows use the on.workflow_call.inputs keyword to define parameters passed from a caller workflow [1][2]. When defining these inputs, you must specify a type for each, which dictates the expected data type [1][3]. For on.workflow_call, the supported input types are boolean, number, and string [1][2]. To implement these in your YAML configuration: 1. In the Reusable Workflow: Define the inputs within the on.workflow_call block and specify the type [2][4]. yaml on: workflow_call: inputs: username: description: 'A username' type: string required: true dry-run: description: 'Perform a dry run' type: boolean default: false 2. In the Caller Workflow: Use the with keyword within your job to pass the values [2][4]. The data type of the value provided in the caller must match the type defined in the reusable workflow [2][4]. yaml jobs: call-workflow: uses: octo-org/example-repo/.github/workflows/reusable.yml@main with: username: 'octocat' dry-run: true If a default value is not provided in the reusable workflow, the system defaults to false for booleans, 0 for numbers, and "" (empty string) for strings [1][3]. Within the reusable workflow, you access these inputs using the inputs context (e.g., ${{ inputs.username }}) [1][3].

Citations:


🌐 Web query:

GitHub Actions workflow_call string input boolean literal true quoted default false type mismatch

💡 Result:

In GitHub Actions, the type of an input in a workflow_call must strictly match the type defined in the called (reusable) workflow [1][2]. If you define an input with type: boolean, GitHub expects a boolean value [1][3]. Providing a string literal like "true" or "false" from the caller workflow as an input can trigger a type mismatch error because the runner expects a boolean type [3]. Key Technical Details: 1. Strict Typing in workflow_call: Unlike workflow_dispatch, which treats all inputs as strings [4], workflow_call enforces the data type (boolean, number, or string) specified in the input definition [1][4]. 2. Avoiding the Mismatch: When passing a boolean value to a reusable workflow, ensure you pass the value as an expression that resolves to a boolean, not a quoted string [4]. - Correct usage in the caller workflow: with: my-input: ${{ true }} (or the result of a boolean expression) [5][4]. - Incorrect usage: with: my-input: "true" (this will be rejected as a string) [3]. 3. Handling Boolean Inputs in Scripts: Once passed, the boolean input in the called workflow is treated as an actual boolean [4]. If you need to use it in a shell script step (which expects strings), you must map it to an environment variable or convert it [6]. - Example fix for scripts: env: MY_BOOL: ${{ inputs.my-input }} run: echo "The value is $MY_BOOL" Comparison Note: Be aware that behavior can be inconsistent between workflow_call and workflow_dispatch events [5][4]. While workflow_call requires strict boolean types, workflow_dispatch often requires comparisons against string literals (e.g., inputs.x == 'true') [5][4]. If you find your reusable workflow is failing, verify that the caller is passing a native boolean type and that your expressions inside the called workflow compare against true/false literals rather than "true"/"false" strings [5][7][4].

Citations:


🌐 Web query:

GitHub workflow_call input type string accepts unquoted true false as strings or errors

💡 Result:

In GitHub Actions, the handling of boolean-like inputs in workflows—particularly when using workflow_call for reusable workflows—is frequently a source of confusion due to inconsistent typing across different event triggers [1][2]. When you define an input with type: string in a workflow_call schema, GitHub treats the input value strictly as a string [3]. If you attempt to pass unquoted values like true or false from a caller workflow to a string input, you may encounter configuration errors or unexpected behavior depending on how the YAML is parsed and subsequently evaluated by the workflow runner [4][1]. Key points regarding this behavior: 1. Inconsistent Type Evaluation: While workflow_call inputs defined as type: boolean are evaluated as actual boolean values in expressions (e.g., if: inputs.my-bool == true), inputs defined as type: string are always treated as strings [1][2]. If you pass a boolean value to a string input, the runner may struggle with type compatibility [4][5]. 2. Best Practices for Booleans: If you need to handle true/false logic, it is highly recommended to explicitly define the input type as type: boolean in your workflow_call definition [3]. 3. Debugging Comparisons: Because boolean inputs in workflow_call behave as actual booleans, comparing them to strings (e.g., inputs.my-bool == 'true') will often result in false [1]. Instead, compare them directly to boolean literals: if: ${{ inputs.my-bool == true }} 4. Working with Bash/Shell: If you must pass boolean values into a script (e.g., run:), the value may not be directly usable as a boolean depending on the context [6]. A reliable way to handle this is to pass the input through an environment variable in the job step, which converts the value to a string for the shell environment [6]: env: MY_BOOL: ${{ inputs.my-bool }} run: echo "The value is $MY_BOOL" If you are experiencing errors with unquoted true/false values, ensure that the input definition in the reusable workflow matches the intended data type and that your expressions in the caller workflow use the correct syntax for that type [3][1].

Citations:


🌐 Web query:

site:docs.github.com "workflow_call" "type: string" boolean input "true" string

💡 Result:

In GitHub Actions, when using the workflow_call trigger to define a reusable workflow, the type parameter for an input is mandatory and must be one of three values: string, boolean, or number [1]. To define these inputs in your reusable workflow, you use the on.workflow_call.inputs syntax [1]: on: workflow_call: inputs: my-string: type: string required: true my-boolean: type: boolean required: true my-number: type: number required: true When calling this workflow from another workflow, the values passed via the with keyword must match the data types specified [2][3]. If no default value is provided for an input, the default value defaults to an empty string ("") for string, false for boolean, and 0 for number [1].

Citations:


🌐 Web query:

site:docs.github.com actions workflow syntax reusable workflow inputs type string boolean number

💡 Result:

In GitHub Actions, reusable workflows (defined with the on.workflow_call trigger) allow you to define specific inputs that must be passed from a caller workflow [1][2]. For reusable workflows, the type of an input must be one of the following three values: boolean, number, or string [2]. When defining these inputs in your reusable workflow file, you must specify the type property [2]: on: workflow_call: inputs: username: description: 'A username' type: string required: true port: description: 'A port number' type: number default: 8080 enabled: description: 'Enable feature' type: boolean default: false Key syntax details include: 1. Type Enforcement: The type parameter is required for all inputs defined under on.workflow_call.inputs [2]. 2. Default Values: If no default is provided, the implicit default values are false for boolean, 0 for number, and "" (empty string) for string [2]. 3. Matching Types: When calling the workflow using the with keyword, the data type of the value provided in the caller workflow must match the type specified in the reusable workflow [1][3]. 4. Context Access: Inside the reusable workflow, these inputs are accessed using the inputs context (e.g., ${{ inputs.username }}) [4]. Note that while workflow_dispatch triggers also support input types, they include additional types like choice and environment, which are not available for reusable workflows [2].

Citations:


🌐 Web query:

GitHub Actions reusable workflow input type string coerce boolean YAML scalar to string docs

💡 Result:

In GitHub Actions, there is no explicit configuration to "coerce" a boolean YAML scalar to a string for inputs; however, the behavior of how boolean inputs are handled depends significantly on the workflow trigger event, often leading to confusion [1]. Key points regarding boolean input behavior: 1. Inconsistent Type Handling: Inputs defined as type: boolean act as actual boolean values within reusable workflows (workflow_call) but often behave as strings when they originate from workflow_dispatch events [2][1]. 2. Comparison Behavior: Because of this inconsistency, comparing a boolean input in a workflow_call must be done against a boolean literal (e.g., if: inputs.my-boolean == true), whereas doing the same for a workflow_dispatch event usually requires comparing against a string literal (e.g., if: inputs.my-boolean == 'true') [2][1]. 3. Passing Data: When passing inputs to a reusable workflow, the data type provided in the with: block must match the type defined in the called workflow [3]. If you are experiencing type mismatches when passing values between workflows, users often use the fromJSON function to force conversion or ensure the passed value matches the expected type [4][2]. 4. Custom Actions: Note that when writing custom actions (e.g., using @actions/core), inputs are always retrieved as strings by default [5][6]. The toolkit provides a specific function, core.getBooleanInput, to correctly handle boolean input parsing from YAML for custom actions [5]. If you find that a boolean is being treated as a string, you can generally normalize the comparison by evaluating it within an expression or using string-based comparison operators, such as testing the string value of the input: if: ${{ inputs.my-input == 'true' }} [2][1].

Citations:


🌐 Web query:

site:docs.github.com GitHub Actions workflow_call inputs type validation string boolean rejected

💡 Result:

When using the workflow_call trigger in GitHub Actions, inputs must be explicitly defined with a type [1][2]. The allowed values for this type are boolean, number, or string [1][2]. If a caller workflow provides an input value that does not match the type defined in the reusable workflow, the execution will typically fail because the data types must match [3][4][5]. There is no automatic type casting for mismatched inputs (e.g., passing a string "true" to an input typed as boolean will be rejected if it cannot be implicitly coerced or is strictly validated as a non-boolean type) [3][5]. Key technical details: - Definition: Within the reusable workflow, you must specify the type for each input under on.workflow_call.inputs.<input_id>.type [1][2]. - Validation: When an external workflow calls the reusable workflow using the with keyword, the data types of the passed values must strictly match the types specified in the called workflow [3][4]. - Errors: If the caller passes an input not defined in the reusable workflow, or if there is a fundamental mismatch in expectations, the workflow run will result in an error [1][2]. It is important to distinguish between reusable workflows (workflow_call) and composite actions. In reusable workflows, types are strictly enforced as described above [1][2]. In contrast, action metadata (used in composite or JavaScript actions) does not support a type parameter for inputs in the same way, and inputs there are generally treated as strings, with the action author responsible for any necessary validation or parsing within their code [6][7][8].

Citations:


🌐 Web query:

GitHub Actions workflow_call string input unquoted true false accepted as string

💡 Result:

In GitHub Actions, the handling of true and false values for workflow_call inputs depends on the type defined for that input [1][2]. When an input is explicitly defined with type: boolean in a reusable workflow, GitHub Actions treats the value as a formal boolean type, not a string [2][3]. Consequently, you should compare or evaluate these inputs against boolean values (true or false) rather than string literals ( 'true' or 'false') [2][3]. For example: if: ${{ inputs.my-boolean == true }} If an input is defined with type: string, the values true and false are treated as literal strings [2]. A common point of confusion arises because other workflow event types, such as workflow_dispatch, handle inputs differently—often treating them as strings regardless of their intended purpose [4][2]. Additionally, attempting to use a boolean input directly in a shell script can sometimes cause errors because shell environments generally only support strings [5]. To use a boolean input in a script, it is standard practice to map it to an environment variable [5]: jobs: my-job: runs-on: ubuntu-latest steps: - name: Use boolean env: MY_VAR: ${{ inputs.my-boolean }} run: echo "The value is $MY_VAR" If you are experiencing unexpected results with unquoted true/false values, ensure that the input type in your workflow_call definition matches the data type you are passing from the caller workflow [1]. Passing a string representation (e.g., 'true') into an input defined as type: boolean may cause a type mismatch error [6].

Citations:


🌐 Web query:

site:docs.github.com "workflow_call" "The data type of the input value must match the type declared in the called workflow"

💡 Result:

When using the workflow_call event in GitHub Actions to create a reusable workflow, you must explicitly define inputs and their corresponding data types [1][2]. The error stating that the data type of the input value must match the type declared in the called workflow occurs when there is a mismatch between the type provided by the caller workflow and the type specified in the called workflow's definition [3][4]. To resolve this issue, ensure the following requirements are met: 1. Defined Types: In the reusable (called) workflow, every input must have a type specified under the on.workflow_call.inputs.<input_id>.type key [1][4]. Valid types are string, number, or boolean [3][1]. 2. Consistent Values: The caller workflow must pass values that correspond to the type declared in the reusable workflow [3][5]. For example, if an input is defined as a boolean, the caller must pass a boolean value, not a string representation of one (e.g., use true instead of "true") [3][4]. If you define an input in the reusable workflow but fail to provide a value (and have not set a default), or provide a value that cannot be coerced into the required type, the workflow execution will fail [1][4]. Always check that the input definition in the reusable workflow aligns exactly with the values passed via the with keyword in the caller workflow [3][6].

Citations:


Quote the true/false examples. Keep submodules as a string so recursive still works, but change the workflow description and docs examples to 'true'/'false'; bare YAML booleans do not match this reusable-workflow input contract.

📍 Affects 2 files
  • .github/workflows/mutation-cargo.yml#L69-L79 (this comment)
  • docs/mutation-cargo-workflow.md#L87-L97
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/mutation-cargo.yml around lines 69 - 79, Quote the
true/false examples in the submodules input documentation while keeping its type
as string and preserving recursive. Update the description at
.github/workflows/mutation-cargo.yml lines 69-79 and the corresponding examples
at docs/mutation-cargo-workflow.md lines 87-97 to use 'true' and 'false', with
no other changes.

Source: MCP tools


# Default the token to no scopes; jobs opt in explicitly.
permissions: {}
Expand All @@ -84,6 +95,7 @@ jobs:
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
fetch-depth: 0
submodules: ${{ inputs.submodules }}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🔵 Trivial

Verify credentials for private submodules.

Forwarding submodules alone does not grant access to private secondary repositories. The checkouts expose no PAT or SSH-key input and rely on the default github.token, which is scoped to the caller repository. Expose least-privilege credentials or document that only accessible/public submodules are supported.

Also applies to: 198-198

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/mutation-cargo.yml at line 98, Update the checkout
configuration associated with the submodules input to support least-privilege
credentials for private submodules, exposing and forwarding an appropriate PAT
or SSH-key input instead of relying solely on the caller repository’s default
github.token; if private access is intentionally unsupported, document that only
accessible or public submodules are supported.

Source: MCP tools

persist-credentials: false

- name: Resolve workflow source
Expand Down Expand Up @@ -183,6 +195,7 @@ jobs:
- name: Checkout caller repository
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
submodules: ${{ inputs.submodules }}
persist-credentials: false

- name: Resolve workflow source
Expand Down
8 changes: 8 additions & 0 deletions docs/mutation-cargo-workflow.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,9 +84,17 @@ jobs:
| `cargo-mutants-version` | pinned | Tool version; the summary parser is validated against it. |
| `extra-args` | (empty) | Extra cargo-mutants arguments (shell-lexed), e.g. `--all-features`. |
| `setup-commands` | (empty) | Shell commands run before cargo-mutants in each mutants job (e.g. `sudo apt-get install -y mold` when the repo's `.cargo/config.toml` selects that linker). |
| `submodules` | `false` | How to check out the caller's git submodules, forwarded to `actions/checkout` (`false`, `true` or `recursive`). Set for callers that vendor a build-graph dependency as a submodule. |

## Notes

- Set `submodules: recursive` (or `true`) when the caller vendors a
build-graph dependency as a git submodule — for example a Cargo path
dependency under `third_party/`. The default checkout leaves the
submodule directory empty, so the unmutated baseline fails to build
(`failed to read .../Cargo.toml`) before any mutant runs. The input is
a no-op for callers without a `.gitmodules`.

- The `cargo-mutants-version` default is pinned because the
`outcomes.json` format is documented as unstable and the summary
parser must match it. Override only alongside a parser check.
Expand Down
33 changes: 33 additions & 0 deletions workflow_scripts/tests/test_mutation_workflow_shape.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,39 @@ def _step_names(steps: list[dict[str, object]]) -> list[object]:
return [step.get("name") for step in steps]


CALLER_CHECKOUT_STEP = "Checkout caller repository"
SUBMODULES_EXPR = "${{ inputs.submodules }}"


def test_caller_checkout_forwards_submodules_input() -> None:
"""Every caller checkout in mutation-cargo forwards the submodules input.

A caller that vendors a build-graph dependency as a git submodule (a
Cargo path dependency under ``third_party/``, say) needs the submodule
directory populated for the unmutated baseline to build. The checkout
defaults to no submodules, so each ``Checkout caller repository`` step
must forward the ``submodules`` input to ``actions/checkout`` (issue
#363).
"""
checkouts = [
(job_name, step)
for job_name, job in _jobs("mutation-cargo.yml").items()
for step in _steps(job)
if step.get("name") == CALLER_CHECKOUT_STEP
]
assert checkouts, "mutation-cargo.yml must check out the caller repository"
for job_name, step in checkouts:
params = step.get("with")
assert isinstance(params, dict), (
f"mutation-cargo.yml:{job_name} caller checkout must set `with`"
)
assert params.get("submodules") == SUBMODULES_EXPR, (
f"mutation-cargo.yml:{job_name} caller checkout must forward "
f"the submodules input (issue #363), got "
f"{params.get('submodules')!r}"
)
Comment on lines +72 to +88

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Assert every actual caller checkout.

The test only requires one step named Checkout caller repository and never checks uses. It passes if either detect or mutants loses its checkout, or if an unrelated action reuses that name. Assert the complete expected job set and validate the action identity.

     checkouts = [
         (job_name, step)
         for job_name, job in _jobs("mutation-cargo.yml").items()
         for step in _steps(job)
         if step.get("name") == CALLER_CHECKOUT_STEP
     ]
     assert checkouts, "mutation-cargo.yml must check out the caller repository"
+    expected_jobs = {"detect", "mutants"}
+    assert {job_name for job_name, _ in checkouts} == expected_jobs
     for job_name, step in checkouts:
+        uses = step.get("uses")
+        assert isinstance(uses, str) and uses.startswith("actions/checkout@"), (
+            f"mutation-cargo.yml:{job_name} caller checkout must use actions/checkout"
+        )
         params = step.get("with")
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@workflow_scripts/tests/test_mutation_workflow_shape.py` around lines 72 - 88,
Strengthen the checkout validation in the test collecting steps named by
CALLER_CHECKOUT_STEP: require exactly the detect and mutants jobs, and verify
each matching step uses an actions/checkout action before validating its with
parameters. Preserve the existing submodules forwarding assertion and
diagnostics.



@pytest.mark.parametrize("workflow_name", WORKFLOW_NAMES)
def test_every_workflow_checkout_is_followed_by_relocation(
workflow_name: str,
Expand Down
Loading