Taskfile.yml 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398
  1. version: '3'
  2. tasks:
  3. go:mod:tidy:
  4. summary: Runs `go mod tidy`
  5. internal: true
  6. # Universal/multi-arch builds invoke this concurrently from parallel deps;
  7. # two `go mod tidy` processes racing on go.mod can corrupt it (#4637).
  8. run: once
  9. cmds:
  10. - go mod tidy
  11. install:frontend:deps:
  12. summary: Install frontend dependencies
  13. run: once
  14. cmds:
  15. - task: install:frontend:deps:{{.PACKAGE_MANAGER}}
  16. install:frontend:deps:npm:
  17. dir: frontend
  18. sources:
  19. - package.json
  20. - package-lock.json
  21. generates:
  22. - node_modules
  23. preconditions:
  24. - sh: npm version
  25. msg: "Looks like npm isn't installed. Npm is part of the Node installer: https://nodejs.org/en/download/"
  26. cmds:
  27. - npm install
  28. install:frontend:deps:bun:
  29. dir: frontend
  30. sources:
  31. - package.json
  32. - bun.lock
  33. - bun.lockb
  34. generates:
  35. - node_modules
  36. preconditions:
  37. - sh: bun --version
  38. msg: "bun not found"
  39. cmds:
  40. - bun install
  41. install:frontend:deps:pnpm:
  42. dir: frontend
  43. sources:
  44. - package.json
  45. - pnpm-lock.yaml
  46. generates:
  47. - node_modules
  48. preconditions:
  49. - sh: pnpm --version
  50. msg: "pnpm not found"
  51. cmds:
  52. - pnpm install
  53. install:frontend:deps:yarn:
  54. dir: frontend
  55. sources:
  56. - package.json
  57. - yarn.lock
  58. status:
  59. - test -d node_modules || test -f .pnp.cjs
  60. preconditions:
  61. - sh: yarn --version
  62. msg: "yarn not found"
  63. cmds:
  64. - yarn install
  65. build:frontend:
  66. label: build:frontend (DEV={{.DEV}} RUNNER={{.PACKAGE_MANAGER}})
  67. summary: Build the frontend project
  68. # darwin:build:universal runs its per-arch builds as parallel deps, each of
  69. # which depends on this task. Without run:once the two executions race:
  70. # one regenerates frontend/bindings (-clean deletes it first) while the
  71. # other's bundler is reading it, failing intermittently with
  72. # 'Could not resolve "./bindings/<pkg>"' (#4637).
  73. run: once
  74. dir: frontend
  75. sources:
  76. - "**/*"
  77. - exclude: node_modules/**/*
  78. generates:
  79. - dist/**/*
  80. deps:
  81. - task: install:frontend:deps
  82. - task: generate:bindings
  83. vars:
  84. BUILD_FLAGS:
  85. ref: .BUILD_FLAGS
  86. OBFUSCATED:
  87. ref: .OBFUSCATED
  88. cmds:
  89. - task: frontend:run
  90. vars:
  91. SCRIPT: '{{if eq .DEV "true"}}build:dev{{else}}build{{end}}'
  92. env:
  93. PRODUCTION: '{{if eq .DEV "true"}}false{{else}}true{{end}}'
  94. frontend:run:
  95. summary: Run a frontend script with selected runner
  96. cmds:
  97. - task: frontend:run:{{.PACKAGE_MANAGER}}
  98. vars:
  99. SCRIPT: "{{.SCRIPT}}"
  100. vars:
  101. SCRIPT: "{{.SCRIPT}}"
  102. frontend:run:npm:
  103. dir: frontend
  104. cmds:
  105. - npm run {{.SCRIPT}} -q
  106. vars:
  107. SCRIPT: "{{.SCRIPT}}"
  108. frontend:run:yarn:
  109. dir: frontend
  110. cmds:
  111. - yarn {{.SCRIPT}}
  112. vars:
  113. SCRIPT: "{{.SCRIPT}}"
  114. frontend:run:pnpm:
  115. dir: frontend
  116. cmds:
  117. - pnpm run {{.SCRIPT}}
  118. vars:
  119. SCRIPT: "{{.SCRIPT}}"
  120. frontend:run:bun:
  121. dir: frontend
  122. cmds:
  123. - bun run {{.SCRIPT}}
  124. vars:
  125. SCRIPT: "{{.SCRIPT}}"
  126. frontend:vendor:puppertino:
  127. summary: Fetches Puppertino CSS into frontend/public for consistent mobile styling
  128. sources:
  129. - frontend/public/puppertino/puppertino.css
  130. generates:
  131. - frontend/public/puppertino/puppertino.css
  132. cmds:
  133. - |
  134. set -euo pipefail
  135. mkdir -p frontend/public/puppertino
  136. # If bundled Puppertino exists, prefer it. Otherwise, try to fetch, but don't fail build on error.
  137. if [ ! -f frontend/public/puppertino/puppertino.css ]; then
  138. echo "No bundled Puppertino found. Attempting to fetch from GitHub..."
  139. if curl -fsSL https://raw.githubusercontent.com/codedgar/Puppertino/main/dist/css/full.css -o frontend/public/puppertino/puppertino.css; then
  140. curl -fsSL https://raw.githubusercontent.com/codedgar/Puppertino/main/LICENSE -o frontend/public/puppertino/LICENSE || true
  141. echo "Puppertino CSS downloaded to frontend/public/puppertino/puppertino.css"
  142. else
  143. echo "Warning: Could not fetch Puppertino CSS. Proceeding without download since template may bundle it."
  144. fi
  145. else
  146. echo "Using bundled Puppertino at frontend/public/puppertino/puppertino.css"
  147. fi
  148. # Ensure index.html includes Puppertino CSS and button classes
  149. INDEX_HTML=frontend/index.html
  150. if [ -f "$INDEX_HTML" ]; then
  151. if ! grep -q 'href="/puppertino/puppertino.css"' "$INDEX_HTML"; then
  152. # Insert Puppertino link tag after style.css link
  153. awk '
  154. /href="\/style.css"\/?/ && !x { print; print " <link rel=\"stylesheet\" href=\"/puppertino/puppertino.css\"/>"; x=1; next }1
  155. ' "$INDEX_HTML" > "$INDEX_HTML.tmp" && mv "$INDEX_HTML.tmp" "$INDEX_HTML"
  156. fi
  157. # Replace default .btn with Puppertino primary button classes if present
  158. sed -E -i'' 's/class=\"btn\"/class=\"p-btn p-prim-col\"/g' "$INDEX_HTML" || true
  159. fi
  160. generate:bindings:
  161. summary: Generates bindings for the frontend
  162. run: once
  163. deps:
  164. - task: go:mod:tidy
  165. sources:
  166. - "**/*.[jt]s"
  167. - exclude: frontend/**/*
  168. - frontend/bindings/**/* # Rerun when switching between dev/production mode causes changes in output
  169. - "**/*.go"
  170. - go.mod
  171. - go.sum
  172. generates:
  173. - frontend/bindings/**/*
  174. cmds:
  175. - wails3 generate bindings -f '{{.BUILD_FLAGS}}' -clean=true -time-type=Date{{if eq .OBFUSCATED "true"}} -obfuscated{{end}}
  176. generate:icons:
  177. 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).
  178. run: once
  179. dir: build
  180. sources:
  181. - "appicon.png"
  182. - "appicon.icon"
  183. generates:
  184. - "darwin/icons.icns"
  185. - "windows/icon.ico"
  186. cmds:
  187. - wails3 generate icons -input appicon.png -macfilename darwin/icons.icns -windowsfilename windows/icon.ico -iconcomposerinput appicon.icon -macassetdir darwin
  188. dev:frontend:
  189. summary: Runs the frontend in development mode
  190. deps:
  191. - task: install:frontend:deps
  192. cmds:
  193. - task: frontend:dev:{{.PACKAGE_MANAGER}}
  194. frontend:dev:npm:
  195. dir: frontend
  196. cmds:
  197. - npm run dev -- --port {{.VITE_PORT}} --strictPort
  198. frontend:dev:yarn:
  199. dir: frontend
  200. cmds:
  201. - yarn dev --port {{.VITE_PORT}} --strictPort
  202. frontend:dev:pnpm:
  203. dir: frontend
  204. cmds:
  205. - pnpm dev --port {{.VITE_PORT}} --strictPort
  206. frontend:dev:bun:
  207. dir: frontend
  208. cmds:
  209. - bun run dev --port {{.VITE_PORT}} --strictPort
  210. update:build-assets:
  211. summary: Updates the build assets
  212. dir: build
  213. cmds:
  214. - wails3 update build-assets -name "{{.APP_NAME}}" -binaryname "{{.APP_NAME}}" -config config.yml -dir .
  215. build:server:
  216. summary: Builds the application in server mode (no GUI, HTTP server only)
  217. desc: |
  218. Builds a production server binary by default: -tags server,production,
  219. -trimpath and a stripped binary, mirroring the desktop `build` task.
  220. Server mode runs as a pure HTTP server without native GUI dependencies.
  221. Usage: task build:server [DEV=true] [OBFUSCATED=true] [EXTRA_TAGS=tag1,tag2]
  222. DEV=true development server (-tags server, no strip, inlining kept)
  223. OBFUSCATED=true obfuscated build via garble (requires garble installed)
  224. EXTRA_TAGS additional comma-separated build tags
  225. deps:
  226. - task: build:frontend
  227. vars:
  228. DEV:
  229. ref: .DEV
  230. BUILD_FLAGS:
  231. ref: .BUILD_FLAGS
  232. OBFUSCATED:
  233. ref: .OBFUSCATED
  234. preconditions:
  235. - sh: '{{if eq .OBFUSCATED "true"}}command -v garble >/dev/null 2>&1{{else}}true{{end}}'
  236. 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."
  237. cmds:
  238. - '{{if eq .OBFUSCATED "true"}}garble {{.GARBLE_ARGS}} build{{else}}go build{{end}} {{.BUILD_FLAGS}} -o "{{.BIN_DIR}}/{{.APP_NAME}}-server{{exeExt}}"'
  239. vars:
  240. 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}}'
  241. run:server:
  242. summary: Builds and runs a development server (DEV=true)
  243. deps:
  244. - task: build:server
  245. vars:
  246. DEV: "true"
  247. cmds:
  248. - '"./{{.BIN_DIR}}/{{.APP_NAME}}-server{{exeExt}}"'
  249. build:docker:
  250. summary: Builds a Docker image for server mode deployment
  251. desc: |
  252. Creates a minimal Docker image containing the production server binary.
  253. Defaults to a pure-Go static binary on a distroless/static base. The
  254. production frontend is built first so the embedded assets are current.
  255. Usage: task build:docker [TAG=myapp:latest] [CGO_ENABLED=1] [GO_IMAGE=...] [RUNTIME_IMAGE=...]
  256. For CGO apps, set CGO_ENABLED=1 with libc-compatible builder/runtime images, e.g.:
  257. task build:docker CGO_ENABLED=1 GO_IMAGE=golang:bookworm RUNTIME_IMAGE=gcr.io/distroless/base-debian12
  258. deps:
  259. # Build the production frontend so frontend/dist (embedded by the Go build
  260. # inside the image) is present and current in the Docker build context.
  261. # Pass the server,production tags so binding generation analyses the same
  262. # build the Docker image compiles, not the default-tag build.
  263. - task: build:frontend
  264. vars:
  265. BUILD_FLAGS: "-tags server,production"
  266. cmds:
  267. - >-
  268. docker build
  269. --build-arg CGO_ENABLED={{.CGO_ENABLED | default "0"}}
  270. --build-arg GO_IMAGE={{.GO_IMAGE | default "golang:alpine"}}
  271. --build-arg RUNTIME_IMAGE={{.RUNTIME_IMAGE | default "gcr.io/distroless/static-debian12"}}
  272. -t {{.TAG | default (printf "%s:latest" .APP_NAME)}}
  273. -f build/docker/Dockerfile.server .
  274. vars:
  275. TAG: "{{.TAG}}"
  276. preconditions:
  277. - sh: docker info > /dev/null 2>&1
  278. msg: "Docker is required. Please install Docker first."
  279. - sh: test -f build/docker/Dockerfile.server
  280. msg: "Dockerfile.server not found. Run 'wails3 update build-assets' to generate it."
  281. run:docker:
  282. summary: Builds and runs the Docker image
  283. desc: |
  284. Builds the Docker image and runs it, exposing port 8080.
  285. Usage: task run:docker [TAG=myapp:latest] [PORT=8080]
  286. Note: The internal container port is always 8080. The PORT variable
  287. only changes the host port mapping. Ensure your app uses port 8080
  288. or modify the Dockerfile to match your ServerOptions.Port setting.
  289. deps:
  290. - task: build:docker
  291. vars:
  292. TAG:
  293. ref: .TAG
  294. cmds:
  295. - docker run --rm -p {{.PORT | default "8080"}}:8080 {{.TAG | default (printf "%s:latest" .APP_NAME)}}
  296. vars:
  297. TAG: "{{.TAG}}"
  298. PORT: "{{.PORT}}"
  299. setup:docker:
  300. summary: Builds Docker image for cross-compilation (~800MB download)
  301. desc: |
  302. Builds the Docker image needed for cross-compiling to any platform.
  303. Run this once to enable cross-platform builds from any OS.
  304. cmds:
  305. - docker build -t wails-cross -f build/docker/Dockerfile.cross build/docker/
  306. preconditions:
  307. - sh: docker info > /dev/null 2>&1
  308. msg: "Docker is required. Please install Docker first."
  309. ios:device:list:
  310. summary: Lists connected iOS devices (UDIDs)
  311. cmds:
  312. - xcrun xcdevice list
  313. ios:run:device:
  314. summary: Build, install, and launch on a physical iPhone using Apple tools (xcodebuild/devicectl)
  315. vars:
  316. PROJECT: '{{.PROJECT}}' # e.g., build/ios/xcode/<YourProject>.xcodeproj
  317. SCHEME: '{{.SCHEME}}' # e.g., ios.dev
  318. CONFIG: '{{.CONFIG | default "Debug"}}'
  319. DERIVED: '{{.DERIVED | default "build/ios/DerivedData"}}'
  320. UDID: '{{.UDID}}' # from `task ios:device:list`
  321. BUNDLE_ID: '{{.BUNDLE_ID}}' # e.g., com.yourco.wails.ios.dev
  322. TEAM_ID: '{{.TEAM_ID}}' # optional, if your project is not already set up for signing
  323. preconditions:
  324. - sh: xcrun -f xcodebuild
  325. msg: "xcodebuild not found. Please install Xcode."
  326. - sh: xcrun -f devicectl
  327. msg: "devicectl not found. Please update to Xcode 15+ (which includes devicectl)."
  328. - sh: test -n '{{.PROJECT}}'
  329. msg: "Set PROJECT to your .xcodeproj path (e.g., PROJECT=build/ios/xcode/App.xcodeproj)."
  330. - sh: test -n '{{.SCHEME}}'
  331. msg: "Set SCHEME to your app scheme (e.g., SCHEME=ios.dev)."
  332. - sh: test -n '{{.UDID}}'
  333. msg: "Set UDID to your device UDID (see: task ios:device:list)."
  334. - sh: test -n '{{.BUNDLE_ID}}'
  335. msg: "Set BUNDLE_ID to your app's bundle identifier (e.g., com.yourco.wails.ios.dev)."
  336. cmds:
  337. - |
  338. set -euo pipefail
  339. echo "Building for device: UDID={{.UDID}} SCHEME={{.SCHEME}} PROJECT={{.PROJECT}}"
  340. XCB_ARGS=(
  341. -project "{{.PROJECT}}"
  342. -scheme "{{.SCHEME}}"
  343. -configuration "{{.CONFIG}}"
  344. -destination "id={{.UDID}}"
  345. -derivedDataPath "{{.DERIVED}}"
  346. -allowProvisioningUpdates
  347. -allowProvisioningDeviceRegistration
  348. )
  349. # Optionally inject signing identifiers if provided
  350. if [ -n '{{.TEAM_ID}}' ]; then XCB_ARGS+=(DEVELOPMENT_TEAM={{.TEAM_ID}}); fi
  351. if [ -n '{{.BUNDLE_ID}}' ]; then XCB_ARGS+=(PRODUCT_BUNDLE_IDENTIFIER={{.BUNDLE_ID}}); fi
  352. xcodebuild "${XCB_ARGS[@]}" build | xcpretty || true
  353. # If xcpretty isn't installed, run without it
  354. if [ "${PIPESTATUS[0]}" -ne 0 ]; then
  355. xcodebuild "${XCB_ARGS[@]}" build
  356. fi
  357. # Find built .app
  358. APP_PATH=$(find "{{.DERIVED}}/Build/Products" -type d -name "*.app" -maxdepth 3 | head -n 1)
  359. if [ -z "$APP_PATH" ]; then
  360. echo "Could not locate built .app under {{.DERIVED}}/Build/Products" >&2
  361. exit 1
  362. fi
  363. echo "Installing: $APP_PATH"
  364. xcrun devicectl device install app --device "{{.UDID}}" "$APP_PATH"
  365. echo "Launching: {{.BUNDLE_ID}}"
  366. xcrun devicectl device process launch --device "{{.UDID}}" --stderr console --stdout console "{{.BUNDLE_ID}}"