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
11 changes: 6 additions & 5 deletions action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -63,13 +63,13 @@ inputs:
description: "Base name for release assets, before the -<os>-<arch> suffix"
required: false
default: ""
task:
wails3-command:
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.
Wails v3 stack: which wails3 CLI verb to run — `build` for the bare
binary, `package` for the platform's installable artefact (.app, the NSIS
installer, the Linux packages).
required: false
default: ""
default: "build"
entry:
description: >
Deno stack: entry module to compile. Empty probes for run.ts, main.ts,
Expand Down Expand Up @@ -139,6 +139,7 @@ runs:
app-working-directory: ${{ inputs.app-working-directory }}
go-directory: ${{ inputs.go-directory }}
release-asset-prefix: ${{ inputs.release-asset-prefix }}
wails3-command: ${{ inputs.wails3-command }}
entry: ${{ inputs.entry }}
permissions: ${{ inputs.permissions }}
deb: ${{ inputs.deb }}
Expand Down
15 changes: 9 additions & 6 deletions actions/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,10 @@ inputs:
description: "Base name for release assets, before the -<os>-<arch> suffix"
required: false
default: ""
wails3-command:
description: "Wails v3 stack: the wails3 CLI verb — build or package."
required: false
default: "build"
entry:
description: "Deno stack: entry module to compile. Empty probes for the usual names."
required: false
Expand Down Expand Up @@ -172,12 +176,11 @@ 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 }}
# build gives the bare binary, package the installable artefact. A
# caller wanting the second and getting the first finds out at whatever
# step next expects a bundle, so this is the difference between a
# release that has installers and one that does not.
wails3-command: ${{ inputs.wails3-command }}

- name: Call Go wrapper
id: go
Expand Down
8 changes: 4 additions & 4 deletions actions/build/wails3/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,10 @@ inputs:
description: "Root of the app being built"
required: false
default: "."
task:
description: "Taskfile target; defaults to <os>:build from the runner"
wails3-command:
description: "The wails3 CLI verb — build for the binary, package for the installable artefact"
required: false
default: ""
default: "build"
wails3-tool:
description: "How to invoke wails3; defaults to the module-pinned `go tool wails3`"
required: false
Expand Down Expand Up @@ -103,7 +103,7 @@ runs:
build: ${{ inputs.build }}
app-working-directory: ${{ inputs.app-working-directory }}
build-name: ${{ inputs.build-name }}
task: ${{ inputs.task }}
wails3-command: ${{ inputs.wails3-command }}
wails3-tool: ${{ inputs.wails3-tool }}

- name: Sign artifacts (OS-conditional)
Expand Down
73 changes: 33 additions & 40 deletions actions/build/wails3/build/action.yml
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
name: "Build Wails v3 App"
description: >
Builds a Wails v3 application through its Taskfile and normalises the
executable bits per OS.
Builds or packages a Wails v3 application through the wails3 CLI and
normalises the executable bits per OS.

Wails v3 is not v2 with a new flag set. There is no `wails build` invocation
to compose: the project owns a Taskfile, wails3 runs targets in it, and the
per-platform targets are where the real build lives. So this action runs the
project's own target rather than reconstructing the command, which keeps CI
and a developer's local build the same path.
Wails v3 is not v2 with a new flag set — there are no build flags to compose,
because a v3 project describes its own build. But the CLI still owns the
verbs: `wails3 build` and `wails3 package`. Both dispatch to the project's
build description internally, which is Wails' business rather than this
action's, so this runs the verb and lets Wails decide what it means. CI and a
developer's machine then take the same path through the same tool.

inputs:
build:
Expand All @@ -21,12 +22,13 @@ inputs:
build-name:
description: "Binary or bundle name, used to locate the output"
required: true
task:
wails3-command:
description: >
Taskfile target to run. Defaults to <os>:build, resolved from the runner —
darwin:build, linux:build, windows:build — which is the wails3 convention.
Which wails3 CLI verb to run: `build` for the bare binary, `package` for
the platform's installable artefact — the .app bundle, the NSIS
installer, the Linux packages.
required: false
default: ""
default: "build"
wails3-tool:
description: >
How to invoke wails3. Defaults to `go tool wails3`, which resolves the
Expand All @@ -37,33 +39,33 @@ inputs:
default: "go tool wails3"

