Skip to content
Merged
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
7 changes: 7 additions & 0 deletions action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,13 @@ inputs:
description: "Base name for release assets, before the -<os>-<arch> suffix"
required: false
default: ""
task:
description: >
Wails v3 stack: the Taskfile target to run. Empty runs <os>:build, which
produces a bare binary — a project wanting the bundle or an installer
names its package target here.
required: false
default: ""
Comment on lines +66 to +72

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

echo "== files =="
git ls-files | rg '(^|/)action\.yml$'

echo "== action.yml around task/input declarations =="
if [ -f action.yml ]; then
  nl -ba action.yml | sed -n '1,120p'
fi

echo "== actions/action.yml relevant sections =="
if [ -f actions/action.yml ]; then
  nl -ba actions/action.yml | sed -n '1,230p'
fi

echo "== task input mentions =="
rg -n "task:|inputs\.task|task \(\$|task:" -S .

Repository: dAppCore/build

Length of output: 1358


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== action.yml first 130 lines =="
awk 'NR>=1 && NR<=130 {printf "%5d\t%s\n", NR, $0}' action.yml

echo "== actions/action.yml first 230 lines =="
awk 'NR>=1 && NR<=230 {printf "%5d\t%s\n", NR, $0}' actions/action.yml

echo "== task input mentions =="
rg -n "task:|inputs\.task|task" -S action.yml actions -g '*.yml' -g '*.yaml'

Repository: dAppCore/build

Length of output: 19727


🌐 Web query:

GitHub Actions composite actions with key not declared in inputs ignored documentation

💡 Result:

In GitHub Actions, composite actions do not automatically create INPUT_<VARIABLE_NAME> environment variables for their inputs, unlike Docker or JavaScript actions [1][2]. Consequently, if an input is passed to a composite action but is not declared in its metadata (the action.yml file), the action has no native way to access that undeclared input via the inputs context [1][3]. Key technical points regarding this behavior: 1. Absence of Automatic Env Vars: Composite actions do not receive input values as environment variables automatically [1][4]. Authors must explicitly map inputs to environment variables within the steps section of the action.yml file if they wish to access them as environment variables [5][6]. 2. Declaration Requirement: Because composite actions rely on the inputs context to access values, an input must be explicitly defined in the inputs section of the metadata [1][7]. If a user provides an input that is not declared in the action's metadata, that value is effectively ignored by the action logic [7]. The action cannot dynamically "see" or reference inputs that were not part of its design [7]. 3. Recommended Practice: To use inputs within a composite action, you must declare them in the metadata and then explicitly map them into each step where they are required, either by direct reference using ${{ inputs.input-id }} or by defining them in the env block of the specific step [5][6]. If you are encountering issues where inputs are being ignored, ensure they are declared in your action.yml file and correctly passed through the steps as shown in official documentation [6][8]. Using linting tools or static analysis, such as eslint-plugin-github-actions, can help detect references to undeclared inputs that would otherwise cause failures or incorrect behavior at runtime [7].

Citations:


Complete task propagation through both composite-action boundaries.

task is declared on the root action, but the root does not pass it to actions/action.yml; the orchestrator forwards it to the Wails v3 wrapper but declares no task input, so composite-action metadata treats it as absent and actions/build/wails3/build resolves to the <os>:build fallback.

  • action.yml: include task: ${{ inputs.task }} in the directory-orchestrator with: block.
  • actions/action.yml: declare task in the orchestrator inputs block before forwarding it to the Wails v3 wrapper.
📍 Affects 2 files
  • action.yml#L66-L72 (this comment)
  • actions/action.yml#L175-L180
🤖 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 `@action.yml` around lines 66 - 72, The task input is not propagated across
both composite-action boundaries. In action.yml, add task: ${{ inputs.task }} to
the directory-orchestrator with block; in actions/action.yml, declare task in
the orchestrator inputs block before its existing forwarding to the Wails v3
wrapper, preserving the configured task through to actions/build/wails3/build.

Source: MCP tools

