| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400 |
- version: '3'
- tasks:
- go:mod:tidy:
- summary: Runs `go mod tidy`
- internal: true
- # Universal/multi-arch builds invoke this concurrently from parallel deps;
- # two `go mod tidy` processes racing on go.mod can corrupt it (#4637).
- run: once
- cmds:
- - go mod tidy
- install:frontend:deps:
- summary: Install frontend dependencies
- run: once
- cmds:
- - task: install:frontend:deps:{{.PACKAGE_MANAGER}}
- install:frontend:deps:npm:
- dir: frontend
- sources:
- - package.json
- - package-lock.json
- generates:
- - node_modules
- preconditions:
- - sh: npm version
- msg: "Looks like npm isn't installed. Npm is part of the Node installer: https://nodejs.org/en/download/"
- cmds:
- - npm install
- install:frontend:deps:bun:
- dir: frontend
- sources:
- - package.json
- - bun.lock
- - bun.lockb
- generates:
- - node_modules
- preconditions:
- - sh: bun --version
- msg: "bun not found"
- cmds:
- - bun install
- install:frontend:deps:pnpm:
- dir: frontend
- sources:
- - package.json
- - pnpm-lock.yaml
- generates:
- - node_modules
- preconditions:
- - sh: pnpm --version
- msg: "pnpm not found"
- cmds:
- - pnpm install
- install:frontend:deps:yarn:
- dir: frontend
- sources:
- - package.json
- - yarn.lock
- status:
- - test -d node_modules || test -f .pnp.cjs
- preconditions:
- - sh: yarn --version
- msg: "yarn not found"
- cmds:
- - yarn install
- build:frontend:
- label: build:frontend (DEV={{.DEV}} RUNNER={{.PACKAGE_MANAGER}})
- summary: Build the frontend project
- # darwin:build:universal runs its per-arch builds as parallel deps, each of
- # which depends on this task. Without run:once the two executions race:
- # one regenerates frontend/bindings (-clean deletes it first) while the
- # other's bundler is reading it, failing intermittently with
- # 'Could not resolve "./bindings/<pkg>"' (#4637).
- run: once
- dir: frontend
- sources:
- - "**/*"
- - exclude: node_modules/**/*
- generates:
- - dist/**/*
- deps:
- - task: install:frontend:deps
- - task: generate:bindings
- vars:
- BUILD_FLAGS:
- ref: .BUILD_FLAGS
- OBFUSCATED:
- ref: .OBFUSCATED
- cmds:
- - task: frontend:run
- vars:
- SCRIPT: '{{if eq .DEV "true"}}build:dev{{else}}build{{end}}'
- env:
- PRODUCTION: '{{if eq .DEV "true"}}false{{else}}true{{end}}'
- frontend:run:
- summary: Run a frontend script with selected runner
- cmds:
- - task: frontend:run:{{.PACKAGE_MANAGER}}
- vars:
- SCRIPT: "{{.SCRIPT}}"
- vars:
- SCRIPT: "{{.SCRIPT}}"
- frontend:run:npm:
- dir: frontend
- cmds:
- - npm run {{.SCRIPT}} -q
- vars:
- SCRIPT: "{{.SCRIPT}}"
- frontend:run:yarn:
- dir: frontend
- cmds:
- - yarn {{.SCRIPT}}
- vars:
- SCRIPT: "{{.SCRIPT}}"
- frontend:run:pnpm:
- dir: frontend
- cmds:
- - pnpm run {{.SCRIPT}}
- vars:
- SCRIPT: "{{.SCRIPT}}"
- frontend:run:bun:
- dir: frontend
- cmds:
- - bun run {{.SCRIPT}}
- vars:
- SCRIPT: "{{.SCRIPT}}"
- frontend:vendor:puppertino:
- summary: Fetches Puppertino CSS into frontend/public for consistent mobile styling
- sources:
- - frontend/public/puppertino/puppertino.css
- generates:
- - frontend/public/puppertino/puppertino.css
- cmds:
- - |
- set -euo pipefail
- mkdir -p frontend/public/puppertino
- # If bundled Puppertino exists, prefer it. Otherwise, try to fetch, but don't fail build on error.
- if [ ! -f frontend/public/puppertino/puppertino.css ]; then
- echo "No bundled Puppertino found. Attempting to fetch from GitHub..."
- if curl -fsSL https://raw.githubusercontent.com/codedgar/Puppertino/main/dist/css/full.css -o frontend/public/puppertino/puppertino.css; then
- curl -fsSL https://raw.githubusercontent.com/codedgar/Puppertino/main/LICENSE -o frontend/public/puppertino/LICENSE || true
- echo "Puppertino CSS downloaded to frontend/public/puppertino/puppertino.css"
- else
- echo "Warning: Could not fetch Puppertino CSS. Proceeding without download since template may bundle it."
- fi
- else
- echo "Using bundled Puppertino at frontend/public/puppertino/puppertino.css"
- fi
- # Ensure index.html includes Puppertino CSS and button classes
- INDEX_HTML=frontend/index.html
- if [ -f "$INDEX_HTML" ]; then
- if ! grep -q 'href="/puppertino/puppertino.css"' "$INDEX_HTML"; then
- # Insert Puppertino link tag after style.css link
- awk '
- /href="\/style.css"\/?/ && !x { print; print " <link rel=\"stylesheet\" href=\"/puppertino/puppertino.css\"/>"; x=1; next }1
- ' "$INDEX_HTML" > "$INDEX_HTML.tmp" && mv "$INDEX_HTML.tmp" "$INDEX_HTML"
- fi
- # Replace default .btn with Puppertino primary button classes if present
- sed -E -i'' 's/class=\"btn\"/class=\"p-btn p-prim-col\"/g' "$INDEX_HTML" || true
- fi
- generate:bindings:
- summary: Generates bindings for the frontend
- run: once
- deps:
- - task: go:mod:tidy
- sources:
- - "**/*.[jt]s"
- - exclude: frontend/**/*
- - exclude: voicewake/**/* # 独立语音唤醒模块,不参与 Wails 构建/监听
- - frontend/bindings/**/* # Rerun when switching between dev/production mode causes changes in output
- - "**/*.go"
- - exclude: voicewake/**/* # 独立语音唤醒模块,不参与 Wails 构建/监听
- - go.mod
- - go.sum
- generates:
- - frontend/bindings/**/*
- cmds:
- - wails3 generate bindings -f '{{.BUILD_FLAGS}}' -clean=true -time-type=Date{{if eq .OBFUSCATED "true"}} -obfuscated{{end}}
- generate:icons:
- summary: Generates Windows `.ico` and Mac `.icns` from an image; on macOS, `-iconcomposerinput appicon.icon -macassetdir darwin` also produces `Assets.car` from a `.icon` file (skipped on other platforms).
- run: once
- dir: build
- sources:
- - "appicon.png"
- - "appicon.icon"
- generates:
- - "darwin/icons.icns"
- - "windows/icon.ico"
- cmds:
- - wails3 generate icons -input appicon.png -macfilename darwin/icons.icns -windowsfilename windows/icon.ico -iconcomposerinput appicon.icon -macassetdir darwin
- dev:frontend:
- summary: Runs the frontend in development mode
- deps:
- - task: install:frontend:deps
- cmds:
- - task: frontend:dev:{{.PACKAGE_MANAGER}}
- frontend:dev:npm:
- dir: frontend
- cmds:
- - npm run dev -- --port {{.VITE_PORT}} --strictPort
- frontend:dev:yarn:
- dir: frontend
- cmds:
- - yarn dev --port {{.VITE_PORT}} --strictPort
- frontend:dev:pnpm:
- dir: frontend
- cmds:
- - pnpm dev --port {{.VITE_PORT}} --strictPort
- frontend:dev:bun:
- dir: frontend
- cmds:
- - bun run dev --port {{.VITE_PORT}} --strictPort
- update:build-assets:
- summary: Updates the build assets
- dir: build
- cmds:
- - wails3 update build-assets -name "{{.APP_NAME}}" -binaryname "{{.APP_NAME}}" -config config.yml -dir .
- build:server:
- summary: Builds the application in server mode (no GUI, HTTP server only)
- desc: |
- Builds a production server binary by default: -tags server,production,
- -trimpath and a stripped binary, mirroring the desktop `build` task.
- Server mode runs as a pure HTTP server without native GUI dependencies.
- Usage: task build:server [DEV=true] [OBFUSCATED=true] [EXTRA_TAGS=tag1,tag2]
- DEV=true development server (-tags server, no strip, inlining kept)
- OBFUSCATED=true obfuscated build via garble (requires garble installed)
- EXTRA_TAGS additional comma-separated build tags
- deps:
- - task: build:frontend
- vars:
- DEV:
- ref: .DEV
- BUILD_FLAGS:
- ref: .BUILD_FLAGS
- OBFUSCATED:
- ref: .OBFUSCATED
- preconditions:
- - sh: '{{if eq .OBFUSCATED "true"}}command -v garble >/dev/null 2>&1{{else}}true{{end}}'
- msg: "garble is required for obfuscated builds. Install it with: go install mvdan.cc/garble@v0.16.0 (requires Go 1.24+). See https://github.com/burrowers/garble/releases for version/toolchain compatibility."
- cmds:
- - '{{if eq .OBFUSCATED "true"}}garble {{.GARBLE_ARGS}} build{{else}}go build{{end}} {{.BUILD_FLAGS}} -o "{{.BIN_DIR}}/{{.APP_NAME}}-server{{exeExt}}"'
- vars:
- BUILD_FLAGS: '-tags server{{if eq .DEV "true"}}{{if eq .OBFUSCATED "true"}},wails_obfuscated{{end}}{{if .EXTRA_TAGS}},{{.EXTRA_TAGS}}{{end}} -buildvcs=false -gcflags=all="-l"{{else}},production{{if eq .OBFUSCATED "true"}},wails_obfuscated{{end}}{{if .EXTRA_TAGS}},{{.EXTRA_TAGS}}{{end}} -trimpath -buildvcs=false -ldflags="-w -s"{{end}}'
- run:server:
- summary: Builds and runs a development server (DEV=true)
- deps:
- - task: build:server
- vars:
- DEV: "true"
- cmds:
- - '"./{{.BIN_DIR}}/{{.APP_NAME}}-server{{exeExt}}"'
- build:docker:
- summary: Builds a Docker image for server mode deployment
- desc: |
- Creates a minimal Docker image containing the production server binary.
- Defaults to a pure-Go static binary on a distroless/static base. The
- production frontend is built first so the embedded assets are current.
- Usage: task build:docker [TAG=myapp:latest] [CGO_ENABLED=1] [GO_IMAGE=...] [RUNTIME_IMAGE=...]
- For CGO apps, set CGO_ENABLED=1 with libc-compatible builder/runtime images, e.g.:
- task build:docker CGO_ENABLED=1 GO_IMAGE=golang:bookworm RUNTIME_IMAGE=gcr.io/distroless/base-debian12
- deps:
- # Build the production frontend so frontend/dist (embedded by the Go build
- # inside the image) is present and current in the Docker build context.
- # Pass the server,production tags so binding generation analyses the same
- # build the Docker image compiles, not the default-tag build.
- - task: build:frontend
- vars:
- BUILD_FLAGS: "-tags server,production"
- cmds:
- - >-
- docker build
- --build-arg CGO_ENABLED={{.CGO_ENABLED | default "0"}}
- --build-arg GO_IMAGE={{.GO_IMAGE | default "golang:alpine"}}
- --build-arg RUNTIME_IMAGE={{.RUNTIME_IMAGE | default "gcr.io/distroless/static-debian12"}}
- -t {{.TAG | default (printf "%s:latest" .APP_NAME)}}
- -f build/docker/Dockerfile.server .
- vars:
- TAG: "{{.TAG}}"
- preconditions:
- - sh: docker info > /dev/null 2>&1
- msg: "Docker is required. Please install Docker first."
- - sh: test -f build/docker/Dockerfile.server
- msg: "Dockerfile.server not found. Run 'wails3 update build-assets' to generate it."
- run:docker:
- summary: Builds and runs the Docker image
- desc: |
- Builds the Docker image and runs it, exposing port 8080.
- Usage: task run:docker [TAG=myapp:latest] [PORT=8080]
- Note: The internal container port is always 8080. The PORT variable
- only changes the host port mapping. Ensure your app uses port 8080
- or modify the Dockerfile to match your ServerOptions.Port setting.
- deps:
- - task: build:docker
- vars:
- TAG:
- ref: .TAG
- cmds:
- - docker run --rm -p {{.PORT | default "8080"}}:8080 {{.TAG | default (printf "%s:latest" .APP_NAME)}}
- vars:
- TAG: "{{.TAG}}"
- PORT: "{{.PORT}}"
- setup:docker:
- summary: Builds Docker image for cross-compilation (~800MB download)
- desc: |
- Builds the Docker image needed for cross-compiling to any platform.
- Run this once to enable cross-platform builds from any OS.
- cmds:
- - docker build -t wails-cross -f build/docker/Dockerfile.cross build/docker/
- preconditions:
- - sh: docker info > /dev/null 2>&1
- msg: "Docker is required. Please install Docker first."
- ios:device:list:
- summary: Lists connected iOS devices (UDIDs)
- cmds:
- - xcrun xcdevice list
- ios:run:device:
- summary: Build, install, and launch on a physical iPhone using Apple tools (xcodebuild/devicectl)
- vars:
- PROJECT: '{{.PROJECT}}' # e.g., build/ios/xcode/<YourProject>.xcodeproj
- SCHEME: '{{.SCHEME}}' # e.g., ios.dev
- CONFIG: '{{.CONFIG | default "Debug"}}'
- DERIVED: '{{.DERIVED | default "build/ios/DerivedData"}}'
- UDID: '{{.UDID}}' # from `task ios:device:list`
- BUNDLE_ID: '{{.BUNDLE_ID}}' # e.g., com.yourco.wails.ios.dev
- TEAM_ID: '{{.TEAM_ID}}' # optional, if your project is not already set up for signing
- preconditions:
- - sh: xcrun -f xcodebuild
- msg: "xcodebuild not found. Please install Xcode."
- - sh: xcrun -f devicectl
- msg: "devicectl not found. Please update to Xcode 15+ (which includes devicectl)."
- - sh: test -n '{{.PROJECT}}'
- msg: "Set PROJECT to your .xcodeproj path (e.g., PROJECT=build/ios/xcode/App.xcodeproj)."
- - sh: test -n '{{.SCHEME}}'
- msg: "Set SCHEME to your app scheme (e.g., SCHEME=ios.dev)."
- - sh: test -n '{{.UDID}}'
- msg: "Set UDID to your device UDID (see: task ios:device:list)."
- - sh: test -n '{{.BUNDLE_ID}}'
- msg: "Set BUNDLE_ID to your app's bundle identifier (e.g., com.yourco.wails.ios.dev)."
- cmds:
- - |
- set -euo pipefail
- echo "Building for device: UDID={{.UDID}} SCHEME={{.SCHEME}} PROJECT={{.PROJECT}}"
- XCB_ARGS=(
- -project "{{.PROJECT}}"
- -scheme "{{.SCHEME}}"
- -configuration "{{.CONFIG}}"
- -destination "id={{.UDID}}"
- -derivedDataPath "{{.DERIVED}}"
- -allowProvisioningUpdates
- -allowProvisioningDeviceRegistration
- )
- # Optionally inject signing identifiers if provided
- if [ -n '{{.TEAM_ID}}' ]; then XCB_ARGS+=(DEVELOPMENT_TEAM={{.TEAM_ID}}); fi
- if [ -n '{{.BUNDLE_ID}}' ]; then XCB_ARGS+=(PRODUCT_BUNDLE_IDENTIFIER={{.BUNDLE_ID}}); fi
- xcodebuild "${XCB_ARGS[@]}" build | xcpretty || true
- # If xcpretty isn't installed, run without it
- if [ "${PIPESTATUS[0]}" -ne 0 ]; then
- xcodebuild "${XCB_ARGS[@]}" build
- fi
- # Find built .app
- APP_PATH=$(find "{{.DERIVED}}/Build/Products" -type d -name "*.app" -maxdepth 3 | head -n 1)
- if [ -z "$APP_PATH" ]; then
- echo "Could not locate built .app under {{.DERIVED}}/Build/Products" >&2
- exit 1
- fi
- echo "Installing: $APP_PATH"
- xcrun devicectl device install app --device "{{.UDID}}" "$APP_PATH"
- echo "Launching: {{.BUNDLE_ID}}"
- xcrun devicectl device process launch --device "{{.UDID}}" --stderr console --stdout console "{{.BUNDLE_ID}}"
|