outputs:
TASK_RUN:
description: "The Taskfile target that was executed"
value: ${{ steps.target.outputs.TASK }}
WAILS3_COMMAND:
description: "The wails3 verb that was executed"
value: ${{ steps.verb.outputs.COMMAND }}

runs:
using: "composite"
steps:
- name: Resolve the Taskfile target
id: target
# Only the two verbs. Anything else would reach the CLI as a subcommand it
# does not have, and the failure would name wails3 rather than the caller
# that asked for it.
- name: Resolve the wails3 verb
id: verb
shell: bash
working-directory: ${{ inputs.app-working-directory }}
env:
REQUESTED: ${{ inputs.task }}
REQUESTED: ${{ inputs.wails3-command }}
run: |
set -euo pipefail
if [ -n "$REQUESTED" ]; then
TASK="$REQUESTED"
else
case "${RUNNER_OS}" in
macOS) TASK="darwin:build" ;;
Linux) TASK="linux:build" ;;
Windows) TASK="windows:build" ;;
*) echo "::error::unsupported runner OS '${RUNNER_OS}'"; exit 1 ;;
esac
fi
echo "TASK=$TASK" >> "$GITHUB_OUTPUT"
echo "[DEBUG_LOG] (wails3) target=$TASK"
COMMAND="${REQUESTED:-build}"
case "$COMMAND" in
build|package) ;;
*)
echo "::error::wails3-command must be 'build' or 'package', got '$COMMAND'"
exit 1
;;
esac
echo "COMMAND=$COMMAND" >> "$GITHUB_OUTPUT"
echo "[DEBUG_LOG] (wails3) command=$COMMAND"

# A project that pins wails3 with a go.mod tool directive builds with the
# version it was tested against. Falling back to an install is for projects
Expand All @@ -86,20 +88,11 @@ runs:
go install github.com/wailsapp/wails/v3/cmd/wails3@latest
fi

- name: Install Task
shell: bash
run: |
set -euo pipefail
if ! command -v task >/dev/null 2>&1; then
go install github.com/go-task/task/v3/cmd/task@latest
echo "$(go env GOPATH)/bin" >> "$GITHUB_PATH"
fi

- name: Build
if: inputs.build == 'true'
shell: bash
working-directory: ${{ inputs.app-working-directory }}
run: ${{ inputs.wails3-tool }} task ${{ steps.target.outputs.TASK }}
run: ${{ inputs.wails3-tool }} ${{ steps.verb.outputs.COMMAND }}

# wails3 writes to bin/. The bundle layout differs per platform, so the
# executable bit is set where each platform actually keeps the binary.
Expand Down
15 changes: 15 additions & 0 deletions tests/action_contracts.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,21 @@ def main() -> int:

# Inputs need a description or the marketplace listing renders blanks.
declared = (doc.get("inputs") or {})

# ${{ inputs.X }} where X is not declared in *this* file resolves to the
# empty string. Nothing warns — not the runner, not the caller, not the
# forwarding check above, which only asks whether a name a file declares
# gets passed on. Forwarding an input the file never declared satisfies
# it while passing nothing, so a chain can be repaired at one hop, report
# sound, and still be severed at the next. That is how `task` was added
# to the root action and forwarded from the orchestrator, and every
# Wails v3 build carried on running its default target.
for i, line in enumerate(path.read_text().split("\n"), 1):
for ref in re.findall(r"inputs\.([A-Za-z0-9_-]+)", line):
if ref not in declared:
problems.append(
f"{rel}:{i}: references inputs.{ref}, which this file does "
f"not declare — it will always be empty")
for name, spec in declared.items():
if not isinstance(spec, dict) or not spec.get("description"):
problems.append(f"{rel}: input {name} has no description")
Expand Down
Loading