entry:
description: >
Deno stack: entry module to compile. Empty probes for run.ts, main.ts,
Expand Down
6 changes: 6 additions & 0 deletions actions/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,12 @@ runs:
build-name: ${{ inputs.build-name }}
app-working-directory: ${{ inputs.app-working-directory }}
release-asset-prefix: ${{ inputs.release-asset-prefix }}
# The v3 stack runs the project's own Taskfile target, so which target
# is the whole configuration. Unforwarded it defaulted to <os>:build on
# every caller — a caller asking for a package target got a bare binary
# and no error, because Actions ignores a `with:` key the action does
# not declare.
task: ${{ inputs.task }}

- name: Call Go wrapper
id: go
Expand Down
34 changes: 25 additions & 9 deletions tests/action_contracts.py
Original file line number Diff line number Diff line change
Expand Up @@ -150,15 +150,28 @@ def main() -> int:
# the output. Reach for `--asymmetries` when wiring a new stack or hunting
# an option that appears to do nothing.
asymmetries: list[str] = []
# Inputs no caller wires at all. The asymmetry report cannot show these —
# it compares callers against each other, and unanimous silence looks like
# agreement — yet they are the worse case: the option exists on the callee,
# is documented there, and cannot be reached through the caller at all. A
# workflow that sets one gets no error, because Actions ignores an unknown
# `with:` key. That is how `task` reached the wails3 stack and never left
# the root action, pinning every v3 build to <os>:build.
unreachable: list[str] = []
for callee, inputs in sorted(wiring.items()):
for name, callers in sorted(inputs.items()):
missing = sorted(c for c, ok in callers.items() if not ok)
if missing and len(missing) != len(callers):
has = sorted(c for c, ok in callers.items() if ok)
asymmetries.append(
f" {callee} :: {name}\n"
f" wired by {', '.join(has)}\n"
f" not by {', '.join(missing)}")
if not missing:
continue
if len(missing) == len(callers):
unreachable.append(f" {callee} :: {name}\n"
f" reached by no caller ({', '.join(missing)})")
Comment on lines 163 to +168

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

echo "== repo files =="
git ls-files | sed -n '1,120p'

echo "== locate action_contracts.py and action.yml =="
fd -a 'action_contracts.py|action.yml' . | sed 's#^\./##'

echo "== relevant sections =="
if [ -f tests/action_contracts.py ]; then
  wc -l tests/action_contracts.py
  sed -n '120,190p' tests/action_contracts.py | cat -n
fi

echo "== workflow and action references =="
rg -n "forward|forwarded|inputs|uses:.*action|action.yml|task" -S . --glob '!node_modules' --glob '!dist' --glob '!build' | sed -n '1,240p'

Repository: dAppCore/build

Length of output: 29282


Do not treat missing callee inputs as reachable.

The contract checker currently records reachability from ${{ inputs.<name> }} in the caller’s with: block without confirming that <name> is declared by the callee’s inputs. Since actions/action.yml uses ${{ inputs.task }} but does not declare task, this edge is treated as usable and the chain remains broken. Validate forwarded inputs against the callee’s declared inputs before reporting them as reachable and emitting a dangling-input diagnostic where appropriate.

🤖 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 `@tests/action_contracts.py` around lines 163 - 168, Update the
contract-checking logic around the callers/missing calculation to verify each
forwarded `${{ inputs.<name> }}` against the callee’s declared inputs before
marking that edge reachable. Treat undeclared callee inputs as missing and emit
the existing dangling-input diagnostic, ensuring they can contribute to the
unreachable-callee result.

Source: MCP tools

continue
has = sorted(c for c, ok in callers.items() if ok)
asymmetries.append(
f" {callee} :: {name}\n"
f" wired by {', '.join(has)}\n"
f" not by {', '.join(missing)}")

if problems:
print("\n".join(f" {p}" for p in problems))
Expand All @@ -170,9 +183,12 @@ def main() -> int:
print(f"\n{len(asymmetries)} input(s) wired by some callers of a callee "
f"and not others:\n")
print("\n".join(asymmetries))
elif asymmetries:
print(f"({len(asymmetries)} caller asymmetries — "
f"run with --asymmetries to list them)")
print(f"\n{len(unreachable)} input(s) no caller wires — settable on the "
f"callee, unreachable through it:\n")
print("\n".join(unreachable))
elif asymmetries or unreachable:
print(f"({len(asymmetries)} caller asymmetries, {len(unreachable)} "
f"unreachable inputs — run with --asymmetries to list them)")
return 0


Expand Down
Loading