mirror of
https://github.com/wailsapp/wails.git
synced 2026-03-14 22:55:48 +01:00
Update CLI to pass through variables (#4488)
* feat: Update CLI to pass parameters through to Task commands - Modified build and package commands to accept otherArgs parameter - Updated task wrapper to forward CLI variables to Task - Enhanced task.go to properly parse and handle CLI variables (KEY=VALUE format) - Fixes issue where 'wails3 build' and 'wails3 package' commands weren't forwarding parameters Fixes #4422 * Update changelog * Apply suggestion from @coderabbitai[bot] Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> * Fix cli.mdx --------- Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
This commit is contained in:
parent
c2ba85663f
commit
bf805b4152
10 changed files with 809 additions and 32 deletions
|
|
@ -1,6 +1,7 @@
|
|||
---
|
||||
title: CLI Reference
|
||||
description: Complete reference for the Wails CLI commands
|
||||
sidebar:
|
||||
sidebar:
|
||||
order: 1
|
||||
---
|
||||
|
|
@ -9,7 +10,7 @@ The Wails CLI provides a comprehensive set of commands to help you develop, buil
|
|||
|
||||
## Core Commands
|
||||
|
||||
Core commands are the primary commands used for project creation, development, and building.
|
||||
Core commands are the primary commands used for project creation, development, and building.
|
||||
|
||||
All CLI commands are of the following format: `wails3 <command>`.
|
||||
|
||||
|
|
@ -52,7 +53,7 @@ When provided, this flag will:
|
|||
4. Add all files
|
||||
|
||||
### `dev`
|
||||
Runs the application in development mode. This will give you a live view of your frontend code, and you can make changes and see them reflected
|
||||
Runs the application in development mode. This will give you a live view of your frontend code, and you can make changes and see them reflected
|
||||
in the running application without having to rebuild the entire application. Changes to your Go code will also be detected and the application
|
||||
will automatically rebuild and relaunch.
|
||||
|
||||
|
|
@ -76,22 +77,30 @@ wails3 dev [flags]
|
|||
### `build`
|
||||
Builds a debug version of your application. It defaults to building for the current platform and architecture.
|
||||
|
||||
|
||||
```bash
|
||||
wails3 build
|
||||
wails3 build [CLI variables...]
|
||||
```
|
||||
|
||||
You can pass CLI variables to customize the build:
|
||||
```bash
|
||||
wails3 build PLATFORM=linux CONFIG=production
|
||||
```
|
||||
|
||||
:::note
|
||||
This is equivalent to running `wails3 task build` which runs the `build` task in the project's main Taskfile. You can customise the build process by editing the `Taskfile.yml` file.
|
||||
This is equivalent to running `wails3 task build` which runs the `build` task in the project's main Taskfile. Any CLI variables passed to `build` are forwarded to the underlying task. You can customise the build process by editing the `Taskfile.yml` file.
|
||||
:::
|
||||
|
||||
### `package`
|
||||
|
||||
Creates platform-specific packages for distribution.
|
||||
|
||||
|
||||
```bash
|
||||
wails3 package
|
||||
wails3 package [CLI variables...]
|
||||
```
|
||||
|
||||
You can pass CLI variables to customize the packaging:
|
||||
```bash
|
||||
wails3 package VERSION=2.0.0 OUTPUT=myapp.pkg
|
||||
```
|
||||
|
||||
#### Package Types
|
||||
|
|
@ -104,13 +113,80 @@ The following package types are available for each platform:
|
|||
| macOS | `.app`, |
|
||||
| Linux | `.AppImage`, `.deb`, `.rpm`, `.archlinux` |
|
||||
|
||||
|
||||
|
||||
:::note
|
||||
This is equivalent to `wails3 task package` which runs the `package` task in the project's main Taskfile. You can customise the packaging process by editing the `Taskfile.yml` file.
|
||||
This is equivalent to `wails3 task package` which runs the `package` task in the project's main Taskfile. Any CLI variables passed to `package` are forwarded to the underlying task. You can customise the packaging process by editing the `Taskfile.yml` file.
|
||||
:::
|
||||
|
||||
|
||||
### `task`
|
||||
Runs tasks defined in your project's Taskfile.yml. This is an embedded version of [Taskfile](https://taskfile.dev) that allows you to define and run custom build, test, and deployment tasks.
|
||||
|
||||
```bash
|
||||
wails3 task [taskname] [CLI variables...] [flags]
|
||||
```
|
||||
|
||||
#### CLI Variables
|
||||
You can pass variables to tasks in the format `KEY=VALUE`:
|
||||
```bash
|
||||
wails3 task build PLATFORM=linux CONFIG=production
|
||||
wails3 task deploy ENV=staging VERSION=1.2.3
|
||||
```
|
||||
|
||||
These variables can be accessed in your Taskfile.yml using Go template syntax:
|
||||
```yaml
|
||||
tasks:
|
||||
build:
|
||||
cmds:
|
||||
- echo "Building for {{.PLATFORM | default "darwin"}}"
|
||||
- echo "Config: {{.CONFIG | default "debug"}}"
|
||||
```
|
||||
|
||||
#### Flags
|
||||
| Flag | Description | Default |
|
||||
|--------------|-------------------------------------------|----------|
|
||||
| `-h` | Shows Task usage | `false` |
|
||||
| `-i` | Creates a new Taskfile.yml | `false` |
|
||||
| `-list` | Lists tasks with descriptions | `false` |
|
||||
| `-list-all` | Lists all tasks (with or without descriptions) | `false` |
|
||||
| `-json` | Formats task list as JSON | `false` |
|
||||
| `-status` | Exits with non-zero if task is not up-to-date | `false` |
|
||||
| `-f` | Forces execution even when task is up-to-date | `false` |
|
||||
| `-w` | Enables watch mode for the given task | `false` |
|
||||
| `-v` | Enables verbose mode | `false` |
|
||||
| `-version` | Prints Task version | `false` |
|
||||
| `-s` | Disables echoing | `false` |
|
||||
| `-p` | Executes tasks in parallel | `false` |
|
||||
| `-dry` | Compiles and prints tasks without executing | `false` |
|
||||
| `-summary` | Shows summary about a task | `false` |
|
||||
| `-x` | Pass-through the exit code of the task | `false` |
|
||||
| `-dir` | Sets directory of execution | |
|
||||
| `-taskfile` | Choose which Taskfile to run | |
|
||||
| `-output` | Sets output style: [interleaved|group|prefixed] | |
|
||||
| `-c` | Colored output (enabled by default) | `true` |
|
||||
| `-C` | Limit number of tasks to run concurrently | |
|
||||
| `-interval` | Interval to watch for changes (in seconds) | |
|
||||
|
||||
#### Examples
|
||||
```bash
|
||||
# Run the default task
|
||||
wails3 task
|
||||
|
||||
# Run a specific task
|
||||
wails3 task test
|
||||
|
||||
# Run a task with variables
|
||||
wails3 task build PLATFORM=windows ARCH=amd64
|
||||
|
||||
# List all available tasks
|
||||
wails3 task --list
|
||||
|
||||
# Run multiple tasks in parallel
|
||||
wails3 task -p task1 task2 task3
|
||||
|
||||
# Watch for changes and re-run task
|
||||
wails3 task -w dev
|
||||
```
|
||||
|
||||
### `doctor`
|
||||
Performs a system check and displays a status report.
|
||||
|
||||
|
|
|
|||
|
|
@ -138,6 +138,29 @@ commands, Wails internally translates them to the appropriate task execution:
|
|||
- `wails3 build` → `wails3 task build`
|
||||
- `wails3 package` → `wails3 task package`
|
||||
|
||||
### Passing Parameters to Tasks
|
||||
|
||||
You can pass CLI variables to tasks using the `KEY=VALUE` format. These variables are forwarded through the alias commands:
|
||||
|
||||
```bash
|
||||
# These are equivalent:
|
||||
wails3 build PLATFORM=linux CONFIG=production
|
||||
wails3 task build PLATFORM=linux CONFIG=production
|
||||
|
||||
# Package with custom version:
|
||||
wails3 package VERSION=2.0.0 OUTPUT=myapp.pkg
|
||||
```
|
||||
|
||||
In your `Taskfile.yml`, you can access these variables using Go template syntax:
|
||||
|
||||
```yaml
|
||||
tasks:
|
||||
build:
|
||||
cmds:
|
||||
- echo "Building for {{.PLATFORM | default "darwin"}}"
|
||||
- go build -tags {{.CONFIG | default "debug"}} -o myapp
|
||||
```
|
||||
|
||||
## Common Build Process
|
||||
|
||||
Across all platforms, the build process typically includes the following steps:
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ After processing, the content will be moved to the main changelog and this file
|
|||
## Added
|
||||
<!-- New features, capabilities, or enhancements -->
|
||||
- Add Content Protection on Windows/Mac by [@leaanthony](https://github.com/leaanthony) based on the original work of [@Taiterbase](https://github.com/Taiterbase) in this [PR](https://github.com/wailsapp/wails/pull/4241)
|
||||
- Add support for passing CLI variables to Task commands through `wails3 build` and `wails3 package` aliases (#4422) by @leaanthony in [PR](https://github.com/wailsapp/wails/pull/4488)
|
||||
|
||||
## Changed
|
||||
<!-- Changes in existing functionality -->
|
||||
|
|
|
|||
|
|
@ -33,9 +33,22 @@ func main() {
|
|||
app := clir.NewCli("wails", "The Wails3 CLI", "v3")
|
||||
app.NewSubCommand("docs", "Open the docs").Action(openDocs)
|
||||
app.NewSubCommandFunction("init", "Initialise a new project", commands.Init)
|
||||
app.NewSubCommandFunction("build", "Build the project", commands.Build)
|
||||
|
||||
build := app.NewSubCommand("build", "Build the project")
|
||||
var buildFlags flags.Build
|
||||
build.AddFlags(&buildFlags)
|
||||
build.Action(func() error {
|
||||
return commands.Build(&buildFlags, build.OtherArgs())
|
||||
})
|
||||
|
||||
app.NewSubCommandFunction("dev", "Run in Dev mode", commands.Dev)
|
||||
app.NewSubCommandFunction("package", "Package application", commands.Package)
|
||||
|
||||
pkg := app.NewSubCommand("package", "Package application")
|
||||
var pkgFlags flags.Package
|
||||
pkg.AddFlags(&pkgFlags)
|
||||
pkg.Action(func() error {
|
||||
return commands.Package(&pkgFlags, pkg.OtherArgs())
|
||||
})
|
||||
app.NewSubCommandFunction("doctor", "System status report", commands.Doctor)
|
||||
app.NewSubCommandFunction("releasenotes", "Show release notes", commands.ReleaseNotes)
|
||||
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ require (
|
|||
github.com/pkg/errors v0.9.1
|
||||
github.com/pterm/pterm v0.12.80
|
||||
github.com/samber/lo v1.49.1
|
||||
github.com/stretchr/testify v1.10.0
|
||||
github.com/tc-hib/winres v0.3.1
|
||||
github.com/wailsapp/go-webview2 v1.0.21
|
||||
github.com/wailsapp/mimetype v1.4.1
|
||||
|
|
@ -50,6 +51,7 @@ require (
|
|||
github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd // indirect
|
||||
github.com/charmbracelet/x/term v0.2.1 // indirect
|
||||
github.com/ncruces/go-strftime v0.1.9 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
)
|
||||
|
||||
require (
|
||||
|
|
|
|||
|
|
@ -123,29 +123,62 @@ func RunTask(options *RunTaskOptions, otherArgs []string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
var index int
|
||||
var arg string
|
||||
for index, arg = range os.Args[2:] {
|
||||
if !strings.HasPrefix(arg, "-") {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// Parse task name and CLI variables from otherArgs or os.Args
|
||||
var tasksAndVars []string
|
||||
for _, taskAndVar := range os.Args[index+2:] {
|
||||
if taskAndVar == "--" {
|
||||
break
|
||||
|
||||
// Check if we have a task name specified in options
|
||||
if options.Name != "" {
|
||||
// If task name is provided via options, use it and treat otherArgs as CLI variables
|
||||
tasksAndVars = append([]string{options.Name}, otherArgs...)
|
||||
} else if len(otherArgs) > 0 {
|
||||
// Use otherArgs directly if provided
|
||||
tasksAndVars = otherArgs
|
||||
} else {
|
||||
// Fall back to parsing os.Args for backward compatibility
|
||||
var index int
|
||||
var arg string
|
||||
for index, arg = range os.Args[2:] {
|
||||
if !strings.HasPrefix(arg, "-") {
|
||||
break
|
||||
}
|
||||
}
|
||||
tasksAndVars = append(tasksAndVars, taskAndVar)
|
||||
}
|
||||
|
||||
if len(tasksAndVars) > 0 && len(otherArgs) > 0 {
|
||||
if tasksAndVars[0] == otherArgs[0] {
|
||||
otherArgs = otherArgs[1:]
|
||||
for _, taskAndVar := range os.Args[index+2:] {
|
||||
if taskAndVar == "--" {
|
||||
break
|
||||
}
|
||||
tasksAndVars = append(tasksAndVars, taskAndVar)
|
||||
}
|
||||
}
|
||||
|
||||
if err := e.RunTask(context.Background(), &ast.Call{Task: tasksAndVars[0]}); err != nil {
|
||||
// Default task
|
||||
if len(tasksAndVars) == 0 {
|
||||
tasksAndVars = []string{"default"}
|
||||
}
|
||||
|
||||
// Parse task name and CLI variables
|
||||
taskName := tasksAndVars[0]
|
||||
cliVars := tasksAndVars[1:]
|
||||
|
||||
// Create call with CLI variables
|
||||
call := &ast.Call{
|
||||
Task: taskName,
|
||||
Vars: &ast.Vars{},
|
||||
}
|
||||
|
||||
// Parse CLI variables (format: KEY=VALUE)
|
||||
for _, v := range cliVars {
|
||||
if strings.Contains(v, "=") {
|
||||
parts := strings.SplitN(v, "=", 2)
|
||||
if len(parts) == 2 {
|
||||
call.Vars.Set(parts[0], ast.Var{
|
||||
Value: parts[1],
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if err := e.RunTask(context.Background(), call); err != nil {
|
||||
fatal(err.Error())
|
||||
}
|
||||
return nil
|
||||
|
|
|
|||
289
v3/internal/commands/task_integration_test.go
Normal file
289
v3/internal/commands/task_integration_test.go
Normal file
|
|
@ -0,0 +1,289 @@
|
|||
package commands
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/wailsapp/task/v3/taskfile/ast"
|
||||
)
|
||||
|
||||
func TestTaskParameterPassing(t *testing.T) {
|
||||
// Skip if running in CI without proper environment
|
||||
if os.Getenv("CI") == "true" && os.Getenv("SKIP_INTEGRATION_TESTS") == "true" {
|
||||
t.Skip("Skipping integration test in CI")
|
||||
}
|
||||
|
||||
// Create a temporary directory for test
|
||||
tmpDir, err := os.MkdirTemp("", "wails-task-test-*")
|
||||
require.NoError(t, err)
|
||||
defer os.RemoveAll(tmpDir)
|
||||
|
||||
// Create a test Taskfile
|
||||
taskfileContent := `version: '3'
|
||||
|
||||
tasks:
|
||||
build:
|
||||
cmds:
|
||||
- echo "PLATFORM={{.PLATFORM | default "default-platform"}}"
|
||||
- echo "CONFIG={{.CONFIG | default "default-config"}}"
|
||||
silent: true
|
||||
|
||||
package:
|
||||
cmds:
|
||||
- echo "VERSION={{.VERSION | default "1.0.0"}}"
|
||||
- echo "OUTPUT={{.OUTPUT | default "output.pkg"}}"
|
||||
silent: true
|
||||
|
||||
test:
|
||||
cmds:
|
||||
- echo "ENV={{.ENV | default "test"}}"
|
||||
- echo "FLAGS={{.FLAGS | default "none"}}"
|
||||
silent: true
|
||||
`
|
||||
|
||||
taskfilePath := filepath.Join(tmpDir, "Taskfile.yml")
|
||||
err = os.WriteFile(taskfilePath, []byte(taskfileContent), 0644)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Save current directory
|
||||
originalWd, err := os.Getwd()
|
||||
require.NoError(t, err)
|
||||
defer os.Chdir(originalWd)
|
||||
|
||||
// Change to test directory
|
||||
err = os.Chdir(tmpDir)
|
||||
require.NoError(t, err)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
options *RunTaskOptions
|
||||
otherArgs []string
|
||||
expectedOutput []string
|
||||
}{
|
||||
{
|
||||
name: "Build task with parameters",
|
||||
options: &RunTaskOptions{Name: "build"},
|
||||
otherArgs: []string{"PLATFORM=linux", "CONFIG=production"},
|
||||
expectedOutput: []string{
|
||||
"PLATFORM=linux",
|
||||
"CONFIG=production",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Package task with parameters",
|
||||
options: &RunTaskOptions{Name: "package"},
|
||||
otherArgs: []string{"VERSION=2.5.0", "OUTPUT=myapp.pkg"},
|
||||
expectedOutput: []string{
|
||||
"VERSION=2.5.0",
|
||||
"OUTPUT=myapp.pkg",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Task with default values",
|
||||
options: &RunTaskOptions{Name: "build"},
|
||||
otherArgs: []string{},
|
||||
expectedOutput: []string{
|
||||
"PLATFORM=default-platform",
|
||||
"CONFIG=default-config",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Task with partial parameters",
|
||||
options: &RunTaskOptions{Name: "test"},
|
||||
otherArgs: []string{"ENV=staging"},
|
||||
expectedOutput: []string{
|
||||
"ENV=staging",
|
||||
"FLAGS=none",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
// Capture output
|
||||
output := captureTaskOutput(t, tt.options, tt.otherArgs)
|
||||
|
||||
// Verify expected output
|
||||
for _, expected := range tt.expectedOutput {
|
||||
assert.Contains(t, output, expected, "Output should contain: %s", expected)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCLIParameterFormats(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
otherArgs []string
|
||||
expectError bool
|
||||
expectedVars map[string]string
|
||||
}{
|
||||
{
|
||||
name: "Standard KEY=VALUE format",
|
||||
otherArgs: []string{"build", "KEY1=value1", "KEY2=value2"},
|
||||
expectedVars: map[string]string{
|
||||
"KEY1": "value1",
|
||||
"KEY2": "value2",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Values with equals signs",
|
||||
otherArgs: []string{"build", "URL=https://example.com?key=value", "FORMULA=a=b+c"},
|
||||
expectedVars: map[string]string{
|
||||
"URL": "https://example.com?key=value",
|
||||
"FORMULA": "a=b+c",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Values with spaces (quoted)",
|
||||
otherArgs: []string{"build", "MESSAGE=Hello World", "PATH=/usr/local/bin"},
|
||||
expectedVars: map[string]string{
|
||||
"MESSAGE": "Hello World",
|
||||
"PATH": "/usr/local/bin",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Mixed valid and invalid arguments",
|
||||
otherArgs: []string{"build", "VALID=yes", "invalid-arg", "ANOTHER=value", "--flag"},
|
||||
expectedVars: map[string]string{
|
||||
"VALID": "yes",
|
||||
"ANOTHER": "value",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Empty value",
|
||||
otherArgs: []string{"build", "EMPTY=", "KEY=value"},
|
||||
expectedVars: map[string]string{
|
||||
"EMPTY": "",
|
||||
"KEY": "value",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
call := parseTaskCall(&RunTaskOptions{}, tt.otherArgs)
|
||||
|
||||
// Verify variables
|
||||
for key, expectedValue := range tt.expectedVars {
|
||||
var actualValue string
|
||||
found := false
|
||||
if call.Vars != nil {
|
||||
call.Vars.Range(func(k string, v ast.Var) error {
|
||||
if k == key {
|
||||
actualValue = v.Value.(string)
|
||||
found = true
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
assert.True(t, found, "Variable %s not found", key)
|
||||
assert.Equal(t, expectedValue, actualValue, "Variable %s mismatch", key)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Helper function to capture task output
|
||||
func captureTaskOutput(t *testing.T, options *RunTaskOptions, otherArgs []string) string {
|
||||
// Save original stdout and stderr
|
||||
oldStdout := os.Stdout
|
||||
oldStderr := os.Stderr
|
||||
defer func() {
|
||||
os.Stdout = oldStdout
|
||||
os.Stderr = oldStderr
|
||||
}()
|
||||
|
||||
// Create pipe to capture output
|
||||
r, w, err := os.Pipe()
|
||||
require.NoError(t, err)
|
||||
|
||||
os.Stdout = w
|
||||
os.Stderr = w
|
||||
|
||||
// Run task in a goroutine
|
||||
done := make(chan bool)
|
||||
var taskErr error
|
||||
go func() {
|
||||
// Note: This is a simplified version for testing
|
||||
// In real tests, you might want to mock the Task executor
|
||||
taskErr = RunTask(options, otherArgs)
|
||||
w.Close()
|
||||
done <- true
|
||||
}()
|
||||
|
||||
// Read output
|
||||
var buf bytes.Buffer
|
||||
_, err = buf.ReadFrom(r)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Wait for task to complete
|
||||
<-done
|
||||
|
||||
// Check for errors (might be expected in some tests)
|
||||
if taskErr != nil && !strings.Contains(taskErr.Error(), "expected") {
|
||||
t.Logf("Task error (might be expected): %v", taskErr)
|
||||
}
|
||||
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
func TestBackwardCompatibility(t *testing.T) {
|
||||
// Test that the old way of calling tasks still works
|
||||
tests := []struct {
|
||||
name string
|
||||
osArgs []string
|
||||
expectedTask string
|
||||
expectedVars map[string]string
|
||||
}{
|
||||
{
|
||||
name: "Legacy os.Args parsing",
|
||||
osArgs: []string{"wails3", "task", "build", "PLATFORM=windows"},
|
||||
expectedTask: "build",
|
||||
expectedVars: map[string]string{
|
||||
"PLATFORM": "windows",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Legacy with flags before task",
|
||||
osArgs: []string{"wails3", "task", "--verbose", "test", "ENV=prod"},
|
||||
expectedTask: "test",
|
||||
expectedVars: map[string]string{
|
||||
"ENV": "prod",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
// Save original os.Args
|
||||
originalArgs := os.Args
|
||||
defer func() { os.Args = originalArgs }()
|
||||
|
||||
os.Args = tt.osArgs
|
||||
|
||||
// Parse using the backward compatibility path
|
||||
call := parseTaskCall(&RunTaskOptions{}, []string{})
|
||||
|
||||
assert.Equal(t, tt.expectedTask, call.Task)
|
||||
|
||||
for key, expectedValue := range tt.expectedVars {
|
||||
var actualValue string
|
||||
if call.Vars != nil {
|
||||
call.Vars.Range(func(k string, v ast.Var) error {
|
||||
if k == key {
|
||||
actualValue = v.Value.(string)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
assert.Equal(t, expectedValue, actualValue, "Variable %s mismatch", key)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
188
v3/internal/commands/task_test.go
Normal file
188
v3/internal/commands/task_test.go
Normal file
|
|
@ -0,0 +1,188 @@
|
|||
package commands
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/wailsapp/task/v3/taskfile/ast"
|
||||
)
|
||||
|
||||
func TestParseTaskAndVars(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
options *RunTaskOptions
|
||||
otherArgs []string
|
||||
osArgs []string
|
||||
expectedTask string
|
||||
expectedVars map[string]string
|
||||
}{
|
||||
{
|
||||
name: "Task name in options with CLI variables",
|
||||
options: &RunTaskOptions{Name: "build"},
|
||||
otherArgs: []string{"PLATFORM=linux", "CONFIG=production"},
|
||||
expectedTask: "build",
|
||||
expectedVars: map[string]string{
|
||||
"PLATFORM": "linux",
|
||||
"CONFIG": "production",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Task name and variables in otherArgs",
|
||||
options: &RunTaskOptions{},
|
||||
otherArgs: []string{"test", "ENV=staging", "DEBUG=true"},
|
||||
expectedTask: "test",
|
||||
expectedVars: map[string]string{
|
||||
"ENV": "staging",
|
||||
"DEBUG": "true",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Only task name, no variables",
|
||||
options: &RunTaskOptions{},
|
||||
otherArgs: []string{"deploy"},
|
||||
expectedTask: "deploy",
|
||||
expectedVars: map[string]string{},
|
||||
},
|
||||
{
|
||||
name: "Default task when no args provided",
|
||||
options: &RunTaskOptions{},
|
||||
otherArgs: []string{},
|
||||
osArgs: []string{"wails3", "task"}, // Set explicit os.Args to avoid test framework interference
|
||||
expectedTask: "default",
|
||||
expectedVars: map[string]string{},
|
||||
},
|
||||
{
|
||||
name: "Variables with equals signs in values",
|
||||
options: &RunTaskOptions{Name: "build"},
|
||||
otherArgs: []string{"URL=https://example.com?key=value", "CONFIG=key1=val1,key2=val2"},
|
||||
expectedTask: "build",
|
||||
expectedVars: map[string]string{
|
||||
"URL": "https://example.com?key=value",
|
||||
"CONFIG": "key1=val1,key2=val2",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Skip non-variable arguments",
|
||||
options: &RunTaskOptions{Name: "build"},
|
||||
otherArgs: []string{"PLATFORM=linux", "some-arg", "CONFIG=debug", "--flag"},
|
||||
expectedTask: "build",
|
||||
expectedVars: map[string]string{
|
||||
"PLATFORM": "linux",
|
||||
"CONFIG": "debug",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
// Save original os.Args
|
||||
originalArgs := os.Args
|
||||
defer func() { os.Args = originalArgs }()
|
||||
|
||||
if tt.osArgs != nil {
|
||||
os.Args = tt.osArgs
|
||||
}
|
||||
|
||||
// Parse the task and variables
|
||||
call := parseTaskCall(tt.options, tt.otherArgs)
|
||||
|
||||
// Verify task name
|
||||
assert.Equal(t, tt.expectedTask, call.Task)
|
||||
|
||||
// Verify variables
|
||||
if len(tt.expectedVars) > 0 {
|
||||
require.NotNil(t, call.Vars)
|
||||
|
||||
// Check each expected variable
|
||||
for key, expectedValue := range tt.expectedVars {
|
||||
var actualValue string
|
||||
call.Vars.Range(func(k string, v ast.Var) error {
|
||||
if k == key {
|
||||
actualValue = v.Value.(string)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
assert.Equal(t, expectedValue, actualValue, "Variable %s mismatch", key)
|
||||
}
|
||||
} else if call.Vars != nil {
|
||||
// Ensure no variables were set when none expected
|
||||
count := 0
|
||||
call.Vars.Range(func(k string, v ast.Var) error {
|
||||
count++
|
||||
return nil
|
||||
})
|
||||
assert.Equal(t, 0, count, "Expected no variables but found %d", count)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Helper function to extract the task parsing logic for testing
|
||||
func parseTaskCall(options *RunTaskOptions, otherArgs []string) *ast.Call {
|
||||
var tasksAndVars []string
|
||||
|
||||
// Check if we have a task name specified in options
|
||||
if options.Name != "" {
|
||||
// If task name is provided via options, use it and treat otherArgs as CLI variables
|
||||
tasksAndVars = append([]string{options.Name}, otherArgs...)
|
||||
} else if len(otherArgs) > 0 {
|
||||
// Use otherArgs directly if provided
|
||||
tasksAndVars = otherArgs
|
||||
} else {
|
||||
// Fall back to parsing os.Args for backward compatibility
|
||||
var index int
|
||||
var arg string
|
||||
for index, arg = range os.Args[2:] {
|
||||
if len(arg) > 0 && arg[0] != '-' {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
for _, taskAndVar := range os.Args[index+2:] {
|
||||
if taskAndVar == "--" {
|
||||
break
|
||||
}
|
||||
tasksAndVars = append(tasksAndVars, taskAndVar)
|
||||
}
|
||||
}
|
||||
|
||||
// Default task
|
||||
if len(tasksAndVars) == 0 {
|
||||
tasksAndVars = []string{"default"}
|
||||
}
|
||||
|
||||
// Parse task name and CLI variables
|
||||
taskName := tasksAndVars[0]
|
||||
cliVars := tasksAndVars[1:]
|
||||
|
||||
// Create call with CLI variables
|
||||
call := &ast.Call{
|
||||
Task: taskName,
|
||||
Vars: &ast.Vars{},
|
||||
}
|
||||
|
||||
// Parse CLI variables (format: KEY=VALUE)
|
||||
for _, v := range cliVars {
|
||||
if idx := findEquals(v); idx != -1 {
|
||||
key := v[:idx]
|
||||
value := v[idx+1:]
|
||||
call.Vars.Set(key, ast.Var{
|
||||
Value: value,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return call
|
||||
}
|
||||
|
||||
// Helper to find the first equals sign
|
||||
func findEquals(s string) int {
|
||||
for i, r := range s {
|
||||
if r == '=' {
|
||||
return i
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
|
@ -7,16 +7,23 @@ import (
|
|||
"github.com/wailsapp/wails/v3/internal/flags"
|
||||
)
|
||||
|
||||
func Build(_ *flags.Build) error {
|
||||
return wrapTask("build")
|
||||
// runTaskFunc is a variable to allow mocking in tests
|
||||
var runTaskFunc = RunTask
|
||||
|
||||
func Build(_ *flags.Build, otherArgs []string) error {
|
||||
return wrapTask("build", otherArgs)
|
||||
}
|
||||
|
||||
func Package(_ *flags.Package) error {
|
||||
return wrapTask("package")
|
||||
func Package(_ *flags.Package, otherArgs []string) error {
|
||||
return wrapTask("package", otherArgs)
|
||||
}
|
||||
|
||||
func wrapTask(command string) error {
|
||||
func wrapTask(command string, otherArgs []string) error {
|
||||
term.Warningf("`wails3 %s` is an alias for `wails3 task %s`. Use `wails task` for better control and more options.\n", command, command)
|
||||
os.Args = []string{"wails3", "task", command}
|
||||
return RunTask(&RunTaskOptions{}, []string{})
|
||||
// Rebuild os.Args to include the command and all additional arguments
|
||||
newArgs := []string{"wails3", "task", command}
|
||||
newArgs = append(newArgs, otherArgs...)
|
||||
os.Args = newArgs
|
||||
// Pass the task name via options and otherArgs as CLI variables
|
||||
return runTaskFunc(&RunTaskOptions{Name: command}, otherArgs)
|
||||
}
|
||||
|
|
|
|||
145
v3/internal/commands/task_wrapper_test.go
Normal file
145
v3/internal/commands/task_wrapper_test.go
Normal file
|
|
@ -0,0 +1,145 @@
|
|||
package commands
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/wailsapp/wails/v3/internal/flags"
|
||||
)
|
||||
|
||||
func TestWrapTask(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
command string
|
||||
otherArgs []string
|
||||
expectedOsArgs []string
|
||||
}{
|
||||
{
|
||||
name: "Build with parameters",
|
||||
command: "build",
|
||||
otherArgs: []string{"PLATFORM=linux", "CONFIG=debug"},
|
||||
expectedOsArgs: []string{"wails3", "task", "build", "PLATFORM=linux", "CONFIG=debug"},
|
||||
},
|
||||
{
|
||||
name: "Package with parameters",
|
||||
command: "package",
|
||||
otherArgs: []string{"VERSION=1.0.0", "OUTPUT=app.pkg"},
|
||||
expectedOsArgs: []string{"wails3", "task", "package", "VERSION=1.0.0", "OUTPUT=app.pkg"},
|
||||
},
|
||||
{
|
||||
name: "Build without parameters",
|
||||
command: "build",
|
||||
otherArgs: []string{},
|
||||
expectedOsArgs: []string{"wails3", "task", "build"},
|
||||
},
|
||||
{
|
||||
name: "Build with complex parameter values",
|
||||
command: "build",
|
||||
otherArgs: []string{"URL=https://example.com?key=value", "TAGS=tag1,tag2,tag3"},
|
||||
expectedOsArgs: []string{"wails3", "task", "build", "URL=https://example.com?key=value", "TAGS=tag1,tag2,tag3"},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
// Save original os.Args
|
||||
originalArgs := os.Args
|
||||
defer func() { os.Args = originalArgs }()
|
||||
|
||||
// Mock RunTask to capture the arguments
|
||||
originalRunTask := runTaskFunc
|
||||
var capturedOptions *RunTaskOptions
|
||||
var capturedOtherArgs []string
|
||||
runTaskFunc = func(options *RunTaskOptions, otherArgs []string) error {
|
||||
capturedOptions = options
|
||||
capturedOtherArgs = otherArgs
|
||||
return nil
|
||||
}
|
||||
defer func() { runTaskFunc = originalRunTask }()
|
||||
|
||||
// Execute wrapTask
|
||||
err := wrapTaskInternal(tt.command, tt.otherArgs)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Verify os.Args was set correctly
|
||||
assert.Equal(t, tt.expectedOsArgs, os.Args)
|
||||
|
||||
// Verify RunTask was called with correct parameters
|
||||
assert.Equal(t, tt.command, capturedOptions.Name)
|
||||
assert.Equal(t, tt.otherArgs, capturedOtherArgs)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuildCommand(t *testing.T) {
|
||||
// Save original RunTask
|
||||
originalRunTask := runTaskFunc
|
||||
defer func() { runTaskFunc = originalRunTask }()
|
||||
|
||||
// Mock RunTask to capture the arguments
|
||||
var capturedOptions *RunTaskOptions
|
||||
var capturedOtherArgs []string
|
||||
runTaskFunc = func(options *RunTaskOptions, otherArgs []string) error {
|
||||
capturedOptions = options
|
||||
capturedOtherArgs = otherArgs
|
||||
return nil
|
||||
}
|
||||
|
||||
// Save original os.Args
|
||||
originalArgs := os.Args
|
||||
defer func() { os.Args = originalArgs }()
|
||||
|
||||
// Test Build command
|
||||
buildFlags := &flags.Build{}
|
||||
otherArgs := []string{"PLATFORM=darwin", "CONFIG=release"}
|
||||
|
||||
err := Build(buildFlags, otherArgs)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "build", capturedOptions.Name)
|
||||
assert.Equal(t, otherArgs, capturedOtherArgs)
|
||||
}
|
||||
|
||||
func TestPackageCommand(t *testing.T) {
|
||||
// Save original RunTask
|
||||
originalRunTask := runTaskFunc
|
||||
defer func() { runTaskFunc = originalRunTask }()
|
||||
|
||||
// Mock RunTask to capture the arguments
|
||||
var capturedOptions *RunTaskOptions
|
||||
var capturedOtherArgs []string
|
||||
runTaskFunc = func(options *RunTaskOptions, otherArgs []string) error {
|
||||
capturedOptions = options
|
||||
capturedOtherArgs = otherArgs
|
||||
return nil
|
||||
}
|
||||
|
||||
// Save original os.Args
|
||||
originalArgs := os.Args
|
||||
defer func() { os.Args = originalArgs }()
|
||||
|
||||
// Test Package command
|
||||
packageFlags := &flags.Package{}
|
||||
otherArgs := []string{"VERSION=2.0.0", "OUTPUT=myapp.dmg"}
|
||||
|
||||
err := Package(packageFlags, otherArgs)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "package", capturedOptions.Name)
|
||||
assert.Equal(t, otherArgs, capturedOtherArgs)
|
||||
}
|
||||
|
||||
// Variables to enable mocking in tests
|
||||
var (
|
||||
wrapTaskFunc = wrapTask
|
||||
)
|
||||
|
||||
// Internal version that uses the function variables for testing
|
||||
func wrapTaskInternal(command string, otherArgs []string) error {
|
||||
// Note: We skip the warning message in tests
|
||||
// Rebuild os.Args to include the command and all additional arguments
|
||||
newArgs := []string{"wails3", "task", command}
|
||||
newArgs = append(newArgs, otherArgs...)
|
||||
os.Args = newArgs
|
||||
// Pass the task name via options and otherArgs as CLI variables
|
||||
return runTaskFunc(&RunTaskOptions{Name: command}, otherArgs)
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue